source: trunk/FACT++/src/dataLogger.cc@ 10270

Last change on this file since 10270 was 10265, checked in by lyard, 14 years ago
File size: 33.2 KB
Line 
1//****************************************************************
2/** @class DataLogger
3
4 @brief Logs all message and infos between the services
5
6 This is the main logging class facility.
7 It derives from StateMachineDim and DimInfoHandler. the first parent is here to enforce
8 a state machine behaviour, while the second one is meant to make the dataLogger receive
9 dim services to which it subscribed from.
10 The possible states and transitions of the machine are:
11 \dot
12 digraph datalogger {
13 node [shape=record, fontname=Helvetica, fontsize=10];
14 e [label="Error" color="red"];
15 r [label="Ready"]
16 d [label="DailyOpen"]
17 w [label="WaitingRun"]
18 l [label="Logging"]
19 b [label="BadDailyconfig" color="red"]
20 c [label="BadRunConfig" color="red"]
21
22 e -> r
23 r -> e
24 r -> d
25 r -> b
26 d -> w
27 d -> r
28 w -> r
29 l -> r
30 l -> w
31 b -> d
32 w -> c
33 w -> l
34 b -> r
35 c -> r
36 c -> l
37 }
38 \enddot
39
40 @todo
41 - Retrieve also the messages, not only the infos
42 */
43 //****************************************************************
44#include "Event.h"
45#include "Time.h"
46#include "StateMachineDim.h"
47#include "ServiceList.h"
48#include "Converter.h"
49#include "MessageImp.h"
50
51//#define HAS_FITS
52
53#include <fstream>
54
55#include <boost/bind.hpp>
56
57#ifdef HAS_FITS
58#include <astroroot.h>
59#endif
60
61class DataLogger : public StateMachineDim, DimInfoHandler
62{
63public:
64 /// The list of existing states specific to the DataLogger
65 enum
66 {
67 kSM_DailyOpen = 20, ///< Daily file openned and writing
68 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
69 kSM_Logging = 40, ///< both files openned and writing
70 kSM_BadDailyConfig = 0x101, ///< the folder specified for daily logging does not exist or has bad permissions
71 kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number
72 } localstates_t;
73
74 DataLogger();
75 ~DataLogger();
76
77private:
78 //Define all the data structure specific to the DataLogger here
79 /// ofstream for the dailyLogfile
80 std::ofstream fDailyLogFile;
81 /// ofstream for the run-specific Log file
82 std::ofstream fRunLogFile;
83
84 /// ofstream for the daily report file
85 std::ofstream fDailyReportFile;
86 /// ofstream for the run-specific report file
87 std::ofstream fRunReportFile;
88 /// base path of the dailyfile
89 std::string fDailyFileName;
90 ///base path of the run file
91 std::string fRunFileName;
92 ///run number (-1 means no run number specified)
93 int fRunNumber;
94 ///Current year
95 short fYear;
96 ///Current Month
97 short fMonth;
98 ///Current Day
99 short fDay;
100 ///Current Hour
101 short fHour;
102 ///Current Minute
103 short fMin;
104 ///Current Second
105 short fSec;
106 ///Current Milliseconds
107 int fMs;
108 ///Current Service Quality
109 int fQuality;
110 ///Modified Julian Date
111 double fMjD;
112
113 ///Define all the static names
114 static const char* fConfigDay;
115 static const char* fConfigRun;
116 static const char* fConfigRunNumber;
117 static const char* fConfigLog;
118 static const char* fTransStart;
119 static const char* fTransStop;
120 static const char* fTransStartRun;
121 static const char* fTransStopRun;
122 static const char* fTransReset;
123 static const char* fTransWait;
124 static const char* fRunNumberInfo; ///< This is the name of the dimInfo received to specify the run number. It must be updated once the final name will be defined
125 ///Inherited from state machine impl
126 int Execute();
127
128 ///Inherited from state machine impl
129 int Transition(const Event& evt);
130
131 ///Inherited from state machine impl
132 int Configure(const Event& evt);
133
134 //overloading of DIM's infoHandler function
135 void infoHandler();
136
137 ///for obtaining the name of the existing services
138 ServiceList fServiceList;
139
140
141 ///A std pair to store both the DimInfo name and the actual DimInfo pointer
142// typedef std::pair<std::string, DimStampedInfo*> subscriptionType;
143 ///All the services to which we've subscribed to. Sorted by server name
144// std::map<const std::string, std::vector<subscriptionType > > fServiceSubscriptions;
145
146 ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file
147 struct SubscriptionType
148 {
149#ifdef HAS_FITS
150 ///daily FITS output file
151 AstroRootIo dailyFile;
152 ///run-specific FITS output file
153 AstroRootIo runFile;
154#endif
155 ///the actual dimInfo pointer
156 DimStampedInfo* dimInfo;
157 ///the number of existing handlers to this structure.
158 ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor
159 int* numCopies;
160 void operator = (const SubscriptionType& other)
161 {
162#ifdef HAS_FITS
163 dailyFile = other.dailyFile;
164 runFile = other.runFile;
165#endif
166 dimInfo = other.dimInfo;
167 numCopies = other.numCopies;
168 (*numCopies)++;
169 }
170 SubscriptionType(const SubscriptionType& other)
171 {
172#ifdef HAS_FITS
173 dailyFile = other.dailyFile;
174 runFile = other.runFile;
175#endif
176 dimInfo = other.dimInfo;
177 numCopies = other.numCopies;
178 (*numCopies)++;
179 }
180 SubscriptionType(DimStampedInfo* info)
181 {
182 dimInfo = info;
183 numCopies = new int(1);
184 }
185 SubscriptionType()
186 {
187 dimInfo = NULL;
188 numCopies = new int(1);
189 }
190 ~SubscriptionType()
191 {
192 if (numCopies)
193 (*numCopies)--;
194 if (numCopies)
195 if (*numCopies < 1)
196 {
197#ifdef HAS_FITS
198 if (dailyFile.IsOpen())
199 dailyFile.Close();
200 if (runFile.IsOpen())
201 runFile.Close();
202#endif
203 if (dimInfo)
204 delete dimInfo;
205 if (numCopies)
206 delete numCopies;
207 dimInfo = NULL;
208 numCopies = NULL;
209 }
210 }
211 };
212 typedef std::map<const std::string, std::map<std::string, SubscriptionType>> SubscriptionsListType;
213 ///All the services to which we have subscribed to, sorted by server name.
214 SubscriptionsListType fServiceSubscriptions;
215
216
217 ///Reporting method for the services info received
218 void ReportPlease(DimInfo* I, SubscriptionType& sub);
219
220 ///Configuration of the daily file path
221 int ConfigureDailyFileName(const Event& evt);
222 ///Configuration fo the file name
223 int ConfigureRunFileName(const Event& evt);
224 ///DEPREC - configuration of the run number
225 int ConfigureRunNumber(const Event& evt);
226 ///logging method for the messages
227 int LogMessagePlease(const Event& evt);
228 ///checks whether or not the current info being treated is a run number
229 void CheckForRunNumber(DimInfo* I);
230
231 /// start transition
232 int StartPlease();
233 ///from waiting to logging transition
234 int StartRunPlease();
235 /// from logging to waiting transition
236 int StopRunPlease();
237 ///stop and reset transition
238 int GoToReadyPlease();
239 ///from dailyOpen to waiting transition
240 int DailyToWaitRunPlease();
241#ifdef HAS_FITS
242 ///Open fits files
243 void OpenFITSFilesPlease(SubscriptionType& sub);
244 ///Write data to FITS files
245 void WriteToFITS(SubscriptionType& sub);
246 ///Allocate the buffers required for fits
247 void AllocateFITSBuffers(SubscriptionType& sub);
248#endif
249public:
250 ///checks with fServiceList whether or not the services got updated
251 void CheckForServicesUpdate();
252
253}; //DataLogger
254
255//static members initialization
256//since I do not check the transition/config names any longer, indeed maybe these could be hard-coded... but who knows what will happen in the future ?
257const char* DataLogger::fConfigDay = "CONFIG_DAY";
258const char* DataLogger::fConfigRun = "CONFIG_RUN";
259const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
260const char* DataLogger::fConfigLog = "LOG";
261const char* DataLogger::fTransStart = "START";
262const char* DataLogger::fTransStop = "STOP";
263const char* DataLogger::fTransStartRun = "START_RUN";
264const char* DataLogger::fTransStopRun = "STOP_RUN";
265const char* DataLogger::fTransReset = "RESET";
266const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
267const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
268
269// --------------------------------------------------------------------------
270//
271//! Default constructor. The name of the machine is given DATA_LOGGER
272//! and the state is set to kSM_Ready at the end of the function.
273//
274//!Setup the allows states, configs and transitions for the data logger
275//
276DataLogger::DataLogger() : StateMachineDim(std::cout, "DATA_LOGGER")
277{
278 //initialize member data
279 fDailyFileName = "/home/lyard/log";
280 fRunFileName = "/home/lyard/log";
281 fRunNumber = 12345;
282 //Give a name to this machine's specific states
283 AddStateName(kSM_DailyOpen, "Daily_File_Openened");
284 AddStateName(kSM_WaitingRun, "Waiting_Run_Number");
285 AddStateName(kSM_Logging, "Logging data");
286 AddStateName(kSM_BadDailyConfig, "Folder_For_Daily_Logging_Badly_Configured");
287 AddStateName(kSM_BadRunConfig, "Folder_For_Run_Logging_Badly Configured");
288
289 /*Add the possible transitions for this machine*/
290 AddTransition(kSM_DailyOpen, fTransStart, kSM_Ready, kSM_BadDailyConfig) //start the daily logging. daily file location must be specified already
291 ->AssignFunction(boost::bind(&DataLogger::StartPlease, this));
292 AddTransition(kSM_Ready, fTransStop, kSM_DailyOpen, kSM_WaitingRun, kSM_Logging) //stop the data logging
293 ->AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this));
294 AddTransition(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig) //start the run logging. run file location must be specified already.
295 ->AssignFunction(boost::bind(&DataLogger::StartRunPlease, this));
296 AddTransition(kSM_WaitingRun, fTransStopRun, kSM_Logging)
297 ->AssignFunction(boost::bind(&DataLogger::StopRunPlease, this));
298 AddTransition(kSM_Ready, fTransReset, kSM_Error, kSM_BadDailyConfig, kSM_BadRunConfig, kSM_Error) //transition to exit error states. dunno if required or not, would close the daily file if already openned.
299 ->AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this));
300 AddTransition(kSM_WaitingRun, fTransWait, kSM_DailyOpen)
301 ->AssignFunction(boost::bind(&DataLogger::DailyToWaitRunPlease, this));
302 /*Add the possible configurations for this machine*/
303 AddConfiguration(fConfigDay, "C", kSM_Ready, kSM_BadDailyConfig) //configure the daily file location. cannot be done before the file is actually opened
304 ->AssignFunction(boost::bind(&DataLogger::ConfigureDailyFileName, this, _1));
305 AddConfiguration(fConfigRun, "C", kSM_Ready, kSM_BadDailyConfig, kSM_DailyOpen, kSM_WaitingRun, kSM_BadRunConfig) //configure the run file location. cannot be done before the file is actually opened, and not in a dailly related error.
306 ->AssignFunction(boost::bind(&DataLogger::ConfigureRunFileName, this, _1));
307
308 //Provide a logging command
309 //I get the feeling that I should be going through the EventImp
310 //instead of DimCommand directly, mainly because the commandHandler
311 //is already done in StateMachineImp.cc
312 //Thus I'll simply add a configuration, which I will treat as the logging command
313 AddConfiguration(fConfigLog, "C", kSM_DailyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig)
314 ->AssignFunction(boost::bind(&DataLogger::LogMessagePlease, this, _1));
315
316 fServiceList.SetHandler(this);
317 CheckForServicesUpdate();
318
319 //All configuration done, callback funtions set, go to ready state and thus prevent the run loop from being executed
320 SetReady();
321}
322// --------------------------------------------------------------------------
323//
324//! Checks for changes in the existing services.
325//! Any new service will be added to the service list, while the ones which disappeared are removed.
326//! @todo
327//! add the configuration (using the conf class ?)
328//
329void DataLogger::CheckForServicesUpdate()
330{
331
332 //get the current server list
333 const std::vector<std::string> serverList = fServiceList.GetServerList();
334 //first let's remove the servers that may have disapeared
335 //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
336 std::vector<std::string> toBeDeleted;
337 for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
338 {
339 std::vector<std::string>::const_iterator givenServers;
340 for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
341 if (cListe->first == *givenServers)
342 break;
343 if (givenServers == serverList.end())//server vanished. Remove it
344 toBeDeleted.push_back(cListe->first);
345 }
346 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
347 fServiceSubscriptions.erase(*it);
348
349 //now crawl through the list of servers, and see if there was some updates
350 for (std::vector<std::string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
351 {
352 //skip the two obvious excluded services
353 if ((i->find("DIS_DNS") != std::string::npos) ||
354 (i->find("DATA_LOGGER") != std::string::npos))
355 continue;
356 //find the current server in our subscription list
357 SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
358 //get the service list of the current server
359 std::vector<std::string> cServicesList = fServiceList.GetServiceList(*i);
360 if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
361 { //then check and update our list of subscriptions
362 //first, remove the services that may have dissapeared.
363 std::map<std::string, SubscriptionType>::iterator serverSubs;
364 std::vector<std::string>::const_iterator givenSubs;
365 toBeDeleted.clear();
366 for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
367 {
368 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
369 if (serverSubs->first == *givenSubs)
370 break;
371 if (givenSubs == cServicesList.end())
372 {
373 toBeDeleted.push_back(serverSubs->first);
374 }
375 }
376 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
377 cSubs->second.erase(*it);
378 //now check for new services
379 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
380 {
381 if (*givenSubs == "SERVICE_LIST")
382 continue;
383 if (cSubs->second.find(*givenSubs) == cSubs->second.end())
384 {//service not found. Add it
385 cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast<char*>(""), this);
386 }
387 }
388 }
389 else //server not found in our list. Create its entry
390 {
391 fServiceSubscriptions[*i] = std::map<std::string, SubscriptionType>();
392 std::map<std::string, SubscriptionType>& liste = fServiceSubscriptions[*i];
393 for (std::vector<std::string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
394 {
395 if (*j == "SERVICE_LIST")
396 continue;
397 liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast<char*>(""), this);
398 }
399 }
400 }
401}
402// --------------------------------------------------------------------------
403//
404//! Destructor
405//
406DataLogger::~DataLogger()
407{
408 //close the files
409 if (fDailyLogFile.is_open())
410 fDailyLogFile.close();
411 if (fDailyReportFile.is_open())
412 fDailyReportFile.close();
413 if (fRunLogFile.is_open())
414 fRunLogFile.close();
415 if (fRunReportFile.is_open())
416 fRunReportFile.close();
417 //release the services subscriptions
418 fServiceSubscriptions.clear();
419}
420// --------------------------------------------------------------------------
421//
422//! Execute
423//! Shouldn't be run as we use callbacks instead
424//
425int DataLogger::Execute()
426{
427 //due to the callback mecanism, this function should never be called
428 return kSM_FatalError;
429
430 switch (GetCurrentState())
431 {
432 case kSM_Error:
433 case kSM_Ready:
434 case kSM_DailyOpen:
435 case kSM_WaitingRun:
436 case kSM_Logging:
437 case kSM_BadDailyConfig:
438 case kSM_BadRunConfig:
439 return GetCurrentState();
440 }
441 //this line below should never be hit. It here mainly to remove warnings at compilation
442 return kSM_FatalError;
443}
444// --------------------------------------------------------------------------
445//
446//! Shouldn't be run as we use callbacks instead
447//
448 int DataLogger::Transition(const Event& evt)
449{
450 //due to the callback mecanism, this function should never be called
451 return kSM_FatalError;
452
453 switch (evt.GetTargetState())
454 {
455 case kSM_Ready:
456 /*here we must figure out whether the STOP or RESET command was sent*/
457 /*close opened files and go back to ready state*/
458 switch (GetCurrentState())
459 {
460 case kSM_BadDailyConfig:
461 case kSM_BadRunConfig:
462 case kSM_Error:
463 return GoToReadyPlease();
464
465 case kSM_Logging:
466 case kSM_WaitingRun:
467 case kSM_DailyOpen:
468 return GoToReadyPlease();
469 }
470 break;
471
472 case kSM_DailyOpen:
473 /*Attempt to open the daily file */
474 switch (GetCurrentState())
475 {
476 case kSM_Ready:
477 case kSM_BadDailyConfig:
478 return StartPlease();
479 }
480 break;
481
482 case kSM_WaitingRun:
483 /*either close the run file, or just go to the waitingrun state (if coming from daily open*/
484 switch (GetCurrentState())
485 {
486 case kSM_DailyOpen:
487 return kSM_WaitingRun;
488
489 case kSM_Logging:
490 return StopRunPlease();
491 }
492 break;
493
494 case kSM_Logging:
495 /*Attempt to open run file */
496 switch (GetCurrentState())
497 {
498 case kSM_WaitingRun:
499 case kSM_BadRunConfig:
500 return StartRunPlease();
501 }
502 break;
503 }
504 //Getting here means that an invalid transition has been asked.
505 //TODO Log an error message
506 //and return the fatal error state
507 return kSM_FatalError;
508}
509// --------------------------------------------------------------------------
510//
511//! Shouldn't be run as we use callbacks instead
512//
513 int DataLogger::Configure(const Event& evt)
514{
515 //due to the callback mecanism, this function should never be called
516 return kSM_FatalError;
517
518 switch (evt.GetTargetState())
519 {
520 case kSM_Ready:
521 case kSM_BadDailyConfig:
522 return ConfigureDailyFileName(evt);
523 break;
524
525 case kSM_WaitingRun:
526 case kSM_BadRunConfig:
527 return ConfigureRunFileName(evt);
528 break;
529
530 case kSM_Logging:
531 case kSM_DailyOpen:
532//TODO check that this is indeed correct
533 return 0;//LogMessagePlease(evt);
534 break;
535
536 }
537 //Getting here means that an invalid configuration has been asked.
538 //TODO Log an error message
539 //and return the fatal error state
540 return kSM_FatalError;
541}
542// --------------------------------------------------------------------------
543//
544//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
545//
546void DataLogger::infoHandler()
547{
548 DimInfo* I = getInfo();
549 if (I==NULL)
550 {
551 CheckForServicesUpdate();
552 return;
553 }
554 //check if the service pointer corresponds to something that we subscribed to
555 //this is a fix for a bug that provides bad Infos when a server starts
556 bool found = false;
557 SubscriptionsListType::iterator x;
558 std::map<std::string, SubscriptionType>::iterator y;
559 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
560 {//instead of extracting the server and service names, I crawl through my records. dunno what's faster, but both should work
561 for (y=x->second.begin(); y!=x->second.end();y++)
562 if (y->second.dimInfo == I)
563 {
564 found = true;
565 break;
566 }
567 if (found)
568 break;
569 }
570 if (!found)
571 return;
572 if (I->getSize() <= 0)
573 return;
574 //check that the message has been updated by something, i.e. must be different from its initial value
575 if (I->getTimestamp() == 0)
576 return;
577
578 CheckForRunNumber(I);
579 ReportPlease(I, y->second);
580}
581
582// --------------------------------------------------------------------------
583//
584//! Checks whether or not the current info is a run number.
585//! If so, then remember it. A run number is required to open the run-log file
586//! @param I
587//! the current DimInfo
588//
589void DataLogger::CheckForRunNumber(DimInfo* I)
590{
591 return;
592 if (strstr(I->getName(), fRunNumberInfo) != NULL)
593 {//assumes that the run number is an integer
594 //TODO check the format here
595 fRunNumber = I->getInt();
596 }
597}
598// --------------------------------------------------------------------------
599//
600//! write infos to log files.
601//! @param I
602//! The current DimInfo
603//
604void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
605{
606 //should we log or report this info ? (i.e. is it a message ?)
607 bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
608
609// std::ofstream & dailyFile = fDailyReportFile;//isItaReport? fDailyReportFile : fDailyLogFile;
610// std::ofstream & runFile = fRunReportFile;//isItaReport? fRunReportFile : fRunLogFile;
611
612 //TODO add service exclusion
613 if (!fDailyReportFile.is_open())
614 return;
615
616 //construct the header
617 std::stringstream header;
618
619 Time cTime((time_t)(I->getTimestamp()), I->getTimestampMillisecs());
620
621 //Buffer them for FITS write
622 //TODO this has been replaced by MjD. So I guess that these member variables can go.
623 fYear = cTime.Y(); fMonth = cTime.M(); fDay = cTime.D();
624 fHour = cTime.h(); fMin = cTime.m(); fSec = cTime.s();
625 fMs = cTime.ms(); fQuality = I->getQuality();
626
627 fMjD = cTime.Mjd();
628
629 if (isItaReport)
630 {
631 //write text header
632 header << I->getName() << " " << fQuality << " ";
633 header << fYear << " " << fMonth << " " << fDay << " ";
634 header << fHour << " " << fMin << " " << fSec << " ";
635 header << fMs << " " << I->getTimestamp() << " ";
636
637 Converter conv(std::cout, I->getFormat(), I->getData(), I->getSize());
638
639 if (conv.Ptr() == NULL)
640 return;
641
642 std::string text(conv.Ptr());
643
644 if (text.empty())
645 return;
646
647 if (fDailyReportFile.is_open())
648 fDailyReportFile << header.str();
649 if (fRunReportFile.is_open())
650 fRunReportFile << header.str();
651
652 //replace bizarre characters by white space
653 for (unsigned int i=0; i<text.size(); i++)
654 {
655 if (text[i] == '\n')
656 text[i] = '\\';
657 else
658 if (iscntrl(text[i]))
659 text[i] = ' ';
660 }
661 if (fDailyReportFile.is_open())
662 fDailyReportFile << text << std::endl;
663 if (fRunReportFile.is_open())
664 fRunReportFile << text << std::endl;
665 }
666 else
667 {
668 std::string n = I->getName();
669 std::stringstream msg;
670 msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString();
671 MessageImp dailyMess(fDailyLogFile);
672 dailyMess.Write(cTime, msg.str().c_str(), fQuality);
673 if (fRunLogFile.is_open())
674 {
675 MessageImp runMess(fRunLogFile);
676 runMess.Write(cTime, msg.str().c_str(), fQuality);
677 }
678 }
679
680#ifdef HAS_FITS
681 if (!sub.dailyFile.IsOpen() || !sub.runFile.IsOpen())
682 OpenFITSFilesPlease(sub);
683 WriteToFITS(sub);
684#endif
685
686}
687// --------------------------------------------------------------------------
688//
689//! write messages to logs.
690//! @param evt
691//! the current event to log
692//! @returns
693//! the new state. Currently, always the current state
694//
695//DEPREC I guess that this function should not be any longer
696int DataLogger::LogMessagePlease(const Event& evt)
697{
698 if (!fDailyLogFile.is_open())
699 return GetCurrentState();
700
701 std::stringstream header;
702 const Time& cTime = evt.GetTime();
703 header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
704 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
705 header << cTime.ms() << " ";
706
707// std::string text = ToString(evt.GetFormat().c_str(), evt.GetData(), evt.GetSize());
708 Converter conv(std::cout, evt.GetFormat().c_str(), evt.GetData(), evt.GetSize());
709 if (conv.Ptr() == NULL)
710 return GetCurrentState();
711
712 std::string text(conv.Ptr());
713
714 if (!text.empty())
715 {
716 if (fDailyLogFile.is_open())
717 fDailyLogFile << header;
718 if (fRunLogFile.is_open())
719 fRunLogFile << header;
720
721 //replace bizarre characters by white space
722 for (unsigned int i=0; i<text.size(); i++)
723 {
724 if (text[i] == '\n')
725 text[i] = '\\';
726 else
727 if (iscntrl(text[i]))
728 text[i] = ' ';
729 }
730 if (fDailyLogFile.is_open())
731 fDailyLogFile << text;
732 if (fRunLogFile.is_open())
733 fRunLogFile << text;
734 }
735
736 return GetCurrentState();
737}
738// --------------------------------------------------------------------------
739//
740//! Sets the path to use for the daily log file.
741//! @param evt
742//! the event transporting the path
743//! @returns
744//! currently only the current state.
745//
746int DataLogger::ConfigureDailyFileName(const Event& evt)
747{
748 if (evt.GetText() != NULL)
749 fDailyFileName = std::string(evt.GetText());
750 else
751 Error("Empty daily folder");
752
753 return GetCurrentState();
754}
755// --------------------------------------------------------------------------
756//
757//! Sets the path to use for the run log file.
758//! @param evt
759//! the event transporting the path
760//! @returns
761//! currently only the current state
762int DataLogger::ConfigureRunFileName(const Event& evt)
763{
764 if (evt.GetText() != NULL)
765 fRunFileName = std::string(evt.GetText());
766 else
767 Error("Empty daily folder");
768
769 return GetCurrentState();
770}
771// --------------------------------------------------------------------------
772//
773//! Sets the run number.
774//! @param evt
775//! the event transporting the run number
776//! @returns
777//! currently only the current state
778int DataLogger::ConfigureRunNumber(const Event& evt)
779{
780 fRunNumber = evt.GetInt();
781
782 return GetCurrentState();
783}
784// --------------------------------------------------------------------------
785//
786//! Implements the Start transition.
787//! Concatenates the given path for the daily file and the filename itself (based on the day),
788//! and tries to open it.
789//! @returns
790//! kSM_DailyOpen if success, kSM_BadDailyConfig if failure
791int DataLogger::StartPlease()
792{
793 //TODO concatenate the dailyFileName and the formatted date and extension to obtain the full file name
794 Time time;//(Time::utc);
795 std::stringstream sTime;
796 sTime << time.Y() << "_" << time.M() << "_" << time.D();
797 std::string fullName = fDailyFileName + '/' + sTime.str() + ".log";
798
799 fDailyLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be "app" instead of "ate" ??
800 fullName = fDailyFileName + '/' + sTime.str() + ".rep";
801 fDailyReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app);
802 if (!fDailyLogFile.is_open() || !fDailyReportFile.is_open())
803 {
804 //TODO send an error message
805 return kSM_BadDailyConfig;
806 }
807 return kSM_DailyOpen;
808}
809
810#ifdef HAS_FITS
811// --------------------------------------------------------------------------
812//
813//! open if required a the FITS files corresponding to a given subscription
814//! @param sub
815//! the current DimInfo subscription being examined
816void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub)
817{
818 std::string serviceName(sub.dimInfo->getName());
819 for (unsigned int i=0;i<serviceName.size(); i++)
820 {
821 if (serviceName[i] == '/')
822 {
823 serviceName[i] = '_';
824 break;
825 }
826 }
827 Time time;
828 std::stringstream sTime;
829 sTime << time.Y() << "_" << time.M() << "_" << time.D();
830 //we open the dailyFile anyway, otherwise this function shouldn't have been called.
831 if (!sub.dailyFile.IsOpen())
832 {
833 std::string partialName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits";
834 std::string fullName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits[" + serviceName + "]";
835
836 AllocateFITSBuffers(sub);
837 //currently, the FITS are written in the same directory as the text files.
838 //thus the write permissions have already been checked by the text files.
839 //if the target folder changes, then I should check the write permissions here
840 //now we only check whether the target file exists or not
841 std::ifstream readTest(partialName.c_str());
842 if (readTest.is_open())
843 {
844 readTest.close();
845 sub.dailyFile.Open(fullName.c_str(), "UPDATE");
846 }
847 else {
848 sub.dailyFile.Open(fullName.c_str(), "CREATE");
849 }
850
851//TODO Write the header's attributes
852 }
853 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
854 {//buffer for the run file have already been allocated when doing the daily file
855 std::stringstream sRun;
856 sRun << fRunNumber;
857 std::string partialName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits";
858 std::string fullName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits[" + serviceName + "]";
859
860 std::ifstream readTest(partialName.c_str());
861 if (readTest.is_open())
862 {
863 readTest.close();
864 sub.runFile.Open(fullName.c_str(), "UPDATE");
865 }
866 else
867 sub.runFile.Open(fullName.c_str(), "CREATE");
868//TODO Write the header's attributes
869 }
870}
871// --------------------------------------------------------------------------
872//
873void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
874{
875 const char* format = sub.dimInfo->getFormat();
876 const int size = sub.dimInfo->getSize();
877
878 //Init the time columns of the file
879 sub.dailyFile.InitCol("Date", "double", &fMjD);
880 sub.runFile.InitCol("Date", "double", &fMjD);
881
882// sub.dailyFile.InitCol("Year", "short", &fYear);
883// sub.dailyFile.InitCol("Month", "short", &fMonth);
884// sub.dailyFile.InitCol("Day", "short", &fDay);
885// sub.dailyFile.InitCol("Hour", "short", &fHour);
886// sub.dailyFile.InitCol("Minute", "short", &fMin);
887// sub.dailyFile.InitCol("Second", "short", &fSec);
888// sub.dailyFile.InitCol("MilliSec", "int", &fMs);
889 sub.dailyFile.InitCol("QoS", "int", &fQuality);
890
891// sub.runFile.InitCol("Year", "short", &fYear);
892// sub.runFile.InitCol("Month", "short", &fMonth);
893// sub.runFile.InitCol("Day", "short", &fDay);
894// sub.runFile.InitCol("Hour", "short", &fHour);
895// sub.runFile.InitCol("Minute", "short", &fMin);
896// sub.runFile.InitCol("Second", "short", &fSec);
897// sub.runFile.InitCol("MilliSec", "int", &fMs);
898 sub.runFile.InitCol("QoS", "int", &fQuality);
899
900 Converter::FormatList flist = Converter::Convert(std::cout, std::string(format));
901
902 //we've got a nice structure describing the format of this service's messages.
903 //Let's create the appropriate FITS columns
904 for (unsigned int i=0;i<flist.size();i++)
905 {
906 std::stringstream colName;
907 std::stringstream dataQualifier;
908 void * dataPointer = static_cast<char*>(sub.dimInfo->getData()) + flist[i].second.second;
909 colName << "Data" << i;
910 dataQualifier << flist[i].second.first;
911 switch (flist[i].first.first)
912 {
913 case 'c':
914 dataQualifier << "S";
915 break;
916 case 's':
917 dataQualifier << "I";
918 break;
919 case 'i':
920 dataQualifier << "J";
921 break;
922 case 'l':
923 dataQualifier << "J";
924 //TODO triple check that in FITS, long = int
925 break;
926 case 'f':
927 dataQualifier << "E";
928 break;
929 case 'd':
930 dataQualifier << "D";
931 break;
932 case 'x':
933 dataQualifier << "K";
934 break;
935 case 'S':
936 //for strings, the number of elements I get is wrong. Correct it
937 dataQualifier.str(""); //clear
938 dataQualifier << size-1 << "A";
939 break;
940
941 default:
942 Error("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 962.");
943 };
944 sub.dailyFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer);
945 sub.runFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer);
946 }
947
948//TODO init the attributes
949}
950// --------------------------------------------------------------------------
951//
952//! write a dimInfo data to its corresponding FITS files
953//
954void DataLogger::WriteToFITS(SubscriptionType& sub)
955{
956 //dailyFile status (open or not) already checked
957 if (sub.dailyFile.IsOpen())
958 sub.dailyFile.Write();
959 if (sub.runFile.IsOpen())
960 sub.runFile.Write();
961}
962#endif //if has_fits
963// --------------------------------------------------------------------------
964//
965//! Implements the StartRun transition.
966//! Concatenates the given path for the run file and the filename itself (based on the run number),
967//! and tries to open it.
968//! @returns
969//! kSM_Logging if success, kSM_BadRunConfig if failure.
970int DataLogger::StartRunPlease()
971{
972 //attempt to open run file with current parameters
973 if (fRunNumber == -1)
974 return kSM_BadRunConfig;
975 std::stringstream sRun;
976 sRun << fRunNumber;
977 std::string fullName = fRunFileName + '/' + sRun.str() + ".log";
978 fRunLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate
979
980 fullName = fRunFileName + '/' + sRun.str() + ".rep";
981 fRunReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app);
982
983 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
984 {
985 //TODO send an error message
986 return kSM_BadRunConfig;
987 }
988
989 return kSM_Logging;
990}
991// --------------------------------------------------------------------------
992//
993//! Implements the StopRun transition.
994//! Attempts to close the run file.
995//! @returns
996//! kSM_WaitingRun if success, kSM_FatalError otherwise
997int DataLogger::StopRunPlease()
998{
999 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1000 return kSM_FatalError;
1001
1002 fRunLogFile.close();
1003 fRunReportFile.close();
1004#ifdef HAS_FITS
1005 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1006 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1007 {
1008 if (j->second.runFile.IsOpen())
1009 j->second.runFile.Close();
1010 }
1011#endif
1012 return kSM_WaitingRun;
1013
1014}
1015// --------------------------------------------------------------------------
1016//
1017//! Implements the Stop and Reset transitions.
1018//! Attempts to close any openned file.
1019//! @returns
1020//! kSM_Ready
1021int DataLogger::GoToReadyPlease()
1022{
1023 if (fDailyLogFile.is_open())
1024 fDailyLogFile.close();
1025 if (fDailyReportFile.is_open())
1026 fDailyReportFile.close();
1027
1028 if (fRunLogFile.is_open())
1029 fRunLogFile.close();
1030 if (fRunReportFile.is_open())
1031 fRunReportFile.close();
1032
1033#ifdef HAS_FITS
1034 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1035 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1036 {
1037 if (j->second.dailyFile.IsOpen())
1038 j->second.dailyFile.Close();
1039 if (j->second.runFile.IsOpen())
1040 j->second.runFile.Close();
1041 }
1042#endif
1043 return kSM_Ready;
1044}
1045// --------------------------------------------------------------------------
1046//
1047//! Implements the transition towards kSM_WaitingRun
1048//! Does nothing really.
1049//! @returns
1050//! kSM_WaitingRun
1051int DataLogger::DailyToWaitRunPlease()
1052{
1053 return kSM_WaitingRun;
1054}
1055
1056// --------------------------------------------------------------------------
1057//
1058int main()
1059{
1060//No more Dim Checking ??
1061// if (!CheckDim())
1062// return -1;
1063
1064 DataLogger log;
1065 while (!log.fExitRequested)
1066 {
1067 usleep(10);
1068 }
1069 return 0;
1070
1071}
Note: See TracBrowser for help on using the repository browser.