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

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