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

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