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

Last change on this file since 11290 was 11290, checked in by lyard, 13 years ago
Moved the files statistics to a separate class and improved transitions
File size: 97.1 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 return GetCurrentState();
1699}
1700// --------------------------------------------------------------------------
1701//
1702//! set the statistics update period duration. 0 disables the statistics
1703//! @param evt
1704//! the current event. contains the new duration.
1705//! @returns
1706//! the new state. Which, in that case, is the current state
1707//!
1708int DataLogger::SetStatsPeriod(const Event& evt)
1709{
1710 const float backupDuration = fStatsPeriodDuration;
1711
1712 fStatsPeriodDuration = evt.GetFloat();
1713
1714 if (fStatsPeriodDuration < 0)
1715 {
1716 Error("Statistics period duration should be greater than zero. Discarding provided value.");
1717 fStatsPeriodDuration = backupDuration;
1718 return GetCurrentState();
1719 }
1720 if (!finite(fStatsPeriodDuration))// != fStatsPeriodDuration)
1721 {
1722 Error("Provided duration does not appear to be a valid float. Discarding it.");
1723 fStatsPeriodDuration = backupDuration;
1724 return GetCurrentState();
1725 }
1726 if (backupDuration == fStatsPeriodDuration)
1727 Warn("Statistics period not modified. Supplied value already in use.");
1728
1729 if (fStatsPeriodDuration == 0.0f)
1730 Message("Statistics are now OFF");
1731 else
1732 {
1733 ostringstream str;
1734 str << "Statistics period is now " << fStatsPeriodDuration << " seconds";
1735 Message(str);
1736 }
1737
1738 return GetCurrentState();
1739}
1740// --------------------------------------------------------------------------
1741//
1742//! set the opened files service on or off.
1743//! @param evt
1744//! the current event. contains the instruction string. similar to setdebugonoff
1745//! @returns
1746//! the new state. Which, in that case, is the current state
1747//!
1748int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1749{
1750 const bool backupOpened = fOpenedFilesIsOn;
1751
1752 fOpenedFilesIsOn = evt.GetBool();
1753
1754 if (fOpenedFilesIsOn == backupOpened)
1755 Message("Opened files service mode was already in the requested state.");
1756
1757 ostringstream str;
1758 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1759 Message(str);
1760
1761 return GetCurrentState();
1762}
1763
1764// --------------------------------------------------------------------------
1765//
1766//! set the number of subscriptions and opened fits on and off
1767//! @param evt
1768//! the current event. contains the instruction string. similar to setdebugonoff
1769//! @returns
1770//! the new state. Which, in that case, is the current state
1771//!
1772int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1773{
1774 const bool backupSubs = fNumSubAndFitsIsOn;
1775
1776 fNumSubAndFitsIsOn = evt.GetBool();
1777
1778 if (fNumSubAndFitsIsOn == backupSubs)
1779 Message("Number of subscriptions service mode was already in the requested state");
1780
1781 ostringstream str;
1782 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1783 Message(str);
1784
1785 return GetCurrentState();
1786}
1787// --------------------------------------------------------------------------
1788//
1789//! set the timeout delay for old run numbers
1790//! @param evt
1791//! the current event. contains the timeout delay long value
1792//! @returns
1793//! the new state. Which, in that case, is the current state
1794//!
1795int DataLogger::SetRunTimeoutDelay(const Event& evt)
1796{
1797 const long backupTimeout = fRunNumberTimeout;
1798 fRunNumberTimeout = evt.GetXtra();
1799
1800 if (fRunNumberTimeout == 0)
1801 {
1802 fRunNumberTimeout = backupTimeout;
1803 Error("Timeout delays for old run numbers must be greater than 0. Ignored.");
1804 return GetCurrentState();
1805 }
1806
1807 if (fRunNumberTimeout == backupTimeout)
1808 Message("New timeout for old run numbers is same value as previous one.");
1809
1810 ostringstream str;
1811 str << "Timeout delay for old run numbers is now " << fRunNumberTimeout << " seconds";
1812 Message(str);
1813
1814 return GetCurrentState();
1815}
1816
1817// --------------------------------------------------------------------------
1818//
1819//! Configure a given file name
1820//! @param target
1821//! where to put the result
1822//! @param type
1823//! what to append to the created path. currently only run or nightly
1824//! @param evt
1825//! the event transporting the path
1826//! @returns
1827//! currently only the current state.
1828//
1829int DataLogger::ConfigureFileName(string &target, const string &type, const EventImp &evt)
1830{
1831 if (!evt.GetText())
1832 {
1833 Error("Empty "+type+" folder given. Please specify a valid path.");
1834 return GetCurrentState();
1835 }
1836
1837 const string givenPath = evt.GetText();
1838 if (!DoesPathExist(givenPath))
1839 {
1840 Error("Provided "+type+" path '"+givenPath+"' is not a valid folder. Ignored");
1841 return GetCurrentState();
1842 }
1843
1844 Message("New "+type+" folder: "+givenPath);
1845
1846 target = givenPath;
1847
1848 return GetCurrentState();
1849}
1850
1851// --------------------------------------------------------------------------
1852//
1853//! Sets the path to use for the Nightly log file.
1854//! @param evt
1855//! the event transporting the path
1856//! @returns
1857//! currently only the current state.
1858//
1859int DataLogger::ConfigureNightlyFileName(const Event& evt)
1860{
1861 return ConfigureFileName(fNightlyFilePath, "nightly", evt);
1862}
1863
1864// --------------------------------------------------------------------------
1865//
1866//! Sets the path to use for the run log file.
1867//! @param evt
1868//! the event transporting the path
1869//! @returns
1870//! currently only the current state
1871int DataLogger::ConfigureRunFileName(const Event& evt)
1872{
1873 return ConfigureFileName(fRunFilePath, "run", evt);
1874}
1875
1876// --------------------------------------------------------------------------
1877//
1878//! Sets the run number.
1879//! @param evt
1880//! the event transporting the run number
1881//! @returns
1882//! currently only the current state
1883int DataLogger::ConfigureRunNumber(const Event& evt)
1884{
1885 AddNewRunNumber(evt.GetXtra(), evt.GetTime());
1886 return GetCurrentState();
1887}
1888// --------------------------------------------------------------------------
1889//
1890//! Notifies the DIM service that a particular file was opened
1891//! @ param name the base name of the opened file, i.e. without path nor extension.
1892//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1893//! this is not a problem though because file are not opened so often.
1894//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1895inline void DataLogger::NotifyOpenedFile(const string &name, int type, DimDescribedService* service)
1896{
1897 if (!fOpenedFilesIsOn)
1898 return;
1899
1900 if (fDebugIsOn)
1901 {
1902 ostringstream str;
1903 str << "Updating " << service->getName() << " file '" << name << "' (type=" << type << ")";
1904 Debug(str);
1905
1906 str.str("");
1907 str << "Num subscriptions: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS files: " << fNumSubAndFitsData.numOpenFits;
1908 Debug(str);
1909 }
1910
1911 if (name.size()+1 > FILENAME_MAX)
1912 {
1913 Error("Provided file name '" + name + "' is longer than allowed file name length.");
1914 return;
1915 }
1916
1917 OpenFileToDim fToDim;
1918 fToDim.code = type;
1919 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1920
1921 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(int));
1922 service->setQuality(0);
1923 service->updateService();
1924}
1925// --------------------------------------------------------------------------
1926//
1927//! Implements the Start transition.
1928//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1929//! and tries to open it.
1930//! @returns
1931//! kSM_NightlyOpen if success, kSM_BadNightlyConfig if failure
1932int DataLogger::StartPlease()
1933{
1934 if (fDebugIsOn)
1935 {
1936 Debug("Starting...");
1937 }
1938 fFullNightlyLogFileName = CompileFileNameWithPath(fNightlyFilePath, "", "log");
1939 if (!OpenTextFilePlease(fNightlyLogFile, fFullNightlyLogFileName))
1940 return kSM_BadNightlyConfig;
1941
1942
1943 fFullNightlyReportFileName = CompileFileNameWithPath(fNightlyFilePath, "", "rep");
1944 if (!OpenTextFilePlease(fNightlyReportFile, fFullNightlyReportFileName))
1945 {
1946 fNightlyLogFile.close();
1947 Info("Closed: "+fFullNightlyReportFileName);
1948 return kSM_BadNightlyConfig;
1949 }
1950 //get the size of the newly opened file.
1951// fBaseSizeNightly = GetFileSize(fFullNightlyLogFileName);
1952// fBaseSizeNightly += GetFileSize(fFullNightlyReportFileName);
1953// fFileSizesMap.clear();
1954// fBaseSizeRun = 0;
1955// fPreviousSize = 0;
1956 fFilesStats.Reset();
1957 fFilesStats.FileOpened(fFullNightlyLogFileName);
1958 fFilesStats.FileOpened(fFullNightlyReportFileName);
1959 //notify that a new file has been opened.
1960 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
1961 NotifyOpenedFile(baseFileName, 3, fOpenedNightlyFiles);
1962
1963 fOpenedNightlyFits.clear();
1964
1965 return kSM_NightlyOpen;
1966}
1967
1968#ifdef HAVE_FITS
1969// --------------------------------------------------------------------------
1970//
1971//! open if required a the FITS files corresponding to a given subscription
1972//! @param sub
1973//! the current DimInfo subscription being examined
1974void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub, RunNumberType* cRunNumber)
1975{
1976 string serviceName(sub.dimInfo->getName());
1977
1978 //if run number has changed, reopen a new fits file with the correct run number.
1979 if (sub.runFile.IsOpen() && sub.runFile.fRunNumber != sub.runNumber)
1980 {
1981 sub.runFile.Close();
1982 Info("Closed: "+sub.runFile.fFileName+" (new run number)");
1983 }
1984
1985 //we must check if we should group this service subscription to a single fits file before we replace the / by _
1986 bool hasGrouping = false;
1987 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1988 {//will we find this service in the grouping list ?
1989 for (set<string>::const_iterator it=fGrouping.begin(); it!=fGrouping.end(); it++)
1990 {
1991 if (serviceName.find(*it) != string::npos)
1992 {
1993 hasGrouping = true;
1994 break;
1995 }
1996 }
1997 }
1998 for (unsigned int i=0;i<serviceName.size(); i++)
1999 {
2000 if (serviceName[i] == '/')
2001 {
2002 serviceName[i] = '_';
2003 break;
2004 }
2005 }
2006 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
2007 if (!sub.nightlyFile.IsOpen())
2008 {
2009 string partialName = CompileFileNameWithPath(fNightlyFilePath, serviceName, "fits");
2010
2011 const string fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
2012 if (!sub.fitsBufferAllocated)
2013 AllocateFITSBuffers(sub);
2014 //get the size of the file we're about to open
2015 if (fFilesStats.FileOpened(partialName))
2016// if (RememberFileOrigSizePlease(partialName, true))//and remember that the file was opened (i.e. not an update)
2017 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
2018
2019 ostringstream str;
2020 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
2021 Info(str);
2022
2023 if (!sub.nightlyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, 0))
2024 {
2025 GoToRunWriteErrorState();
2026 //SetCurrentState(kSM_WriteError);
2027 return;
2028 }
2029 //notify the opening
2030 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
2031 NotifyOpenedFile(baseFileName, 7, fOpenedNightlyFiles);
2032 if (fNumSubAndFitsIsOn)
2033 fNumSubAndFits->updateService();
2034 }
2035 //do the actual file open
2036 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging) && sub.runNumber != 0)
2037 {//buffer for the run file have already been allocated when doing the Nightly file
2038 string fileNameOnly;
2039 string partialName;
2040 if (hasGrouping)
2041 {
2042 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "fits");
2043 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
2044 }
2045 else
2046 {
2047 partialName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, serviceName, "fits");
2048 fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
2049 }
2050 //get the size of the file we're about to open
2051 if (fFilesStats.FileOpened(partialName))
2052// if (RememberFileOrigSizePlease(partialName, false))//and remember that the file was opened (i.e. not an update)
2053 cRunNumber->openedFits[fileNameOnly].push_back(serviceName);
2054 else
2055 if (hasGrouping)
2056 {
2057 cRunNumber->addServiceToOpenedFits(fileNameOnly, serviceName);
2058 }
2059
2060 if (hasGrouping && (!cRunNumber->runFitsFile.get()))
2061 try
2062 {
2063 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>(new CCfits::FITS(partialName, CCfits::RWmode::Write));
2064 (fNumSubAndFitsData.numOpenFits)++;
2065 }
2066 catch (CCfits::FitsException e)
2067 {
2068 ostringstream str;
2069 str << "Open FITS file " << partialName << ": " << e.message();
2070 Error(str);
2071 cRunNumber->runFitsFile = shared_ptr<CCfits::FITS>();//NULL;
2072 GoToRunWriteErrorState();
2073 //SetCurrentState(kSM_WriteError);
2074 return;
2075 }
2076
2077 const string baseFileName = CompileFileNameWithPath(fRunFilePath, sub.runNumber, "", "");
2078 NotifyOpenedFile(baseFileName, 7, fOpenedRunFiles);// + '_' + serviceName, 4);
2079
2080 ostringstream str;
2081 str << "Opening: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
2082 Info(str);
2083
2084 if (hasGrouping)
2085 {
2086 if (!sub.runFile.Open(partialName, serviceName, (cRunNumber->runFitsFile).get(), &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2087 {
2088 GoToRunWriteErrorState();
2089 //SetCurrentState(kSM_WriteError);
2090 return;
2091 }
2092 }
2093 else
2094 {
2095 if (!sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, this, sub.runNumber))
2096 {
2097 GoToRunWriteErrorState();
2098 //SetCurrentState(kSM_WriteError);
2099 return;
2100 }
2101 }
2102 if (fNumSubAndFitsIsOn)
2103 fNumSubAndFits->updateService();
2104 }
2105}
2106// --------------------------------------------------------------------------
2107//
2108//! Allocates the required memory for a given pair of fits files (nightly and run)
2109//! @param sub the subscription of interest.
2110//
2111void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
2112{
2113 //Init the time columns of the file
2114 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MjD"));
2115 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2116 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
2117
2118 Description QoSDesc("Qos", "Quality of service", "None");
2119 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2120 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
2121
2122 const Converter::FormatList flist = sub.fConv->GetList();
2123 // Compilation failed
2124 if (!sub.fConv->valid())
2125 {
2126 Error("Compilation of format string failed.");
2127 return;
2128 }
2129
2130 //we've got a nice structure describing the format of this service's messages.
2131 //Let's create the appropriate FITS columns
2132 int size = 0;
2133
2134 vector<string> dataFormatsLocal;
2135 for (unsigned int i=0;i<flist.size()-1;i++)
2136 {
2137 ostringstream dataQualifier;
2138
2139 dataQualifier << flist[i].second.first;
2140 switch (flist[i].first.first->name()[0])
2141 {
2142 case 'c': dataQualifier << 'B'; break;
2143 case 's': dataQualifier << 'I'; break;
2144 case 'i': dataQualifier << 'J'; break;
2145 case 'l': dataQualifier << 'J'; break;
2146 case 'f': dataQualifier << 'E'; break;
2147 case 'd': dataQualifier << 'D'; break;
2148 case 'x': dataQualifier << 'K'; break;
2149 case 'S': //we skip the variable length strings
2150 continue;
2151
2152 default:
2153 Fatal("THIS SHOULD NEVER BE REACHED.");
2154 };
2155
2156 size += flist[i].first.second * flist[i].second.first;
2157 dataFormatsLocal.push_back(dataQualifier.str());
2158 }
2159 sub.nightlyFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2160 sub.runFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, sub.dimInfo->getData(), size);
2161 sub.fitsBufferAllocated = true;
2162}
2163// --------------------------------------------------------------------------
2164//
2165//! write a dimInfo data to its corresponding FITS files
2166//
2167void DataLogger::WriteToFITS(SubscriptionType& sub)
2168{
2169 //nightly File status (open or not) already checked
2170 if (sub.nightlyFile.IsOpen())
2171 {
2172 if (!sub.nightlyFile.Write(sub.fConv.get()))
2173 {
2174 sub.nightlyFile.Close();
2175 GoToNightlyWriteErrorState();
2176 return;
2177 //SetCurrentState(kSM_WriteError);
2178 }
2179 }
2180
2181 if (sub.runFile.IsOpen())
2182 {
2183 if (!sub.runFile.Write(sub.fConv.get()))
2184 {
2185 sub.runFile.Close();
2186 GoToRunWriteErrorState();
2187 return;
2188 //SetCurrentState(kSM_WriteError);
2189 }
2190 }
2191}
2192#endif //if has_fits
2193// --------------------------------------------------------------------------
2194//
2195//! Go to Run Write Error State
2196// A write error has occurred. Checks what is the current state and take appropriate action
2197void DataLogger::GoToRunWriteErrorState()
2198{
2199 if ((GetCurrentState() != kSM_RunWriteError) &&
2200 (GetCurrentState() != kSM_DailyWriteError))
2201 SetCurrentState(kSM_RunWriteError);
2202}
2203// --------------------------------------------------------------------------
2204//
2205//! Go to Nightly Write Error State
2206// A write error has occurred. Checks what is the current state and take appropriate action
2207void DataLogger::GoToNightlyWriteErrorState()
2208{
2209 if (GetCurrentState() != kSM_DailyWriteError)
2210 SetCurrentState(kSM_DailyWriteError);
2211}
2212
2213// --------------------------------------------------------------------------
2214//
2215//! Implements the StartRun transition.
2216//! Concatenates the given path for the run file and the filename itself (based on the run number),
2217//! and tries to open it.
2218//! @returns
2219//! kSM_Logging if success, kSM_BadRunConfig if failure.
2220int DataLogger::StartRunPlease()
2221{
2222 if (fDebugIsOn)
2223 {
2224 Debug("Starting Run Logging...");
2225 }
2226 //open all the relevant run-files. i.e. all the files associated with run numbers.
2227 for (list<RunNumberType>::iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2228 if (OpenRunFile(*it) != 0)
2229 {
2230 StopRunPlease();
2231 return kSM_BadRunConfig;
2232 }
2233
2234 return kSM_Logging;
2235}
2236
2237#ifdef HAVE_FITS
2238// --------------------------------------------------------------------------
2239//
2240//! Create a fits group file with all the run-fits that were written (either daily or run)
2241//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
2242//! single file can contain several tables to group
2243//! @param runNumber the run number that should be used for grouping. 0 means nightly group
2244//
2245void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup, int runNumber)
2246{
2247 if (fDebugIsOn)
2248 {
2249 ostringstream str;
2250 str << "Creating fits group for ";
2251 if (runNumber != 0)
2252 str << "run files";
2253 else
2254 str << "nightly files";
2255 Debug(str);
2256 }
2257 //create the FITS group corresponding to the ending run.
2258 CCfits::FITS* groupFile;
2259 unsigned int numFilesToGroup = 0;
2260 unsigned int maxCharLength = 0;
2261 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
2262 {
2263 //add the number of tables in this file to the total number to group
2264 numFilesToGroup += it->second.size();
2265 //check the length of all the strings to be written, to determine the max string length to write
2266 if (it->first.size() > maxCharLength)
2267 maxCharLength = it->first.size();
2268 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++)
2269 if (jt->size() > maxCharLength)
2270 maxCharLength = jt->size();
2271 }
2272
2273 if (fDebugIsOn)
2274 {
2275 ostringstream str;
2276 str << "There are " << numFilesToGroup << " tables to group";
2277 Debug(str);
2278 }
2279 if (numFilesToGroup <= 1)
2280 {
2281 filesToGroup.clear();
2282 return;
2283 }
2284 string groupName;
2285 if (runNumber != 0)
2286 groupName = CompileFileNameWithPath(fRunFilePath, runNumber, "", "fits");
2287 else
2288 groupName = CompileFileNameWithPath(fNightlyFilePath, "", "fits");
2289
2290 Info("Creating FITS group in: "+groupName);
2291
2292 CCfits::Table* groupTable;
2293// const int maxCharLength = FILENAME_MAX;
2294 try
2295 {
2296 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
2297 //setup the column names
2298 ostringstream pathTypeName;
2299 pathTypeName << maxCharLength << "A";
2300 vector<string> names;
2301 vector<string> dataTypes;
2302 names.push_back("MEMBER_XTENSION");
2303 dataTypes.push_back("8A");
2304 names.push_back("MEMBER_URI_TYPE");
2305 dataTypes.push_back("3A");
2306 names.push_back("MEMBER_LOCATION");
2307 dataTypes.push_back(pathTypeName.str());
2308 names.push_back("MEMBER_NAME");
2309 dataTypes.push_back(pathTypeName.str());
2310
2311 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
2312//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
2313 }
2314 catch (CCfits::FitsException e)
2315 {
2316 ostringstream str;
2317 str << "Creating FITS table GROUPING in " << groupName << ": " << e.message();
2318 Error(str);
2319 return;
2320 }
2321
2322 //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.
2323 //use cfitsio routines instead
2324 groupTable->makeThisCurrent();
2325 //create appropriate buffer.
2326 const unsigned int n = 8 + 3 + 2*maxCharLength + 1; //+1 for trailling character
2327
2328 vector<unsigned char> realBuffer;
2329 realBuffer.resize(n);
2330 unsigned char* fitsBuffer = &realBuffer[0];
2331 memset(fitsBuffer, 0, n);
2332
2333 char* startOfExtension = reinterpret_cast<char*>(fitsBuffer);
2334 char* startOfURI = reinterpret_cast<char*>(&fitsBuffer[8]);
2335 char* startOfLocation = reinterpret_cast<char*>(&fitsBuffer[8 + 3]);
2336 char* startOfName = reinterpret_cast<char*>(&fitsBuffer[8+3+maxCharLength]);
2337
2338 strcpy(startOfExtension, "BINTABLE");
2339 strcpy(startOfURI, "URL");
2340
2341 int i=1;
2342 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
2343 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
2344 {
2345 strcpy(startOfLocation, it->first.c_str());
2346 strcpy(startOfName, jt->c_str());
2347
2348 if (fDebugIsOn)
2349 {
2350 ostringstream str;
2351 str << "Grouping " << it->first << " " << *jt;
2352 Debug(str);
2353 }
2354
2355 int status = 0;
2356 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength, fitsBuffer, &status);
2357 if (status)
2358 {
2359 char text[30];//max length of cfitsio error strings (from doc)
2360 fits_get_errstatus(status, text);
2361 ostringstream str;
2362 str << "Writing FITS row " << i << " in " << groupName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
2363 Error(str);
2364 GoToRunWriteErrorState();
2365 delete groupFile;
2366 return;
2367 }
2368 }
2369
2370 filesToGroup.clear();
2371 delete groupFile;
2372}
2373#endif //HAVE_FITS
2374
2375// --------------------------------------------------------------------------
2376//
2377//! Implements the StopRun transition.
2378//! Attempts to close the run file.
2379//! @returns
2380//! kSM_WaitingRun if success, kSM_FatalError otherwise
2381int DataLogger::StopRunPlease()
2382{
2383
2384 if (fDebugIsOn)
2385 {
2386 Debug("Stopping Run Logging...");
2387 }
2388 //it may be that dim tries to write a dimInfo at the same time as we're closing files. Prevent this
2389
2390// dim_lock();
2391 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it != fRunNumber.end(); it++)
2392 {
2393#ifdef RUN_LOGS
2394 if (!it->logFile->is_open() || !it->reportFile->is_open())
2395#else
2396 if (!it->reportFile->is_open())
2397#endif
2398 return kSM_FatalError;
2399#ifdef RUN_LOGS
2400 it->logFile->close();
2401 Info("Closed: "+it->logName);
2402
2403#endif
2404 it->reportFile->close();
2405 Info("Closed: "+it->reportName);
2406 }
2407
2408#ifdef HAVE_FITS
2409 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2410 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2411 {
2412 if (j->second.runFile.IsOpen())
2413 j->second.runFile.Close();
2414 }
2415#endif
2416 NotifyOpenedFile("", 0, fOpenedRunFiles);
2417 if (fNumSubAndFitsIsOn)
2418 fNumSubAndFits->updateService();
2419
2420 while (fRunNumber.size() > 0)
2421 {
2422 RemoveOldestRunNumber();
2423 }
2424// dim_unlock();
2425 return kSM_WaitingRun;
2426}
2427// --------------------------------------------------------------------------
2428//
2429//! Implements the Stop and Reset transitions.
2430//! Attempts to close any openned file.
2431//! @returns
2432//! kSM_Ready
2433int DataLogger::GoToReadyPlease()
2434{
2435 if (fDebugIsOn)
2436 {
2437 Debug("Going to the Ready state...");
2438 }
2439 if (GetCurrentState() == kSM_Logging)
2440 StopRunPlease();
2441 //it may be that dim tries to write a dimInfo while we're closing files. Prevent that
2442// dim_lock();
2443 const string baseFileName = CompileFileNameWithPath(fNightlyFilePath, "", "");
2444 if (fNightlyLogFile.is_open())
2445 {
2446 fNightlyLogFile.close();
2447 Info("Closed: "+baseFileName+".log");
2448 }
2449 if (fNightlyReportFile.is_open())
2450 {
2451 fNightlyReportFile.close();
2452 Info("Closed: "+baseFileName+".rep");
2453 }
2454#ifdef HAVE_FITS
2455 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2456 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2457 {
2458 if (j->second.nightlyFile.IsOpen())
2459 j->second.nightlyFile.Close();
2460 }
2461#endif
2462 if (GetCurrentState() == kSM_Logging ||
2463 GetCurrentState() == kSM_WaitingRun ||
2464 GetCurrentState() == kSM_NightlyOpen)
2465 {
2466 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2467 if (fNumSubAndFitsIsOn)
2468 fNumSubAndFits->updateService();
2469 }
2470#ifdef HAVE_FITS
2471 CreateFitsGrouping(fOpenedNightlyFits, 0);
2472#endif
2473// dim_unlock();
2474 return kSM_Ready;
2475}
2476
2477// --------------------------------------------------------------------------
2478//
2479//! Implements the transition towards kSM_WaitingRun
2480//! If current state is kSM_Ready, then tries to go to nightlyOpen state first.
2481//! @returns
2482//! kSM_WaitingRun or kSM_badNightlyConfig
2483int DataLogger::NightlyToWaitRunPlease()
2484{
2485 int cState = GetCurrentState();
2486 if (cState == kSM_Ready)
2487 cState = StartPlease();
2488
2489 if (cState != kSM_NightlyOpen)
2490 return GetCurrentState();
2491
2492 if (fDebugIsOn)
2493 {
2494 Debug("Going to Wait Run Number state...");
2495 }
2496 return kSM_WaitingRun;
2497}
2498// --------------------------------------------------------------------------
2499//
2500//! Implements the transition from wait for run number to nightly open
2501//! Does nothing really.
2502//! @returns
2503//! kSM_WaitingRun
2504int DataLogger::BackToNightlyOpenPlease()
2505{
2506 if (fDebugIsOn)
2507 {
2508 Debug("Going to NightlyOpen state...");
2509 }
2510 return kSM_NightlyOpen;
2511}
2512// --------------------------------------------------------------------------
2513//
2514//! Setup Logger's configuration from a Configuration object
2515//! @param conf the configuration object that should be used
2516//!
2517int DataLogger::EvalConfiguration(const Configuration& conf)
2518{
2519 fDebugIsOn = conf.Get<bool>("debug");
2520
2521 //Set the block or allow list
2522 fBlackList.clear();
2523 fWhiteList.clear();
2524
2525 //Adding entries that should ALWAYS be ignored
2526 //fBlackList.insert("DATA_LOGGER/");
2527 fBlackList.insert("/SERVICE_LIST");
2528 fBlackList.insert("DIS_DNS/");
2529
2530 //set the black list
2531 if (conf.Has("block"))
2532 {
2533 const vector<string> vec = conf.Get<vector<string>>("block");
2534
2535 fBlackList.insert(vec.begin(), vec.end());
2536 }
2537
2538 //set the white list
2539 if (conf.Has("allow"))
2540 {
2541 const vector<string> vec = conf.Get<vector<string>>("allow");
2542 fWhiteList.insert(vec.begin(), vec.end());
2543 }
2544
2545 //Set the grouping
2546 if (conf.Has("group"))
2547 {
2548 const vector<string> vec = conf.Get<vector<string>>("group");
2549 fGrouping.insert(vec.begin(), vec.end());
2550 }
2551
2552 //set the old run numbers timeout delay
2553 if (conf.Has("runtimeout"))
2554 {
2555 const long timeout = conf.Get<long>("runtimeout");
2556 if (timeout <= 0)
2557 {
2558 Error("Time out delay for old run numbers should be greater than 0 minute");
2559 return 1;
2560 }
2561 fRunNumberTimeout = timeout;
2562 }
2563 return -1;
2564}
2565
2566// --------------------------------------------------------------------------
2567/*
2568 int RunDim(Configuration &conf)
2569{
2570 WindowLog wout;
2571
2572 ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);
2573
2574 //log.SetWindow(stdscr);
2575 if (conf.Has("log"))
2576 if (!wout.OpenLogFile(conf.Get<string>("log")))
2577 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2578
2579 // Start io_service.Run to use the StateMachineImp::Run() loop
2580 // Start io_service.run to only use the commandHandler command detaching
2581 DataLogger logger(wout);
2582 if (!logger.EvalConfiguration(conf))
2583 return -1;
2584
2585 logger.Run(true);
2586
2587 return 0;
2588}
2589*/
2590// --------------------------------------------------------------------------
2591
2592#include "Main.h"
2593/*
2594void RunThread(DataLogger* logger)
2595{
2596 // This is necessary so that the StateMachine Thread can signal the
2597 // Readline thread to exit
2598 logger->Run(true);
2599 Readline::Stop();
2600}
2601*/
2602// --------------------------------------------------------------------------
2603template<class T>
2604int RunShell(Configuration &conf)
2605{
2606 return Main<T, DataLogger>(conf, true);
2607/*
2608 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
2609
2610 WindowLog &win = shell.GetStreamIn();
2611 WindowLog &wout = shell.GetStreamOut();
2612
2613 if (conf.Has("log"))
2614 if (!wout.OpenLogFile(conf.Get<string>("log")))
2615 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;
2616
2617 DataLogger logger(wout);
2618
2619 if (!logger.EvalConfiguration(conf))
2620 return -1;
2621
2622 shell.SetReceiver(logger);
2623
2624 boost::thread t(boost::bind(RunThread, &logger));
2625
2626 if (conf.Has("cmd"))
2627 {
2628 const vector<string> v = conf.Get<vector<string>>("cmd");
2629 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2630 shell.ProcessLine(*it);
2631 }
2632
2633 if (conf.Has("exec"))
2634 {
2635 const vector<string> v = conf.Get<vector<string>>("exec");
2636 for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
2637 shell.Execute(*it);
2638 }
2639
2640 if (conf.Get<bool>("quit"))
2641 shell.Stop();
2642
2643
2644 shell.Run(); // Run the shell
2645
2646 logger.Stop();
2647
2648 //Wait until the StateMachine has finished its thread
2649 //before returning and destroyinng the dim objects which might
2650 //still be in use.
2651 t.join();
2652
2653 return 0;
2654*/
2655}
2656
2657/*
2658 Extract usage clause(s) [if any] for SYNOPSIS.
2659 Translators: "Usage" and "or" here are patterns (regular expressions) which
2660 are used to match the usage synopsis in program output. An example from cp
2661 (GNU coreutils) which contains both strings:
2662 Usage: cp [OPTION]... [-T] SOURCE DEST
2663 or: cp [OPTION]... SOURCE... DIRECTORY
2664 or: cp [OPTION]... -t DIRECTORY SOURCE...
2665 */
2666void PrintUsage()
2667{
2668 cout << "\n"
2669 "The data logger connects to all available Dim services and "
2670 "writes them to ascii and fits files.\n"
2671 "\n"
2672 "The default is that the program is started without user interaction. "
2673 "All actions are supposed to arrive as DimCommands. Using the -c "
2674 "option, a local shell can be initialized. With h or help a short "
2675 "help message about the usage can be brought to the screen.\n"
2676 "\n"
2677 "Usage: datalogger [-c type] [OPTIONS]\n"
2678 " or: datalogger [OPTIONS]\n";
2679 cout << endl;
2680
2681}
2682// --------------------------------------------------------------------------
2683void PrintHelp()
2684{
2685 /* Additional help text which is printed after the configuration
2686 options goes here */
2687 cout <<
2688 "\n"
2689 "If the allow list has any element, only the servers and/or services "
2690 "specified in the list will be used for subscription. The black list "
2691 "will disable service subscription and has higher priority than the "
2692 "allow list. If the allow list is not present by default all services "
2693 "will be subscribed."
2694 "\n"
2695 "For example, block=DIS_DNS/ will skip all the services offered by "
2696 "the DIS_DNS server, while block=/SERVICE_LIST will skip all the "
2697 "SERVICE_LIST services offered by any server and DIS_DNS/SERVICE_LIST "
2698 "will skip DIS_DNS/SERVICE_LIST.\n"
2699 << endl;
2700}
2701
2702// --------------------------------------------------------------------------
2703void SetupConfiguration(Configuration &conf)
2704{
2705 const string n = conf.GetName()+".log";
2706
2707 po::options_description configp("Program options");
2708 configp.add_options()
2709 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
2710 ("log,l", var<string>(n), "Write log-file")
2711 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
2712 ("cmd", vars<string>(), "Execute one or more commands at startup")
2713 ("exec,e", vars<string>(), "Execute one or more scrips at startup")
2714 ("quit", po_switch(), "Quit after startup");
2715 ;
2716
2717 po::options_description configs("DataLogger options");
2718 configs.add_options()
2719 ("block,b", vars<string>(), "Black-list of services")
2720 ("allow,a", vars<string>(), "White-list of services")
2721 ("debug", po_bool(), "Debug mode. Print clear text of received service reports to log-stream")
2722 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2723 ("runtimeout", var<long>(), "Time out delay for old run numbers")
2724 ;
2725
2726 conf.AddEnv("dns", "DIM_DNS_NODE");
2727
2728 conf.AddOptions(configp);
2729 conf.AddOptions(configs);
2730}
2731// --------------------------------------------------------------------------
2732int main(int argc, const char* argv[])
2733{
2734 Configuration conf(argv[0]);
2735 conf.SetPrintUsage(PrintUsage);
2736 SetupConfiguration(conf);
2737
2738 po::variables_map vm;
2739 try
2740 {
2741 vm = conf.Parse(argc, argv);
2742 }
2743#if BOOST_VERSION > 104000
2744 catch (po::multiple_occurrences &e)
2745 {
2746 cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
2747 return -1;
2748 }
2749#endif
2750 catch (exception& e)
2751 {
2752 cerr << "Program options invalid due to: " << e.what() << endl;
2753 return -1;
2754 }
2755
2756 if (conf.HasVersion() || conf.HasPrint())
2757 return -1;
2758
2759 if (conf.HasHelp())
2760 {
2761 PrintHelp();
2762 return -1;
2763 }
2764
2765 Dim::Setup(conf.Get<string>("dns"));
2766
2767// try
2768 {
2769 // No console access at all
2770 if (!conf.Has("console"))
2771 return RunShell<LocalStream>(conf);
2772
2773 // Console access w/ and w/o Dim
2774 if (conf.Get<int>("console")==0)
2775 return RunShell<LocalShell>(conf);
2776 else
2777 return RunShell<LocalConsole>(conf);
2778 }
2779/* catch (exception& e)
2780 {
2781 cerr << "Exception: " << e.what() << endl;
2782 return -1;
2783 }*/
2784
2785 return 0;
2786}
Note: See TracBrowser for help on using the repository browser.