/* Copyright (C) 2001 Marc Casaldaliga Albisu ================================================================ This code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Emacs (which is required to make this stuff work); if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ================================================================== */ //it is a good idea to encapsulate all the configuration file dependence in one a wrapper class SubFormatSubsystem, and derive from it //#include "SubFormatSubsystem.hxx" #include "Subsystem.hxx" #include "PeriodicAction.hxx" #include "TCPListener.hxx" #include "TCPSender.hxx" #include "IONotifier.hxx" #include //for signals/slots (Callbacks in C++, see http://libsigc.sourceforge.net/) #include //libsigc must be installed #include using namespace SigC; #include //for usleep #include #include Subsystem::Subsystem (unsigned short int portCommandListener, unsigned long int reportPeriod, char * ccName, unsigned short int portReportListener , unsigned short int maxTimeoutCount_, char * specialReportOnStartup ) : TCPListener(portCommandListener), reportSender(ccName,portReportListener), locked(false), reporting(false),maxTimeoutCount(maxTimeoutCount_),reportPeriod(reportPeriod) { //the behaviour is the following: commandListener has been started as a being derived from TCPListener. When connection from cc is opened commands are processed in this->process() //when REPOR arrives reportSender(TCPSender) here tries to connect reportListener in CC. When this happens will start a periodic reporting //by now //demo report: strcpy(reportString,"standby:11:44:20:11:01:6:34:12:6.0:6.1:6.05:11:01:36:34:12:10.6:6.0:6.0:00:00:30.0:00:00:4.6:0.1:0.05:0.004:0.0:0.001:0.003:guiding:0.1deg:on:3.1Hz:0.09deg:J2000:Mkn421:messagefromthedrive:"); pthread_mutex_init(&mutex4report,NULL); pthread_mutex_init(&mutex4reporting,NULL); } void * Subsystem::ReportingAndCheckingLoop() { #ifdef DEBUG printf("Subsystem: ReportingAndChecking Thread created\n"); #endif timeoutCount=0; acknowledgeReceivedForLastReport=true; while(!reportSender.isTrialToConnectSuccesful()){ #ifdef DEBUG printf("Subsystem: retrying in %d usec\n",reportPeriod); #endif usleep(reportPeriod); } fd_set read_fd_set; FD_ZERO (&read_fd_set); FD_SET(reportSender.IODescriptor(), &read_fd_set); struct timeval tv; tv.tv_sec = 0; tv.tv_usec = (reportPeriod); while(Reporting()) { /* Block until input arrives, which is ack. */ if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, &tv) < 0) { perror("Subsystem: select"); exit (EXIT_FAILURE); } if (FD_ISSET(reportSender.IODescriptor(), &read_fd_set)) { //input arrived, which is the ack #ifdef DEBUG printf("Subsystem: ack received\n"); #endif CheckReportAcknowledge(); FD_SET (reportSender.IODescriptor(), &read_fd_set); continue; //in linux, tv after select represents the time not slept, so we feed it again to select to complete the reportPeriod }else{ //input didn't arrrive so it's time to report if(!SendReport()){ //sending was not succesfull //ResetConnectionToCC(); if(locked){ ULock(); } reportSender.CloseConnection(); #ifdef DEBUG printf("Subsystem: ReportingAndChecking Thread exited on ack checking \n"); #endif //TO DO!!!!!!!!!!!!! // TCPListener::ClosingChannel(); break; } FD_SET (reportSender.IODescriptor(), &read_fd_set); tv.tv_sec = 0; tv.tv_usec = reportPeriod; } } //if program reaches here is because it hasn't been able to report correctly #ifdef DEBUG printf("Subsystem: ReportingAndChecking Thread finished\n"); #endif return 0; } void Subsystem::SetReportString(char * report) { SuspendComm(); strcpy(this->reportString,report); ResumeComm(); } bool Subsystem::SendSpecialReportContaining(char * specialReport) { if(!acknowledgeReceivedForLastReport){ timeoutCount++; if(timeoutCount==maxTimeoutCount){ #ifdef DEBUG printf("Subsystem: Timeout expired for report acknowledge. ResetConnectionToCC\n"); #endif return false; //SendReport is not succesfull so this will stop Periodic action } } //before writing the report or sending it make sure no other thread thouches it pthread_mutex_lock(&mutex4report); // SubFormatSubsystem::ElaborateReport //calls overriden method GenerateReport that will do the //append the tag for specialReport in this protocol sprintf(this->reportString,"SPECIAL:%s",specialReport); //GenerateReport may have filled timestamp. Record it to checkreportacknowledge in future ExtractTimeStampFromLastReportTo(lastTimeStamp); reportSender.Send(this->reportString); #ifdef DEBUG printf("Subsystem: Sending %s \n",this->reportString); #endif pthread_mutex_unlock(&mutex4report); acknowledgeReceivedForLastReport=false; //????????Order mutexes are lock/unlock???????????? return true; //if it goes here reporting was succesfull (Used in PeriodicAction reportingLoop) } void Subsystem::ClosingChannel() { // #ifdef DEBUG // printf("Subsystem: cancelling ReportingAndChecking\n"); // #endif // pthread_cancel(reportThread); // #ifdef DEBUG // printf("Subsystem: cancelled ReportingAndChecking\n"); // #endif TCPListener::ClosingChannel(); if(locked){ ULock(); } #ifdef DEBUG printf("Subsystem: resetting connection\n"); #endif SetReporting(false); #ifdef DEBUG printf("Subsystem: waiting ReportingAndChecking Thread to finish\n"); #endif pthread_join(reportThread,NULL); #ifdef DEBUG printf("Subsystem: ReportingAndChecking finished\n"); #endif reportSender.CloseConnection(); } void Subsystem::ResetConnectionToCC () { if(locked){ ULock(); } #ifdef DEBUG printf("Subsystem: resetting connection\n"); #endif SetReporting(false); #ifdef DEBUG printf("Subsystem: waiting ReportingAndChecking Thread to finish\n"); #endif pthread_join(reportThread,NULL); #ifdef DEBUG printf("Subsystem: ReportingAndChecking finished\n"); #endif reportSender.CloseConnection(); TCPListener::ClosingChannel(); //but still is waiting for new connections } void Subsystem::process(){ if (!locked){ if(!Reporting()){ if ( !strcmp(TCPListener::receivedStream, "REPOR") ) { #ifdef DEBUG printf("Subsystem: received REPOR\n"); #endif //before startreporting, the connection to cc by reportSender has to be stablished pthread_create(&reportThread,NULL,&Subsystem::pthread_ReportingAndCheckingLoop,this); SetReporting(true); return; } } else { //already reporting if ( !strcmp(TCPListener::receivedStream,"LOCK!") ) { Lock(); return; } } } else { //locked //as preliminary tests instead of commands, the new subsystem state is sent if (! strncmp(TCPListener::receivedStream, "ULOCK", 5) ) { ULock(); return; } #ifdef DEBUG printf("Subsystem: processing ... seting new state: %s \n", TCPListener::receivedStream); #endif //calls overriden method thet will process the command itself ProcessCmd(TCPListener::receivedStream); //state=receivedStream; return; } //if program reaches here is because it hasn't Received REPOR & LOCK in the proper way. Network partner it's not (a properly working) CC. Closing the opened connection and waiting for new one ResetConnectionToCC(); } bool Subsystem::SendReport () { if(!acknowledgeReceivedForLastReport){ timeoutCount++; if(timeoutCount==maxTimeoutCount){ #ifdef DEBUG printf("Subsystem: Timeout expired for report acknowledge. ResetConnectionToCC\n"); #endif return false; //SendReport is not succesfull so this will stop Periodic action } } //before writing the report or sending it make sure no other thread thouches it pthread_mutex_lock(&mutex4report); // SubFormatSubsystem::ElaborateReport //calls overriden method GenerateReport that will do the GenerateReport(); //GenerateReport may have filled timestamp. Record it to checkreportacknowledge in future ExtractTimeStampFromLastReportTo(lastTimeStamp); reportSender.Send(this->reportString); #ifdef DEBUG printf("Subsystem: Sending %s \n",this->reportString); #endif pthread_mutex_unlock(&mutex4report); acknowledgeReceivedForLastReport=false; return true; //if it goes here reporting was succesfull (Used in PeriodicAction reportingLoop) //acknowledge check is done elsewhere, when it arrives (asynchronously) } void Subsystem::CheckReportAcknowledge() { reportSender.Receive(); //incorporate lastTimeStamp in check if (reportSender.ReturnNew() == "RECV@" ) { acknowledgeReceivedForLastReport=true; timeoutCount=0; }else{ cerr<<"wrong ack!\n"; } } void Subsystem::Lock(){ #ifdef DEBUG printf("Subsystem: Locking to CC mode\n"); #endif locked=true; } void Subsystem::ULock(){ #ifdef DEBUG printf("Subsystem: UNLocking to CC mode\n"); #endif locked=false; } void Subsystem::SuspendComm() { pthread_mutex_lock(&mutex4report); }; void Subsystem::ResumeComm() { pthread_mutex_unlock(&mutex4report); }; void Subsystem::ExtractTimeStampFromLastReportTo(char * lastTimeStamp) { string report(this->reportString); string::iterator it=report.begin(); //point it to the first appearence of : it+=report.find(":"); //cut the first field (between :) which is the status report.erase(report.begin(),it); //#define TIMESTAMP_LEN 12 //%02.2d:%02.2d:%02.2d:%03.3d it=report.begin(); //back to the beginning it+=12; //cut from the timestamp end to the rest report.erase(it,report.end()); strcpy(lastTimeStamp,report.c_str()); } void Subsystem::Shutdown() {} void Subsystem::WaitingForShutdown() { sleep(6000); } //TO BE OVERRIDEN: void Subsystem::ProcessCmd(char *) {}; void Subsystem::GenerateReport() {}; void Subsystem::HandleConnectionTimeoutIsOver() {};