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

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