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

Last change on this file since 10488 was 10488, checked in by tbretz, 9 years ago
EXIT command was ignored when running in a shell - fixed.
File size: 49.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="DailyOpen"]
17   w [label="WaitingRun"]
18        l [label="Logging"]
19   b [label="BadDailyconfig" color="red"]
20   c [label="BadRunConfig" color="red"]
21 
22  e -> r
23  r -> e
24  r -> d
25  r -> b
26  d -> w
27  d -> r
28  w -> r
29  l -> r
30  l -> w
31  b -> d
32  w -> c
33  w -> l
34  b -> r
35  c -> r
36  c -> l
37  }
38  \enddot
39 
40  @todo
41        - Retrieve also the messages, not only the infos
42 */
43 //****************************************************************
44#include "Event.h"
45#include "Time.h"
46#include "StateMachineDim.h"
47#include "WindowLog.h"
48#include "Configuration.h"
49#include "ServiceList.h"
50#include "Converter.h"
51#include "MessageImp.h"
52#include "LocalControl.h"
53#include "DimDescriptionService.h"
54
55#include "Description.h"
56
57//for getting stat of opened files
58#include <unistd.h>
59//for getting disk free space
60#include <sys/statvfs.h>
61//for getting files sizes
62#include <sys/stat.h>
63
64#define HAS_FITS
65//#define ONE_RUN_FITS_ONLY
66
67#include <fstream>
68
69#include <boost/bind.hpp>
70#include <boost/thread.hpp>
71
72#ifdef HAS_FITS
73#include "Fits.h"
74#endif
75
76//Dim structures
77struct DataLoggerStats {
78        long long sizeWritten;
79        long long freeSpace;
80        long writingRate;
81};
82
83struct NumSubAndFitsType {
84        int numSubscriptions;
85        int numOpenFits;
86};
87
88class DataLogger : public StateMachineDim, DimInfoHandler
89{
90public:
91        /// The list of existing states specific to the DataLogger
92        enum
93        {
94                kSM_DailyOpen = 20, ///< Daily file openned and writing
95                kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
96                kSM_Logging = 40, ///< both files openned and writing
97                kSM_BadDailyConfig = 0x101, ///< the folder specified for daily logging does not exist or has bad permissions
98                kSM_BadRunConfig = 0x102, ///<  the folder specified for the run logging does not exist or has wrong permissions or no run number
99        } localstates_t;
100       
101        DataLogger(std::ostream &out);
102        ~DataLogger(); 
103       
104private:
105        //Define all the data structure specific to the DataLogger here
106        /// ofstream for the dailyLogfile
107        std::ofstream fDailyLogFile;
108        /// ofstream for the run-specific Log file
109        std::ofstream fRunLogFile; 
110
111        /// ofstream for the daily report file
112        std::ofstream fDailyReportFile;
113        /// ofstream for the run-specific report file
114        std::ofstream fRunReportFile;
115        /// base path of the dailyfile
116        std::string fDailyFileName; 
117        ///base path of the run file
118        std::string fRunFileName; 
119        ///run number (-1 means no run number specified)
120        int fRunNumber; 
121        ///Current Service Quality
122        int fQuality;
123        ///Modified Julian Date
124        double fMjD;
125       
126        ///Define all the static names
127        static const char* fConfigDay;
128        static const char* fConfigRun;
129        static const char* fConfigRunNumber;
130        static const char* fConfigLog;
131        static const char* fTransStart;
132        static const char* fTransStop;
133        static const char* fTransStartRun;
134        static const char* fTransStopRun;
135        static const char* fTransReset;
136        static const char* fTransWait;
137        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
138        ///Inherited from state machine impl
139        //int Execute();
140       
141        ///Inherited from state machine impl
142        //int Transition(const Event& evt);
143       
144        ///Inherited from state machine impl
145        //int Configure(const Event& evt);
146       
147        //overloading of DIM's infoHandler function
148        void infoHandler(); 
149       
150        ///for obtaining the name of the existing services
151        ServiceList fServiceList;
152       
153       
154        ///A std pair to store both the DimInfo name and the actual DimInfo pointer
155//      typedef std::pair<std::string, DimStampedInfo*> subscriptionType;
156        ///All the services to which we've subscribed to. Sorted by server name
157//      std::map<const std::string, std::vector<subscriptionType > > fServiceSubscriptions;
158
159        ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file
160        struct SubscriptionType
161        { 
162#ifdef HAS_FITS
163                ///daily FITS output file
164                Fits    dailyFile;
165                ///run-specific FITS output file
166                Fits    runFile;
167#endif
168                ///the actual dimInfo pointer
169                DimStampedInfo* dimInfo;
170                ///the converter for outputting the data according to the format
171                Converter* fConv;
172                ///the number of existing handlers to this structure.
173                ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor
174                int* numCopies;
175                void operator = (const SubscriptionType& other)
176                {
177#ifdef HAS_FITS
178                        dailyFile = other.dailyFile;
179                        runFile = other.runFile;
180#endif
181                        dimInfo = other.dimInfo;       
182                        numCopies = other.numCopies;
183                        fConv = other.fConv;
184                        (*numCopies)++;
185                }
186                SubscriptionType(const SubscriptionType& other)
187                {
188#ifdef HAS_FITS
189                        dailyFile = other.dailyFile;
190                        runFile = other.runFile;
191#endif
192                        dimInfo = other.dimInfo;
193                        numCopies = other.numCopies;
194                        fConv = other.fConv;
195                        (*numCopies)++; 
196                }
197                SubscriptionType(DimStampedInfo* info)
198                {
199                        dimInfo = info; 
200                        fConv = NULL;
201                        numCopies = new int(1);
202                }
203                SubscriptionType()
204                {
205                        dimInfo = NULL;
206                        fConv = NULL;
207                        numCopies = new int(1);
208                }
209                ~SubscriptionType()
210                {
211                        if (numCopies)
212                        (*numCopies)--;
213                        if (numCopies)
214                        if (*numCopies < 1)
215                        {
216                                if (dimInfo)
217                                delete dimInfo;
218#ifdef HAS_FITS
219                                if (dailyFile.IsOpen())
220                                        dailyFile.Close();
221                                if (runFile.IsOpen())
222                                        runFile.Close();
223#endif
224                                if (numCopies) 
225                                delete numCopies;
226                                delete fConv;
227                                fConv = NULL;
228                                dimInfo = NULL;
229                                numCopies = NULL;
230                        }
231                }
232        };
233        typedef std::map<const std::string, std::map<std::string, SubscriptionType>> SubscriptionsListType;
234        ///All the services to which we have subscribed to, sorted by server name.
235        SubscriptionsListType fServiceSubscriptions;
236
237
238        ///Reporting method for the services info received
239        void ReportPlease(DimInfo* I, SubscriptionType& sub); 
240
241        ///Configuration of the daily file path
242        int ConfigureDailyFileName(const Event& evt); 
243        ///Configuration fo the file name
244        int ConfigureRunFileName(const Event& evt); 
245        ///DEPREC - configuration of the run number
246        int ConfigureRunNumber(const Event& evt); 
247        ///logging method for the messages
248        int LogMessagePlease(const Event& evt); 
249        ///checks whether or not the current info being treated is a run number
250        void CheckForRunNumber(DimInfo* I); 
251        /// start transition
252        int StartPlease(); 
253        ///from waiting to logging transition
254        int StartRunPlease(); 
255        /// from logging to waiting transition
256        int StopRunPlease(); 
257        ///stop and reset transition
258        int GoToReadyPlease(); 
259        ///from dailyOpen to waiting transition
260        int DailyToWaitRunPlease(); 
261#ifdef HAS_FITS
262        ///Open fits files
263        void OpenFITSFilesPlease(SubscriptionType& sub);
264        ///Write data to FITS files
265        void WriteToFITS(SubscriptionType& sub);
266        ///Allocate the buffers required for fits
267        void AllocateFITSBuffers(SubscriptionType& sub);
268               
269#ifdef ONE_RUN_FITS_ONLY
270        ///FITS file for runs. only one, hence dealt with in the dataLogger itself
271        FITS* fRunFitsFile;
272#endif //one_run_fits_only
273#endif//has_fits
274public: 
275        ///checks with fServiceList whether or not the services got updated
276        bool CheckForServicesUpdate(); 
277
278private:       
279        ///monitoring notification loop
280        void ServicesMonitoring();
281        ///services notification thread
282        boost::thread fMonitoringThread;
283        ///end of the monitoring
284        bool fContinueMonitoring;
285        ///required for accurate monitoring
286        std::map<std::string, long long> fFileSizesMap;
287        std::string fFullDailyLogFileName;
288        std::string fFullDailyReportFileName;
289        std::string fFullRunLogFileName;
290        std::string fFullRunReportFileName;
291        long long fBaseSizeDaily;
292        long long fPreviousSize;
293        long long fBaseSizeRun;
294        ///Service for opened files
295        DimService* fOpenedDailyFiles;
296        DimService* fOpenedRunFiles;
297        DimService* fNumSubAndFits;
298        NumSubAndFitsType fNumSubAndFitsData;
299//      char* fDimBuffer;
300        inline void NotifyOpenedFile(std::string name, int type, DimService* service);
301       
302}; //DataLogger
303
304//static members initialization
305//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 ?
306const char* DataLogger::fConfigDay = "CONFIG_DAY";
307const char* DataLogger::fConfigRun = "CONFIG_RUN";
308const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
309const char* DataLogger::fConfigLog = "LOG";
310const char* DataLogger::fTransStart = "START";
311const char* DataLogger::fTransStop = "STOP";
312const char* DataLogger::fTransStartRun = "START_RUN";
313const char* DataLogger::fTransStopRun = "STOP_RUN";
314const char* DataLogger::fTransReset = "RESET";
315const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
316const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
317
318
319void DataLogger::ServicesMonitoring()
320{
321                //create the DIM service
322                int dataSize = 2*sizeof(long long) + sizeof(long);
323
324                DataLoggerStats statVar;
325                statVar.sizeWritten = 0;
326                statVar.freeSpace = 0;
327                statVar.writingRate = 0;
328
329                struct statvfs vfs;
330                if (!statvfs(fDailyFileName.c_str(), &vfs))
331                        statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
332                else
333                        statVar.freeSpace = -1;
334//        DimDescribedService srvc((GetName()+"/STATS").c_str(), "x:2;l:1", &statVar, dataSize,//static_cast<void*>(data), dataSize,
335//                                        "Add description here");
336                DimService srvc ("DATA_LOGGER/STATS", "x:2;l:1", &statVar, dataSize);
337                double deltaT = 1;
338                fPreviousSize = 0;
339                //loop-wait for broadcast
340                while (fContinueMonitoring)
341                {
342                        sleep(deltaT);
343                        //update the fits files sizes
344#ifdef HAS_FITS
345                        SubscriptionsListType::iterator x;
346                        std::map<std::string, SubscriptionType>::iterator y;
347                        bool runFileDone = false;
348                        for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
349                        {
350                                for (y=x->second.begin(); y != x->second.end(); y++)
351                                {
352                                        if (y->second.runFile.IsOpen() && !runFileDone)
353                                        {
354                                                        fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
355                                                        runFileDone = true;
356                                        }
357                                        if (y->second.dailyFile.IsOpen())
358                                                fFileSizesMap[y->second.dailyFile.fFileName] = y->second.dailyFile.GetWrittenSize();
359                                }
360                        }
361#endif
362                        struct stat st;
363                        //gather log and report files sizes on disk
364                        if (fDailyLogFile.is_open())
365                        {
366                                stat(fFullDailyLogFileName.c_str(), &st);
367                                fFileSizesMap[fFullDailyLogFileName] = st.st_size;     
368                        }
369                        if (fDailyReportFile.is_open())
370                        {
371                                stat(fFullDailyReportFileName.c_str(), &st);
372                                fFileSizesMap[fFullDailyReportFileName] = st.st_size;   
373                        }
374                        if (fRunLogFile.is_open())
375                        {
376                                stat(fFullRunLogFileName.c_str(), &st);
377                                fFileSizesMap[fFullRunLogFileName] = st.st_size;       
378                        }
379                        if (fRunReportFile.is_open())
380                        {
381                                stat(fFullRunReportFileName.c_str(), &st);
382                                fFileSizesMap[fFullRunReportFileName] = st.st_size;
383                        }       
384
385                        if (!statvfs(fDailyFileName.c_str(), &vfs))
386                                statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
387                        else
388                                statVar.freeSpace = -1;
389                       
390                        //sum up all the file sizes. past and present
391                        statVar.sizeWritten = 0;
392                        for (std::map<std::string, long long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end();  it++)
393                                statVar.sizeWritten += it->second;
394                        statVar.sizeWritten -= fBaseSizeDaily;
395                        statVar.sizeWritten -= fBaseSizeRun;
396                        //FIXME get the actual time elapsed
397                        statVar.writingRate = (statVar.sizeWritten - fPreviousSize)/deltaT; 
398                        fPreviousSize = statVar.sizeWritten;
399                        if (statVar.writingRate != 0) //if data has been written
400                        {
401//std::cout << "rate: " << statVar.writingRate << std::endl;
402                                srvc.updateService(&statVar, dataSize);//static_cast<void*>(data), dataSize);
403                        }
404                }
405}
406
407
408// --------------------------------------------------------------------------
409//
410//! Default constructor. The name of the machine is given DATA_LOGGER
411//! and the state is set to kSM_Ready at the end of the function.
412//
413//!Setup the allows states, configs and transitions for the data logger
414//
415DataLogger::DataLogger(std::ostream &out) : StateMachineDim(out, "DATA_LOGGER")
416{       
417                //initialize member data
418                fDailyFileName = "/home/lyard/log";//".";
419                fRunFileName = "/home/lyard/log";//".";
420                fRunNumber = 12345;
421#ifdef HAS_FITS
422#ifdef ONE_RUN_FITS_ONLY
423                fRunFitsFile = NULL;
424#endif
425#endif
426                //Give a name to this machine's specific states
427                AddStateName(kSM_DailyOpen,      "DailyFileOpen",  "Add description here");
428                AddStateName(kSM_WaitingRun,     "WaitForRun",     "Add description here");
429                AddStateName(kSM_Logging,        "Logging",        "Add description here");
430                AddStateName(kSM_BadDailyConfig, "ErrDailyFolder", "Add description here");
431                AddStateName(kSM_BadRunConfig,   "ErrRunFolder",   "Add description here");
432
433        /*Add the possible transitions for this machine*/
434                AddTransition(kSM_DailyOpen, fTransStart, kSM_Ready, kSM_BadDailyConfig)
435                    (boost::bind(&DataLogger::StartPlease, this));
436//                    ("start the daily logging. daily file location must be specified already");
437
438                AddTransition(kSM_Ready, fTransStop, kSM_DailyOpen, kSM_WaitingRun, kSM_Logging)
439                    (boost::bind(&DataLogger::GoToReadyPlease, this));
440//                   ("stop the data logging");
441
442                AddTransition(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig)
443                    (boost::bind(&DataLogger::StartRunPlease, this));
444//                    ("start the run logging. run file location must be specified already.");
445
446                AddTransition(kSM_WaitingRun, fTransStopRun, kSM_Logging)
447                    (boost::bind(&DataLogger::StopRunPlease, this));
448//                    ("");
449
450                AddTransition(kSM_Ready, fTransReset, kSM_Error, kSM_BadDailyConfig, kSM_BadRunConfig, kSM_Error)
451                    (boost::bind(&DataLogger::GoToReadyPlease, this));
452//                    ("transition to exit error states. dunno if required or not, would close the daily file if already openned.");
453
454                AddTransition(kSM_WaitingRun, fTransWait, kSM_DailyOpen)
455                    (boost::bind(&DataLogger::DailyToWaitRunPlease, this));
456
457        /*Add the possible configurations for this machine*/
458       
459                AddConfiguration(fConfigDay, "C", kSM_Ready, kSM_BadDailyConfig)
460                    (boost::bind(&DataLogger::ConfigureDailyFileName, this, _1));;
461//                    ("configure the daily file location. cannot be done before the file is actually opened");
462
463                AddConfiguration(fConfigRun, "C", kSM_Ready, kSM_BadDailyConfig, kSM_DailyOpen, kSM_WaitingRun, kSM_BadRunConfig)
464                    (boost::bind(&DataLogger::ConfigureRunFileName, this, _1));
465//                    ("configure the run file location. cannot be done before the file is actually opened, and not in a dailly related error.");
466
467                //Provide a logging command
468                //I get the feeling that I should be going through the EventImp
469                //instead of DimCommand directly, mainly because the commandHandler
470                //is already done in StateMachineImp.cc
471                //Thus I'll simply add a configuration, which I will treat as the logging command
472                AddConfiguration(fConfigLog, "C", kSM_DailyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig)
473                    (boost::bind(&DataLogger::LogMessagePlease, this, _1));
474
475                fServiceList.SetHandler(this);
476                CheckForServicesUpdate();
477               
478                //start the monitoring service
479                fContinueMonitoring = true;
480                fMonitoringThread = boost::thread(boost::bind(&DataLogger::ServicesMonitoring, this));
481                fBaseSizeDaily = 0;
482                fBaseSizeRun = 0;
483                //start the open files service
484//              fDimBuffer = new char[256];
485//              memset(fDimBuffer, 0, sizeof(int));
486//              fDimBuffer[sizeof(int)] = '\0';
487
488                //gives the entire buffer size. Otherwise, dim overwrites memory at bizarre locations if smaller size is given at creation time.
489//        fOpenedFiles =        new DimDescribedService((GetName()+"/FILENAME").c_str(), "i:1;C", static_cast<void*>(fDimBuffer), 256, "Add description here");
490                fOpenedDailyFiles = new DimService((GetName() + "/FILENAME_DAILY").c_str(), const_cast<char*>(""));//"i:1;C", static_cast<void*>(fDimBuffer), 256);
491                fOpenedRunFiles = new DimService((GetName() + "/FILENAME_RUN").c_str(), const_cast<char*>(""));
492                fOpenedDailyFiles->setQuality(0);
493                fOpenedRunFiles->setQuality(0);
494                fOpenedDailyFiles->updateService();
495                fOpenedRunFiles->updateService();
496                fNumSubAndFitsData.numSubscriptions = 0;
497                fNumSubAndFitsData.numOpenFits = 0;
498                fNumSubAndFits = new DimService((GetName() + "/NUM_SUBS").c_str(), "i:2", &fNumSubAndFitsData, sizeof(NumSubAndFitsType));
499
500}
501// --------------------------------------------------------------------------
502//
503//! Checks for changes in the existing services.
504//! Any new service will be added to the service list, while the ones which disappeared are removed.
505//! @todo
506//!     add the configuration (using the conf class ?)
507//
508//FIXME The service must be udpated so that I get the first notification. This should not be
509bool DataLogger::CheckForServicesUpdate()
510{ 
511        bool serviceUpdated = false;
512        //get the current server list
513        const std::vector<std::string> serverList = fServiceList.GetServerList();
514        //first let's remove the servers that may have disapeared
515        //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
516        std::vector<std::string> toBeDeleted;
517        for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
518        {
519                std::vector<std::string>::const_iterator givenServers;
520                for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
521                        if (cListe->first == *givenServers)
522                                break;
523                if (givenServers == serverList.end())//server vanished. Remove it
524                {       
525                        toBeDeleted.push_back(cListe->first);
526                        serviceUpdated = true;
527                }
528                       
529        }
530        for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
531                fServiceSubscriptions.erase(*it);
532        //now crawl through the list of servers, and see if there was some updates
533        for (std::vector<std::string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
534        {
535                //skip the two obvious excluded services
536                if ((i->find("DIS_DNS") != std::string::npos) ||
537                    (i->find("DATA_LOGGER") != std::string::npos))
538                        continue;
539                //find the current server in our subscription list     
540                SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
541                //get the service list of the current server
542                std::vector<std::string> cServicesList = fServiceList.GetServiceList(*i);
543                if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
544                {                                                                                //then check and update our list of subscriptions
545                        //first, remove the services that may have dissapeared.
546                        std::map<std::string, SubscriptionType>::iterator serverSubs;
547                        std::vector<std::string>::const_iterator givenSubs;
548                        toBeDeleted.clear();
549                        for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
550                        {
551                                for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
552                                        if (serverSubs->first == *givenSubs)
553                                                break;
554                                if (givenSubs == cServicesList.end())
555                                {
556                                        toBeDeleted.push_back(serverSubs->first);
557                                        serviceUpdated = true;
558                                }       
559                        }
560                        for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
561                                cSubs->second.erase(*it);
562                        //now check for new services
563                        for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
564                        {
565                                if (*givenSubs == "SERVICE_LIST")
566                                        continue;
567                                if (cSubs->second.find(*givenSubs) == cSubs->second.end())
568                                {//service not found. Add it
569                                        cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast<char*>(""), this);
570                                        serviceUpdated = true;
571                                }       
572                        }
573                }
574                else //server not found in our list. Create its entry
575                {
576                        fServiceSubscriptions[*i] = std::map<std::string, SubscriptionType>();
577                        std::map<std::string, SubscriptionType>& liste = fServiceSubscriptions[*i];
578                        for (std::vector<std::string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
579                        {
580                                if (*j == "SERVICE_LIST")
581                                        continue;
582                                liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast<char*>(""), this);
583                                serviceUpdated = true;
584                        }
585                }       
586        }
587        return serviceUpdated;
588}
589// --------------------------------------------------------------------------
590//
591//! Destructor
592//
593DataLogger::~DataLogger()
594{
595        //release the services subscriptions
596        fServiceSubscriptions.clear();
597        //exit the monitoring loop
598        fContinueMonitoring = false;
599//      delete[] fDimBuffer;
600        fMonitoringThread.join();
601        //close the files
602        if (fDailyLogFile.is_open())
603                fDailyLogFile.close();
604        if (fDailyReportFile.is_open())
605                fDailyReportFile.close();
606        if (fRunLogFile.is_open())
607                fRunLogFile.close();
608        if (fRunReportFile.is_open())
609                fRunReportFile.close();
610        delete fOpenedDailyFiles;
611        delete fOpenedRunFiles;
612        delete fNumSubAndFits;
613//TODO notify that all files were closed
614#ifdef HAS_FITS
615#ifdef ONE_RUN_FITS_ONLY
616        if (fRunFitsFile != NULL)
617                delete fRunFitsFile;
618        fRunFitsFile = NULL;
619#endif
620#endif
621}
622/*
623// --------------------------------------------------------------------------
624//
625//! Execute
626//! Shouldn't be run as we use callbacks instead
627//
628int DataLogger::Execute()
629{
630        //due to the callback mecanism, this function should never be called
631        return kSM_FatalError;
632       
633        switch (GetCurrentState())
634        {
635        case kSM_Error:
636        case kSM_Ready:
637        case kSM_DailyOpen:
638        case kSM_WaitingRun:
639        case kSM_Logging:
640        case kSM_BadDailyConfig:
641        case kSM_BadRunConfig:
642                return GetCurrentState();
643        }
644        //this line below should never be hit. It here mainly to remove warnings at compilation
645        return kSM_FatalError;
646}
647// --------------------------------------------------------------------------
648//
649//! Shouldn't be run as we use callbacks instead
650//
651 int DataLogger::Transition(const Event& evt)
652{
653        //due to the callback mecanism, this function should never be called
654        return kSM_FatalError;
655       
656        switch (evt.GetTargetState())
657        {
658                case kSM_Ready:
659                //here we must figure out whether the STOP or RESET command was sent
660                //close opened files and go back to ready state
661                        switch (GetCurrentState())
662                        {
663                                case kSM_BadDailyConfig:
664                                case kSM_BadRunConfig:
665                                case kSM_Error:
666                                        return GoToReadyPlease();
667                                       
668                                case kSM_Logging:
669                                case kSM_WaitingRun:
670                                case kSM_DailyOpen:
671                                        return GoToReadyPlease();
672                        }
673                break;
674               
675                case kSM_DailyOpen:
676                        //Attempt to open the daily file
677                        switch (GetCurrentState())
678                        {
679                                case kSM_Ready:
680                                case kSM_BadDailyConfig:
681                                        return StartPlease();   
682                        }
683                break;
684               
685                case kSM_WaitingRun:
686                        //either close the run file, or just go to the waitingrun state (if coming from daily open
687                        switch (GetCurrentState())
688                        {
689                                case kSM_DailyOpen:
690                                        return kSM_WaitingRun;
691                               
692                                case kSM_Logging:
693                                        return StopRunPlease();
694                        }
695                break;
696               
697                case kSM_Logging:
698                        //Attempt to open run file
699                        switch (GetCurrentState())
700                        {
701                                case kSM_WaitingRun:
702                                case kSM_BadRunConfig:
703                                        return StartRunPlease();
704                        }       
705                break;
706        }
707        //Getting here means that an invalid transition has been asked.
708        //TODO Log an error message
709        //and return the fatal error state
710        return kSM_FatalError;
711}
712// --------------------------------------------------------------------------
713//
714//! Shouldn't be run as we use callbacks instead
715//
716 int DataLogger::Configure(const Event& evt)
717{
718        //due to the callback mecanism, this function should never be called
719        return kSM_FatalError;
720
721        switch (evt.GetTargetState())
722        {
723                case kSM_Ready:
724                case kSM_BadDailyConfig:
725                        return ConfigureDailyFileName(evt);
726                break;
727               
728                case kSM_WaitingRun:
729                case kSM_BadRunConfig:
730                        return ConfigureRunFileName(evt);       
731                break;
732               
733                case kSM_Logging:
734                case kSM_DailyOpen:
735                                return 0;
736                break;
737
738        }       
739
740        return kSM_FatalError;
741}
742*/
743// --------------------------------------------------------------------------
744//
745//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
746//
747void DataLogger::infoHandler()
748{
749        DimInfo* I = getInfo();
750        SubscriptionsListType::iterator x;
751        std::map<std::string, SubscriptionType>::iterator y;
752        if (I==NULL)
753        {
754                if (CheckForServicesUpdate())
755                {
756                        //services were updated. Notify
757                        fNumSubAndFitsData.numSubscriptions = 0;
758                        for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
759                                fNumSubAndFitsData.numSubscriptions += x->second.size();
760                        fNumSubAndFits->updateService();
761                }
762                return;
763        }
764        //check if the service pointer corresponds to something that we subscribed to
765        //this is a fix for a bug that provides bad Infos when a server starts
766        bool found = false;
767        for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
768        {//find current service is subscriptions
769                for (y=x->second.begin(); y!=x->second.end();y++)
770                        if (y->second.dimInfo == I)
771                        {
772                                found = true;   
773                                break;
774                        }
775                if (found)
776                        break;
777        }
778        if (!found)
779                return;
780        if (I->getSize() <= 0)
781                return;
782        //check that the message has been updated by something, i.e. must be different from its initial value
783        if (I->getTimestamp() == 0)
784                return;
785
786        CheckForRunNumber(I);
787        ReportPlease(I, y->second);
788
789}
790
791// --------------------------------------------------------------------------
792//
793//! Checks whether or not the current info is a run number.
794//! If so, then remember it. A run number is required to open the run-log file
795//! @param I
796//!             the current DimInfo
797//
798void DataLogger::CheckForRunNumber(DimInfo* I)
799{
800        if (strstr(I->getName(), fRunNumberInfo) != NULL)
801        {//assumes that the run number is an integer
802                //TODO check the format here
803                fRunNumber = I->getInt();       
804        }
805}
806
807// --------------------------------------------------------------------------
808//
809//! write infos to log files.
810//! @param I
811//!     The current DimInfo
812//
813void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
814{
815        //should we log or report this info ? (i.e. is it a message ?)
816        bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
817       
818        //TODO add service exclusion
819       
820        if (!fDailyReportFile.is_open())
821                return;
822
823        //create the converter for that service
824        if (sub.fConv == NULL)
825        {
826                sub.fConv = new Converter(Out(), I->getFormat());       
827                if (!sub.fConv)
828                {
829                        Error("Couldn't properly parse the format... service ignored.");
830                        return; 
831                }
832        }
833               
834        //construct the header
835        std::stringstream header;
836        Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
837        fQuality = I->getQuality();
838        fMjD = cTime.Mjd();
839
840        if (isItaReport)
841        {
842                //write text header
843                header << I->getName() << " " << fQuality << " ";
844                header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
845                header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
846                header << cTime.ms() << " " << I->getTimestamp() << " ";
847
848                std::string text;
849        try
850        {
851                text = sub.fConv->GetString(I->getData(), I->getSize());
852        }
853        catch (const std::runtime_error &e)
854        {
855                Out() << kRed << e.what() << endl;
856            Error("Couldn't properly parse the data... ignored.");
857            return;
858        }
859
860                if (text.empty())
861                return;
862
863        //replace bizarre characters by white space
864        replace(text.begin(), text.end(), '\n', '\\');
865        replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
866       
867                if (fDailyReportFile.is_open())
868                        fDailyReportFile << header.str();
869                if (fRunReportFile.is_open())
870                        fRunReportFile << header.str();
871
872        if (fDailyReportFile.is_open())
873                        fDailyReportFile << text << std::endl;
874                if (fRunReportFile.is_open())
875                        fRunReportFile << text << std::endl;
876        }
877        else
878        {
879                std::string n = I->getName();
880                std::stringstream msg;
881                msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString();
882                MessageImp dailyMess(fDailyLogFile);
883                dailyMess.Write(cTime, msg.str().c_str(), fQuality);
884                if (fRunLogFile.is_open())
885                {
886                        MessageImp runMess(fRunLogFile);
887                        runMess.Write(cTime, msg.str().c_str(), fQuality);
888                }
889        }
890
891#ifdef HAS_FITS
892        if (!sub.dailyFile.IsOpen() || !sub.runFile.IsOpen())
893                OpenFITSFilesPlease(sub);       
894        WriteToFITS(sub);
895               
896#endif
897
898}
899
900// --------------------------------------------------------------------------
901//
902//! write messages to logs.
903//! @param evt
904//!             the current event to log
905//! @returns
906//!             the new state. Currently, always the current state
907//!
908//! @deprecated
909//!    I guess that this function should not be any longer
910//
911//TODO isn't that function not used any longer ? If so I guess that we should get rid of it...
912int DataLogger::LogMessagePlease(const Event& evt)
913{
914        if (!fDailyLogFile.is_open())
915                return GetCurrentState();
916       
917        std::stringstream header;
918        const Time& cTime = evt.GetTime();
919        header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
920        header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
921        header << cTime.ms() << " ";
922               
923    const Converter conv(Out(), evt.GetFormat());
924    if (!conv)
925    {
926        Error("Couldn't properly parse the format... ignored.");
927        return GetCurrentState();
928    }
929
930    std::string text;
931    try
932    {
933        text = conv.GetString(evt.GetData(), evt.GetSize());
934    }
935    catch (const std::runtime_error &e)
936    {
937        Out() << kRed << e.what() << endl;
938        Error("Couldn't properly parse the data... ignored.");
939        return GetCurrentState();
940    }
941
942        if (text.empty())
943        return GetCurrentState();
944
945    //replace bizarre characters by white space
946    replace(text.begin(), text.end(), '\n', '\\');
947    replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
948
949        if (fDailyLogFile.is_open())
950                fDailyLogFile << header;
951        if (fRunLogFile.is_open())
952                fRunLogFile << header;
953
954        if (fDailyLogFile.is_open())
955                fDailyLogFile << text;
956        if (fRunLogFile.is_open())
957                fRunLogFile << text;
958
959        return GetCurrentState();
960}
961// --------------------------------------------------------------------------
962//
963//!     Sets the path to use for the daily log file.
964//! @param evt
965//!     the event transporting the path
966//! @returns
967//!             currently only the current state.
968//
969int DataLogger::ConfigureDailyFileName(const Event& evt)
970{
971std::cout << "Configure Daily File Name" << std::endl;
972        if (evt.GetText() != NULL)
973                fDailyFileName = std::string(evt.GetText());   
974        else
975                Error("Empty daily folder");
976
977        return GetCurrentState();
978}
979// --------------------------------------------------------------------------
980//
981//! Sets the path to use for the run log file.
982//! @param evt
983//!             the event transporting the path
984//! @returns
985//!     currently only the current state
986int DataLogger::ConfigureRunFileName(const Event& evt)
987{
988std::cout << "Configure Run File Name" << std::endl;
989        if (evt.GetText() != NULL)
990                fRunFileName = std::string(evt.GetText());
991        else
992                Error("Empty daily folder");
993
994        return GetCurrentState();
995}
996// --------------------------------------------------------------------------
997//
998//! Sets the run number.
999//! @param evt
1000//!             the event transporting the run number
1001//! @returns
1002//!     currently only the current state
1003int DataLogger::ConfigureRunNumber(const Event& evt)
1004{
1005        fRunNumber = evt.GetInt();
1006
1007        return GetCurrentState();
1008}
1009// --------------------------------------------------------------------------
1010//
1011//! Notifies the DIM service that a particular file was opened
1012//! @ param name the base name of the opened file, i.e. without path nor extension.
1013//!     WARNING: use string instead of string& because I pass values that do not convert to string&.
1014//!             this is not a problem though because file are not opened so often.
1015//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1016inline void DataLogger::NotifyOpenedFile(std::string name, int type, DimService* service)
1017{
1018//std::cout << "name: " << name << " size: " << name.size() << std::endl;
1019//      reinterpret_cast<int*>(fDimBuffer)[0] = type;
1020//      strcpy(&fDimBuffer[sizeof(int)], name.c_str());
1021        service->setQuality(type);
1022        service->updateService(const_cast<char*>(name.c_str()));
1023//      fOpenedFiles->updateService(static_cast<void*>(fDimBuffer), name.size() + 1 + sizeof(int));
1024}
1025// --------------------------------------------------------------------------
1026//
1027//! Implements the Start transition.
1028//! Concatenates the given path for the daily file and the filename itself (based on the day),
1029//! and tries to open it.
1030//! @returns
1031//!             kSM_DailyOpen if success, kSM_BadDailyConfig if failure
1032int DataLogger::StartPlease()
1033{
1034        //TODO concatenate the dailyFileName and the formatted date and extension to obtain the full file name
1035        Time time;//(Time::utc);
1036        std::stringstream sTime;
1037        sTime << time.Y() << "_" << time.M() << "_" << time.D();
1038        fFullDailyLogFileName = fDailyFileName + '/' + sTime.str() + ".log"; 
1039       
1040        fDailyLogFile.open(fFullDailyLogFileName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be "app" instead of "ate" ??
1041        fFullDailyReportFileName = fDailyFileName + '/' + sTime.str() + ".rep";
1042        fDailyReportFile.open(fFullDailyReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
1043       
1044        if (!fDailyLogFile.is_open() || !fDailyReportFile.is_open())
1045        {       
1046                //TODO send an error message   
1047            return kSM_BadDailyConfig;
1048        }
1049        //get the size of the newly opened file.
1050        struct stat st;
1051        stat(fFullDailyLogFileName.c_str(), &st);
1052        fBaseSizeDaily = st.st_size;   
1053        stat(fFullDailyReportFileName.c_str(), &st);
1054        fBaseSizeDaily += st.st_size;
1055        fFileSizesMap.clear();
1056        fBaseSizeRun = 0;
1057        fPreviousSize = 0;
1058        //notify that files were opened
1059        std::string actualTargetDir;
1060        if (fDailyFileName == ".")
1061        {
1062                char currentPath[FILENAME_MAX];
1063                getcwd(currentPath, sizeof(currentPath));
1064                actualTargetDir = currentPath;
1065        }
1066        else
1067        {
1068                actualTargetDir = fDailyFileName;       
1069        }
1070std::cout << actualTargetDir << '/' << sTime.str() << std::endl;
1071        NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 3, fOpenedDailyFiles);
1072       
1073       
1074        return kSM_DailyOpen;   
1075}
1076
1077#ifdef HAS_FITS
1078// --------------------------------------------------------------------------
1079//
1080//! open if required a the FITS files corresponding to a given subscription
1081//! @param sub
1082//!     the current DimInfo subscription being examined
1083void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub)
1084{
1085        std::string serviceName(sub.dimInfo->getName());
1086        for (unsigned int i=0;i<serviceName.size(); i++)
1087        {
1088                if (serviceName[i] == '/')
1089                {
1090                        serviceName[i] = '_';
1091                        break; 
1092                }       
1093        }
1094        Time time;
1095        std::stringstream sTime;
1096        sTime << time.Y() << "_" << time.M() << "_" << time.D();
1097        //we open the dailyFile anyway, otherwise this function shouldn't have been called.
1098        if (!sub.dailyFile.IsOpen())
1099        {
1100                std::string partialName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits";
1101                AllocateFITSBuffers(sub);
1102                //get the size of the file we're about to open
1103                if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1104                {
1105                        struct stat st;
1106                        if (!stat(partialName.c_str(), &st))
1107                                fBaseSizeDaily += st.st_size;
1108                        fFileSizesMap[partialName] = 0;
1109                }
1110                sub.dailyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits);
1111                //notify the opening
1112                std::string actualTargetDir;
1113                if (fDailyFileName == ".")
1114                {
1115                        char currentPath[FILENAME_MAX];
1116                        getcwd(currentPath, sizeof(currentPath));
1117                        actualTargetDir = currentPath;
1118                }
1119                else
1120                {
1121                        actualTargetDir = fDailyFileName;       
1122                }               
1123                NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 4, fOpenedDailyFiles);
1124                fNumSubAndFits->updateService();
1125        }
1126        if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1127        {//buffer for the run file have already been allocated when doing the daily file
1128                std::stringstream sRun;
1129                sRun << fRunNumber;
1130#ifdef ONE_RUN_FITS_ONLY
1131                std::string partialName = fRunFileName + '/' + sRun.str() + ".fits";//'_' + serviceName + ".fits";
1132                if (fRunFitsFile == NULL)
1133                {
1134#else
1135                std::string partialName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits";
1136#endif
1137                        //get the size of the file we're about to open
1138                        if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1139                        {
1140                                struct stat st;
1141                                if (!stat(partialName.c_str(), &st))
1142                                        fBaseSizeRun += st.st_size;
1143                                else
1144                                        fBaseSizeRun = 0;
1145                                fFileSizesMap[partialName] = 0; 
1146                        }
1147#ifdef ONE_RUN_FITS_ONLY
1148                        try
1149                        {
1150                                fRunFitsFile = new FITS(partialName, RWmode::Write);   
1151                                (fNumSubAndFitsData.numOpenFits)++;
1152                        }       
1153                        catch (CCfits::FitsError e)
1154                        {
1155                                std::ostringstream err;
1156                                err << "Could not open run file " << partialName;
1157                                throw runtime_error(err.str()); 
1158                        }
1159#endif
1160                        std::string actualTargetDir;
1161                        if (fRunFileName == ".")
1162                        {
1163                                char currentPath[FILENAME_MAX];
1164                                getcwd(currentPath, sizeof(currentPath));
1165                                actualTargetDir = currentPath;
1166                        }
1167                        else
1168                        {
1169                                actualTargetDir = fRunFileName; 
1170                        }               
1171                        NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 4, fOpenedRunFiles);// + '_' + serviceName, 4);
1172#ifdef ONE_RUN_FITS_ONLY
1173                }
1174                sub.runFile.Open(partialName, serviceName, fRunFitsFile, &fNumSubAndFitsData.numOpenFits);
1175#else
1176                sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits);
1177#endif //one_run_fits_only
1178           fNumSubAndFits->updateService();
1179
1180        }
1181}       
1182// --------------------------------------------------------------------------
1183//
1184void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
1185{
1186        int size = sub.dimInfo->getSize();
1187         
1188        //Init the time columns of the file
1189        Description dateDesc(std::string("Time"), std::string("Modified Julian Date"), std::string("MjD"));
1190        sub.dailyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1191        sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1192
1193        Description QoSDesc("Qos", "Quality of service", "None");
1194        sub.dailyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1195        sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1196
1197        const Converter::FormatList flist = sub.fConv->GetList();
1198    // Compilation failed
1199    if (flist.empty() || flist.back().first.second!=0)
1200    {
1201        Error("Compilation of format string failed.");
1202        return;
1203    }
1204
1205        //we've got a nice structure describing the format of this service's messages.
1206        //Let's create the appropriate FITS columns
1207        std::vector<std::string> dataFormatsLocal;
1208        for (unsigned int i=0;i<flist.size()-1;i++)
1209        {
1210                std::stringstream dataQualifier; 
1211
1212                dataQualifier << flist[i].second.first;
1213                switch (flist[i].first.first->name()[0])
1214                {//TODO handle all the data format cases
1215                        case 'c':
1216                                dataQualifier <<  "S";
1217                        break;
1218                        case 's':
1219                                dataQualifier << "I";
1220                        break;
1221                        case 'i':
1222                                dataQualifier << "J";
1223                        break;
1224                        case 'l':
1225                                dataQualifier << "J";
1226                                //TODO triple check that in FITS, long = int
1227                        break;
1228                        case 'f':
1229                                dataQualifier << "E";
1230                        break;
1231                        case 'd':
1232                                dataQualifier << "D";
1233                        break;
1234                        case 'x':
1235                        case 'X':
1236                                dataQualifier << "K";
1237                        break;
1238                        case 'S':
1239                                //for strings, the number of elements I get is wrong. Correct it
1240                                dataQualifier.str(""); //clear
1241                                dataQualifier << size-1 <<  "A";
1242                                size = size-1;
1243                        break;
1244                       
1245                        default:
1246                                Error("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 948.");
1247                };
1248                dataFormatsLocal.push_back(dataQualifier.str());
1249         }
1250
1251         sub.dailyFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1252         sub.runFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1253}
1254// --------------------------------------------------------------------------
1255//
1256//! write a dimInfo data to its corresponding FITS files
1257//
1258void DataLogger::WriteToFITS(SubscriptionType& sub)
1259{
1260                //dailyFile status (open or not) already checked
1261                if (sub.dailyFile.IsOpen())
1262                        sub.dailyFile.Write(sub.fConv);
1263                if (sub.runFile.IsOpen())
1264                        sub.runFile.Write(sub.fConv);
1265}
1266#endif //if has_fits
1267// --------------------------------------------------------------------------
1268//
1269//! Implements the StartRun transition.
1270//! Concatenates the given path for the run file and the filename itself (based on the run number),
1271//! and tries to open it.
1272//! @returns
1273//!             kSM_Logging if success, kSM_BadRunConfig if failure.
1274int DataLogger::StartRunPlease()
1275{
1276        //attempt to open run file with current parameters
1277        if (fRunNumber == -1)
1278                return kSM_BadRunConfig;
1279        std::stringstream sRun;
1280        sRun << fRunNumber;
1281        fFullRunLogFileName = fRunFileName + '/' + sRun.str() + ".log";
1282        fRunLogFile.open(fFullRunLogFileName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate
1283
1284        fFullRunReportFileName = fRunFileName + '/' + sRun.str() + ".rep";
1285        fRunReportFile.open(fFullRunReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
1286       
1287        if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1288        {
1289                //TODO send an error message
1290                return kSM_BadRunConfig;       
1291        }
1292        //get the size of the newly opened file.
1293        struct stat st;
1294        fBaseSizeRun = 0;
1295        if (fFileSizesMap.find(fFullRunLogFileName) == fFileSizesMap.end())
1296        {
1297                stat(fFullRunLogFileName.c_str(), &st);
1298                fBaseSizeRun += st.st_size;
1299                fFileSizesMap[fFullRunLogFileName] = 0;
1300        }
1301        if (fFileSizesMap.find(fFullRunReportFileName) == fFileSizesMap.end())
1302        {
1303                stat(fFullRunReportFileName.c_str(), &st);
1304                fBaseSizeRun += st.st_size;
1305                fFileSizesMap[fFullRunReportFileName] = 0;
1306        }
1307        std::string actualTargetDir;
1308        if (fRunFileName == ".")
1309        {
1310                char currentPath[FILENAME_MAX];
1311                getcwd(currentPath, sizeof(currentPath));
1312                actualTargetDir = currentPath;
1313        }
1314        else
1315        {
1316                actualTargetDir = fRunFileName; 
1317        }               
1318        NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 3, fOpenedRunFiles);
1319       
1320        return kSM_Logging;
1321}
1322// --------------------------------------------------------------------------
1323//
1324//! Implements the StopRun transition.
1325//! Attempts to close the run file.
1326//! @returns
1327//!             kSM_WaitingRun if success, kSM_FatalError otherwise
1328int DataLogger::StopRunPlease()
1329{
1330        if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1331                return kSM_FatalError;
1332       
1333        fRunLogFile.close();
1334        fRunReportFile.close();
1335#ifdef HAS_FITS
1336        for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1337                for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1338                {
1339                                if (j->second.runFile.IsOpen())
1340                                        j->second.runFile.Close();     
1341                }
1342#ifdef ONE_RUN_FITS_ONLY
1343        if (fRunFitsFile != NULL)
1344        {
1345                delete fRunFitsFile;
1346                fRunFitsFile = NULL;   
1347std::cout << "FNumSub2: " << fNumSubAndFitsData.numOpenFits << std::endl;
1348                (fNumSubAndFitsData.numOpenFits)--;
1349std::cout << "FNumSub3: " << fNumSubAndFitsData.numOpenFits << std::endl;
1350        }
1351#endif
1352#endif
1353        NotifyOpenedFile("None", 0, fOpenedRunFiles);
1354        fNumSubAndFits->updateService();
1355        return kSM_WaitingRun;
1356
1357}
1358// --------------------------------------------------------------------------
1359//
1360//! Implements the Stop and Reset transitions.
1361//! Attempts to close any openned file.
1362//! @returns
1363//!     kSM_Ready
1364int DataLogger::GoToReadyPlease()
1365{
1366        if (fDailyLogFile.is_open())
1367                fDailyLogFile.close();
1368        if (fDailyReportFile.is_open())
1369                fDailyReportFile.close();
1370
1371        if (fRunLogFile.is_open())
1372                fRunLogFile.close();
1373        if (fRunReportFile.is_open())
1374                fRunReportFile.close();
1375               
1376#ifdef HAS_FITS
1377        for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1378                for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1379                {
1380                                if (j->second.dailyFile.IsOpen())
1381                                        j->second.dailyFile.Close();
1382                                if (j->second.runFile.IsOpen())
1383                                        j->second.runFile.Close();     
1384                }
1385#ifdef ONE_RUN_FITS_ONLY
1386        if (fRunFitsFile != NULL)
1387        {
1388                delete fRunFitsFile;
1389                fRunFitsFile = NULL;
1390                (fNumSubAndFitsData.numOpenFits)--;
1391        }
1392#endif
1393#endif
1394        if (GetCurrentState() == kSM_Logging)
1395                NotifyOpenedFile("None", 0, fOpenedRunFiles);
1396        if (GetCurrentState() == kSM_Logging || 
1397            GetCurrentState() == kSM_WaitingRun || 
1398            GetCurrentState() == kSM_DailyOpen)
1399        { 
1400                NotifyOpenedFile("None", 0, fOpenedDailyFiles);
1401                fNumSubAndFits->updateService();
1402        }
1403        return kSM_Ready;
1404}
1405// --------------------------------------------------------------------------
1406//
1407//! Implements the transition towards kSM_WaitingRun
1408//! Does nothing really.
1409//!     @returns
1410//!             kSM_WaitingRun
1411int DataLogger::DailyToWaitRunPlease()
1412{
1413        return kSM_WaitingRun; 
1414}
1415
1416// --------------------------------------------------------------------------
1417
1418int RunDim(Configuration &conf)
1419{
1420    WindowLog wout;
1421
1422    //log.SetWindow(stdscr);
1423    if (conf.Has("log"))
1424        if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1425            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1426
1427    // Start io_service.Run to use the StateMachineImp::Run() loop
1428    // Start io_service.run to only use the commandHandler command detaching
1429    DataLogger logger(wout);
1430    logger.Run(true);
1431
1432    return 0;
1433}
1434
1435void RunThread(DataLogger *logger)
1436{
1437    // This is necessary so that the StateMachien Thread can signal the
1438    // Readline thread to exit
1439    logger->Run(true);
1440    Readline::Stop();
1441}
1442
1443template<class T>
1444int RunShell(Configuration &conf)
1445{
1446    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1447
1448    WindowLog &win  = shell.GetStreamIn();
1449    WindowLog &wout = shell.GetStreamOut();
1450
1451    if (conf.Has("log"))
1452        if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1453            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1454
1455    DataLogger logger(wout);
1456    shell.SetReceiver(logger);
1457
1458    boost::thread t(boost::bind(RunThread, &logger));
1459
1460    shell.Run();    // Run the shell
1461
1462    logger.Stop();  // Signal Loop-thread to stop
1463
1464    // Wait until the StateMachine has finished its thread
1465    // before returning and destroying the dim objects which might
1466    // still be in use.
1467    t.join();
1468
1469    return 0;
1470}
1471
1472/*
1473 Extract usage clause(s) [if any] for SYNOPSIS.
1474 Translators: "Usage" and "or" here are patterns (regular expressions) which
1475 are used to match the usage synopsis in program output.  An example from cp
1476 (GNU coreutils) which contains both strings:
1477  Usage: cp [OPTION]... [-T] SOURCE DEST
1478    or:  cp [OPTION]... SOURCE... DIRECTORY
1479    or:  cp [OPTION]... -t DIRECTORY SOURCE...
1480 */
1481void PrintUsage()
1482{
1483    cout << "\n"
1484        "The data logger connects to all available Dim services and "
1485        "writes them to ascii and fits files.\n"
1486        "\n"
1487        "Usage: dataLogger [-c type] [OPTIONS]\n"
1488        "  or:  dataLogger [OPTIONS]\n"
1489        "\n"
1490        "Options:\n"
1491        "The following describes the available commandline options. "
1492        "For further details on how command line option are parsed "
1493        "and in which order which configuration sources are accessed "
1494        "please refer to the class reference of the Configuration class.";
1495    cout << endl;
1496
1497}
1498
1499void PrintHelp()
1500{
1501    cout << "\n"
1502        "The default is that the program is started without user interaction. "
1503        "All actions are supposed to arrive as DimCommands. Using the -c "
1504        "option, a local shell can be initialized. With h or help a short "
1505        "help message about the usuage can be brought to the screen."
1506        << endl;
1507
1508    /*
1509     cout << "bla bla bla" << endl << endl;
1510     cout << endl;
1511     cout << "Environment:" << endl;
1512     cout << "environment" << endl;
1513     cout << endl;
1514     cout << "Examples:" << endl;
1515     cout << "test exam" << endl;
1516     cout << endl;
1517     cout << "Files:" << endl;
1518     cout << "files" << endl;
1519     cout << endl;
1520     */
1521}
1522
1523/*
1524 The first line of the --version information is assumed to be in one
1525 of the following formats:
1526
1527   <version>
1528   <program> <version>
1529   {GNU,Free} <program> <version>
1530   <program> ({GNU,Free} <package>) <version>
1531   <program> - {GNU,Free} <package> <version>
1532
1533 and separated from any copyright/author details by a blank line.
1534
1535 Handle multi-line bug reporting sections of the form:
1536
1537   Report <program> bugs to <addr>
1538   GNU <package> home page: <url>
1539   ...
1540*/
1541void PrintVersion(const char *name)
1542{
1543    cout <<
1544        name << " - "PACKAGE_STRING"\n"
1545        "\n"
1546        "Written by Thomas Bretz et al.\n"
1547        "\n"
1548        "Report bugs to <"PACKAGE_BUGREPORT">\n"
1549        "Home page: "PACKAGE_URL"\n"
1550        "\n"
1551        "Copyright (C) 2011 by the FACT Collaboration.\n"
1552        "This is free software; see the source for copying conditions.\n"
1553        << endl;
1554}
1555
1556
1557void SetupConfiguration(Configuration &conf)
1558{
1559    const string n = conf.GetName()+".log";
1560
1561    po::options_description config("Program options");
1562    config.add_options()
1563        ("dns",       var<string>("localhost"),  "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1564        ("log,l",     var<string>(n), "Write log-file")
1565        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1566        ;
1567
1568    conf.AddEnv("dns", "DIM_DNS_NODE");
1569
1570    conf.AddOptions(config);
1571}
1572
1573int main(int argc, const char* argv[])
1574{
1575    Configuration conf(argv[0]);
1576    conf.SetPrintUsage(PrintUsage);
1577    SetupConfiguration(conf);
1578
1579    po::variables_map vm;
1580    try
1581    {
1582        vm = conf.Parse(argc, argv);
1583    }
1584    catch (std::exception &e)
1585    {
1586#if BOOST_VERSION > 104000
1587        po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
1588        if (MO)
1589            cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
1590        else
1591#endif
1592            cout << "Error: " << e.what() << endl;
1593        cout << endl;
1594
1595        return -1;
1596    }
1597
1598    if (conf.HasPrint())
1599        return -1;
1600
1601    if (conf.HasVersion())
1602    {
1603        PrintVersion(argv[0]);
1604        return -1;
1605    }
1606
1607    if (conf.HasHelp())
1608    {
1609        PrintHelp();
1610        return -1;
1611    }
1612
1613    setenv("DIM_DNS_NODE", conf.Get<string>("dns").c_str(), 1);
1614
1615    try
1616    {
1617        // No console access at all
1618        if (!conf.Has("console"))
1619            return RunDim(conf);
1620
1621        // Console access w/ and w/o Dim
1622        if (conf.Get<int>("console")==0)
1623            return RunShell<LocalShell>(conf);
1624        else
1625            return RunShell<LocalConsole>(conf);
1626    }
1627    catch (std::exception& e)
1628    {
1629        cerr << "Exception: " << e.what() << endl;
1630        return -1;
1631    }
1632
1633    return 0;
1634}
Note: See TracBrowser for help on using the repository browser.