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

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