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

Last change on this file since 10292 was 10292, checked in by tbretz, 9 years ago
Replaced -> by . in AddTransition and AddConfiguration.
File size: 37.2 KB
Line 
1//****************************************************************
2/** @class DataLogger
3 
4  @brief Logs all message and infos between the services
5 
6  This is the main logging class facility.
7  It derives from StateMachineDim and DimInfoHandler. the first parent is here to enforce
8  a state machine behaviour, while the second one is meant to make the dataLogger receive
9  dim services to which it subscribed from.
10  The possible states and transitions of the machine are:
11  \dot
12  digraph datalogger {
13                node [shape=record, fontname=Helvetica, fontsize=10];
14        e [label="Error" color="red"];
15   r [label="Ready"]
16   d [label="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
54//#define HAS_FITS
55
56#include <fstream>
57
58#include <boost/bind.hpp>
59
60#ifdef HAS_FITS
61#include <astroroot.h>
62#endif
63
64class DataLogger : public StateMachineDim, DimInfoHandler
65{
66public:
67        /// The list of existing states specific to the DataLogger
68        enum
69        {
70                kSM_DailyOpen = 20, ///< Daily file openned and writing
71                kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
72                kSM_Logging = 40, ///< both files openned and writing
73                kSM_BadDailyConfig = 0x101, ///< the folder specified for daily logging does not exist or has bad permissions
74                kSM_BadRunConfig = 0x102, ///<  the folder specified for the run logging does not exist or has wrong permissions or no run number
75        } localstates_t;
76       
77        DataLogger(std::ostream &out);
78        ~DataLogger(); 
79       
80private:
81        //Define all the data structure specific to the DataLogger here
82        /// ofstream for the dailyLogfile
83        std::ofstream fDailyLogFile;
84        /// ofstream for the run-specific Log file
85        std::ofstream fRunLogFile; 
86
87        /// ofstream for the daily report file
88        std::ofstream fDailyReportFile;
89        /// ofstream for the run-specific report file
90        std::ofstream fRunReportFile;
91        /// base path of the dailyfile
92        std::string fDailyFileName; 
93        ///base path of the run file
94        std::string fRunFileName; 
95        ///run number (-1 means no run number specified)
96        int fRunNumber; 
97        ///Current year
98        short fYear;
99        ///Current Month
100        short fMonth;
101        ///Current Day
102        short fDay;
103        ///Current Hour
104        short fHour;
105        ///Current Minute
106        short fMin;
107        ///Current Second
108        short fSec;
109        ///Current Milliseconds
110        int fMs;
111        ///Current Service Quality
112        int fQuality;
113        ///Modified Julian Date
114        double fMjD;
115       
116        ///Define all the static names
117        static const char* fConfigDay;
118        static const char* fConfigRun;
119        static const char* fConfigRunNumber;
120        static const char* fConfigLog;
121        static const char* fTransStart;
122        static const char* fTransStop;
123        static const char* fTransStartRun;
124        static const char* fTransStopRun;
125        static const char* fTransReset;
126        static const char* fTransWait;
127        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
128        ///Inherited from state machine impl
129        int Execute(); 
130       
131        ///Inherited from state machine impl
132        int Transition(const Event& evt); 
133       
134        ///Inherited from state machine impl
135        int Configure(const Event& evt); 
136       
137        //overloading of DIM's infoHandler function
138        void infoHandler(); 
139       
140        ///for obtaining the name of the existing services
141        ServiceList fServiceList;
142       
143       
144        ///A std pair to store both the DimInfo name and the actual DimInfo pointer
145//      typedef std::pair<std::string, DimStampedInfo*> subscriptionType;
146        ///All the services to which we've subscribed to. Sorted by server name
147//      std::map<const std::string, std::vector<subscriptionType > > fServiceSubscriptions;
148
149        ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file
150        struct SubscriptionType
151        { 
152#ifdef HAS_FITS
153                ///daily FITS output file
154                AstroRootIo     dailyFile;
155                ///run-specific FITS output file
156                AstroRootIo     runFile;
157#endif
158                ///the actual dimInfo pointer
159                DimStampedInfo* dimInfo;
160                ///the number of existing handlers to this structure.
161                ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor
162                int* numCopies;
163                void operator = (const SubscriptionType& other)
164                {
165#ifdef HAS_FITS
166                        dailyFile = other.dailyFile;
167                        runFile = other.runFile;
168#endif
169                        dimInfo = other.dimInfo;       
170                        numCopies = other.numCopies;
171                        (*numCopies)++;
172                }
173                SubscriptionType(const SubscriptionType& other)
174                {
175#ifdef HAS_FITS
176                        dailyFile = other.dailyFile;
177                        runFile = other.runFile;
178#endif
179                        dimInfo = other.dimInfo;
180                        numCopies = other.numCopies;
181                        (*numCopies)++; 
182                }
183                SubscriptionType(DimStampedInfo* info)
184                {
185                        dimInfo = info; 
186                        numCopies = new int(1);
187                }
188                SubscriptionType()
189                {
190                        dimInfo = NULL;
191                        numCopies = new int(1);
192                }
193                ~SubscriptionType()
194                {
195                        if (numCopies)
196                        (*numCopies)--;
197                        if (numCopies)
198                        if (*numCopies < 1)
199                        {
200#ifdef HAS_FITS
201                                if (dailyFile.IsOpen())
202                                        dailyFile.Close();
203                                if (runFile.IsOpen())
204                                        runFile.Close();
205#endif
206                                if (dimInfo)
207                                delete dimInfo;
208                                if (numCopies) 
209                                delete numCopies;
210                                dimInfo = NULL;
211                                numCopies = NULL;
212                        }
213                }
214        };
215        typedef std::map<const std::string, std::map<std::string, SubscriptionType>> SubscriptionsListType;
216        ///All the services to which we have subscribed to, sorted by server name.
217        SubscriptionsListType fServiceSubscriptions;
218
219
220        ///Reporting method for the services info received
221        void ReportPlease(DimInfo* I, SubscriptionType& sub); 
222
223        ///Configuration of the daily file path
224        int ConfigureDailyFileName(const Event& evt); 
225        ///Configuration fo the file name
226        int ConfigureRunFileName(const Event& evt); 
227        ///DEPREC - configuration of the run number
228        int ConfigureRunNumber(const Event& evt); 
229        ///logging method for the messages
230        int LogMessagePlease(const Event& evt); 
231        ///checks whether or not the current info being treated is a run number
232        void CheckForRunNumber(DimInfo* I); 
233
234        /// start transition
235        int StartPlease(); 
236        ///from waiting to logging transition
237        int StartRunPlease(); 
238        /// from logging to waiting transition
239        int StopRunPlease(); 
240        ///stop and reset transition
241        int GoToReadyPlease(); 
242        ///from dailyOpen to waiting transition
243        int DailyToWaitRunPlease(); 
244#ifdef HAS_FITS
245        ///Open fits files
246        void OpenFITSFilesPlease(SubscriptionType& sub);
247        ///Write data to FITS files
248        void WriteToFITS(SubscriptionType& sub);
249        ///Allocate the buffers required for fits
250        void AllocateFITSBuffers(SubscriptionType& sub);
251#endif
252public: 
253        ///checks with fServiceList whether or not the services got updated
254        void CheckForServicesUpdate(); 
255       
256}; //DataLogger
257
258//static members initialization
259//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 ?
260const char* DataLogger::fConfigDay = "CONFIG_DAY";
261const char* DataLogger::fConfigRun = "CONFIG_RUN";
262const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
263const char* DataLogger::fConfigLog = "LOG";
264const char* DataLogger::fTransStart = "START";
265const char* DataLogger::fTransStop = "STOP";
266const char* DataLogger::fTransStartRun = "START_RUN";
267const char* DataLogger::fTransStopRun = "STOP_RUN";
268const char* DataLogger::fTransReset = "RESET";
269const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
270const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
271
272// --------------------------------------------------------------------------
273//
274//! Default constructor. The name of the machine is given DATA_LOGGER
275//! and the state is set to kSM_Ready at the end of the function.
276//
277//!Setup the allows states, configs and transitions for the data logger
278//
279DataLogger::DataLogger(std::ostream &out) : StateMachineDim(out, "DATA_LOGGER")
280{
281                //initialize member data
282                fDailyFileName = "/home/lyard/log";
283                fRunFileName = "/home/lyard/log";
284                fRunNumber = 12345;
285                //Give a name to this machine's specific states
286                AddStateName(kSM_DailyOpen,      "DailyFileOpen");
287                AddStateName(kSM_WaitingRun,     "WaitForRun");
288                AddStateName(kSM_Logging,        "Logging");
289                AddStateName(kSM_BadDailyConfig, "ErrDailyFolder");
290                AddStateName(kSM_BadRunConfig,   "ErrRunFolder");
291
292                /*Add the possible transitions for this machine*/
293
294                //start the daily logging. daily file location must be specified already
295                AddTransition(kSM_DailyOpen, fTransStart, kSM_Ready, kSM_BadDailyConfig). 
296                    AssignFunction(boost::bind(&DataLogger::StartPlease, this));
297
298                //stop the data logging
299                AddTransition(kSM_Ready, fTransStop, kSM_DailyOpen, kSM_WaitingRun, kSM_Logging). 
300                    AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this));
301
302                //start the run logging. run file location must be specified already.
303                AddTransition(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig).
304                    AssignFunction(boost::bind(&DataLogger::StartRunPlease, this));
305
306                AddTransition(kSM_WaitingRun, fTransStopRun, kSM_Logging).
307                    AssignFunction(boost::bind(&DataLogger::StopRunPlease, this));
308
309                 //transition to exit error states. dunno if required or not, would close the daily file if already openned.
310                AddTransition(kSM_Ready, fTransReset, kSM_Error, kSM_BadDailyConfig, kSM_BadRunConfig, kSM_Error).
311                    AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this));
312
313                AddTransition(kSM_WaitingRun, fTransWait, kSM_DailyOpen).
314                    AssignFunction(boost::bind(&DataLogger::DailyToWaitRunPlease, this));
315
316                /*Add the possible configurations for this machine*/
317                //configure the daily file location. cannot be done before the file is actually opened
318                AddConfiguration(fConfigDay, "C", kSM_Ready, kSM_BadDailyConfig).
319                    AssignFunction(boost::bind(&DataLogger::ConfigureDailyFileName, this, _1));
320
321                 //configure the run file location. cannot be done before the file is actually opened, and not in a dailly related error.
322                AddConfiguration(fConfigRun, "C", kSM_Ready, kSM_BadDailyConfig, kSM_DailyOpen, kSM_WaitingRun, kSM_BadRunConfig).
323                    AssignFunction(boost::bind(&DataLogger::ConfigureRunFileName, this, _1));
324
325                //Provide a logging command
326                //I get the feeling that I should be going through the EventImp
327                //instead of DimCommand directly, mainly because the commandHandler
328                //is already done in StateMachineImp.cc
329                //Thus I'll simply add a configuration, which I will treat as the logging command
330                AddConfiguration(fConfigLog, "C", kSM_DailyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig).
331                    AssignFunction(boost::bind(&DataLogger::LogMessagePlease, this, _1));
332
333                fServiceList.SetHandler(this);
334                CheckForServicesUpdate();
335}
336// --------------------------------------------------------------------------
337//
338//! Checks for changes in the existing services.
339//! Any new service will be added to the service list, while the ones which disappeared are removed.
340//! @todo
341//!     add the configuration (using the conf class ?)
342//
343void DataLogger::CheckForServicesUpdate()
344{ 
345
346        //get the current server list
347        const std::vector<std::string> serverList = fServiceList.GetServerList();
348        //first let's remove the servers that may have disapeared
349        //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
350        std::vector<std::string> toBeDeleted;
351        for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
352        {
353                std::vector<std::string>::const_iterator givenServers;
354                for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
355                        if (cListe->first == *givenServers)
356                                break;
357                if (givenServers == serverList.end())//server vanished. Remove it
358                        toBeDeleted.push_back(cListe->first);
359        }
360        for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
361                fServiceSubscriptions.erase(*it);
362
363        //now crawl through the list of servers, and see if there was some updates
364        for (std::vector<std::string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
365        {
366                //skip the two obvious excluded services
367                if ((i->find("DIS_DNS") != std::string::npos) ||
368                    (i->find("DATA_LOGGER") != std::string::npos))
369                        continue;
370                //find the current server in our subscription list     
371                SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
372                //get the service list of the current server
373                std::vector<std::string> cServicesList = fServiceList.GetServiceList(*i);
374                if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
375                {                                                                                //then check and update our list of subscriptions
376                        //first, remove the services that may have dissapeared.
377                        std::map<std::string, SubscriptionType>::iterator serverSubs;
378                        std::vector<std::string>::const_iterator givenSubs;
379                        toBeDeleted.clear();
380                        for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
381                        {
382                                for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
383                                        if (serverSubs->first == *givenSubs)
384                                                break;
385                                if (givenSubs == cServicesList.end())
386                                {
387                                        toBeDeleted.push_back(serverSubs->first);
388                                }       
389                        }
390                        for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
391                                cSubs->second.erase(*it);
392                        //now check for new services
393                        for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
394                        {
395                                if (*givenSubs == "SERVICE_LIST")
396                                        continue;
397                                if (cSubs->second.find(*givenSubs) == cSubs->second.end())
398                                {//service not found. Add it
399                                        cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast<char*>(""), this);
400                                }       
401                        }
402                }
403                else //server not found in our list. Create its entry
404                {
405                        fServiceSubscriptions[*i] = std::map<std::string, SubscriptionType>();
406                        std::map<std::string, SubscriptionType>& liste = fServiceSubscriptions[*i];
407                        for (std::vector<std::string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
408                        {
409                                if (*j == "SERVICE_LIST")
410                                        continue;
411                                liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast<char*>(""), this);
412                        }
413                }       
414        }
415}
416// --------------------------------------------------------------------------
417//
418//! Destructor
419//
420DataLogger::~DataLogger()
421{
422        //close the files
423        if (fDailyLogFile.is_open())
424                fDailyLogFile.close();
425        if (fDailyReportFile.is_open())
426                fDailyReportFile.close();
427        if (fRunLogFile.is_open())
428                fRunLogFile.close();
429        if (fRunReportFile.is_open())
430                fRunReportFile.close();
431        //release the services subscriptions
432        fServiceSubscriptions.clear();
433}
434// --------------------------------------------------------------------------
435//
436//! Execute
437//! Shouldn't be run as we use callbacks instead
438//
439int DataLogger::Execute()
440{
441        //due to the callback mecanism, this function should never be called
442        return kSM_FatalError;
443       
444        switch (GetCurrentState())
445        {
446        case kSM_Error:
447        case kSM_Ready:
448        case kSM_DailyOpen:
449        case kSM_WaitingRun:
450        case kSM_Logging:
451        case kSM_BadDailyConfig:
452        case kSM_BadRunConfig:
453                return GetCurrentState();
454        }
455        //this line below should never be hit. It here mainly to remove warnings at compilation
456        return kSM_FatalError;
457}
458// --------------------------------------------------------------------------
459//
460//! Shouldn't be run as we use callbacks instead
461//
462 int DataLogger::Transition(const Event& evt)
463{
464        //due to the callback mecanism, this function should never be called
465        return kSM_FatalError;
466       
467        switch (evt.GetTargetState())
468        {
469                case kSM_Ready:
470                /*here we must figure out whether the STOP or RESET command was sent*/
471                /*close opened files and go back to ready state*/
472                        switch (GetCurrentState())
473                        {
474                                case kSM_BadDailyConfig:
475                                case kSM_BadRunConfig:
476                                case kSM_Error:
477                                        return GoToReadyPlease();
478                                       
479                                case kSM_Logging:
480                                case kSM_WaitingRun:
481                                case kSM_DailyOpen:
482                                        return GoToReadyPlease();
483                        }
484                break;
485               
486                case kSM_DailyOpen:
487                        /*Attempt to open the daily file */
488                        switch (GetCurrentState())
489                        {
490                                case kSM_Ready:
491                                case kSM_BadDailyConfig:
492                                        return StartPlease();   
493                        }
494                break;
495               
496                case kSM_WaitingRun:
497                        /*either close the run file, or just go to the waitingrun state (if coming from daily open*/
498                        switch (GetCurrentState())
499                        {
500                                case kSM_DailyOpen:
501                                        return kSM_WaitingRun;
502                               
503                                case kSM_Logging:
504                                        return StopRunPlease(); 
505                        }
506                break;
507               
508                case kSM_Logging:
509                        /*Attempt to open run file */
510                        switch (GetCurrentState())
511                        {
512                                case kSM_WaitingRun:
513                                case kSM_BadRunConfig:
514                                        return StartRunPlease();
515                        }       
516                break;
517        }
518        //Getting here means that an invalid transition has been asked.
519        //TODO Log an error message
520        //and return the fatal error state
521        return kSM_FatalError;
522}
523// --------------------------------------------------------------------------
524//
525//! Shouldn't be run as we use callbacks instead
526//
527 int DataLogger::Configure(const Event& evt)
528{
529        //due to the callback mecanism, this function should never be called
530        return kSM_FatalError;
531
532        switch (evt.GetTargetState())
533        {
534                case kSM_Ready:
535                case kSM_BadDailyConfig:
536                        return ConfigureDailyFileName(evt);
537                break;
538               
539                case kSM_WaitingRun:
540                case kSM_BadRunConfig:
541                        return ConfigureRunFileName(evt);       
542                break;
543               
544                case kSM_Logging:
545                case kSM_DailyOpen:
546//TODO check that this is indeed correct
547                                return 0;//LogMessagePlease(evt);       
548                break;
549
550        }       
551        //Getting here means that an invalid configuration has been asked.
552        //TODO Log an error message
553        //and return the fatal error state
554        return kSM_FatalError;
555}
556// --------------------------------------------------------------------------
557//
558//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
559//
560void DataLogger::infoHandler()
561{
562        DimInfo* I = getInfo();
563        if (I==NULL)
564        {
565                CheckForServicesUpdate();
566                return;
567        }
568        //check if the service pointer corresponds to something that we subscribed to
569        //this is a fix for a bug that provides bad Infos when a server starts
570        bool found = false;
571        SubscriptionsListType::iterator x;
572        std::map<std::string, SubscriptionType>::iterator y;
573        for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
574        {//instead of extracting the server and service names, I crawl through my records. dunno what's faster, but both should work
575                for (y=x->second.begin(); y!=x->second.end();y++)
576                        if (y->second.dimInfo == I)
577                        {
578                                found = true;   
579                                break;
580                        }
581                if (found)
582                        break;
583        }
584        if (!found)
585                return;
586        if (I->getSize() <= 0)
587                return;
588        //check that the message has been updated by something, i.e. must be different from its initial value
589        if (I->getTimestamp() == 0)
590                return;
591
592        CheckForRunNumber(I);
593        ReportPlease(I, y->second);
594}
595
596// --------------------------------------------------------------------------
597//
598//! Checks whether or not the current info is a run number.
599//! If so, then remember it. A run number is required to open the run-log file
600//! @param I
601//!             the current DimInfo
602//
603void DataLogger::CheckForRunNumber(DimInfo* I)
604{
605        return;
606        if (strstr(I->getName(), fRunNumberInfo) != NULL)
607        {//assumes that the run number is an integer
608                //TODO check the format here
609                fRunNumber = I->getInt();       
610        }
611}
612
613// --------------------------------------------------------------------------
614//
615//! write infos to log files.
616//! @param I
617//!     The current DimInfo
618//
619void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
620{
621        //should we log or report this info ? (i.e. is it a message ?)
622        bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
623       
624//      std::ofstream & dailyFile = fDailyReportFile;//isItaReport? fDailyReportFile : fDailyLogFile;
625//      std::ofstream & runFile = fRunReportFile;//isItaReport? fRunReportFile : fRunLogFile;
626
627        //TODO add service exclusion
628        if (!fDailyReportFile.is_open())
629                return;
630               
631        //construct the header
632        std::stringstream header;
633       
634        Time cTime((time_t)(I->getTimestamp()), I->getTimestampMillisecs());
635
636        //Buffer them for FITS write
637        //TODO this has been replaced by MjD. So I guess that these member variables can go.
638        fYear = cTime.Y(); fMonth = cTime.M(); fDay = cTime.D();
639        fHour = cTime.h(); fMin = cTime.m(); fSec = cTime.s();
640        fMs = cTime.ms(); fQuality = I->getQuality();
641       
642        fMjD = cTime.Mjd();
643       
644        if (isItaReport)
645        {
646                //write text header
647                header << I->getName() << " " << fQuality << " ";
648                header << fYear << " " << fMonth << " " << fDay << " ";
649                header << fHour << " " << fMin << " " << fSec << " ";
650                header << fMs << " " << I->getTimestamp() << " ";
651
652                const Converter conv(Out(), I->getFormat());
653                if (!conv)
654                {
655                    Error("Couldn't properly parse the format... ignored.");
656                    return;
657                }
658
659                std::string text;
660                try
661                {
662                    text = conv.GetString(I->getData(), I->getSize());
663                }
664                catch (const std::runtime_error &e)
665                {
666                    Out() << kRed << e.what() << endl;
667                    Error("Couldn't properly parse the data... ignored.");
668                    return;
669                }
670
671                if (text.empty())
672                    return;
673
674                //replace bizarre characters by white space
675                replace(text.begin(), text.end(), '\n', '\\');
676                replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
677       
678                if (fDailyReportFile.is_open())
679                        fDailyReportFile << header.str();
680                if (fRunReportFile.is_open())
681                        fRunReportFile << header.str();
682
683                if (fDailyReportFile.is_open())
684                        fDailyReportFile << text << std::endl;
685                if (fRunReportFile.is_open())
686                        fRunReportFile << text << std::endl;
687        }
688        else
689        {
690                std::string n = I->getName();
691                std::stringstream msg;
692                msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString();
693                MessageImp dailyMess(fDailyLogFile);
694                dailyMess.Write(cTime, msg.str().c_str(), fQuality);
695                if (fRunLogFile.is_open())
696                {
697                        MessageImp runMess(fRunLogFile);
698                        runMess.Write(cTime, msg.str().c_str(), fQuality);
699                }
700        }
701       
702#ifdef HAS_FITS
703        if (!sub.dailyFile.IsOpen() || !sub.runFile.IsOpen())
704                OpenFITSFilesPlease(sub);       
705        WriteToFITS(sub);
706#endif
707
708}
709// --------------------------------------------------------------------------
710//
711//! write messages to logs.
712//! @param evt
713//!             the current event to log
714//! @returns
715//!             the new state. Currently, always the current state
716//!
717//! @deprecated
718//!    I guess that this function should not be any longer
719//
720int DataLogger::LogMessagePlease(const Event& evt)
721{
722        if (!fDailyLogFile.is_open())
723                return GetCurrentState();
724       
725        std::stringstream header;
726        const Time& cTime = evt.GetTime();
727        header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
728        header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
729        header << cTime.ms() << " ";
730               
731//      std::string text = ToString(evt.GetFormat().c_str(), evt.GetData(), evt.GetSize());
732
733        const Converter conv(Out(), evt.GetFormat());
734        if (!conv)
735        {
736            Error("Couldn't properly parse the format... ignored.");
737            return GetCurrentState();
738        }
739
740        std::string text;
741        try
742        {
743            text = conv.GetString(evt.GetData(), evt.GetSize());
744        }
745        catch (const std::runtime_error &e)
746        {
747            Out() << kRed << e.what() << endl;
748            Error("Couldn't properly parse the data... ignored.");
749            return GetCurrentState();
750        }
751
752
753        if (text.empty())
754            return GetCurrentState();
755
756        //replace bizarre characters by white space
757        replace(text.begin(), text.end(), '\n', '\\');
758        replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
759
760        if (fDailyLogFile.is_open())
761                fDailyLogFile << header;
762        if (fRunLogFile.is_open())
763                fRunLogFile << header;
764
765        if (fDailyLogFile.is_open())
766                fDailyLogFile << text;
767        if (fRunLogFile.is_open())
768                fRunLogFile << text;
769
770        return GetCurrentState();
771}
772// --------------------------------------------------------------------------
773//
774//!     Sets the path to use for the daily log file.
775//! @param evt
776//!     the event transporting the path
777//! @returns
778//!             currently only the current state.
779//
780int DataLogger::ConfigureDailyFileName(const Event& evt)
781{
782        if (evt.GetText() != NULL)
783                fDailyFileName = std::string(evt.GetText());   
784        else
785                Error("Empty daily folder");
786
787        return GetCurrentState();
788}
789// --------------------------------------------------------------------------
790//
791//! Sets the path to use for the run log file.
792//! @param evt
793//!             the event transporting the path
794//! @returns
795//!     currently only the current state
796int DataLogger::ConfigureRunFileName(const Event& evt)
797{
798        if (evt.GetText() != NULL)
799                fRunFileName = std::string(evt.GetText());
800        else
801                Error("Empty daily folder");
802
803        return GetCurrentState();
804}
805// --------------------------------------------------------------------------
806//
807//! Sets the run number.
808//! @param evt
809//!             the event transporting the run number
810//! @returns
811//!     currently only the current state
812int DataLogger::ConfigureRunNumber(const Event& evt)
813{
814        fRunNumber = evt.GetInt();
815
816        return GetCurrentState();
817}
818// --------------------------------------------------------------------------
819//
820//! Implements the Start transition.
821//! Concatenates the given path for the daily file and the filename itself (based on the day),
822//! and tries to open it.
823//! @returns
824//!             kSM_DailyOpen if success, kSM_BadDailyConfig if failure
825int DataLogger::StartPlease()
826{
827        //TODO concatenate the dailyFileName and the formatted date and extension to obtain the full file name
828        Time time;//(Time::utc);
829        std::stringstream sTime;
830        sTime << time.Y() << "_" << time.M() << "_" << time.D();
831        std::string fullName = fDailyFileName + '/' + sTime.str() + ".log"; 
832       
833        fDailyLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be "app" instead of "ate" ??
834        fullName = fDailyFileName + '/' + sTime.str() + ".rep";
835        fDailyReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app);
836        if (!fDailyLogFile.is_open() || !fDailyReportFile.is_open())
837        {
838                //TODO send an error message   
839            return kSM_BadDailyConfig;
840        }
841        return kSM_DailyOpen;   
842}
843
844#ifdef HAS_FITS
845// --------------------------------------------------------------------------
846//
847//! open if required a the FITS files corresponding to a given subscription
848//! @param sub
849//!     the current DimInfo subscription being examined
850void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub)
851{
852        std::string serviceName(sub.dimInfo->getName());
853        for (unsigned int i=0;i<serviceName.size(); i++)
854        {
855                if (serviceName[i] == '/')
856                {
857                        serviceName[i] = '_';
858                        break; 
859                }       
860        }
861        Time time;
862        std::stringstream sTime;
863        sTime << time.Y() << "_" << time.M() << "_" << time.D();
864        //we open the dailyFile anyway, otherwise this function shouldn't have been called.
865        if (!sub.dailyFile.IsOpen())
866        {
867                std::string partialName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits";
868                std::string fullName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits[" + serviceName + "]";
869
870                AllocateFITSBuffers(sub);
871                //currently, the FITS are written in the same directory as the text files.
872                //thus the write permissions have already been checked by the text files.
873                //if the target folder changes, then I should check the write permissions here
874                //now we only check whether the target file exists or not
875                std::ifstream readTest(partialName.c_str());
876                if (readTest.is_open())
877                {
878                        readTest.close();
879                        sub.dailyFile.Open(fullName.c_str(), "UPDATE");
880                }
881                else {
882                        sub.dailyFile.Open(fullName.c_str(), "CREATE"); 
883                }
884               
885//TODO Write the header's attributes
886        }
887        if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
888        {//buffer for the run file have already been allocated when doing the daily file
889                std::stringstream sRun;
890                sRun << fRunNumber;
891                std::string partialName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits";
892                std::string fullName = fRunFileName + '/' +  sRun.str() + '_' + serviceName + ".fits[" + serviceName + "]";
893               
894                std::ifstream readTest(partialName.c_str());
895                if (readTest.is_open())
896                {
897                        readTest.close();
898                        sub.runFile.Open(fullName.c_str(), "UPDATE");
899                }
900                else
901                        sub.runFile.Open(fullName.c_str(), "CREATE");
902//TODO Write the header's attributes
903        }
904}       
905// --------------------------------------------------------------------------
906//
907void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
908{
909         const char* format = sub.dimInfo->getFormat();
910         const int size = sub.dimInfo->getSize();
911         
912         //Init the time columns of the file
913         sub.dailyFile.InitCol("Date", "double", &fMjD);
914         sub.runFile.InitCol("Date", "double", &fMjD);
915         
916//       sub.dailyFile.InitCol("Year", "short", &fYear);
917//       sub.dailyFile.InitCol("Month", "short", &fMonth);
918//       sub.dailyFile.InitCol("Day", "short", &fDay);
919//       sub.dailyFile.InitCol("Hour", "short", &fHour);
920//       sub.dailyFile.InitCol("Minute", "short", &fMin);
921//       sub.dailyFile.InitCol("Second", "short", &fSec);
922//       sub.dailyFile.InitCol("MilliSec", "int", &fMs);
923         sub.dailyFile.InitCol("QoS", "int", &fQuality);
924
925//       sub.runFile.InitCol("Year", "short", &fYear);
926//       sub.runFile.InitCol("Month", "short", &fMonth);
927//       sub.runFile.InitCol("Day", "short", &fDay);
928//       sub.runFile.InitCol("Hour", "short", &fHour);
929//       sub.runFile.InitCol("Minute", "short", &fMin);
930//       sub.runFile.InitCol("Second", "short", &fSec);
931//       sub.runFile.InitCol("MilliSec", "int", &fMs);
932         sub.runFile.InitCol("QoS", "int", &fQuality);
933
934         const Converter::FormatList flist = Converter::Compile(Out(), format);
935
936         // Compilation failed
937         if (fList.empty() || fList.back().first.second!=0)
938         {
939             Error("Compilation of format string failed.");
940             return;
941         }
942
943         //we've got a nice structure describing the format of this service's messages.
944         //Let's create the appropriate FITS columns
945         for (unsigned int i=0;i<flist.size();i++)
946         {
947                std::stringstream colName;
948                std::stringstream dataQualifier; 
949                void * dataPointer = static_cast<char*>(sub.dimInfo->getData()) + flist[i].second.second;
950                colName << "Data" << i;
951                dataQualifier << flist[i].second.first;
952                switch (flist[i].first.first)
953                {
954                        case 'c':
955                                dataQualifier <<  "S";
956                        break;
957                        case 's':
958                                dataQualifier << "I";
959                        break;
960                        case 'i':
961                                dataQualifier << "J";
962                        break;
963                        case 'l':
964                                dataQualifier << "J";
965                                //TODO triple check that in FITS, long = int
966                        break;
967                        case 'f':
968                                dataQualifier << "E";
969                        break;
970                        case 'd':
971                                dataQualifier << "D";
972                        break;
973                        case 'x':
974                                dataQualifier << "K";
975                        break;
976                        case 'S':
977                                //for strings, the number of elements I get is wrong. Correct it
978                                dataQualifier.str(""); //clear
979                                dataQualifier << size-1 <<  "A";
980                        break;
981                       
982                        default:
983                                Error("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 962.");
984                };
985                sub.dailyFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer);
986                sub.runFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer);
987         }
988
989//TODO init the attributes
990}
991// --------------------------------------------------------------------------
992//
993//! write a dimInfo data to its corresponding FITS files
994//
995void DataLogger::WriteToFITS(SubscriptionType& sub)
996{
997                //dailyFile status (open or not) already checked
998                if (sub.dailyFile.IsOpen())
999                        sub.dailyFile.Write();
1000                if (sub.runFile.IsOpen())
1001                        sub.runFile.Write();
1002}
1003#endif //if has_fits
1004// --------------------------------------------------------------------------
1005//
1006//! Implements the StartRun transition.
1007//! Concatenates the given path for the run file and the filename itself (based on the run number),
1008//! and tries to open it.
1009//! @returns
1010//!             kSM_Logging if success, kSM_BadRunConfig if failure.
1011int DataLogger::StartRunPlease()
1012{
1013        //attempt to open run file with current parameters
1014        if (fRunNumber == -1)
1015                return kSM_BadRunConfig;
1016        std::stringstream sRun;
1017        sRun << fRunNumber;
1018        std::string fullName = fRunFileName + '/' + sRun.str() + ".log";
1019        fRunLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate
1020
1021        fullName = fRunFileName + '/' + sRun.str() + ".rep";
1022        fRunReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app);
1023       
1024        if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1025        {
1026                //TODO send an error message
1027                return kSM_BadRunConfig;       
1028        }
1029       
1030        return kSM_Logging;
1031}
1032// --------------------------------------------------------------------------
1033//
1034//! Implements the StopRun transition.
1035//! Attempts to close the run file.
1036//! @returns
1037//!             kSM_WaitingRun if success, kSM_FatalError otherwise
1038int DataLogger::StopRunPlease()
1039{
1040        if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1041                return kSM_FatalError;
1042       
1043        fRunLogFile.close();
1044        fRunReportFile.close();
1045#ifdef HAS_FITS
1046        for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1047                for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1048                {
1049                                if (j->second.runFile.IsOpen())
1050                                        j->second.runFile.Close();     
1051                }
1052#endif
1053        return kSM_WaitingRun;
1054
1055}
1056// --------------------------------------------------------------------------
1057//
1058//! Implements the Stop and Reset transitions.
1059//! Attempts to close any openned file.
1060//! @returns
1061//!     kSM_Ready
1062int DataLogger::GoToReadyPlease()
1063{
1064        if (fDailyLogFile.is_open())
1065                fDailyLogFile.close();
1066        if (fDailyReportFile.is_open())
1067                fDailyReportFile.close();
1068
1069        if (fRunLogFile.is_open())
1070                fRunLogFile.close();
1071        if (fRunReportFile.is_open())
1072                fRunReportFile.close();
1073               
1074#ifdef HAS_FITS
1075        for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1076                for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1077                {
1078                                if (j->second.dailyFile.IsOpen())
1079                                        j->second.dailyFile.Close();
1080                                if (j->second.runFile.IsOpen())
1081                                        j->second.runFile.Close();     
1082                }
1083#endif
1084        return kSM_Ready;
1085}
1086// --------------------------------------------------------------------------
1087//
1088//! Implements the transition towards kSM_WaitingRun
1089//! Does nothing really.
1090//!     @returns
1091//!             kSM_WaitingRun
1092int DataLogger::DailyToWaitRunPlease()
1093{
1094        return kSM_WaitingRun; 
1095}
1096
1097// --------------------------------------------------------------------------
1098
1099int RunDim(Configuration &conf)
1100{
1101    WindowLog wout;
1102
1103    //log.SetWindow(stdscr);
1104    if (conf.Has("log"))
1105        if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1106            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1107
1108    // Start io_service.Run to use the StateMachineImp::Run() loop
1109    // Start io_service.run to only use the commandHandler command detaching
1110    DataLogger logger(wout);
1111    logger.Run(true);
1112
1113    return 0;
1114}
1115
1116template<class T>
1117int RunShell(Configuration &conf)
1118{
1119    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1120
1121    WindowLog &win  = shell.GetStreamIn();
1122    WindowLog &wout = shell.GetStreamOut();
1123
1124    if (conf.Has("log"))
1125        if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1126            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1127
1128    DataLogger logger(wout);
1129
1130    shell.SetReceiver(logger);
1131
1132    logger.SetReady();
1133    shell.Run();                 // Run the shell
1134    logger.SetNotReady();
1135
1136    return 0;
1137}
1138
1139void SetupConfiguration(Configuration &conf)
1140{
1141    const std::string n = conf.GetName()+".log";
1142
1143    po::options_description config("Program options");
1144    config.add_options()
1145        ("dns",       var<std::string>("localhost"),       "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1146        ("log,l",     var<std::string>(n), "Write log-file")
1147        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1148        ;
1149
1150    conf.AddEnv("dns", "DIM_DNS_NODE");
1151
1152    conf.AddOptions(config);
1153}
1154
1155int main(int argc, char* argv[])
1156{
1157    Configuration conf(argv[0]);
1158    SetupConfiguration(conf);
1159
1160    po::variables_map vm;
1161    try
1162    {
1163        vm = conf.Parse(argc, argv);
1164    }
1165#if BOOST_VERSION > 104000
1166    catch (po::multiple_occurrences &e)
1167    {
1168        std::cout << "Error: " << e.what() << " of '" << e.get_option_name() << "' option." << std::endl;
1169        std::cout << std::endl;
1170        return -1;
1171    }
1172#endif
1173    catch (std::exception &e)
1174    {
1175        std::cout << "Error: " << e.what() << std::endl;
1176        std::cout << std::endl;
1177
1178        return -1;
1179    }
1180
1181    if (conf.HasHelp() || conf.HasPrint())
1182        return -1;
1183
1184    // To allow overwriting of DIM_DNS_NODE set 0 to 1
1185    setenv("DIM_DNS_NODE", conf.Get<std::string>("dns").c_str(), 1);
1186
1187    try
1188    {
1189        // No console access at all
1190        if (!conf.Has("console"))
1191            return RunDim(conf);
1192
1193        // Cosole access w/ and w/o Dim
1194        if (conf.Get<int>("console")==0)
1195            return RunShell<LocalShell>(conf);
1196        else
1197            return RunShell<LocalConsole>(conf);
1198    }
1199    catch (std::exception& e)
1200    {
1201        cerr << "Exception: " << e.what() << endl;
1202        return -1;
1203    }
1204
1205    return 0;
1206}
Note: See TracBrowser for help on using the repository browser.