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

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