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

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