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

Last change on this file since 11236 was 11221, checked in by tbretz, 14 years ago
Removed q as shortcut for --quit to avoid confusion with --quiet
File size: 94.3 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 bool SetConfiguration(Configuration& conf);
208
209private:
210 /************************************************
211 * MEMBER VARIABLES
212 ************************************************/
213 /// ofstream for the NightlyLogfile
214 ofstream fNightlyLogFile;
215 /// ofstream for the Nightly report file
216 ofstream fNightlyReportFile;
217 /// base path of the Nightlyfile
218 string fNightlyFilePath;
219 ///base path of the run file
220 string fRunFilePath;
221 ///run numbers
222 list<RunNumberType> fRunNumber;
223 ///old run numbers time-out delay (in seconds)
224 long fRunNumberTimeout;
225 ///previous run number. to check if changed while logging
226 int fPreviousRunNumber;
227 ///Current Service Quality
228 int fQuality;
229 ///Modified Julian Date
230 double fMjD;
231 ///for obtaining the name of the existing services
232// ServiceList fServiceList;
233 typedef map<const string, map<string, SubscriptionType> > SubscriptionsListType;
234 ///All the services to which we have subscribed to, sorted by server name.
235 SubscriptionsListType fServiceSubscriptions;
236 ///full name of the nightly log file
237 string fFullNightlyLogFileName;
238 ///full name of the nightly report file
239 string fFullNightlyReportFileName;
240 ///variable to track when the statistic were last calculated
241 Time fPreviousStatsUpdateTime;
242 Time fPreviousOldRunNumberCheck;
243 ///boolean to know whether we should close and reopen daily files or not
244 bool fDailyFileDayChangedAlready;
245
246private:
247 /***************************************************
248 * DIM INFO HANDLER
249 ***************************************************/
250 //overloading of DIM's infoHandler function
251 void infoHandler();
252
253 /***************************************************
254 * TRANSITION FUNCTIONS
255 ***************************************************/
256 ///Reporting method for the services info received
257 void ReportPlease(DimInfo* I, SubscriptionType& sub);
258
259 int ConfigureFileName(string &target, const string &type, const EventImp &evt);
260 ///Configuration of the nightly file path
261 int ConfigureNightlyFileName(const Event& evt);
262 ///Configuration fo the file name
263 int ConfigureRunFileName(const Event& evt);
264 ///DEPREC - configuration of the run number
265 int ConfigureRunNumber(const Event& evt);
266 ///print the current state of the dataLogger
267 int PrintStatePlease(const Event& evt);
268 ///checks whether or not the current info being treated is a run number
269 void CheckForRunNumber(DimInfo* I);
270 /// start transition
271 int StartPlease();
272 ///from waiting to logging transition
273 int StartRunPlease();
274 /// from logging to waiting transition
275 int StopRunPlease();
276 ///stop and reset transition
277 int GoToReadyPlease();
278 ///from NightlyOpen to waiting transition
279 int NightlyToWaitRunPlease();
280 /// from writing to error
281 std::string SetCurrentState(int state, const char *txt="", const std::string &cmd="");
282#ifdef HAVE_FITS
283 ///Open fits files
284 void OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber);
285 ///Write data to FITS files
286 void WriteToFITS(SubscriptionType& sub);
287 ///Allocate the buffers required for fits
288 void AllocateFITSBuffers(SubscriptionType& sub);
289#endif//has_fits
290
291 /***************************************
292 * DIM SERVICES PROVIDED BY THE DATA LOGGER
293 ***************************************/
294 ///monitoring notification loop
295 void ServicesMonitoring();
296 inline void NotifyOpenedFile(const string &name, int type, DimDescribedService* service);
297 ///variables for computing statistics
298 DataLoggerStats fStatVar;
299 ///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
1050 if (fDebugIsOn)
1051 {
1052 Debug("DataLogger desctruction ends");
1053 }
1054}
1055// --------------------------------------------------------------------------
1056//
1057//! checks if the statistic service should be updated, and if so, do it
1058//
1059void DataLogger::UpdateStatisticsService()
1060{
1061 //update the fits files sizes
1062 const Time cTime = Time();
1063
1064 if ((fStatsPeriodDuration == 0) || ((cTime - fPreviousStatsUpdateTime).total_seconds() < fStatsPeriodDuration))
1065 return;
1066
1067 calculateTotalSizeWritten(fStatVar, false);
1068 fStatVar.writingRate = (fStatVar.sizeWritten - fPreviousSize)/((cTime - fPreviousStatsUpdateTime).total_seconds());
1069 fPreviousSize = fStatVar.sizeWritten;
1070 fPreviousStatsUpdateTime = cTime;
1071 //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
1072 fStatsMonitoring->updateService();
1073
1074 if(fDebugIsOn)
1075 {
1076 ostringstream str;
1077 str << "Size written: " << fStatVar.sizeWritten/1000 << " kB; writing rate: ";
1078 str << fStatVar.writingRate/1000 << " kB/s; free space: ";
1079 str << fStatVar.freeSpace/(1000*1000) << " MB";
1080 Debug(str);
1081 }
1082}
1083// --------------------------------------------------------------------------
1084//
1085//! checks if old run numbers should be trimmed and if so, do it
1086//
1087void DataLogger::TrimOldRunNumbers()
1088{
1089 const Time cTime = Time();
1090
1091 if ((cTime - fPreviousOldRunNumberCheck).total_seconds() < fRunNumberTimeout)
1092 return;
1093
1094 while (fRunNumber.size() > 1 && (cTime - fRunNumber.back().time) > boost::posix_time::seconds(fRunNumberTimeout))
1095 {
1096 RemoveOldestRunNumber();
1097 }
1098 fPreviousOldRunNumberCheck = cTime;
1099}
1100// --------------------------------------------------------------------------
1101//
1102//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
1103//
1104void DataLogger::infoHandler()
1105{
1106 // Make sure getTimestamp is called _before_ getTimestampMillisecs
1107 if (fDestructing)
1108 return;
1109
1110 DimInfo* I = getInfo();
1111
1112 if (I==NULL)
1113 return;
1114
1115 //it may happen that we try to write a dimInfo while closing files. Prevent that with a dim_lock()
1116 dim_lock();
1117
1118 //check if the service pointer corresponds to something that we subscribed to
1119 //this is a fix for a bug that provides bad Infos when a server starts
1120 bool found = false;
1121 SubscriptionsListType::iterator x;
1122 map<string, SubscriptionType>::iterator y;
1123 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1124 {//find current service is subscriptions
1125 for (y=x->second.begin(); y!=x->second.end();y++)
1126 if ((y->second.dimInfo).get() == I)
1127 {
1128 found = true;
1129 break;
1130 }
1131 if (found)
1132 break;
1133 }
1134 if (!found)
1135 {
1136 DimServiceInfoList::infoHandler();
1137 dim_unlock();
1138 return;
1139 }
1140 if (I->getSize() <= 0 || I->getData()==NULL)
1141 {
1142 dim_unlock();
1143 return;
1144 }
1145 // Make sure that getTimestampMillisecs is NEVER called before
1146 // getTimestamp is properly called
1147 // check that the message has been updated by something, i.e. must be different from its initial value
1148 if (I->getTimestamp() == 0)
1149 {
1150 dim_unlock();
1151 return;
1152 }
1153 // FIXME: Here we have to check if we have received the
1154 // service with the run-number.
1155 // CheckForRunNumber(I); has been removed because we have to
1156 // subscribe to this service anyway and hence we have the pointer
1157 // (no need to check for the name)
1158
1159 ReportPlease(I, y->second);
1160
1161 dim_unlock();
1162 //update the fits files sizes
1163 UpdateStatisticsService();
1164
1165 //remove old run numbers
1166 TrimOldRunNumbers();
1167}
1168
1169bool DataLogger::OpenStream(shared_ptr<ofstream> stream, const string &filename)
1170{
1171 Info("Opening: "+filename);
1172
1173 if (stream->is_open())
1174 {
1175 ostringstream str;
1176 str << filename << " was already open when trying to open it.";
1177 Error(str);
1178 return false;
1179 }
1180
1181 errno = 0;
1182 stream->open(filename.c_str(), ios_base::out | ios_base::app);
1183 if (errno != 0)
1184 {
1185 ostringstream str;
1186 str << "Unable to open " << filename << ": " << strerror(errno) << " (errno=" << errno << ")";
1187 Error(str);
1188 return false;
1189 }
1190
1191 if (!stream->is_open())
1192 {
1193 ostringstream str;
1194 str << "File " << filename << " not open as it ought to be.";
1195 Error(str);
1196 return false;
1197 }
1198
1199 return true;
1200}
1201
1202// --------------------------------------------------------------------------
1203//
1204//! Open the text files associated with the given run number
1205//! @param run the run number to be dealt with
1206//
1207int DataLogger::OpenRunFile(RunNumberType& run)
1208{
1209#ifdef RUN_LOGS
1210 // open log file
1211 run.logName = CompileFileName(fRunFilePath, run.runNumber, "", "log");
1212 if (!OpenStream(run.logFile, run.logName))
1213 return -1;
1214#endif
1215
1216 // open report file
1217 run.reportName = CompileFileNameWithPath(fRunFilePath, run.runNumber, "", "rep");
1218 if (!OpenStream(run.reportFile, run.reportName))
1219 return -1;
1220
1221 //get the size of the newly opened file.
1222#ifdef RUN_LOGS
1223 RememberFileOrigSizePlease(run.logName, false);
1224#endif
1225 RememberFileOrigSizePlease(run.reportName, false);
1226
1227 //TODO this notification scheme might be messed up now.... fix it !
1228 const string baseFileName = CompileFileNameWithPath(fRunFilePath, run.runNumber, "", "");
1229 NotifyOpenedFile(baseFileName, 3, fOpenedRunFiles);
1230 run.openedFits.clear();
1231 return 0;
1232}
1233// --------------------------------------------------------------------------
1234//
1235//! Add a new active run number
1236//! @param newRun the new run number
1237//! @param time the time at which the new run number was issued
1238//
1239void DataLogger::AddNewRunNumber(int64_t newRun, Time time)
1240{
1241
1242 if (newRun > 0xffffffff)
1243 {
1244 Error("New run number too large, out of range. Ignoring.");
1245 return;
1246 }
1247 for (std::vector<int64_t>::const_iterator it=previousRunNumbers.begin(); it != previousRunNumbers.end(); it++)
1248 {
1249 if (*it == newRun)
1250 {
1251 Error("Newly provided run number has already been used (or is still in use). Going to error state");
1252 SetCurrentState(kSM_BadRunConfig);
1253 return;
1254 }
1255 }
1256 if (fDebugIsOn)
1257 {
1258 ostringstream str;
1259 str << "Adding new run number " << newRun << " that was issued on " << time;
1260 Debug(str);
1261 }
1262 //Add new run number to run number list
1263 fRunNumber.push_back(RunNumberType());
1264 fRunNumber.back().runNumber = uint32_t(newRun);
1265 fRunNumber.back().time = time;
1266
1267 ostringstream str;
1268 str << "The new run number is: " << fRunNumber.back().runNumber;
1269 Message(str);
1270
1271 if (GetCurrentState() != kSM_Logging)
1272 return;
1273 //open the log and report files
1274 if (OpenRunFile(fRunNumber.back()) != 0)
1275 {//an error occured. close current run files and go to error state
1276 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
1277 {
1278 if (it->reportFile->is_open())
1279 {
1280 it->reportFile->close();
1281 Info("Closing: "+it->reportName);
1282 }
1283#ifdef RUN_LOGS
1284 if (it->logFile->is_open())
1285 {
1286 it->logFile->close();
1287 Info("Closing: "+it->logName);
1288 }
1289#endif
1290 }
1291 StopRunPlease();
1292 SetCurrentState(kSM_BadRunConfig);
1293 }
1294}
1295// --------------------------------------------------------------------------
1296//
1297//! Checks whether or not the current info is a run number.
1298//! If so, then remember it. A run number is required to open the run-log file
1299//! @param I
1300//! the current DimInfo
1301//
1302void DataLogger::CheckForRunNumber(DimInfo* I)
1303{
1304 if (strstr(I->getName(), "SET_RUN_NUMBER") != NULL)
1305 {//assumes that the run number is an integer
1306 //check if some run number entries can be deleted leave one so that two remain after adding the new one
1307 AddNewRunNumber(I->getLonglong(), Time(I->getTimestamp(), I->getTimestampMillisecs()*1000));
1308 }
1309}
1310
1311// --------------------------------------------------------------------------
1312//
1313//! write infos to log files.
1314//! @param I
1315//! The current DimInfo
1316//! @param sub
1317//! The dataLogger's subscription corresponding to this DimInfo
1318//
1319void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
1320{
1321 const string fmt(I->getFormat());
1322
1323 const bool isItaReport = fmt!="C";
1324
1325 if (!fNightlyReportFile.is_open())
1326 return;
1327
1328 if (fDebugIsOn && string(I->getName())!="DATA_LOGGER/MESSAGE")
1329 {
1330 ostringstream str;
1331 str << "Logging " << I->getName() << " [" << I->getFormat() << "] (" << I->getSize() << ")";
1332 Debug(str);
1333 }
1334
1335 //Check whether we should close and reopen daily text files or not
1336 //This should work in any case base of the following:
1337 // - fDailyFileDayChangedAlready is initialized to true. So if the dataLogger is started around noon, no file will be closed
1338 // - 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)
1339 //This only applies to text files. Fits are closed and reopened based on the last and current service received time.
1340 //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,
1341 //which could lead to several close/reopen instead of only one.
1342 if (Time().h() == 12 && !fDailyFileDayChangedAlready)
1343 {
1344 if (fDebugIsOn)
1345 Debug("Its Noon! Closing and reopening nightly text files");
1346
1347 fNightlyLogFile.close();
1348 fNightlyReportFile.close();
1349
1350 Info("Closed: "+fFullNightlyLogFileName);
1351 Info("Closed: "+fFullNightlyReportFileName);
1352
1353 fFullNightlyLogFileName = CompileFileNameWithPath(fNightlyFilePath, "", "log");
1354 if (!OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName))
1355 {
1356 GoToReadyPlease();
1357 SetCurrentState(kSM_BadNightlyConfig);
1358 return;
1359 }
1360
1361 fFullNightlyReportFileName = CompileFileNameWithPath(fNightlyFilePath, "", "rep");
1362 if (!OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName))
1363 {
1364 GoToReadyPlease();
1365 SetCurrentState(kSM_BadNightlyConfig);
1366 return;
1367 }
1368
1369 fDailyFileDayChangedAlready = true;
1370 }
1371 if (Time().h() != 12 && fDailyFileDayChangedAlready)
1372 fDailyFileDayChangedAlready = false;
1373
1374 //create the converter for that service
1375 if (!sub.fConv.get())
1376 {
1377 sub.fConv = shared_ptr<Converter>(new Converter(Out(), I->getFormat()));
1378 if (!sub.fConv)
1379 {
1380 ostringstream str;
1381 str << "Couldn't properly parse the format... service " << sub.dimInfo->getName() << " ignored.";
1382 Error(str);
1383 return;
1384 }
1385 }
1386 //construct the header
1387 ostringstream header;
1388 const Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
1389 fQuality = I->getQuality();
1390 fMjD = cTime.Mjd();
1391
1392 //figure out which run file should be used
1393 ofstream* targetRunFile = NULL;
1394 RunNumberType* cRunNumber = NULL;
1395 if (GetCurrentState() == kSM_Logging)
1396 {
1397 list<RunNumberType>::reverse_iterator rit;
1398 for (rit=fRunNumber.rbegin(); rit!=fRunNumber.rend(); rit++)
1399 {
1400 if (rit->time < cTime) //this is the run number that we want to use
1401 {
1402 //Find something better to convert iterator to pointer than the ugly line below....
1403 cRunNumber = &(*rit);
1404 sub.runNumber = rit->runNumber;
1405#ifdef RUN_LOGS
1406 targetRunFile = isItaReport ? (rit->reportFile).get() : (rit->logFile).get();
1407#else
1408 targetRunFile = isItaReport ? (rit->reportFile).get() : NULL;
1409#endif
1410 break;
1411 }
1412 }
1413 if (rit == fRunNumber.rend() && fRunNumber.size() != 0)
1414 {
1415 ostringstream str;
1416 str << "Could not find an appropriate run number for info coming at time: " << cTime;
1417 Error(str);
1418 Error("Active run numbers: ");
1419 for (rit=fRunNumber.rbegin(); rit != fRunNumber.rend(); rit++)
1420 {
1421 str.str("");
1422 str << rit->runNumber;
1423 Error(str);
1424 }
1425
1426 }
1427 }
1428
1429 if (isItaReport)
1430 {
1431 //write text header
1432 header << I->getName() << " " << fQuality << " ";
1433 header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1434 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1435 header << cTime.ms() << " " << I->getTimestamp() << " ";
1436
1437 string text;
1438 try
1439 {
1440 text = sub.fConv->GetString(I->getData(), I->getSize());
1441 }
1442 catch (const runtime_error &e)
1443 {
1444 ostringstream str;
1445 str << "Parsing service " << sub.dimInfo->getName();
1446 str << " failed: " << e.what();
1447 Error(str);
1448 return;
1449 }
1450
1451 if (text.empty())
1452 {
1453 ostringstream str;
1454 str << "Service " << sub.dimInfo->getName() << " sent an empty string";
1455 Info(str);
1456 return;
1457 }
1458 //replace bizarre characters by white space
1459 replace(text.begin(), text.end(), '\n', '\\');
1460 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1461
1462 //write entry to Nightly report
1463 if (fNightlyReportFile.is_open())
1464 {
1465 fNightlyReportFile << header.str() << text << endl;
1466 if (!CheckForOfstreamError(fNightlyReportFile, true))
1467 return;
1468 }
1469 //write entry to run-report
1470 if (targetRunFile && targetRunFile->is_open())
1471 {
1472 *targetRunFile << header.str() << text << endl;
1473 if (!CheckForOfstreamError(*targetRunFile, false))
1474 return;
1475 }
1476#ifdef HAVE_FITS
1477 //check if the last received event was before noon and if current one is after noon.
1478 //if so, close the file so that it gets reopened.
1479 if (sub.nightlyFile.IsOpen())
1480 if ((sub.lastReceivedEvent != Time::None) && (sub.lastReceivedEvent.h() < 12) && (cTime.h() >= 12))
1481 {
1482 sub.nightlyFile.Close();
1483 }
1484 sub.lastReceivedEvent = cTime;
1485 if (!sub.nightlyFile.IsOpen() || !sub.runFile.IsOpen() || sub.runNumber != sub.runFile.fRunNumber)
1486 OpenFITSFilesPlease(sub, cRunNumber);
1487 WriteToFITS(sub);
1488#endif
1489 }
1490 else
1491 {//write entry to both Nightly and run logs
1492 vector<string> strings = sub.fConv->ToStrings(I->getData());
1493 if (strings.size() > 1)
1494 {
1495 ostringstream err;
1496 err << "There was more than one string message in service " << I->getName() << " going to fatal error state";
1497 Error(err.str());
1498 }
1499 ostringstream msg;
1500 msg << I->getName() << ": " << strings[0];
1501
1502 if (fNightlyLogFile.is_open())
1503 {
1504 MessageImp(fNightlyLogFile).Write(cTime, msg.str().c_str(), fQuality);
1505 if (!CheckForOfstreamError(fNightlyLogFile, true))
1506 return;
1507 }
1508 if (targetRunFile && targetRunFile->is_open())
1509 {
1510 MessageImp(*targetRunFile).Write(cTime, msg.str().c_str(), fQuality);
1511 if (!CheckForOfstreamError(*targetRunFile, false))
1512 return;
1513 }
1514 }
1515
1516}
1517
1518// --------------------------------------------------------------------------
1519//
1520//! print the dataLogger's current state. invoked by the PRINT command
1521//! @param evt
1522//! the current event. Not used by the method
1523//! @returns
1524//! the new state. Which, in that case, is the current state
1525//!
1526int DataLogger::PrintStatePlease(const Event& )
1527{
1528 Message("------------------------------------------");
1529 Message("------- DATA LOGGER CURRENT STATE --------");
1530 Message("------------------------------------------");
1531
1532 //print the path configuration
1533 Message("Nightly Path: " + boost::filesystem::system_complete(boost::filesystem::path(fNightlyFilePath)).directory_string());
1534 Message("Run Path: " + boost::filesystem::system_complete(boost::filesystem::path(fRunFilePath)).directory_string());
1535
1536 //print active run numbers
1537 ostringstream str;
1538 str << "Active Run Numbers:";
1539 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1540 str << " " << it->runNumber;
1541 if (fRunNumber.size()==0)
1542 str << " <none>";
1543 Message(str);
1544 //timeout value
1545 str.str("");
1546 str << "Timeout delay for old run numbers: " << fRunNumberTimeout << " seconds";
1547 Message(str);
1548
1549 //print all the open files.
1550 Message("------------ OPENED FILES ----------------");
1551 if (fNightlyLogFile.is_open())
1552 Message("Nightly log-file: OPEN");
1553
1554 if (fNightlyReportFile.is_open())
1555 Message("Nightly report-file: OPEN");
1556
1557 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1558 {
1559#ifdef RUN_LOGS
1560 if (it->logFile->is_open())
1561 Message("Run log-file: " + it->logName + " (OPEN)");
1562#endif
1563 if (it->reportFile->is_open())
1564 Message("Run report-file: " + it->reportName + " (OPEN)");
1565 }
1566
1567 DataLoggerStats statVar;
1568 /*const bool statWarning =*/ calculateTotalSizeWritten(statVar, true);
1569
1570 Message("----------------- STATS ------------------");
1571 str.str("");
1572 str << "Total Size written: " << statVar.sizeWritten << " bytes.";
1573 Message(str);
1574 str.str("");
1575 str << "Disk free space: " << statVar.freeSpace << " bytes.";
1576 Message(str);
1577 str.str("");
1578 str << "Statistics are updated every " << fStatsPeriodDuration << " seconds";
1579 if (fStatsPeriodDuration != 0)
1580 Message(str);
1581 else
1582 Message("Statistics updates are currently disabled");
1583
1584 Message("------------ DIM SUBSCRIPTIONS -----------");
1585 str.str("");
1586 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions.";
1587 Message(str);
1588 for (map<const string, map<string, SubscriptionType> >::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1589 {
1590 Message("Server "+it->first);
1591 for (map<string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1592 Message(" -> "+it2->first);
1593 }
1594 Message("--------------- BLOCK LIST ---------------");
1595 for (set<string>::const_iterator it=fBlackList.begin(); it != fBlackList.end(); it++)
1596 Message(" -> "+*it);
1597 if (fBlackList.size()==0)
1598 Message(" <empty>");
1599
1600 Message("--------------- ALLOW LIST ---------------");
1601 for (set<string>::const_iterator it=fWhiteList.begin(); it != fWhiteList.end(); it++)
1602 Message(" -> "+*it);
1603 if (fWhiteList.size()==0)
1604 Message(" <empty>");
1605
1606 Message("-------------- GROUPING LIST -------------");
1607 Message("The following servers and/or services will");
1608 Message("be grouped into a single fits file:");
1609 for (set<string>::const_iterator it=fGrouping.begin(); it != fGrouping.end(); it++)
1610 Message(" -> "+*it);
1611 if (fGrouping.size()==0)
1612 Message(" <no grouping>");
1613
1614 Message("------------------------------------------");
1615 Message("-------- END OF DATA LOGGER STATE --------");
1616 Message("------------------------------------------");
1617
1618 return GetCurrentState();
1619}
1620
1621// --------------------------------------------------------------------------
1622//
1623//! turn debug mode on and off
1624//! @param evt
1625//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1626//! @returns
1627//! the new state. Which, in that case, is the current state
1628//!
1629int DataLogger::SetDebugOnOff(const Event& evt)
1630{
1631 const bool backupDebug = fDebugIsOn;
1632
1633 fDebugIsOn = evt.GetBool();
1634
1635 if (fDebugIsOn == backupDebug)
1636 Message("Debug mode was already in the requested state.");
1637
1638 ostringstream str;
1639 str << "Debug mode is now " << fDebugIsOn;
1640 Message(str);
1641
1642 return GetCurrentState();
1643}
1644// --------------------------------------------------------------------------
1645//
1646//! set the statistics update period duration. 0 disables the statistics
1647//! @param evt
1648//! the current event. contains the new duration.
1649//! @returns
1650//! the new state. Which, in that case, is the current state
1651//!
1652int DataLogger::SetStatsPeriod(const Event& evt)
1653{
1654 const float backupDuration = fStatsPeriodDuration;
1655
1656 fStatsPeriodDuration = evt.GetFloat();
1657
1658 if (fStatsPeriodDuration < 0)
1659 {
1660 Error("Statistics period duration should be greater than zero. Discarding provided value.");
1661 fStatsPeriodDuration = backupDuration;
1662 return GetCurrentState();
1663 }
1664 if (!finite(fStatsPeriodDuration))// != fStatsPeriodDuration)
1665 {
1666 Error("Provided duration does not appear to be a valid float. Discarding it.");
1667 fStatsPeriodDuration = backupDuration;
1668 return GetCurrentState();
1669 }
1670 if (backupDuration == fStatsPeriodDuration)
1671 Warn("Statistics period not modified. Supplied value already in use.");
1672
1673 if (fStatsPeriodDuration == 0.0f)
1674 Message("Statistics are now OFF");
1675 else
1676 {
1677 ostringstream str;
1678 str << "Statistics period is now " << fStatsPeriodDuration << " seconds";
1679 Message(str);
1680 }
1681
1682 return GetCurrentState();
1683}
1684// --------------------------------------------------------------------------
1685//
1686//! set the opened files service on or off.
1687//! @param evt
1688//! the current event. contains the instruction string. similar to setdebugonoff
1689//! @returns
1690//! the new state. Which, in that case, is the current state
1691//!
1692int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1693{
1694 const bool backupOpened = fOpenedFilesIsOn;
1695
1696 fOpenedFilesIsOn = evt.GetBool();
1697
1698 if (fOpenedFilesIsOn == backupOpened)
1699 Message("Opened files service mode was already in the requested state.");
1700
1701 ostringstream str;
1702 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1703 Message(str);
1704
1705 return GetCurrentState();
1706}
1707
1708// --------------------------------------------------------------------------
1709//
1710//! set the number of subscriptions and opened fits on and off
1711//! @param evt
1712//! the current event. contains the instruction string. similar to setdebugonoff
1713//! @returns
1714//! the new state. Which, in that case, is the current state
1715//!
1716int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1717{
1718 const bool backupSubs = fNumSubAndFitsIsOn;
1719
1720 fNumSubAndFitsIsOn = evt.GetBool();
1721
1722 if (fNumSubAndFitsIsOn == backupSubs)
1723 Message("Number of subscriptions service mode was already in the requested state");
1724
1725 ostringstream str;
1726 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1727 Message(str);
1728
1729 return GetCurrentState();
1730}
1731// --------------------------------------------------------------------------
1732//
1733//! set the timeout delay for old run numbers
1734//! @param evt
1735//! the current event. contains the timeout delay long value
1736//! @returns
1737//! the new state. Which, in that case, is the current state
1738//!
1739int DataLogger::SetRunTimeoutDelay(const Event& evt)
1740{
1741 const long backupTimeout = fRunNumberTimeout;
1742 fRunNumberTimeout = evt.GetXtra();
1743
1744 if (fRunNumberTimeout == 0)
1745 {
1746 fRunNumberTimeout = backupTimeout;
1747 Error("Timeout delays for old run numbers must be greater than 0. Ignored.");
1748 return GetCurrentState();
1749 }
1750
1751 if (fRunNumberTimeout == backupTimeout)
1752 Message("New timeout for old run numbers is same value as previous one.");
1753
1754 ostringstream str;
1755 str << "Timeout delay for old run numbers is now " << fRunNumberTimeout << " seconds";
1756 Message(str);
1757
1758 return GetCurrentState();
1759}
1760
1761// --------------------------------------------------------------------------
1762//
1763//! Configure a given file name
1764//! @param target
1765//! where to put the result
1766//! @param type
1767//! what to append to the created path. currently only run or nightly
1768//! @param evt
1769//! the event transporting the path
1770//! @returns
1771//! currently only the current state.
1772//
1773int DataLogger::ConfigureFileName(string &target, const string &type, const EventImp &evt)
1774{
1775 if (!evt.GetText())
1776 {
1777 Error("Empty "+type+" folder given. Please specify a valid path.");
1778 return GetCurrentState();
1779 }
1780
1781 const string givenPath = evt.GetText();
1782 if (!DoesPathExist(givenPath))
1783 {
1784 Error("Provided "+type+" path '"+givenPath+"' is not a valid folder. Ignored");
1785 return GetCurrentState();
1786 }
1787
1788 Message("New "+type+" folder: "+givenPath);
1789
1790 target = givenPath;
1791
1792 return GetCurrentState();
1793}
1794
1795// --------------------------------------------------------------------------
1796//
1797//! Sets the path to use for the Nightly log file.
1798//! @param evt
1799//! the event transporting the path
1800//! @returns
1801//! currently only the current state.
1802//
1803int DataLogger::ConfigureNightlyFileName(const Event& evt)
1804{
1805 return ConfigureFileName(fNightlyFilePath, "nightly", evt);
1806}
1807
1808// --------------------------------------------------------------------------
1809//
1810//! Sets the path to use for the run log file.
1811//! @param evt
1812//! the event transporting the path
1813//! @returns
1814//! currently only the current state
1815int DataLogger::ConfigureRunFileName(const Event& evt)
1816{
1817 return ConfigureFileName(fRunFilePath, "run", evt);
1818}
1819
1820// --------------------------------------------------------------------------
1821//
1822//! Sets the run number.
1823//! @param evt
1824//! the event transporting the run number
1825//! @returns
1826//! currently only the current state
1827int DataLogger::ConfigureRunNumber(const Event& evt)
1828{
1829 AddNewRunNumber(evt.GetXtra(), evt.GetTime());
1830 return GetCurrentState();
1831}
1832// --------------------------------------------------------------------------
1833//
1834//! Notifies the DIM service that a particular file was opened
1835//! @ param name the base name of the opened file, i.e. without path nor extension.
1836//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1837//! this is not a problem though because file are not opened so often.
1838//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1839inline void DataLogger::NotifyOpenedFile(const string &name, int type, DimDescribedService* service)
1840{
1841 if (!fOpenedFilesIsOn)
1842 return;
1843
1844 if (fDebugIsOn)
1845 {
1846 ostringstream str;
1847 str << "Updating " << service->getName() << " file '" << name << "' (type=" << type << ")";
1848 Debug(str);
1849
1850 str.str("");
1851 str << "Num subscriptions: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS files: " << fNumSubAndFitsData.numOpenFits;
1852 Debug(str);
1853 }
1854
1855 if (name.size()+1 > FILENAME_MAX)
1856 {
1857 Error("Provided file name '" + name + "' is longer than allowed file name length.");
1858 return;
1859 }
1860
1861 OpenFileToDim fToDim;
1862 fToDim.code = type;
1863 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1864
1865 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(int));
1866 service->setQuality(0);
1867 service->updateService();
1868}
1869// --------------------------------------------------------------------------
1870//
1871//! Implements the Start transition.
1872//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1873//! and tries to open it.
1874//! @returns
1875//! kSM_NightlyOpen if success, kSM_BadNightlyConfig if failure
1876int DataLogger::StartPlease()
1877{
1878 if (fDebugIsOn)
1879 {
1880 Debug("Starting...");
1881 }
1882 fFullNightlyLogFileName = CompileFileNameWithPath(fNightlyFilePath, "", "log");
1883 if (!OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName))
1884 return kSM_BadNightlyConfig;
1885
1886
1887 fFullNightlyReportFileName = CompileFileNameWithPath(fNightlyFilePath, "", "rep");
1888 if (!OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName))
1889 {
1890 fNightlyLogFile.close();
1891 Info("Closed: "+fFullNightlyReportFileName);
1892 return kSM_BadNightlyConfig;
1893 }
1894 //get the size of the newly opened file.
1895 fBaseSizeNightly = GetFileSize(fFullNightlyLogFileName);
1896 fBaseSizeNightly += GetFileSize(fFullNightlyReportFileName);
1897 fFileSizesMap.clear();
1898 fBaseSizeRun = 0;
1899 fPreviousSize = 0;
1900
1901 //notify that a new file has been opened.
1902 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
1903 NotifyOpenedFile(baseFileName, 3, fOpenedNightlyFiles);
1904
1905 fOpenedNightlyFits.clear();
1906
1907 return kSM_NightlyOpen;
1908}
1909
1910#ifdef HAVE_FITS
1911// --------------------------------------------------------------------------
1912//
1913//! open if required a the FITS files corresponding to a given subscription
1914//! @param sub
1915//! the current DimInfo subscription being examined
1916void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber)
1917{
1918 string serviceName(sub.dimInfo->getName());
1919
1920 //if run number has changed, reopen a new fits file with the correct run number.
1921 if (sub.runFile.IsOpen() && sub.runFile.fRunNumber != sub.runNumber)
1922 {
1923 sub.runFile.Close();
1924 Info("Closed: "+sub.runFile.fFileName+" (new run number)");
1925 }
1926
1927 //we must check if we should group this service subscription to a single fits file before we replace the / by _
1928 bool hasGrouping = false;
1929 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1930 {//will we find this service in the grouping list ?
1931 for (set<string>::const_iterator it=fGrouping.begin(); it!=fGrouping.end(); it++)
1932 {
1933 if (serviceName.find(*it) != string::npos)
1934 {
1935 hasGrouping = true;
1936 break;
1937 }
1938 }
1939 }
1940 for (unsigned int i=0;i<serviceName.size(); i++)
1941 {
1942 if (serviceName[i] == '/')
1943 {
1944 serviceName[i] = '_';
1945 break;
1946 }
1947 }
1948 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1949 if (!sub.nightlyFile.IsOpen())
1950 {
1951 string partialName = CompileFileNameWithPath(fNightlyFilePath, serviceName, "fits");
1952
1953 const string fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1954 if (!sub.fitsBufferAllocated)
1955 AllocateFITSBuffers(sub);
1956 //get the size of the file we're about to open
1957 if (RememberFileOrigSizePlease(partialName, true))//and remember that the file was opened (i.e. not an update)
1958 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
1959
1960 ostringstream str;
1961 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
1962 Info(str);
1963
1964 if (!sub.nightlyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, 0))
1965 {
1966 GoToRunWriteErrorState();
1967 //SetCurrentState(kSM_WriteError);
1968 return;
1969 }
1970 //notify the opening
1971 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
1972 NotifyOpenedFile(baseFileName, 7, fOpenedNightlyFiles);
1973 if (fNumSubAndFitsIsOn)
1974 fNumSubAndFits->updateService();
1975 }
1976 //do the actual file open
1977 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging) && sub.runNumber != 0)
1978 {//buffer for the run file have already been allocated when doing the Nightly file
1979 string fileNameOnly;
1980 string partialName;
1981 if (hasGrouping)
1982 {
1983 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "fits");
1984 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1985 }
1986 else
1987 {
1988 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, serviceName, "fits");
1989 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1990 }
1991 //get the size of the file we're about to open
1992 if (RememberFileOrigSizePlease(partialName, false))//and remember that the file was opened (i.e. not an update)
1993 cRunNumber->openedFits[fileNameOnly].push_back(serviceName);
1994 else
1995 if (hasGrouping)
1996 {
1997 cRunNumber->addServiceToOpenedFits(fileNameOnly, serviceName);
1998 }
1999
2000 if (hasGrouping && (!cRunNumber->runFitsFile.get()))
2001 try
2002 {
2003 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>(new CCfits::FITS(partialName, CCfits::RWmode::Write));
2004 (fNumSubAndFitsData.numOpenFits)++;
2005 }
2006 catch (CCfits::FitsException e)
2007 {
2008 ostringstream str;
2009 str << "Open FITS file " << partialName << ": " << e.message();
2010 Error(str);
2011 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>();//NULL;
2012 GoToRunWriteErrorState();
2013 //SetCurrentState(kSM_WriteError);
2014 return;
2015 }
2016
2017 const string baseFileName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "");
2018 NotifyOpenedFile(baseFileName, 7, fOpenedRunFiles);// + '_' + serviceName, 4);
2019
2020 ostringstream str;
2021 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
2022 Info(str);
2023
2024 if (hasGrouping)
2025 {
2026 if (!sub.runFile.Open(partialName, serviceName, (cRunNumber->runFitsFile).get(), &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2027 {
2028 GoToRunWriteErrorState();
2029 //SetCurrentState(kSM_WriteError);
2030 return;
2031 }
2032 }
2033 else
2034 {
2035 if (!sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2036 {
2037 GoToRunWriteErrorState();
2038 //SetCurrentState(kSM_WriteError);
2039 return;
2040 }
2041 }
2042 if (fNumSubAndFitsIsOn)
2043 fNumSubAndFits->updateService();
2044 }
2045}
2046// --------------------------------------------------------------------------
2047//
2048//! Allocates the required memory for a given pair of fits files (nightly and run)
2049//! @param sub the subscription of interest.
2050//
2051void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
2052{
2053 //Init the time columns of the file
2054 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MjD"));
2055 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2056 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2057
2058 Description QoSDesc("Qos", "Quality of service", "None");
2059 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2060 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2061
2062 const Converter::FormatList flist = sub.fConv->GetList();
2063 // Compilation failed
2064 if (!sub.fConv->valid())
2065 {
2066 Error("Compilation of format string failed.");
2067 return;
2068 }
2069
2070 //we've got a nice structure describing the format of this service's messages.
2071 //Let's create the appropriate FITS columns
2072 int size = 0;
2073
2074 vector<string> dataFormatsLocal;
2075 for (unsigned int i=0;i<flist.size()-1;i++)
2076 {
2077 ostringstream dataQualifier;
2078
2079 dataQualifier << flist[i].second.first;
2080 switch (flist[i].first.first->name()[0])
2081 {
2082 case 'c': dataQualifier << "S"; break;
2083 case 's': dataQualifier << "I"; break;
2084 case 'i': dataQualifier << "J"; break;
2085 case 'l': dataQualifier << "J"; break;
2086 case 'f': dataQualifier << "E"; break;
2087 case 'd': dataQualifier << "D"; break;
2088 case 'x': dataQualifier << "K"; break;
2089 case 'S': //we skip the variable length strings
2090 continue;
2091
2092 default:
2093 Fatal("THIS SHOULD NEVER BE REACHED.");
2094 };
2095
2096 size += flist[i].first.second * flist[i].second.first;
2097 dataFormatsLocal.push_back(dataQualifier.str());
2098 }
2099 sub.nightlyFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2100 sub.runFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2101 sub.fitsBufferAllocated = true;
2102}
2103// --------------------------------------------------------------------------
2104//
2105//! write a dimInfo data to its corresponding FITS files
2106//
2107void DataLogger::WriteToFITS(SubscriptionType& sub)
2108{
2109 //nightly File status (open or not) already checked
2110 if (sub.nightlyFile.IsOpen())
2111 {
2112 if (!sub.nightlyFile.Write(sub.fConv.get()))
2113 {
2114 sub.nightlyFile.Close();
2115 GoToNightlyWriteErrorState();
2116 return;
2117 //SetCurrentState(kSM_WriteError);
2118 }
2119 }
2120
2121 if (sub.runFile.IsOpen())
2122 {
2123 if (!sub.runFile.Write(sub.fConv.get()))
2124 {
2125 sub.runFile.Close();
2126 GoToRunWriteErrorState();
2127 return;
2128 //SetCurrentState(kSM_WriteError);
2129 }
2130 }
2131}
2132#endif //if has_fits
2133// --------------------------------------------------------------------------
2134//
2135//! Go to Run Write Error State
2136// A write error has occurred. Checks what is the current state and take appropriate action
2137void DataLogger::GoToRunWriteErrorState()
2138{
2139 if ((GetCurrentState() != kSM_RunWriteError) &&
2140 (GetCurrentState() != kSM_DailyWriteError))
2141 SetCurrentState(kSM_RunWriteError);
2142}
2143// --------------------------------------------------------------------------
2144//
2145//! Go to Nightly Write Error State
2146// A write error has occurred. Checks what is the current state and take appropriate action
2147void DataLogger::GoToNightlyWriteErrorState()
2148{
2149 if (GetCurrentState() != kSM_DailyWriteError)
2150 SetCurrentState(kSM_DailyWriteError);
2151}
2152std::string DataLogger::SetCurrentState(int state, const char *txt, const std::string &cmd)
2153{
2154// if (state == kSM_WriteError && GetCurrentState() == kSM_WriteError)
2155// return "";
2156 return StateMachineImp::SetCurrentState(state, txt, cmd);
2157}
2158// --------------------------------------------------------------------------
2159//
2160//! Implements the StartRun transition.
2161//! Concatenates the given path for the run file and the filename itself (based on the run number),
2162//! and tries to open it.
2163//! @returns
2164//! kSM_Logging if success, kSM_BadRunConfig if failure.
2165int DataLogger::StartRunPlease()
2166{
2167 if (fDebugIsOn)
2168 {
2169 Debug("Starting Run Logging...");
2170 }
2171 //open all the relevant run-files. i.e. all the files associated with run numbers.
2172 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2173 if (OpenRunFile(*it) != 0)
2174 {
2175 StopRunPlease();
2176 return kSM_BadRunConfig;
2177 }
2178
2179 return kSM_Logging;
2180}
2181
2182#ifdef HAVE_FITS
2183// --------------------------------------------------------------------------
2184//
2185//! Create a fits group file with all the run-fits that were written (either daily or run)
2186//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
2187//! single file can contain several tables to group
2188//! @param runNumber the run number that should be used for grouping. 0 means nightly group
2189//
2190void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup, int runNumber)
2191{
2192 if (fDebugIsOn)
2193 {
2194 ostringstream str;
2195 str << "Creating fits group for ";
2196 if (runNumber != 0)
2197 str << "run files";
2198 else
2199 str << "nightly files";
2200 Debug(str);
2201 }
2202 //create the FITS group corresponding to the ending run.
2203 CCfits::FITS* groupFile;
2204 unsigned int numFilesToGroup = 0;
2205 unsigned int maxCharLength = 0;
2206 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
2207 {
2208 //add the number of tables in this file to the total number to group
2209 numFilesToGroup += it->second.size();
2210 //check the length of all the strings to be written, to determine the max string length to write
2211 if (it->first.size() > maxCharLength)
2212 maxCharLength = it->first.size();
2213 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++)
2214 if (jt->size() > maxCharLength)
2215 maxCharLength = jt->size();
2216 }
2217
2218 if (fDebugIsOn)
2219 {
2220 ostringstream str;
2221 str << "There are " << numFilesToGroup << " tables to group";
2222 Debug(str);
2223 }
2224 if (numFilesToGroup <= 1)
2225 {
2226 filesToGroup.clear();
2227 return;
2228 }
2229 string groupName;
2230 if (runNumber != 0)
2231 groupName = CompileFileNameWithPath(fRunFilePath, runNumber, "", "fits");
2232 else
2233 groupName = CompileFileNameWithPath(fNightlyFilePath, "", "fits");
2234
2235 Info("Creating FITS group in: "+groupName);
2236
2237 CCfits::Table* groupTable;
2238// const int maxCharLength = FILENAME_MAX;
2239 try
2240 {
2241 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
2242 //setup the column names
2243 ostringstream pathTypeName;
2244 pathTypeName << maxCharLength << "A";
2245 vector<string> names;
2246 vector<string> dataTypes;
2247 names.push_back("MEMBER_XTENSION");
2248 dataTypes.push_back("8A");
2249 names.push_back("MEMBER_URI_TYPE");
2250 dataTypes.push_back("3A");
2251 names.push_back("MEMBER_LOCATION");
2252 dataTypes.push_back(pathTypeName.str());
2253 names.push_back("MEMBER_NAME");
2254 dataTypes.push_back(pathTypeName.str());
2255
2256 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
2257//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
2258 }
2259 catch (CCfits::FitsException e)
2260 {
2261 ostringstream str;
2262 str << "Creating FITS table GROUPING in " << groupName << ": " << e.message();
2263 Error(str);
2264 return;
2265 }
2266
2267 //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.
2268 //use cfitsio routines instead
2269 groupTable->makeThisCurrent();
2270 //create appropriate buffer.
2271 const unsigned int n = 8 + 3 + 2*maxCharLength + 1; //+1 for trailling character
2272
2273 vector<unsigned char> realBuffer;
2274 realBuffer.resize(n);
2275 unsigned char* fitsBuffer = &realBuffer[0];
2276 memset(fitsBuffer, 0, n);
2277
2278 char* startOfExtension = reinterpret_cast<char*>(fitsBuffer);
2279 char* startOfURI = reinterpret_cast<char*>(&fitsBuffer[8]);
2280 char* startOfLocation = reinterpret_cast<char*>(&fitsBuffer[8 + 3]);
2281 char* startOfName = reinterpret_cast<char*>(&fitsBuffer[8+3+maxCharLength]);
2282
2283 strcpy(startOfExtension, "BINTABLE");
2284 strcpy(startOfURI, "URL");
2285
2286 int i=1;
2287 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
2288 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
2289 {
2290 strcpy(startOfLocation, it->first.c_str());
2291 strcpy(startOfName, jt->c_str());
2292
2293 if (fDebugIsOn)
2294 {
2295 ostringstream str;
2296 str << "Grouping " << it->first << " " << *jt;
2297 Debug(str);
2298 }
2299
2300 int status = 0;
2301 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength, fitsBuffer, &status);
2302 if (status)
2303 {
2304 char text[30];//max length of cfitsio error strings (from doc)
2305 fits_get_errstatus(status, text);
2306 ostringstream str;
2307 str << "Writing FITS row " << i << " in " << groupName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
2308 Error(str);
2309 GoToRunWriteErrorState();
2310 delete groupFile;
2311 return;
2312 }
2313 }
2314
2315 filesToGroup.clear();
2316 delete groupFile;
2317}
2318#endif //HAVE_FITS
2319
2320// --------------------------------------------------------------------------
2321//
2322//! Implements the StopRun transition.
2323//! Attempts to close the run file.
2324//! @returns
2325//! kSM_WaitingRun if success, kSM_FatalError otherwise
2326int DataLogger::StopRunPlease()
2327{
2328
2329 if (fDebugIsOn)
2330 {
2331 Debug("Stopping Run Logging...");
2332 }
2333 //it may be that dim tries to write a dimInfo at the same time as we're closing files. Prevent this
2334
2335 dim_lock();
2336 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2337 {
2338#ifdef RUN_LOGS
2339 if (!it->logFile->is_open() || !it->reportFile->is_open())
2340#else
2341 if (!it->reportFile->is_open())
2342#endif
2343 return kSM_FatalError;
2344#ifdef RUN_LOGS
2345 it->logFile->close();
2346 Info("Closed: "+it->logName);
2347
2348#endif
2349 it->reportFile->close();
2350 Info("Closed: "+it->reportName);
2351 }
2352
2353#ifdef HAVE_FITS
2354 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2355 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2356 {
2357 if (j->second.runFile.IsOpen())
2358 j->second.runFile.Close();
2359 }
2360#endif
2361 NotifyOpenedFile("", 0, fOpenedRunFiles);
2362 if (fNumSubAndFitsIsOn)
2363 fNumSubAndFits->updateService();
2364
2365 while (fRunNumber.size() > 0)
2366 {
2367 RemoveOldestRunNumber();
2368 }
2369 dim_unlock();
2370 return kSM_WaitingRun;
2371}
2372// --------------------------------------------------------------------------
2373//
2374//! Implements the Stop and Reset transitions.
2375//! Attempts to close any openned file.
2376//! @returns
2377//! kSM_Ready
2378int DataLogger::GoToReadyPlease()
2379{
2380 if (fDebugIsOn)
2381 {
2382 Debug("Going to the Ready state...");
2383 }
2384 if (GetCurrentState() == kSM_Logging)
2385 StopRunPlease();
2386 //it may be that dim tries to write a dimInfo while we're closing files. Prevent that
2387 dim_lock();
2388 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
2389 if (fNightlyLogFile.is_open())
2390 {
2391 fNightlyLogFile.close();
2392 Info("Closed: "+baseFileName+".log");
2393 }
2394 if (fNightlyReportFile.is_open())
2395 {
2396 fNightlyReportFile.close();
2397 Info("Closed: "+baseFileName+".rep");
2398 }
2399#ifdef HAVE_FITS
2400 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2401 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2402 {
2403 if (j->second.nightlyFile.IsOpen())
2404 j->second.nightlyFile.Close();
2405 }
2406#endif
2407 if (GetCurrentState() == kSM_Logging ||
2408 GetCurrentState() == kSM_WaitingRun ||
2409 GetCurrentState() == kSM_NightlyOpen)
2410 {
2411 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2412 if (fNumSubAndFitsIsOn)
2413 fNumSubAndFits->updateService();
2414 }
2415#ifdef HAVE_FITS
2416 CreateFitsGrouping(fOpenedNightlyFits, 0);
2417#endif
2418 dim_unlock();
2419 return kSM_Ready;
2420}
2421
2422// --------------------------------------------------------------------------
2423//
2424//! Implements the transition towards kSM_WaitingRun
2425//! Does nothing really.
2426//! @returns
2427//! kSM_WaitingRun
2428int DataLogger::NightlyToWaitRunPlease()
2429{
2430 if (fDebugIsOn)
2431 {
2432 Debug("Going to Wait Run Number state...");
2433 }
2434 return kSM_WaitingRun;
2435}
2436// --------------------------------------------------------------------------
2437//
2438//! Setup Logger's configuration from a Configuration object
2439//! @param conf the configuration object that should be used
2440//!
2441bool DataLogger::SetConfiguration(Configuration& conf)
2442{
2443 fDebugIsOn = conf.Get<bool>("debug");
2444
2445 //Set the block or allow list
2446 fBlackList.clear();
2447 fWhiteList.clear();
2448
2449 //Adding entries that should ALWAYS be ignored
2450 //fBlackList.insert("DATA_LOGGER/");
2451 fBlackList.insert("/SERVICE_LIST");
2452 fBlackList.insert("DIS_DNS/");
2453
2454 //set the black list
2455 if (conf.Has("block"))
2456 {
2457 const vector<string> vec = conf.Get<vector<string>>("block");
2458
2459 fBlackList.insert(vec.begin(), vec.end());
2460 }
2461
2462 //set the white list
2463 if (conf.Has("allow"))
2464 {
2465 const vector<string> vec = conf.Get<vector<string>>("allow");
2466 fWhiteList.insert(vec.begin(), vec.end());
2467 }
2468
2469 //Set the grouping
2470 if (conf.Has("group"))
2471 {
2472 const vector<string> vec = conf.Get<vector<string>>("group");
2473 fGrouping.insert(vec.begin(), vec.end());
2474 }
2475
2476 //set the old run numbers timeout delay
2477 if (conf.Has("runtimeout"))
2478 {
2479 const long timeout = conf.Get<long>("runtimeout");
2480 if (timeout <= 0)
2481 {
2482 Error("Time out delay for old run numbers should be greater than 0 minute");
2483 return false;
2484 }
2485 fRunNumberTimeout = timeout;
2486 }
2487 return true;
2488}
2489
2490// --------------------------------------------------------------------------
2491int RunDim(Configuration &conf)
2492{
2493 WindowLog wout;
2494
2495 ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);
2496
2497 //log.SetWindow(stdscr);
2498 if (conf.Has("log"))
2499 if (!wout.OpenLogFile(conf.Get<string>("log")))
2500 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2501
2502 // Start io_service.Run to use the StateMachineImp::Run() loop
2503 // Start io_service.run to only use the commandHandler command detaching
2504 DataLogger logger(wout);
2505 if (!logger.SetConfiguration(conf))
2506 return -1;
2507
2508 logger.Run(true);
2509
2510 return 0;
2511}
2512// --------------------------------------------------------------------------
2513void RunThread(DataLogger* logger)
2514{
2515 // This is necessary so that the StateMachine Thread can signal the
2516 // Readline thread to exit
2517 logger->Run(true);
2518 Readline::Stop();
2519}
2520// --------------------------------------------------------------------------
2521template<class T>
2522int RunShell(Configuration &conf)
2523{
2524 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
2525
2526 WindowLog &win = shell.GetStreamIn();
2527 WindowLog &wout = shell.GetStreamOut();
2528
2529 if (conf.Has("log"))
2530 if (!wout.OpenLogFile(conf.Get<string>("log")))
2531 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2532
2533 DataLogger logger(wout);
2534
2535 if (!logger.SetConfiguration(conf))
2536 return -1;
2537
2538 shell.SetReceiver(logger);
2539
2540 boost::thread t(boost::bind(RunThread, &logger));
2541
2542 if (conf.Has("cmd"))
2543 {
2544 const vector<string> v = conf.Get<vector<string>>("cmd");
2545 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2546 shell.ProcessLine(*it);
2547 }
2548
2549 if (conf.Has("exec"))
2550 {
2551 const vector<string> v = conf.Get<vector<string>>("exec");
2552 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2553 shell.Execute(*it);
2554 }
2555
2556 if (conf.Get<bool>("quit"))
2557 shell.Stop();
2558
2559
2560 shell.Run(); // Run the shell
2561
2562 logger.Stop();
2563
2564 //Wait until the StateMachine has finished its thread
2565 //before returning and destroyinng the dim objects which might
2566 //still be in use.
2567 t.join();
2568
2569 return 0;
2570}
2571
2572/*
2573 Extract usage clause(s) [if any] for SYNOPSIS.
2574 Translators: "Usage" and "or" here are patterns (regular expressions) which
2575 are used to match the usage synopsis in program output. An example from cp
2576 (GNU coreutils) which contains both strings:
2577 Usage: cp [OPTION]... [-T] SOURCE DEST
2578 or: cp [OPTION]... SOURCE... DIRECTORY
2579 or: cp [OPTION]... -t DIRECTORY SOURCE...
2580 */
2581void PrintUsage()
2582{
2583 cout << "\n"
2584 "The data logger connects to all available Dim services and "
2585 "writes them to ascii and fits files.\n"
2586 "\n"
2587 "The default is that the program is started without user interaction. "
2588 "All actions are supposed to arrive as DimCommands. Using the -c "
2589 "option, a local shell can be initialized. With h or help a short "
2590 "help message about the usage can be brought to the screen.\n"
2591 "\n"
2592 "Usage: datalogger [-c type] [OPTIONS]\n"
2593 " or: datalogger [OPTIONS]\n";
2594 cout << endl;
2595
2596}
2597// --------------------------------------------------------------------------
2598void PrintHelp()
2599{
2600 /* Additional help text which is printed after the configuration
2601 options goes here */
2602 cout <<
2603 "\n"
2604 "If the allow list has any element, only the servers and/or services "
2605 "specified in the list will be used for subscription. The black list "
2606 "will disable service subscription and has higher priority than the "
2607 "allow list. If the allow list is not present by default all services "
2608 "will be subscribed."
2609 "\n"
2610 "For example, block=DIS_DNS/ will skip all the services offered by "
2611 "the DIS_DNS server, while block=/SERVICE_LIST will skip all the "
2612 "SERVICE_LIST services offered by any server and DIS_DNS/SERVICE_LIST "
2613 "will skip DIS_DNS/SERVICE_LIST.\n"
2614 << endl;
2615}
2616
2617// --------------------------------------------------------------------------
2618void SetupConfiguration(Configuration &conf)
2619{
2620 const string n = conf.GetName()+".log";
2621
2622 po::options_description configp("Program options");
2623 configp.add_options()
2624 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
2625 ("log,l", var<string>(n), "Write log-file")
2626 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
2627 ("cmd", vars<string>(), "Execute one or more commands at startup")
2628 ("exec,e", vars<string>(), "Execute one or more scrips at startup")
2629 ("quit", po_switch(), "Quit after startup");
2630 ;
2631
2632 po::options_description configs("DataLogger options");
2633 configs.add_options()
2634 ("block,b", vars<string>(), "Black-list of services")
2635 ("allow,a", vars<string>(), "White-list of services")
2636 ("debug", po_bool(), "Debug mode. Print clear text of received service reports to log-stream")
2637 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2638 ("runtimeout", var<long>(), "Time out delay for old run numbers")
2639 ;
2640
2641 conf.AddEnv("dns", "DIM_DNS_NODE");
2642
2643 conf.AddOptions(configp);
2644 conf.AddOptions(configs);
2645}
2646// --------------------------------------------------------------------------
2647int main(int argc, const char* argv[])
2648{
2649 Configuration conf(argv[0]);
2650 conf.SetPrintUsage(PrintUsage);
2651 SetupConfiguration(conf);
2652
2653 po::variables_map vm;
2654 try
2655 {
2656 vm = conf.Parse(argc, argv);
2657 }
2658#if BOOST_VERSION > 104000
2659 catch (po::multiple_occurrences &e)
2660 {
2661 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
2662 return -1;
2663 }
2664#endif
2665 catch (exception& e)
2666 {
2667 cerr << "Program options invalid due to: " << e.what() << endl;
2668 return -1;
2669 }
2670
2671 if (conf.HasVersion() || conf.HasPrint())
2672 return -1;
2673
2674 if (conf.HasHelp())
2675 {
2676 PrintHelp();
2677 return -1;
2678 }
2679
2680 Dim::Setup(conf.Get<string>("dns"));
2681
2682// try
2683 {
2684 // No console access at all
2685 if (!conf.Has("console"))
2686 return RunDim(conf);
2687
2688 // Console access w/ and w/o Dim
2689 if (conf.Get<int>("console")==0)
2690 return RunShell<LocalShell>(conf);
2691 else
2692 return RunShell<LocalConsole>(conf);
2693 }
2694/* catch (exception& e)
2695 {
2696 cerr << "Exception: " << e.what() << endl;
2697 return -1;
2698 }*/
2699
2700 return 0;
2701}
Note: See TracBrowser for help on using the repository browser.