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

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