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

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