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

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