source: trunk/FACT++/src/dataLogger.cc@ 10983

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