// %flair:license{ // This file is part of the Flair framework distributed under the // CECILL-C License, Version 1.0. // %flair:license} // created: 2011/08/31 // filename: FrameworkManager.cpp // // author: Guillaume Sanahuja // Copyright Heudiasyc UMR UTC/CNRS 7253 // // version: $Id: $ // // purpose: Classe de base de la librairie // // /*********************************************************************/ #include "FrameworkManager_impl.h" #include "FrameworkManager.h" #include "Widget_impl.h" #include #include "IODevice.h" #include "IODevice_impl.h" #include "TabWidget.h" #include "Layout.h" #include "PushButton.h" #include "communication.h" #include "config.h" #include "Watchdog.h" #include #include #include #include #include #include #include #ifdef __XENO__ #include #endif using namespace std; using namespace flair::core; using namespace flair::gui; ui_com* FrameworkManager_impl::com=NULL; FrameworkManager_impl* FrameworkManager_impl::_this=NULL; namespace { #ifdef __XENO__ #ifdef SIGDEBUG static const char *reason_str[] = { "undefined", "received signal", "invoked syscall", "triggered fault", "affected by priority inversion", "missing mlockall", "runaway thread", }; void warn_upon_switch(int sig, siginfo_t *si, void *context) { unsigned int reason = si->si_value.sival_int; void *bt[32]; int nentries; #ifdef SIGDEBUG_WATCHDOG printf("\nSIGDEBUG received, reason %d: %s\n", reason, reason <= SIGDEBUG_WATCHDOG ? reason_str[reason] : ""); #endif // Dump a backtrace of the frame which caused the switch to secondary mode: nentries = backtrace(bt,sizeof(bt) / sizeof(bt[0])); backtrace_symbols_fd(bt,nentries,fileno(stdout)); } #else //SIGDEBUG void warn_upon_switch(int sig __attribute__((unused))) { void *bt[32]; int nentries; /* Dump a backtrace of the frame which caused the switch to secondary mode: */ nentries = backtrace(bt,sizeof(bt) / sizeof(bt[0])); backtrace_symbols_fd(bt,nentries,fileno(stdout)); } #endif //SIGDEBUG #endif //__XENO__ void seg_fault(int sig __attribute__((unused))) { void *bt[32]; int nentries; printf("Segmentation fault:\n"); /* Dump a backtrace of the frame which caused the segfault: */ nentries = backtrace(bt,sizeof(bt) / sizeof(bt[0])); backtrace_symbols_fd(bt,nentries,fileno(stdout)); exit(1); } } FrameworkManager_impl::FrameworkManager_impl(FrameworkManager *self,string name): Thread(self,"FrameworkManager",FRAMEWORK_TASK_PRIORITY) { this->self=self; is_logging=false; logger_defined=false; disable_errors=false; ui_defined=false; rcv_buf=NULL; _this=this; // Avoids memory swapping for this program mlockall(MCL_CURRENT|MCL_FUTURE); struct sigaction sa; //catch segfault signal(SIGSEGV, seg_fault); //catch primary->secondary switch #ifdef __XENO__ #ifdef SIGDEBUG sigemptyset(&sa.sa_mask); sa.sa_sigaction = warn_upon_switch; sa.sa_flags = SA_SIGINFO; sigaction(SIGDEBUG, &sa, NULL); #else signal(SIGXCPU, warn_upon_switch); #endif string task_name="Framework_"+name; int status=rt_task_shadow(NULL,task_name.c_str(),10,0); if(status!=0) { self->Err("rt_task_shadow error (%s)\n",strerror(-status)); } #endif //__XENO__ } void FrameworkManager_impl::ConnectionLost(void) { Err("connection lost\n"); gcs_watchdog->SafeStop(); connection_lost=true; } FrameworkManager_impl::~FrameworkManager_impl() { //Printf("destruction FrameworkManager_impl\n"); int status; SafeStop(); Join(); if(rcv_buf!=NULL) free(rcv_buf); if(logger_defined==true) { continuer=false; (void)pthread_join(log_th,NULL); status=DeletePipe(&cmd_pipe); if(status!=0) { Err("Error deleting pipe (%s)\n",strerror(-status)); } status=DeletePipe(&data_pipe); if(status!=0) { Err("Error deleting pipe (%s)\n",strerror(-status)); } #ifdef __XENO__ status=rt_heap_delete(&log_heap); if(status!=0) { Err("rt_heap_delete error (%s)\n",strerror(-status)); } #endif logs.clear(); } if(file_doc!=NULL) xmlFreeDoc(file_doc); if(ui_defined) delete top_layout; if(com!=NULL) { delete com; status=UDT::close(com_sock); if(status!=0) printf("Error udt::close %s",UDT::getlasterror().getErrorMessage()); status=UDT::close(file_sock); if(status!=0) printf("Error udt::close %s",UDT::getlasterror().getErrorMessage()); SleepMS(200);//a revoir, sinon UDT::cleanup bloque en RT UDT::cleanup(); } //Printf("destruction FrameworkManager_impl ok\n"); } void FrameworkManager_impl::SetupConnection(string address,uint16_t port,size_t rcv_buf_size) { UDT::startup(); this->rcv_buf_size=rcv_buf_size; //socket file_socket, doit être créé en premier, cf station sol Printf("Connecting to %s:%i\n",address.c_str(),port); file_sock=GetSocket(address,port); com_sock=GetSocket(address,port); //receive buffer allocation rcv_buf=(char*)malloc(rcv_buf_size); if(rcv_buf==NULL) { Err("receive buffer malloc error\n"); } com=new ui_com(this,com_sock); //file managment bool blocking = true; UDT::setsockopt(file_sock, 0, UDT_SNDSYN, &blocking, sizeof(bool)); UDT::setsockopt(file_sock, 0, UDT_RCVSYN, &blocking, sizeof(bool)); //configure timeout of blocking receive, short timeout to have priority on the other socket (see run method) int timeout=1;//ms UDT::setsockopt(file_sock, 0, UDT_RCVTIMEO, &timeout, sizeof(int)); timeout=100;//ms UDT::setsockopt(com_sock, 0, UDT_RCVTIMEO, &timeout, sizeof(int)); Start(); //watchdog for connection with ground station connection_lost=false; gcs_watchdog=new Watchdog(this,std::bind(&FrameworkManager_impl::ConnectionLost,this),(Time)1000000000); gcs_watchdog->Start(); } void FrameworkManager_impl::SetupUserInterface(string xml_file) { ui_defined=true; this->xml_file=xml_file; //top_layout=new Layout(NULL,XML_ROOT_ELEMENT,XML_ROOT_TYPE); top_layout=new Layout(NULL,self->ObjectName(),XML_ROOT_TYPE); //xml setup of the main widget if(xml_file!="") { xmlNodePtr* file_node=&(((Widget*)(top_layout))->pimpl_->file_node); file_doc = xmlParseFile(xml_file.c_str()); if (file_doc == NULL ) { self->Warn("XML document not parsed successfully. Creating a new one.\n"); file_doc = xmlNewDoc((xmlChar*)"1.0"); *file_node=xmlNewNode(NULL, (xmlChar*)XML_ROOT_TYPE); xmlSetProp(*file_node,(xmlChar*)"name",(xmlChar*)ObjectName().c_str()); xmlDocSetRootElement(file_doc, *file_node); //PrintXml(); } else { *file_node=xmlDocGetRootElement(file_doc); if(xmlStrcmp((*file_node)->name,(xmlChar*)XML_ROOT_TYPE)) { self->Warn("%s, no match found in xml file\n",XML_ROOT_TYPE); *file_node=xmlNewNode(NULL, (xmlChar*)XML_ROOT_TYPE); xmlSetProp(*file_node,(xmlChar*)"name",(xmlChar*)ObjectName().c_str()); xmlDocSetRootElement(file_doc, *file_node); } } } else { self->Err("xml file not defined\n"); } //gui tabwidget=new TabWidget(top_layout->At(0,0),XML_MAIN_TABWIDGET,TabWidget::North); save_button=new PushButton(top_layout->At(1,0),"save config on target (" + self->ObjectName() + ")"); //load_button=new PushButton(top_layout->At(1,1),"load config on target (" + self->ObjectName() + ")"); } //in case of RT, this thread switches to secondary mode when calling com->Receive //it switches back to RT in ProcessXML when mutex are locked void FrameworkManager_impl::Run(void) { while(!ToBeStopped()) { ssize_t bytesRead; bytesRead=com->Receive(rcv_buf,rcv_buf_size); if(bytesRead==(ssize_t)rcv_buf_size) Err("FrameworkManager max receive size, augmenter le buffer size!\n"); if(bytesRead>0) { //printf("recu %ld, trame %x\n",bytesRead,(uint8_t)rcv_buf[0]); //rcv_buf[bytesRead-1]=0;//pour affichage //printf("%s\n",rcv_buf); switch((uint8_t)rcv_buf[0]) { case XML_HEADER: { xmlDoc *doc; rcv_buf[bytesRead]=0; doc = xmlReadMemory(rcv_buf, (int)bytesRead, "include.xml", "ISO-8859-1", 0); xmlNode *cur_node = NULL; for (cur_node = xmlDocGetRootElement(doc); cur_node; cur_node = cur_node->next) { if (cur_node->type == XML_ELEMENT_NODE) { if(!xmlStrcmp(cur_node->name,(xmlChar*)XML_ROOT_TYPE) ) { #ifdef __XENO__ WarnUponSwitches(true);//ProcessXML should not switch to secondary mode #endif top_layout->Widget::pimpl_->ProcessXML(cur_node->children); #ifdef __XENO__ WarnUponSwitches(false);//other parts of this thread can switch to secondary mode #endif break; } } } xmlFreeDoc(doc); if(save_button->Clicked()) SaveXml(); SaveXmlChange(rcv_buf); break; } case WATCHDOG_HEADER: { gcs_watchdog->Touch(); break; } default: Err("unknown id: %x\n",(uint8_t)rcv_buf[0]); break; } } else { if(com->ConnectionLost()) SleepMS(10);//avoid infinite loop in this case } } } void FrameworkManager_impl::SaveXml(void) { if(ui_defined) xmlSaveFormatFile(xml_file.c_str(),file_doc,1); } void FrameworkManager_impl::SaveXmlChange(char* buf) { if(is_logging==true) { FILE *xml_change; char filename[256]; Time time=GetTime(); sprintf(filename,"%s/changes_at_%lld.xml",log_path.c_str(),time); xml_change=fopen(filename,"a"); fprintf(xml_change,"%s",buf); fclose(xml_change); sprintf(filename,"changes_at_%lld.xml",time); xml_changes.push_back(filename); } } void FrameworkManager_impl::SendFile(string path,string name) { char *buf,*more_buf; int size; filebuf *pbuf; ssize_t nb_write; string filename=path+ "/" +name; // open the file fstream ifs(filename.c_str(), ios::in | ios::binary); ifs.seekg(0, ios::end); size = ifs.tellg(); ifs.seekg(0, ios::beg); pbuf=ifs.rdbuf(); if(size<=0) { Err("error opening file %s\n",filename.c_str()); return; } buf=(char*)malloc(sizeof(uint8_t)+sizeof(int)+name.size()); if(buf==NULL) { Err("malloc error, not sending file\n"); return; } if(IsBigEndian()) { buf[0]=FILE_INFO_BIG_ENDIAN; } else { buf[0]=FILE_INFO_LITTLE_ENDIAN; } memcpy(buf+1,&size,sizeof(int)); memcpy(buf+1+sizeof(int),name.c_str(),name.size()); Printf("sending %s, size: %i\n",filename.c_str(),size); // send file information UDT::sendmsg(file_sock, buf, sizeof(uint8_t)+sizeof(int)+name.size(), -1,true); more_buf=(char*)realloc((void*)buf,size); if(more_buf==NULL) { Err("realloc error, not sending file\n"); free(buf); return; } else { buf=more_buf; } pbuf->sgetn (buf,size); // send the file nb_write=UDT::sendmsg(file_sock, buf, size,-1,true); if(nb_write<0) { Err("UDT::sendmsg error (%s)\n",UDT::getlasterror().getErrorMessage()); } else if (nb_write != size) { Err("UDT::sendmsg error, sent %ld/%i\n",nb_write,size); } ifs.close(); free(buf); } void FrameworkManager_impl::FinishSending() { char rm_cmd[256]; //send orignal xml SendFile(log_path,"setup.xml"); sprintf(rm_cmd,"rm %s/setup.xml",log_path.c_str()); system(rm_cmd); //send xml changes for(size_t i=0;iObjectName()+ "-" + name; //xenomai limitation if(name.size()>31) self->Err("rt_pipe_create error (%s is too long)\n",name.c_str()); //start log writter #ifdef RT_PIPE_SIZE return rt_pipe_create(fd, name.c_str(), P_MINOR_AUTO,RT_PIPE_SIZE); #else return rt_pipe_create(fd, name.c_str(), P_MINOR_AUTO,0); #endif } #else int FrameworkManager_impl::CreatePipe(int (*fd)[2],string name) { //if(pipe2(fd[0],O_NONBLOCK) == -1) if(pipe(fd[0]) == -1) { return errno; } else { int attr=fcntl((*fd)[0], F_GETFL,0); if(attr == -1) { return errno; } if(fcntl((*fd)[0], F_SETFL, attr|O_NONBLOCK)== -1) { return errno; } attr=fcntl((*fd)[1], F_GETFL,0); if(attr == -1) { return errno; } if(fcntl((*fd)[1], F_SETFL, attr|O_NONBLOCK)== -1) { return errno; } return 0; } } #endif #ifdef __XENO__ int FrameworkManager_impl::DeletePipe(RT_PIPE* fd) { return rt_pipe_delete(fd); } #else int FrameworkManager_impl::DeletePipe(int (*fd)[2]) { int status1=close((*fd)[0]); int status2=close((*fd)[1]); if(status1==0 && status2==0) return 0; if(status1!=0) return status1; if(status2!=0) return status2; } #endif void FrameworkManager_impl::SetupLogger(string log_path) { if(logger_defined==true) { Warn("SetupLogger() was already called.\n"); return; } this->log_path=log_path; int status=CreatePipe(&cmd_pipe,"log_cmd"); if(status!=0) { Err("Error creating pipe (%s)\n",strerror(-status)); return; } status=CreatePipe(&data_pipe,"log_data"); if(status!=0) { Err("Error creating pipe (%s)\n",strerror(-status)); return; } #ifdef __XENO__ string tmp_name; tmp_name=self->ObjectName() + "-log_heap"; status=rt_heap_create(&log_heap,tmp_name.c_str(),LOG_HEAP,H_FIFO); if(status!=0) { Err("rt_heap_create error (%s)\n",strerror(-status)); return; } #endif //__XENO__ continuer=true; #ifdef NRT_STACK_SIZE // Initialize thread creation attributes pthread_attr_t attr; if(pthread_attr_init(&attr) != 0) { Err("pthread_attr_init error\n"); return; } if(pthread_attr_setstacksize(&attr, NRT_STACK_SIZE) != 0) { Err("pthread_attr_setstacksize error\n"); return; } if(pthread_create(&log_th, &attr, write_log_user, (void*)this) < 0) #else if(pthread_create(&log_th, NULL, write_log_user, (void*)this) < 0) #endif { Err("pthread_create error\n"); return; } #ifdef NRT_STACK_SIZE if(pthread_attr_destroy(&attr) != 0) { Err("pthread_attr_destroy error\n"); return; } #endif logger_defined=true; } void FrameworkManager_impl::AddDeviceToLog(IODevice *device) { if(logger_defined==false) { Err("SetupLogger() was not called, not starting log\n"); return; } if(is_logging==false) { if(device->pimpl_->SetToBeLogged()) { log_desc_t tmp; tmp.device=device; logs.push_back(tmp); } else { Warn("not adding it twice\n"); } } else { Err("impossible while logging\n"); } } void FrameworkManager_impl::StartLog(void) { if(logger_defined==false) { Err("SetupLogger() was not called, not starting log\n"); return; } ssize_t written; size_t nb_err=0; if(logs.size()==0) { Warn("Not starting log: nothing to log!\n"); return; } if(is_logging==false) { for(size_t i=0;ipimpl_->LogSize(); #ifdef __XENO__ written=rt_pipe_write(&cmd_pipe,&logs.at(i),sizeof(log_desc_t),P_NORMAL); #else written=write(cmd_pipe[1],&logs.at(i),sizeof(log_desc_t)); #endif if(written<0) { Err("write pipe error (%s)\n",strerror(-written)); nb_err++; logs.at(i).running=false; } else if (written != sizeof(log_desc_t)) { Err("write pipe error %ld/%ld\n",written,sizeof(log_desc_t)); nb_err++; logs.at(i).running=false; } } if(nb_err!=logs.size()) is_logging=true; } else { Warn("Already logging\n"); } } void FrameworkManager_impl::StopLog(void) { ssize_t written; if(is_logging==true) { for(size_t i=0;i logs; #ifdef __XENO__ filename=NRT_PIPE_PATH+ caller->self->ObjectName() + "-log_cmd"; while(cmd_pipe<0) { cmd_pipe = open(filename.c_str(), O_RDWR); if (cmd_pipe < 0 && errno!=ENOENT) caller->self->Err("open rt_pipe error: %s %i\n",filename.c_str(),errno); usleep(1000); } filename=NRT_PIPE_PATH+ caller->self->ObjectName() + "-log_data"; while(data_pipe<0) { data_pipe = open(filename.c_str(), O_RDWR); if (data_pipe < 0 && errno!=ENOENT) caller->self->Err("open rt_pipe error: %s %i\n",filename.c_str(),errno); usleep(1000); } #else cmd_pipe=caller->cmd_pipe[0]; data_pipe=caller->data_pipe[0]; #endif while(caller->continuer==true) { FD_ZERO(&set); FD_SET(cmd_pipe, &set); FD_SET(data_pipe, &set); timeout.tv_sec = 0; timeout.tv_usec = SELECT_TIMEOUT_MS*1000; rv = select(FD_SETSIZE, &set, NULL, NULL, &timeout); if(rv == -1) { caller->Err("select error\n"); // an error accured } else if(rv == 0) { //printf("timeout write_log_user %s\n",caller->ObjectName().c_str()); // a timeout occured } else { if(FD_ISSET(cmd_pipe, &set)) { log_desc_t tmp; read(cmd_pipe,&tmp,sizeof(log_desc_t)); if(tmp.running==true)//start logging { filename= caller->log_path + "/" +caller->FileName(tmp.device)+ ".dbt"; printf("Creating log file %s (log size %i)\n",filename.c_str(), (int)tmp.size); tmp.dbtFile = inithdFile((char*)filename.c_str(),UAV,tmp.size); logs.push_back(tmp); if(logs.size()==1) { filename= caller->log_path + "/setup.xml"; xmlSaveFile(filename.c_str(),caller->file_doc); } } else//stop logging { for(size_t i=0;iFileName(logs.at(i).device)+ ".dbt"; caller->SendFile(caller->log_path,filename); fstream txt_file; filename= caller->FileName(logs.at(i).device)+ ".txt"; txt_file.open((caller->log_path+"/"+filename).c_str(),fstream::out); txt_file << "1: time (us)\n2: time (ns)\n"; int index=3; logs.at(i).device->pimpl_->WriteLogsDescriptors(txt_file,&index); txt_file.close(); caller->SendFile(caller->log_path,filename); } } //a revoir celui ci est le xml enregistré et pas forcement l'actuel //if(caller->xml_file!="") caller->SendFile(caller->xml_file); caller->FinishSending(); logs.clear(); } } if(FD_ISSET(data_pipe, &set)) { log_header_t header; read(data_pipe,&header,sizeof(log_header_t)); for(size_t i=0;iObjectName().c_str()); //for(int i=0;iself->Err("%s log size is not correct %i/%i\n",header.device->ObjectName().c_str(),header.size,logs.at(i).size); } free(buf); } } } } } #ifdef __XENO__ close(cmd_pipe); close(data_pipe); #endif pthread_exit(0); } string FrameworkManager_impl::FileName(IODevice* device) { return getFrameworkManager()->ObjectName() + "_"+device->ObjectName(); } void FrameworkManager_impl::PrintXml(void) const { xmlChar *xmlbuff; int buffersize; xmlDocDumpFormatMemory(file_doc, &xmlbuff, &buffersize, 1); Printf("xml:\n%s\n",xmlbuff); xmlFree(xmlbuff); }