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

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