| 1 | #ifndef SUBSYSTEM
|
|---|
| 2 | #define SUBSYSTEM
|
|---|
| 3 |
|
|---|
| 4 | #include "PeriodicAction.hxx"
|
|---|
| 5 | #include "TCPListener.hxx"
|
|---|
| 6 | #include "PeriodicAction.hxx"
|
|---|
| 7 | #include "TCPSender.hxx"
|
|---|
| 8 | #define TIMESTAMP_LEN 12
|
|---|
| 9 | //%02.2d:%02.2d:%02.2d:%03.3d
|
|---|
| 10 | //in Makefile#define MAXMSG 4096
|
|---|
| 11 | /**
|
|---|
| 12 | * Base class for MAGIC Subsystem's communication with CC
|
|---|
| 13 | *
|
|---|
| 14 | * It implements commandListener (see MAGIC-TDAS 00-07) as a TCPListener
|
|---|
| 15 | * derived class, to be able to custom the behaviour by overriding its
|
|---|
| 16 | * methods
|
|---|
| 17 | *
|
|---|
| 18 | * Missing properties: Special Report in a GenerateSpecialReport method, also
|
|---|
| 19 | * a final implementation of CheckReportAcknowledge (CC sends back
|
|---|
| 20 | * "RECV:timestamp", with timestamp the one extracted from subsystem
|
|---|
| 21 | * report. So subsystem has to have a member lastReportTimestamp to compare).
|
|---|
| 22 | * Timeout in CheckReportAcknowledge?????.
|
|---|
| 23 | *
|
|---|
| 24 | * @short Base class for MAGIC Subsystem's communication with CC
|
|---|
| 25 | * @author Marc Casaldaliga
|
|---|
| 26 | * @version 0.9
|
|---|
| 27 | * @see TCPListener
|
|---|
| 28 | */
|
|---|
| 29 | class Subsystem: public TCPListener
|
|---|
| 30 | {
|
|---|
| 31 |
|
|---|
| 32 | public:
|
|---|
| 33 |
|
|---|
| 34 | /**
|
|---|
| 35 | * Set the string to be sent in the next report that will be sent to
|
|---|
| 36 | * CC (with timestamp but without protocol specific item,
|
|---|
| 37 | * e.g. trailing \n). The timestamp is the data one, the time when
|
|---|
| 38 | * data was taken (the relevant for analysis), nothing to do with the
|
|---|
| 39 | * the time the report will be sent.
|
|---|
| 40 | *
|
|---|
| 41 | * It takes care internally of suspending and resuming communications
|
|---|
| 42 | * not to send something is being modified etc
|
|---|
| 43 | *
|
|---|
| 44 | * @param report CString with pure report (without "\n" ...)
|
|---|
| 45 | */
|
|---|
| 46 | void SetReportString(char * report);
|
|---|
| 47 |
|
|---|
| 48 | /**
|
|---|
| 49 | * Class unique constructor with the parameters which specify
|
|---|
| 50 | * Subsystem configuration.
|
|---|
| 51 | *
|
|---|
| 52 | * @param portCommandListener TCP/IP port Subsystem will open to
|
|---|
| 53 | * listen to cc commands
|
|---|
| 54 | *
|
|---|
| 55 | * @param reportPeriod Aproximate (+- 500ms) time in microsec,
|
|---|
| 56 | * between each report is sent to CC
|
|---|
| 57 | *
|
|---|
| 58 | * @param ccName C string with CC machine name
|
|---|
| 59 | *
|
|---|
| 60 | * @param portReportListener TCP/IP port opened in CC which Subsystem
|
|---|
| 61 | * will contact to send reports
|
|---|
| 62 | *
|
|---|
| 63 | * @param maxTimeoutCount_. Times subsystem tries to reconnect CC
|
|---|
| 64 | * (with reportPeriod) periodicity, before it considers CC not
|
|---|
| 65 | * available. After this count is reached Subsystem has to react some
|
|---|
| 66 | * way (parking itself, ...). This is done by
|
|---|
| 67 | * Subsystem::HandleConnectionTimeoutIsOver
|
|---|
| 68 | *
|
|---|
| 69 | * @param specialReportOnStartup When connection with CC is
|
|---|
| 70 | * stablished for first time after a Subsystem startup a special
|
|---|
| 71 | * report to CC with subsystem setup must be sent to ensure any setup
|
|---|
| 72 | * changes (which may have happened while CC was down) are
|
|---|
| 73 | * recorded. This C string only contains report content without any
|
|---|
| 74 | * protocol dependent character
|
|---|
| 75 | *
|
|---|
| 76 | * @see #HandleConnectionTimeoutIsOver
|
|---|
| 77 | *
|
|---|
| 78 | */
|
|---|
| 79 | Subsystem (unsigned short int portCommandListener, unsigned long int
|
|---|
| 80 | reportPeriod, char * ccName, unsigned short int
|
|---|
| 81 | portReportListener, unsigned short int maxTimeoutCount_, char *
|
|---|
| 82 | specialReportOnStartup );
|
|---|
| 83 |
|
|---|
| 84 | /**
|
|---|
| 85 | * Send (immediately) a special report which a part from
|
|---|
| 86 | * protocol specific items (e.g. trailing \n and special report
|
|---|
| 87 | * identifier) contains the argument specialReport
|
|---|
| 88 | *
|
|---|
| 89 | * ??? timestamp ????
|
|---|
| 90 | *
|
|---|
| 91 | * It takes care internally of suspending and resuming communications
|
|---|
| 92 | * not to send something is being modified etc
|
|---|
| 93 | *
|
|---|
| 94 | * @param specialReport CString with pure special report (without
|
|---|
| 95 | * "\n" ...)
|
|---|
| 96 | *
|
|---|
| 97 | * @return Returns whether sending will be succesful (true) or not
|
|---|
| 98 | * (false)
|
|---|
| 99 | */
|
|---|
| 100 | bool SendSpecialReportContaining(char * specialReport);
|
|---|
| 101 |
|
|---|
| 102 | /**
|
|---|
| 103 | * Blocks main thread until Subsystem::Shutdown is called
|
|---|
| 104 | */
|
|---|
| 105 | void WaitingForShutdown();
|
|---|
| 106 |
|
|---|
| 107 | /**
|
|---|
| 108 | * Subsystem communications will be shutdown definitely. Call this
|
|---|
| 109 | * inside ProcessCmd or HandleConnectionTimeoutIsOver when your
|
|---|
| 110 | * subsystem has to be stopped. If you only want to go standalone but
|
|---|
| 111 | * still in contact with CC, you have to Ulock it but not this.
|
|---|
| 112 | *
|
|---|
| 113 | * This call unblocks WaitingForShutdown in main, so your subsystem
|
|---|
| 114 | * application can finish.
|
|---|
| 115 | */
|
|---|
| 116 | void Shutdown();
|
|---|
| 117 |
|
|---|
| 118 | protected:
|
|---|
| 119 | /**
|
|---|
| 120 | * Field with the report that will be sent to CC (with timestamp but
|
|---|
| 121 | * without protocol specific item, e.g. trailing \n)
|
|---|
| 122 | *
|
|---|
| 123 | * To be modified only within GenerateReport
|
|---|
| 124 | */
|
|---|
| 125 | char reportString[MAXMSG];
|
|---|
| 126 |
|
|---|
| 127 | /** To be overriden. It has to do all the job of interpreting CC
|
|---|
| 128 | * commands: (not the ones
|
|---|
| 129 | * regarding starting connection, locking ...) but the ones for the
|
|---|
| 130 | * subsystem function (This way we separate the protocol dependent
|
|---|
| 131 | * stuff).
|
|---|
| 132 | *
|
|---|
| 133 | * Compares Subsystem::ccCmd with known commands from cc and executes
|
|---|
| 134 | * the appropiate function
|
|---|
| 135 | *
|
|---|
| 136 | * This method is automatically called when a command from cc is
|
|---|
| 137 | * received, after checking it is not a communication/locking command
|
|---|
| 138 | *
|
|---|
| 139 | * If receiving a nonsense command call to ResetConnectionToCC()
|
|---|
| 140 | * (Network partner it's not -a properly working- CC). In principle
|
|---|
| 141 | * this will close and open again, but let's leave it this way so that
|
|---|
| 142 | * it is not protocol dependent
|
|---|
| 143 | *
|
|---|
| 144 | * @param ccCmd CString with pure CC command (without "\n" ...)
|
|---|
| 145 | */
|
|---|
| 146 | virtual void ProcessCmd(char *ccCmd);
|
|---|
| 147 |
|
|---|
| 148 | /** GenerateReport (to be overriden) This next method is automatically
|
|---|
| 149 | * called before sending a report.
|
|---|
| 150 | *
|
|---|
| 151 | * According to state and data this should write
|
|---|
| 152 | * Subsystem::reportString (it
|
|---|
| 153 | * should include the timestamp of the data sent, but without any
|
|---|
| 154 | * trailing \n--this is part of the protocol)
|
|---|
| 155 | *
|
|---|
| 156 | * After it, this Subsystem::reportString plus any protocol dependent
|
|---|
| 157 | * thing is sent. This GenerateReport and send is periodically
|
|---|
| 158 | * repeated with reportPeriod
|
|---|
| 159 | *
|
|---|
| 160 | * Alternatively, one may leave GenerateReport empty and from outside
|
|---|
| 161 | * call to Subsystem::SetReportString when hardware info is updated
|
|---|
| 162 | * (probably what one what like to do).
|
|---|
| 163 | */
|
|---|
| 164 | virtual void GenerateReport();
|
|---|
| 165 |
|
|---|
| 166 | /** HandleConnectionTimeoutIsOver. Does what the subsystem is suposed
|
|---|
| 167 | * to do when it has lost definitely connection to CC. To be
|
|---|
| 168 | * overriden.
|
|---|
| 169 | *
|
|---|
| 170 | * After Subsystem::maxTimeoutCount times of reconnection tries CC is
|
|---|
| 171 | * considered not available. Subsystem has to react some way (parking
|
|---|
| 172 | * itself, ... ) depending on its autonomy. This is done by
|
|---|
| 173 | * overriding this method with the desired behaviour.
|
|---|
| 174 | *
|
|---|
| 175 | * After HandleConnectionTimeoutIsOver is done Subsystem will listen
|
|---|
| 176 | * to CC again and start all communication process.
|
|---|
| 177 | *
|
|---|
| 178 | * see #maxTimeoutCount
|
|---|
| 179 | */
|
|---|
| 180 | virtual void HandleConnectionTimeoutIsOver();
|
|---|
| 181 |
|
|---|
| 182 | /** SuspendComm. Suspend communication threads
|
|---|
| 183 | *
|
|---|
| 184 | * To prevent Subsystem to access its resources (e.g. send
|
|---|
| 185 | * reportString) when we are about to access them externally (fill
|
|---|
| 186 | * reportString with actual data), one can call to this method and
|
|---|
| 187 | * suspend subsystem activity.
|
|---|
| 188 | * To resume it call to Subsystem::ResumeComm.
|
|---|
| 189 | *
|
|---|
| 190 | * Always paired with a ResumeComm.
|
|---|
| 191 | *
|
|---|
| 192 | * If Subsystem is at that time accessing the resources, SuspendComm
|
|---|
| 193 | * will wait for them to be released and then will lock them.
|
|---|
| 194 | *
|
|---|
| 195 | * @see #ResumeComm
|
|---|
| 196 | *
|
|---|
| 197 | */
|
|---|
| 198 | void SuspendComm();
|
|---|
| 199 |
|
|---|
| 200 | /** ResumeComm. Resume communication threads
|
|---|
| 201 | *
|
|---|
| 202 | * After calling SuspendComm() and having access to Subsystem
|
|---|
| 203 | * resources (e.g. fill reportString with actual data) allow
|
|---|
| 204 | * Subsystem to access them again and resume it's activity. we are
|
|---|
| 205 | * about to access them externally (fill reportString with actual
|
|---|
| 206 | * data), one can call to this method and suspend subsystem activity.
|
|---|
| 207 | *
|
|---|
| 208 | * @see #SuspendComm
|
|---|
| 209 | *
|
|---|
| 210 | */
|
|---|
| 211 | void ResumeComm();
|
|---|
| 212 |
|
|---|
| 213 | /**
|
|---|
| 214 | * Resets communication to CC, to be used when a nonsense command
|
|---|
| 215 | * arrives from CC
|
|---|
| 216 | * @see #ProcessCmd
|
|---|
| 217 | */
|
|---|
| 218 | void ResetConnectionToCC();
|
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 | /////////////////////////////////////////////////////////////////////////
|
|---|
| 222 | //////////////// IMPLEMENTATION DETAILS /////////////////////////////////
|
|---|
| 223 | /////////////////////////////////////////////////////////////////////////
|
|---|
| 224 |
|
|---|
| 225 | //we implement commandListener as a TCPListener derived class, to be
|
|---|
| 226 | //able to custom the behaviour by overriding it's methods, namely
|
|---|
| 227 | //process, which is called when something arrives.
|
|---|
| 228 | //This could be though as inheritance for implementation, which is not
|
|---|
| 229 | //desired. Can be avoided by using signals/slots
|
|---|
| 230 | private:
|
|---|
| 231 | /**
|
|---|
| 232 | * C string with CC machine name
|
|---|
| 233 | */
|
|---|
| 234 | char* ccName;
|
|---|
| 235 |
|
|---|
| 236 | /**
|
|---|
| 237 | * Aproximate (+- 500ms) time in microsec, between each report is sent
|
|---|
| 238 | * to CC.
|
|---|
| 239 | * It is expressed in microsec, despite the big uncertanty in these
|
|---|
| 240 | * units, because is feed directly to usleep. This should be changed at
|
|---|
| 241 | * least to ms: remember then to multiply per 1000 in usleep. Take care
|
|---|
| 242 | * also with long periods: usleep will accept only long int
|
|---|
| 243 | */
|
|---|
| 244 | unsigned long int reportPeriod;
|
|---|
| 245 |
|
|---|
| 246 | /**
|
|---|
| 247 | * TCP/IP port for commandListener socket, here at subsystem machine
|
|---|
| 248 | */
|
|---|
| 249 | unsigned short int portCommandListener;
|
|---|
| 250 |
|
|---|
| 251 | /**
|
|---|
| 252 | * TCP/IP port for reportListener socket in CC machine
|
|---|
| 253 | */
|
|---|
| 254 | unsigned short int portReportListener;
|
|---|
| 255 |
|
|---|
| 256 |
|
|---|
| 257 | /* maxTimeoutCount. Times subsystem tries to reconnect CC (with reportPeriod)
|
|---|
| 258 | * periodicity, before it considers CC not available.
|
|---|
| 259 | *
|
|---|
| 260 | * After this count is reached Subsystem has to react some way
|
|---|
| 261 | * (parking itself, ...). This is done by
|
|---|
| 262 | * Subsystem::HandleConnectionTimeoutIsOver
|
|---|
| 263 | *
|
|---|
| 264 | * @see #HandleConnectionTimeoutIsOver
|
|---|
| 265 | *
|
|---|
| 266 | */
|
|---|
| 267 |
|
|---|
| 268 | short int maxTimeoutCount;
|
|---|
| 269 |
|
|---|
| 270 | /**
|
|---|
| 271 | * If locked == true Subsystem is being commanded by CC.
|
|---|
| 272 | */
|
|---|
| 273 | bool locked;
|
|---|
| 274 |
|
|---|
| 275 | /**
|
|---|
| 276 | * When REPOR received, reporting = true, and thread
|
|---|
| 277 | * ReportingAndCheckingLoop is started
|
|---|
| 278 | */
|
|---|
| 279 | bool reporting;
|
|---|
| 280 | /**
|
|---|
| 281 | * mutex used to prevent that while reporting state is being checked
|
|---|
| 282 | * in ReportingAndCheckingLoop thread, connection closing is detected
|
|---|
| 283 | * in TCPListener thread and reporting is set at the same time.
|
|---|
| 284 | *
|
|---|
| 285 | * I'm not sure if really needed
|
|---|
| 286 | */
|
|---|
| 287 | pthread_mutex_t mutex4reporting;
|
|---|
| 288 | /**
|
|---|
| 289 | * Accessor function for reporting bool variable that takes care of
|
|---|
| 290 | * properly locking the resource
|
|---|
| 291 | */
|
|---|
| 292 | inline bool Reporting(){
|
|---|
| 293 | pthread_mutex_lock(&mutex4reporting);
|
|---|
| 294 | bool ret=reporting;
|
|---|
| 295 | pthread_mutex_unlock(&mutex4reporting);
|
|---|
| 296 | return ret;
|
|---|
| 297 | }
|
|---|
| 298 |
|
|---|
| 299 | /**
|
|---|
| 300 | * Accessor function for reporting bool variable that takes care of
|
|---|
| 301 | * properly locking the resource
|
|---|
| 302 | */
|
|---|
| 303 | inline void SetReporting(bool trueValue)
|
|---|
| 304 | {
|
|---|
| 305 | pthread_mutex_lock(&mutex4reporting);
|
|---|
| 306 | reporting=trueValue;
|
|---|
| 307 | pthread_mutex_unlock(&mutex4reporting);
|
|---|
| 308 | }
|
|---|
| 309 |
|
|---|
| 310 | /**
|
|---|
| 311 | * We implement reportSender as an instance of class TCPSender
|
|---|
| 312 | */
|
|---|
| 313 | TCPSender reportSender;
|
|---|
| 314 |
|
|---|
| 315 | /**
|
|---|
| 316 | * mutex used to prevent that while report is being send
|
|---|
| 317 | * in ReportingAndCheckingLoop thread, it is changed at the same time
|
|---|
| 318 | * in the main thread
|
|---|
| 319 | *
|
|---|
| 320 | * This mutex is for sure crucial
|
|---|
| 321 | *
|
|---|
| 322 | * SuspendComm and ResumeComm lock/unlock it to achieve this purpose
|
|---|
| 323 | */
|
|---|
| 324 | pthread_mutex_t mutex4report;
|
|---|
| 325 |
|
|---|
| 326 | /**
|
|---|
| 327 | * Method that we override from TCPListener and gets called when some
|
|---|
| 328 | * command arrives. Here is where all the LOCK, REPOR protocol is
|
|---|
| 329 | * checked and where ReportingAndCheckingLoop thread is started and
|
|---|
| 330 | * stopped
|
|---|
| 331 | */
|
|---|
| 332 | virtual void process();
|
|---|
| 333 |
|
|---|
| 334 | /**
|
|---|
| 335 | * Method that we also override from TCPListener and mainly gets
|
|---|
| 336 | * called when endofline is received (connection closed by the other
|
|---|
| 337 | * partner); we don't call it explicitely when the protocol is
|
|---|
| 338 | * not followed (this is done in ResetConnectionToCC). As in
|
|---|
| 339 | * ResetConnectionToCC we properly
|
|---|
| 340 | * close reportSender, and stop ReportingAndCheckingLoop
|
|---|
| 341 | */
|
|---|
| 342 | virtual void ClosingChannel();
|
|---|
| 343 |
|
|---|
| 344 | /**
|
|---|
| 345 | * Method that actually sends the report via reportSender and returns
|
|---|
| 346 | * if it was successful (to be used inside a PeriodicAction or
|
|---|
| 347 | * whatever).
|
|---|
| 348 | * It will also fail if we didn't receive an acknowledge for
|
|---|
| 349 | * last report several (timeoutCount) times, as asynchronously checked
|
|---|
| 350 | * in method CheckReportAcknowledge.
|
|---|
| 351 | * Before actually sending this->reportString it calls the
|
|---|
| 352 | * GenerateReport method that some real subsystems (as Subsystem
|
|---|
| 353 | * derived classes) will override to fill this string. It also calls
|
|---|
| 354 | * ExtractTimeStampFromLastReportTo to be able to check the timestamp
|
|---|
| 355 | * later (in CheckReportAcknowledge) when the acknowledge arrives.
|
|---|
| 356 | */
|
|---|
| 357 | bool SendReport();
|
|---|
| 358 |
|
|---|
| 359 | /**
|
|---|
| 360 | * Does the actual check of the report acknowledge (when it arrives)
|
|---|
| 361 | * and puts acknowledgeReceivedForLastReport to true; for synchronizing
|
|---|
| 362 | * the threads its better it does
|
|---|
| 363 | * not do anything else like resetting connection itself. If the
|
|---|
| 364 | * acknowledge doesn't arrive acknowledgeReceivedForLastReport will
|
|---|
| 365 | * remain false. SendReport will know and increase the timeoutCount.
|
|---|
| 366 | */
|
|---|
| 367 | void CheckReportAcknowledge();
|
|---|
| 368 |
|
|---|
| 369 | /**
|
|---|
| 370 | * to be able to checkreportacknowledge on has to have the timestamp of
|
|---|
| 371 | * the last report sent
|
|---|
| 372 | */
|
|---|
| 373 | char lastTimeStamp[TIMESTAMP_LEN];
|
|---|
| 374 |
|
|---|
| 375 | /**
|
|---|
| 376 | * Extracts this timestamp to its parameter lastTimeStamp
|
|---|
| 377 | */
|
|---|
| 378 | void ExtractTimeStampFromLastReportTo(char * lastTimeStamp);
|
|---|
| 379 |
|
|---|
| 380 | /**
|
|---|
| 381 | * the thread handle itself for ReportingAndCheckingLoop
|
|---|
| 382 | */
|
|---|
| 383 | pthread_t reportThread;
|
|---|
| 384 |
|
|---|
| 385 | /**
|
|---|
| 386 | * make up for pthread use. Allows pthread to effectively start a
|
|---|
| 387 | * non-static function, which is very convenient to avoid the use of
|
|---|
| 388 | * self
|
|---|
| 389 | */
|
|---|
| 390 | inline static void * pthread_ReportingAndCheckingLoop(void* self)
|
|---|
| 391 | {
|
|---|
| 392 | return (void *) ( ((Subsystem* )self)->ReportingAndCheckingLoop() );
|
|---|
| 393 | }
|
|---|
| 394 |
|
|---|
| 395 | /**
|
|---|
| 396 | * The actual method started in reportThread
|
|---|
| 397 | */
|
|---|
| 398 | void * ReportingAndCheckingLoop();
|
|---|
| 399 |
|
|---|
| 400 | bool acknowledgeReceivedForLastReport;
|
|---|
| 401 | short int timeoutCount;
|
|---|
| 402 | void Lock();
|
|---|
| 403 | void ULock();
|
|---|
| 404 |
|
|---|
| 405 | };
|
|---|
| 406 |
|
|---|
| 407 | #endif
|
|---|