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

Last change on this file since 11281 was 11270, checked in by tbretz, 14 years ago
Added a missing ;
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.