#ifndef SUBSYSTEM #define SUBSYSTEM #include "PeriodicAction.hxx" #include "TCPListener.hxx" #include "IONotifier.hxx" #include "PeriodicAction.hxx" #include "TCPSender.hxx" #define TIMESTAMP_LEN 12 //%02.2d:%02.2d:%02.2d:%03.3d //in Makefile#define MAXMSG 4096 /** * Base class for MAGIC Subsystem's communication with CC * * It implements commandListener (see MAGIC-TDAS 00-07) as a TCPListener * derived class, to be able to custom the behaviour by overriding its * methods * * Missing properties: Special Report in a GenerateSpecialReport method, also * a final implementation of CheckReportAcknowledge (CC sends back * "RECV:timestamp", with timestamp the one extracted from subsystem * report. So subsystem has to have a member lastReportTimestamp to compare). * Timeout in CheckReportAcknowledge?????. * * @short Base class for MAGIC Subsystem's communication with CC * @author Marc Casaldaliga * @version 0.9 * @see TCPListener */ class Subsystem: public TCPListener { public: /** * Set the string to be sent in the next report that will be sent to * CC (with timestamp but without protocol specific item, * e.g. trailing \n). The timestamp is the data one, the time when * data was taken (the relevant for analysis), nothing to do with the * the time the report will be sent. * * It takes care internally of suspending and resuming communications * not to send something is being modified etc * * @param report CString with pure report (without "\n" ...) */ void SetReportString(char * report); /** * Class unique constructor with the parameters which specify * Subsystem configuration. * * @param portCommandListener TCP/IP port Subsystem will open to * listen to cc commands * * @param reportPeriod Aproximate (+- 500ms) time in microsec, * between each report is sent to CC * * @param ccName C string with CC machine name * * @param portReportListener TCP/IP port opened in CC which Subsystem * will contact to send reports * * @param maxTimeoutCount_. Times subsystem tries to reconnect CC * (with reportPeriod) periodicity, before it considers CC not * available. After this count is reached Subsystem has to react some * way (parking itself, ...). This is done by * Subsystem::HandleConnectionTimeoutIsOver * * @see #HandleConnectionTimeoutIsOver * */ Subsystem (unsigned short int portCommandListener, unsigned long int reportPeriod, char * ccName, unsigned short int portReportListener, unsigned short int maxTimeoutCount_ ); /** * Send (immediately) a special report which a part from * protocol specific items (e.g. trailing \n and special report * identifier) contains the argument specialReport * * ??? timestamp ???? * * It takes care internally of suspending and resuming communications * not to send something is being modified etc * * @param specialReport CString with pure special report (without * "\n" ...) * * @return Returns whether sending will be succesful (true) or not * (false) */ bool SendSpecialReportContaining(char * specialReport); protected: /** * Field with the report that will be sent to CC (with timestamp but * without protocol specific item, e.g. trailing \n) * * To be modified only within GenerateReport */ char reportString[MAXMSG]; /** To be overriden. It has to do all the job of interpreting CC * commands: (not the ones * regarding starting connection, locking ...) but the ones for the * subsystem function (This way we separate the protocol dependent * stuff). * * Compares Subsystem::ccCmd with known commands from cc and executes * the appropiate function * * This method is automatically called when a command from cc is * received, after checking it is not a communication/locking command * * If receiving a nonsense command call to ResetConnectionToCC() * (Network partner it's not -a properly working- CC). In principle * this will close and open again, but let's leave it this way so that * it is not protocol dependent * * @param ccCmd CString with pure CC command (without "\n" ...) */ virtual void ProcessCmd(char *ccCmd); /** GenerateReport (to be overriden) This next method is automatically * called before sending a report. * * According to state and data this should write * Subsystem::reportString (it * should include the timestamp of the data sent, but without any * trailing \n--this is part of the protocol) * * After it, this Subsystem::reportString plus any protocol dependent * thing is sent. This GenerateReport and send is periodically * repeated with reportPeriod * * Alternatively, one may leave GenerateReport empty and from outside * call to Subsystem::SetReportString when hardware info is updated * (probably what one what like to do). */ virtual void GenerateReport(); /** HandleConnectionTimeoutIsOver. Does what the subsystem is suposed * to do when it has lost definitely connection to CC. To be * overriden. * * After Subsystem::maxTimeoutCount times of reconnection tries CC is * considered not available. Subsystem has to react some way (parking * itself, ... ) depending on its autonomy. This is done by * overriding this method with the desired behaviour. * * After HandleConnectionTimeoutIsOver is done Subsystem will listen * to CC again and start all communication process. * * see #maxTimeoutCount */ virtual void HandleConnectionTimeoutIsOver(); /** SuspendComm. Suspend communication threads * * To prevent Subsystem to access its resources (e.g. send * reportString) when we are about to access them externally (fill * reportString with actual data), one can call to this method and * suspend subsystem activity. * To resume it call to Subsystem::ResumeComm. * * Always paired with a ResumeComm. * * If Subsystem is at that time accessing the resources, SuspendComm * will wait for them to be released and then will lock them. * * @see #ResumeComm * */ void SuspendComm(); /** ResumeComm. Resume communication threads * * After calling SuspendComm() and having access to Subsystem * resources (e.g. fill reportString with actual data) allow * Subsystem to access them again and resume it's activity. we are * about to access them externally (fill reportString with actual * data), one can call to this method and suspend subsystem activity. * * @see #SuspendComm * */ void ResumeComm(); /** * Resets communication to CC, to be used when a nonsense command * arrives from CC * @see #ProcessCmd */ void ResetConnectionToCC(); private: /** * C string with CC machine name */ char* ccName; /** * Aproximate (+- 500ms) time in microsec, between each report is sent * to CC */ unsigned long reportPeriod; unsigned short int portCommandListener; unsigned short int portReportListener; //we implement commandListener as a TCPListener derived class, to be //able to custom the behaviour by overriding it's methods, namely //process /* maxTimeoutCount. Times subsystem tries to reconnect CC (with reportPeriod) * periodicity, before it considers CC not available. * * After this count is reached Subsystem has to react some way * (parking itself, ...). This is done by * Subsystem::HandleConnectionTimeoutIsOver * * @see #HandleConnectionTimeoutIsOver * */ short int maxTimeoutCount; bool locked; bool reporting; IONotifier* reportAckNotifier; //we implement reportSender as an instance of class TCPSender TCPSender reportSender; //reporting and trying connections to cc are implemented as //PeriodicActions PeriodicAction tryReportConn,reportingLoop; pthread_mutex_t mutex4report; inline void StartReporting(); virtual void process(); bool SendReport(); void CheckReportAcknowledge(); //to be able to checkreportacknowledge on has to have the timestamp of //the last report sent char lastTimeStamp[TIMESTAMP_LEN]; void ExtractTimeStampFromLastReportTo(char *); bool acknowledgeReceivedForLastReport; short int timeoutCount; pthread_mutex_t mutex4ack; void Lock(); void ULock(); }; #endif