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

Last change on this file since 10836 was 10829, checked in by lyard, 14 years ago
Moved many things to functions
File size: 95.4 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="NightlyOpen"]
17 w [label="WaitingRun"]
18 l [label="Logging"]
19 b [label="BadNightlyconfig" 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 //****************************************************************
41#include "Dim.h"
42#include "Event.h"
43#include "Time.h"
44#include "StateMachineDim.h"
45#include "WindowLog.h"
46#include "Configuration.h"
47#include "ServiceList.h"
48#include "Converter.h"
49#include "MessageImp.h"
50#include "LocalControl.h"
51#include "DimDescriptionService.h"
52
53#include "Description.h"
54
55#include "DimServiceInfoList.h"
56
57//for getting stat of opened files
58#include <unistd.h>
59//for getting disk free space
60#include <sys/statvfs.h>
61//for getting files sizes
62#include <sys/stat.h>
63
64#include <fstream>
65
66#include <boost/bind.hpp>
67#if BOOST_VERSION < 104400
68#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
69#undef BOOST_HAS_RVALUE_REFS
70#endif
71#endif
72#include <boost/thread.hpp>
73
74#ifdef HAVE_FITS
75#include "Fits.h"
76#endif
77
78//Dim structures
79///Distributes the writting statistics
80struct DataLoggerStats {
81 long sizeWritten;
82 long freeSpace;
83 long writingRate;
84};
85///distributes the number of opened subscriptions and fits files
86struct NumSubAndFitsType {
87 int numSubscriptions;
88 int numOpenFits;
89};
90///distributes which files were opened.
91struct OpenFileToDim {
92 int code;
93 char fileName[FILENAME_MAX];
94};
95
96///Run number record. Used to keep track of which run numbers are still active
97struct RunNumberType {
98 ///the run number log file
99 ofstream* logFile;
100 ///the run number report file
101 ofstream* reportFile;
102#ifdef HAVE_FITS
103 ///the run number group fits file
104 CCfits::FITS* runFitsFile;
105#endif
106 ///the log filename
107 string logName;
108 ///the report filename
109 string reportName;
110 ///the actual run number
111 int runNumber;
112 ///the time at which the run number was received
113 Time time;
114 ///internal counter used for smart referencing
115 int* numCopies;
116 ///list of opened fits used to create the fits grouping when the run ends
117 map<string, vector<string> > openedFits;
118 ///default constructor
119 RunNumberType()
120 {
121 logFile = new ofstream();
122 reportFile = new ofstream();
123#ifdef HAVE_FITS
124 runFitsFile = NULL;
125#endif
126 runNumber = -1;
127 //give it a meaningless, 0 time to distinguish with actual, valid times
128 time = Time(0,0);
129 numCopies = new int(1);
130 }
131 ///default destructor
132 ~RunNumberType()
133 {
134 if (numCopies)
135 {
136 (*numCopies)--;
137 if (*numCopies < 1)
138 {
139 if (logFile)
140 {
141 if (logFile->is_open())
142 logFile->close();
143 delete logFile;
144 logFile = NULL;
145 }
146 if (reportFile)
147 {
148 if (reportFile->is_open())
149 reportFile->close();
150 delete reportFile;
151 reportFile = NULL;
152 }
153#ifdef HAVE_FITS
154 if (runFitsFile)
155 {
156 delete runFitsFile;
157 runFitsFile = NULL;
158 }
159#endif
160 delete numCopies;
161 numCopies = NULL;
162 }
163 }
164 }
165 ///copy operator
166 void operator = (const RunNumberType& other)
167 {
168 logFile = other.logFile;
169 reportFile = other.reportFile;
170 logName = other.logName;
171 reportName = other.reportName;
172 runNumber = other.runNumber;
173 time = other.time;
174 numCopies = other.numCopies;
175#ifdef HAVE_FITS
176 runFitsFile = other.runFitsFile;
177#endif
178 (*numCopies)++;
179 }
180 ///copy constructor
181 RunNumberType(const RunNumberType& other)
182 {
183 logFile = other.logFile;
184 reportFile = other.reportFile;
185 logName = other.logName;
186 reportName = other.reportName;
187 runNumber = other.runNumber;
188 time = other.time;
189 numCopies = other.numCopies;
190#ifdef HAVE_FITS
191 runFitsFile = other.runFitsFile;
192#endif
193 (*numCopies)++;
194 }
195
196};
197///Dim subscription type. Stores all the relevant info to handle a Dim subscription
198struct SubscriptionType
199{
200#ifdef HAVE_FITS
201 ///Nightly FITS output file
202 Fits nightlyFile;
203 ///run-specific FITS output file
204 Fits runFile;
205#endif
206 ///the actual dimInfo pointer
207 DimStampedInfo* dimInfo;
208 ///the converter for outputting the data according to the format
209 Converter* fConv;
210 ///internal counter used for smart referencing
211 int* numCopies;
212 ///the current run number used by this subscription
213 int runNumber;
214 ///copy operator
215 void operator = (const SubscriptionType& other)
216 {
217#ifdef HAVE_FITS
218 nightlyFile = other.nightlyFile;
219 runFile = other.runFile;
220#endif
221 dimInfo = other.dimInfo;
222 numCopies = other.numCopies;
223 fConv = other.fConv;
224 runNumber = other.runNumber;
225 (*numCopies)++;
226 }
227 ///copy constructor
228 SubscriptionType(const SubscriptionType& other)
229 {
230#ifdef HAVE_FITS
231 nightlyFile = other.nightlyFile;
232 runFile = other.runFile;
233#endif
234 dimInfo = other.dimInfo;
235 numCopies = other.numCopies;
236 fConv = other.fConv;
237 runNumber = other.runNumber;
238 (*numCopies)++;
239 }
240 ///Dim info constructor
241 SubscriptionType(DimStampedInfo* info)
242 {
243 dimInfo = info;
244 fConv = NULL;
245 runNumber = -1;
246 numCopies = new int(1);
247 }
248 ///default constructor
249 SubscriptionType()
250 {
251 dimInfo = NULL;
252 fConv = NULL;
253 runNumber = -1;
254 numCopies = new int(1);
255 }
256 ///default destructor
257 ~SubscriptionType()
258 {
259 if (numCopies)
260 (*numCopies)--;
261 if (numCopies)
262 if (*numCopies < 1)
263 {
264 if (dimInfo)
265 delete dimInfo;
266#ifdef HAVE_FITS
267 if (nightlyFile.IsOpen())
268 nightlyFile.Close();
269 if (runFile.IsOpen())
270 runFile.Close();
271#endif
272 if (numCopies)
273 delete numCopies;
274 delete fConv;
275 fConv = NULL;
276 dimInfo = NULL;
277 numCopies = NULL;
278 }
279 }
280};
281
282class DataLogger : public StateMachineDim, DimInfoHandler
283{
284public:
285 /// The list of existing states specific to the DataLogger
286 enum
287 {
288 kSM_NightlyOpen = 20, ///< Nightly file openned and writing
289 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
290 kSM_Logging = 40, ///< both files openned and writing
291 kSM_BadNightlyConfig = 0x101, ///< the folder specified for Nightly logging does not exist or has bad permissions
292 kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number
293 } localstates_t;
294
295 DataLogger(ostream &out);
296 ~DataLogger();
297
298 bool SetConfiguration(Configuration& conf);
299
300private:
301 /************************************************
302 * MEMBER VARIABLES
303 ************************************************/
304 /// ofstream for the NightlyLogfile
305 ofstream fNightlyLogFile;
306 /// ofstream for the Nightly report file
307 ofstream fNightlyReportFile;
308 /// base path of the Nightlyfile
309 string fNightlyFilePath;
310 ///base path of the run file
311 string fRunFilePath;
312 ///run numbers
313 list<RunNumberType> fRunNumber;
314 ///previous run number. to check if changed while logging
315 int fPreviousRunNumber;
316 ///Current Service Quality
317 int fQuality;
318 ///Modified Julian Date
319 double fMjD;
320 ///for obtaining the name of the existing services
321 ServiceList fServiceList;
322 typedef map<const string, map<string, SubscriptionType> > SubscriptionsListType;
323 ///All the services to which we have subscribed to, sorted by server name.
324 SubscriptionsListType fServiceSubscriptions;
325 ///full name of the nightly log file
326 string fFullNightlyLogFileName;
327 ///full name of the nightly report file
328 string fFullNightlyReportFileName;
329
330public:
331 /***************************************************
332 * STATIC COMMAND NAMES
333 ***************************************************/
334 ///Define all the static names
335 static const char* fConfigDay;
336 static const char* fConfigRun;
337 static const char* fConfigRunNumber;
338 static const char* fConfigLog;
339 static const char* fTransStart;
340 static const char* fTransStop;
341 static const char* fTransStartRun;
342 static const char* fTransStopRun;
343 static const char* fTransReset;
344 static const char* fTransWait;
345 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
346 static const char* fPrintCommand;
347 static const char* fDebugOnOff;
348 static const char* fStatsPeriod;
349 static const char* fStartStopOpenedFiles;
350 static const char* fStartStopNumSubsAndFits;
351private:
352 /***************************************************
353 * DIM INFO HANDLER
354 ***************************************************/
355 //overloading of DIM's infoHandler function
356 void infoHandler();
357
358 /***************************************************
359 * TRANSITION FUNCTIONS
360 ***************************************************/
361 ///Reporting method for the services info received
362 void ReportPlease(DimInfo* I, SubscriptionType& sub);
363 ///Configuration of the nightly file path
364 int ConfigureNightlyFileName(const Event& evt);
365 ///Configuration fo the file name
366 int ConfigureRunFileName(const Event& evt);
367 ///DEPREC - configuration of the run number
368 int ConfigureRunNumber(const Event& evt);
369 ///logging method for the messages
370// int LogMessagePlease(const Event& evt);
371 ///print the current state of the dataLogger
372 int PrintStatePlease(const Event& evt);
373 ///checks whether or not the current info being treated is a run number
374 void CheckForRunNumber(DimInfo* I);
375 /// start transition
376 int StartPlease();
377 ///from waiting to logging transition
378 int StartRunPlease();
379 /// from logging to waiting transition
380 int StopRunPlease();
381 ///stop and reset transition
382 int GoToReadyPlease();
383 ///from NightlyOpen to waiting transition
384 int NightlyToWaitRunPlease();
385#ifdef HAVE_FITS
386 ///Open fits files
387 void OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber);
388 ///Write data to FITS files
389 void WriteToFITS(SubscriptionType& sub);
390 ///Allocate the buffers required for fits
391 void AllocateFITSBuffers(SubscriptionType& sub);
392#endif//has_fits
393
394 /***************************************
395 * DIM SERVICES PROVIDED BY THE DATA LOGGER
396 ***************************************/
397 ///monitoring notification loop
398 void ServicesMonitoring();
399 inline void NotifyOpenedFile(string name, int type, DimDescribedService* service);
400 ///services notification thread
401 boost::thread fMonitoringThread;
402 ///end of the monitoring
403 bool fContinueMonitoring;
404 ///stores the size of each file that is or was open
405 map<string, long> fFileSizesMap;
406 ///total size of the opened files BEFORE they were opened by the logger
407 long fBaseSizeNightly;
408 long fPreviousSize;
409 long fBaseSizeRun;
410 ///Service for opened files
411 DimDescribedService* fOpenedNightlyFiles;
412 DimDescribedService* fOpenedRunFiles;
413 DimDescribedService* fNumSubAndFits;
414 NumSubAndFitsType fNumSubAndFitsData;
415 ///Small function for calculating the total size written so far
416 void calculateTotalSizeWritten(DataLoggerStats& statVar, bool& shouldWarn, bool isPrinting);
417
418 /***************************************************
419 * DATA LOGGER's CONFIGURATION STUFF
420 ***************************************************/
421 ///black/white listing
422 set<string> fBlackList;
423 set<string> fWhiteList;
424 ///list of services to be grouped
425 set<string> fGrouping;
426 ///configuration flags
427 bool fHasBlackList;
428 bool fHasWhiteList;
429 bool fDebugIsOn;
430 float fStatsPeriodDuration;
431 bool fOpenedFilesIsOn;
432 bool fNumSubAndFitsIsOn;
433 //functions for controlling the services behavior
434 int SetDebugOnOff(const Event& evt);
435 int SetStatsPeriod(const Event& evt);
436 int SetOpenedFilesOnOff(const Event& evt);
437 int SetNumSubsAndFitsOnOff(const Event& evt);
438
439 ///boolean to prevent DIM update while desctructing the dataLogger
440 bool fDestructing;
441 /***************************************************
442 * UTILITIES
443 ***************************************************/
444 ///vectors to keep track of opened Fits files, for grouping purposes.
445 map<string, vector<string> > fOpenedNightlyFits;
446 ///creates a group fits file based on a list of files to be grouped
447 void CreateFitsGrouping(map<string, vector<string> >& filesToGroup, int runNumber);
448 ///Open the relevant text files related to a particular run
449 int OpenRunFile(RunNumberType& run);
450 ///add a new run number
451 void AddNewRunNumber(int newRun, Time time);
452 ///removes the oldest run number, and close the relevant files.
453 void RemoveOldestRunNumber();
454 ///checks with fServiceList whether or not the services got updated
455 bool CheckForServicesUpdate();
456 ///retrieves the size of a file
457 off_t GetFileSize(string&);
458 ///Form the name of the nightly text files (logs and reports)
459 void FormNightlyTextFileName(string& name, bool isReport);
460 ///Form the name of the run text files (logs and reports)
461 void FormRunTextFileName(string& name, bool isReport, const int runNumber);
462 ///Form the name of the nightly Fits files
463 void FormNightlyFitsFileName(string& fileName, string& fileNameWithPath, const string& serviceName);
464 ///Form the name of the run Fits files
465 void FormRunFitsFileName(string& fileName, string& fileNameWithPath, const string& serviceName, const int runNumber);
466 ///Form the name of the grouped fits file (i.e. the file where several services are written)
467 void FormGroupedRunFitsFileName(string& fileName, string& fileNameWithPath, const int runNumber);
468 ///Form the name of the grouping fits file (i.e. the file that is created after a run ends)
469 void FormFitsGroupingFileName(string& fileNameWithPath, const int runNumber);
470 ///Check whether service is in black and/or white list
471 bool ShouldSubscribe(const string& server, const string& service);
472 ///Subscribe to a given server and service
473 DimStampedInfo* SubscribeToPlease(const string& server, const string& service);
474 ///Open a text file and checks for ofstream status
475 void OpenTextFilePlease(ofstream& stream, const string& name);
476 ///Check if a dir is . and returns the actual string corresponding to .
477 string CheckIfDirIsDot(const string& dir);
478 ///Remembers the size of newly opened files. for statistic purposes
479 bool RememberFileOrigSizePlease(string& fileName, bool nightly);
480}; //DataLogger
481
482// --------------------------------------------------------------------------
483//
484//!
485//
486bool DataLogger::RememberFileOrigSizePlease(string& fileName, bool nightly)
487{
488 //get the size of the file we're about to open
489 if (fFileSizesMap.find(fileName) == fFileSizesMap.end())
490 {
491 if (nightly)
492 fBaseSizeNightly += GetFileSize(fileName);
493 else
494 fBaseSizeRun += GetFileSize(fileName);
495 fFileSizesMap[fileName] = 0;
496 return true;
497 }
498 return false;
499}
500// --------------------------------------------------------------------------
501//
502//!
503//
504string DataLogger::CheckIfDirIsDot(const string& dir)
505{
506 if (dir == ".")
507 {
508 char currentPath[FILENAME_MAX];
509 if (!getcwd(currentPath, sizeof(currentPath)))
510 {
511 if (errno != 0)
512 {
513 ostringstream str;
514 str << "Unable to retrieve the current path" << ". Reason: " << strerror(errno) << " [" << errno << "]";
515 Error(str);
516 }
517 }
518 return string(currentPath);
519 }
520 else
521 {
522 return string(dir);
523 }
524}
525// --------------------------------------------------------------------------
526//
527//!
528//
529void DataLogger::OpenTextFilePlease(ofstream& stream, const string& name)
530{
531 stream.open(name.c_str(), ios_base::out | ios_base::app);
532 if (errno != 0)
533 {
534 ostringstream str;
535 str << "Unable to open file: " << name << ". Reason: " << strerror(errno) << " [" << errno << "]";
536 Error(str);
537 }
538}
539// --------------------------------------------------------------------------
540//
541//! Create a new dim subscription to a given server and service
542//! @param server the server name
543//! @param service the service name
544//
545DimStampedInfo* DataLogger::SubscribeToPlease(const string& server, const string& service)
546{
547 if (fDebugIsOn)
548 {
549 ostringstream str;
550 str << "Subscribing to service " << server << "/" << service;
551 Debug(str);
552 }
553 return new DimStampedInfo((server + "/" + service).c_str(), const_cast<char*>(""), this);
554}
555// --------------------------------------------------------------------------
556//
557//! Check whether a service should be subscribed to, based on the black/white list entries
558//! @param server the server name associated with the service being checked
559//! @param service the service name associated with the service being checked
560//
561bool DataLogger::ShouldSubscribe(const string& server, const string& service)
562{
563 if (service == "SERVICE_LIST")
564 return false;
565 if (fHasWhiteList && (fWhiteList.find(server + "/") == fWhiteList.end()) &&
566 (fWhiteList.find(server + "/" + service) == fWhiteList.end()) &&
567 (fWhiteList.find("/" + service) == fWhiteList.end()))
568 return false;
569 if (fHasBlackList && ((fBlackList.find(server + "/") != fBlackList.end()) ||
570 (fBlackList.find(server + "/" + service) != fBlackList.end()) ||
571 (fBlackList.find("/" + service) != fBlackList.end())))
572 return false;
573
574 return true;
575}
576// --------------------------------------------------------------------------
577//
578//!Form the name of the grouped fits file (i.e. the file where several services are written)
579//! @param fileName only the file name itself (no path)
580//! @param fileNameWithPath fileName, augmented with the prefix path
581//! @param runNumber the current run number for which the file should be opened.
582//
583void DataLogger::FormGroupedRunFitsFileName(string& fileName, string& fileNameWithPath, const int runNumber)
584{
585 ostringstream sRun;
586 sRun << runNumber;
587 fileName = sRun.str() + "_group.fits";
588 fileNameWithPath = fRunFilePath + '/' + fileName;
589}
590// --------------------------------------------------------------------------
591//
592//! Form the name of the grouping fits file (i.e. the file that is created after a run ends)
593//! This file is meant to link the various fits files related to a particular run from a single file
594//! @param fileNameWithPath the full filename
595//! @param runNumber the current run number for which the file should be opened. -1 means nightly grouping
596//
597void DataLogger::FormFitsGroupingFileName(string& fileNameWithPath, const int runNumber)
598{
599 ostringstream groupName;
600 if (runNumber != -1)
601 {
602 groupName << fRunFilePath << '/' << runNumber << ".fits";
603 }
604 else
605 {
606 Time time;
607 ostringstream sTime;
608 sTime << time.Y() << "_" << time.M() << "_" << time.D();
609 groupName << fNightlyFilePath << '/' << sTime.str() << ".fits";
610 }
611 fileNameWithPath = groupName.str();
612
613}
614// --------------------------------------------------------------------------
615//
616//! Form the name of the nightly Fits files
617//! @param fileName the file name only
618//! @param fileNameWithPath the fileName augmented by the prefix path
619//! @param serviceName the service for which this file should be opened
620//
621void DataLogger::FormNightlyFitsFileName(string& fileName, string& fileNameWithPath, const string& serviceName)
622{
623 Time time;
624 ostringstream sTime;
625 sTime << time.Y() << "_" << time.M() << "_" << time.D();
626 fileName = sTime.str() + '_' + serviceName + ".fits";
627 fileNameWithPath = fNightlyFilePath + '/' + fileName;
628}
629// --------------------------------------------------------------------------
630//
631//! Form the name of the run Fits files
632//! @param fileName the file name only
633//! @param fileNameWithPath the fileName augmented by the prefix path
634//! @param serviceName the service for which this file should be opened
635//! @param runNumber the current run number for which this file should be opened
636//
637void DataLogger::FormRunFitsFileName(string& fileName, string& fileNameWithPath, const string& serviceName, const int runNumber)
638{
639 ostringstream sRun;
640 sRun << runNumber;
641 fileName = sRun.str() + '_' + serviceName + ".fits";
642 fileNameWithPath = fRunFilePath + '/' + fileName;
643}
644// --------------------------------------------------------------------------
645//
646//! Form the name of the run text files (logs and reports)
647//! @param name the full file name
648//! @param isReport whether a log or report name should be formed
649//! @param runNumber the current run number for which this file should be opened
650//
651void DataLogger::FormRunTextFileName(string& name, bool isReport, const int runNumber)
652{
653 ostringstream sRun;
654 sRun << runNumber;
655
656 name = fRunFilePath + '/' + sRun.str();
657
658 if (isReport)
659 name += ".rep";
660 else
661 name += ".log";
662}
663// --------------------------------------------------------------------------
664//
665//! Form the name of the nightly text files (logs and reports)
666//! @param name the full file name
667//! @param isReport whether a log or report name should be formed
668//
669void DataLogger::FormNightlyTextFileName(string& name, bool isReport)
670{
671 Time time;
672 ostringstream sTime;
673 sTime << time.Y() << "_" << time.M() << "_" << time.D();
674
675 name = fNightlyFilePath + '/' + sTime.str();
676 if (isReport)
677 name += ".rep";
678 else
679 name += ".log";
680}
681// --------------------------------------------------------------------------
682//
683//!retrieves the size on disk of a file
684//! @param fileName the full file name for which the size on disk should be retrieved
685//! @return the size of the file on disk, in bytes. 0 if the file does not exist or if an error occured
686//
687off_t DataLogger::GetFileSize(string& fileName)
688{
689 struct stat st;
690 if (!stat(fileName.c_str(), &st))
691 return st.st_size;
692
693 if (errno != 0)
694 {
695 ostringstream str;
696 str << "Unable to stat " << fileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
697 Error(str);
698 }
699
700 return 0;
701}
702// --------------------------------------------------------------------------
703//
704//! Removes the oldest run number and closes the fits files that should be closed
705//! Also creates the fits grouping file
706//
707void DataLogger::RemoveOldestRunNumber()
708{
709 if (fDebugIsOn)
710 {
711 ostringstream str;
712 str << "Removing run number " << fRunNumber.front().runNumber;
713 Debug(str);
714 }
715 CreateFitsGrouping(fRunNumber.front().openedFits, fRunNumber.front().runNumber);
716 //crawl through the subscriptions to see if there are still corresponding fits files opened.
717 SubscriptionsListType::iterator x;
718 map<string, SubscriptionType>::iterator y;
719 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
720 for (y=x->second.begin(); y != x->second.end(); y++)
721 if (y->second.runFile.fRunNumber == fRunNumber.front().runNumber && y->second.runFile.IsOpen())
722 {
723 if (fDebugIsOn)
724 {
725 ostringstream str;
726 str << "Closing Fits run file " << y->second.runFile.fFileName;
727 Debug(str);
728 }
729 y->second.runFile.Close();
730 }
731 //if a grouping file is on, decrease the number of opened fits manually
732 if (fRunNumber.front().runFitsFile)
733 (fNumSubAndFitsData.numOpenFits)--;
734 //remove the entry
735 fRunNumber.pop_front();
736}
737
738// --------------------------------------------------------------------------
739//
740//! Calculate the total number of written bytes since the logger was started
741//! @param statVar the data structure that should be updated
742//! @param shouldWarn whether or not error messages should be outputted
743//! @param isPrinting whether this function was called from the PRINT command or not. If so, displays relevant information
744//
745void DataLogger::calculateTotalSizeWritten(DataLoggerStats& statVar, bool& shouldWarn, bool isPrinting)
746{
747#ifdef HAVE_FITS
748 if (isPrinting)
749 {
750 ostringstream str;
751 str << "There are " << fNumSubAndFitsData.numOpenFits << " FITS files open:";
752 Message(str);
753 }
754 SubscriptionsListType::iterator x;
755 map<string, SubscriptionType>::iterator y;
756///TODO the grouping file is dealt with several times. This should not be a problem but well, better to fix it I guess.
757 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
758 {
759 for (y=x->second.begin(); y != x->second.end(); y++)
760 {
761 if (y->second.runFile.IsOpen())
762 {
763 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
764 if (isPrinting)
765 Message("-> "+y->second.runFile.fFileName);
766 }
767 if (y->second.nightlyFile.IsOpen())
768 {
769 fFileSizesMap[y->second.nightlyFile.fFileName] = y->second.nightlyFile.GetWrittenSize();
770 if (isPrinting)
771 Message("-> "+y->second.nightlyFile.fFileName);
772 }
773 }
774 }
775#else
776 if (isPrinting)
777 Message("FITS output disabled at compilation");
778#endif
779 //gather log and report files sizes on disk
780 if (fNightlyLogFile.is_open())
781 fFileSizesMap[fFullNightlyLogFileName] = GetFileSize(fFullNightlyLogFileName);
782 if (fNightlyReportFile.is_open())
783 fFileSizesMap[fFullNightlyReportFileName] = GetFileSize(fFullNightlyReportFileName);
784 for (list<RunNumberType>::iterator it = fRunNumber.begin(); it != fRunNumber.end(); it++)
785 {
786 if (it->reportFile->is_open())
787 fFileSizesMap[it->reportName] = GetFileSize(it->reportName);
788 if (it->logFile->is_open())
789 fFileSizesMap[it->logName] = GetFileSize(it->logName);
790 }
791 struct statvfs vfs;
792 if (!statvfs(fNightlyFilePath.c_str(), &vfs))
793 {
794 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
795 shouldWarn = false;
796 }
797 else
798 {
799 ostringstream str;
800 str << "Unable to retrieve stats for " << fNightlyFilePath << ". Reason: " << strerror(errno) << " [" << errno << "]";
801 if (!shouldWarn)
802 Error(str);
803 shouldWarn = true;
804 statVar.freeSpace = -1;
805 }
806 //sum up all the file sizes. past and present
807 statVar.sizeWritten = 0;
808 for (map<string, long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
809 statVar.sizeWritten += it->second;
810 statVar.sizeWritten -= fBaseSizeNightly;
811 statVar.sizeWritten -= fBaseSizeRun;
812}
813//static members initialization
814//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 ?
815const char* DataLogger::fConfigDay = "CONFIG_DAY";
816const char* DataLogger::fConfigRun = "CONFIG_RUN";
817const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
818const char* DataLogger::fConfigLog = "LOG";
819const char* DataLogger::fTransStart = "START";
820const char* DataLogger::fTransStop = "STOP";
821const char* DataLogger::fTransStartRun = "START_RUN";
822const char* DataLogger::fTransStopRun = "STOP_RUN";
823const char* DataLogger::fTransReset = "RESET";
824const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
825const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
826const char* DataLogger::fPrintCommand = "PRINT";
827const char* DataLogger::fDebugOnOff = "DEBUG";
828const char* DataLogger::fStatsPeriod = "STATS_PERIOD";
829const char* DataLogger::fStartStopOpenedFiles = "OPENED_FILES_SRVC";
830const char* DataLogger::fStartStopNumSubsAndFits = "NUM_SUBS_SRVC";
831
832// --------------------------------------------------------------------------
833//
834//! Monitor the number of opened files and total size written, and distributes this data through a Dim service
835//
836//
837void DataLogger::ServicesMonitoring()
838{
839 DataLoggerStats statVar;
840 statVar.sizeWritten = 0;
841 statVar.freeSpace = 0;
842 statVar.writingRate = 0;
843
844 struct statvfs vfs;
845 if (!statvfs(fNightlyFilePath.c_str(), &vfs))
846 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
847 else
848 statVar.freeSpace = -1;
849
850 DimDescribedService srvc ("DATA_LOGGER/STATS", "X:3", statVar, "Add description here");
851 fPreviousSize = 0;
852 bool statWarning = false;
853 //loop-wait for broadcast
854 while (fContinueMonitoring)
855 {
856 //check if some run number entries can be deleted
857 while (fRunNumber.size() > 1 && (Time() - fRunNumber.front().time) > boost::posix_time::time_duration(0,0,10,0))
858 {
859 RemoveOldestRunNumber();
860 }
861 if (fStatsPeriodDuration == 0.0f)
862 {
863 sleep(0.1f);
864 continue;
865 }
866 else
867 sleep(fStatsPeriodDuration);
868 //update the fits files sizes
869 calculateTotalSizeWritten(statVar, statWarning, false);
870 if (fStatsPeriodDuration == 0.0f)
871 continue;
872 statVar.writingRate = (statVar.sizeWritten - fPreviousSize)/fStatsPeriodDuration;
873
874 fPreviousSize = statVar.sizeWritten;
875 if (statVar.writingRate != 0) //if data has been written
876 {
877 srvc.updateService();
878 if(fDebugIsOn)
879 {
880 ostringstream str;
881 str << "Size written: " << statVar.sizeWritten/1024 << " KB; writting rate: ";
882 str << statVar.writingRate/1024 << " KB/s; free space: ";
883 str << statVar.freeSpace/(1024*1024) << " MB";
884 Debug(str);
885 }
886 }
887 }
888}
889
890// --------------------------------------------------------------------------
891//
892//! Default constructor. The name of the machine is given DATA_LOGGER
893//! and the state is set to kSM_Ready at the end of the function.
894//
895//!Setup the allows states, configs and transitions for the data logger
896//
897DataLogger::DataLogger(ostream &out) : StateMachineDim(out, "DATA_LOGGER")
898{
899 //initialize member data
900 fNightlyFilePath = ".";
901 fRunFilePath = ".";
902
903 //Give a name to this machine's specific states
904 AddStateName(kSM_NightlyOpen, "NightlyFileOpen", "The summary files for the night are open.");
905 AddStateName(kSM_WaitingRun, "WaitForRun", "The summary files for the night are open and we wait for a run to be started.");
906 AddStateName(kSM_Logging, "Logging", "The summary files for the night and the files for a single run are open.");
907 AddStateName(kSM_BadNightlyConfig, "ErrNightlyFolder", "The folder for the nighly summary files is invalid.");
908 AddStateName(kSM_BadRunConfig, "ErrRunFolder", "The folder for the run files is invalid.");
909
910 /*Add the possible transitions for this machine*/
911 AddEvent(kSM_NightlyOpen, fTransStart, kSM_Ready, kSM_BadNightlyConfig)
912 (boost::bind(&DataLogger::StartPlease, this))
913 ("Start the nightly logging. Nightly file location must be specified already");
914
915 AddEvent(kSM_Ready, fTransStop, kSM_NightlyOpen, kSM_WaitingRun, kSM_Logging)
916 (boost::bind(&DataLogger::GoToReadyPlease, this))
917 ("Stop all data logging, close all files.");
918
919 AddEvent(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig)
920 (boost::bind(&DataLogger::StartRunPlease, this))
921 ("Start the run logging. Run file location must be specified already.");
922
923 AddEvent(kSM_WaitingRun, fTransStopRun, kSM_Logging)
924 (boost::bind(&DataLogger::StopRunPlease, this))
925 ("Wait for a run to be started, open run-files as soon as a run number arrives.");
926
927 AddEvent(kSM_Ready, fTransReset, kSM_Error, kSM_BadNightlyConfig, kSM_BadRunConfig, kSM_Error)
928 (boost::bind(&DataLogger::GoToReadyPlease, this))
929 ("Transition to exit error states. Closes the nightly file if already opened.");
930
931 AddEvent(kSM_WaitingRun, fTransWait, kSM_NightlyOpen)
932 (boost::bind(&DataLogger::NightlyToWaitRunPlease, this));
933
934 /*Add the possible configurations for this machine*/
935 AddEvent(fConfigDay, "C", kSM_Ready, kSM_BadNightlyConfig)
936 (boost::bind(&DataLogger::ConfigureNightlyFileName, this, _1))
937 ("Configure the folder for the nightly files."
938 "|Path[string]:Absolute or relative path name where the nightly files should be stored.");
939
940 AddEvent(fConfigRun, "C", kSM_Ready, kSM_BadNightlyConfig, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig)
941 (boost::bind(&DataLogger::ConfigureRunFileName, this, _1))
942 ("Configure the folder for the run files."
943 "|Path[string]:Absolute or relative path name where the run files should be stored.");
944
945 AddEvent(fConfigRunNumber, "I", kSM_Ready, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig, kSM_Logging)
946 (boost::bind(&DataLogger::ConfigureRunNumber, this, _1))
947 ("configure the run number. cannot be done in logging state");
948
949 //Provide a logging command
950 //I get the feeling that I should be going through the EventImp
951 //instead of DimCommand directly, mainly because the commandHandler
952 //is already done in StateMachineImp.cc
953 //Thus I'll simply add a configuration, which I will treat as the logging command
954// AddEvent(fConfigLog, "C", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig)
955 // (boost::bind(&DataLogger::LogMessagePlease, this, _1))
956 // ("Log a single message to the log-files."
957// "|Message[string]:Message to be logged.");
958
959 //Provide a print command
960 ostringstream str;
961 str << kSM_Ready << " " << kSM_NightlyOpen << " " << kSM_WaitingRun << " " << kSM_Logging << " " << kSM_BadNightlyConfig;
962 str << " " << kSM_BadRunConfig;
963 AddEvent(fPrintCommand, str.str().c_str(), "")
964 (boost::bind(&DataLogger::PrintStatePlease, this, _1))
965 ("Print information about the internal status of the data logger.");
966
967 fServiceList.SetHandler(this);
968 CheckForServicesUpdate();
969
970 //start the monitoring service
971 fContinueMonitoring = true;
972 fMonitoringThread = boost::thread(boost::bind(&DataLogger::ServicesMonitoring, this));
973 fBaseSizeNightly = 0;
974 fBaseSizeRun = 0;
975 OpenFileToDim fToDim;
976 fToDim.code = 0;
977 fToDim.fileName[0] = '\0';
978
979 fOpenedNightlyFiles = new DimDescribedService(GetName() + "/FILENAME_NIGHTLY", "I:1;C", fToDim,
980 "Path and base name which is used to compile the filenames for the nightly files."
981 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
982 "|Name[string]:path and base file name");
983
984 fOpenedRunFiles = new DimDescribedService(GetName() + "/FILENAME_RUN", "I:1;C", fToDim,
985 "Path and base name which is used to compile the filenames for the run files."
986 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
987 "|Name[string]:path and base file name");
988
989 fNumSubAndFitsData.numSubscriptions = 0;
990 fNumSubAndFitsData.numOpenFits = 0;
991 fNumSubAndFits = new DimDescribedService(GetName() + "/NUM_SUBS", "I:2", fNumSubAndFitsData,
992 "Shows number of services to which the data logger is currently subscribed and the total number of open files."
993 "|Subscriptions[int]:number of dim services to which the data logger is currently subscribed."
994 "|NumOpenFiles[int]:number of files currently open by the data logger");
995
996 //black/white list
997 fHasBlackList = false;
998 fHasWhiteList = false;
999 fBlackList.clear();
1000 fWhiteList.clear();
1001 //services parameters
1002 fDebugIsOn = false;
1003 fStatsPeriodDuration = 1.0f;
1004 fOpenedFilesIsOn = true;
1005 fNumSubAndFitsIsOn = true;
1006
1007 //provide services control commands
1008 AddEvent(fDebugOnOff, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1009 (boost::bind(&DataLogger::SetDebugOnOff, this, _1))
1010 ("Switch debug mode on off. Debug mode prints ifnormation about every service written to a file."
1011 "|Enable[bool]:Enable of disable debuig mode (yes/no).");
1012
1013 AddEvent(fStatsPeriod, "F", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1014 (boost::bind(&DataLogger::SetStatsPeriod, this, _1))
1015 ("Interval in which the data-logger statitistics service (STATS) is updated."
1016 "Interval[s]:Floating point value in seconds.");
1017
1018 AddEvent(fStartStopOpenedFiles, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1019 (boost::bind(&DataLogger::SetOpenedFilesOnOff ,this, _1))
1020 ("Can be used to switch the service off which distributes information about the open files.");
1021
1022 AddEvent(fStartStopNumSubsAndFits, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1023 (boost::bind(&DataLogger::SetNumSubsAndFitsOnOff, this, _1))
1024 ("Can be used to switch the service off which distributes information about the number of subscriptions and open files.");
1025
1026 fDestructing = false;
1027 if(fDebugIsOn)
1028 {
1029 Debug("DataLogger Init Done.");
1030 }
1031}
1032// --------------------------------------------------------------------------
1033//
1034//! Checks for changes in the existing services.
1035//! Any new service will be added to the service list, while the ones which disappeared are removed.
1036//
1037//FIXME The service must be udpated so that I get the first notification. This should not be
1038bool DataLogger::CheckForServicesUpdate()
1039{
1040 bool serviceUpdated = false;
1041 //get the current server list
1042 const vector<string> serverList = fServiceList.GetServerList();
1043 //first let's remove the servers that may have disapeared
1044 //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
1045 vector<string> toBeDeleted;
1046 for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
1047 {
1048 vector<string>::const_iterator givenServers;
1049 for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
1050 if (cListe->first == *givenServers)
1051 break;
1052 if (givenServers == serverList.end())//server vanished. Remove it
1053 {
1054 toBeDeleted.push_back(cListe->first);
1055 serviceUpdated = true;
1056 }
1057 }
1058 for (vector<string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
1059 fServiceSubscriptions.erase(*it);
1060 //now crawl through the list of servers, and see if there was some updates
1061 for (vector<string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
1062 {
1063 //skip the two de-fact excluded services
1064 //Dim crashes if the publisher subscribes to its own service. This sounds weird, I agree.
1065 //I agree: hard coded values are bad, but in this case these two entries should always be here otherwise Dim crashes.
1066 if ((i->find("DIS_DNS") != string::npos) ||
1067 (i->find("DATA_LOGGER") != string::npos))
1068 continue;
1069 //find the current server in our subscription list
1070 SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
1071 //get the service list of the current server
1072 vector<string> cServicesList = fServiceList.GetServiceList(*i);
1073 if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
1074 { //then check and update our list of subscriptions
1075 //first, remove the services that may have disapeared.
1076 map<string, SubscriptionType>::iterator serverSubs;
1077 vector<string>::const_iterator givenSubs;
1078 toBeDeleted.clear();
1079 for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
1080 {
1081 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
1082 if (serverSubs->first == *givenSubs)
1083 break;
1084 if (givenSubs == cServicesList.end())
1085 {
1086 toBeDeleted.push_back(serverSubs->first);
1087 serviceUpdated = true;
1088 }
1089 }
1090 for (vector<string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
1091 cSubs->second.erase(*it);
1092 //now check for new services
1093 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
1094 {
1095 if (!ShouldSubscribe(*i, *givenSubs))
1096 continue;
1097 if (cSubs->second.find(*givenSubs) == cSubs->second.end())
1098 {//service not found. Add it
1099 cSubs->second[*givenSubs].dimInfo = SubscribeToPlease(*i, *givenSubs);
1100 serviceUpdated = true;
1101 }
1102 }
1103 }
1104 else //server not found in our list. Create its entry
1105 {
1106 fServiceSubscriptions[*i] = map<string, SubscriptionType>();
1107 map<string, SubscriptionType>& liste = fServiceSubscriptions[*i];
1108 for (vector<string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
1109 {
1110 if (!ShouldSubscribe(*i, *j))
1111 continue;
1112 liste[*j].dimInfo = SubscribeToPlease(*i, *j);
1113 serviceUpdated = true;
1114 }
1115 }
1116 }
1117 return serviceUpdated;
1118}
1119// --------------------------------------------------------------------------
1120//
1121//! Destructor
1122//
1123DataLogger::~DataLogger()
1124{
1125 if (fDebugIsOn)
1126 {
1127 Debug("DataLogger destruction starts");
1128 }
1129 fDestructing = true;
1130 //first let's go to the ready state
1131 GoToReadyPlease();
1132 //release the services subscriptions
1133 fServiceSubscriptions.clear();
1134 //exit the monitoring loop
1135 fContinueMonitoring = false;
1136
1137 fMonitoringThread.join();
1138 //clear any remaining run number (should remain only one)
1139 while (fRunNumber.size() > 0)
1140 {
1141 RemoveOldestRunNumber();
1142 }
1143
1144 delete fOpenedNightlyFiles;
1145 delete fOpenedRunFiles;
1146 delete fNumSubAndFits;
1147
1148 if (fDebugIsOn)
1149 {
1150 Debug("DataLogger desctruction ends");
1151 }
1152}
1153
1154// --------------------------------------------------------------------------
1155//
1156//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
1157//
1158void DataLogger::infoHandler()
1159{
1160 // Make sure getTimestamp is called _before_ getTimestampMillisecs
1161 if (fDestructing)
1162 return;
1163
1164 DimInfo* I = getInfo();
1165 SubscriptionsListType::iterator x;
1166 map<string, SubscriptionType>::iterator y;
1167 if (I==NULL)
1168 {
1169 if (CheckForServicesUpdate())
1170 {
1171 //services were updated. Notify
1172 fNumSubAndFitsData.numSubscriptions = 0;
1173 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1174 fNumSubAndFitsData.numSubscriptions += x->second.size();
1175 if (fNumSubAndFitsIsOn)
1176 {
1177 if (fDebugIsOn)
1178 {
1179 ostringstream str;
1180 str << "Updating number of subscriptions service: Num Subs=" << fNumSubAndFitsData.numSubscriptions << " Num open FITS=" << fNumSubAndFitsData.numOpenFits;
1181 Debug(str);
1182 }
1183 fNumSubAndFits->updateService();
1184 }
1185 }
1186 return;
1187 }
1188 //check if the service pointer corresponds to something that we subscribed to
1189 //this is a fix for a bug that provides bad Infos when a server starts
1190 bool found = false;
1191 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1192 {//find current service is subscriptions
1193 for (y=x->second.begin(); y!=x->second.end();y++)
1194 if (y->second.dimInfo == I)
1195 {
1196 found = true;
1197 break;
1198 }
1199 if (found)
1200 break;
1201 }
1202 if (!found)
1203 return;
1204 if (I->getSize() <= 0)
1205 return;
1206
1207 // Make sure that getTimestampMillisecs is NEVER called before
1208 // getTimestamp is properly called
1209 // check that the message has been updated by something, i.e. must be different from its initial value
1210 if (I->getTimestamp() == 0)
1211 return;
1212
1213 CheckForRunNumber(I);
1214
1215 ReportPlease(I, y->second);
1216
1217}
1218// --------------------------------------------------------------------------
1219//
1220//! Open the text files associated with the given run number
1221//! @param run the run number to be dealt with
1222//
1223int DataLogger::OpenRunFile(RunNumberType& run)
1224{
1225 if (run.logFile->is_open())
1226 {
1227 ostringstream str;
1228 str << "Log file " << run.logName << " was already open when trying to open it in OpenRunFile";
1229 Error(str);
1230 return -1;
1231 }
1232 FormRunTextFileName(run.logName, false, run.runNumber);
1233 run.logFile->open(run.logName.c_str(), ios_base::out | ios_base::app);
1234 if (errno != 0)
1235 {
1236 ostringstream str;
1237 str << "Unable to open run Log " << run.logName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1238 Error(str);
1239 }
1240 //open report file
1241 FormRunTextFileName(run.reportName, true, run.runNumber);
1242 if (run.reportFile->is_open())
1243 {
1244 ostringstream str;
1245 str << "Report file " << run.reportName << " was already open when trying to open it in OpenRunFile";
1246 Error(str);
1247 return -1;
1248 }
1249 run.reportFile->open(run.reportName.c_str(), ios_base::out | ios_base::app);
1250 if (errno != 0)
1251 {
1252 ostringstream str;
1253 str << "Unable to open run report " << run.reportName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1254 Error(str);
1255 }
1256
1257 if (!run.logFile->is_open() || !run.reportFile->is_open())
1258 {
1259 ostringstream str;
1260 str << "Something went wrong while openning nightly files " << run.logName << " and " << run.reportName;
1261 Error(str);
1262 return -1;
1263 }
1264 //get the size of the newly opened file.
1265 RememberFileOrigSizePlease(run.logName, false);
1266 RememberFileOrigSizePlease(run.reportName, false);
1267
1268 //TODO this notification scheme might be messed up now.... fix it !
1269 ostringstream sRun;
1270 sRun << run.runNumber;
1271 NotifyOpenedFile(CheckIfDirIsDot(fRunFilePath) + '/' + sRun.str(), 3, fOpenedRunFiles);
1272 run.openedFits.clear();
1273 return 0;
1274}
1275// --------------------------------------------------------------------------
1276//
1277//! Add a new active run number
1278//! @param newRun the new run number
1279//! @param time the time at which the new run number was issued
1280//
1281void DataLogger::AddNewRunNumber(int newRun, Time time)
1282{
1283 if (fDebugIsOn)
1284 {
1285 ostringstream str;
1286 str << "Adding new run number " << newRun << " that was issued on " << time;
1287 Debug(str);
1288 }
1289 //Add new run number to run number list
1290 fRunNumber.push_back(RunNumberType());
1291 fRunNumber.back().runNumber = newRun;
1292 fRunNumber.back().time = time;
1293 if (GetCurrentState() != kSM_Logging)
1294 return;
1295 //open the log and report files
1296 OpenRunFile(fRunNumber.back());
1297}
1298// --------------------------------------------------------------------------
1299//
1300//! Checks whether or not the current info is a run number.
1301//! If so, then remember it. A run number is required to open the run-log file
1302//! @param I
1303//! the current DimInfo
1304//
1305void DataLogger::CheckForRunNumber(DimInfo* I)
1306{
1307 if (strstr(I->getName(), fRunNumberInfo) != NULL)
1308 {//assumes that the run number is an integer
1309 AddNewRunNumber(I->getInt(), Time(I->getTimestamp(), I->getTimestampMillisecs()*1000));
1310 ostringstream str;
1311 str << "New run number is " << fRunNumber.back().runNumber;
1312 Message(str);
1313 }
1314}
1315
1316// --------------------------------------------------------------------------
1317//
1318//! write infos to log files.
1319//! @param I
1320//! The current DimInfo
1321//! @param sub
1322//! The dataLogger's subscription corresponding to this DimInfo
1323//
1324void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
1325{
1326 //should we log or report this info ? (i.e. is it a message ?)
1327 bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
1328 if (I->getFormat()[0] == 'C')
1329 isItaReport = false;
1330
1331 if (!fNightlyReportFile.is_open())
1332 return;
1333
1334 //create the converter for that service
1335 if (sub.fConv == NULL && isItaReport)
1336 {
1337 //trick the converter in case of 'C'. why do I do this ? well simple: the converter checks that the right number
1338 //of bytes was written. because I skip 'C' with fits, the bytes will not be allocated, hence the "size copied ckeck"
1339 //of the converter will fail, hence throwing an exception.
1340 string fakeFormat(I->getFormat());
1341 if (fakeFormat[fakeFormat.size()-1] == 'C')
1342 fakeFormat = fakeFormat.substr(0, fakeFormat.size()-1);
1343 sub.fConv = new Converter(Out(), I->getFormat());
1344 if (!sub.fConv)
1345 {
1346 ostringstream str;
1347 str << "Couldn't properly parse the format... service " << sub.dimInfo->getName() << " ignored.";
1348 Error(str);
1349 return;
1350 }
1351 }
1352 //construct the header
1353 ostringstream header;
1354 Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
1355 fQuality = I->getQuality();
1356 fMjD = cTime.Mjd();
1357
1358 //figure out which run file should be used
1359 ofstream* targetRunFile = NULL;
1360 RunNumberType* cRunNumber = NULL;
1361 if (GetCurrentState() == kSM_Logging)
1362 {
1363 list<RunNumberType>::reverse_iterator rit;
1364 for (rit=fRunNumber.rbegin(); rit != fRunNumber.rend(); rit++)
1365 {
1366 if (rit->time < cTime) //this is the run number that we want to use
1367 {
1368 //Find something better to convert iterator to pointer than the ugly line below....
1369 cRunNumber = &(*rit);
1370 sub.runNumber = rit->runNumber;
1371 targetRunFile = isItaReport ? rit->reportFile : rit->logFile;
1372 break;
1373 }
1374 }
1375 if (rit == fRunNumber.rend() && fRunNumber.size() != 0)
1376 {
1377 ostringstream str;
1378 str << "Could not find an appropriate run number for info coming at time: " << cTime;
1379 Error(str);
1380 Error("Active run numbers: ");
1381 for (rit=fRunNumber.rbegin(); rit != fRunNumber.rend(); rit++)
1382 {
1383 str.str("");
1384 str << rit->runNumber;
1385 Error(str);
1386 }
1387 }
1388 }
1389
1390 if (isItaReport)
1391 {
1392 //write text header
1393 header << I->getName() << " " << fQuality << " ";
1394 header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1395 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1396 header << cTime.ms() << " " << I->getTimestamp() << " ";
1397
1398 string text;
1399 try
1400 {
1401 text = sub.fConv->GetString(I->getData(), I->getSize());
1402 }
1403 catch (const runtime_error &e)
1404 {
1405 Out() << kRed << e.what() << endl;
1406 ostringstream str;
1407 str << "Could not properly parse the data for service " << sub.dimInfo->getName();
1408 str << " reason: " << e.what() << ". Entry ignored";
1409 Error(str);
1410 return;
1411 }
1412
1413 if (text.empty())
1414 {
1415 ostringstream str;
1416 str << "Service " << sub.dimInfo->getName() << " sent an empty string";
1417 Info(str);
1418 return;
1419 }
1420 //replace bizarre characters by white space
1421 replace(text.begin(), text.end(), '\n', '\\');
1422 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1423
1424 //write entry to Nightly report
1425 if (fNightlyReportFile.is_open())
1426 {
1427 fNightlyReportFile << header.str() << text << endl;
1428 //check if either eof, bailbit or batbit are set
1429 if (!fNightlyReportFile.good())
1430 {
1431 Error("An error occured while writing to the nightly report file. Closing it");
1432 if (fNightlyReportFile.is_open())
1433 fNightlyReportFile.close();
1434 }
1435 }
1436 //write entry to run-report
1437 if (targetRunFile && targetRunFile->is_open())
1438 {
1439 *targetRunFile << header.str() << text << endl;
1440 if (!targetRunFile->good())
1441 {
1442 Error("An error occured while writing to the run report file. Closing it.");
1443 if (targetRunFile->is_open())
1444 targetRunFile->close();
1445 }
1446 }
1447 }
1448 else
1449 {//write entry to both Nightly and run logs
1450 string n = I->getName();
1451 ostringstream msg;
1452 msg << n << ": " << I->getString();
1453
1454 if (fNightlyLogFile.is_open())
1455 {
1456 MessageImp nightlyMess(fNightlyLogFile);
1457 nightlyMess.Write(cTime, msg.str().c_str(), fQuality);
1458 if (!fNightlyLogFile.good())
1459 {
1460 Error("An error occured while writing to the nightly log file. Closing it.");
1461 if (fNightlyLogFile.is_open())
1462 fNightlyLogFile.close();
1463 }
1464 }
1465 if (targetRunFile && targetRunFile->is_open())
1466 {
1467 MessageImp runMess(*targetRunFile);
1468 runMess.Write(cTime, msg.str().c_str(), fQuality);
1469 if (!targetRunFile->good())
1470 {
1471 Error("An error occured while writing to the run log file. Closing it.");
1472 if (targetRunFile->is_open())
1473 targetRunFile->close();
1474 }
1475 }
1476 }
1477
1478#ifdef HAVE_FITS
1479 if (isItaReport)
1480 {
1481 if (!sub.nightlyFile.IsOpen() || !sub.runFile.IsOpen() || sub.runNumber != sub.runFile.fRunNumber)
1482 OpenFITSFilesPlease(sub, cRunNumber);
1483 WriteToFITS(sub);
1484 }
1485#endif
1486
1487}
1488
1489// --------------------------------------------------------------------------
1490//
1491//! write messages to logs.
1492/*
1493//! @param evt
1494//! the current event to log
1495//! @returns
1496//! the new state. Currently, always the current state
1497//!
1498//! @deprecated
1499//! I guess that this function should not be any longer
1500//
1501//Otherwise re-write it properly with the MessageImp class
1502int DataLogger::LogMessagePlease(const Event& evt)
1503{
1504 if (!fNightlyLogFile.is_open())
1505 return GetCurrentState();
1506 Warn("LogMessagePlease has not been checked nor updated since a long while. Undefined behavior to be expected");
1507 ostringstream header;
1508 const Time& cTime = evt.GetTime();
1509 header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1510 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1511 header << cTime.ms() << " ";
1512
1513 const Converter conv(Out(), evt.GetFormat());
1514 if (!conv)
1515 {
1516 Error("Couldn't properly parse the format... ignored.");
1517 return GetCurrentState();
1518 }
1519
1520 string text;
1521 try
1522 {
1523 text = conv.GetString(evt.GetData(), evt.GetSize());
1524 }
1525 catch (const runtime_error &e)
1526 {
1527 Out() << kRed << e.what() << endl;
1528 Error("Couldn't properly parse the data... ignored.");
1529 return GetCurrentState();
1530 }
1531
1532 if (text.empty())
1533 return GetCurrentState();
1534
1535 //replace bizarre characters by white space
1536 replace(text.begin(), text.end(), '\n', '\\');
1537 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1538 if (fDebugIsOn)
1539 {
1540 ostringstream str;
1541 str << "Logging: \"" << header << text << "\"";
1542 Debug(str.str());
1543 }
1544
1545 if (fNightlyLogFile.is_open())
1546 {
1547 fNightlyLogFile << header;
1548 if (!fNightlyLogFile.good())
1549 {
1550 Error("An error occured while writing to the run log file. Closing it.");
1551 if (fNightlyLogFile.is_open())
1552 fNightlyLogFile.close();
1553 }
1554 }
1555 if (fRunLogFile.is_open())
1556 {
1557 fRunLogFile << header;
1558 if (!fRunLogFile.good())
1559 {
1560 Error("An error occured while writing to the run log file. Closing it.");
1561 if (fRunLogFile.is_open())
1562 fRunLogFile.close();
1563 }
1564 }
1565 if (fNightlyLogFile.is_open())
1566 {
1567 fNightlyLogFile << text;
1568 if (!fNightlyLogFile.good())
1569 {
1570 Error("An error occured while writing to the run log file. Closing it.");
1571 if (fNightlyLogFile.is_open())
1572 fNightlyLogFile.close();
1573 }
1574 }
1575 if (fRunLogFile.is_open())
1576 {
1577 fRunLogFile << text;
1578 if (!fRunLogFile.good())
1579 {
1580 Error("An error occured while writing to the run log file. Closing it.");
1581 if (fRunLogFile.is_open())
1582 fRunLogFile.close();
1583 }
1584 }
1585 return GetCurrentState();
1586}*/
1587// --------------------------------------------------------------------------
1588//
1589//! print the dataLogger's current state. invoked by the PRINT command
1590//! @param evt
1591//! the current event. Not used by the method
1592//! @returns
1593//! the new state. Which, in that case, is the current state
1594//!
1595int DataLogger::PrintStatePlease(const Event& )
1596{
1597 Message("-----------------------------------------");
1598 Message("------ DATA LOGGER CURRENT STATE --------");
1599 Message("-----------------------------------------");
1600 //print the path configuration
1601 Message("Nightly Path: " + CheckIfDirIsDot(fNightlyFilePath));
1602 Message("Run Path: " + CheckIfDirIsDot(fRunFilePath));
1603
1604 //print active run numbers
1605 ostringstream str;
1606 str << "Active Run Numbers: ";
1607 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1608 str << "\n" << it->runNumber;
1609 Message(str);
1610 Message("----------- OPENED FILES ----------------");
1611 //print all the open files.
1612 if (fNightlyLogFile.is_open())
1613 Message("Nightly Log..........OPEN");
1614 else
1615 Message("Nightly log........CLOSED");
1616 if (fNightlyReportFile.is_open())
1617 Message("Nightly Report.......OPEN");
1618 else
1619 Message("Nightly Report.....CLOSED");
1620 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1621 {
1622 if (it->logFile->is_open())
1623 Message("Run Log " + it->logName + " is OPEN");
1624 else
1625 Message("Run Log " + it->logName + " is CLOSED");
1626 if (it->reportFile->is_open())
1627 Message("Run Report " + it->reportName + " is OPEN");
1628 else
1629 Message("Run Report " + it->reportName + " CLOSED");
1630 }
1631 bool statWarning = false;
1632 DataLoggerStats statVar;
1633 calculateTotalSizeWritten(statVar, statWarning, false);
1634 Message("---------------- STATS ------------------");
1635 str.str("");
1636 str << "Total Size written: " << statVar.sizeWritten << " bytes.";
1637 Message(str);
1638 str.str("");
1639 str << "Disk free space: " << statVar.freeSpace << " bytes.";
1640 Message(str);
1641 str.str("");
1642 str << "Statistics are updated every " << fStatsPeriodDuration << " seconds";
1643 if (fStatsPeriodDuration != 0)
1644 Message(str);
1645 else
1646 Message("Statistics updates are currently disabled");
1647 Message("----------- DIM SUBSCRIPTIONS -----------");
1648
1649 str.str("");
1650 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions:";
1651 Message(str);
1652
1653 for (map<const string, map<string, SubscriptionType> >::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1654 {
1655 Message("Server "+it->first);
1656 for (map<string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1657 Message(" -> "+it2->first);
1658 }
1659 if (fHasBlackList)
1660 {
1661 Message("------------- BLOCK LIST ----------------");
1662 for (set<string>::iterator it=fBlackList.begin(); it != fBlackList.end(); it++)
1663 Message(*it);
1664 }
1665 if (fHasWhiteList)
1666 {
1667 Message("----------- ALLOW LIST ------------------");
1668 for (set<string>::iterator it=fWhiteList.begin(); it != fWhiteList.end(); it++)
1669 Message(*it);
1670 }
1671 if (fGrouping.size() != 0)
1672 {
1673 Message("--------- GROUPING LIST -----------------");
1674 Message("The following servers and/or services will be grouping under a single run fits file:");
1675 for (set<string>::iterator it=fGrouping.begin(); it != fGrouping.end(); it++)
1676 Message(*it);
1677 }
1678 Message("-----------------------------------------");
1679 Message("------ END OF DATA LOGGER STATE ---------");
1680 Message("-----------------------------------------");
1681
1682 return GetCurrentState();
1683}
1684
1685// --------------------------------------------------------------------------
1686//
1687//! turn debug mode on and off
1688//! @param evt
1689//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1690//! @returns
1691//! the new state. Which, in that case, is the current state
1692//!
1693int DataLogger::SetDebugOnOff(const Event& evt)
1694{
1695 bool backupDebug = fDebugIsOn;
1696 fDebugIsOn = evt.GetBool();
1697 if (fDebugIsOn == backupDebug)
1698 Warn("Warning: debug mode was already in the requested state");
1699 else
1700 {
1701 ostringstream str;
1702 str << "Debug mode is now " << fDebugIsOn;
1703 Message(str);
1704 }
1705 return GetCurrentState();
1706}
1707// --------------------------------------------------------------------------
1708//
1709//! set the statistics update period duration. 0 disables the statistics
1710//! @param evt
1711//! the current event. contains the new duration.
1712//! @returns
1713//! the new state. Which, in that case, is the current state
1714//!
1715int DataLogger::SetStatsPeriod(const Event& evt)
1716{
1717 float backupDuration = fStatsPeriodDuration;
1718 fStatsPeriodDuration = evt.GetFloat();
1719 if (fStatsPeriodDuration < 0)
1720 {
1721 Error("Statistics period duration should be greater than zero. Discarding provided value.");
1722 fStatsPeriodDuration = backupDuration;
1723 return GetCurrentState();
1724 }
1725 if (!finite(fStatsPeriodDuration))// != fStatsPeriodDuration)
1726 {
1727 Error("Provided duration does not appear to be a valid float. discarding it.");
1728 fStatsPeriodDuration = backupDuration;
1729 return GetCurrentState();
1730 }
1731 if (backupDuration == fStatsPeriodDuration)
1732 Warn("Warning: statistics period was not modified: supplied value already in use");
1733 else
1734 {
1735 if (fStatsPeriodDuration == 0.0f)
1736 Message("Statistics are now OFF");
1737 else
1738 {
1739 ostringstream str;
1740 str << "Statistics period is now " << fStatsPeriodDuration << " seconds";
1741 Message(str);
1742 }
1743 }
1744 return GetCurrentState();
1745}
1746// --------------------------------------------------------------------------
1747//
1748//! set the opened files service on or off.
1749//! @param evt
1750//! the current event. contains the instruction string. similar to setdebugonoff
1751//! @returns
1752//! the new state. Which, in that case, is the current state
1753//!
1754int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1755{
1756 bool backupOpened = fOpenedFilesIsOn;
1757 fOpenedFilesIsOn = evt.GetBool();
1758 if (fOpenedFilesIsOn == backupOpened)
1759 Warn("Warning: opened files service mode was already in the requested state");
1760 else
1761 {
1762 ostringstream str;
1763 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1764 Message(str);
1765 }
1766 return GetCurrentState();
1767
1768}
1769// --------------------------------------------------------------------------
1770//
1771//! set the number of subscriptions and opened fits on and off
1772//! @param evt
1773//! the current event. contains the instruction string. similar to setdebugonoff
1774//! @returns
1775//! the new state. Which, in that case, is the current state
1776//!
1777int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1778{
1779 bool backupSubs = fNumSubAndFitsIsOn;
1780 fNumSubAndFitsIsOn = evt.GetBool();
1781 if (fNumSubAndFitsIsOn == backupSubs)
1782 Warn("Warning: Number of subscriptions service mode was already in the requested state");
1783 else
1784 {
1785 ostringstream str;
1786 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1787 Message(str);
1788 }
1789 return GetCurrentState();
1790}
1791// --------------------------------------------------------------------------
1792//
1793//! Sets the path to use for the Nightly log file.
1794//! @param evt
1795//! the event transporting the path
1796//! @returns
1797//! currently only the current state.
1798//
1799int DataLogger::ConfigureNightlyFileName(const Event& evt)
1800{
1801 if (evt.GetText() != NULL)
1802 {
1803 fNightlyFilePath = string(evt.GetText());
1804 Message("New Nightly folder specified: " + fNightlyFilePath);
1805 }
1806 else
1807 Error("Empty Nightly folder given. Please specify a valid path.");
1808
1809 return GetCurrentState();
1810}
1811// --------------------------------------------------------------------------
1812//
1813//! Sets the path to use for the run log file.
1814//! @param evt
1815//! the event transporting the path
1816//! @returns
1817//! currently only the current state
1818int DataLogger::ConfigureRunFileName(const Event& evt)
1819{
1820 if (evt.GetText() != NULL)
1821 {
1822 fRunFilePath = string(evt.GetText());
1823 Message("New Run folder specified: " + fRunFilePath);
1824 }
1825 else
1826 Error("Empty Nightly folder given. Please specify a valid path");
1827
1828 return GetCurrentState();
1829}
1830// --------------------------------------------------------------------------
1831//
1832//! Sets the run number.
1833//! @param evt
1834//! the event transporting the run number
1835//! @returns
1836//! currently only the current state
1837int DataLogger::ConfigureRunNumber(const Event& evt)
1838{
1839 AddNewRunNumber(evt.GetInt(), evt.GetTime());
1840// fRunNumber = evt.GetInt();
1841 ostringstream str;
1842 str << "The new run number is: " << fRunNumber.back().runNumber;
1843 Message(str);
1844 return GetCurrentState();
1845}
1846// --------------------------------------------------------------------------
1847//
1848//! Notifies the DIM service that a particular file was opened
1849//! @ param name the base name of the opened file, i.e. without path nor extension.
1850//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1851//! this is not a problem though because file are not opened so often.
1852//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1853inline void DataLogger::NotifyOpenedFile(string name, int type, DimDescribedService* service)
1854{
1855 if (fOpenedFilesIsOn)
1856 {
1857 if (fDebugIsOn)
1858 {
1859 ostringstream str;
1860 str << "Updating files service " << service->getName() << "with code: " << type << " and file: " << name;
1861 Debug(str);
1862 str.str("");
1863 str << "Num subs: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS: " << fNumSubAndFitsData.numOpenFits;
1864 Debug(str);
1865 }
1866 OpenFileToDim fToDim;
1867 fToDim.code = type;
1868 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1869 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(int));
1870 service->setQuality(0);
1871 service->updateService();
1872 }
1873}
1874// --------------------------------------------------------------------------
1875//
1876//! Implements the Start transition.
1877//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1878//! and tries to open it.
1879//! @returns
1880//! kSM_NightlyOpen if success, kSM_BadNightlyConfig if failure
1881int DataLogger::StartPlease()
1882{
1883 if (fDebugIsOn)
1884 {
1885 Debug("Starting...");
1886 }
1887
1888 FormNightlyTextFileName(fFullNightlyLogFileName, false);
1889 OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName);
1890
1891 FormNightlyTextFileName(fFullNightlyReportFileName, true);
1892 OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName);
1893
1894 if (!fNightlyLogFile.is_open() || !fNightlyReportFile.is_open())
1895 {
1896 ostringstream str;
1897 str << "Something went wrong while openning nightly files " << fFullNightlyLogFileName << " and " << fFullNightlyReportFileName;
1898 Error(str);
1899 return kSM_BadNightlyConfig;
1900 }
1901 //get the size of the newly opened file.
1902 fBaseSizeNightly = GetFileSize(fFullNightlyLogFileName);
1903 fBaseSizeNightly += GetFileSize(fFullNightlyReportFileName);
1904 fFileSizesMap.clear();
1905 fBaseSizeRun = 0;
1906 fPreviousSize = 0;
1907
1908 //notify that a new file has been opened.
1909 Time time;
1910 ostringstream sTime;
1911 sTime << time.Y() << "_" << time.M() << "_" << time.D();
1912 NotifyOpenedFile(CheckIfDirIsDot(fNightlyFilePath) + '/' + sTime.str(), 3, fOpenedNightlyFiles);
1913
1914 fOpenedNightlyFits.clear();
1915
1916 return kSM_NightlyOpen;
1917}
1918
1919#ifdef HAVE_FITS
1920// --------------------------------------------------------------------------
1921//
1922//! open if required a the FITS files corresponding to a given subscription
1923//! @param sub
1924//! the current DimInfo subscription being examined
1925void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber)
1926{
1927 string serviceName(sub.dimInfo->getName());
1928 //if run number has changed, reopen a new fits file with the correct run number.
1929 if (sub.runFile.IsOpen() && sub.runFile.fRunNumber != sub.runNumber)
1930 {
1931 if (fDebugIsOn)
1932 Debug("Run number changed. Closing " + sub.runFile.fFileName);
1933 sub.runFile.Close();
1934 }
1935 //we must check if we should group this service subscription to a single fits file before we replace the / by _
1936 bool hasGrouping = false;
1937 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1938 {//will we find this service in the grouping list ?
1939 for (set<string>::iterator it=fGrouping.begin(); it!=fGrouping.end(); it++)
1940 {
1941 if (serviceName.find(*it) != string::npos)
1942 {
1943 hasGrouping = true;
1944 break;
1945 }
1946 }
1947 }
1948 hasGrouping = true;
1949 for (unsigned int i=0;i<serviceName.size(); i++)
1950 {
1951 if (serviceName[i] == '/')
1952 {
1953 serviceName[i] = '_';
1954 break;
1955 }
1956 }
1957 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1958 if (!sub.nightlyFile.IsOpen())
1959 {
1960 string fileNameOnly, partialName;
1961 FormNightlyFitsFileName(fileNameOnly, partialName, serviceName);
1962 AllocateFITSBuffers(sub);
1963 //get the size of the file we're about to open
1964 if (RememberFileOrigSizePlease(partialName, true))//and remember that the file was opened (i.e. not an update)
1965 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
1966
1967 sub.nightlyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, -1);//Out());
1968
1969 //notify the opening
1970 Time time;
1971 ostringstream sTime;
1972 sTime << time.Y() << "_" << time.M() << "_" << time.D();
1973 NotifyOpenedFile(CheckIfDirIsDot(fNightlyFilePath) + '/' + sTime.str(), 7, fOpenedNightlyFiles);
1974 if (fNumSubAndFitsIsOn)
1975 fNumSubAndFits->updateService();
1976 if (fDebugIsOn)
1977 {
1978 ostringstream str;
1979 str << "Opened Nightly FITS: " << partialName << " and table: FACT-" << serviceName << ".current number of opened FITS: " << fNumSubAndFitsData.numOpenFits;
1980 Debug(str);
1981 }
1982 }
1983 //do the actual file open
1984 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging) && sub.runNumber != -1)
1985 {//buffer for the run file have already been allocated when doing the Nightly file
1986 string fileNameOnly;
1987 string partialName;
1988
1989 if (hasGrouping)
1990 {
1991 FormGroupedRunFitsFileName(fileNameOnly, partialName, sub.runNumber);
1992 }
1993 else
1994 {
1995 FormRunFitsFileName(fileNameOnly, partialName, serviceName, sub.runNumber);
1996 }
1997 //get the size of the file we're about to open
1998 if (RememberFileOrigSizePlease(partialName, false))//and remember that the file was opened (i.e. not an update)
1999 cRunNumber->openedFits[fileNameOnly].push_back(serviceName);
2000 else
2001 if (hasGrouping)
2002 {//most likely I should add this service name.
2003 //the only case for which I should not add it is if a service disapeared, hence the file was closed
2004 //and reopened again. Unlikely to happen, but well it may
2005 bool found = false;
2006 for (vector<string>::iterator it=cRunNumber->openedFits[fileNameOnly].begin(); it!=cRunNumber->openedFits[fileNameOnly].end(); it++)
2007 if (*it == serviceName)
2008 {
2009 found = true;
2010 break;
2011 }
2012 if (!found)
2013 cRunNumber->openedFits[fileNameOnly].push_back(serviceName);
2014 }
2015
2016 if (hasGrouping && cRunNumber->runFitsFile == NULL)
2017 try
2018 {
2019 cRunNumber->runFitsFile = new CCfits::FITS(partialName, CCfits::RWmode::Write);
2020 (fNumSubAndFitsData.numOpenFits)++;
2021 }
2022 catch (CCfits::FitsException e)
2023 {
2024 ostringstream str;
2025 str << "Could not open FITS Run file " << partialName << " reason: " << e.message();
2026 Error(str);
2027 cRunNumber->runFitsFile = NULL;
2028 }
2029
2030 ostringstream sRun;
2031 sRun << sub.runNumber;
2032 NotifyOpenedFile(CheckIfDirIsDot(fRunFilePath) + '/' + sRun.str(), 7, fOpenedRunFiles);// + '_' + serviceName, 4);
2033 if (hasGrouping)
2034 sub.runFile.Open(partialName, serviceName, cRunNumber->runFitsFile, &fNumSubAndFitsData.numOpenFits, this, sub.runNumber);//Out());
2035 else
2036 sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, sub.runNumber);//Out());
2037
2038 if (fNumSubAndFitsIsOn)
2039 fNumSubAndFits->updateService();
2040 if (fDebugIsOn)
2041 {
2042 ostringstream str;
2043 str << "Opened Run FITS: " << partialName << " and table: FACT-" << serviceName << ".current number of opened FITS: " << fNumSubAndFitsData.numOpenFits;
2044 Debug(str);
2045 }
2046 }
2047}
2048// --------------------------------------------------------------------------
2049//
2050//! Allocates the required memory for a given pair of fits files (nightly and run)
2051//! @param sub the subscription of interest.
2052//
2053void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
2054{
2055 int size = sub.dimInfo->getSize();
2056
2057 //Init the time columns of the file
2058 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MjD"));
2059 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2060 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2061
2062 Description QoSDesc("Qos", "Quality of service", "None");
2063 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2064 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2065
2066 const Converter::FormatList flist = sub.fConv->GetList();
2067 // Compilation failed
2068 if (!sub.fConv->valid())
2069 {
2070 Error("Compilation of format string failed.");
2071 return;
2072 }
2073
2074 //we've got a nice structure describing the format of this service's messages.
2075 //Let's create the appropriate FITS columns
2076 vector<string> dataFormatsLocal;
2077 for (unsigned int i=0;i<flist.size()-1;i++)
2078 {
2079 ostringstream dataQualifier;
2080
2081 dataQualifier << flist[i].second.first;
2082 switch (flist[i].first.first->name()[0])
2083 {
2084 case 'c':
2085 case 'C':
2086 dataQualifier.str("S");
2087 break;
2088 case 's':
2089 dataQualifier << "I";
2090 break;
2091 case 'i':
2092 case 'I':
2093 dataQualifier << "J";
2094 break;
2095 case 'l':
2096 case 'L':
2097 dataQualifier << "J";
2098 break;
2099 case 'f':
2100 case 'F':
2101 dataQualifier << "E";
2102 break;
2103 case 'd':
2104 case 'D':
2105 dataQualifier << "D";
2106 break;
2107 case 'x':
2108 case 'X':
2109 dataQualifier << "K";
2110 break;
2111 case 'S':
2112 //for strings, the number of elements I get is wrong. Correct it
2113 dataQualifier.str(""); //clear
2114 dataQualifier << size-1 << "A";
2115 size = size-1;
2116 break;
2117
2118 default:
2119 Fatal("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 1198.");
2120 };
2121 //we skip the variable length strings for now (in fits only)
2122 if (dataQualifier.str() != "S")
2123 dataFormatsLocal.push_back(dataQualifier.str());
2124 }
2125 sub.nightlyFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
2126 sub.runFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
2127}
2128// --------------------------------------------------------------------------
2129//
2130//! write a dimInfo data to its corresponding FITS files
2131//
2132void DataLogger::WriteToFITS(SubscriptionType& sub)
2133{
2134 //nightly File status (open or not) already checked
2135 if (sub.nightlyFile.IsOpen())
2136 {
2137 sub.nightlyFile.Write(sub.fConv);
2138 if (fDebugIsOn)
2139 {
2140 Debug("Writing to nightly FITS " + sub.nightlyFile.fFileName);
2141 }
2142 }
2143 if (sub.runFile.IsOpen())
2144 {
2145 sub.runFile.Write(sub.fConv);
2146 if (fDebugIsOn)
2147 {
2148 Debug("Writing to Run FITS " + sub.runFile.fFileName);
2149 }
2150 }
2151}
2152#endif //if has_fits
2153// --------------------------------------------------------------------------
2154//
2155//! Implements the StartRun transition.
2156//! Concatenates the given path for the run file and the filename itself (based on the run number),
2157//! and tries to open it.
2158//! @returns
2159//! kSM_Logging if success, kSM_BadRunConfig if failure.
2160int DataLogger::StartRunPlease()
2161{
2162 if (fDebugIsOn)
2163 {
2164 Debug("Starting Run Logging...");
2165 }
2166 //open all the relevant run-files. i.e. all the files associated with run numbers.
2167 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2168 OpenRunFile(*it);
2169
2170 return kSM_Logging;
2171}
2172
2173#ifdef HAVE_FITS
2174// --------------------------------------------------------------------------
2175//
2176//! Create a fits group file with all the run-fits that were written (either daily or run)
2177//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
2178//! single file can contain several tables to group
2179//! @param runNumber the run number that should be used for grouping. -1 means nightly group
2180//
2181void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup, int runNumber)
2182{
2183 if (fDebugIsOn)
2184 {
2185 ostringstream str;
2186 str << "Creating fits group for ";
2187 if (runNumber != -1)
2188 str << "run files";
2189 else
2190 str << "nightly files";
2191 Debug(str);
2192 }
2193 //create the FITS group corresponding to the ending run.
2194 CCfits::FITS* groupFile;
2195 unsigned int numFilesToGroup = 0;
2196 for (map<string, vector<string> >::iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
2197 {
2198 numFilesToGroup += it->second.size();
2199 }
2200 if (fDebugIsOn)
2201 {
2202 ostringstream str;
2203 str << "There are " << numFilesToGroup << " tables to group";
2204 Debug(str);
2205 }
2206 if (numFilesToGroup <= 1)
2207 {
2208 filesToGroup.clear();
2209 return;
2210 }
2211 string groupName;
2212 FormFitsGroupingFileName(groupName, runNumber);
2213 CCfits::Table* groupTable;
2214 int maxCharLength = 50;//FILENAME_MAX;
2215 try
2216 {
2217 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
2218 //setup the column names
2219 ostringstream pathTypeName;
2220 pathTypeName << maxCharLength << "A";
2221 vector<string> names;
2222 vector<string> dataTypes;
2223 names.push_back("MEMBER_XTENSION");
2224 dataTypes.push_back("8A");
2225 names.push_back("MEMBER_URI_TYPE");
2226 dataTypes.push_back("3A");
2227 names.push_back("MEMBER_LOCATION");
2228 dataTypes.push_back(pathTypeName.str());
2229 names.push_back("MEMBER_NAME");
2230 dataTypes.push_back(pathTypeName.str());
2231
2232 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
2233//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
2234 }
2235 catch (CCfits::FitsException e)
2236 {
2237 ostringstream str;
2238 str << "Could not open or create FITS table GROUPING in file " << groupName << " reason: " << e.message();
2239 Error(str);
2240 return;
2241 }
2242
2243 //CCfits seems to be buggy somehow: can't use the column's function "write": it create a compilation error: maybe strings were not thought about.
2244 //use cfitsio routines instead
2245 groupTable->makeThisCurrent();
2246 //create appropriate buffer.
2247 unsigned char* fitsBuffer = new unsigned char[8 + 3 + 2*maxCharLength + 1]; //+1 for trailling character
2248 memset(fitsBuffer, 0, 8 + 3 + 2*maxCharLength + 1);
2249 char* startOfExtension = reinterpret_cast<char*>(fitsBuffer);
2250 char* startOfURI = reinterpret_cast<char*>(&fitsBuffer[8]);
2251 char* startOfLocation = reinterpret_cast<char*>(&fitsBuffer[8 + 3]);
2252 char* startOfName = reinterpret_cast<char*>(&fitsBuffer[8+3+maxCharLength]);
2253
2254 sprintf(startOfExtension, "%s", "BINTABLE");
2255 sprintf(startOfURI, "%s", "URL");
2256 int i=1;
2257 for (map<string, vector<string> >::iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
2258 for (vector<string>::iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
2259 {
2260 strcpy(startOfLocation, it->first.c_str());
2261 strcpy(startOfName, jt->c_str());
2262 if (fDebugIsOn)
2263 {
2264 ostringstream str;
2265 str << "Grouping " << it->first << " " << *jt;
2266 Debug(str);
2267 }
2268 int status = 0;
2269 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength, fitsBuffer, &status);
2270 if (status)
2271 {
2272 ostringstream str;
2273 str << "Could not write row #" << i << "In the fits grouping file " << groupName << ". Cfitsio error code: " << status;
2274 Error(str);
2275 }
2276 }
2277
2278 filesToGroup.clear();
2279 delete groupFile;
2280}
2281#endif //HAVE_FITS
2282
2283// --------------------------------------------------------------------------
2284//
2285//! Implements the StopRun transition.
2286//! Attempts to close the run file.
2287//! @returns
2288//! kSM_WaitingRun if success, kSM_FatalError otherwise
2289int DataLogger::StopRunPlease()
2290{
2291
2292 if (fDebugIsOn)
2293 {
2294 Debug("Stopping Run Logging...");
2295 }
2296 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2297 {
2298 if (!it->logFile->is_open() || !it->reportFile->is_open())
2299 return kSM_FatalError;
2300 it->logFile->close();
2301 it->reportFile->close();
2302 }
2303
2304#ifdef HAVE_FITS
2305 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2306 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2307 {
2308 if (j->second.runFile.IsOpen())
2309 j->second.runFile.Close();
2310 }
2311#endif
2312 NotifyOpenedFile("", 0, fOpenedRunFiles);
2313 if (fNumSubAndFitsIsOn)
2314 fNumSubAndFits->updateService();
2315
2316 while (fRunNumber.size() > 0)
2317 {
2318 RemoveOldestRunNumber();
2319 }
2320
2321 return kSM_WaitingRun;
2322}
2323// --------------------------------------------------------------------------
2324//
2325//! Implements the Stop and Reset transitions.
2326//! Attempts to close any openned file.
2327//! @returns
2328//! kSM_Ready
2329int DataLogger::GoToReadyPlease()
2330{
2331 if (fDebugIsOn)
2332 {
2333 Debug("Going to the Ready state...");
2334 }
2335 if (GetCurrentState() == kSM_Logging)
2336 StopRunPlease();
2337
2338 if (fNightlyLogFile.is_open())
2339 fNightlyLogFile.close();
2340 if (fNightlyReportFile.is_open())
2341 fNightlyReportFile.close();
2342
2343#ifdef HAVE_FITS
2344 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2345 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2346 {
2347 if (j->second.nightlyFile.IsOpen())
2348 j->second.nightlyFile.Close();;
2349 }
2350#endif
2351 if (GetCurrentState() == kSM_Logging ||
2352 GetCurrentState() == kSM_WaitingRun ||
2353 GetCurrentState() == kSM_NightlyOpen)
2354 {
2355 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2356 if (fNumSubAndFitsIsOn)
2357 fNumSubAndFits->updateService();
2358 }
2359#ifdef HAVE_FITS
2360 CreateFitsGrouping(fOpenedNightlyFits, -1);
2361#endif
2362 return kSM_Ready;
2363}
2364// --------------------------------------------------------------------------
2365//
2366//! Implements the transition towards kSM_WaitingRun
2367//! Does nothing really.
2368//! @returns
2369//! kSM_WaitingRun
2370int DataLogger::NightlyToWaitRunPlease()
2371{
2372 if (fDebugIsOn)
2373 {
2374 Debug("Going to Wait Run Number state...");
2375 }
2376 return kSM_WaitingRun;
2377}
2378// --------------------------------------------------------------------------
2379//
2380//! Setup Logger's configuration from a Configuration object
2381//! @param conf the configuration object that should be used
2382//!
2383bool DataLogger::SetConfiguration(Configuration& conf)
2384{
2385 fDebugIsOn = conf.Get<bool>("debug");
2386
2387 //Set the block or allow list
2388 fBlackList.clear();
2389 fWhiteList.clear();
2390 if (conf.Has("block"))
2391 {
2392 vector<string> vec = conf.Get<vector<string>>("block");
2393 if (vec.size() != 0)
2394 {
2395 fHasBlackList = true;
2396 if (fDebugIsOn)
2397 Debug("Setting BLOCK list:");
2398 }
2399 for (vector<string>::iterator it = vec.begin(); it != vec.end(); it++)
2400 {
2401 fBlackList.insert(*it);
2402 if (fDebugIsOn)
2403 Debug(" " + *it);
2404 }
2405//Adding entries that should ALWAYS be ignored, because of Dim: otherwise the DataLogger would crash
2406 fBlackList.insert("DATA_LOGGER/");
2407 fBlackList.insert("/SERVICE_LIST");
2408 fBlackList.insert("DIS_DNS/");
2409 }
2410 if (conf.Has("allow"))
2411 {
2412 vector<string> vec = conf.Get<vector<string>>("allow");
2413 if (vec.size() != 0)
2414 {
2415 fHasWhiteList = true;
2416 if (fDebugIsOn)
2417 Debug("Setting ALLOW list:");
2418 }
2419 for (vector<string>::iterator it=vec.begin(); it != vec.end(); it++)
2420 {
2421 fWhiteList.insert(*it);
2422 if (fDebugIsOn)
2423 Debug(" " + *it);
2424 }
2425 }
2426 //Set the grouping
2427 if (conf.Has("group"))
2428 {
2429 vector<string> vec = conf.Get<vector<string>>("group");
2430 if (vec.size() != 0)
2431 if (fDebugIsOn)
2432 Debug("Setting GROUPING list:");
2433 for (vector<string>::iterator it=vec.begin(); it != vec.end(); it++)
2434 {
2435 fGrouping.insert(*it);
2436 if (fDebugIsOn)
2437 Debug(" " + *it);
2438 }
2439 }
2440 return true;
2441}
2442
2443// --------------------------------------------------------------------------
2444int RunDim(Configuration &conf)
2445{
2446 WindowLog wout;
2447
2448 //log.SetWindow(stdscr);
2449 if (conf.Has("log"))
2450 if (!wout.OpenLogFile(conf.Get<string>("log")))
2451 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2452
2453 // Start io_service.Run to use the StateMachineImp::Run() loop
2454 // Start io_service.run to only use the commandHandler command detaching
2455 DataLogger logger(wout);
2456 if (!logger.SetConfiguration(conf))
2457 return -1;
2458
2459 logger.Run(true);
2460
2461 return 0;
2462}
2463// --------------------------------------------------------------------------
2464void RunThread(DataLogger* logger)
2465{
2466 // This is necessary so that the StateMachine Thread can signal the
2467 // Readline thread to exit
2468 logger->Run(true);
2469 Readline::Stop();
2470}
2471// --------------------------------------------------------------------------
2472template<class T>
2473int RunShell(Configuration &conf)
2474{
2475 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
2476
2477 WindowLog &win = shell.GetStreamIn();
2478 WindowLog &wout = shell.GetStreamOut();
2479
2480 if (conf.Has("log"))
2481 if (!wout.OpenLogFile(conf.Get<string>("log")))
2482 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2483
2484 DataLogger logger(wout);
2485 if (!logger.SetConfiguration(conf))
2486 return -1;
2487
2488 shell.SetReceiver(logger);
2489
2490 boost::thread t(boost::bind(RunThread, &logger));
2491
2492 shell.Run(); // Run the shell
2493
2494 logger.Stop();
2495
2496 //Wait until the StateMachine has finished its thread
2497 //before returning and destroyinng the dim objects which might
2498 //still be in use.
2499 t.join();
2500
2501 return 0;
2502}
2503
2504/*
2505 Extract usage clause(s) [if any] for SYNOPSIS.
2506 Translators: "Usage" and "or" here are patterns (regular expressions) which
2507 are used to match the usage synopsis in program output. An example from cp
2508 (GNU coreutils) which contains both strings:
2509 Usage: cp [OPTION]... [-T] SOURCE DEST
2510 or: cp [OPTION]... SOURCE... DIRECTORY
2511 or: cp [OPTION]... -t DIRECTORY SOURCE...
2512 */
2513void PrintUsage()
2514{
2515 cout << "\n"
2516 "The data logger connects to all available Dim services and "
2517 "writes them to ascii and fits files.\n"
2518 "\n"
2519 "The default is that the program is started without user interaction. "
2520 "All actions are supposed to arrive as DimCommands. Using the -c "
2521 "option, a local shell can be initialized. With h or help a short "
2522 "help message about the usage can be brought to the screen.\n"
2523 "\n"
2524 "Usage: dataLogger [-c type] [OPTIONS]\n"
2525 " or: dataLogger [OPTIONS]\n";
2526 cout << endl;
2527
2528}
2529// --------------------------------------------------------------------------
2530void PrintHelp()
2531{
2532 /* Additional help text which is printed after the configuration
2533 options goes here */
2534 cout <<
2535 "\n"
2536 "The block option has priority over the allow, "
2537 "i.e. if both are present, only the block list is kept. "
2538 "If only a server name or service without its server prefix is given "
2539 "then all the services of that server, or all the services that "
2540 "correspond to the given suffix are ignored or considered.\n"
2541 "\n"
2542 "For example, block=DIS_DNS will skip all the services offered by "
2543 "the DIS_DNS server, while block=SERVICE_LIST will skip all the "
2544 "SERVICE_LIST services offered by any server.\n"
2545 "\n"
2546 "The commands offered by the dataLoger are the following: \n";
2547 cout << setw(20) << DataLogger::fConfigDay << " : specify the path where to put the nightly files\n";
2548 cout << setw(20) << DataLogger::fConfigRun << " : specify the path where to put the run files\n";
2549 cout << setw(20) << DataLogger::fConfigRunNumber << " : specify the run number\n";
2550 cout << setw(20) << DataLogger::fConfigLog << " : log a particular message\n";
2551 cout << setw(20) << DataLogger::fTransStart << " : start the nightly logging\n";
2552 cout << setw(20) << DataLogger::fTransStop << " : stop the nightly logging\n";
2553 cout << setw(20) << DataLogger::fTransStartRun << " : start the run logging\n";
2554 cout << setw(20) << DataLogger::fTransStopRun << " : stop the run logging\n";
2555 cout << setw(20) << DataLogger::fTransReset << " : stop any logging and/or recover from an error state\n";
2556 cout << setw(20) << DataLogger::fTransWait << " : go to the wait for run number state\n";
2557 cout << setw(20) << DataLogger::fPrintCommand << " : print the current state of the logger to the shell\n";
2558 cout << setw(20) << DataLogger::fDebugOnOff << " : turn on or off the debug mode\n";
2559 cout << setw(20) << DataLogger::fStatsPeriod << " : set the periodicity of the statistics. 0 disable them\n";
2560 cout << endl;
2561}
2562// --------------------------------------------------------------------------
2563void SetupConfiguration(Configuration &conf)
2564{
2565 const string n = conf.GetName()+".log";
2566
2567 po::options_description configp("Program options");
2568 configp.add_options()
2569 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
2570 ("log,l", var<string>(n), "Write log-file")
2571 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
2572 ;
2573
2574 po::options_description configs("DataLogger options");
2575 configs.add_options()
2576 ("block,b", vars<string>(), "Black-list of services")
2577 ("allow,a", vars<string>(), "White-list of services")
2578 ("debug", po_bool(), "Debug mode. Print clear text of received service reports to log-stream")
2579 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2580 ;
2581
2582 conf.AddEnv("dns", "DIM_DNS_NODE");
2583
2584 conf.AddOptions(configp);
2585 conf.AddOptions(configs);
2586}
2587// --------------------------------------------------------------------------
2588int main(int argc, const char* argv[])
2589{
2590
2591 float salut1 = 1.0f/0.0f;
2592 if (salut1 != salut1)
2593 cout << "NaN !";
2594 else
2595 cout << "regular number";
2596
2597 Configuration conf(argv[0]);
2598 conf.SetPrintUsage(PrintUsage);
2599 SetupConfiguration(conf);
2600
2601 po::variables_map vm;
2602 try
2603 {
2604 vm = conf.Parse(argc, argv);
2605 }
2606#if BOOST_VERSION > 104000
2607 catch (po::multiple_occurrences &e)
2608 {
2609 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
2610 return -1;
2611 }
2612#endif
2613 catch (exception& e)
2614 {
2615 cerr << "Program options invalid due to: " << e.what() << endl;
2616 return -1;
2617 }
2618
2619 if (conf.HasVersion() || conf.HasPrint())
2620 return -1;
2621
2622 if (conf.HasHelp())
2623 {
2624 PrintHelp();
2625 return -1;
2626 }
2627
2628 Dim::Setup(conf.Get<string>("dns"));
2629
2630// try
2631 {
2632 // No console access at all
2633 if (!conf.Has("console"))
2634 return RunDim(conf);
2635
2636 // Console access w/ and w/o Dim
2637 if (conf.Get<int>("console")==0)
2638 return RunShell<LocalShell>(conf);
2639 else
2640 return RunShell<LocalConsole>(conf);
2641 }
2642/* catch (exception& e)
2643 {
2644 cerr << "Exception: " << e.what() << endl;
2645 return -1;
2646 }*/
2647
2648 return 0;
2649}
Note: See TracBrowser for help on using the repository browser.