source: trunk/FACT++/src/datalogger.cc@ 14602

Last change on this file since 14602 was 14574, checked in by lyard, 12 years ago
moved Mjd back 12 hours so that files are closed at noon
File size: 75.7 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 // FIXME FIXME: Error states missing...
13 digraph datalogger
14 {
15 node [shape=record, fontname=Helvetica, fontsize=10];
16
17 srt [label="Start" style="rounded"]
18 rdy [label="Ready"]
19 nop [label="NightlyOpen"]
20 wait [label="WaitingRun"]
21 log [label="Logging"]
22
23 //e [label="Error" color="red"];
24 //c [label="BadFolder" color="red"]
25
26
27 cmd_start [label="START" shape="none" height="0"]
28 cmd_stop [label="STOP" shape="none" height="0"]
29 cmd_stopr [label="STOP_RUN_LOGGING" shape="none" height="0"]
30 cmd_startr [label="START_RUN_LOGGING" shape="none" height="0"]
31
32 { rank=same; cmd_startr cmd_stopr }
33 { rank=same; cmd_start cmd_stop }
34
35
36 srt -> rdy
37
38 rdy -> cmd_start [ arrowhead="open" dir="both" arrowtail="tee" weight=10 ]
39 cmd_start -> nop
40
41 nop -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
42 wait -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
43 log -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
44 cmd_stop -> rdy
45
46 wait -> cmd_stopr [ arrowhead="none" dir="both" arrowtail="inv" ]
47 log -> cmd_stopr [ arrowhead="none" dir="both" arrowtail="inv" ]
48 cmd_stopr -> nop
49
50 nop -> cmd_startr [ arrowhead="none" dir="both" arrowtail="inv" weight=10 ]
51 rdy -> cmd_startr [ arrowhead="none" dir="both" arrowtail="inv" ]
52 cmd_startr -> wait [ weight=10 ]
53
54
55 wait -> log
56 log -> wait
57 }
58 \enddot
59
60 For questions or bug report, please contact Etienne Lyard (etienne.lyard@unige.ch) or Thomas Bretz.
61 */
62 //****************************************************************
63#include <unistd.h> //for getting stat of opened files
64//#include <sys/statvfs.h> //for getting disk free space
65//#include <sys/stat.h> //for getting files sizes
66#include <fstream>
67#include <functional>
68
69#include <boost/filesystem.hpp>
70
71#include "Dim.h"
72#include "Event.h"
73#include "StateMachineDim.h"
74#include "LocalControl.h"
75#include "Configuration.h"
76#include "Converter.h"
77#include "DimWriteStatistics.h"
78
79#include "Description.h"
80//#include "DimNetwork.h"
81
82#ifdef HAVE_FITS
83#include "Fits.h"
84#endif
85
86#include "DimState.h"
87
88//Dim structures
89///distributes the number of opened subscriptions and fits files
90struct NumSubAndFitsType {
91 uint32_t numSubscriptions;
92 uint32_t numOpenFits;
93};
94///distributes which files were opened.
95struct OpenFileToDim {
96 uint32_t code;
97 char fileName[FILENAME_MAX];
98};
99
100///Run number record. Used to keep track of which run numbers are still active
101struct RunNumberType {
102
103 ///the actual run number
104 int32_t runNumber;
105 ///the time at which the run number was received
106 Time time;
107 ///default constructor
108 RunNumberType()
109 {
110 runNumber = 0;
111 }
112 ///default destructor
113 ~RunNumberType()
114 {
115
116 }
117};
118
119EventImp nullEventImp;
120///Dim subscription type. Stores all the relevant info to handle a Dim subscription
121struct SubscriptionType
122{
123#ifdef HAVE_FITS
124 ///Nightly FITS output file
125 Fits nightlyFile;
126#endif
127 ///the server
128 string server;
129 ///the service
130 string service;
131 ///the converter for outputting the data according to the format
132 shared_ptr<Converter> fConv;
133 ///the current run number used by this subscription
134 int32_t runNumber;
135 ///time of the latest received event
136 Time lastReceivedEvent;
137 ///whether or not the fits buffer was allocated already
138 bool fitsBufferAllocated;
139 ///the actual dimInfo pointer (must be the last in the list to ensure
140 /// that it is the first which is deleted -- and consequently none of
141 /// the other members can still be in use in an infoHandler)
142 //DIM_REPLACE
143 //shared_ptr<DimStampedInfo> dimInfo;
144 unsigned int index;
145
146 ///Dim info constructor
147 //DIM_REPLACE
148// SubscriptionType(DimStampedInfo* info=NULL)
149 SubscriptionType()
150 {
151 fConv = shared_ptr<Converter>();
152 runNumber = 0;
153 lastReceivedEvent = Time::None;
154 fitsBufferAllocated = false;
155 // Should be the last instantiated to make sure that all other
156 // variables which might be used are already initialized
157 //DIM_REPLACE
158 //dimInfo = shared_ptr<DimStampedInfo>(info);
159 index = 0;
160 }
161 ///default destructor
162 ~SubscriptionType()
163 {
164 }
165};
166
167class DataLogger : public StateMachineDim
168//DIM_REPLACE
169//, DimServiceInfoListImp
170{
171public:
172 /// The list of existing states specific to the DataLogger
173 enum
174 {
175 kSM_NightlyOpen = 20, ///< Nightly file openned and writing
176 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
177 kSM_Logging = 40, ///< both files openned and writing
178 kSM_BadFolder = 0x101, ///< the folder specified for Nightly logging does not exist or has bad permissions
179 kSM_RunWriteError = 0x103, ///< Denotes that an error occured while writing a run file (text or fits).
180 kSM_DailyWriteError = 0x103, ///< Denots that an error occured while writing a daily file (text or fits).
181 } localstates_t;
182
183 DataLogger(ostream &out);
184 ~DataLogger();
185
186 int EvalOptions(Configuration& conf);
187
188private:
189 /************************************************
190 * MEMBER VARIABLES
191 ************************************************/
192 /// ofstream for the NightlyLogfile
193 ofstream fNightlyLogFile;
194 /// ofstream for the Nightly report file
195 ofstream fNightlyReportFile;
196 /// base path of files
197 string fFilePath;
198 ///run numbers
199 list<RunNumberType> fRunNumber;
200 ///old run numbers time-out delay (in seconds)
201 uint32_t fRunNumberTimeout;
202 ///previous run number. to check if changed while logging
203 int fPreviousRunNumber;
204 ///Current Service Quality
205 int fQuality;
206 ///Modified Julian Date
207 double fMjD;
208 ///for obtaining the name of the existing services
209// ServiceList fServiceList;
210 typedef map<const string, map<string, SubscriptionType> > SubscriptionsListType;
211 ///All the services to which we have subscribed to, sorted by server name.
212 SubscriptionsListType fServiceSubscriptions;
213 ///full name of the nightly log file
214 string fFullNightlyLogFileName;
215 ///full name of the nightly report file
216 string fFullNightlyReportFileName;
217 ///variable to track when the statistic were last calculated
218// Time fPreviousStatsUpdateTime;
219 Time fPreviousOldRunNumberCheck;
220 ///boolean to know whether we should close and reopen daily files or not
221 bool fDailyFileDayChangedAlready;
222
223 DimWriteStatistics fFilesStats;
224
225 ///map and mutex for storing services description
226 map<string, vector<Description> > fServiceDescriptionList;
227 mutex fMutex;
228 int HandleDescriptions(DimDescriptions* desc);
229 vector<Description> GetDescription(const string& server, const string& service);
230private:
231 /***************************************************
232 * DIM INFO HANDLER
233 ***************************************************/
234 //overloading of DIM's infoHandler function
235 int infoCallback(const EventImp& evt, unsigned int infoIndex);
236
237 /***************************************************
238 * TRANSITION FUNCTIONS
239 ***************************************************/
240 ///Reporting method for the services info received
241 void Report(const EventImp& evt, SubscriptionType& sub);
242
243 ///Configuration of the nightly file path
244 int ConfigureFilePath(const Event& evt);
245 ///print the current state of the dataLogger
246 int PrintState(const Event& evt);
247 ///checks whether or not the current info being treated is a run number
248 void CheckForRunNumber(const EventImp& evt, unsigned int index);
249 /// start transition
250 int Start();
251 ///from waiting to logging transition
252 //int StartRun();
253 // from logging to waiting transition
254 int StopRunLogging();
255 ///stop and reset transition
256 int GoToReady();
257 ///from NightlyOpen to waiting transition
258 int NightlyToWaitRun();
259 ///from wait for run number to nightly open
260 int BackToNightlyOpen();
261#ifdef HAVE_FITS
262 ///Open fits files
263 void OpenFITSFiles(SubscriptionType& sub);
264 ///Write data to FITS files
265 void WriteToFITS(SubscriptionType& sub, const void* data);
266 ///Allocate the buffers required for fits
267 void AllocateFITSBuffers(SubscriptionType& sub);
268#endif//has_fits
269
270 /***************************************
271 * DIM SERVICES PROVIDED BY THE DATA LOGGER
272 ***************************************/
273 ///monitoring notification loop
274 void ServicesMonitoring();
275 inline void NotifyOpenedFile(const string &name, int type, DimDescribedService* service);
276 ///Service for opened files
277 DimDescribedService* fOpenedNightlyFiles;
278 DimDescribedService* fOpenedRunFiles;
279 DimDescribedService* fNumSubAndFits;
280 NumSubAndFitsType fNumSubAndFitsData;
281
282 /***************************************************
283 * DATA LOGGER's CONFIGURATION STUFF
284 ***************************************************/
285 ///black/white listing
286 set<string> fBlackList;
287 set<string> fWhiteList;
288 ///list of services to be grouped
289 set<string> fGrouping;
290 ///configuration flags
291 bool fDebugIsOn;
292 bool fOpenedFilesIsOn;
293 bool fNumSubAndFitsIsOn;
294 //functions for controlling the services behavior
295 int SetDebugOnOff(const Event& evt);
296 int SetStatsPeriod(const Event& evt);
297 int SetOpenedFilesOnOff(const Event& evt);
298 int SetNumSubsAndFitsOnOff(const Event& evt);
299 int SetRunTimeoutDelay(const Event& evt);
300
301 ///boolean to prevent DIM update while desctructing the dataLogger
302 bool fDestructing;
303 /***************************************************
304 * UTILITIES
305 ***************************************************/
306 ///vectors to keep track of opened Fits files, for grouping purposes.
307 map<string, vector<string> > fOpenedNightlyFits;
308 ///creates a group fits file based on a list of files to be grouped
309 void CreateFitsGrouping(map<string, vector<string> >& filesToGroup);
310
311 bool OpenStreamImp(ofstream &stream, const string &filename, bool mightbeopen);
312 bool OpenStream(shared_ptr<ofstream> stream, const string &filename);
313 ///Open the relevant text files related to a particular run
314 int OpenRunFile(RunNumberType& run);
315 ///add a new run number
316 void AddNewRunNumber(int64_t newRun, Time time);
317 std::vector<int64_t> previousRunNumbers;
318 ///removes the oldest run number, and close the relevant files.
319 void RemoveOldestRunNumber();
320 ///retrieves the size of a file
321 off_t GetFileSize(const string&);
322 ///Get the digits of year, month and day for filenames and paths
323 void GetYearMonthDayForFiles(unsigned short& year, unsigned short& month, unsigned short& day);
324 ///Appends the relevant year month day to a given path
325 void AppendYearMonthDaytoPath(string& path);
326 ///Form the files path
327 string CompileFileNameWithPath(const string &path, const string &service, const string & extension);
328 ///Form the file names only
329 string CompileFileName(const string& service, const string& extension, const Time& time=Time()) const;
330 ///Check whether service is in black and/or white list
331 bool ShouldSubscribe(const string& server, const string& service);
332 ///Subscribe to a given server and service
333// EventImp& SubscribeTo(const string& server, const string& service);
334 ///Open a text file and checks for ofstream status
335 bool OpenTextFile(ofstream& stream, const string& name);
336 ///Checks if the input osftream is in error state, and if so close it.
337 bool CheckForOfstreamError(ofstream& out, bool isDailyStream);
338 ///Goes to Write error states
339 void GoToRunWriteErrorState();
340 void GoToNightlyWriteErrorState();
341 ///Checks if a given path exist
342 bool DoesPathExist(string path);
343 ///Check if old run numbers can be trimmed, and if so, do it
344 void TrimOldRunNumbers();
345 ///Create a given directory
346 bool CreateDirectory(string path);
347 /***************************************************
348 * INHERITED FROM DimServiceInfoList
349 ***************************************************/
350 ///Add a new server subscription
351 void AddServer(const string& server);
352 ///Add a new service subscription
353 void AddService(const Service& svc);
354 ///Remove a given service subscription
355 //FIXME unused
356 void RemoveService(const string, const string, bool);
357 ///Remove all the services associated with a given server
358 //FIXME unused
359 void RemoveAllServices(const string&);
360 ///pointer to the dim's subscription that should distribute the run numbers.
361 //DIM_REPLACE
362 //DimInfo* fRunNumberService;
363 unsigned int fRunNumberService;
364 /***************************************************
365 * Overwritten from MessageImp
366 ***************************************************/
367 vector<string> backLogBuffer;
368 bool shouldBackLog;
369 bool fShouldAutoStart;
370 bool fAutoStarted;
371
372 //Current day variable. Used to close nightly files when night changes
373 int fCurrentDay;
374 Time lastFlush;
375
376 DimDnsServiceList fDimList;
377 vector<DimDescriptions*> fServerDescriptionsList;
378
379 //counter for keeping tracker of services
380 unsigned int servicesCounter;
381public:
382 int Write(const Time &time, const std::string &txt, int qos=kMessage);
383
384}; //DataLogger
385
386
387/**
388 * @brief the two methods below were copied from StateMachineDimControl.cc
389 *
390 */
391int DataLogger::HandleDescriptions(DimDescriptions* desc)
392{
393 fMutex.lock();
394 for (auto it=desc->descriptions.begin(); it != desc->descriptions.end(); it++) {
395 if (fDebugIsOn)
396 {
397 Debug("Adding description for service: " + it->front().name);
398 }
399 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
400 }
401 fMutex.unlock();
402
403 return GetCurrentState();
404}
405vector<Description> DataLogger::GetDescription(const string& server, const string& service)
406{
407 const lock_guard<mutex> guard(fMutex);
408 const auto it = fServiceDescriptionList.find(server+"/"+service);
409 return it==fServiceDescriptionList.end()?vector<Description>():it->second;
410}
411// --------------------------------------------------------------------------
412//
413//! Overwritten write function. This way we directly log the datalogger's messages, without going through dim's dns,
414//! thus increasing robustness.
415//! @param time: see MessageImp class param
416//! @param txt: see MessageImp class param
417//! @param qos: see MessageImp class param
418//! @return see MessageImp class param
419//
420int DataLogger::Write(const Time&time, const std::string& txt, int qos)
421{
422 ostringstream ss;
423 ss << "datalogger: " << txt;
424 if (fNightlyLogFile.is_open())
425 {
426 MessageImp mimp(fNightlyLogFile);
427 mimp.Write(time, ss.str(), qos);
428 }
429 else if (shouldBackLog)
430 {
431 ostringstream str;
432 MessageImp mimp(str);
433 mimp.Write(time, ss.str(), qos);
434 backLogBuffer.push_back(str.str());
435 }
436 return StateMachineDim::Write(time, ss.str(), qos);
437}
438// --------------------------------------------------------------------------
439//
440//! Check if a given path exists
441//! @param path the path to be checked
442//! @return whether or not the creation has been successfull
443//
444bool DataLogger::CreateDirectory(string path)
445{
446 try
447 {
448 DimWriteStatistics::CreateDirectory(path);
449 return true;
450 }
451 catch (const runtime_error &e)
452 {
453 Error(e.what());
454 return false;
455 }
456}
457// --------------------------------------------------------------------------
458//
459//! Check if a given path exists
460//! @param path the path to be checked
461//! @return whether or not the given path exists
462//
463bool DataLogger::DoesPathExist(string path)
464{
465 return DimWriteStatistics::DoesPathExist(path, *this);
466}
467
468
469void DataLogger::AddServer(const string& server)
470{
471 Info("Got request to add server " + server );
472 if (server != "DIS_DNS")
473 {
474 for (auto it=fServerDescriptionsList.begin(); it != fServerDescriptionsList.end(); it++)
475 if ((*it)->server == server)
476 {
477 if (fDebugIsOn)
478 {
479 ostringstream str;
480 str << "Already got description for server " << server << ". Ignoring." << endl;
481 Debug(str.str());
482 return;
483 }
484 }
485 DimDescriptions* d = new DimDescriptions(server);
486 d->SetCallbackDescriptions(bind(&DataLogger::HandleDescriptions, this, d));
487 d->Subscribe(*this);
488 fServerDescriptionsList.push_back(d);
489 }
490
491}
492
493// --------------------------------------------------------------------------
494//
495//! Add a new service subscription
496//! @param server the server for which the subscription should be created
497//! @param service the service for which the subscription should be created
498//! @param isCmd whether this is a Dim Command or not. Commands are not logged
499//
500void DataLogger::AddService(const Service& svc)
501{
502 const string& server = svc.server;
503 const string& service = svc.service;
504 const bool isCmd = svc.iscmd;
505
506 //dataLogger does not subscribe to commands
507 if (isCmd)
508 return;
509
510 Info("Got request to add service: "+server+"/"+service);
511
512 //check the given subscription against black and white lists
513 if (!ShouldSubscribe(server, service))
514 return;
515
516 map<string, SubscriptionType> &list = fServiceSubscriptions[server];
517
518 if (list.find(service) != list.end())
519 {
520 // Error("Service " + server + "/" + service + " is already in the dataLogger's list... ignoring update.");
521 return;
522 }
523 //DIM_REPLACE
524// list[service].dimInfo.reset(SubscribeTo(server, service));
525 if (fDebugIsOn)
526 Debug("Subscribing to service "+server+"/"+service);
527 Subscribe(server + "/" + service)
528 (bind(&DataLogger::infoCallback, this, placeholders::_1, servicesCounter));
529 list[service].server = server;
530 list[service].service = service;
531 list[service].index = servicesCounter;
532 fNumSubAndFitsData.numSubscriptions++;
533 //check if this is the run numbers service
534 if ((server == "FAD_CONTROL") && (service == "START_RUN"))
535 fRunNumberService = servicesCounter;
536 servicesCounter++;
537 Info("Added subscription to " + server + "/" + service);
538}
539// --------------------------------------------------------------------------
540//
541//! Remove a given service subscription
542//! @param server the server for which the subscription should be removed
543//! @param service the service that should be removed
544//! @param isCmd whether or not this is a command
545//
546void DataLogger::RemoveService(string server, string service, bool isCmd)
547{
548
549 Info("Got request to remove service: "+server+"/"+service);
550 if (fDestructing)//this function is called by the super class, after the destructor has deleted its own subscriptions
551 return;
552//FIXME unused
553 return;
554
555 if (isCmd)
556 return;
557
558 if (fServiceSubscriptions.find(server) == fServiceSubscriptions.end())
559 {
560 Error("Request to remove service "+service+" from server "+server+", but service not found.");
561 return;
562 }
563
564 if (fServiceSubscriptions[server].erase(service) != 1)
565 {
566 //check the given subscription against black and white lists
567 if (!ShouldSubscribe(server, service))
568 return;
569
570 Error("Subscription "+server+"/"+service+" could not be removed as it is not present");
571 return;
572 }
573 fNumSubAndFitsData.numSubscriptions--;
574
575 if ((server == "FAD_CONTROL") && (service == "START_RUN"))
576 fRunNumberService = 0;
577
578 Info("Removed subscription to " + server + "/" + service);
579}
580// --------------------------------------------------------------------------
581//
582//! Remove all the services associated with a given server
583//! @param server the server for which all the services should be removed
584//
585void DataLogger::RemoveAllServices(const string& server)
586{
587 Info("Got request for removing all services from: "+server);
588 if (fServiceSubscriptions.find(server)==fServiceSubscriptions.end())
589 {
590 Warn("Request to remove all services, but corresponding server " + server + " not found.");
591 return;
592 }
593//FIXME unused
594 return;
595 fNumSubAndFitsData.numSubscriptions -= fServiceSubscriptions[server].size();
596
597 fServiceSubscriptions[server].clear();
598 fServiceSubscriptions.erase(server);
599
600 if (server == "FAD_CONTROL")
601 fRunNumberService = 0;
602
603 if (fDebugIsOn)
604 Debug("Removed all subscriptions to " + server + "/");
605}
606
607// --------------------------------------------------------------------------
608//
609//! Checks if the given ofstream is in error state and if so, close it
610//! @param out the ofstream that should be checked
611//
612bool DataLogger::CheckForOfstreamError(ofstream& out, bool isDailyStream)
613{
614 if (out.good())
615 return true;
616
617 Error("An error occured while writing to a text file. Closing it");
618 if (out.is_open())
619 out.close();
620 if (isDailyStream)
621 GoToNightlyWriteErrorState();
622 else
623 GoToRunWriteErrorState();
624
625 return false;
626}
627
628bool DataLogger::OpenStreamImp(ofstream &stream, const string &filename, bool mightbeopen)
629{
630 if (stream.is_open())
631 {
632 if (!mightbeopen)
633 Error(filename+" was already open when trying to open it.");
634 return mightbeopen;
635 }
636
637 errno = 0;
638 stream.open(filename.c_str(), ios_base::out | ios_base::app);
639 if (!stream /*|| errno!=0*/)
640 {
641 ostringstream str;
642 str << "ofstream::open() failed for '" << filename << "': " << strerror(errno) << " [errno=" << errno << "]";
643 Error(str);
644 return false;
645 }
646
647 if (!stream.is_open())
648 {
649 Error("File "+filename+" not open as it ought to be.");
650 return false;
651 }
652
653 Info("Opened: "+filename);
654
655 return true;
656}
657
658bool DataLogger::OpenStream(shared_ptr<ofstream> stream, const string &filename)
659{
660 return OpenStreamImp(*stream, filename, false);
661}
662
663// --------------------------------------------------------------------------
664//
665//! Open a text file and checks for error code
666//! @param stream the ofstream for which the file should be opened
667//! @name the file name
668//
669bool DataLogger::OpenTextFile(ofstream& stream, const string& name)
670{
671 return OpenStreamImp(stream, name, true);
672}
673
674// --------------------------------------------------------------------------
675//
676//! Create a new dim subscription to a given server and service
677//! @param server the server name
678//! @param service the service name
679//
680/*EventImp& DataLogger::SubscribeTo(const string& server, const string& service)
681{
682
683 //DIM_REPLACE
684 //return new DimStampedInfo((server + "/" + service).c_str(), (void*)NULL, 0, this);
685 EventImp& newSubscription = Subscribe(server + "/" + service);
686 newSubscription.bind(&infoHandler, this, placeholders::_1);
687 return newSubscription;
688}*/
689// --------------------------------------------------------------------------
690//
691//! Check whether a service should be subscribed to, based on the black/white list entries
692//! @param server the server name associated with the service being checked
693//! @param service the service name associated with the service being checked
694//
695bool DataLogger::ShouldSubscribe(const string& server, const string& service)
696{
697 if ((fBlackList.find(server + "/") != fBlackList.end()) ||
698 (fBlackList.find(server + "/" + service) != fBlackList.end()) ||
699 (fBlackList.find("/" + service) != fBlackList.end()))
700 {
701 if (fWhiteList.size()>0 &&
702 (fWhiteList.find(server + "/" + service) != fWhiteList.end()))
703 {
704 if (fDebugIsOn)
705 Debug("White list saved service " + server + "/" + service + " from blacklisting");
706 return true;
707 }
708 if (fDebugIsOn)
709 Debug("Blacklist banned service " + server + "/" + service);
710 return false;
711 }
712 return true;
713}
714// --------------------------------------------------------------------------
715//
716//! Compiles a file name
717//! @param path the base path where to put the file
718//! @param time the time at which the file is created
719//! @param service the service name, if any
720//! @param extension the extension to add, if any
721//
722string DataLogger::CompileFileName(const string& service, const string& extension, const Time& time) const
723{
724 ostringstream str;
725
726 const Time ftime(time);//removed this as already done by nightAsInt: -boost::posix_time::hours(12));
727 str << ftime.NightAsInt();
728
729 if (!service.empty())
730 str << '.' << service;
731
732 if (!extension.empty())
733 str << "." << extension;
734
735 return str.str();
736}
737
738string DataLogger::CompileFileNameWithPath(const string& path, const string& service, const string& extension)
739{
740 ostringstream str;
741
742 const Time time;
743
744 //calculate time suitable for naming files.
745 const Time ftime = time-boost::posix_time::hours(12);
746
747 //output it
748 str << path << ftime.GetAsStr("/%Y/%m/%d");
749
750 //check if target directory exist
751 if (!DoesPathExist(str.str()))
752 CreateDirectory(str.str());
753
754 str << '/' << CompileFileName(service, extension, time);
755
756 return str.str();
757
758
759}
760
761// --------------------------------------------------------------------------
762//
763//!retrieves the size on disk of a file
764//! @param fileName the full file name for which the size on disk should be retrieved
765//! @return the size of the file on disk, in bytes. 0 if the file does not exist or if an error occured
766//
767off_t DataLogger::GetFileSize(const string& fileName)
768{
769 return DimWriteStatistics::GetFileSizeOnDisk(fileName, *this);
770}
771
772// --------------------------------------------------------------------------
773//
774//! Removes the oldest run number and closes the fits files that should be closed
775//! Also creates the fits grouping file
776//
777void DataLogger::RemoveOldestRunNumber()
778{
779 if (fDebugIsOn)
780 {
781 ostringstream str;
782 str << "Removing run number " << fRunNumber.front().runNumber;
783 Debug(str);
784 }
785 //remove the entry
786 fRunNumber.pop_front();
787}
788
789// --------------------------------------------------------------------------
790//
791//! Default constructor. The name of the machine is given DATA_LOGGER
792//! and the state is set to kSM_Ready at the end of the function.
793//
794//!Setup the allows states, configs and transitions for the data logger
795//
796DataLogger::DataLogger(ostream &out) : StateMachineDim(out, "DATA_LOGGER"),
797 fFilesStats("DATA_LOGGER", *this)
798{
799 shouldBackLog = true;
800
801 servicesCounter=1;
802
803 //initialize member data
804 fFilePath = ".";
805
806 fDimList.Subscribe(*this);
807 fDimList.SetCallbackServerAdd(bind(&DataLogger::AddServer, this, placeholders::_1));
808 fDimList.SetCallbackServiceAdd(bind(&DataLogger::AddService, this, placeholders::_1));
809
810 //calculate time "centered" around noon instead of midnight
811 const Time timeNow;
812 const Time nowMinusTwelve = timeNow-boost::posix_time::hours(12);
813 fCurrentDay = (int)(nowMinusTwelve.Mjd());//nowMinusTwelve.M()*31 + nowMinusTwelve.D();//assume 31 days per month. we do not really care, only want unique number per day of the year
814 lastFlush = Time();
815
816 //Give a name to this machine's specific states
817 AddStateName(kSM_NightlyOpen, "NightlyFileOpen", "The summary files for the night are open.");
818 AddStateName(kSM_WaitingRun, "WaitForRun", "The summary files for the night are open and we wait for a run to be started.");
819 AddStateName(kSM_Logging, "Logging", "The summary files for the night and the files for a single run are open.");
820 AddStateName(kSM_BadFolder, "ErrInvalidFolder", "The folder for the files is not invalid.");
821 AddStateName(kSM_DailyWriteError, "ErrDailyWrite", "An error occured while writing to a daily (and run) file.");
822 AddStateName(kSM_RunWriteError, "ErrRunWrite", "An error occured while writing to a run file.");
823
824 // Add the possible transitions for this machine
825 AddEvent("START", kSM_Ready, kSM_BadFolder)
826 (bind(&DataLogger::Start, this))
827 ("Start the nightly logging. Nightly file location must be specified already");
828
829 AddEvent("STOP", kSM_NightlyOpen, kSM_WaitingRun, kSM_Logging, kSM_DailyWriteError, kSM_RunWriteError)
830 (bind(&DataLogger::GoToReady, this))
831 ("Stop all data logging, close all files.");
832
833 AddEvent("RESET", kSM_Error, kSM_BadFolder, kSM_DailyWriteError, kSM_RunWriteError)
834 (bind(&DataLogger::GoToReady, this))
835 ("Transition to exit error states. Closes the any open file.");
836
837 AddEvent("START_RUN_LOGGING", /*kSM_Logging,*/ kSM_NightlyOpen, kSM_Ready)
838 (bind(&DataLogger::NightlyToWaitRun, this))
839 ("Go to waiting for run number state. In this state with any received run-number a new file is opened.");
840
841 AddEvent("STOP_RUN_LOGGING", kSM_WaitingRun, kSM_Logging)
842 (bind(&DataLogger::BackToNightlyOpen, this))
843 ("Go from the wait for run to nightly open state.");
844
845 // Provide a print command
846 AddEvent("PRINT_INFO")
847 (bind(&DataLogger::PrintState, this, placeholders::_1))
848 ("Print information about the internal status of the data logger.");
849
850 OpenFileToDim fToDim;
851 fToDim.code = 0;
852 fToDim.fileName[0] = '\0';
853
854 fOpenedNightlyFiles = new DimDescribedService(GetName() + "/FILENAME_NIGHTLY", "I:1;C", fToDim,
855 "Path and base name used for the nightly files."
856 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
857 "|Name[string]:path and base file name");
858
859 fOpenedRunFiles = new DimDescribedService(GetName() + "/FILENAME_RUN", "I:1;C", fToDim,
860 "Path and base name used for the run files."
861 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
862 "|Name[string]:path and base file name");
863
864 fNumSubAndFitsData.numSubscriptions = 0;
865 fNumSubAndFitsData.numOpenFits = 0;
866 fNumSubAndFits = new DimDescribedService(GetName() + "/NUM_SUBS", "I:2", fNumSubAndFitsData,
867 "Num. open files + num. subscribed services"
868 "|NSubAndOpenFiles[int]:Num. of subs and open files");
869
870 //services parameters
871 fDebugIsOn = false;
872 fOpenedFilesIsOn = true;
873 fNumSubAndFitsIsOn = true;
874
875 // provide services control commands
876 AddEvent("SET_DEBUG_MODE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
877 (bind(&DataLogger::SetDebugOnOff, this, placeholders::_1))
878 ("Switch debug mode on or off. Debug mode prints information about every service written to a file."
879 "|Enable[bool]:Enable of disable debug mode (yes/no).");
880
881 AddEvent("SET_STATISTICS_UPDATE_INTERVAL", "S:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
882 (bind(&DataLogger::SetStatsPeriod, this, placeholders::_1))
883 ("Interval in which the data-logger statistics service (STATS) is updated."
884 "|Interval[ms]:Value in milliseconds (<=0: no update).");
885
886 AddEvent("ENABLE_FILENAME_SERVICES", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
887 (bind(&DataLogger::SetOpenedFilesOnOff ,this, placeholders::_1))
888 ("Switch service which distributes information about the open files on or off."
889 "|Enable[bool]:Enable of disable filename services (yes/no).");
890
891 AddEvent("ENABLE_NUMSUBS_SERVICE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
892 (bind(&DataLogger::SetNumSubsAndFitsOnOff, this, placeholders::_1))
893 ("Switch the service which distributes information about the number of subscriptions and open files on or off."
894 "|Enable[bool]:Enable of disable NUM_SUBS service (yes/no).");
895
896 AddEvent("SET_RUN_TIMEOUT", "L:1", kSM_Ready, kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun)
897 (bind(&DataLogger::SetRunTimeoutDelay, this, placeholders::_1))
898 ("Set the timeout delay for old run numbers."
899 "|timeout[min]:Time out in minutes after which files for expired runs are closed.");
900
901 fDestructing = false;
902
903 fPreviousOldRunNumberCheck = Time().Mjd();
904
905 fDailyFileDayChangedAlready = true;
906 fRunNumberTimeout = 60000; //default run-timeout set to 1 minute
907 fRunNumber.push_back(RunNumberType());
908 fRunNumber.back().runNumber = -1;
909 fRunNumber.back().time = Time();
910 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
911 NotifyOpenedFile("", 0, fOpenedRunFiles);
912
913 fRunNumberService = 0;
914
915 fShouldAutoStart = false;
916 fAutoStarted = false;
917
918
919 if(fDebugIsOn)
920 {
921 Debug("DataLogger Init Done.");
922 }
923}
924
925// --------------------------------------------------------------------------
926//
927//! Destructor
928//
929DataLogger::~DataLogger()
930{
931 if (fDebugIsOn)
932 Debug("DataLogger destruction starts");
933
934 //this boolean should not be required anymore
935 fDestructing = true;
936
937 //now clear the services subscriptions
938 dim_lock();
939 fServiceSubscriptions.clear();
940 dim_unlock();
941
942 //clear any remaining run number (should remain only one)
943 while (fRunNumber.size() > 0)
944 {
945 RemoveOldestRunNumber();
946 }
947 //go to the ready state. i.e. close all files, run-wise first
948 GoToReady();
949
950 Info("Will soon close the daily log file");
951
952 delete fOpenedNightlyFiles;
953 delete fOpenedRunFiles;
954 delete fNumSubAndFits;
955
956 if (fNightlyLogFile.is_open())//this file is the only one that has not been closed by GoToReady
957 {
958 fNightlyLogFile << endl;
959 fNightlyLogFile.close();
960 }
961
962 for (auto it=fServerDescriptionsList.begin(); it!= fServerDescriptionsList.end(); it++)
963 delete *it;
964
965 if (fDebugIsOn)
966 Debug("DataLogger desctruction ends");
967}
968
969// --------------------------------------------------------------------------
970//
971//! checks if old run numbers should be trimmed and if so, do it
972//
973void DataLogger::TrimOldRunNumbers()
974{
975 const Time cTime = Time();
976
977 if (cTime - fPreviousOldRunNumberCheck < boost::posix_time::milliseconds(fRunNumberTimeout))
978 return;
979
980 while (fRunNumber.size() > 1 && (cTime - fRunNumber.back().time) > boost::posix_time::milliseconds(fRunNumberTimeout))
981 {
982 RemoveOldestRunNumber();
983 }
984 fPreviousOldRunNumberCheck = cTime;
985}
986// --------------------------------------------------------------------------
987//
988//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
989//
990int DataLogger::infoCallback(const EventImp& evt, unsigned int subIndex)
991{
992// if (fDebugIsOn)
993// {
994// ostringstream str;
995// str << "Got infoCallback called with service index= " << subIndex;
996// Debug(str.str());
997// }
998
999 if ((GetCurrentState() == kSM_Ready) && (!fAutoStarted) && fShouldAutoStart)
1000 {
1001 fAutoStarted = true;
1002 SetCurrentState(Start());
1003 }
1004 else
1005 {
1006 if (GetCurrentState() > kSM_Ready)
1007 fAutoStarted = true;
1008 }
1009
1010
1011 //check if the service pointer corresponds to something that we subscribed to
1012 //this is a fix for a bug that provides bad Infos when a server starts
1013 bool found = false;
1014 SubscriptionsListType::iterator x;
1015 map<string, SubscriptionType>::iterator y;
1016 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1017 {//find current service is subscriptions
1018 //Edit: this should be useless now... remove it sometimes ?
1019 for (y=x->second.begin(); y!=x->second.end();y++)
1020 if (y->second.index == subIndex)
1021 {
1022 found = true;
1023 break;
1024 }
1025 if (found)
1026 break;
1027 }
1028
1029 if (!found && fDebugIsOn)
1030 {
1031 ostringstream str;
1032 str << "Service " << evt.GetName() << " not found in subscriptions" << endl;
1033 Debug(str.str());
1034 }
1035 if (!found)
1036 return GetCurrentState();
1037
1038
1039 if (evt.GetSize() == 0 && fDebugIsOn)
1040 {
1041 ostringstream str;
1042 str << "Got 0 size for " << evt.GetName() << endl;
1043 Debug(str.str());
1044 }
1045 if (evt.GetSize() == 0)
1046 return GetCurrentState();
1047
1048 if (evt.GetFormat() == "" && fDebugIsOn)
1049 {
1050 ostringstream str;
1051 str << "Got no format for " << evt.GetName() << endl;
1052 Debug(str.str());
1053 }
1054 if (evt.GetFormat() == "")
1055 return GetCurrentState();
1056
1057// cout.precision(20);
1058// cout << "Orig timestamp: " << Time(I->getTimestamp(), I->getTimestampMillisecs()*1000).Mjd() << endl;
1059 // FIXME: Here we have to check if we have received the
1060 // service with the run-number.
1061 // CheckForRunNumber(I); has been removed because we have to
1062 // subscribe to this service anyway and hence we have the pointer
1063 // (no need to check for the name)
1064 CheckForRunNumber(evt, subIndex);
1065
1066 Report(evt, y->second);
1067
1068 //remove old run numbers
1069 TrimOldRunNumbers();
1070
1071 return GetCurrentState();
1072}
1073
1074// --------------------------------------------------------------------------
1075//
1076//! Add a new active run number
1077//! @param newRun the new run number
1078//! @param time the time at which the new run number was issued
1079//
1080void DataLogger::AddNewRunNumber(int64_t newRun, Time time)
1081{
1082
1083 if (newRun > 0xffffffff)
1084 {
1085 Error("New run number too large, out of range. Ignoring.");
1086 return;
1087 }
1088 for (std::vector<int64_t>::const_iterator it=previousRunNumbers.begin(); it != previousRunNumbers.end(); it++)
1089 {
1090 if (*it == newRun)
1091 {
1092 Error("Newly provided run number has already been used (or is still in use). Going to error state");
1093 SetCurrentState(kSM_BadFolder);
1094 return;
1095 }
1096 }
1097 if (fDebugIsOn)
1098 {
1099 ostringstream str;
1100 str << "Adding new run number " << newRun << " issued at " << time;
1101 Debug(str);
1102 }
1103 //Add new run number to run number list
1104 fRunNumber.push_back(RunNumberType());
1105 fRunNumber.back().runNumber = int32_t(newRun);
1106 fRunNumber.back().time = time;
1107
1108 ostringstream str;
1109 str << "The new run number is: " << fRunNumber.back().runNumber;
1110 Message(str);
1111
1112 if (GetCurrentState() != kSM_Logging && GetCurrentState() != kSM_WaitingRun )
1113 return;
1114
1115 if (newRun > 0 && GetCurrentState() == kSM_WaitingRun)
1116 SetCurrentState(kSM_Logging);
1117 if (newRun < 0 && GetCurrentState() == kSM_Logging)
1118 SetCurrentState(kSM_WaitingRun);
1119}
1120// --------------------------------------------------------------------------
1121//
1122//! Checks whether or not the current info is a run number.
1123//! If so, then remember it. A run number is required to open the run-log file
1124//! @param I
1125//! the current DimInfo
1126//
1127void DataLogger::CheckForRunNumber(const EventImp& evt, unsigned int index)
1128{
1129 if (index != fRunNumberService)
1130 return;
1131// int64_t newRun = reinterpret_cast<const uint64_t*>(evt.GetData())[0];
1132 AddNewRunNumber(evt.GetXtra(), evt.GetTime());
1133}
1134
1135// --------------------------------------------------------------------------
1136//
1137//! write infos to log files.
1138//! @param I
1139//! The current DimInfo
1140//! @param sub
1141//! The dataLogger's subscription corresponding to this DimInfo
1142//
1143void DataLogger::Report(const EventImp& evt, SubscriptionType& sub)
1144{
1145 const string fmt(evt.GetFormat());
1146
1147 const bool isItaReport = fmt!="C";
1148
1149 if (!fNightlyLogFile.is_open())
1150 return;
1151
1152 if (fDebugIsOn && string(evt.GetName())!="DATA_LOGGER/MESSAGE")
1153 {
1154 ostringstream str;
1155 str << "Logging " << evt.GetName() << " [" << evt.GetFormat() << "] (" << evt.GetSize() << ")";
1156 Debug(str);
1157 }
1158
1159 //
1160 // Check whether we should close and reopen daily text files or not
1161 // calculate time "centered" around noon instead of midnight
1162 // if number of days has changed, then files should be closed and reopenned.
1163 const Time timeNow;
1164 const Time nowMinusTwelve = timeNow-boost::posix_time::hours(12);
1165 int newDayNumber = (int)(nowMinusTwelve.Mjd());//nowMinusTwelve.M()*31 + nowMinusTwelve.D();//assume 31 days per month. we do not really care, only want unique number per day of the year
1166
1167 //also check if we should flush the nightly files
1168 if (lastFlush < timeNow-boost::posix_time::minutes(1))
1169 {
1170 lastFlush = timeNow;
1171 SubscriptionsListType::iterator x;
1172 map<string, SubscriptionType>::iterator y;
1173 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1174 {//find current service is subscriptions
1175 for (y=x->second.begin(); y!=x->second.end();y++)
1176 if (y->second.nightlyFile.IsOpen())
1177 {
1178 y->second.nightlyFile.Flush();
1179 }
1180 }
1181 if (fDebugIsOn)
1182 Debug("Just flushed nightly fits files to the disk");
1183 }
1184
1185 if (newDayNumber != fCurrentDay)
1186 {
1187 fCurrentDay = newDayNumber;
1188 //crawl through the subcriptions and close any open nightly file
1189 SubscriptionsListType::iterator x;
1190 map<string, SubscriptionType>::iterator y;
1191 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1192 {//find current service is subscriptions
1193 for (y=x->second.begin(); y!=x->second.end();y++)
1194 if (y->second.nightlyFile.IsOpen())
1195 {
1196 y->second.nightlyFile.Close();
1197 }
1198 }
1199
1200 if (fDebugIsOn)
1201 Debug("Day have changed! Closing and reopening nightly files");
1202
1203 fNightlyLogFile << endl;
1204 fNightlyLogFile.close();
1205 fNightlyReportFile.close();
1206
1207 Info("Closed: "+fFullNightlyLogFileName);
1208 Info("Closed: "+fFullNightlyReportFileName);
1209
1210 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
1211 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
1212 {
1213 GoToReady();
1214 SetCurrentState(kSM_BadFolder);
1215 return;
1216 }
1217 fNightlyLogFile << endl;
1218
1219 fFullNightlyReportFileName = CompileFileNameWithPath(fFilePath, "", "rep");
1220 if (!OpenTextFile(fNightlyReportFile, fFullNightlyReportFileName))
1221 {
1222 GoToReady();
1223 SetCurrentState(kSM_BadFolder);
1224 return;
1225 }
1226 }
1227 //create the converter for that service
1228 if (!sub.fConv)
1229 {
1230 sub.fConv = shared_ptr<Converter>(new Converter(Out(), evt.GetFormat()));
1231 if (!sub.fConv->valid())
1232 {
1233 ostringstream str;
1234 str << "Couldn't properly parse the format... service " << evt.GetName() << " ignored.";
1235 Error(str);
1236 return;
1237 }
1238 }
1239 //construct the header
1240 ostringstream header;
1241 const Time cTime(evt.GetTime());
1242 fQuality = evt.GetQoS();
1243/* //I had strange surprises with the quality from Dim before. Double check that the value is indeed valid.
1244 if (fQuality != kMessage &&
1245 fQuality != kInfo &&
1246 fQuality != kWarn &&
1247 fQuality != kError &&
1248 fQuality != kFatal &&
1249 fQuality != kComment &&
1250 fQuality != kDebug)
1251 fQuality = kError;
1252*/
1253 fMjD = cTime.Mjd() ? cTime.Mjd()-40587 : 0;
1254
1255 if (isItaReport)
1256 {
1257 //write text header
1258 string serviceName = (sub.service == "MESSAGE") ? "" : "_"+sub.service;
1259 header << sub.server << serviceName << " " << fQuality << " ";
1260 header << evt.GetTime() << " ";
1261
1262 string text;
1263 try
1264 {
1265 text = sub.fConv->GetString(evt.GetData(), evt.GetSize());
1266 }
1267 catch (const runtime_error &e)
1268 {
1269 ostringstream str;
1270 str << "Parsing service " << evt.GetName();
1271 str << " failed: " << e.what() << " removing the subscription for now.";
1272 Error(str);
1273 //remove this subscription from the list.
1274 //because these operators use references to elements, and because they're supposed here to erase these objects on the way, I'm not too sure... so duplicate the names !
1275 RemoveService(sub.server, sub.service, false);
1276 return;
1277 }
1278
1279 if (text.empty())
1280 {
1281 ostringstream str;
1282 str << "Service " << evt.GetName() << " sent an empty string";
1283 Info(str);
1284 return;
1285 }
1286 //replace bizarre characters by white space
1287 replace(text.begin(), text.end(), '\n', '\\');
1288 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1289
1290 //write entry to Nightly report
1291 if (fNightlyReportFile.is_open())
1292 {
1293 fNightlyReportFile << header.str() << text << endl;
1294 if (!CheckForOfstreamError(fNightlyReportFile, true))
1295 return;
1296 }
1297#ifdef HAVE_FITS
1298 //check if the last received event was before noon and if current one is after noon.
1299 //if so, close the file so that it gets reopened.
1300 sub.lastReceivedEvent = cTime;
1301 if (!sub.nightlyFile.IsOpen())
1302 if (GetCurrentState() != kSM_Ready)
1303 OpenFITSFiles(sub);
1304 WriteToFITS(sub, evt.GetData());
1305#endif
1306 }
1307 else
1308 {//write entry to Nightly log
1309 vector<string> strings;
1310 try
1311 {
1312 strings = sub.fConv->ToStrings(evt.GetData());
1313 }
1314 catch (const runtime_error &e)
1315 {
1316 ostringstream str;
1317 str << "Parsing service " << evt.GetName();
1318 str << " failed: " << e.what() << " removing the subscription for now.";
1319 Error(str);
1320 //remove this subscription from the list.
1321 //because these operators use references to elements, and because they're supposed here to erase these objects on the way, I'm not too sure... so duplicate the names !
1322 RemoveService(sub.server, sub.service, false);
1323 return;
1324 }
1325 if (strings.size() > 1)
1326 {
1327 ostringstream err;
1328 err << "There was more than one string message in service " << evt.GetName() << " going to fatal error state";
1329 Error(err.str());
1330 }
1331 ostringstream msg;
1332 string serviceName = (sub.service == "MESSAGE") ? "" : "_"+sub.service;
1333 msg << sub.server << serviceName << ": " << strings[0];
1334
1335 if (fNightlyLogFile.is_open())
1336 {
1337 MessageImp(fNightlyLogFile).Write(cTime, msg.str().c_str(), fQuality);
1338 if (!CheckForOfstreamError(fNightlyLogFile, true))
1339 return;
1340 }
1341
1342 sub.lastReceivedEvent = cTime;
1343 if (!sub.nightlyFile.IsOpen())
1344 if (GetCurrentState() != kSM_Ready)
1345 OpenFITSFiles(sub);
1346 WriteToFITS(sub, evt.GetData());
1347 }
1348
1349}
1350
1351// --------------------------------------------------------------------------
1352//
1353//! print the dataLogger's current state. invoked by the PRINT command
1354//! @param evt
1355//! the current event. Not used by the method
1356//! @returns
1357//! the new state. Which, in that case, is the current state
1358//!
1359int DataLogger::PrintState(const Event& )
1360{
1361 Message("------------------------------------------");
1362 Message("------- DATA LOGGER CURRENT STATE --------");
1363 Message("------------------------------------------");
1364
1365 //print the path configuration
1366#if BOOST_VERSION < 104600
1367 Message("File path: " + boost::filesystem::system_complete(boost::filesystem::path(fFilePath)).directory_string());
1368#else
1369 Message("File path: " + boost::filesystem::system_complete(boost::filesystem::path(fFilePath)).parent_path().string());
1370#endif
1371
1372 //print active run numbers
1373 ostringstream str;
1374 //timeout value
1375 str << "Timeout delay for old run numbers: " << fRunNumberTimeout << " ms";
1376 Message(str);
1377 str.str("");
1378 str << "Active Run Numbers:";
1379 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1380 str << " " << it->runNumber;
1381 if (fRunNumber.size()==0)
1382 str << " <none>";
1383 Message(str);
1384
1385 //print all the open files.
1386 Message("------------ OPEN FILES ----------------");
1387 if (fNightlyLogFile.is_open())
1388 Message("Nightly log-file: "+fFullNightlyLogFileName);
1389
1390 if (fNightlyReportFile.is_open())
1391 Message("Nightly report-file: "+fFullNightlyReportFileName);
1392
1393 const DimWriteStatistics::Stats statVar = fFilesStats.GetTotalSizeWritten();
1394 // /*const bool statWarning =*/ calculateTotalSizeWritten(statVar, true);
1395#ifdef HAVE_FITS
1396 str.str("");
1397 str << "Number of open FITS files: " << fNumSubAndFitsData.numOpenFits;
1398 Message(str);
1399 // FIXME: Print list of open FITS files
1400#else
1401 Message("FITS output disabled at compilation");
1402#endif
1403 Message("----------------- STATS ------------------");
1404 if (fFilesStats.GetUpdateInterval()>0)
1405 {
1406 str.str("");
1407 str << "Statistics are updated every " << fFilesStats.GetUpdateInterval() << " ms";
1408 Message(str);
1409 }
1410 else
1411 Message("Statistics updates are currently disabled.");
1412 str.str("");
1413 str << "Total Size written: " << statVar.sizeWritten/1000 << " kB";
1414 Message(str);
1415 str.str("");
1416 str << "Disk free space: " << statVar.freeSpace/1000000 << " MB";
1417 Message(str);
1418
1419 Message("------------ DIM SUBSCRIPTIONS -----------");
1420 str.str("");
1421 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions.";
1422 Message(str);
1423 for (map<const string, map<string, SubscriptionType> >::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1424 {
1425 Message("Server "+it->first);
1426 for (map<string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1427 Message(" -> "+it2->first);
1428 }
1429 Message("--------------- BLOCK LIST ---------------");
1430 for (set<string>::const_iterator it=fBlackList.begin(); it != fBlackList.end(); it++)
1431 Message(" -> "+*it);
1432 if (fBlackList.size()==0)
1433 Message(" <empty>");
1434
1435 Message("--------------- ALLOW LIST ---------------");
1436 for (set<string>::const_iterator it=fWhiteList.begin(); it != fWhiteList.end(); it++)
1437 Message(" -> "+*it);
1438 if (fWhiteList.size()==0)
1439 Message(" <empty>");
1440
1441 Message("-------------- GROUPING LIST -------------");
1442 Message("The following servers and/or services will");
1443 Message("be grouped into a single fits file:");
1444 for (set<string>::const_iterator it=fGrouping.begin(); it != fGrouping.end(); it++)
1445 Message(" -> "+*it);
1446 if (fGrouping.size()==0)
1447 Message(" <no grouping>");
1448
1449 Message("------------------------------------------");
1450 Message("-------- END OF DATA LOGGER STATE --------");
1451 Message("------------------------------------------");
1452
1453 return GetCurrentState();
1454}
1455
1456// --------------------------------------------------------------------------
1457//
1458//! turn debug mode on and off
1459//! @param evt
1460//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1461//! @returns
1462//! the new state. Which, in that case, is the current state
1463//!
1464int DataLogger::SetDebugOnOff(const Event& evt)
1465{
1466 const bool backupDebug = fDebugIsOn;
1467
1468 fDebugIsOn = evt.GetBool();
1469
1470 if (fDebugIsOn == backupDebug)
1471 Message("Debug mode was already in the requested state.");
1472
1473 ostringstream str;
1474 str << "Debug mode is now " << fDebugIsOn;
1475 Message(str);
1476
1477 fFilesStats.SetDebugMode(fDebugIsOn);
1478
1479 return GetCurrentState();
1480}
1481// --------------------------------------------------------------------------
1482//
1483//! set the statistics update period duration. 0 disables the statistics
1484//! @param evt
1485//! the current event. contains the new duration.
1486//! @returns
1487//! the new state. Which, in that case, is the current state
1488//!
1489int DataLogger::SetStatsPeriod(const Event& evt)
1490{
1491 fFilesStats.SetUpdateInterval(evt.GetShort());
1492 return GetCurrentState();
1493}
1494// --------------------------------------------------------------------------
1495//
1496//! set the opened files service on or off.
1497//! @param evt
1498//! the current event. contains the instruction string. similar to setdebugonoff
1499//! @returns
1500//! the new state. Which, in that case, is the current state
1501//!
1502int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1503{
1504 const bool backupOpened = fOpenedFilesIsOn;
1505
1506 fOpenedFilesIsOn = evt.GetBool();
1507
1508 if (fOpenedFilesIsOn == backupOpened)
1509 Message("Opened files service mode was already in the requested state.");
1510
1511 ostringstream str;
1512 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1513 Message(str);
1514
1515 return GetCurrentState();
1516}
1517
1518// --------------------------------------------------------------------------
1519//
1520//! set the number of subscriptions and opened fits on and off
1521//! @param evt
1522//! the current event. contains the instruction string. similar to setdebugonoff
1523//! @returns
1524//! the new state. Which, in that case, is the current state
1525//!
1526int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1527{
1528 const bool backupSubs = fNumSubAndFitsIsOn;
1529
1530 fNumSubAndFitsIsOn = evt.GetBool();
1531
1532 if (fNumSubAndFitsIsOn == backupSubs)
1533 Message("Number of subscriptions service mode was already in the requested state");
1534
1535 ostringstream str;
1536 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1537 Message(str);
1538
1539 return GetCurrentState();
1540}
1541// --------------------------------------------------------------------------
1542//
1543//! set the timeout delay for old run numbers
1544//! @param evt
1545//! the current event. contains the timeout delay long value
1546//! @returns
1547//! the new state. Which, in that case, is the current state
1548//!
1549int DataLogger::SetRunTimeoutDelay(const Event& evt)
1550{
1551 if (evt.GetUInt() == 0)
1552 {
1553 Error("Timeout delays for old run numbers must be greater than 0... ignored.");
1554 return GetCurrentState();
1555 }
1556
1557 if (fRunNumberTimeout == evt.GetUInt())
1558 Message("New timeout for old run numbers is same value as previous one.");
1559
1560 fRunNumberTimeout = evt.GetUInt();
1561
1562 ostringstream str;
1563 str << "Timeout delay for old run numbers is now " << fRunNumberTimeout << " ms";
1564 Message(str);
1565
1566 return GetCurrentState();
1567}
1568
1569// --------------------------------------------------------------------------
1570//
1571//! Notifies the DIM service that a particular file was opened
1572//! @ param name the base name of the opened file, i.e. without path nor extension.
1573//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1574//! this is not a problem though because file are not opened so often.
1575//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1576inline void DataLogger::NotifyOpenedFile(const string &name, int type, DimDescribedService* service)
1577{
1578 if (!fOpenedFilesIsOn)
1579 return;
1580
1581 if (fDebugIsOn)
1582 {
1583 ostringstream str;
1584 str << "Updating " << service->getName() << " file '" << name << "' (type=" << type << ")";
1585 Debug(str);
1586
1587 str.str("");
1588 str << "Num subscriptions: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS files: " << fNumSubAndFitsData.numOpenFits;
1589 Debug(str);
1590 }
1591
1592 if (name.size()+1 > FILENAME_MAX)
1593 {
1594 Error("Provided file name '" + name + "' is longer than allowed file name length.");
1595 return;
1596 }
1597
1598 OpenFileToDim fToDim;
1599 fToDim.code = type;
1600 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1601
1602 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(uint32_t));
1603 service->setQuality(0);
1604 service->Update();
1605}
1606// --------------------------------------------------------------------------
1607//
1608//! Implements the Start transition.
1609//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1610//! and tries to open it.
1611//! @returns
1612//! kSM_NightlyOpen if success, kSM_BadFolder if failure
1613int DataLogger::Start()
1614{
1615 if (fDebugIsOn)
1616 {
1617 Debug("Starting...");
1618 }
1619 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
1620 bool nightlyLogOpen = fNightlyLogFile.is_open();
1621 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
1622 return kSM_BadFolder;
1623 if (!nightlyLogOpen)
1624 fNightlyLogFile << endl;
1625
1626 fFullNightlyReportFileName = CompileFileNameWithPath(fFilePath, "", "rep");
1627 if (!OpenTextFile(fNightlyReportFile, fFullNightlyReportFileName))
1628 {
1629 fNightlyLogFile.close();
1630 Info("Closed: "+fFullNightlyReportFileName);
1631 return kSM_BadFolder;
1632 }
1633
1634 fFilesStats.FileOpened(fFullNightlyLogFileName);
1635 fFilesStats.FileOpened(fFullNightlyReportFileName);
1636 //notify that a new file has been opened.
1637 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
1638 NotifyOpenedFile(baseFileName, 3, fOpenedNightlyFiles);
1639
1640 fOpenedNightlyFits.clear();
1641
1642 return kSM_NightlyOpen;
1643}
1644
1645#ifdef HAVE_FITS
1646// --------------------------------------------------------------------------
1647//
1648//! open if required a the FITS files corresponding to a given subscription
1649//! @param sub
1650//! the current DimInfo subscription being examined
1651void DataLogger::OpenFITSFiles(SubscriptionType& sub)
1652{
1653 string serviceName(sub.server + "_" + sub.service);//evt.GetName());
1654
1655 for (unsigned int i=0;i<serviceName.size(); i++)
1656 {
1657 if (serviceName[i] == '/')
1658 {
1659 serviceName[i] = '_';
1660 break;
1661 }
1662 }
1663 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1664 if (!sub.nightlyFile.IsOpen())
1665 {
1666 const string partialName = CompileFileNameWithPath(fFilePath, serviceName, "fits");
1667
1668 const string fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1669 if (!sub.fitsBufferAllocated)
1670 AllocateFITSBuffers(sub);
1671 //get the size of the file we're about to open
1672 if (fFilesStats.FileOpened(partialName))
1673 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
1674
1675 if (!sub.nightlyFile.Open(partialName, serviceName, &fNumSubAndFitsData.numOpenFits, this, 0))
1676 {
1677 GoToRunWriteErrorState();
1678 return;
1679 }
1680
1681 ostringstream str;
1682 str << "Opened: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
1683 Info(str);
1684
1685 //notify the opening
1686 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
1687 NotifyOpenedFile(baseFileName, 7, fOpenedNightlyFiles);
1688 if (fNumSubAndFitsIsOn)
1689 fNumSubAndFits->Update();
1690 }
1691
1692}
1693// --------------------------------------------------------------------------
1694//
1695//! Allocates the required memory for a given pair of fits files (nightly and run)
1696//! @param sub the subscription of interest.
1697//
1698void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
1699{
1700 //Init the time columns of the file
1701 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MJD"));
1702 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1703
1704 Description QoSDesc("QoS", "Quality of service", "");
1705 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1706
1707 // Compilation failed
1708 if (!sub.fConv->valid())
1709 {
1710 Error("Compilation of format string failed.");
1711 return;
1712 }
1713
1714 //we've got a nice structure describing the format of this service's messages.
1715 //Let's create the appropriate FITS columns
1716 const vector<string> dataFormatsLocal = sub.fConv->GetFitsFormat();
1717
1718 ostringstream str;
1719 str << "Initializing data columns for service " << sub.server << "/" << sub.service;
1720 Info(str);
1721 sub.nightlyFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, this);
1722
1723 sub.fitsBufferAllocated = true;
1724}
1725// --------------------------------------------------------------------------
1726//
1727//! write a dimInfo data to its corresponding FITS files
1728//
1729//FIXME: DO I REALLY NEED THE EVENT IMP HERE ???
1730void DataLogger::WriteToFITS(SubscriptionType& sub, const void* data)
1731{
1732 //nightly File status (open or not) already checked
1733 if (sub.nightlyFile.IsOpen())
1734 {
1735 if (!sub.nightlyFile.Write(*sub.fConv.get(), data))
1736 {
1737 RemoveService(sub.server, sub.service, false);
1738 GoToNightlyWriteErrorState();
1739 return;
1740 }
1741 }
1742}
1743#endif //if has_fits
1744// --------------------------------------------------------------------------
1745//
1746//! Go to Run Write Error State
1747// A write error has occurred. Checks what is the current state and take appropriate action
1748void DataLogger::GoToRunWriteErrorState()
1749{
1750 if ((GetCurrentState() != kSM_RunWriteError) &&
1751 (GetCurrentState() != kSM_DailyWriteError))
1752 SetCurrentState(kSM_RunWriteError);
1753}
1754// --------------------------------------------------------------------------
1755//
1756//! Go to Nightly Write Error State
1757// A write error has occurred. Checks what is the current state and take appropriate action
1758void DataLogger::GoToNightlyWriteErrorState()
1759{
1760 if (GetCurrentState() != kSM_DailyWriteError)
1761 SetCurrentState(kSM_DailyWriteError);
1762}
1763
1764
1765#ifdef HAVE_FITS
1766// --------------------------------------------------------------------------
1767//
1768//! Create a fits group file with all the run-fits that were written (either daily or run)
1769//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
1770//! single file can contain several tables to group
1771//! @param runNumber the run number that should be used for grouping. 0 means nightly group
1772//
1773void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup)
1774{
1775 if (fDebugIsOn)
1776 {
1777 ostringstream str;
1778 str << "Creating fits group for nightly files";
1779 Debug(str);
1780 }
1781 //create the FITS group corresponding to the ending run.
1782 CCfits::FITS* groupFile;
1783 unsigned int numFilesToGroup = 0;
1784 unsigned int maxCharLength = 0;
1785 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
1786 {
1787 //add the number of tables in this file to the total number to group
1788 numFilesToGroup += it->second.size();
1789 //check the length of all the strings to be written, to determine the max string length to write
1790 if (it->first.size() > maxCharLength)
1791 maxCharLength = it->first.size();
1792 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++)
1793 if (jt->size() > maxCharLength)
1794 maxCharLength = jt->size();
1795 }
1796
1797 if (fDebugIsOn)
1798 {
1799 ostringstream str;
1800 str << "There are " << numFilesToGroup << " tables to group";
1801 Debug(str);
1802 }
1803 if (numFilesToGroup <= 1)
1804 {
1805 filesToGroup.clear();
1806 return;
1807 }
1808 const string groupName = CompileFileNameWithPath(fFilePath, "", "fits");
1809
1810 Info("Creating FITS group in: "+groupName);
1811
1812 CCfits::Table* groupTable;
1813
1814 try
1815 {
1816 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
1817 //setup the column names
1818 ostringstream pathTypeName;
1819 pathTypeName << maxCharLength << "A";
1820 vector<string> names;
1821 vector<string> dataTypes;
1822 names.push_back("MEMBER_XTENSION");
1823 dataTypes.push_back("8A");
1824 names.push_back("MEMBER_URI_TYPE");
1825 dataTypes.push_back("3A");
1826 names.push_back("MEMBER_LOCATION");
1827 dataTypes.push_back(pathTypeName.str());
1828 names.push_back("MEMBER_NAME");
1829 dataTypes.push_back(pathTypeName.str());
1830 names.push_back("MEMBER_VERSION");
1831 dataTypes.push_back("1J");
1832 names.push_back("MEMBER_POSITION");
1833 dataTypes.push_back("1J");
1834
1835 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
1836//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
1837 }
1838 catch (CCfits::FitsException e)
1839 {
1840 ostringstream str;
1841 str << "Creating FITS table GROUPING in " << groupName << ": " << e.message();
1842 Error(str);
1843 return;
1844 }
1845 try
1846 {
1847 groupTable->addKey("GRPNAME", "FACT_RAW_DATA", "Data from the FACT telescope");
1848 }
1849 catch (CCfits::FitsException e)
1850 {
1851 Error("CCfits::Table::addKey failed for 'GRPNAME' in '"+groupName+"-GROUPING': "+e.message());
1852 return;
1853 }
1854 //CCfits seems to be buggy somehow: can't use the column's function "write": it create a compilation error: maybe strings were not thought about.
1855 //use cfitsio routines instead
1856 groupTable->makeThisCurrent();
1857 //create appropriate buffer.
1858 const unsigned int n = 8 + 3 + 2*maxCharLength + 1 + 8; //+1 for trailling character
1859
1860 vector<char> realBuffer(n);
1861
1862 char *startOfExtension = realBuffer.data();
1863 char *startOfURI = realBuffer.data()+8;
1864 char *startOfLocation = realBuffer.data()+8+3;
1865 char *startOfName = realBuffer.data()+8+3+maxCharLength;
1866
1867 strcpy(startOfExtension, "BINTABLE");
1868 strcpy(startOfURI, "URL");
1869
1870 realBuffer[8+3+2*maxCharLength+3] = 1;
1871 realBuffer[8+3+2*maxCharLength+7] = 1;
1872
1873 int i=1;
1874 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
1875 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
1876 {
1877 memset(startOfLocation, 0, 2*maxCharLength+1+8);
1878
1879 strcpy(startOfLocation, it->first.c_str());
1880 strcpy(startOfName, jt->c_str());
1881
1882 if (fDebugIsOn)
1883 {
1884 ostringstream str;
1885 str << "Grouping " << it->first << " " << *jt;
1886 Debug(str);
1887 }
1888
1889 int status = 0;
1890 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength +8,
1891 reinterpret_cast<unsigned char*>(realBuffer.data()), &status);
1892 if (status)
1893 {
1894 char text[30];//max length of cfitsio error strings (from doc)
1895 fits_get_errstatus(status, text);
1896 ostringstream str;
1897 str << "Writing FITS row " << i << " in " << groupName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
1898 Error(str);
1899 GoToRunWriteErrorState();
1900 delete groupFile;
1901 return;
1902 }
1903 }
1904
1905 filesToGroup.clear();
1906 delete groupFile;
1907}
1908#endif //HAVE_FITS
1909
1910// --------------------------------------------------------------------------
1911//
1912//! Implements the StopRun transition.
1913//! Attempts to close the run file.
1914//! @returns
1915//! kSM_WaitingRun if success, kSM_FatalError otherwise
1916int DataLogger::StopRunLogging()
1917{
1918
1919 if (fDebugIsOn)
1920 {
1921 Debug("Stopping Run Logging...");
1922 }
1923
1924 if (fNumSubAndFitsIsOn)
1925 fNumSubAndFits->Update();
1926
1927 while (fRunNumber.size() > 0)
1928 {
1929 RemoveOldestRunNumber();
1930 }
1931 return kSM_WaitingRun;
1932}
1933// --------------------------------------------------------------------------
1934//
1935//! Implements the Stop and Reset transitions.
1936//! Attempts to close any openned file.
1937//! @returns
1938//! kSM_Ready
1939int DataLogger::GoToReady()
1940{
1941 if (fDebugIsOn)
1942 {
1943 Debug("Going to the Ready state...");
1944 }
1945 if (GetCurrentState() == kSM_Logging || GetCurrentState() == kSM_WaitingRun)
1946 StopRunLogging();
1947
1948 //it may be that dim tries to write a dimInfo while we're closing files. Prevent that
1949 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
1950
1951 if (fNightlyReportFile.is_open())
1952 {
1953 fNightlyReportFile.close();
1954 Info("Closed: "+baseFileName+".rep");
1955 }
1956#ifdef HAVE_FITS
1957 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1958 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1959 {
1960 if (j->second.nightlyFile.IsOpen())
1961 j->second.nightlyFile.Close();
1962 }
1963#endif
1964 if (GetCurrentState() == kSM_Logging ||
1965 GetCurrentState() == kSM_WaitingRun ||
1966 GetCurrentState() == kSM_NightlyOpen)
1967 {
1968 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
1969 if (fNumSubAndFitsIsOn)
1970 fNumSubAndFits->Update();
1971 }
1972#ifdef HAVE_FITS
1973 CreateFitsGrouping(fOpenedNightlyFits);
1974#endif
1975 return kSM_Ready;
1976}
1977
1978// --------------------------------------------------------------------------
1979//
1980//! Implements the transition towards kSM_WaitingRun
1981//! If current state is kSM_Ready, then tries to go to nightlyOpen state first.
1982//! @returns
1983//! kSM_WaitingRun or kSM_BadFolder
1984int DataLogger::NightlyToWaitRun()
1985{
1986 int cState = GetCurrentState();
1987
1988 if (cState == kSM_Ready)
1989 cState = Start();
1990
1991 if (cState != kSM_NightlyOpen)
1992 return GetCurrentState();
1993
1994 if (fDebugIsOn)
1995 {
1996 Debug("Going to Wait Run Number state...");
1997 }
1998 return kSM_WaitingRun;
1999}
2000// --------------------------------------------------------------------------
2001//
2002//! Implements the transition from wait for run number to nightly open
2003//! Does nothing really.
2004//! @returns
2005//! kSM_WaitingRun
2006int DataLogger::BackToNightlyOpen()
2007{
2008 if (GetCurrentState()==kSM_Logging)
2009 StopRunLogging();
2010
2011 if (fDebugIsOn)
2012 {
2013 Debug("Going to NightlyOpen state...");
2014 }
2015 return kSM_NightlyOpen;
2016}
2017// --------------------------------------------------------------------------
2018//
2019//! Setup Logger's configuration from a Configuration object
2020//! @param conf the configuration object that should be used
2021//!
2022int DataLogger::EvalOptions(Configuration& conf)
2023{
2024 fDebugIsOn = conf.Get<bool>("debug");
2025 fFilesStats.SetDebugMode(fDebugIsOn);
2026
2027 //Set the block or allow list
2028 fBlackList.clear();
2029 fWhiteList.clear();
2030
2031 //Adding entries that should ALWAYS be ignored
2032 fBlackList.insert("DATA_LOGGER/MESSAGE");
2033 fBlackList.insert("/SERVICE_LIST");
2034 fBlackList.insert("DIS_DNS/");
2035
2036 //set the black list, white list and the goruping
2037 const vector<string> vec1 = conf.Vec<string>("block");
2038 const vector<string> vec2 = conf.Vec<string>("allow");
2039 const vector<string> vec3 = conf.Vec<string>("group");
2040
2041 fBlackList.insert(vec1.begin(), vec1.end());
2042 fWhiteList.insert(vec2.begin(), vec2.end());
2043 fGrouping.insert( vec3.begin(), vec3.end());
2044
2045 //set the old run numbers timeout delay
2046 if (conf.Has("run-timeout"))
2047 {
2048 const uint32_t timeout = conf.Get<uint32_t>("run-timeout");
2049 if (timeout == 0)
2050 {
2051 Error("Time out delay for old run numbers must not be 0.");
2052 return 1;
2053 }
2054 fRunNumberTimeout = timeout;
2055 }
2056
2057 //configure the run files directory
2058 if (conf.Has("destination-folder"))
2059 {
2060 const string folder = conf.Get<string>("destination-folder");
2061 if (!fFilesStats.SetCurrentFolder(folder))
2062 return 2;
2063
2064 fFilePath = folder;
2065 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
2066 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
2067 return 3;
2068
2069 fNightlyLogFile << endl;
2070 NotifyOpenedFile(fFullNightlyLogFileName, 1, fOpenedNightlyFiles);
2071 for (vector<string>::iterator it=backLogBuffer.begin();it!=backLogBuffer.end();it++)
2072 fNightlyLogFile << *it;
2073 }
2074
2075 shouldBackLog = false;
2076 backLogBuffer.clear();
2077
2078 //configure the interval between statistics updates
2079 if (conf.Has("stats-interval"))
2080 fFilesStats.SetUpdateInterval(conf.Get<int16_t>("stats-interval"));
2081
2082 //configure if the filenames service is on or off
2083 fOpenedFilesIsOn = !conf.Get<bool>("no-filename-service");
2084
2085 //configure if the number of subscriptions and fits files is on or off.
2086 fNumSubAndFitsIsOn = !conf.Get<bool>("no-numsubs-service");
2087 //should we open the daily files at startup ?
2088 if (conf.Has("start-daily-files"))
2089 if (conf.Get<bool>("start-daily-files"))
2090 {
2091 fShouldAutoStart = true;
2092 }
2093 return -1;
2094}
2095
2096
2097#include "Main.h"
2098
2099// --------------------------------------------------------------------------
2100template<class T>
2101int RunShell(Configuration &conf)
2102{
2103 return Main::execute<T, DataLogger>(conf);//, true);
2104}
2105
2106/*
2107 Extract usage clause(s) [if any] for SYNOPSIS.
2108 Translators: "Usage" and "or" here are patterns (regular expressions) which
2109 are used to match the usage synopsis in program output. An example from cp
2110 (GNU coreutils) which contains both strings:
2111 Usage: cp [OPTION]... [-T] SOURCE DEST
2112 or: cp [OPTION]... SOURCE... DIRECTORY
2113 or: cp [OPTION]... -t DIRECTORY SOURCE...
2114 */
2115void PrintUsage()
2116{
2117 cout << "\n"
2118 "The data logger connects to all available Dim services and "
2119 "writes them to ascii and fits files.\n"
2120 "\n"
2121 "The default is that the program is started without user interaction. "
2122 "All actions are supposed to arrive as DimCommands. Using the -c "
2123 "option, a local shell can be initialized. With h or help a short "
2124 "help message about the usage can be brought to the screen.\n"
2125 "\n"
2126 "Usage: datalogger [-c type] [OPTIONS]\n"
2127 " or: datalogger [OPTIONS]\n";
2128 cout << endl;
2129
2130}
2131// --------------------------------------------------------------------------
2132void PrintHelp()
2133{
2134 /* Additional help text which is printed after the configuration
2135 options goes here */
2136 cout <<
2137 "\n"
2138 "If the allow list has any element, only the servers and/or services "
2139 "specified in the list will be used for subscription. The black list "
2140 "will disable service subscription and has higher priority than the "
2141 "allow list. If the allow list is not present by default all services "
2142 "will be subscribed."
2143 "\n"
2144 "For example, block=DIS_DNS/ will skip all the services offered by "
2145 "the DIS_DNS server, while block=/SERVICE_LIST will skip all the "
2146 "SERVICE_LIST services offered by any server and DIS_DNS/SERVICE_LIST "
2147 "will skip DIS_DNS/SERVICE_LIST.\n"
2148 << endl;
2149
2150 Main::PrintHelp<DataLogger>();
2151}
2152
2153// --------------------------------------------------------------------------
2154void SetupConfiguration(Configuration &conf)
2155{
2156 po::options_description configs("DataLogger options");
2157 configs.add_options()
2158 ("block,b", vars<string>(), "Black-list to block services")
2159 ("allow,a", vars<string>(), "White-list to only allowe certain services")
2160 ("debug,d", po_bool(), "Debug mode. Print clear text of received service reports.")
2161 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2162 ("run-timeout", var<uint32_t>(), "Time out delay for old run numbers in milliseconds.")
2163 ("destination-folder", var<string>(), "Base path for the nightly and run files")
2164 ("stats-interval", var<int16_t>(), "Interval in milliseconds for write statistics update")
2165 ("no-filename-service", po_bool(), "Disable update of filename service")
2166 ("no-numsubs-service", po_bool(), "Disable update of number-of-subscriptions service")
2167 ("start-daily-files", po_bool(), "Starts the logger in DailyFileOpen instead of Ready")
2168 ;
2169
2170 conf.AddOptions(configs);
2171}
2172// --------------------------------------------------------------------------
2173int main(int argc, const char* argv[])
2174{
2175 Configuration conf(argv[0]);
2176 conf.SetPrintUsage(PrintUsage);
2177 Main::SetupConfiguration(conf);
2178 SetupConfiguration(conf);
2179
2180 if (!conf.DoParse(argc, argv, PrintHelp))
2181 return 127;
2182
2183 {
2184 // No console access at all
2185 if (!conf.Has("console"))
2186 return RunShell<LocalStream>(conf);
2187
2188 // Console access w/ and w/o Dim
2189 if (conf.Get<int>("console")==0)
2190 return RunShell<LocalShell>(conf);
2191 else
2192 return RunShell<LocalConsole>(conf);
2193 }
2194
2195
2196 return 0;
2197}
Note: See TracBrowser for help on using the repository browser.