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

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