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

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