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

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