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

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