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

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