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

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