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

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