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

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