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

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