Line data Source code
1 : /********************************************************************\
2 :
3 : Name: tmfe.h
4 : Created by: Konstantin Olchanski - TRIUMF
5 :
6 : Contents: C++ MIDAS frontend
7 :
8 : \********************************************************************/
9 :
10 : #ifndef TMFE_H
11 : #define TMFE_H
12 :
13 : #include <stdint.h>
14 : #include <string>
15 : #include <vector>
16 : #include <mutex> // std::mutex
17 : #include <thread> // std::thread
18 : #include <atomic> // std::atomic
19 : //#include "midas.h"
20 : #include "mvodb.h"
21 :
22 : // from midas.h
23 :
24 : #define TID_BYTE 1 /**< DEPRECATED, use TID_UINT8 instead */
25 : #define TID_UINT8 1 /**< unsigned byte 0 255 */
26 : #define TID_SBYTE 2 /**< DEPRECATED, use TID_INT8 instead */
27 : #define TID_INT8 2 /**< signed byte -128 127 */
28 : #define TID_CHAR 3 /**< single character 0 255 */
29 : #define TID_WORD 4 /**< DEPRECATED, use TID_UINT16 instead */
30 : #define TID_UINT16 4 /**< two bytes 0 65535 */
31 : #define TID_SHORT 5 /**< DEPRECATED, use TID_INT16 instead */
32 : #define TID_INT16 5 /**< signed word -32768 32767 */
33 : #define TID_DWORD 6 /**< DEPRECATED, use TID_UINT32 instead */
34 : #define TID_UINT32 6 /**< four bytes 0 2^32-1 */
35 : #define TID_INT 7 /**< DEPRECATED, use TID_INT32 instead */
36 : #define TID_INT32 7 /**< signed dword -2^31 2^31-1 */
37 : #define TID_BOOL 8 /**< four bytes bool 0 1 */
38 : #define TID_FLOAT 9 /**< 4 Byte float format */
39 : #define TID_FLOAT32 9 /**< 4 Byte float format */
40 : #define TID_DOUBLE 10 /**< 8 Byte float format */
41 : #define TID_FLOAT64 10 /**< 8 Byte float format */
42 : #define TID_BITFIELD 11 /**< 32 Bits Bitfield 0 111... (32) */
43 : #define TID_STRING 12 /**< zero terminated string */
44 : #define TID_ARRAY 13 /**< array with unknown contents */
45 : #define TID_STRUCT 14 /**< structure with fixed length */
46 : #define TID_KEY 15 /**< key in online database */
47 : #define TID_LINK 16 /**< link in online database */
48 : #define TID_INT64 17 /**< 8 bytes int -2^63 2^63-1 */
49 : #define TID_UINT64 18 /**< 8 bytes unsigned int 0 2^64-1 */
50 : #define TID_QWORD 18 /**< 8 bytes unsigned int 0 2^64-1 */
51 : #define TID_LAST 19 /**< end of TID list indicator */
52 :
53 : /**
54 : System message types */
55 : #define MT_ERROR (1<<0) /**< - */
56 : #define MT_INFO (1<<1) /**< - */
57 : #define MT_DEBUG (1<<2) /**< - */
58 : #define MT_USER (1<<3) /**< - */
59 : #define MT_LOG (1<<4) /**< - */
60 : #define MT_TALK (1<<5) /**< - */
61 : #define MT_CALL (1<<6) /**< - */
62 : #define MT_ALL 0xFF /**< - */
63 :
64 : #define MT_ERROR_STR "ERROR"
65 : #define MT_INFO_STR "INFO"
66 : #define MT_DEBUG_STR "DEBUG"
67 : #define MT_USER_STR "USER"
68 : #define MT_LOG_STR "LOG"
69 : #define MT_TALK_STR "TALK"
70 : #define MT_CALL_STR "CALL"
71 :
72 : #define MERROR MT_ERROR, __FILE__, __LINE__ /**< - */
73 : #define MINFO MT_INFO, __FILE__, __LINE__ /**< - */
74 : #define MDEBUG MT_DEBUG, __FILE__, __LINE__ /**< - */
75 : #define MUSER MT_USER, __FILE__, __LINE__ /**< produced by interactive user */
76 : #define MLOG MT_LOG, __FILE__, __LINE__ /**< info message which is only logged */
77 : #define MTALK MT_TALK, __FILE__, __LINE__ /**< info message for speech system */
78 : #define MCALL MT_CALL, __FILE__, __LINE__ /**< info message for telephone call */
79 :
80 : #if defined __GNUC__
81 : #define MATTRPRINTF(a, b) __attribute__ ((format (printf, a, b)))
82 : #else
83 : #define MATTRPRINTF(a, b)
84 : #endif
85 :
86 0 : class TMFeResult
87 : {
88 : public:
89 : bool error_flag = false;
90 : int error_code = 0;
91 : std::string error_message = "success";
92 :
93 : public:
94 6 : TMFeResult() { // default ctor for success
95 3 : }
96 :
97 0 : TMFeResult(int code, const std::string& str) { // ctor
98 0 : error_flag = true;
99 0 : error_code = code;
100 0 : error_message = str;
101 0 : }
102 : };
103 :
104 : // special TMFeResult constructors
105 :
106 2 : inline TMFeResult TMFeOk() { return TMFeResult(); }
107 : TMFeResult TMFeErrorMessage(const std::string& message);
108 : TMFeResult TMFeMidasError(const std::string& message, const char* midas_function_name, int midas_status);
109 :
110 : class TMFE;
111 : class TMFrontend;
112 : class TMFrontendRpcHelper;
113 : class MVOdb;
114 :
115 : class TMEventBuffer
116 : {
117 : public:
118 : TMFE* fMfe = NULL;
119 : std::string fBufName;
120 : size_t fBufSize = 0; // buffer size
121 : size_t fBufMaxEventSize = 0; // buffer max event size
122 :
123 : public:
124 : TMEventBuffer(TMFE* mfe); // ctor
125 : ~TMEventBuffer(); // dtor
126 : TMFeResult OpenBuffer(const char* bufname, size_t bufsize = 0);
127 : TMFeResult CloseBuffer();
128 : TMFeResult SetCacheSize(size_t read_cache_size, size_t write_cache_size);
129 : TMFeResult AddRequest(int event_id, int trigger_mask, const char* sampling_type_string);
130 : TMFeResult ReceiveEvent(std::vector<char> *e, int timeout_msec = 0); // thread-safe
131 : TMFeResult SendEvent(const char *e);
132 : TMFeResult SendEvent(const std::vector<char>& e);
133 : TMFeResult SendEvent(const std::vector<std::vector<char>>& e);
134 : TMFeResult SendEvent(int sg_n, const char* const sg_ptr[], const size_t sg_len[]);
135 : TMFeResult FlushCache(bool wait = true);
136 :
137 : public: // internal state, user can read but should not write these variables
138 : int fBufHandle = 0; // bm_open_buffer() handle
139 : size_t fBufReadCacheSize = 0;
140 : size_t fBufWriteCacheSize = 0;
141 : std::vector<int> fBufRequests;
142 : };
143 :
144 0 : class TMFeRpcHandlerInterface
145 : {
146 : public:
147 0 : virtual TMFeResult HandleBeginRun(int run_number) { return TMFeOk(); };
148 0 : virtual TMFeResult HandleEndRun(int run_number) { return TMFeOk(); };
149 0 : virtual TMFeResult HandlePauseRun(int run_number) { return TMFeOk(); };
150 0 : virtual TMFeResult HandleResumeRun(int run_number) { return TMFeOk(); };
151 0 : virtual TMFeResult HandleStartAbortRun(int run_number) { return TMFeOk(); };
152 0 : virtual TMFeResult HandleRpc(const char* cmd, const char* args, std::string& result) { return TMFeOk(); };
153 0 : virtual TMFeResult HandleBinaryRpc(const char* cmd, const char* args, std::vector<char>& result) { return TMFeOk(); };
154 : };
155 :
156 : class TMFeEquipment : public TMFeRpcHandlerInterface
157 : {
158 : public: // general configuration, should not be changed by user
159 : std::string fEqName;
160 : std::string fEqFilename;
161 :
162 : public: // equipment configuration stored in ODB Common
163 :
164 : bool fEqConfEnableRpc = true;
165 : bool fEqConfEnablePeriodic = true;
166 : bool fEqConfEnablePoll = false;
167 :
168 : bool fEqConfReadConfigFromOdb = true; // read equipment common from ODB
169 :
170 : bool fEqConfEnabled = true;
171 : uint16_t fEqConfEventID = 1;
172 : uint16_t fEqConfTriggerMask = 0;
173 : std::string fEqConfBuffer = "SYSTEM";
174 : int fEqConfType = 0; // not used
175 : int fEqConfSource = 0; // not used
176 : std::string fEqConfFormat = "MIDAS"; // TBI
177 : int fEqConfReadOn = 0;
178 : int fEqConfPeriodMilliSec = 1000;
179 : double fEqConfEventLimit = 0;
180 : uint32_t fEqConfNumSubEvents = 0; // not used
181 : int fEqConfLogHistory = 0;
182 : bool fEqConfHidden = false;
183 : #ifdef MIN_WRITE_CACHE_SIZE
184 : int fEqConfWriteCacheSize = MIN_WRITE_CACHE_SIZE;
185 : #else
186 : int fEqConfWriteCacheSize = 10000000;
187 : #endif
188 : //std::string FrontendHost;
189 : //std::string FrontendName;
190 : //std::string FrontendFileName;
191 : //std::string Status;
192 : //std::string StatusColor;
193 :
194 : public: // equipment configuration not in ODB Common
195 :
196 : bool fEqConfReadOnlyWhenRunning = true; // RO_RUNNING
197 : bool fEqConfWriteEventsToOdb = false; // RO_ODB
198 : double fEqConfPeriodStatisticsSec = 1.0; // period for updating ODB statistics
199 : double fEqConfPollSleepSec = 0.000100; // shortest sleep for linux is 50-6-70 microseconds
200 : size_t fEqConfMaxEventSize = 0; // requested maximum event size
201 : size_t fEqConfBufferSize = 0; // requested event buffer size
202 :
203 : public: // multithread lock
204 : std::mutex fEqMutex;
205 :
206 : public: // connection to MIDAS
207 : TMFE* fMfe = NULL;
208 : TMFrontend* fFe = NULL;
209 :
210 : public: // connection to ODB
211 : MVOdb* fOdbEq = NULL; ///< ODB Equipment/EQNAME
212 : MVOdb* fOdbEqCommon = NULL; ///< ODB Equipment/EQNAME/Common
213 : MVOdb* fOdbEqSettings = NULL; ///< ODB Equipment/EQNAME/Settings
214 : MVOdb* fOdbEqVariables = NULL; ///< ODB Equipment/EQNAME/Variables
215 : MVOdb* fOdbEqStatistics = NULL; ///< ODB Equipment/EQNAME/Statistics
216 :
217 : public: // connection to event buffer
218 : TMEventBuffer* fEqEventBuffer = NULL; // pointer to buffer entry inside TMFE
219 : int fEqSerial = 0;
220 :
221 : public: // statistics
222 : double fEqStatEvents = 0;
223 : double fEqStatBytes = 0;
224 : double fEqStatEpS = 0; // events/sec
225 : double fEqStatKBpS = 0; // kbytes/sec (factor 1000, not 1024)
226 :
227 : // statistics rate computations
228 : double fEqStatLastTime = 0;
229 : double fEqStatLastEvents = 0;
230 : double fEqStatLastBytes = 0;
231 :
232 : // statistics write to odb timer
233 : double fEqStatLastWrite = 0;
234 : double fEqStatNextWrite = 0;
235 :
236 : public: // periodic scheduler
237 : double fEqPeriodicLastCallTime = 0;
238 : double fEqPeriodicNextCallTime = 0;
239 :
240 : public: // poll scheduler
241 : std::atomic_bool fEqPollThreadStarting{false};
242 : std::atomic_bool fEqPollThreadRunning{false};
243 : std::atomic_bool fEqPollThreadShutdownRequested{false};
244 :
245 : public: // contructors and initialization. not thread-safe.
246 : TMFeEquipment(const char* eqname, const char* eqfilename); // ctor
247 : virtual ~TMFeEquipment(); // dtor
248 : TMFeResult EqInit(const std::vector<std::string>& args); ///< Initialize equipment
249 : TMFeResult EqPreInit(); ///< Initialize equipment, before EquipmentBase::Init()
250 : TMFeResult EqPostInit(); ///< Initialize equipment, after EquipmentBase::Init()
251 : TMFeResult EqReadCommon(); ///< Read TMFeEqInfo from ODB /Equipment/NAME/Common
252 : TMFeResult EqWriteCommon(bool create=false); ///< Write TMFeEqInfo to ODB /Equipment/NAME/Common
253 :
254 : private: // default ctor is not permitted
255 : TMFeEquipment() {}; // ctor
256 :
257 : public: // handlers for initialization are called by the main thread
258 0 : virtual TMFeResult HandleInit(const std::vector<std::string>& args) { return TMFeOk(); };
259 0 : virtual void HandleUsage() {};
260 :
261 : public: // optional RPC handlers are called by the RPC thread
262 0 : virtual TMFeResult HandleBeginRun(int run_number) { return TMFeOk(); };
263 0 : virtual TMFeResult HandleEndRun(int run_number) { return TMFeOk(); };
264 0 : virtual TMFeResult HandlePauseRun(int run_number) { return TMFeOk(); };
265 0 : virtual TMFeResult HandleResumeRun(int run_number) { return TMFeOk(); };
266 0 : virtual TMFeResult HandleStartAbortRun(int run_number) { return TMFeOk(); };
267 0 : virtual TMFeResult HandleRpc(const char* cmd, const char* args, std::string& response) { return TMFeOk(); };
268 :
269 : public: // optional periodic equipment handler is called by the periodic thread
270 0 : virtual void HandlePeriodic() {};
271 :
272 : public: // optional polled equipment handler is called by the per-equipment poll thread
273 0 : virtual bool HandlePoll() { return false; };
274 0 : virtual void HandlePollRead() {};
275 :
276 : public: // per-equipment poll thread
277 : std::thread* fEqPollThread = NULL;
278 : void EqPollThread();
279 : void EqStartPollThread();
280 : void EqStopPollThread();
281 :
282 : //public: // optional ODB watch handler runs from the midas poll thread
283 : //virtual void HandleOdbWatch(const std::string& odbpath, int odbarrayindex) {};
284 :
285 : public: // temporary event composition methods, to bre replaced by the "event object"
286 : TMFeResult ComposeEvent(char* pevent, size_t size) const;
287 : TMFeResult BkInit(char* pevent, size_t size) const;
288 : void* BkOpen(char* pevent, const char* bank_name, int bank_type) const;
289 : TMFeResult BkClose(char* pevent, void* ptr) const;
290 : int BkSize(const char* pevent) const;
291 :
292 : public: // thread-safe methods
293 : TMFeResult EqSendEvent(const char* pevent, bool write_to_odb = true);
294 : TMFeResult EqSendEvent(const std::vector<char>& event, bool write_to_odb = true);
295 : TMFeResult EqSendEvent(const std::vector<std::vector<char>>& event, bool write_to_odb = true);
296 : TMFeResult EqSendEvent(int sg_n, const char* sg_ptr[], const size_t sg_len[], bool write_to_odb = true);
297 : TMFeResult EqWriteEventToOdb(const char* pevent);
298 : TMFeResult EqZeroStatistics();
299 : TMFeResult EqWriteStatistics();
300 : TMFeResult EqSetStatus(const char* status, const char* color);
301 :
302 : private: // non-thread-safe methods
303 : TMFeResult EqWriteEventToOdb_locked(const char* pevent);
304 : };
305 :
306 : class TMFrontend
307 : {
308 : public: // configuration
309 : TMFE* fMfe = NULL;
310 : TMFrontendRpcHelper* fFeRpcHelper = NULL;
311 :
312 : public: // configuration
313 : int fFeIndex = 0; //< frontend index
314 :
315 : bool fFeIfRunningCallExit = false;
316 : bool fFeIfRunningCallBeginRun = true;
317 :
318 : public: // multithreaded lock
319 : std::mutex fFeMutex;
320 :
321 : public: // constructor
322 : TMFrontend(); // ctor
323 : virtual ~TMFrontend(); // dtor
324 :
325 : public: // main program, main event loop
326 : int FeMain(int argc, char* argv[]);
327 : int FeMain(const std::vector<std::string>& args);
328 : void FeUsage(const char* argv0);
329 :
330 : public: // user provided handlers, see tmfe.md
331 0 : virtual TMFeResult HandleArguments(const std::vector<std::string>& args) { return TMFeOk(); };
332 0 : virtual void HandleUsage() { };
333 0 : virtual TMFeResult HandleFrontendInit(const std::vector<std::string>& args) { return TMFeOk(); };
334 0 : virtual TMFeResult HandleFrontendReady(const std::vector<std::string>& args) { return TMFeOk(); };
335 0 : virtual void HandleFrontendExit() { };
336 :
337 : public: // frontend init functions
338 : void FeSetName(const char* program_name);
339 : TMFeResult FeAddEquipment(TMFeEquipment* eq);
340 :
341 : public: // equipment functions
342 : // NOTE: fFeEquipments must be protected against multithreaded write access. K.O.
343 : std::vector<TMFeEquipment*> fFeEquipments;
344 :
345 : TMFeResult FeRemoveEquipment(TMFeEquipment* eq);
346 :
347 : TMFeResult FeInitEquipments(const std::vector<std::string>& args);
348 : void FeDeleteEquipments();
349 :
350 : void FeStopEquipmentPollThreads();
351 :
352 : double FePeriodicTasks(); //< run periodic tasks: equipment periodic handlers, write statistics. returns next time it should be called
353 : double FePollTasks(double next_periodic_time); //< run equipment poll. returns requested poll sleep time, value 0 for poll busy loop
354 :
355 : public: // main loop
356 : TMFeResult FeInit(const std::vector<std::string>& args);
357 : void FeMainLoop();
358 : void FeShutdown();
359 :
360 : public: // scheduler
361 : void FePollMidas(double sleep_sec);
362 :
363 : public: // periodic thread methods, thread-safe
364 : void FePeriodicThread();
365 : void FeStartPeriodicThread();
366 : void FeStopPeriodicThread();
367 :
368 : public: // periodic thread internal data
369 : std::thread* fFePeriodicThread = NULL;
370 : std::atomic_bool fFePeriodicThreadStarting{false};
371 : std::atomic_bool fFePeriodicThreadRunning{false};
372 : std::atomic_bool fFePeriodicThreadShutdownRequested{false};
373 :
374 : public: // flush write cache
375 : double fFeFlushWriteCachePeriodSec = 0.5;
376 : double fFeFlushWriteCacheNextCallTime = 0;
377 :
378 : };
379 :
380 : class TMFE
381 : {
382 : public: // configuration
383 :
384 : std::string fExptname; ///< experiment name, blank if only one experiment defined in exptab
385 : std::string fMserverHostname; ///< hostname where the mserver is running, blank if using shared memory
386 :
387 : std::string fProgramName; ///< frontend program name
388 : std::string fHostname; ///< hostname we are running on
389 :
390 : public: // multithreaded lock
391 : std::mutex fMutex;
392 :
393 : public: // ODB access
394 : int fDB = 0; ///< ODB database handle
395 : MVOdb* fOdbRoot = NULL; ///< ODB root
396 :
397 : public: // shutdown
398 : std::atomic_bool fShutdownRequested{false}; ///< shutdown was requested by Ctrl-C or by RPC command
399 :
400 : public: // run state
401 : int fRunNumber = 0; ///< current run number
402 : bool fStateRunning = false; ///< run state is running or paused
403 :
404 : public: // internal threads
405 : std::thread* fRpcThread = NULL;
406 : std::atomic_bool fRpcThreadStarting{false};
407 : std::atomic_bool fRpcThreadRunning{false};
408 : std::atomic_bool fRpcThreadShutdownRequested{false};
409 :
410 : private:
411 : /// TMFE is a singleton class: only one
412 : /// instance is allowed at any time
413 : static TMFE* gfMFE;
414 :
415 : TMFE(); ///< default constructor is private for singleton classes
416 : virtual ~TMFE(); ///< destructor is private for singleton classes
417 :
418 : public:
419 :
420 : /// TMFE is a singleton class. Call instance() to get a reference
421 : /// to the one instance of this class.
422 : static TMFE* Instance();
423 :
424 : static bool gfVerbose;
425 :
426 : TMFeResult Connect(const char* progname = NULL, const char*hostname = NULL, const char*exptname = NULL);
427 : TMFeResult Disconnect();
428 :
429 : public: // RPC thread methods, thread-safe
430 : void RpcThread();
431 : void StartRpcThread();
432 : void StopRpcThread();
433 :
434 : public: // event buffer data
435 : std::mutex fEventBuffersMutex;
436 : std::vector<TMEventBuffer*> fEventBuffers;
437 :
438 : public: // event buffer methods, thread-safe
439 : TMFeResult EventBufferOpen(TMEventBuffer** pbuf, const char* bufname, size_t bufsize = 0); // factory method
440 : TMFeResult EventBufferFlushCacheAll(bool wait = true);
441 : TMFeResult EventBufferCloseAll();
442 :
443 : public: // run control
444 : bool fRunStopRequested = false; ///< run stop was requested by equipment
445 : double fRunStartTime = 0; ///< start a new run at this time
446 :
447 : void StopRun();
448 : void StartRun();
449 :
450 : public:
451 : TMFeResult SetWatchdogSec(int sec);
452 :
453 : void Yield(double sleep_sec);
454 : void MidasPeriodicTasks();
455 :
456 : TMFeResult TriggerAlarm(const char* name, const char* message, const char* aclass);
457 : TMFeResult ResetAlarm(const char* name);
458 :
459 : void Msg(int message_type, const char *filename, int line, const char *routine, const char *format, ...) MATTRPRINTF(6,7);
460 : //void Msg(int message_type, const char *filename, int line, const char *routine, const char *message); // ambigious with format Msg()
461 : void Msg(int message_type, const char *filename, int line, const char *routine, const std::string& message);
462 :
463 : public: // run transitions and RPCs
464 : std::vector<TMFeRpcHandlerInterface*> fRpcHandlers;
465 :
466 : void AddRpcHandler(TMFeRpcHandlerInterface*);
467 : void RemoveRpcHandler(TMFeRpcHandlerInterface*);
468 :
469 : void SetTransitionSequenceStart(int seqno);
470 : void SetTransitionSequenceStop(int seqno);
471 : void SetTransitionSequencePause(int seqno);
472 : void SetTransitionSequenceResume(int seqno);
473 : void SetTransitionSequenceStartAbort(int seqno);
474 : void DeregisterTransitions();
475 : void DeregisterTransitionStart();
476 : void DeregisterTransitionStop();
477 : void DeregisterTransitionPause();
478 : void DeregisterTransitionResume();
479 : void DeregisterTransitionStartAbort();
480 : void RegisterTransitionStartAbort();
481 : void RegisterRPCs();
482 :
483 : public:
484 : static double GetTime(); ///< return current time in seconds, with micro-second precision
485 : static void Sleep(double sleep_time_sec); ///< sleep, with micro-second precision
486 : static std::string GetThreadId(); ///< return identification of this thread
487 : };
488 :
489 : #endif
490 : /* emacs
491 : * Local Variables:
492 : * tab-width: 8
493 : * c-basic-offset: 3
494 : * indent-tabs-mode: nil
495 : * End:
496 : */
|