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

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