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

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