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

Last change on this file since 11218 was 11218, checked in by tbretz, 13 years ago
Added --cmd and --quit program option.
File size: 93.1 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="NightlyOpen"]
17 w [label="WaitingRun"]
18 l [label="Logging"]
19 b [label="BadNightlyconfig" 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 //****************************************************************
41#include "Dim.h"
42#include "Event.h"
43#include "Time.h"
44#include "StateMachineDim.h"
45#include "WindowLog.h"
46#include "Configuration.h"
47#include "ServiceList.h"
48#include "Converter.h"
49#include "MessageImp.h"
50#include "LocalControl.h"
51#include "DimDescriptionService.h"
52
53#include "Description.h"
54
55//#include "DimServiceInfoList.h"
56#include "DimNetwork.h"
57//for getting stat of opened files
58#include <unistd.h>
59//for getting disk free space
60#include <sys/statvfs.h>
61//for getting files sizes
62#include <sys/stat.h>
63
64#include <fstream>
65#include <mutex>
66
67#include <boost/bind.hpp>
68#if BOOST_VERSION < 104400
69#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
70#undef BOOST_HAS_RVALUE_REFS
71#endif
72#endif
73#include <boost/thread.hpp>
74
75#ifdef HAVE_FITS
76#include "Fits.h"
77#endif
78
79//Dim structures
80///Distributes the writing statistics
81struct DataLoggerStats {
82 long sizeWritten;
83 long freeSpace;
84 long writingRate;
85};
86///distributes the number of opened subscriptions and fits files
87struct NumSubAndFitsType {
88 int numSubscriptions;
89 int numOpenFits;
90};
91///distributes which files were opened.
92struct OpenFileToDim {
93 int code;
94 char fileName[FILENAME_MAX];
95};
96
97///Run number record. Used to keep track of which run numbers are still active
98struct RunNumberType {
99#ifdef RUN_LOGS
100 ///the run number log file
101 shared_ptr<ofstream> logFile;
102#endif
103 ///the run number report file
104 shared_ptr<ofstream> reportFile;
105#ifdef HAVE_FITS
106 ///the run number group fits file
107 shared_ptr<CCfits::FITS> runFitsFile;
108#endif
109#ifdef RUN_LOGS
110 ///the log filename
111 string logName;
112#endif
113 ///the report filename
114 string reportName;
115 ///the actual run number
116 uint32_t runNumber;
117 ///the time at which the run number was received
118 Time time;
119 ///list of opened fits used to create the fits grouping when the run ends
120 map<string, vector<string> > openedFits;
121 ///default constructor
122 RunNumberType()
123 {
124#ifdef RUN_LOGS
125 logFile = shared_ptr<ofstream>(new ofstream());
126#endif
127 reportFile = shared_ptr<ofstream>(new ofstream());
128#ifdef HAVE_FITS
129 runFitsFile = shared_ptr<CCfits::FITS>();
130#endif
131 runNumber = 0;
132 }
133 ///default destructor
134 ~RunNumberType()
135 {
136
137 }
138
139 void addServiceToOpenedFits(const string& fileName, const string& serviceName)
140 {
141 //most likely I should add this service name.
142 //the only case for which I should not add it is if a service disapeared, hence the file was closed
143 //and reopened again. Unlikely to happen, but well it may
144
145 if (find(openedFits[fileName].begin(), openedFits[fileName].end(),
146 serviceName)==openedFits[fileName].end())
147 openedFits[fileName].push_back(serviceName);
148 }
149};
150///Dim subscription type. Stores all the relevant info to handle a Dim subscription
151struct SubscriptionType
152{
153#ifdef HAVE_FITS
154 ///Nightly FITS output file
155 Fits nightlyFile;
156 ///run-specific FITS output file
157 Fits runFile;
158#endif
159 ///the actual dimInfo pointer
160 shared_ptr<DimStampedInfo> dimInfo;
161 ///the server
162 string server;
163 ///the service
164 string service;
165 ///the converter for outputting the data according to the format
166 shared_ptr<Converter> fConv;
167 ///the current run number used by this subscription
168 uint32_t runNumber;
169 ///time of the latest received event
170 Time lastReceivedEvent;
171 ///whether or not the fits buffer was allocated already
172 bool fitsBufferAllocated;
173
174 ///Dim info constructor
175 SubscriptionType(DimStampedInfo* info=NULL)
176 {
177 dimInfo = shared_ptr<DimStampedInfo>(info);
178 fConv = shared_ptr<Converter>();
179 runNumber = 0;
180 lastReceivedEvent = Time::None;
181 fitsBufferAllocated = false;
182 }
183
184 ///default destructor
185 ~SubscriptionType()
186 {
187 }
188};
189
190class DataLogger : public StateMachineDim, DimServiceInfoList
191{
192public:
193 /// The list of existing states specific to the DataLogger
194 enum
195 {
196 kSM_NightlyOpen = 20, ///< Nightly file openned and writing
197 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
198 kSM_Logging = 40, ///< both files openned and writing
199 kSM_BadNightlyConfig = 0x101, ///< the folder specified for Nightly logging does not exist or has bad permissions
200 kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number
201 kSM_WriteError = 0x103, ///< Denotes that an error occured while writing a file (text or fits).
202 } localstates_t;
203
204 DataLogger(ostream &out);
205 ~DataLogger();
206
207 bool SetConfiguration(Configuration& conf);
208
209private:
210 /************************************************
211 * MEMBER VARIABLES
212 ************************************************/
213 /// ofstream for the NightlyLogfile
214 ofstream fNightlyLogFile;
215 /// ofstream for the Nightly report file
216 ofstream fNightlyReportFile;
217 /// base path of the Nightlyfile
218 string fNightlyFilePath;
219 ///base path of the run file
220 string fRunFilePath;
221 ///run numbers
222 list<RunNumberType> fRunNumber;
223 ///old run numbers time-out delay (in seconds)
224 long fRunNumberTimeout;
225 ///previous run number. to check if changed while logging
226 int fPreviousRunNumber;
227 ///Current Service Quality
228 int fQuality;
229 ///Modified Julian Date
230 double fMjD;
231 ///for obtaining the name of the existing services
232// ServiceList fServiceList;
233 typedef map<const string, map<string, SubscriptionType> > SubscriptionsListType;
234 ///All the services to which we have subscribed to, sorted by server name.
235 SubscriptionsListType fServiceSubscriptions;
236 ///full name of the nightly log file
237 string fFullNightlyLogFileName;
238 ///full name of the nightly report file
239 string fFullNightlyReportFileName;
240 ///variable to track when the statistic were last calculated
241 Time fPreviousStatsUpdateTime;
242 Time fPreviousOldRunNumberCheck;
243 ///boolean to know whether we should close and reopen daily files or not
244 bool fDailyFileDayChangedAlready;
245
246private:
247 /***************************************************
248 * DIM INFO HANDLER
249 ***************************************************/
250 //overloading of DIM's infoHandler function
251 void infoHandler();
252
253 /***************************************************
254 * TRANSITION FUNCTIONS
255 ***************************************************/
256 ///Reporting method for the services info received
257 void ReportPlease(DimInfo* I, SubscriptionType& sub);
258
259 int ConfigureFileName(string &target, const string &type, const EventImp &evt);
260 ///Configuration of the nightly file path
261 int ConfigureNightlyFileName(const Event& evt);
262 ///Configuration fo the file name
263 int ConfigureRunFileName(const Event& evt);
264 ///DEPREC - configuration of the run number
265 int ConfigureRunNumber(const Event& evt);
266 ///print the current state of the dataLogger
267 int PrintStatePlease(const Event& evt);
268 ///checks whether or not the current info being treated is a run number
269 void CheckForRunNumber(DimInfo* I);
270 /// start transition
271 int StartPlease();
272 ///from waiting to logging transition
273 int StartRunPlease();
274 /// from logging to waiting transition
275 int StopRunPlease();
276 ///stop and reset transition
277 int GoToReadyPlease();
278 ///from NightlyOpen to waiting transition
279 int NightlyToWaitRunPlease();
280 /// from writing to error
281 std::string SetCurrentState(int state, const char *txt="", const std::string &cmd="");
282#ifdef HAVE_FITS
283 ///Open fits files
284 void OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber);
285 ///Write data to FITS files
286 void WriteToFITS(SubscriptionType& sub);
287 ///Allocate the buffers required for fits
288 void AllocateFITSBuffers(SubscriptionType& sub);
289#endif//has_fits
290
291 /***************************************
292 * DIM SERVICES PROVIDED BY THE DATA LOGGER
293 ***************************************/
294 ///monitoring notification loop
295 void ServicesMonitoring();
296 inline void NotifyOpenedFile(const string &name, int type, DimDescribedService* service);
297 ///variables for computing statistics
298 DataLoggerStats fStatVar;
299 ///mutex to make sure that the Stats are not accessed while updating
300// mutex fStatsMutex;
301 ///services notification thread
302// boost::thread fMonitoringThread;
303 ///end of the monitoring
304// bool fContinueMonitoring;
305 ///stores the size of each file that is or was open
306 map<string, long> fFileSizesMap;
307 ///total size of the opened files BEFORE they were opened by the logger
308 long fBaseSizeNightly;
309 long fPreviousSize;
310 long fBaseSizeRun;
311 ///Service for opened files
312 DimDescribedService* fOpenedNightlyFiles;
313 DimDescribedService* fOpenedRunFiles;
314 DimDescribedService* fNumSubAndFits;
315 DimDescribedService* fStatsMonitoring;
316 NumSubAndFitsType fNumSubAndFitsData;
317 ///Small function for calculating the total size written so far
318 bool calculateTotalSizeWritten(DataLoggerStats& statVar, bool isPrinting);
319
320 /***************************************************
321 * DATA LOGGER's CONFIGURATION STUFF
322 ***************************************************/
323 ///black/white listing
324 set<string> fBlackList;
325 set<string> fWhiteList;
326 ///list of services to be grouped
327 set<string> fGrouping;
328 ///configuration flags
329 bool fDebugIsOn;
330 float fStatsPeriodDuration;
331 bool fOpenedFilesIsOn;
332 bool fNumSubAndFitsIsOn;
333 //functions for controlling the services behavior
334 int SetDebugOnOff(const Event& evt);
335 int SetStatsPeriod(const Event& evt);
336 int SetOpenedFilesOnOff(const Event& evt);
337 int SetNumSubsAndFitsOnOff(const Event& evt);
338 int SetRunTimeoutDelay(const Event& evt);
339
340 ///boolean to prevent DIM update while desctructing the dataLogger
341 bool fDestructing;
342 /***************************************************
343 * UTILITIES
344 ***************************************************/
345 ///vectors to keep track of opened Fits files, for grouping purposes.
346 map<string, vector<string> > fOpenedNightlyFits;
347 ///creates a group fits file based on a list of files to be grouped
348 void CreateFitsGrouping(map<string, vector<string> >& filesToGroup, int runNumber);
349
350 bool OpenStream(shared_ptr<ofstream> stream, const string &filename);
351 ///Open the relevant text files related to a particular run
352 int OpenRunFile(RunNumberType& run);
353 ///add a new run number
354 void AddNewRunNumber(int64_t newRun, Time time);
355 ///removes the oldest run number, and close the relevant files.
356 void RemoveOldestRunNumber();
357 ///retrieves the size of a file
358 off_t GetFileSize(const string&);
359 ///Get the digits of year, month and day for filenames and paths
360 void GetYearMonthDayForFiles(unsigned short& year, unsigned short& month, unsigned short& day);
361 ///Appends the relevant year month day to a given path
362 void AppendYearMonthDaytoPath(string& path);
363 ///Form the files path
364 string CompileFileNameWithPath(const string &path, const string &service, const string & extension, const Time &time=Time());
365 ///Form the file names only
366 string CompileFileName(const string& service, const string& extension, const Time& time=Time());
367 ///Form the files path
368 string CompileFileNameWithPath(const string &path, const uint32_t run, const string &service, const string & extension, const Time &time=Time());
369 ///Form the file names only
370 string CompileFileName(const uint32_t run, const string& service, const string& extension);//, const Time& time=Time());
371 ///Check whether service is in black and/or white list
372 bool ShouldSubscribe(const string& server, const string& service);
373 ///Subscribe to a given server and service
374 DimStampedInfo* SubscribeToPlease(const string& server, const string& service);
375 ///Open a text file and checks for ofstream status
376 bool OpenTextFilePlease(ofstream& stream, const string& name);
377 ///Check if a dir is . and returns the actual string corresponding to .
378// string CheckIfDirIsDot(const string& dir);
379 ///Remembers the size of newly opened files. for statistic purposes
380 bool RememberFileOrigSizePlease(string& fileName, bool nightly);
381 ///Checks if the input osftream is in error state, and if so close it.
382 bool CheckForOfstreamError(ofstream& out);
383 ///Goes to Write error state and also closes all opened files
384 void GoToWriteErrorState();
385 ///Checks if a given path exist
386 bool DoesPathExist(string path);
387 ///Check if the statistics service should be updated, and if so, do it
388 void UpdateStatisticsService();
389 ///Check if old run numbers can be trimmed, and if so, do it
390 void TrimOldRunNumbers();
391 ///Create a given directory
392 bool CreateDirectory(string path);
393 /***************************************************
394 * INHERITED FROM DIMSERVICEINFOLIST
395 ***************************************************/
396 ///Add a new service subscription
397 void AddService(const string&, const string&, const string&, bool);
398 ///Remove a given service subscription
399 void RemoveService(const string&, const string&, bool);
400 ///Remove all the services associated with a given server
401 void RemoveAllServices(const string&);
402}; //DataLogger
403
404// --------------------------------------------------------------------------
405//
406//! Check if a given path exists
407//! @param path the path to be checked
408//! @return whether or not the creation has been successfull
409//
410bool DataLogger::CreateDirectory(string path)
411{
412 //remove last '/', if present
413 if (path[path.size()-1] == '/')
414 path = path.substr(0, path.size()-1);
415
416 //create boost path
417 const boost::filesystem::path fullPath = boost::filesystem::system_complete(boost::filesystem::path(path));
418
419 //if path does not exist, check if upper levels exist already
420 if (boost::filesystem::exists(fullPath))
421 {
422 //if path already exist, make sure it does not designate a file (filenames cannot be checked if they do not exist)
423 if (boost::filesystem::is_directory(fullPath))
424 return true;
425
426 Error("Path to be created contains a file name: '" + path + "'");
427 return false;
428 }
429
430 if (path.size() <= 1)
431 {//we're hitting "/", which SHOULD have existed...
432 Error("Something unexpected happened while creating a path");
433 }
434 CreateDirectory(path.substr(0, path.find_last_of('/')));
435
436 //path does not exist, and upper level have been created already by recusrion.
437 const mode_t rightsMask = S_IRWXU | S_IXGRP | S_IRGRP | S_IXOTH | S_IROTH; //everybody read, owner writes
438
439 const int returnValue = mkdir(path.c_str(), rightsMask);
440
441 if (returnValue != 0)
442 {
443 ostringstream str;
444 str << "Could not create directory " << path << " mkdir error code: " << errno;
445 Error(str.str());
446 return false;
447 }
448
449 return true;
450}
451// --------------------------------------------------------------------------
452//
453//! Check if a given path exists
454//! @param path the path to be checked
455//! @return whether or not the given path exists
456//
457bool DataLogger::DoesPathExist(string path)
458{
459 const boost::filesystem::path fullPath = boost::filesystem::system_complete(boost::filesystem::path(path));
460
461 if (!boost::filesystem::exists(fullPath))
462 return false;
463
464 if (!boost::filesystem::is_directory(fullPath))
465 {
466 Error("Path given for checking '" + path + "' designate a file name. Please provide a path name only");
467 return false;
468 }
469
470 if (access(path.c_str(), R_OK|W_OK|X_OK) != 0)
471 {
472 Error("Missing read, write or execute permissions on directory '" + path + "'");
473 return false;
474 }
475
476 return true;
477}
478// --------------------------------------------------------------------------
479//
480//! Add a new service subscription
481//! @param server the server for which the subscription should be created
482//! @param service the service for which the subscription should be created
483//! @param isCmd whether this is a Dim Command or not. Commands are not logged
484//
485void DataLogger::AddService(const string& server, const string& service, const string&, bool isCmd)
486{
487 //dataLogger does not subscribe to commands
488 if (isCmd)
489 return;
490
491 //check the given subscription against black and white lists
492 if (!ShouldSubscribe(server, service))
493 return;
494
495 map<string, SubscriptionType> &list = fServiceSubscriptions[server];
496
497 if (list.find(service) != list.end())
498 {
499 Error("Service " + server + "/" + service + " is already in the dataLogger's list. ignoring its update.");
500 return;
501 }
502
503 list[service].dimInfo = shared_ptr<DimStampedInfo>(SubscribeToPlease(server, service));
504 list[service].server = server;
505 list[service].service = service;
506 fNumSubAndFitsData.numSubscriptions++;
507 if (fDebugIsOn)
508 Debug("Added subscription to " + server + "/" + service);
509}
510// --------------------------------------------------------------------------
511//
512//! Remove a given service subscription
513//! @param server the server for which the subscription should be removed
514//! @param service the service that should be removed
515//! @param isCmd whether or not this is a command
516//
517void DataLogger::RemoveService(const string& server, const string& service, bool isCmd)
518{
519 if (isCmd)
520 return;
521
522 if (fServiceSubscriptions[server].erase(service) != 1)
523 {
524 //check the given subscription against black and white lists
525 if (!ShouldSubscribe(server, service))
526 return;
527
528 ostringstream str;
529 str << "Subscription " << server << "/" << service << " could not be removed as it is not present";
530 Error(str.str());
531 return;
532 }
533 fNumSubAndFitsData.numSubscriptions--;
534 if (fDebugIsOn)
535 {
536 Debug("Removed subscription to " + server + "/" + service);
537 }
538}
539// --------------------------------------------------------------------------
540//
541//! Remove all the services associated with a given server
542//! @param server the server for which all the services should be removed
543//
544void DataLogger::RemoveAllServices(const string& server)
545{
546 fNumSubAndFitsData.numSubscriptions -= fServiceSubscriptions[server].size();
547 fServiceSubscriptions[server].clear();
548 if (fDebugIsOn)
549 {
550 Debug("Removed all subscriptions to " + server + "/");
551 }
552}
553// --------------------------------------------------------------------------
554//
555//! Checks if the given ofstream is in error state and if so, close it
556//! @param out the ofstream that should be checked
557//
558bool DataLogger::CheckForOfstreamError(ofstream& out)
559{
560 if (out.good())
561 return true;
562
563 Error("An error occured while writing to a text file. Closing it");
564// if (out.is_open())
565// out.close();
566 GoToWriteErrorState();
567
568 return false;
569// SetCurrentState(kSM_WriteError);
570}
571// --------------------------------------------------------------------------
572//
573//! Checks the size on disk of a given size, and remembers it in the relevant member variable
574//! @param fileName the file for which the size on disk should be retrieved
575//! @param nightly whether this is a run or nightly file, so that its size is added to the correct member variable
576//
577bool DataLogger::RememberFileOrigSizePlease(string& fileName, bool nightly)
578{
579 //get the size of the file we're about to open
580 if (fFileSizesMap.find(fileName) != fFileSizesMap.end())
581 return false;
582
583 if (nightly)
584 fBaseSizeNightly += GetFileSize(fileName);
585 else
586 fBaseSizeRun += GetFileSize(fileName);
587 fFileSizesMap[fileName] = 0;
588 return true;
589}
590
591// --------------------------------------------------------------------------
592//
593//! Open a text file and checks for error code
594//! @param stream the ofstream for which the file should be opened
595//! @name the file name
596//
597bool DataLogger::OpenTextFilePlease(ofstream& stream, const string& name)
598{
599 Info("Opening: "+name);
600
601 errno = 0;
602 stream.open(name.c_str(), ios_base::out | ios_base::app);
603 if (!stream)
604 {
605 ostringstream str;
606 str << "Trying to open file " << name << ": " << strerror(errno) << " (errno=" << errno << ")";
607 Error(str);
608 return false;
609 }
610
611 return true;
612}
613
614// --------------------------------------------------------------------------
615//
616//! Create a new dim subscription to a given server and service
617//! @param server the server name
618//! @param service the service name
619//
620DimStampedInfo* DataLogger::SubscribeToPlease(const string& server, const string& service)
621{
622 if (fDebugIsOn)
623 {
624 ostringstream str;
625 str << "Subscribing to service " << server << "/" << service;
626 Debug(str);
627 }
628 return new DimStampedInfo((server + "/" + service).c_str(), (void*)NULL, 0, this);
629}
630// --------------------------------------------------------------------------
631//
632//! Check whether a service should be subscribed to, based on the black/white list entries
633//! @param server the server name associated with the service being checked
634//! @param service the service name associated with the service being checked
635//
636bool DataLogger::ShouldSubscribe(const string& server, const string& service)
637{
638 if (fWhiteList.size()>0 &&
639 (fWhiteList.find(server + "/") == fWhiteList.end()) &&
640 (fWhiteList.find(server + "/" + service) == fWhiteList.end()) &&
641 (fWhiteList.find("/" + service) == fWhiteList.end()))
642 return false;
643
644 if ((fBlackList.find(server + "/") != fBlackList.end()) ||
645 (fBlackList.find(server + "/" + service) != fBlackList.end()) ||
646 (fBlackList.find("/" + service) != fBlackList.end()))
647 return false;
648
649 return true;
650}
651// --------------------------------------------------------------------------
652//
653//! Compiles a file name
654//! @param path the base path where to put the file
655//! @param time the time at which the file is created
656//! @param service the service name, if any
657//! @param extension the extension to add, if any
658//
659//string DataLogger::CompileFileName(const string &path, const string &service, const string & extension, const Time &time)
660string DataLogger::CompileFileName(const string& service, const string& extension, const Time& time)
661{
662 ostringstream str;
663 //calculate time suitable for naming path.
664 const Time ftime(time-boost::posix_time::time_duration(12,0,0));
665
666 //output base of file name
667 str << Time::fmt("%Y_%m_%d") << ftime;
668
669 //output service name
670 if (!service.empty())
671 str << "_" << service;
672
673 //output appropriate extension
674 if (!extension.empty())
675 str << "." << extension;
676
677 return str.str();
678}
679
680string DataLogger::CompileFileNameWithPath(const string& path, const string& service, const string& extension, const Time& time)
681{
682 ostringstream str;
683 //calculate time suitable for naming files.
684 const Time ftime(time-boost::posix_time::time_duration(12,0,0));
685
686 //output it
687 str << path << Time::fmt("/%Y/%m/%d") << ftime;
688
689 //check if target directory exist
690 if (!DoesPathExist(str.str()))
691 CreateDirectory(str.str());
692
693 str << '/' << CompileFileName(service, extension, time);
694
695 return str.str();
696
697
698}
699// --------------------------------------------------------------------------
700//
701//! Compiles a file name
702//! @param path the base path where to put the file
703//! @param time the time at which the file is created
704//! @param run the run number
705//! @param service the service name, if any
706//! @param extension the extension to add, if any
707//
708//string DataLogger::CompileFileName(const string &path, uint32_t run, const string &service, const string & extension, const Time &time)
709string DataLogger::CompileFileName(const uint32_t run, const string& service, const string& extension)//, const Time& time)
710{
711 ostringstream str;
712 //output base of file name
713 str << setfill('0') << setw(8) << run;
714
715 //output service name
716 if (!service.empty())
717 str << "_" << service;
718
719 //output appropriate extension
720 if (!extension.empty())
721 str << "." << extension;
722 return str.str();
723}
724
725string DataLogger::CompileFileNameWithPath(const string& path, const uint32_t run, const string& service, const string& extension, const Time& time)
726{
727 ostringstream str;
728 //calculate suitable time for naming files and output it
729 str << path << Time::fmt("/%Y/%m/%d") << (time-boost::posix_time::time_duration(12,0,0));
730
731 //check if target directory exist
732 if (!DoesPathExist(str.str()))
733 CreateDirectory(str.str());
734
735 str << '/' << CompileFileName(run, service, extension);//, time);
736
737 return str.str();
738
739}
740// --------------------------------------------------------------------------
741//
742//!retrieves the size on disk of a file
743//! @param fileName the full file name for which the size on disk should be retrieved
744//! @return the size of the file on disk, in bytes. 0 if the file does not exist or if an error occured
745//
746off_t DataLogger::GetFileSize(const string& fileName)
747{
748 errno = 0;
749 struct stat st;
750 if (!stat(fileName.c_str(), &st))
751 return st.st_size;
752
753 if (errno != 0 && errno != 2)//ignoring error #2: no such file or directory is not an error for new files
754 {
755 ostringstream str;
756 str << "Unable to stat " << fileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
757 Error(str);
758 }
759
760 return 0;
761}
762// --------------------------------------------------------------------------
763//
764//! Removes the oldest run number and closes the fits files that should be closed
765//! Also creates the fits grouping file
766//
767void DataLogger::RemoveOldestRunNumber()
768{
769 if (fDebugIsOn)
770 {
771 ostringstream str;
772 str << "Removing run number " << fRunNumber.front().runNumber;
773 Debug(str);
774 }
775 CreateFitsGrouping(fRunNumber.front().openedFits, fRunNumber.front().runNumber);
776
777 //crawl through the subscriptions to see if there are still corresponding fits files opened.
778 for (SubscriptionsListType::iterator x=fServiceSubscriptions.begin();
779 x!=fServiceSubscriptions.end(); x++)
780 for (map<string, SubscriptionType>::iterator y=x->second.begin();
781 y!=x->second.end(); y++)
782 if (y->second.runFile.fRunNumber == fRunNumber.front().runNumber && y->second.runFile.IsOpen())
783 {
784 y->second.runFile.Close();
785
786 Info("Closed: "+y->second.runFile.fFileName);
787 }
788 //if a grouping file is on, decrease the number of opened fits manually
789 if (fRunNumber.front().runFitsFile)
790 (fNumSubAndFitsData.numOpenFits)--;
791 //remove the entry
792 fRunNumber.pop_front();
793}
794
795// --------------------------------------------------------------------------
796//
797//! Calculate the total number of written bytes since the logger was started
798//! @param statVar the data structure that should be updated
799//! @param shouldWarn whether or not error messages should be outputted
800//! @param isPrinting whether this function was called from the PRINT command or not. If so, displays relevant information
801//
802bool DataLogger::calculateTotalSizeWritten(DataLoggerStats& statVar, bool isPrinting)
803{
804//mutex
805// if (!isPrinting)
806// fStatsMutex.lock();
807#ifdef HAVE_FITS
808 if (isPrinting)
809 {
810 ostringstream str;
811 str << "There are " << fNumSubAndFitsData.numOpenFits << " FITS files open:";
812 Message(str);
813 }
814
815 ///TODO the grouping file is dealt with several times. This should not be a problem but well, better to fix it I guess.
816 for (SubscriptionsListType::const_iterator x=fServiceSubscriptions.begin();
817 x!=fServiceSubscriptions.end(); x++)
818 {
819 for (map<string, SubscriptionType>::const_iterator y=x->second.begin();
820 y!=x->second.end(); y++)
821 {
822 if (y->second.runFile.IsOpen())
823 {
824 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
825 if (isPrinting)
826 Message("-> "+y->second.runFile.fFileName);
827 }
828 if (y->second.nightlyFile.IsOpen())
829 {
830 fFileSizesMap[y->second.nightlyFile.fFileName] = y->second.nightlyFile.GetWrittenSize();
831 if (isPrinting)
832 Message("-> "+y->second.nightlyFile.fFileName);
833 }
834 }
835 }
836#else
837 if (isPrinting)
838 Message("FITS output disabled at compilation");
839#endif
840 //gather log and report files sizes on disk
841 if (fNightlyLogFile.is_open())
842 fFileSizesMap[fFullNightlyLogFileName] = GetFileSize(fFullNightlyLogFileName);
843 if (fNightlyReportFile.is_open())
844 fFileSizesMap[fFullNightlyReportFileName] = GetFileSize(fFullNightlyReportFileName);
845 for (list<RunNumberType>::iterator it = fRunNumber.begin(); it != fRunNumber.end(); it++)
846 {
847 if (it->reportFile->is_open())
848 fFileSizesMap[it->reportName] = GetFileSize(it->reportName);
849#ifdef RUN_LOGS
850 if (it->logFile->is_open())
851 fFileSizesMap[it->logName] = GetFileSize(it->logName);
852#endif
853 }
854
855 bool shouldWarn = false;
856 struct statvfs vfs;
857 if (!statvfs(fNightlyFilePath.c_str(), &vfs))
858 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
859 else
860 {
861 ostringstream str;
862 str << "Unable to retrieve stats for " << fNightlyFilePath << ". Reason: " << strerror(errno) << " [" << errno << "]";
863 if (!shouldWarn)
864 Error(str);
865 statVar.freeSpace = -1;
866 }
867 //sum up all the file sizes. past and present
868 statVar.sizeWritten = 0;
869 for (map<string, long>::const_iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
870 statVar.sizeWritten += it->second;
871 statVar.sizeWritten -= fBaseSizeNightly;
872 statVar.sizeWritten -= fBaseSizeRun;
873
874//mutex
875// if (!isPrinting)
876// fStatsMutex.unlock();
877
878 return shouldWarn;
879}
880
881// --------------------------------------------------------------------------
882//
883//! Monitor the number of opened files and total size written, and distributes this data through a Dim service
884//
885//
886/*
887void DataLogger::ServicesMonitoring()
888{
889 struct statvfs vfs;
890 if (!statvfs(fNightlyFilePath.c_str(), &vfs))
891 fStatVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
892 else
893 fStatVar.freeSpace = -1;
894
895 DimDescribedService srvc ("DATA_LOGGER/STATS", "X:3", fStatVar, "Add description here");
896 fPreviousSize = 0;
897 //loop-wait for broadcast
898 while (fContinueMonitoring)
899 {
900 if (fStatsPeriodDuration == 0.0f)
901 {
902 sleep(0.1f);
903 continue;
904 }
905
906 sleep(fStatsPeriodDuration);
907
908
909 fStatsMutex.lock();
910
911 if (fStatVar.writingRate != 0) //if data has been written
912 {
913 srvc.updateService();
914
915 if(fDebugIsOn)
916 {
917 ostringstream str;
918 str << "Size written: " << fStatVar.sizeWritten/1000 << " kB; writing rate: ";
919 str << fStatVar.writingRate/1000 << " kB/s; free space: ";
920 str << fStatVar.freeSpace/(1000*1000) << " MB";
921 Debug(str);
922 }
923 }
924 fStatsMutex.unlock();
925 }
926}
927*/
928// --------------------------------------------------------------------------
929//
930//! Default constructor. The name of the machine is given DATA_LOGGER
931//! and the state is set to kSM_Ready at the end of the function.
932//
933//!Setup the allows states, configs and transitions for the data logger
934//
935DataLogger::DataLogger(ostream &out) : StateMachineDim(out, "DATA_LOGGER")
936{
937 //initialize member data
938 fNightlyFilePath = ".";
939 fRunFilePath = ".";
940
941 //Give a name to this machine's specific states
942 AddStateName(kSM_NightlyOpen, "NightlyFileOpen", "The summary files for the night are open.");
943 AddStateName(kSM_WaitingRun, "WaitForRun", "The summary files for the night are open and we wait for a run to be started.");
944 AddStateName(kSM_Logging, "Logging", "The summary files for the night and the files for a single run are open.");
945 AddStateName(kSM_BadNightlyConfig, "ErrNightlyFolder", "The folder for the nighly summary files is invalid.");
946 AddStateName(kSM_BadRunConfig, "ErrRunFolder", "The folder for the run files is invalid.");
947 AddStateName(kSM_WriteError, "ErrWrite", "An error occured while writing to a file.");
948
949 // Add the possible transitions for this machine
950 AddEvent(kSM_NightlyOpen, "START", kSM_Ready, kSM_BadNightlyConfig)
951 (boost::bind(&DataLogger::StartPlease, this))
952 ("Start the nightly logging. Nightly file location must be specified already");
953
954 AddEvent(kSM_Ready, "STOP", kSM_NightlyOpen, kSM_WaitingRun, kSM_Logging, kSM_WriteError)
955 (boost::bind(&DataLogger::GoToReadyPlease, this))
956 ("Stop all data logging, close all files.");
957
958 AddEvent(kSM_Logging, "START_RUN", kSM_WaitingRun, kSM_BadRunConfig)
959 (boost::bind(&DataLogger::StartRunPlease, this))
960 ("Start the run logging. Run file location must be specified already.");
961
962 AddEvent(kSM_WaitingRun, "STOP_RUN", kSM_Logging)
963 (boost::bind(&DataLogger::StopRunPlease, this))
964 ("Wait for a run to be started, open run-files as soon as a run number arrives.");
965
966 AddEvent(kSM_Ready, "RESET", kSM_Error, kSM_BadNightlyConfig, kSM_BadRunConfig, kSM_WriteError)
967 (boost::bind(&DataLogger::GoToReadyPlease, this))
968 ("Transition to exit error states. Closes the any open file.");
969
970 AddEvent(kSM_WaitingRun, "WAIT_FOR_RUN_NUMBER", kSM_NightlyOpen)
971 (boost::bind(&DataLogger::NightlyToWaitRunPlease, this))
972 ("Go to waiting for run number state. In this state with any received run-number a new file is opened.");
973
974 // Add the possible configurations for this machine
975 AddEvent("SET_NIGHTLY_FOLDER", "C", kSM_Ready, kSM_BadNightlyConfig)
976 (boost::bind(&DataLogger::ConfigureNightlyFileName, this, _1))
977 ("Configure the base folder for the nightly files."
978 "|Path[string]:Absolute or relative path name where the nightly files should be stored.");
979
980 AddEvent("SET_RUN_FOLDER", "C", kSM_Ready, kSM_BadNightlyConfig, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig)
981 (boost::bind(&DataLogger::ConfigureRunFileName, this, _1))
982 ("Configure the base folder for the run files."
983 "|Path[string]:Absolute or relative path name where the run files should be stored.");
984
985 AddEvent("SET_RUN_NUMBER", "X", kSM_Ready, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig, kSM_Logging)
986 (boost::bind(&DataLogger::ConfigureRunNumber, this, _1))
987 ("Configure the run number. Cannot be done in logging state");
988
989 // Provide a print command
990 AddEvent("PRINT")
991 (boost::bind(&DataLogger::PrintStatePlease, this, _1))
992 ("Print information about the internal status of the data logger.");
993
994 OpenFileToDim fToDim;
995 fToDim.code = 0;
996 fToDim.fileName[0] = '\0';
997
998 fOpenedNightlyFiles = new DimDescribedService(GetName() + "/FILENAME_NIGHTLY", "I:1;C", fToDim,
999 "Path and base name which is used to compile the filenames for the nightly files."
1000 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
1001 "|Name[string]:path and base file name");
1002
1003 fOpenedRunFiles = new DimDescribedService(GetName() + "/FILENAME_RUN", "I:1;C", fToDim,
1004 "Path and base name which is used to compile the filenames for the run files."
1005 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
1006 "|Name[string]:path and base file name");
1007
1008 fNumSubAndFitsData.numSubscriptions = 0;
1009 fNumSubAndFitsData.numOpenFits = 0;
1010 fNumSubAndFits = new DimDescribedService(GetName() + "/NUM_SUBS", "I:2", fNumSubAndFitsData,
1011 "Shows number of services to which the data logger is currently subscribed and the total number of open files."
1012 "|Subscriptions[int]:number of dim services to which the data logger is currently subscribed."
1013 "|NumOpenFiles[int]:number of files currently open by the data logger");
1014
1015 //services parameters
1016 fDebugIsOn = false;
1017 fStatsPeriodDuration = 1.0f;
1018 fOpenedFilesIsOn = true;
1019 fNumSubAndFitsIsOn = true;
1020
1021 // provide services control commands
1022 AddEvent("SET_DEUG_MODE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1023 (boost::bind(&DataLogger::SetDebugOnOff, this, _1))
1024 ("Switch debug mode on or off. Debug mode prints ifnormation about every service written to a file."
1025 "|Enable[bool]:Enable of disable debug mode (yes/no).");
1026
1027 AddEvent("SET_STATISTICS_UPDATE_INTERVAL", "F", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1028 (boost::bind(&DataLogger::SetStatsPeriod, this, _1))
1029 ("Interval in which the data-logger statistics service (STATS) is updated."
1030 "|Interval[s]:Floating point value in seconds.");
1031
1032 AddEvent("ENABLE_FILENAME_SERVICES", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1033 (boost::bind(&DataLogger::SetOpenedFilesOnOff ,this, _1))
1034 ("Switch service which distributes information about the open files on or off."
1035 "|Enable[bool]:Enable of disable filename services (yes/no).");
1036
1037 AddEvent("ENABLE_NUMSUBS_SERVICE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1038 (boost::bind(&DataLogger::SetNumSubsAndFitsOnOff, this, _1))
1039 ("Switch the service which distributes information about the number of subscriptions and open files on or off."
1040 "|Enable[bool]:Enable of disable NUM_SUBS service (yes/no).");
1041
1042 AddEvent("SET_RUN_TIMEOUT", "L:1", kSM_Ready, kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun)
1043 (boost::bind(&DataLogger::SetRunTimeoutDelay, this, _1))
1044 ("Set the timeout delay for old run numbers."
1045 "|timeout[min]:Time out in minutes after which files for expired runs are closed.");
1046
1047 fDestructing = false;
1048
1049 //start the monitoring service
1050 fStatVar.sizeWritten = 0;
1051 fStatVar.freeSpace = 0;
1052 fStatVar.writingRate = 0;
1053 fPreviousStatsUpdateTime = Time().Mjd();
1054 fPreviousOldRunNumberCheck = Time().Mjd();
1055 fPreviousSize = 0;
1056
1057 struct statvfs vfs;
1058 if (!statvfs(fNightlyFilePath.c_str(), &vfs))
1059 fStatVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
1060 else
1061 fStatVar.freeSpace = -1;
1062
1063 fStatsMonitoring = new DimDescribedService(GetName() + "/STATS", "X:3", fStatVar, "Add description here");
1064
1065//mutex
1066// fContinueMonitoring = true;
1067// fMonitoringThread = boost::thread(boost::bind(&DataLogger::ServicesMonitoring, this));
1068 fBaseSizeNightly = 0;
1069 fBaseSizeRun = 0;
1070 fDailyFileDayChangedAlready = true;
1071 fRunNumberTimeout = 60; //default run-timeout set to 1 minute
1072 if(fDebugIsOn)
1073 {
1074 Debug("DataLogger Init Done.");
1075 }
1076}
1077
1078// --------------------------------------------------------------------------
1079//
1080//! Destructor
1081//
1082DataLogger::~DataLogger()
1083{
1084 if (fDebugIsOn)
1085 {
1086 Debug("DataLogger destruction starts");
1087 }
1088 fDestructing = true;
1089 //first let's go to the ready state
1090 GoToReadyPlease();
1091 //release the services subscriptions
1092 fServiceSubscriptions.clear();
1093 //exit the monitoring loop
1094//mutex
1095// fContinueMonitoring = false;
1096// fMonitoringThread.join();
1097 //clear any remaining run number (should remain only one)
1098 while (fRunNumber.size() > 0)
1099 {
1100 RemoveOldestRunNumber();
1101 }
1102
1103 delete fOpenedNightlyFiles;
1104 delete fOpenedRunFiles;
1105 delete fNumSubAndFits;
1106
1107 if (fDebugIsOn)
1108 {
1109 Debug("DataLogger desctruction ends");
1110 }
1111}
1112// --------------------------------------------------------------------------
1113//
1114//! checks if the statistic service should be updated, and if so, do it
1115//
1116void DataLogger::UpdateStatisticsService()
1117{
1118 //update the fits files sizes
1119 const Time cTime = Time();
1120
1121 if ((fStatsPeriodDuration == 0) || ((cTime - fPreviousStatsUpdateTime).total_seconds() < fStatsPeriodDuration))
1122 return;
1123
1124 calculateTotalSizeWritten(fStatVar, false);
1125 fStatVar.writingRate = (fStatVar.sizeWritten - fPreviousSize)/((cTime - fPreviousStatsUpdateTime).total_seconds());
1126 fPreviousSize = fStatVar.sizeWritten;
1127 fPreviousStatsUpdateTime = cTime;
1128 //update the service. No need to check if data has been written, because some must have been, otherwise we would not have hit this piece of code
1129 fStatsMonitoring->updateService();
1130
1131 if(fDebugIsOn)
1132 {
1133 ostringstream str;
1134 str << "Size written: " << fStatVar.sizeWritten/1000 << " kB; writing rate: ";
1135 str << fStatVar.writingRate/1000 << " kB/s; free space: ";
1136 str << fStatVar.freeSpace/(1000*1000) << " MB";
1137 Debug(str);
1138 }
1139}
1140// --------------------------------------------------------------------------
1141//
1142//! checks if old run numbers should be trimmed and if so, do it
1143//
1144void DataLogger::TrimOldRunNumbers()
1145{
1146 const Time cTime = Time();
1147
1148 if ((cTime - fPreviousOldRunNumberCheck).total_seconds() < fRunNumberTimeout)
1149 return;
1150
1151 while (fRunNumber.size() > 1 && (cTime - fRunNumber.back().time) > boost::posix_time::seconds(fRunNumberTimeout))
1152 {
1153 RemoveOldestRunNumber();
1154 }
1155 fPreviousOldRunNumberCheck = cTime;
1156}
1157// --------------------------------------------------------------------------
1158//
1159//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
1160//
1161void DataLogger::infoHandler()
1162{
1163 // Make sure getTimestamp is called _before_ getTimestampMillisecs
1164 if (fDestructing)
1165 return;
1166
1167 DimInfo* I = getInfo();
1168
1169 if (I==NULL)
1170 return;
1171 //check if the service pointer corresponds to something that we subscribed to
1172 //this is a fix for a bug that provides bad Infos when a server starts
1173 bool found = false;
1174 SubscriptionsListType::iterator x;
1175 map<string, SubscriptionType>::iterator y;
1176 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1177 {//find current service is subscriptions
1178 for (y=x->second.begin(); y!=x->second.end();y++)
1179 if ((y->second.dimInfo).get() == I)
1180 {
1181 found = true;
1182 break;
1183 }
1184 if (found)
1185 break;
1186 }
1187 if (!found)
1188 {
1189 DimServiceInfoList::infoHandler();
1190 return;
1191 }
1192 if (I->getSize() <= 0 || I->getData()==NULL)
1193 return;
1194
1195 // Make sure that getTimestampMillisecs is NEVER called before
1196 // getTimestamp is properly called
1197 // check that the message has been updated by something, i.e. must be different from its initial value
1198 if (I->getTimestamp() == 0)
1199 return;
1200
1201 // FIXME: Here we have to check if we have received the
1202 // service with the run-number.
1203 // CheckForRunNumber(I); has been removed because we have to
1204 // subscribe to this service anyway and hence we have the pointer
1205 // (no need to check for the name)
1206
1207 ReportPlease(I, y->second);
1208
1209 //update the fits files sizes
1210 UpdateStatisticsService();
1211
1212 //remove old run numbers
1213 TrimOldRunNumbers();
1214}
1215
1216bool DataLogger::OpenStream(shared_ptr<ofstream> stream, const string &filename)
1217{
1218 Info("Opening: "+filename);
1219
1220 if (stream->is_open())
1221 {
1222 ostringstream str;
1223 str << filename << " was already open when trying to open it.";
1224 Error(str);
1225 return false;
1226 }
1227
1228 errno = 0;
1229 stream->open(filename.c_str(), ios_base::out | ios_base::app);
1230 if (errno != 0)
1231 {
1232 ostringstream str;
1233 str << "Unable to open " << filename << ": " << strerror(errno) << " (errno=" << errno << ")";
1234 Error(str);
1235 return false;
1236 }
1237
1238 if (!stream->is_open())
1239 {
1240 ostringstream str;
1241 str << "File " << filename << " not open as it ought to be.";
1242 Error(str);
1243 return false;
1244 }
1245
1246 return true;
1247}
1248
1249// --------------------------------------------------------------------------
1250//
1251//! Open the text files associated with the given run number
1252//! @param run the run number to be dealt with
1253//
1254int DataLogger::OpenRunFile(RunNumberType& run)
1255{
1256#ifdef RUN_LOGS
1257 // open log file
1258 run.logName = CompileFileName(fRunFilePath, run.runNumber, "", "log");
1259 if (!OpenStream(run.logFile, run.logName))
1260 return -1;
1261#endif
1262
1263 // open report file
1264 run.reportName = CompileFileNameWithPath(fRunFilePath, run.runNumber, "", "rep");
1265 if (!OpenStream(run.reportFile, run.reportName))
1266 return -1;
1267
1268 //get the size of the newly opened file.
1269#ifdef RUN_LOGS
1270 RememberFileOrigSizePlease(run.logName, false);
1271#endif
1272 RememberFileOrigSizePlease(run.reportName, false);
1273
1274 //TODO this notification scheme might be messed up now.... fix it !
1275 const string baseFileName = CompileFileNameWithPath(fRunFilePath, run.runNumber, "", "");
1276 NotifyOpenedFile(baseFileName, 3, fOpenedRunFiles);
1277 run.openedFits.clear();
1278 return 0;
1279}
1280// --------------------------------------------------------------------------
1281//
1282//! Add a new active run number
1283//! @param newRun the new run number
1284//! @param time the time at which the new run number was issued
1285//
1286void DataLogger::AddNewRunNumber(int64_t newRun, Time time)
1287{
1288
1289 if (newRun > 0xffffffff)
1290 {
1291 Error("New run number too large, out of range. Ignoring.");
1292 return;
1293 }
1294 if (fDebugIsOn)
1295 {
1296 ostringstream str;
1297 str << "Adding new run number " << newRun << " that was issued on " << time;
1298 Debug(str);
1299 }
1300 //Add new run number to run number list
1301 fRunNumber.push_back(RunNumberType());
1302 fRunNumber.back().runNumber = uint32_t(newRun);
1303 fRunNumber.back().time = time;
1304
1305 ostringstream str;
1306 str << "The new run number is: " << fRunNumber.back().runNumber;
1307 Message(str);
1308
1309 if (GetCurrentState() != kSM_Logging)
1310 return;
1311 //open the log and report files
1312 if (OpenRunFile(fRunNumber.back()) != 0)
1313 {//an error occured. close current run files and go to error state
1314 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
1315 {
1316 if (it->reportFile->is_open())
1317 it->reportFile->close();
1318#ifdef RUN_LOGS
1319 if (it->logFile->is_open())
1320 it->logFile->close();
1321#endif
1322 }
1323 StopRunPlease();
1324 SetCurrentState(kSM_BadRunConfig);
1325 }
1326}
1327// --------------------------------------------------------------------------
1328//
1329//! Checks whether or not the current info is a run number.
1330//! If so, then remember it. A run number is required to open the run-log file
1331//! @param I
1332//! the current DimInfo
1333//
1334void DataLogger::CheckForRunNumber(DimInfo* I)
1335{
1336 if (strstr(I->getName(), "SET_RUN_NUMBER") != NULL)
1337 {//assumes that the run number is an integer
1338 //check if some run number entries can be deleted leave one so that two remain after adding the new one
1339 AddNewRunNumber(I->getLonglong(), Time(I->getTimestamp(), I->getTimestampMillisecs()*1000));
1340 }
1341}
1342
1343// --------------------------------------------------------------------------
1344//
1345//! write infos to log files.
1346//! @param I
1347//! The current DimInfo
1348//! @param sub
1349//! The dataLogger's subscription corresponding to this DimInfo
1350//
1351void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
1352{
1353 const string fmt(I->getFormat());
1354
1355 const bool isItaReport = fmt!="C";
1356
1357 if (!fNightlyReportFile.is_open())
1358 return;
1359
1360 if (fDebugIsOn && string(I->getName())!="DATA_LOGGER/MESSAGE")
1361 {
1362 ostringstream str;
1363 str << "Logging " << I->getName() << " [" << I->getFormat() << "] (" << I->getSize() << ")";
1364 Debug(str);
1365 }
1366
1367 //Check whether we should close and reopen daily text files or not
1368 //This should work in any case base of the following:
1369 // - fDailyFileDayChangedAlready is initialized to true. So if the dataLogger is started around noon, no file will be closed
1370 // - fDailyFileDayChangedAlready is set to false if (time != 12), so the file will be closed and reopened only if the logger runs since before noon (which is the required behavior)
1371 //This only applies to text files. Fits are closed and reopened based on the last and current service received time.
1372 //this was not applicable to text files, because as they gather several services, we have no guarantee that the received time will be greater than the previous one,
1373 //which could lead to several close/reopen instead of only one.
1374 if (Time().h() == 12 && !fDailyFileDayChangedAlready)
1375 {
1376 if (fDebugIsOn)
1377 Debug("Its Noon! Closing and reopening nightly text files");
1378
1379 fNightlyLogFile.close();
1380 fNightlyReportFile.close();
1381
1382 Info("Closed: "+fFullNightlyLogFileName);
1383 Info("Closed: "+fFullNightlyReportFileName);
1384
1385 fFullNightlyLogFileName = CompileFileNameWithPath(fNightlyFilePath, "", "log");
1386 if (!OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName))
1387 {
1388 GoToReadyPlease();
1389 SetCurrentState(kSM_BadNightlyConfig);
1390 return;
1391 }
1392
1393 fFullNightlyReportFileName = CompileFileNameWithPath(fNightlyFilePath, "", "rep");
1394 if (!OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName))
1395 {
1396 GoToReadyPlease();
1397 SetCurrentState(kSM_BadNightlyConfig);
1398 return;
1399 }
1400
1401 fDailyFileDayChangedAlready = true;
1402 }
1403 if (Time().h() != 12 && fDailyFileDayChangedAlready)
1404 fDailyFileDayChangedAlready = false;
1405
1406 //create the converter for that service
1407 if ((!sub.fConv.get()) && isItaReport)
1408 {
1409 sub.fConv = shared_ptr<Converter>(new Converter(Out(), I->getFormat()));
1410 if (!sub.fConv)
1411 {
1412 ostringstream str;
1413 str << "Couldn't properly parse the format... service " << sub.dimInfo->getName() << " ignored.";
1414 Error(str);
1415 return;
1416 }
1417 }
1418 //construct the header
1419 ostringstream header;
1420 const Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
1421 fQuality = I->getQuality();
1422 fMjD = cTime.Mjd();
1423
1424 //figure out which run file should be used
1425 ofstream* targetRunFile = NULL;
1426 RunNumberType* cRunNumber = NULL;
1427 if (GetCurrentState() == kSM_Logging)
1428 {
1429 list<RunNumberType>::reverse_iterator rit;
1430 for (rit=fRunNumber.rbegin(); rit!=fRunNumber.rend(); rit++)
1431 {
1432 if (rit->time < cTime) //this is the run number that we want to use
1433 {
1434 //Find something better to convert iterator to pointer than the ugly line below....
1435 cRunNumber = &(*rit);
1436 sub.runNumber = rit->runNumber;
1437#ifdef RUN_LOGS
1438 targetRunFile = isItaReport ? (rit->reportFile).get() : (rit->logFile).get();
1439#else
1440 targetRunFile = isItaReport ? (rit->reportFile).get() : NULL;
1441#endif
1442 break;
1443 }
1444 }
1445 if (rit == fRunNumber.rend() && fRunNumber.size() != 0)
1446 {
1447 ostringstream str;
1448 str << "Could not find an appropriate run number for info coming at time: " << cTime;
1449 Error(str);
1450 Error("Active run numbers: ");
1451 for (rit=fRunNumber.rbegin(); rit != fRunNumber.rend(); rit++)
1452 {
1453 str.str("");
1454 str << rit->runNumber;
1455 Error(str);
1456 }
1457
1458 }
1459 }
1460
1461 if (isItaReport)
1462 {
1463 //write text header
1464 header << I->getName() << " " << fQuality << " ";
1465 header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1466 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1467 header << cTime.ms() << " " << I->getTimestamp() << " ";
1468
1469 string text;
1470 try
1471 {
1472 text = sub.fConv->GetString(I->getData(), I->getSize());
1473 }
1474 catch (const runtime_error &e)
1475 {
1476 ostringstream str;
1477 str << "Parsing service " << sub.dimInfo->getName();
1478 str << " failed: " << e.what();
1479 Error(str);
1480 return;
1481 }
1482
1483 if (text.empty())
1484 {
1485 ostringstream str;
1486 str << "Service " << sub.dimInfo->getName() << " sent an empty string";
1487 Info(str);
1488 return;
1489 }
1490 //replace bizarre characters by white space
1491 replace(text.begin(), text.end(), '\n', '\\');
1492 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1493
1494 //write entry to Nightly report
1495 if (fNightlyReportFile.is_open())
1496 {
1497 fNightlyReportFile << header.str() << text << endl;
1498 if (!CheckForOfstreamError(fNightlyReportFile))
1499 return;
1500 }
1501 //write entry to run-report
1502 if (targetRunFile && targetRunFile->is_open())
1503 {
1504 *targetRunFile << header.str() << text << endl;
1505 if (!CheckForOfstreamError(*targetRunFile))
1506 return;
1507 }
1508 }
1509 else
1510 {//write entry to both Nightly and run logs
1511 ostringstream msg;
1512 msg << I->getName() << ": " << I->getString();
1513
1514 if (fNightlyLogFile.is_open())
1515 {
1516 MessageImp(fNightlyLogFile).Write(cTime, msg.str().c_str(), fQuality);
1517 if (!CheckForOfstreamError(fNightlyLogFile))
1518 return;
1519 }
1520 if (targetRunFile && targetRunFile->is_open())
1521 {
1522 MessageImp(*targetRunFile).Write(cTime, msg.str().c_str(), fQuality);
1523 if (!CheckForOfstreamError(*targetRunFile))
1524 return;
1525 }
1526 }
1527
1528#ifdef HAVE_FITS
1529 if (isItaReport)
1530 {
1531 //check if the last received event was before noon and if current one is after noon.
1532 //if so, close the file so that it gets reopened.
1533 if (sub.nightlyFile.IsOpen())
1534 if ((sub.lastReceivedEvent != Time::None) && (sub.lastReceivedEvent.h() < 12) && (cTime.h() >= 12))
1535 {
1536 sub.nightlyFile.Close();
1537 }
1538 sub.lastReceivedEvent = cTime;
1539 if (!sub.nightlyFile.IsOpen() || !sub.runFile.IsOpen() || sub.runNumber != sub.runFile.fRunNumber)
1540 OpenFITSFilesPlease(sub, cRunNumber);
1541 WriteToFITS(sub);
1542 }
1543#endif
1544
1545}
1546
1547// --------------------------------------------------------------------------
1548//
1549//! print the dataLogger's current state. invoked by the PRINT command
1550//! @param evt
1551//! the current event. Not used by the method
1552//! @returns
1553//! the new state. Which, in that case, is the current state
1554//!
1555int DataLogger::PrintStatePlease(const Event& )
1556{
1557 Message("------------------------------------------");
1558 Message("------- DATA LOGGER CURRENT STATE --------");
1559 Message("------------------------------------------");
1560
1561 //print the path configuration
1562 Message("Nightly Path: " + boost::filesystem::system_complete(boost::filesystem::path(fNightlyFilePath)).directory_string());
1563 Message("Run Path: " + boost::filesystem::system_complete(boost::filesystem::path(fRunFilePath)).directory_string());
1564
1565 //print active run numbers
1566 ostringstream str;
1567 str << "Active Run Numbers:";
1568 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1569 str << " " << it->runNumber;
1570 if (fRunNumber.size()==0)
1571 str << " <none>";
1572 Message(str);
1573 //timeout value
1574 str.str("");
1575 str << "Timeout delay for old run numbers: " << fRunNumberTimeout << " seconds";
1576 Message(str);
1577
1578 //print all the open files.
1579 Message("------------ OPENED FILES ----------------");
1580 if (fNightlyLogFile.is_open())
1581 Message("Nightly log-file: OPEN");
1582
1583 if (fNightlyReportFile.is_open())
1584 Message("Nightly report-file: OPEN");
1585
1586 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1587 {
1588#ifdef RUN_LOGS
1589 if (it->logFile->is_open())
1590 Message("Run log-file: " + it->logName + " (OPEN)");
1591#endif
1592 if (it->reportFile->is_open())
1593 Message("Run report-file: " + it->reportName + " (OPEN)");
1594 }
1595
1596 DataLoggerStats statVar;
1597 /*const bool statWarning =*/ calculateTotalSizeWritten(statVar, true);
1598
1599 Message("----------------- STATS ------------------");
1600 str.str("");
1601 str << "Total Size written: " << statVar.sizeWritten << " bytes.";
1602 Message(str);
1603 str.str("");
1604 str << "Disk free space: " << statVar.freeSpace << " bytes.";
1605 Message(str);
1606 str.str("");
1607 str << "Statistics are updated every " << fStatsPeriodDuration << " seconds";
1608 if (fStatsPeriodDuration != 0)
1609 Message(str);
1610 else
1611 Message("Statistics updates are currently disabled");
1612
1613 Message("------------ DIM SUBSCRIPTIONS -----------");
1614 str.str("");
1615 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions.";
1616 Message(str);
1617 for (map<const string, map<string, SubscriptionType> >::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1618 {
1619 Message("Server "+it->first);
1620 for (map<string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1621 Message(" -> "+it2->first);
1622 }
1623 Message("--------------- BLOCK LIST ---------------");
1624 for (set<string>::const_iterator it=fBlackList.begin(); it != fBlackList.end(); it++)
1625 Message(" -> "+*it);
1626 if (fBlackList.size()==0)
1627 Message(" <empty>");
1628
1629 Message("--------------- ALLOW LIST ---------------");
1630 for (set<string>::const_iterator it=fWhiteList.begin(); it != fWhiteList.end(); it++)
1631 Message(" -> "+*it);
1632 if (fWhiteList.size()==0)
1633 Message(" <empty>");
1634
1635 Message("-------------- GROUPING LIST -------------");
1636 Message("The following servers and/or services will");
1637 Message("be grouped into a single fits file:");
1638 for (set<string>::const_iterator it=fGrouping.begin(); it != fGrouping.end(); it++)
1639 Message(" -> "+*it);
1640 if (fGrouping.size()==0)
1641 Message(" <no grouping>");
1642
1643 Message("------------------------------------------");
1644 Message("-------- END OF DATA LOGGER STATE --------");
1645 Message("------------------------------------------");
1646
1647 return GetCurrentState();
1648}
1649
1650// --------------------------------------------------------------------------
1651//
1652//! turn debug mode on and off
1653//! @param evt
1654//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1655//! @returns
1656//! the new state. Which, in that case, is the current state
1657//!
1658int DataLogger::SetDebugOnOff(const Event& evt)
1659{
1660 const bool backupDebug = fDebugIsOn;
1661
1662 fDebugIsOn = evt.GetBool();
1663
1664 if (fDebugIsOn == backupDebug)
1665 Message("Debug mode was already in the requested state.");
1666
1667 ostringstream str;
1668 str << "Debug mode is now " << fDebugIsOn;
1669 Message(str);
1670
1671 return GetCurrentState();
1672}
1673// --------------------------------------------------------------------------
1674//
1675//! set the statistics update period duration. 0 disables the statistics
1676//! @param evt
1677//! the current event. contains the new duration.
1678//! @returns
1679//! the new state. Which, in that case, is the current state
1680//!
1681int DataLogger::SetStatsPeriod(const Event& evt)
1682{
1683 const float backupDuration = fStatsPeriodDuration;
1684
1685 fStatsPeriodDuration = evt.GetFloat();
1686
1687 if (fStatsPeriodDuration < 0)
1688 {
1689 Error("Statistics period duration should be greater than zero. Discarding provided value.");
1690 fStatsPeriodDuration = backupDuration;
1691 return GetCurrentState();
1692 }
1693 if (!finite(fStatsPeriodDuration))// != fStatsPeriodDuration)
1694 {
1695 Error("Provided duration does not appear to be a valid float. Discarding it.");
1696 fStatsPeriodDuration = backupDuration;
1697 return GetCurrentState();
1698 }
1699 if (backupDuration == fStatsPeriodDuration)
1700 Warn("Statistics period not modified. Supplied value already in use.");
1701
1702 if (fStatsPeriodDuration == 0.0f)
1703 Message("Statistics are now OFF");
1704 else
1705 {
1706 ostringstream str;
1707 str << "Statistics period is now " << fStatsPeriodDuration << " seconds";
1708 Message(str);
1709 }
1710
1711 return GetCurrentState();
1712}
1713// --------------------------------------------------------------------------
1714//
1715//! set the opened files service on or off.
1716//! @param evt
1717//! the current event. contains the instruction string. similar to setdebugonoff
1718//! @returns
1719//! the new state. Which, in that case, is the current state
1720//!
1721int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1722{
1723 const bool backupOpened = fOpenedFilesIsOn;
1724
1725 fOpenedFilesIsOn = evt.GetBool();
1726
1727 if (fOpenedFilesIsOn == backupOpened)
1728 Message("Opened files service mode was already in the requested state.");
1729
1730 ostringstream str;
1731 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1732 Message(str);
1733
1734 return GetCurrentState();
1735}
1736
1737// --------------------------------------------------------------------------
1738//
1739//! set the number of subscriptions and opened fits on and off
1740//! @param evt
1741//! the current event. contains the instruction string. similar to setdebugonoff
1742//! @returns
1743//! the new state. Which, in that case, is the current state
1744//!
1745int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1746{
1747 const bool backupSubs = fNumSubAndFitsIsOn;
1748
1749 fNumSubAndFitsIsOn = evt.GetBool();
1750
1751 if (fNumSubAndFitsIsOn == backupSubs)
1752 Message("Number of subscriptions service mode was already in the requested state");
1753
1754 ostringstream str;
1755 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1756 Message(str);
1757
1758 return GetCurrentState();
1759}
1760// --------------------------------------------------------------------------
1761//
1762//! set the timeout delay for old run numbers
1763//! @param evt
1764//! the current event. contains the timeout delay long value
1765//! @returns
1766//! the new state. Which, in that case, is the current state
1767//!
1768int DataLogger::SetRunTimeoutDelay(const Event& evt)
1769{
1770 const long backupTimeout = fRunNumberTimeout;
1771 fRunNumberTimeout = evt.GetXtra();
1772
1773 if (fRunNumberTimeout == 0)
1774 {
1775 fRunNumberTimeout = backupTimeout;
1776 Error("Timeout delays for old run numbers must be greater than 0. Ignored.");
1777 return GetCurrentState();
1778 }
1779
1780 if (fRunNumberTimeout == backupTimeout)
1781 Message("New timeout for old run numbers is same value as previous one.");
1782
1783 ostringstream str;
1784 str << "Timeout delay for old run numbers is now " << fRunNumberTimeout << " seconds";
1785 Message(str);
1786
1787 return GetCurrentState();
1788}
1789
1790int DataLogger::ConfigureFileName(string &target, const string &type, const EventImp &evt)
1791{
1792 if (!evt.GetText())
1793 {
1794 Error("Empty "+type+" folder given. Please specify a valid path.");
1795 return GetCurrentState();
1796 }
1797
1798 const string givenPath = evt.GetText();
1799 if (!DoesPathExist(givenPath))
1800 {
1801 Error("Provided "+type+" path '"+givenPath+"' is not a valid folder. Ignored");
1802 return GetCurrentState();
1803 }
1804
1805 Message("New "+type+" folder: "+givenPath);
1806
1807 target = givenPath;
1808
1809 return GetCurrentState();
1810}
1811
1812// --------------------------------------------------------------------------
1813//
1814//! Sets the path to use for the Nightly log file.
1815//! @param evt
1816//! the event transporting the path
1817//! @returns
1818//! currently only the current state.
1819//
1820int DataLogger::ConfigureNightlyFileName(const Event& evt)
1821{
1822 return ConfigureFileName(fNightlyFilePath, "nightly", evt);
1823}
1824
1825// --------------------------------------------------------------------------
1826//
1827//! Sets the path to use for the run log file.
1828//! @param evt
1829//! the event transporting the path
1830//! @returns
1831//! currently only the current state
1832int DataLogger::ConfigureRunFileName(const Event& evt)
1833{
1834 return ConfigureFileName(fRunFilePath, "run", evt);
1835}
1836
1837// --------------------------------------------------------------------------
1838//
1839//! Sets the run number.
1840//! @param evt
1841//! the event transporting the run number
1842//! @returns
1843//! currently only the current state
1844int DataLogger::ConfigureRunNumber(const Event& evt)
1845{
1846 AddNewRunNumber(evt.GetXtra(), evt.GetTime());
1847 return GetCurrentState();
1848}
1849// --------------------------------------------------------------------------
1850//
1851//! Notifies the DIM service that a particular file was opened
1852//! @ param name the base name of the opened file, i.e. without path nor extension.
1853//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1854//! this is not a problem though because file are not opened so often.
1855//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1856inline void DataLogger::NotifyOpenedFile(const string &name, int type, DimDescribedService* service)
1857{
1858 if (!fOpenedFilesIsOn)
1859 return;
1860
1861 if (fDebugIsOn)
1862 {
1863 ostringstream str;
1864 str << "Updating " << service->getName() << " file '" << name << "' (type=" << type << ")";
1865 Debug(str);
1866
1867 str.str("");
1868 str << "Num subscriptions: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS files: " << fNumSubAndFitsData.numOpenFits;
1869 Debug(str);
1870 }
1871
1872 if (name.size()+1 > FILENAME_MAX)
1873 {
1874 Error("Provided file name '" + name + "' is longer than allowed file name length.");
1875 return;
1876 }
1877
1878 OpenFileToDim fToDim;
1879 fToDim.code = type;
1880 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1881
1882 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(int));
1883 service->setQuality(0);
1884 service->updateService();
1885}
1886// --------------------------------------------------------------------------
1887//
1888//! Implements the Start transition.
1889//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1890//! and tries to open it.
1891//! @returns
1892//! kSM_NightlyOpen if success, kSM_BadNightlyConfig if failure
1893int DataLogger::StartPlease()
1894{
1895 if (fDebugIsOn)
1896 {
1897 Debug("Starting...");
1898 }
1899 fFullNightlyLogFileName = CompileFileNameWithPath(fNightlyFilePath, "", "log");
1900 if (!OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName))
1901 return kSM_BadNightlyConfig;
1902
1903
1904 fFullNightlyReportFileName = CompileFileNameWithPath(fNightlyFilePath, "", "rep");
1905 if (!OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName))
1906 {
1907 fNightlyLogFile.close();
1908 return kSM_BadNightlyConfig;
1909 }
1910 //get the size of the newly opened file.
1911 fBaseSizeNightly = GetFileSize(fFullNightlyLogFileName);
1912 fBaseSizeNightly += GetFileSize(fFullNightlyReportFileName);
1913 fFileSizesMap.clear();
1914 fBaseSizeRun = 0;
1915 fPreviousSize = 0;
1916
1917 //notify that a new file has been opened.
1918 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
1919 NotifyOpenedFile(baseFileName, 3, fOpenedNightlyFiles);
1920
1921 fOpenedNightlyFits.clear();
1922
1923 return kSM_NightlyOpen;
1924}
1925
1926#ifdef HAVE_FITS
1927// --------------------------------------------------------------------------
1928//
1929//! open if required a the FITS files corresponding to a given subscription
1930//! @param sub
1931//! the current DimInfo subscription being examined
1932void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber)
1933{
1934 string serviceName(sub.dimInfo->getName());
1935
1936 //if run number has changed, reopen a new fits file with the correct run number.
1937 if (sub.runFile.IsOpen() && sub.runFile.fRunNumber != sub.runNumber)
1938 {
1939 sub.runFile.Close();
1940 Info("Closed: "+sub.runFile.fFileName+" (new run number)");
1941 }
1942
1943 //we must check if we should group this service subscription to a single fits file before we replace the / by _
1944 bool hasGrouping = false;
1945 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1946 {//will we find this service in the grouping list ?
1947 for (set<string>::const_iterator it=fGrouping.begin(); it!=fGrouping.end(); it++)
1948 {
1949 if (serviceName.find(*it) != string::npos)
1950 {
1951 hasGrouping = true;
1952 break;
1953 }
1954 }
1955 }
1956 for (unsigned int i=0;i<serviceName.size(); i++)
1957 {
1958 if (serviceName[i] == '/')
1959 {
1960 serviceName[i] = '_';
1961 break;
1962 }
1963 }
1964 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1965 if (!sub.nightlyFile.IsOpen())
1966 {
1967 string partialName = CompileFileNameWithPath(fNightlyFilePath, serviceName, "fits");
1968
1969 const string fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1970 if (!sub.fitsBufferAllocated)
1971 AllocateFITSBuffers(sub);
1972 //get the size of the file we're about to open
1973 if (RememberFileOrigSizePlease(partialName, true))//and remember that the file was opened (i.e. not an update)
1974 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
1975
1976 ostringstream str;
1977 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
1978 Info(str);
1979
1980 if (!sub.nightlyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, 0))
1981 {
1982 GoToWriteErrorState();
1983 //SetCurrentState(kSM_WriteError);
1984 return;
1985 }
1986 //notify the opening
1987 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
1988 NotifyOpenedFile(baseFileName, 7, fOpenedNightlyFiles);
1989 if (fNumSubAndFitsIsOn)
1990 fNumSubAndFits->updateService();
1991 }
1992 //do the actual file open
1993 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging) && sub.runNumber != 0)
1994 {//buffer for the run file have already been allocated when doing the Nightly file
1995 string fileNameOnly;
1996 string partialName;
1997 if (hasGrouping)
1998 {
1999 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "fits");
2000 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
2001 }
2002 else
2003 {
2004 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, serviceName, "fits");
2005 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
2006 }
2007 //get the size of the file we're about to open
2008 if (RememberFileOrigSizePlease(partialName, false))//and remember that the file was opened (i.e. not an update)
2009 cRunNumber->openedFits[fileNameOnly].push_back(serviceName);
2010 else
2011 if (hasGrouping)
2012 {
2013 cRunNumber->addServiceToOpenedFits(fileNameOnly, serviceName);
2014 }
2015
2016 if (hasGrouping && (!cRunNumber->runFitsFile.get()))
2017 try
2018 {
2019 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>(new CCfits::FITS(partialName, CCfits::RWmode::Write));
2020 (fNumSubAndFitsData.numOpenFits)++;
2021 }
2022 catch (CCfits::FitsException e)
2023 {
2024 ostringstream str;
2025 str << "Open FITS file " << partialName << ": " << e.message();
2026 Error(str);
2027 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>();//NULL;
2028 GoToWriteErrorState();
2029 //SetCurrentState(kSM_WriteError);
2030 return;
2031 }
2032
2033 const string baseFileName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "");
2034 NotifyOpenedFile(baseFileName, 7, fOpenedRunFiles);// + '_' + serviceName, 4);
2035
2036 ostringstream str;
2037 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
2038 Info(str);
2039
2040 if (hasGrouping)
2041 {
2042 if (!sub.runFile.Open(partialName, serviceName, (cRunNumber->runFitsFile).get(), &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2043 {
2044 GoToWriteErrorState();
2045 //SetCurrentState(kSM_WriteError);
2046 return;
2047 }
2048 }
2049 else
2050 {
2051 if (sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2052 {
2053 GoToWriteErrorState();
2054 //SetCurrentState(kSM_WriteError);
2055 return;
2056 }
2057 }
2058 if (fNumSubAndFitsIsOn)
2059 fNumSubAndFits->updateService();
2060 }
2061}
2062// --------------------------------------------------------------------------
2063//
2064//! Allocates the required memory for a given pair of fits files (nightly and run)
2065//! @param sub the subscription of interest.
2066//
2067void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
2068{
2069 //Init the time columns of the file
2070 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MjD"));
2071 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2072 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2073
2074 Description QoSDesc("Qos", "Quality of service", "None");
2075 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2076 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2077
2078 const Converter::FormatList flist = sub.fConv->GetList();
2079 // Compilation failed
2080 if (!sub.fConv->valid())
2081 {
2082 Error("Compilation of format string failed.");
2083 return;
2084 }
2085
2086 //we've got a nice structure describing the format of this service's messages.
2087 //Let's create the appropriate FITS columns
2088 int size = 0;
2089
2090 vector<string> dataFormatsLocal;
2091 for (unsigned int i=0;i<flist.size()-1;i++)
2092 {
2093 ostringstream dataQualifier;
2094
2095 dataQualifier << flist[i].second.first;
2096 switch (flist[i].first.first->name()[0])
2097 {
2098 case 'c': dataQualifier << "B"; break; // FIXME: To be checked!
2099 case 's': dataQualifier << "I"; break;
2100 case 'i': dataQualifier << "J"; break;
2101 case 'l': dataQualifier << "J"; break;
2102 case 'f': dataQualifier << "E"; break;
2103 case 'd': dataQualifier << "D"; break;
2104 case 'x': dataQualifier << "K"; break;
2105 case 'S': //we skip the variable length strings
2106 continue;
2107
2108 default:
2109 Fatal("THIS SHOULD NEVER BE REACHED.");
2110 };
2111
2112 size += flist[i].first.second * flist[i].second.first;
2113 dataFormatsLocal.push_back(dataQualifier.str());
2114 }
2115 sub.nightlyFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2116 sub.runFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2117 sub.fitsBufferAllocated = true;
2118}
2119// --------------------------------------------------------------------------
2120//
2121//! write a dimInfo data to its corresponding FITS files
2122//
2123void DataLogger::WriteToFITS(SubscriptionType& sub)
2124{
2125 //nightly File status (open or not) already checked
2126 if (sub.nightlyFile.IsOpen())
2127 {
2128 if (!sub.nightlyFile.Write(sub.fConv.get()))
2129 {
2130 GoToWriteErrorState();
2131 return;
2132 //SetCurrentState(kSM_WriteError);
2133 }
2134 }
2135
2136 if (sub.runFile.IsOpen())
2137 {
2138 if (!sub.runFile.Write(sub.fConv.get()))
2139 {
2140 GoToWriteErrorState();
2141 return;
2142 //SetCurrentState(kSM_WriteError);
2143 }
2144 }
2145}
2146#endif //if has_fits
2147
2148void DataLogger::GoToWriteErrorState()
2149{
2150 GoToReadyPlease();
2151 SetCurrentState(kSM_WriteError);
2152}
2153
2154std::string DataLogger::SetCurrentState(int state, const char *txt, const std::string &cmd)
2155{
2156// if (state == kSM_WriteError && GetCurrentState() == kSM_WriteError)
2157// return "";
2158 return StateMachineImp::SetCurrentState(state, txt, cmd);
2159}
2160// --------------------------------------------------------------------------
2161//
2162//! Implements the StartRun transition.
2163//! Concatenates the given path for the run file and the filename itself (based on the run number),
2164//! and tries to open it.
2165//! @returns
2166//! kSM_Logging if success, kSM_BadRunConfig if failure.
2167int DataLogger::StartRunPlease()
2168{
2169 if (fDebugIsOn)
2170 {
2171 Debug("Starting Run Logging...");
2172 }
2173 //open all the relevant run-files. i.e. all the files associated with run numbers.
2174 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2175 if (OpenRunFile(*it) != 0)
2176 {
2177 StopRunPlease();
2178 return kSM_BadRunConfig;
2179 }
2180
2181 return kSM_Logging;
2182}
2183
2184#ifdef HAVE_FITS
2185// --------------------------------------------------------------------------
2186//
2187//! Create a fits group file with all the run-fits that were written (either daily or run)
2188//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
2189//! single file can contain several tables to group
2190//! @param runNumber the run number that should be used for grouping. 0 means nightly group
2191//
2192void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup, int runNumber)
2193{
2194 if (fDebugIsOn)
2195 {
2196 ostringstream str;
2197 str << "Creating fits group for ";
2198 if (runNumber != 0)
2199 str << "run files";
2200 else
2201 str << "nightly files";
2202 Debug(str);
2203 }
2204 //create the FITS group corresponding to the ending run.
2205 CCfits::FITS* groupFile;
2206 unsigned int numFilesToGroup = 0;
2207 unsigned int maxCharLength = 0;
2208 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
2209 {
2210 //add the number of tables in this file to the total number to group
2211 numFilesToGroup += it->second.size();
2212 //check the length of all the strings to be written, to determine the max string length to write
2213 if (it->first.size() > maxCharLength)
2214 maxCharLength = it->first.size();
2215 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++)
2216 if (jt->size() > maxCharLength)
2217 maxCharLength = jt->size();
2218 }
2219
2220 if (fDebugIsOn)
2221 {
2222 ostringstream str;
2223 str << "There are " << numFilesToGroup << " tables to group";
2224 Debug(str);
2225 }
2226 if (numFilesToGroup <= 1)
2227 {
2228 filesToGroup.clear();
2229 return;
2230 }
2231 string groupName;
2232 if (runNumber != 0)
2233 groupName = CompileFileNameWithPath(fRunFilePath, runNumber, "", "fits");
2234 else
2235 groupName = CompileFileNameWithPath(fNightlyFilePath, "", "fits");
2236
2237 Info("Creating FITS group in: "+groupName);
2238
2239 CCfits::Table* groupTable;
2240// const int maxCharLength = FILENAME_MAX;
2241 try
2242 {
2243 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
2244 //setup the column names
2245 ostringstream pathTypeName;
2246 pathTypeName << maxCharLength << "A";
2247 vector<string> names;
2248 vector<string> dataTypes;
2249 names.push_back("MEMBER_XTENSION");
2250 dataTypes.push_back("8A");
2251 names.push_back("MEMBER_URI_TYPE");
2252 dataTypes.push_back("3A");
2253 names.push_back("MEMBER_LOCATION");
2254 dataTypes.push_back(pathTypeName.str());
2255 names.push_back("MEMBER_NAME");
2256 dataTypes.push_back(pathTypeName.str());
2257
2258 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
2259//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
2260 }
2261 catch (CCfits::FitsException e)
2262 {
2263 ostringstream str;
2264 str << "Creating FITS table GROUPING in " << groupName << ": " << e.message();
2265 Error(str);
2266 return;
2267 }
2268
2269 //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.
2270 //use cfitsio routines instead
2271 groupTable->makeThisCurrent();
2272 //create appropriate buffer.
2273 const unsigned int n = 8 + 3 + 2*maxCharLength + 1; //+1 for trailling character
2274
2275 vector<unsigned char> realBuffer;
2276 realBuffer.resize(n);
2277 unsigned char* fitsBuffer = &realBuffer[0];
2278 memset(fitsBuffer, 0, n);
2279
2280 char* startOfExtension = reinterpret_cast<char*>(fitsBuffer);
2281 char* startOfURI = reinterpret_cast<char*>(&fitsBuffer[8]);
2282 char* startOfLocation = reinterpret_cast<char*>(&fitsBuffer[8 + 3]);
2283 char* startOfName = reinterpret_cast<char*>(&fitsBuffer[8+3+maxCharLength]);
2284
2285 strcpy(startOfExtension, "BINTABLE");
2286 strcpy(startOfURI, "URL");
2287
2288 int i=1;
2289 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
2290 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
2291 {
2292 strcpy(startOfLocation, it->first.c_str());
2293 strcpy(startOfName, jt->c_str());
2294
2295 if (fDebugIsOn)
2296 {
2297 ostringstream str;
2298 str << "Grouping " << it->first << " " << *jt;
2299 Debug(str);
2300 }
2301
2302 int status = 0;
2303 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength, fitsBuffer, &status);
2304 if (status)
2305 {
2306 char text[30];//max length of cfitsio error strings (from doc)
2307 fits_get_errstatus(status, text);
2308 ostringstream str;
2309 str << "Writing FITS row " << i << " in " << groupName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
2310 Error(str);
2311 GoToWriteErrorState();
2312 return;
2313 }
2314 }
2315
2316 filesToGroup.clear();
2317 delete groupFile;
2318}
2319#endif //HAVE_FITS
2320
2321// --------------------------------------------------------------------------
2322//
2323//! Implements the StopRun transition.
2324//! Attempts to close the run file.
2325//! @returns
2326//! kSM_WaitingRun if success, kSM_FatalError otherwise
2327int DataLogger::StopRunPlease()
2328{
2329
2330 if (fDebugIsOn)
2331 {
2332 Debug("Stopping Run Logging...");
2333 }
2334 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2335 {
2336#ifdef RUN_LOGS
2337 if (!it->logFile->is_open() || !it->reportFile->is_open())
2338#else
2339 if (!it->reportFile->is_open())
2340#endif
2341 return kSM_FatalError;
2342#ifdef RUN_LOGS
2343 it->logFile->close();
2344#endif
2345 it->reportFile->close();
2346 }
2347
2348#ifdef HAVE_FITS
2349 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2350 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2351 {
2352 if (j->second.runFile.IsOpen())
2353 j->second.runFile.Close();
2354 }
2355#endif
2356 NotifyOpenedFile("", 0, fOpenedRunFiles);
2357 if (fNumSubAndFitsIsOn)
2358 fNumSubAndFits->updateService();
2359
2360 while (fRunNumber.size() > 0)
2361 {
2362 RemoveOldestRunNumber();
2363 }
2364
2365 return kSM_WaitingRun;
2366}
2367// --------------------------------------------------------------------------
2368//
2369//! Implements the Stop and Reset transitions.
2370//! Attempts to close any openned file.
2371//! @returns
2372//! kSM_Ready
2373int DataLogger::GoToReadyPlease()
2374{
2375 if (fDebugIsOn)
2376 {
2377 Debug("Going to the Ready state...");
2378 }
2379 if (GetCurrentState() == kSM_Logging)
2380 StopRunPlease();
2381
2382 if (fNightlyLogFile.is_open())
2383 fNightlyLogFile.close();
2384 if (fNightlyReportFile.is_open())
2385 fNightlyReportFile.close();
2386
2387#ifdef HAVE_FITS
2388 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2389 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2390 {
2391 if (j->second.nightlyFile.IsOpen())
2392 j->second.nightlyFile.Close();;
2393 }
2394#endif
2395 if (GetCurrentState() == kSM_Logging ||
2396 GetCurrentState() == kSM_WaitingRun ||
2397 GetCurrentState() == kSM_NightlyOpen)
2398 {
2399 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2400 if (fNumSubAndFitsIsOn)
2401 fNumSubAndFits->updateService();
2402 }
2403#ifdef HAVE_FITS
2404 CreateFitsGrouping(fOpenedNightlyFits, 0);
2405#endif
2406 return kSM_Ready;
2407}
2408
2409// --------------------------------------------------------------------------
2410//
2411//! Implements the transition towards kSM_WaitingRun
2412//! Does nothing really.
2413//! @returns
2414//! kSM_WaitingRun
2415int DataLogger::NightlyToWaitRunPlease()
2416{
2417 if (fDebugIsOn)
2418 {
2419 Debug("Going to Wait Run Number state...");
2420 }
2421 return kSM_WaitingRun;
2422}
2423// --------------------------------------------------------------------------
2424//
2425//! Setup Logger's configuration from a Configuration object
2426//! @param conf the configuration object that should be used
2427//!
2428bool DataLogger::SetConfiguration(Configuration& conf)
2429{
2430 fDebugIsOn = conf.Get<bool>("debug");
2431
2432 //Set the block or allow list
2433 fBlackList.clear();
2434 fWhiteList.clear();
2435
2436 //Adding entries that should ALWAYS be ignored
2437 //fBlackList.insert("DATA_LOGGER/");
2438 fBlackList.insert("/SERVICE_LIST");
2439 fBlackList.insert("DIS_DNS/");
2440
2441 //set the black list
2442 if (conf.Has("block"))
2443 {
2444 const vector<string> vec = conf.Get<vector<string>>("block");
2445
2446 fBlackList.insert(vec.begin(), vec.end());
2447 }
2448
2449 //set the white list
2450 if (conf.Has("allow"))
2451 {
2452 const vector<string> vec = conf.Get<vector<string>>("allow");
2453 fWhiteList.insert(vec.begin(), vec.end());
2454 }
2455
2456 //Set the grouping
2457 if (conf.Has("group"))
2458 {
2459 const vector<string> vec = conf.Get<vector<string>>("group");
2460 fGrouping.insert(vec.begin(), vec.end());
2461 }
2462
2463 //set the old run numbers timeout delay
2464 if (conf.Has("runtimeout"))
2465 {
2466 const long timeout = conf.Get<long>("runtimeout");
2467 if (timeout <= 0)
2468 {
2469 Error("Time out delay for old run numbers should be greater than 0 minute");
2470 return false;
2471 }
2472 fRunNumberTimeout = timeout;
2473 }
2474 return true;
2475}
2476
2477// --------------------------------------------------------------------------
2478int RunDim(Configuration &conf)
2479{
2480 WindowLog wout;
2481
2482 ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);
2483
2484 //log.SetWindow(stdscr);
2485 if (conf.Has("log"))
2486 if (!wout.OpenLogFile(conf.Get<string>("log")))
2487 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2488
2489 // Start io_service.Run to use the StateMachineImp::Run() loop
2490 // Start io_service.run to only use the commandHandler command detaching
2491 DataLogger logger(wout);
2492 if (!logger.SetConfiguration(conf))
2493 return -1;
2494
2495 logger.Run(true);
2496
2497 return 0;
2498}
2499// --------------------------------------------------------------------------
2500void RunThread(DataLogger* logger)
2501{
2502 // This is necessary so that the StateMachine Thread can signal the
2503 // Readline thread to exit
2504 logger->Run(true);
2505 Readline::Stop();
2506}
2507// --------------------------------------------------------------------------
2508template<class T>
2509int RunShell(Configuration &conf)
2510{
2511 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
2512
2513 WindowLog &win = shell.GetStreamIn();
2514 WindowLog &wout = shell.GetStreamOut();
2515
2516 if (conf.Has("log"))
2517 if (!wout.OpenLogFile(conf.Get<string>("log")))
2518 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2519
2520 DataLogger logger(wout);
2521
2522 if (!logger.SetConfiguration(conf))
2523 return -1;
2524
2525 shell.SetReceiver(logger);
2526
2527 boost::thread t(boost::bind(RunThread, &logger));
2528
2529 if (conf.Has("cmd"))
2530 {
2531 const vector<string> v = conf.Get<vector<string>>("cmd");
2532 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2533 shell.ProcessLine(*it);
2534 }
2535
2536 if (conf.Has("exec"))
2537 {
2538 const vector<string> v = conf.Get<vector<string>>("exec");
2539 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2540 shell.Execute(*it);
2541 }
2542
2543 if (conf.Get<bool>("quit"))
2544 shell.Stop();
2545
2546
2547 shell.Run(); // Run the shell
2548
2549 logger.Stop();
2550
2551 //Wait until the StateMachine has finished its thread
2552 //before returning and destroyinng the dim objects which might
2553 //still be in use.
2554 t.join();
2555
2556 return 0;
2557}
2558
2559/*
2560 Extract usage clause(s) [if any] for SYNOPSIS.
2561 Translators: "Usage" and "or" here are patterns (regular expressions) which
2562 are used to match the usage synopsis in program output. An example from cp
2563 (GNU coreutils) which contains both strings:
2564 Usage: cp [OPTION]... [-T] SOURCE DEST
2565 or: cp [OPTION]... SOURCE... DIRECTORY
2566 or: cp [OPTION]... -t DIRECTORY SOURCE...
2567 */
2568void PrintUsage()
2569{
2570 cout << "\n"
2571 "The data logger connects to all available Dim services and "
2572 "writes them to ascii and fits files.\n"
2573 "\n"
2574 "The default is that the program is started without user interaction. "
2575 "All actions are supposed to arrive as DimCommands. Using the -c "
2576 "option, a local shell can be initialized. With h or help a short "
2577 "help message about the usage can be brought to the screen.\n"
2578 "\n"
2579 "Usage: datalogger [-c type] [OPTIONS]\n"
2580 " or: datalogger [OPTIONS]\n";
2581 cout << endl;
2582
2583}
2584// --------------------------------------------------------------------------
2585void PrintHelp()
2586{
2587 /* Additional help text which is printed after the configuration
2588 options goes here */
2589 cout <<
2590 "\n"
2591 "If the allow list has any element, only the servers and/or services "
2592 "specified in the list will be used for subscription. The black list "
2593 "will disable service subscription and has higher priority than the "
2594 "allow list. If the allow list is not present by default all services "
2595 "will be subscribed."
2596 "\n"
2597 "For example, block=DIS_DNS/ will skip all the services offered by "
2598 "the DIS_DNS server, while block=/SERVICE_LIST will skip all the "
2599 "SERVICE_LIST services offered by any server and DIS_DNS/SERVICE_LIST "
2600 "will skip DIS_DNS/SERVICE_LIST.\n"
2601 << endl;
2602}
2603
2604// --------------------------------------------------------------------------
2605void SetupConfiguration(Configuration &conf)
2606{
2607 const string n = conf.GetName()+".log";
2608
2609 po::options_description configp("Program options");
2610 configp.add_options()
2611 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
2612 ("log,l", var<string>(n), "Write log-file")
2613 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
2614 ("cmd", vars<string>(), "Execute one or more commands at startup")
2615 ("exec,e", vars<string>(), "Execute one or more scrips at startup")
2616 ("quit,q", po_switch(), "Quit after startup");
2617 ;
2618
2619 po::options_description configs("DataLogger options");
2620 configs.add_options()
2621 ("block,b", vars<string>(), "Black-list of services")
2622 ("allow,a", vars<string>(), "White-list of services")
2623 ("debug", po_bool(), "Debug mode. Print clear text of received service reports to log-stream")
2624 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2625 ("runtimeout", var<long>(), "Time out delay for old run numbers")
2626 ;
2627
2628 conf.AddEnv("dns", "DIM_DNS_NODE");
2629
2630 conf.AddOptions(configp);
2631 conf.AddOptions(configs);
2632}
2633// --------------------------------------------------------------------------
2634int main(int argc, const char* argv[])
2635{
2636 Configuration conf(argv[0]);
2637 conf.SetPrintUsage(PrintUsage);
2638 SetupConfiguration(conf);
2639
2640 po::variables_map vm;
2641 try
2642 {
2643 vm = conf.Parse(argc, argv);
2644 }
2645#if BOOST_VERSION > 104000
2646 catch (po::multiple_occurrences &e)
2647 {
2648 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
2649 return -1;
2650 }
2651#endif
2652 catch (exception& e)
2653 {
2654 cerr << "Program options invalid due to: " << e.what() << endl;
2655 return -1;
2656 }
2657
2658 if (conf.HasVersion() || conf.HasPrint())
2659 return -1;
2660
2661 if (conf.HasHelp())
2662 {
2663 PrintHelp();
2664 return -1;
2665 }
2666
2667 Dim::Setup(conf.Get<string>("dns"));
2668
2669// try
2670 {
2671 // No console access at all
2672 if (!conf.Has("console"))
2673 return RunDim(conf);
2674
2675 // Console access w/ and w/o Dim
2676 if (conf.Get<int>("console")==0)
2677 return RunShell<LocalShell>(conf);
2678 else
2679 return RunShell<LocalConsole>(conf);
2680 }
2681/* catch (exception& e)
2682 {
2683 cerr << "Exception: " << e.what() << endl;
2684 return -1;
2685 }*/
2686
2687 return 0;
2688}
Note: See TracBrowser for help on using the repository browser.