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

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