source: trunk/MagicSoft/Control/SubsystemIO/Subsystem.plain.hxx@ 6958

Last change on this file since 6958 was 1113, checked in by casaldaliga, 23 years ago
*** empty log message ***
File size: 15.2 KB
Line 
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 */
29class Subsystem: public TCPListener
30{
31
32public:
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
118protected:
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
230private:
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
Note: See TracBrowser for help on using the repository browser.