source: trunk/FACT++/src/dataLogger.cc@ 10699

Last change on this file since 10699 was 10693, checked in by tbretz, 14 years ago
Implemented support for compilation without fits.
File size: 74.1 KB
Line 
1//****************************************************************
2/** @class DataLogger
3
4 @brief Logs all message and infos between the services
5
6 This is the main logging class facility.
7 It derives from StateMachineDim and DimInfoHandler. the first parent is here to enforce
8 a state machine behaviour, while the second one is meant to make the dataLogger receive
9 dim services to which it subscribed from.
10 The possible states and transitions of the machine are:
11 \dot
12 digraph datalogger {
13 node [shape=record, fontname=Helvetica, fontsize=10];
14 e [label="Error" color="red"];
15 r [label="Ready"]
16 d [label="NightlyOpen"]
17 w [label="WaitingRun"]
18 l [label="Logging"]
19 b [label="BadNightlyconfig" color="red"]
20 c [label="BadRunConfig" color="red"]
21
22 e -> r
23 r -> e
24 r -> d
25 r -> b
26 d -> w
27 d -> r
28 w -> r
29 l -> r
30 l -> w
31 b -> d
32 w -> c
33 w -> l
34 b -> r
35 c -> r
36 c -> l
37 }
38 \enddot
39
40 @todo
41 - Retrieve also the messages, not only the infos
42 */
43 //****************************************************************
44#include "FACT.h"
45#include "Dim.h"
46#include "Event.h"
47#include "Time.h"
48#include "StateMachineDim.h"
49#include "WindowLog.h"
50#include "Configuration.h"
51#include "ServiceList.h"
52#include "Converter.h"
53#include "MessageImp.h"
54#include "LocalControl.h"
55#include "DimDescriptionService.h"
56
57#include "Description.h"
58
59#include "DimServiceInfoList.h"
60
61//for getting stat of opened files
62#include <unistd.h>
63//for getting disk free space
64#include <sys/statvfs.h>
65//for getting files sizes
66#include <sys/stat.h>
67
68//#define ONE_RUN_FITS_ONLY
69
70#include <fstream>
71
72#include <boost/bind.hpp>
73#if BOOST_VERSION < 104400
74#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 4))
75#undef BOOST_HAS_RVALUE_REFS
76#endif
77#endif
78#include <boost/thread.hpp>
79
80#ifdef HAVE_FITS
81#include "Fits.h"
82#endif
83
84//Dim structures
85struct DataLoggerStats {
86 long sizeWritten;
87 long freeSpace;
88 long writingRate;
89};
90
91struct NumSubAndFitsType {
92 int numSubscriptions;
93 int numOpenFits;
94};
95
96struct OpenFileToDim {
97 int code;
98 char fileName[FILENAME_MAX];
99};
100//For debugging DIM's services
101class MyService
102{
103public:
104 MyService(){};
105 MyService(std::string, std::string, void*, int){};
106 MyService(std::string, const char*){};
107 void updateService(){};
108 void updateService(void*, int){};
109 void setQuality(int){};
110};
111class DataLogger : public StateMachineDim, DimInfoHandler//, DimServiceInfoList //,DimInfoHandler
112{
113public:
114 /// The list of existing states specific to the DataLogger
115 enum
116 {
117 kSM_NightlyOpen = 20, ///< Nightly file openned and writing
118 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
119 kSM_Logging = 40, ///< both files openned and writing
120 kSM_BadNightlyConfig = 0x101, ///< the folder specified for Nightly logging does not exist or has bad permissions
121 kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number
122 } localstates_t;
123
124 DataLogger(std::ostream &out);
125 ~DataLogger();
126
127private:
128 //Define all the data structure specific to the DataLogger here
129 /// ofstream for the NightlyLogfile
130 std::ofstream fNightlyLogFile;
131 /// ofstream for the run-specific Log file
132 std::ofstream fRunLogFile;
133
134 /// ofstream for the Nightly report file
135 std::ofstream fNightlyReportFile;
136 /// ofstream for the run-specific report file
137 std::ofstream fRunReportFile;
138 /// base path of the Nightlyfile
139 std::string fNightlyFileName;
140 ///base path of the run file
141 std::string fRunFileName;
142 ///run number (-1 means no run number specified)
143 int fRunNumber;
144 ///previous run number. to check if changed while logging
145 int fPreviousRunNumber;
146 ///Current Service Quality
147 int fQuality;
148 ///Modified Julian Date
149 double fMjD;
150public:
151 ///Define all the static names
152 static const char* fConfigDay;
153 static const char* fConfigRun;
154 static const char* fConfigRunNumber;
155 static const char* fConfigLog;
156 static const char* fTransStart;
157 static const char* fTransStop;
158 static const char* fTransStartRun;
159 static const char* fTransStopRun;
160 static const char* fTransReset;
161 static const char* fTransWait;
162 static const char* fRunNumberInfo; ///< This is the name of the dimInfo received to specify the run number. It must be updated once the final name will be defined
163 static const char* fPrintCommand;
164 static const char* fDebugOnOff;
165 static const char* fStatsPeriod;
166 static const char* fStartStopOpenedFiles;
167 static const char* fStartStopNumSubsAndFits;
168private:
169 //overloading of DIM's infoHandler function
170 void infoHandler();
171
172 ///for obtaining the name of the existing services
173 ServiceList fServiceList;
174
175 ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file
176 struct SubscriptionType
177 {
178#ifdef HAVE_FITS
179 ///Nightly FITS output file
180 Fits nightlyFile;
181 ///run-specific FITS output file
182 Fits runFile;
183#endif
184 ///the actual dimInfo pointer
185 DimStampedInfo* dimInfo;
186 ///the converter for outputting the data according to the format
187 Converter* fConv;
188 ///the number of existing handlers to this structure.
189 ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor
190 int* numCopies;
191 void operator = (const SubscriptionType& other)
192 {
193#ifdef HAVE_FITS
194 nightlyFile = other.nightlyFile;
195 runFile = other.runFile;
196#endif
197 dimInfo = other.dimInfo;
198 numCopies = other.numCopies;
199 fConv = other.fConv;
200 (*numCopies)++;
201 }
202 SubscriptionType(const SubscriptionType& other)
203 {
204#ifdef HAVE_FITS
205 nightlyFile = other.nightlyFile;
206 runFile = other.runFile;
207#endif
208 dimInfo = other.dimInfo;
209 numCopies = other.numCopies;
210 fConv = other.fConv;
211 (*numCopies)++;
212 }
213 SubscriptionType(DimStampedInfo* info)
214 {
215 dimInfo = info;
216 fConv = NULL;
217 numCopies = new int(1);
218 }
219 SubscriptionType()
220 {
221 dimInfo = NULL;
222 fConv = NULL;
223 numCopies = new int(1);
224 }
225 ~SubscriptionType()
226 {
227 if (numCopies)
228 (*numCopies)--;
229 if (numCopies)
230 if (*numCopies < 1)
231 {
232 if (dimInfo)
233 delete dimInfo;
234#ifdef HAVE_FITS
235 if (nightlyFile.IsOpen())
236 nightlyFile.Close();
237 if (runFile.IsOpen())
238 runFile.Close();
239#endif
240 if (numCopies)
241 delete numCopies;
242 delete fConv;
243 fConv = NULL;
244 dimInfo = NULL;
245 numCopies = NULL;
246 }
247 }
248 };
249 typedef std::map<const std::string, std::map<std::string, SubscriptionType>> SubscriptionsListType;
250 ///All the services to which we have subscribed to, sorted by server name.
251 SubscriptionsListType fServiceSubscriptions;
252
253 ///Reporting method for the services info received
254 void ReportPlease(DimInfo* I, SubscriptionType& sub);
255
256 ///Configuration of the nightly file path
257 int ConfigureNightlyFileName(const Event& evt);
258 ///Configuration fo the file name
259 int ConfigureRunFileName(const Event& evt);
260 ///DEPREC - configuration of the run number
261 int ConfigureRunNumber(const Event& evt);
262 ///logging method for the messages
263 int LogMessagePlease(const Event& evt);
264 ///print the current state of the dataLogger
265 int PrintStatePlease(const Event& evt);
266 ///checks whether or not the current info being treated is a run number
267 void CheckForRunNumber(DimInfo* I);
268 /// start transition
269 int StartPlease();
270 ///from waiting to logging transition
271 int StartRunPlease();
272 /// from logging to waiting transition
273 int StopRunPlease();
274 ///stop and reset transition
275 int GoToReadyPlease();
276 ///from NightlyOpen to waiting transition
277 int NightlyToWaitRunPlease();
278#ifdef HAVE_FITS
279 ///Open fits files
280 void OpenFITSFilesPlease(SubscriptionType& sub);
281 ///Write data to FITS files
282 void WriteToFITS(SubscriptionType& sub);
283 ///Allocate the buffers required for fits
284 void AllocateFITSBuffers(SubscriptionType& sub);
285
286#ifdef ONE_RUN_FITS_ONLY
287 ///FITS file for runs. only one, hence dealt with in the dataLogger itself
288 FITS* fRunFitsFile;
289#endif //one_run_fits_only
290#endif//has_fits
291public:
292 ///checks with fServiceList whether or not the services got updated
293 bool CheckForServicesUpdate();
294
295private:
296 ///monitoring notification loop
297 void ServicesMonitoring();
298 ///services notification thread
299 boost::thread fMonitoringThread;
300 ///end of the monitoring
301 bool fContinueMonitoring;
302 ///required for accurate monitoring
303 std::map<std::string, long> fFileSizesMap;
304 std::string fFullNightlyLogFileName;
305 std::string fFullNightlyReportFileName;
306 std::string fFullRunLogFileName;
307 std::string fFullRunReportFileName;
308 long fBaseSizeNightly;
309 long fPreviousSize;
310 long fBaseSizeRun;
311 ///Service for opened files
312 DimDescribedService* fOpenedNightlyFiles;
313 DimDescribedService* fOpenedRunFiles;
314 DimDescribedService* fNumSubAndFits;
315 NumSubAndFitsType fNumSubAndFitsData;
316
317 inline void NotifyOpenedFile(std::string name, int type, DimDescribedService* service);
318public:
319// void setBlackWhiteList(const std::string& , bool);
320 void Setup(Configuration& conf);
321
322private:
323 std::set<std::string> fGreyList;
324 bool fIsBlackList;
325 bool fDebugIsOn;
326 float fStatsPeriodDuration;
327 bool fOpenedFilesIsOn;
328 bool fNumSubAndFitsIsOn;
329 //functions for controlling the services behavior
330 int SetDebugOnOff(const Event& evt);
331 int SetStatsPeriod(const Event& evt);
332 int SetOpenedFilesOnOff(const Event& evt);
333 int SetNumSubsAndFitsOnOff(const Event& evt);
334 ///boolean to prevent DIM update while desctructing the dataLogger
335 bool fDestructing;
336
337 ///Small function for calculating the total size written so far
338 void calculateTotalSizeWritten(DataLoggerStats& statVar, bool& shouldWarn, bool isPrinting);
339}; //DataLogger
340
341void DataLogger::calculateTotalSizeWritten(DataLoggerStats& statVar, bool& shouldWarn, bool isPrinting)
342{
343#ifdef HAVE_FITS
344 if (isPrinting)
345 {
346 stringstream str;
347 str.str("");
348 str << "There are " << fNumSubAndFitsData.numOpenFits << " FITS files open:";
349 Message(str.str());
350 }
351 SubscriptionsListType::iterator x;
352 std::map<std::string, SubscriptionType>::iterator y;
353 bool runFileDone = false;
354 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
355 {
356 for (y=x->second.begin(); y != x->second.end(); y++)
357 {
358 if (y->second.runFile.IsOpen() && !runFileDone)
359 {
360 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
361 if (isPrinting)
362 Message("-> "+y->second.runFile.fFileName);
363#ifdef ONE_FITS_ONLY
364 runFileDone = true;
365#endif
366 }
367 if (y->second.nightlyFile.IsOpen())
368 {
369 fFileSizesMap[y->second.nightlyFile.fFileName] = y->second.nightlyFile.GetWrittenSize();
370 if (isPrinting)
371 Message("-> "+y->second.nightlyFile.fFileName);
372 }
373 }
374 }
375#else
376 if (isPrinting)
377 Message("FITS output disabled at compilation");
378#endif
379 struct stat st;
380 //gather log and report files sizes on disk
381 if (fNightlyLogFile.is_open())
382 {
383 stat(fFullNightlyLogFileName.c_str(), &st);
384 fFileSizesMap[fFullNightlyLogFileName] = st.st_size;
385 }
386 if (fNightlyReportFile.is_open())
387 {
388 stat(fFullNightlyReportFileName.c_str(), &st);
389 fFileSizesMap[fFullNightlyReportFileName] = st.st_size;
390 }
391 if (fRunLogFile.is_open())
392 {
393 stat(fFullRunLogFileName.c_str(), &st);
394 fFileSizesMap[fFullRunLogFileName] = st.st_size;
395 }
396 if (fRunReportFile.is_open())
397 {
398 stat(fFullRunReportFileName.c_str(), &st);
399 fFileSizesMap[fFullRunReportFileName] = st.st_size;
400 }
401 struct statvfs vfs;
402 if (!statvfs(fNightlyFileName.c_str(), &vfs))
403 {
404 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
405 shouldWarn = false;
406 }
407 else
408 {
409 stringstream str;
410 str.str("");
411 str << "Unable to retrieve stats for " << fNightlyFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
412 if (!shouldWarn)
413 Error(str);
414 shouldWarn = true;
415 statVar.freeSpace = -1;
416 }
417 //sum up all the file sizes. past and present
418 statVar.sizeWritten = 0;
419 for (std::map<std::string, long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
420 statVar.sizeWritten += it->second;
421 statVar.sizeWritten -= fBaseSizeNightly;
422 statVar.sizeWritten -= fBaseSizeRun;
423}
424//static members initialization
425//since I do not check the transition/config names any longer, indeed maybe these could be hard-coded... but who knows what will happen in the future ?
426const char* DataLogger::fConfigDay = "CONFIG_DAY";
427const char* DataLogger::fConfigRun = "CONFIG_RUN";
428const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
429const char* DataLogger::fConfigLog = "LOG";
430const char* DataLogger::fTransStart = "START";
431const char* DataLogger::fTransStop = "STOP";
432const char* DataLogger::fTransStartRun = "START_RUN";
433const char* DataLogger::fTransStopRun = "STOP_RUN";
434const char* DataLogger::fTransReset = "RESET";
435const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
436const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
437const char* DataLogger::fPrintCommand = "PRINT";
438const char* DataLogger::fDebugOnOff = "DEBUG";
439const char* DataLogger::fStatsPeriod = "STATS_PERIOD";
440const char* DataLogger::fStartStopOpenedFiles = "OPENED_FILES_SRVC";
441const char* DataLogger::fStartStopNumSubsAndFits = "NUM_SUBS_SRVC";
442
443void DataLogger::ServicesMonitoring()
444{
445 //create the DIM service
446// int dataSize = 2*sizeof(long) + sizeof(long);
447
448 DataLoggerStats statVar;
449 statVar.sizeWritten = 0;
450 statVar.freeSpace = 0;
451 statVar.writingRate = 0;
452
453 struct statvfs vfs;
454 if (!statvfs(fNightlyFileName.c_str(), &vfs))
455 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
456 else
457 statVar.freeSpace = -1;
458
459 DimDescribedService srvc ("DATA_LOGGER/STATS", "X:3", statVar, "Add description here");
460 fPreviousSize = 0;
461 bool statWarning = false;
462 //loop-wait for broadcast
463 while (fContinueMonitoring)
464 {
465 if (fStatsPeriodDuration == 0.0f)
466 {
467 sleep(0.1f);
468 continue;
469 }
470 else
471 sleep(fStatsPeriodDuration);
472 //update the fits files sizes
473 calculateTotalSizeWritten(statVar, statWarning, false);/*
474#ifdef HAVE_FITS
475 SubscriptionsListType::iterator x;
476 std::map<std::string, SubscriptionType>::iterator y;
477 bool runFileDone = false;
478 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
479 {
480 for (y=x->second.begin(); y != x->second.end(); y++)
481 {
482 if (y->second.runFile.IsOpen() && !runFileDone)
483 {
484 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
485#ifdef ONE_FITS_ONLY
486 runFileDone = true;
487#endif
488 }
489 if (y->second.nightlyFile.IsOpen())
490 fFileSizesMap[y->second.nightlyFile.fFileName] = y->second.nightlyFile.GetWrittenSize();
491 }
492 }
493#endif
494 struct stat st;
495 //gather log and report files sizes on disk
496 if (fNightlyLogFile.is_open())
497 {
498 stat(fFullNightlyLogFileName.c_str(), &st);
499 fFileSizesMap[fFullNightlyLogFileName] = st.st_size;
500 }
501 if (fNightlyReportFile.is_open())
502 {
503 stat(fFullNightlyReportFileName.c_str(), &st);
504 fFileSizesMap[fFullNightlyReportFileName] = st.st_size;
505 }
506 if (fRunLogFile.is_open())
507 {
508 stat(fFullRunLogFileName.c_str(), &st);
509 fFileSizesMap[fFullRunLogFileName] = st.st_size;
510 }
511 if (fRunReportFile.is_open())
512 {
513 stat(fFullRunReportFileName.c_str(), &st);
514 fFileSizesMap[fFullRunReportFileName] = st.st_size;
515 }
516
517 if (!statvfs(fNightlyFileName.c_str(), &vfs))
518 {
519 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
520 statWarning = false;
521 }
522 else
523 {
524 std::stringstream str;
525 str << "Unable to retrieve stats for " << fNightlyFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
526 if (!statWarning)
527 Error(str);
528 statWarning = true;
529 statVar.freeSpace = -1;
530 }
531
532 //sum up all the file sizes. past and present
533 statVar.sizeWritten = 0;
534 for (std::map<std::string, long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
535 statVar.sizeWritten += it->second;
536 statVar.sizeWritten -= fBaseSizeNightly;
537 statVar.sizeWritten -= fBaseSizeRun;
538 */
539 if (fStatsPeriodDuration == 0.0f)
540 continue;
541 statVar.writingRate = (statVar.sizeWritten - fPreviousSize)/fStatsPeriodDuration;
542
543 fPreviousSize = statVar.sizeWritten;
544 if (statVar.writingRate != 0) //if data has been written
545 {
546 srvc.updateService();
547 if(fDebugIsOn)
548 {
549 stringstream str;
550 str << "Size written: " << statVar.sizeWritten/1024 << " KB; writting rate: ";
551 str << statVar.writingRate/1024 << " KB/s; free space: ";
552 str << statVar.freeSpace/(1024*1024) << " MB";
553 Debug(str.str());
554 }
555 }
556 }
557}
558
559
560// --------------------------------------------------------------------------
561//
562//! Default constructor. The name of the machine is given DATA_LOGGER
563//! and the state is set to kSM_Ready at the end of the function.
564//
565//!Setup the allows states, configs and transitions for the data logger
566//
567DataLogger::DataLogger(std::ostream &out) : StateMachineDim(out, "DATA_LOGGER")
568{
569 dic_disable_padding();
570 dis_disable_padding();
571
572 //initialize member data
573 fNightlyFileName = ".";//"/home/lyard/log";//
574 fRunFileName = ".";//"/home/lyard/log";
575 fRunNumber = -1;
576 fPreviousRunNumber = fRunNumber;
577#ifdef HAVE_FITS
578#ifdef ONE_RUN_FITS_ONLY
579 fRunFitsFile = NULL;
580#endif
581#endif
582
583 //Give a name to this machine's specific states
584 AddStateName(kSM_NightlyOpen, "NightlyFileOpen", "The summary files for the night are open.");
585 AddStateName(kSM_WaitingRun, "WaitForRun", "The summary files for the night are open and we wait for a run to be started.");
586 AddStateName(kSM_Logging, "Logging", "The summary files for the night and the files for a single run are open.");
587 AddStateName(kSM_BadNightlyConfig, "ErrNightlyFolder", "The folder for the nighly summary files is invalid.");
588 AddStateName(kSM_BadRunConfig, "ErrRunFolder", "The folder for the run files is invalid.");
589
590 /*Add the possible transitions for this machine*/
591 AddEvent(kSM_NightlyOpen, fTransStart, kSM_Ready, kSM_BadNightlyConfig)
592 (boost::bind(&DataLogger::StartPlease, this))
593 ("Start the nightly logging. Nightly file location must be specified already");
594
595 AddEvent(kSM_Ready, fTransStop, kSM_NightlyOpen, kSM_WaitingRun, kSM_Logging)
596 (boost::bind(&DataLogger::GoToReadyPlease, this))
597 ("Stop all data logging, close all files.");
598
599 AddEvent(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig)
600 (boost::bind(&DataLogger::StartRunPlease, this))
601 ("Start the run logging. Run file location must be specified already.");
602
603 AddEvent(kSM_WaitingRun, fTransStopRun, kSM_Logging)
604 (boost::bind(&DataLogger::StopRunPlease, this))
605 ("Wait for a run to be started, open run-files as soon as a run number arrives.");
606
607 AddEvent(kSM_Ready, fTransReset, kSM_Error, kSM_BadNightlyConfig, kSM_BadRunConfig, kSM_Error)
608 (boost::bind(&DataLogger::GoToReadyPlease, this))
609 ("Transition to exit error states. Closes the nightly file if already opened.");
610
611 AddEvent(kSM_WaitingRun, fTransWait, kSM_NightlyOpen)
612 (boost::bind(&DataLogger::NightlyToWaitRunPlease, this));
613
614 /*Add the possible configurations for this machine*/
615 AddEvent(fConfigDay, "C", kSM_Ready, kSM_BadNightlyConfig)
616 (boost::bind(&DataLogger::ConfigureNightlyFileName, this, _1))
617 ("Configure the folder for the nightly files."
618 "|Path[string]:Absolute or relative path name where the nightly files should be stored.");
619
620 AddEvent(fConfigRun, "C", kSM_Ready, kSM_BadNightlyConfig, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig)
621 (boost::bind(&DataLogger::ConfigureRunFileName, this, _1))
622 ("Configure the folder for the run files."
623 "|Path[string]:Absolute or relative path name where the run files should be stored.");
624
625 AddEvent(fConfigRunNumber, "I", kSM_Ready, kSM_BadNightlyConfig, kSM_NightlyOpen, kSM_WaitingRun, kSM_BadRunConfig)
626 (boost::bind(&DataLogger::ConfigureRunNumber, this, _1))
627 ("configure the run number. cannot be done in logging state");
628
629 //Provide a logging command
630 //I get the feeling that I should be going through the EventImp
631 //instead of DimCommand directly, mainly because the commandHandler
632 //is already done in StateMachineImp.cc
633 //Thus I'll simply add a configuration, which I will treat as the logging command
634 AddEvent(fConfigLog, "C", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig)
635 (boost::bind(&DataLogger::LogMessagePlease, this, _1))
636 ("Log a single message to the log-files."
637 "|Message[string]:Message to be logged.");
638
639 //Provide a print command
640 stringstream str;
641 str << kSM_Ready << " " << kSM_NightlyOpen << " " << kSM_WaitingRun << " " << kSM_Logging << " " << kSM_BadNightlyConfig;
642 str << " " << kSM_BadRunConfig;
643
644 AddEvent(fPrintCommand, str.str().c_str(), "")
645// AddEvent(fPrintCommand, kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadNightlyConfig, kSM_BadRunConfig)
646 (boost::bind(&DataLogger::PrintStatePlease, this, _1))
647 ("Print information about the internal status of the data logger.");
648
649 fServiceList.SetHandler(this);
650 CheckForServicesUpdate();
651
652 //start the monitoring service
653 fContinueMonitoring = true;
654 fMonitoringThread = boost::thread(boost::bind(&DataLogger::ServicesMonitoring, this));
655 fBaseSizeNightly = 0;
656 fBaseSizeRun = 0;
657 OpenFileToDim fToDim;
658 fToDim.code = 0;
659 fToDim.fileName[0] = '\0';
660
661 fOpenedNightlyFiles = new DimDescribedService(GetName() + "/FILENAME_NIGHTLY", "I:1;C", fToDim,
662 "Path and base name which is used to compile the filenames for the nightly files."
663 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
664 "|Name[string]:path and base file name");
665
666 fOpenedRunFiles = new DimDescribedService(GetName() + "/FILENAME_RUN", "I:1;C", fToDim,
667 "Path and base name which is used to compile the filenames for the run files."
668 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
669 "|Name[string]:path and base file name");
670
671 fNumSubAndFitsData.numSubscriptions = 0;
672 fNumSubAndFitsData.numOpenFits = 0;
673 fNumSubAndFits = new DimDescribedService(GetName() + "/NUM_SUBS", "I:2", fNumSubAndFitsData,
674 "Shows number of services to which the data logger is currently subscribed and the total number of open files."
675 "|Subscriptions[int]:number of dim services to which the data logger is currently subscribed."
676 "|NumOpenFiles[int]:number of files currently open by the data logger");
677
678 //black/white list
679 fIsBlackList = true;
680 fGreyList.clear();
681
682 //services parameters
683 fDebugIsOn = true;//false;
684 fStatsPeriodDuration = 1.0f;
685 fOpenedFilesIsOn = true;
686 fNumSubAndFitsIsOn = true;
687
688 //provide services control commands
689 AddEvent(fDebugOnOff, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
690 (boost::bind(&DataLogger::SetDebugOnOff, this, _1))
691 ("Switch debug mode on off. Debug mode prints ifnormation about every service written to a file."
692 "|Enable[bool]:Enable of disable debuig mode (yes/no).");
693
694 AddEvent(fStatsPeriod, "F", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
695 (boost::bind(&DataLogger::SetStatsPeriod, this, _1))
696 ("Interval in which the data-logger statitistics service (STATS) is updated."
697 "Interval[s]:Floating point value in seconds.");
698
699 AddEvent(fStartStopOpenedFiles, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
700 (boost::bind(&DataLogger::SetOpenedFilesOnOff ,this, _1))
701 ("Can be used to switch the service off which distributes information about the open files.");
702
703 AddEvent(fStartStopNumSubsAndFits, "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
704 (boost::bind(&DataLogger::SetNumSubsAndFitsOnOff, this, _1))
705 ("Can be used to switch the service off which distributes information about the number of subscriptions and open files.");
706
707 fDestructing = false;
708 if(fDebugIsOn)
709 {
710 Debug("DataLogger Init Done.");
711 }
712}
713// --------------------------------------------------------------------------
714//
715//! Checks for changes in the existing services.
716//! Any new service will be added to the service list, while the ones which disappeared are removed.
717//! @todo
718//! add the configuration (using the conf class ?)
719//
720//FIXME The service must be udpated so that I get the first notification. This should not be
721bool DataLogger::CheckForServicesUpdate()
722{
723 bool serviceUpdated = false;
724 //get the current server list
725 const std::vector<std::string> serverList = fServiceList.GetServerList();
726 //first let's remove the servers that may have disapeared
727 //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
728 std::vector<std::string> toBeDeleted;
729 for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
730 {
731 std::vector<std::string>::const_iterator givenServers;
732 for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
733 if (cListe->first == *givenServers)
734 break;
735 if (givenServers == serverList.end())//server vanished. Remove it
736 {
737 toBeDeleted.push_back(cListe->first);
738 serviceUpdated = true;
739 }
740
741 }
742 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
743 fServiceSubscriptions.erase(*it);
744 //now crawl through the list of servers, and see if there was some updates
745 for (std::vector<std::string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
746 {
747 //skip the two de-fact excluded services
748 //Dim crashes if the publisher subscribes to its own service. This sounds weird, I agree.
749 if ((i->find("DIS_DNS") != std::string::npos) ||
750 (i->find("DATA_LOGGER") != std::string::npos))
751 continue;
752 if (fIsBlackList && (fGreyList.find(*i) != fGreyList.end()))
753 continue;
754 //find the current server in our subscription list
755 SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
756 //get the service list of the current server
757 std::vector<std::string> cServicesList = fServiceList.GetServiceList(*i);
758 if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
759 { //then check and update our list of subscriptions
760 //first, remove the services that may have dissapeared.
761 std::map<std::string, SubscriptionType>::iterator serverSubs;
762 std::vector<std::string>::const_iterator givenSubs;
763 toBeDeleted.clear();
764 for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
765 {
766 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
767 if (serverSubs->first == *givenSubs)
768 break;
769 if (givenSubs == cServicesList.end())
770 {
771 toBeDeleted.push_back(serverSubs->first);
772 serviceUpdated = true;
773 }
774 }
775 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
776 cSubs->second.erase(*it);
777 //now check for new services
778 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
779 {
780 if (*givenSubs == "SERVICE_LIST")
781 continue;
782 if (fIsBlackList && fGreyList.find(*givenSubs) != fGreyList.end())
783 continue;
784
785 if (fIsBlackList && fGreyList.find((*i) + "/" + (*givenSubs)) != fGreyList.end())
786 continue;
787 else if (!fIsBlackList &&
788 (fGreyList.find((*i) + "/" + (*givenSubs)) == fGreyList.end()) &&
789 (fGreyList.find(*i) == fGreyList.end()) &&
790 (fGreyList.find(*givenSubs) == fGreyList.end()))
791 continue;
792 if (cSubs->second.find(*givenSubs) == cSubs->second.end())
793 {//service not found. Add it
794 cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast<char*>(""), this);
795 serviceUpdated = true;
796 if(fDebugIsOn)
797 {
798 stringstream str;
799 str << "Subscribing to service " << *i << "/" << *givenSubs;
800 Debug(str.str());
801 }
802 }
803 }
804 }
805 else //server not found in our list. Create its entry
806 {
807 fServiceSubscriptions[*i] = std::map<std::string, SubscriptionType>();
808 std::map<std::string, SubscriptionType>& liste = fServiceSubscriptions[*i];
809 for (std::vector<std::string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
810 {
811 if (*j == "SERVICE_LIST")
812 continue;
813 if (fIsBlackList && fGreyList.find(*j) != fGreyList.end())
814 continue;
815
816 if (fIsBlackList && fGreyList.find((*i) + "/" + (*j)) != fGreyList.end())
817 continue;
818 else if (!fIsBlackList &&
819 (fGreyList.find((*i) + "/" + (*j)) == fGreyList.end()) &&
820 (fGreyList.find(*i) == fGreyList.end()) &&
821 (fGreyList.find(*j) == fGreyList.end()))
822 continue;
823
824 liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast<char*>(""), this);
825 serviceUpdated = true;
826 if(fDebugIsOn)
827 {
828 stringstream str;
829 str << "Subscribing to service " << *i << "/" << *j;
830 Debug(str.str());
831 }
832 }
833 }
834 }
835 return serviceUpdated;
836}
837// --------------------------------------------------------------------------
838//
839//! Destructor
840//
841DataLogger::~DataLogger()
842{
843 if (fDebugIsOn)
844 {
845 Debug("DataLogger destruction starts");
846 }
847 fDestructing = true;
848 //first let's go to the ready state
849 //TODO some closing done below has already been executed by GoToReady. figure out what should be removed.
850 GoToReadyPlease();
851 //release the services subscriptions
852 fServiceSubscriptions.clear();
853 //exit the monitoring loop
854 fContinueMonitoring = false;
855// delete[] fDimBuffer;
856 fMonitoringThread.join();
857 //close the files
858 if (fNightlyLogFile.is_open())
859 fNightlyLogFile.close();
860 if (fNightlyReportFile.is_open())
861 fNightlyReportFile.close();
862 if (fRunLogFile.is_open())
863 fRunLogFile.close();
864 if (fRunReportFile.is_open())
865 fRunReportFile.close();
866 delete fOpenedNightlyFiles;
867 delete fOpenedRunFiles;
868 delete fNumSubAndFits;
869//TODO notify that all files were closed
870#ifdef HAVE_FITS
871#ifdef ONE_RUN_FITS_ONLY
872 if (fRunFitsFile != NULL)
873 delete fRunFitsFile;
874 fRunFitsFile = NULL;
875#endif
876#endif
877 if (fDebugIsOn)
878 {
879 Debug("DataLogger desctruction ends");
880 }
881}
882
883// --------------------------------------------------------------------------
884//
885//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
886//
887void DataLogger::infoHandler()
888{
889 // Make sure getTimestamp is called _before_ getTimestampMillisecs
890 if (fDestructing)
891 return;
892
893 DimInfo* I = getInfo();
894 SubscriptionsListType::iterator x;
895 std::map<std::string, SubscriptionType>::iterator y;
896 if (I==NULL)
897 {
898 if (CheckForServicesUpdate())
899 {
900 //services were updated. Notify
901 fNumSubAndFitsData.numSubscriptions = 0;
902 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
903 fNumSubAndFitsData.numSubscriptions += x->second.size();
904 if (fNumSubAndFitsIsOn)
905 {
906 if (fDebugIsOn)
907 {
908 stringstream str;
909 str << "Updating number of subscriptions service: Num Subs=" << fNumSubAndFitsData.numSubscriptions << " Num open FITS=" << fNumSubAndFitsData.numOpenFits;
910 Debug(str.str());
911 }
912 fNumSubAndFits->updateService();
913 }
914 }
915 return;
916 }
917 //check if the service pointer corresponds to something that we subscribed to
918 //this is a fix for a bug that provides bad Infos when a server starts
919 bool found = false;
920 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
921 {//find current service is subscriptions
922 for (y=x->second.begin(); y!=x->second.end();y++)
923 if (y->second.dimInfo == I)
924 {
925 found = true;
926 break;
927 }
928 if (found)
929 break;
930 }
931 if (!found)
932 return;
933 if (I->getSize() <= 0)
934 return;
935
936 // Make sure that getTimestampMillisecs is NEVER called before
937 // getTimestamp is properly called
938 // check that the message has been updated by something, i.e. must be different from its initial value
939 if (I->getTimestamp() == 0)
940 return;
941
942 CheckForRunNumber(I);
943
944 if (fPreviousRunNumber != fRunNumber)
945 {//run number has changed. close and reopen run files.
946 StopRunPlease();
947 StartRunPlease();
948 fPreviousRunNumber = fRunNumber;
949 }
950
951 ReportPlease(I, y->second);
952
953}
954
955// --------------------------------------------------------------------------
956//
957//! Checks whether or not the current info is a run number.
958//! If so, then remember it. A run number is required to open the run-log file
959//! @param I
960//! the current DimInfo
961//
962void DataLogger::CheckForRunNumber(DimInfo* I)
963{
964 if (strstr(I->getName(), fRunNumberInfo) != NULL)
965 {//assumes that the run number is an integer
966 //TODO check the format here
967 fRunNumber = I->getInt();
968 stringstream str;
969 str << "New run number is " << fRunNumber;
970 Message(str.str());
971 }
972}
973
974// --------------------------------------------------------------------------
975//
976//! write infos to log files.
977//! @param I
978//! The current DimInfo
979//
980void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
981{
982 //should we log or report this info ? (i.e. is it a message ?)
983 bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
984 if (I->getFormat()[0] == 'C')
985 isItaReport = false;
986 //TODO add service exclusion
987
988 if (!fNightlyReportFile.is_open())
989 return;
990
991 //create the converter for that service
992 if (sub.fConv == NULL && isItaReport)
993 {
994 //trick the converter in case of 'C'. why do I do this ? well simple: the converter checks that the right number
995 //of bytes was written. because I skip 'C' with fits, the bytes will not be allocated, hence the "size copied ckeck"
996 //of the converter will fail, hence throwing an exception.
997 std::string fakeFormat(I->getFormat());
998 if (fakeFormat[fakeFormat.size()-1] == 'C')
999 fakeFormat = fakeFormat.substr(0, fakeFormat.size()-1);
1000 sub.fConv = new Converter(Out(), I->getFormat());
1001 if (!sub.fConv)
1002 {
1003 std::stringstream str;
1004 str << "Couldn't properly parse the format... service " << sub.dimInfo->getName() << " ignored.";
1005 Error(str);
1006 return;
1007 }
1008 }
1009
1010 //construct the header
1011 std::stringstream header;
1012 Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
1013 fQuality = I->getQuality();
1014 fMjD = cTime.Mjd();
1015
1016 if (isItaReport)
1017 {
1018 //write text header
1019 header << I->getName() << " " << fQuality << " ";
1020 header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1021 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1022 header << cTime.ms() << " " << I->getTimestamp() << " ";
1023
1024 std::string text;
1025 try
1026 {
1027 text = sub.fConv->GetString(I->getData(), I->getSize());
1028 }
1029 catch (const std::runtime_error &e)
1030 {
1031 Out() << kRed << e.what() << endl;
1032 std::stringstream str;
1033 str << "Could not properly parse the data for service " << sub.dimInfo->getName();
1034 str << " reason: " << e.what() << ". Entry ignored";
1035 Error(str);
1036 return;
1037 }
1038
1039 if (text.empty())
1040 {
1041 std::stringstream str;
1042 str << "Service " << sub.dimInfo->getName() << " sent an empty string";
1043 Info(str);
1044 return;
1045 }
1046 //replace bizarre characters by white space
1047 replace(text.begin(), text.end(), '\n', '\\');
1048 replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
1049
1050 //write entry to Nightly report
1051 if (fNightlyReportFile.is_open())
1052 {
1053 if (fDebugIsOn)
1054 {
1055 stringstream str;
1056 str << "Writing: \"" << header.str() << text << "\" to Nightly report file";
1057 Debug(str.str());
1058 }
1059 fNightlyReportFile << header.str() << text << std::endl;
1060 //check if either eof, bailbit or batbit are set
1061 if (!fNightlyReportFile.good())
1062 {
1063 Error("An error occured while writing to the nightly report file. Closing it");
1064 if (fNightlyReportFile.is_open())
1065 fNightlyReportFile.close();
1066 }
1067 }
1068 //write entry to run-report
1069 if (fRunReportFile.is_open())
1070 {
1071 if (fDebugIsOn)
1072 {
1073 stringstream str;
1074 str << "Writing: \"" << header.str() << text << "\" to Run report file";
1075 Debug(str.str());
1076 }
1077 fRunReportFile << header.str() << text << std::endl;
1078 if (!fRunReportFile.good())
1079 {
1080 Error("An error occured while writing to the run report file. Closing it.");
1081 if (fRunReportFile.is_open())
1082 fRunReportFile.close();
1083 }
1084 }
1085 }
1086 else
1087 {//write entry to both Nightly and run logs
1088 std::string n = I->getName();
1089 std::stringstream msg;
1090 msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString();
1091
1092 if (fNightlyLogFile.is_open())
1093 {
1094 if (fDebugIsOn)
1095 {
1096 stringstream str;
1097 str << "Writing: \"" << msg.str() << "\" to Nightly log file";
1098 Debug(str.str());
1099 }
1100 MessageImp nightlyMess(fNightlyLogFile);
1101 nightlyMess.Write(cTime, msg.str().c_str(), fQuality);
1102 if (!fNightlyLogFile.good())
1103 {
1104 Error("An error occured while writing to the nightly log file. Closing it.");
1105 if (fNightlyLogFile.is_open())
1106 fNightlyLogFile.close();
1107 }
1108 }
1109 if (fRunLogFile.is_open())
1110 {
1111 if (fDebugIsOn)
1112 {
1113 stringstream str;
1114 str << "Writing: \"" << msg.str() << "\" to Run log file";
1115 Debug(str.str());
1116 }
1117 MessageImp runMess(fRunLogFile);
1118 runMess.Write(cTime, msg.str().c_str(), fQuality);
1119 if (!fRunLogFile.good())
1120 {
1121 Error("An error occured while writing to the run log file. Closing it.");
1122 if (fRunLogFile.is_open())
1123 fRunLogFile.close();
1124 }
1125 }
1126 }
1127
1128#ifdef HAVE_FITS
1129 if (isItaReport)
1130 {
1131 if (!sub.nightlyFile.IsOpen() || !sub.runFile.IsOpen())
1132 OpenFITSFilesPlease(sub);
1133 WriteToFITS(sub);
1134 }
1135#endif
1136
1137}
1138
1139// --------------------------------------------------------------------------
1140//
1141//! write messages to logs.
1142//! @param evt
1143//! the current event to log
1144//! @returns
1145//! the new state. Currently, always the current state
1146//!
1147//! @deprecated
1148//! I guess that this function should not be any longer
1149//
1150//TODO isn't that function not used any longer ? If so I guess that we should get rid of it...
1151//Otherwise re-write it properly with the MessageImp class
1152int DataLogger::LogMessagePlease(const Event& evt)
1153{
1154 if (!fNightlyLogFile.is_open())
1155 return GetCurrentState();
1156
1157 std::stringstream header;
1158 const Time& cTime = evt.GetTime();
1159 header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
1160 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
1161 header << cTime.ms() << " ";
1162
1163 const Converter conv(Out(), evt.GetFormat());
1164 if (!conv)
1165 {
1166 Error("Couldn't properly parse the format... ignored.");
1167 return GetCurrentState();
1168 }
1169
1170 std::string text;
1171 try
1172 {
1173 text = conv.GetString(evt.GetData(), evt.GetSize());
1174 }
1175 catch (const std::runtime_error &e)
1176 {
1177 Out() << kRed << e.what() << endl;
1178 Error("Couldn't properly parse the data... ignored.");
1179 return GetCurrentState();
1180 }
1181
1182 if (text.empty())
1183 return GetCurrentState();
1184
1185 //replace bizarre characters by white space
1186 replace(text.begin(), text.end(), '\n', '\\');
1187 replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
1188 if (fDebugIsOn)
1189 {
1190 stringstream str;
1191 str << "Logging: \"" << header << text << "\"";
1192 Debug(str.str());
1193 }
1194
1195 if (fNightlyLogFile.is_open())
1196 {
1197 fNightlyLogFile << header;
1198 if (!fNightlyLogFile.good())
1199 {
1200 Error("An error occured while writing to the run log file. Closing it.");
1201 if (fNightlyLogFile.is_open())
1202 fNightlyLogFile.close();
1203 }
1204 }
1205 if (fRunLogFile.is_open())
1206 {
1207 fRunLogFile << header;
1208 if (!fRunLogFile.good())
1209 {
1210 Error("An error occured while writing to the run log file. Closing it.");
1211 if (fRunLogFile.is_open())
1212 fRunLogFile.close();
1213 }
1214 }
1215 if (fNightlyLogFile.is_open())
1216 {
1217 fNightlyLogFile << text;
1218 if (!fNightlyLogFile.good())
1219 {
1220 Error("An error occured while writing to the run log file. Closing it.");
1221 if (fNightlyLogFile.is_open())
1222 fNightlyLogFile.close();
1223 }
1224 }
1225 if (fRunLogFile.is_open())
1226 {
1227 fRunLogFile << text;
1228 if (!fRunLogFile.good())
1229 {
1230 Error("An error occured while writing to the run log file. Closing it.");
1231 if (fRunLogFile.is_open())
1232 fRunLogFile.close();
1233 }
1234 }
1235 return GetCurrentState();
1236}
1237// --------------------------------------------------------------------------
1238//
1239//! print the dataLogger's current state. invoked by the PRINT command
1240//! @param evt
1241//! the current event. Not used by the method
1242//! @returns
1243//! the new state. Which, in that case, is the current state
1244//!
1245int DataLogger::PrintStatePlease(const Event& )
1246{
1247 Message("-----------------------------------------");
1248 Message("------ DATA LOGGER CURRENT STATE --------");
1249 Message("-----------------------------------------");
1250 //print the path configuration
1251 std::string actualTargetDir;
1252 if (fNightlyFileName == ".")
1253 {
1254 char currentPath[FILENAME_MAX];
1255 if (getcwd(currentPath, sizeof(currentPath)))
1256 actualTargetDir = currentPath;
1257 }
1258 else
1259 actualTargetDir = fNightlyFileName;
1260 Message("Nightly Path: " + actualTargetDir);
1261 if (fRunFileName == ".")
1262 {
1263 char currentPath[FILENAME_MAX];
1264 if (getcwd(currentPath, sizeof(currentPath)))
1265 actualTargetDir = currentPath;
1266 }
1267 else
1268 actualTargetDir = fRunFileName;
1269 Message("Run Path: " + actualTargetDir);
1270 stringstream str;
1271 str << "Run Number: " << fRunFileName;
1272 Message(str.str());
1273 Message("----------- OPENED FILES ----------------");
1274 //print all the open files.
1275 if (fNightlyLogFile.is_open())
1276 Message("Nightly Log..........OPEN");
1277 else
1278 Message("Nightly log........CLOSED");
1279 if (fNightlyReportFile.is_open())
1280 Message("Nightly Report.......OPEN");
1281 else
1282 Message("Nightly Report.....CLOSED");
1283 if (fRunLogFile.is_open())
1284 Message("Run Log..............OPEN");
1285 else
1286 Message("Run Log............CLOSED");
1287 if (fRunReportFile.is_open())
1288 Message("Run Report...........OPEN");
1289 else
1290 Message("Run Report.........CLOSED");
1291 bool statWarning = false;
1292 DataLoggerStats statVar;
1293 calculateTotalSizeWritten(statVar, statWarning, false);/*
1294#ifdef HAVE_FITS
1295 str.str("");
1296 str << "There are " << fNumSubAndFitsData.numOpenFits << " FITS files open:";
1297 Message(str.str());
1298 SubscriptionsListType::iterator x;
1299 std::map<std::string, SubscriptionType>::iterator y;
1300 bool runFileDone = false;
1301 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1302 {
1303 for (y=x->second.begin(); y != x->second.end(); y++)
1304 {
1305 if (y->second.runFile.IsOpen() && !runFileDone)
1306 {
1307 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
1308 Message("-> "+y->second.runFile.fFileName);
1309#ifdef ONE_FITS_ONLY
1310 runFileDone = true;
1311#endif
1312 }
1313 if (y->second.nightlyFile.IsOpen())
1314 {
1315 fFileSizesMap[y->second.nightlyFile.fFileName] = y->second.nightlyFile.GetWrittenSize();
1316 Message("-> "+y->second.nightlyFile.fFileName);
1317 }
1318 }
1319 }
1320#else
1321 Message("FITS output disabled at compilation");
1322#endif
1323 struct stat st;
1324 DataLoggerStats statVar;
1325 //gather log and report files sizes on disk
1326 if (fNightlyLogFile.is_open())
1327 {
1328 stat(fFullNightlyLogFileName.c_str(), &st);
1329 fFileSizesMap[fFullNightlyLogFileName] = st.st_size;
1330 }
1331 if (fNightlyReportFile.is_open())
1332 {
1333 stat(fFullNightlyReportFileName.c_str(), &st);
1334 fFileSizesMap[fFullNightlyReportFileName] = st.st_size;
1335 }
1336 if (fRunLogFile.is_open())
1337 {
1338 stat(fFullRunLogFileName.c_str(), &st);
1339 fFileSizesMap[fFullRunLogFileName] = st.st_size;
1340 }
1341 if (fRunReportFile.is_open())
1342 {
1343 stat(fFullRunReportFileName.c_str(), &st);
1344 fFileSizesMap[fFullRunReportFileName] = st.st_size;
1345 }
1346 struct statvfs vfs;
1347 if (!statvfs(fNightlyFileName.c_str(), &vfs))
1348 {
1349 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
1350 }
1351 else
1352 {
1353 str.str("");
1354 str << "Unable to retrieve stats for " << fNightlyFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1355 Error(str);;
1356 statVar.freeSpace = -1;
1357 }
1358
1359 //sum up all the file sizes. past and present
1360 statVar.sizeWritten = 0;
1361 for (std::map<std::string, long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
1362 statVar.sizeWritten += it->second;
1363 statVar.sizeWritten -= fBaseSizeNightly;
1364 statVar.sizeWritten -= fBaseSizeRun;
1365 */
1366 Message("---------------- STATS ------------------");
1367 str.str("");
1368 str << "Total Size written: " << statVar.sizeWritten << " bytes.";
1369 Message(str.str());
1370 str.str("");
1371 str << "Disk free space: " << statVar.freeSpace << " bytes.";
1372 Message(str.str());
1373 str.str("");
1374 str << "Statistics are updated every " << fStatsPeriodDuration << " seconds";
1375 if (fStatsPeriodDuration != 0)
1376 Message(str);
1377 else
1378 Message("Statistics updates are currently disabled");
1379 Message("----------- DIM SUBSCRIPTIONS -----------");
1380
1381 str.str("");
1382 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions:";
1383 Message(str.str());
1384
1385 for (std::map<const std::string, std::map<std::string, SubscriptionType>>::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1386 {
1387 Message("Server "+it->first);
1388 for (std::map<std::string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1389 Message("-> "+it2->first);
1390 }
1391 if (fIsBlackList)
1392 Message("------------- BLOCK LIST ----------------");
1393 else
1394 if (fGreyList.size() != 0)
1395 Message("----------- ALLOW LIST ------------------");
1396 for (set<string>::iterator it=fGreyList.begin(); it != fGreyList.end(); it++)
1397 Message(*it);
1398 return GetCurrentState();
1399}
1400
1401// --------------------------------------------------------------------------
1402//
1403//! turn debug mode on and off
1404//! @param evt
1405//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1406//! @returns
1407//! the new state. Which, in that case, is the current state
1408//!
1409int DataLogger::SetDebugOnOff(const Event& evt)
1410{
1411 bool backupDebug = fDebugIsOn;
1412 fDebugIsOn = evt.GetBool();
1413 if (fDebugIsOn == backupDebug)
1414 Warn("Warning: debug mode was already in the requested state");
1415 else
1416 {
1417 stringstream str;
1418 str << "Debug mode is now " << fDebugIsOn;
1419 Message(str.str());
1420 }
1421 return GetCurrentState();
1422}
1423// --------------------------------------------------------------------------
1424//
1425//! set the statistics update period duration. 0 disables the statistics
1426//! @param evt
1427//! the current event. contains the new duration.
1428//! @returns
1429//! the new state. Which, in that case, is the current state
1430//!
1431int DataLogger::SetStatsPeriod(const Event& evt)
1432{
1433 float backupDuration = fStatsPeriodDuration;
1434 fStatsPeriodDuration = evt.GetFloat();
1435 if (fStatsPeriodDuration < 0)
1436 {
1437 Error("Statistics period duration should be greater than zero. Discarding provided value.");
1438 fStatsPeriodDuration = backupDuration;
1439 return GetCurrentState();
1440 }
1441 if (fStatsPeriodDuration != fStatsPeriodDuration)
1442 {
1443 Error("Provided duration does not appear to be a valid float. discarding it.");
1444 fStatsPeriodDuration = backupDuration;
1445 return GetCurrentState();
1446 }
1447 if (backupDuration == fStatsPeriodDuration)
1448 Warn("Warning: statistics period was not modified: supplied value already in use");
1449 else
1450 {
1451 if (fStatsPeriodDuration == 0.0f)
1452 Message("Statistics are now OFF");
1453 else
1454 {
1455 stringstream str;
1456 str << "Statistics period is now " << fStatsPeriodDuration << " seconds";
1457 Message(str.str());
1458 }
1459 }
1460 return GetCurrentState();
1461}
1462// --------------------------------------------------------------------------
1463//
1464//! set the opened files service on or off.
1465//! @param evt
1466//! the current event. contains the instruction string. similar to setdebugonoff
1467//! @returns
1468//! the new state. Which, in that case, is the current state
1469//!
1470int DataLogger::SetOpenedFilesOnOff(const Event& evt)
1471{
1472 bool backupOpened = fOpenedFilesIsOn;
1473 fOpenedFilesIsOn = evt.GetBool();
1474 if (fOpenedFilesIsOn == backupOpened)
1475 Warn("Warning: opened files service mode was already in the requested state");
1476 else
1477 {
1478 stringstream str;
1479 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1480 Message(str.str());
1481 }
1482 return GetCurrentState();
1483
1484}
1485// --------------------------------------------------------------------------
1486//
1487//! set the number of subscriptions and opened fits on and off
1488//! @param evt
1489//! the current event. contains the instruction string. similar to setdebugonoff
1490//! @returns
1491//! the new state. Which, in that case, is the current state
1492//!
1493int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1494{
1495 bool backupSubs = fNumSubAndFitsIsOn;
1496 fNumSubAndFitsIsOn = evt.GetBool();
1497 if (fNumSubAndFitsIsOn == backupSubs)
1498 Warn("Warning: Number of subscriptions service mode was already in the requested state");
1499 else
1500 {
1501 stringstream str;
1502 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1503 Message(str.str());
1504 }
1505 return GetCurrentState();
1506}
1507// --------------------------------------------------------------------------
1508//
1509//! Sets the path to use for the Nightly log file.
1510//! @param evt
1511//! the event transporting the path
1512//! @returns
1513//! currently only the current state.
1514//
1515int DataLogger::ConfigureNightlyFileName(const Event& evt)
1516{
1517 if (evt.GetText() != NULL)
1518 {
1519 fNightlyFileName = std::string(evt.GetText());
1520 Message("New Nightly folder specified: " + fNightlyFileName);
1521 }
1522 else
1523 Error("Empty Nightly folder given. Please specify a valid path.");
1524
1525 return GetCurrentState();
1526}
1527// --------------------------------------------------------------------------
1528//
1529//! Sets the path to use for the run log file.
1530//! @param evt
1531//! the event transporting the path
1532//! @returns
1533//! currently only the current state
1534int DataLogger::ConfigureRunFileName(const Event& evt)
1535{
1536 if (evt.GetText() != NULL)
1537 {
1538 fRunFileName = std::string(evt.GetText());
1539 Message("New Run folder specified: " + fRunFileName);
1540 }
1541 else
1542 Error("Empty Nightly folder given. Please specify a valid path");
1543
1544 return GetCurrentState();
1545}
1546// --------------------------------------------------------------------------
1547//
1548//! Sets the run number.
1549//! @param evt
1550//! the event transporting the run number
1551//! @returns
1552//! currently only the current state
1553//TODO remove this function as the run numbers will be distributed through a dedicated service
1554int DataLogger::ConfigureRunNumber(const Event& evt)
1555{
1556 fRunNumber = evt.GetInt();
1557 std::stringstream str;
1558 str << "The new run number is: " << fRunNumber;
1559 Message(str.str());
1560 return GetCurrentState();
1561}
1562// --------------------------------------------------------------------------
1563//
1564//! Notifies the DIM service that a particular file was opened
1565//! @ param name the base name of the opened file, i.e. without path nor extension.
1566//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1567//! this is not a problem though because file are not opened so often.
1568//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1569inline void DataLogger::NotifyOpenedFile(std::string name, int type, DimDescribedService* service)
1570{
1571 if (fOpenedFilesIsOn)
1572 {
1573 if (fDebugIsOn)
1574 {
1575 stringstream str;
1576 str << "Updating files service " << service->getName() << "with code: " << type << " and file: " << name;
1577 Debug(str.str());
1578 str.str("");
1579 str << "Num subs: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS: " << fNumSubAndFitsData.numOpenFits;
1580 Debug(str.str());
1581 }
1582 OpenFileToDim fToDim;
1583 fToDim.code = type;
1584 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1585 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(int));
1586 service->setQuality(0);
1587 service->updateService();
1588 }
1589}
1590// --------------------------------------------------------------------------
1591//
1592//! Implements the Start transition.
1593//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1594//! and tries to open it.
1595//! @returns
1596//! kSM_NightlyOpen if success, kSM_BadNightlyConfig if failure
1597int DataLogger::StartPlease()
1598{
1599 if (fDebugIsOn)
1600 {
1601 Debug("Starting...");
1602 }
1603 Time time;
1604 std::stringstream sTime;
1605 sTime << time.Y() << "_" << time.M() << "_" << time.D();
1606
1607 fFullNightlyLogFileName = fNightlyFileName + '/' + sTime.str() + ".log";
1608 fNightlyLogFile.open(fFullNightlyLogFileName.c_str(), std::ios_base::out | std::ios_base::app);
1609 if (errno != 0)
1610 {
1611 std::stringstream str;
1612 str << "Unable to open Nightly Log " << fFullNightlyLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1613 Error(str);
1614 }
1615 fFullNightlyReportFileName = fNightlyFileName + '/' + sTime.str() + ".rep";
1616 fNightlyReportFile.open(fFullNightlyReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
1617 if (errno != 0)
1618 {
1619 std::stringstream str;
1620 str << "Unable to open Nightly Report " << fFullNightlyReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1621 Error(str);
1622 }
1623
1624 if (!fNightlyLogFile.is_open() || !fNightlyReportFile.is_open())
1625 {
1626 //TODO send an error message
1627 return kSM_BadNightlyConfig;
1628 }
1629 //get the size of the newly opened file.
1630 struct stat st;
1631 stat(fFullNightlyLogFileName.c_str(), &st);
1632 fBaseSizeNightly = st.st_size;
1633 stat(fFullNightlyReportFileName.c_str(), &st);
1634 fBaseSizeNightly += st.st_size;
1635 fFileSizesMap.clear();
1636 fBaseSizeRun = 0;
1637 fPreviousSize = 0;
1638 //notify that files were opened
1639 std::string actualTargetDir;
1640 if (fNightlyFileName == ".")
1641 {
1642 char currentPath[FILENAME_MAX];
1643 if (!getcwd(currentPath, sizeof(currentPath)))
1644 {
1645 if (errno != 0)
1646 {
1647 std::stringstream str;
1648 str << "Unable retrieve current path" << ". Reason: " << strerror(errno) << " [" << errno << "]";
1649 Error(str);
1650 }
1651 }
1652 actualTargetDir = currentPath;
1653 }
1654 else
1655 {
1656 actualTargetDir = fNightlyFileName;
1657 }
1658 //notify that a new file has been opened.
1659 NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 3, fOpenedNightlyFiles);
1660
1661 return kSM_NightlyOpen;
1662}
1663
1664#ifdef HAVE_FITS
1665// --------------------------------------------------------------------------
1666//
1667//! open if required a the FITS files corresponding to a given subscription
1668//! @param sub
1669//! the current DimInfo subscription being examined
1670void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub)
1671{
1672 std::string serviceName(sub.dimInfo->getName());
1673 for (unsigned int i=0;i<serviceName.size(); i++)
1674 {
1675 if (serviceName[i] == '/')
1676 {
1677 serviceName[i] = '_';
1678 break;
1679 }
1680 }
1681 Time time;
1682 std::stringstream sTime;
1683 sTime << time.Y() << "_" << time.M() << "_" << time.D();
1684 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1685 if (!sub.nightlyFile.IsOpen())
1686 {
1687 std::string partialName = fNightlyFileName + '/' + sTime.str() + '_' + serviceName + ".fits";
1688 AllocateFITSBuffers(sub);
1689 //get the size of the file we're about to open
1690 if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1691 {
1692 struct stat st;
1693 if (!stat(partialName.c_str(), &st))
1694 fBaseSizeNightly += st.st_size;
1695 fFileSizesMap[partialName] = 0;
1696 }
1697 sub.nightlyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, Out());
1698 //notify the opening
1699 std::string actualTargetDir;
1700 if (fNightlyFileName == ".")
1701 {
1702 char currentPath[FILENAME_MAX];
1703 if (getcwd(currentPath, sizeof(currentPath)))
1704 actualTargetDir = currentPath;
1705 }
1706 else
1707 {
1708 actualTargetDir = fNightlyFileName;
1709 }
1710 NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 7, fOpenedNightlyFiles);
1711 if (fNumSubAndFitsIsOn)
1712 fNumSubAndFits->updateService();
1713 if (fDebugIsOn)
1714 {
1715 stringstream str;
1716 str << "Opened Nightly FITS: " << partialName << " and table: FACT-" << serviceName << ".current number of opened FITS: " << fNumSubAndFitsData.numOpenFits;
1717 Debug(str.str());
1718 }
1719 }
1720 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1721 {//buffer for the run file have already been allocated when doing the Nightly file
1722 std::stringstream sRun;
1723 sRun << fRunNumber;
1724#ifdef ONE_RUN_FITS_ONLY
1725 std::string partialName = fRunFileName + '/' + sRun.str() + ".fits";
1726 if (fRunFitsFile == NULL)
1727 {
1728#else
1729 std::string partialName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits";
1730#endif
1731 //get the size of the file we're about to open
1732 if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1733 {
1734 struct stat st;
1735 if (!stat(partialName.c_str(), &st))
1736 fBaseSizeRun += st.st_size;
1737 else
1738 fBaseSizeRun = 0;
1739 fFileSizesMap[partialName] = 0;
1740 }
1741#ifdef ONE_RUN_FITS_ONLY
1742 try
1743 {
1744 fRunFitsFile = new FITS(partialName, RWmode::Write);
1745 (fNumSubAndFitsData.numOpenFits)++;
1746 }
1747 catch (CCfits::FitsError e)
1748 {
1749 std::stringstream str;
1750 str << "Could not open FITS Run file " << partialName << " reason: " << e.message();
1751 Error(str);
1752 fRunFitsFile = NULL;
1753 }
1754#endif
1755 std::string actualTargetDir;
1756 if (fRunFileName == ".")
1757 {
1758 char currentPath[FILENAME_MAX];
1759 if (getcwd(currentPath, sizeof(currentPath)))
1760 actualTargetDir = currentPath;
1761 }
1762 else
1763 {
1764 actualTargetDir = fRunFileName;
1765 }
1766 NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 7, fOpenedRunFiles);// + '_' + serviceName, 4);
1767#ifdef ONE_RUN_FITS_ONLY
1768 }
1769 sub.runFile.Open(partialName, serviceName, fRunFitsFile, &fNumSubAndFitsData.numOpenFits, Out());
1770#else
1771 sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, Out());
1772#endif //one_run_fits_only
1773 if (fNumSubAndFitsIsOn)
1774 fNumSubAndFits->updateService();
1775 if (fDebugIsOn)
1776 {
1777 stringstream str;
1778 str << "Opened Run FITS: " << partialName << " and table: FACT-" << serviceName << ".current number of opened FITS: " << fNumSubAndFitsData.numOpenFits;
1779 Debug(str.str());
1780 }
1781 }
1782}
1783// --------------------------------------------------------------------------
1784//
1785void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
1786{
1787 int size = sub.dimInfo->getSize();
1788
1789 //Init the time columns of the file
1790 Description dateDesc(std::string("Time"), std::string("Modified Julian Date"), std::string("MjD"));
1791 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1792 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1793
1794 Description QoSDesc("Qos", "Quality of service", "None");
1795 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1796 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1797
1798 const Converter::FormatList flist = sub.fConv->GetList();
1799 // Compilation failed
1800 if (flist.empty() || flist.back().first.second!=0)
1801 {
1802 Error("Compilation of format string failed.");
1803 return;
1804 }
1805
1806 //we've got a nice structure describing the format of this service's messages.
1807 //Let's create the appropriate FITS columns
1808 std::vector<std::string> dataFormatsLocal;
1809 for (unsigned int i=0;i<flist.size()-1;i++)
1810 {
1811 std::stringstream dataQualifier;
1812
1813 dataQualifier << flist[i].second.first;
1814 switch (flist[i].first.first->name()[0])
1815 {//TODO handle all the data format cases
1816 case 'c':
1817 case 'C':
1818 dataQualifier.str("S");
1819 break;
1820 case 's':
1821 dataQualifier << "I";
1822 break;
1823 case 'i':
1824 case 'I':
1825 dataQualifier << "J";
1826 break;
1827 case 'l':
1828 case 'L':
1829 dataQualifier << "J";
1830 //TODO triple check that in FITS, long = int
1831 break;
1832 case 'f':
1833 case 'F':
1834 dataQualifier << "E";
1835 break;
1836 case 'd':
1837 case 'D':
1838 dataQualifier << "D";
1839 break;
1840 case 'x':
1841 case 'X':
1842 dataQualifier << "K";
1843 break;
1844 case 'S':
1845 //for strings, the number of elements I get is wrong. Correct it
1846 dataQualifier.str(""); //clear
1847 dataQualifier << size-1 << "A";
1848 size = size-1;
1849 break;
1850
1851 default:
1852 Fatal("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 1198.");
1853 };
1854 //we skip the variable length strings for now (in fits only)
1855 if (dataQualifier.str() != "S")
1856 dataFormatsLocal.push_back(dataQualifier.str());
1857 }
1858 sub.nightlyFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1859 sub.runFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1860}
1861// --------------------------------------------------------------------------
1862//
1863//! write a dimInfo data to its corresponding FITS files
1864//
1865void DataLogger::WriteToFITS(SubscriptionType& sub)
1866{
1867 //nightly File status (open or not) already checked
1868 if (sub.nightlyFile.IsOpen())
1869 {
1870 sub.nightlyFile.Write(sub.fConv);
1871 if (fDebugIsOn)
1872 {
1873 Debug("Writing to nightly FITS " + sub.nightlyFile.fFileName);
1874 }
1875 }
1876 if (sub.runFile.IsOpen())
1877 {
1878 sub.runFile.Write(sub.fConv);
1879 if (fDebugIsOn)
1880 {
1881 Debug("Writing to Run FITS " + sub.runFile.fFileName);
1882 }
1883 }
1884}
1885#endif //if has_fits
1886// --------------------------------------------------------------------------
1887//
1888//! Implements the StartRun transition.
1889//! Concatenates the given path for the run file and the filename itself (based on the run number),
1890//! and tries to open it.
1891//! @returns
1892//! kSM_Logging if success, kSM_BadRunConfig if failure.
1893int DataLogger::StartRunPlease()
1894{
1895 if (fDebugIsOn)
1896 {
1897 Debug("Starting Run Logging...");
1898 }
1899 //attempt to open run file with current parameters
1900// if (fRunNumber == -1)
1901// return kSM_BadRunConfig;
1902 std::stringstream sRun;
1903 sRun << fRunNumber;
1904 fFullRunLogFileName = fRunFileName + '/' + sRun.str() + ".log";
1905 fRunLogFile.open(fFullRunLogFileName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate
1906 if (errno != 0)
1907 {
1908 std::stringstream str;
1909 str << "Unable to open run Log " << fFullRunLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1910 Error(str);
1911 }
1912 fFullRunReportFileName = fRunFileName + '/' + sRun.str() + ".rep";
1913 fRunReportFile.open(fFullRunReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
1914 if (errno != 0)
1915 {
1916 std::stringstream str;
1917 str << "Unable to open run report " << fFullRunReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1918 Error(str);
1919 }
1920
1921 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1922 {
1923 //TODO send an error message
1924 return kSM_BadRunConfig;
1925 }
1926 //get the size of the newly opened file.
1927 struct stat st;
1928 fBaseSizeRun = 0;
1929 if (fFileSizesMap.find(fFullRunLogFileName) == fFileSizesMap.end())
1930 {
1931 stat(fFullRunLogFileName.c_str(), &st);
1932 if (errno != 0)
1933 {
1934 std::stringstream str;
1935 str << "Unable to stat " << fFullRunLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1936 Error(str);
1937 }
1938 else
1939 fBaseSizeRun += st.st_size;
1940 fFileSizesMap[fFullRunLogFileName] = 0;
1941 }
1942 if (fFileSizesMap.find(fFullRunReportFileName) == fFileSizesMap.end())
1943 {
1944 stat(fFullRunReportFileName.c_str(), &st);
1945 if (errno != 0)
1946 {
1947 std::stringstream str;
1948 str << "Unable to stat " << fFullRunReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1949 Error(str);
1950 }
1951 else
1952 fBaseSizeRun += st.st_size;
1953 fFileSizesMap[fFullRunReportFileName] = 0;
1954 }
1955 std::string actualTargetDir;
1956 if (fRunFileName == ".")
1957 {
1958 char currentPath[FILENAME_MAX];
1959 if (!getcwd(currentPath, sizeof(currentPath)))
1960 {
1961 if (errno != 0)
1962 {
1963 std::stringstream str;
1964 str << "Unable to retrieve the current path" << ". Reason: " << strerror(errno) << " [" << errno << "]";
1965 Error(str);
1966 }
1967 }
1968 actualTargetDir = currentPath;
1969 }
1970 else
1971 {
1972 actualTargetDir = fRunFileName;
1973 }
1974 NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 3, fOpenedRunFiles);
1975
1976 return kSM_Logging;
1977}
1978// --------------------------------------------------------------------------
1979//
1980//! Implements the StopRun transition.
1981//! Attempts to close the run file.
1982//! @returns
1983//! kSM_WaitingRun if success, kSM_FatalError otherwise
1984int DataLogger::StopRunPlease()
1985{
1986 if (fDebugIsOn)
1987 {
1988 Debug("Stopping Run Logging...");
1989 }
1990 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1991 return kSM_FatalError;
1992 if (fRunLogFile.is_open())
1993 fRunLogFile.close();
1994 if (fRunReportFile.is_open())
1995 fRunReportFile.close();
1996#ifdef HAVE_FITS
1997 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1998 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1999 {
2000 if (j->second.runFile.IsOpen())
2001 j->second.runFile.Close();
2002 }
2003#ifdef ONE_RUN_FITS_ONLY
2004 if (fRunFitsFile != NULL)
2005 {
2006 delete fRunFitsFile;
2007 fRunFitsFile = NULL;
2008 (fNumSubAndFitsData.numOpenFits)--;
2009 }
2010#endif
2011#endif
2012 NotifyOpenedFile("", 0, fOpenedRunFiles);
2013 if (fNumSubAndFitsIsOn)
2014 fNumSubAndFits->updateService();
2015 return kSM_WaitingRun;
2016
2017}
2018// --------------------------------------------------------------------------
2019//
2020//! Implements the Stop and Reset transitions.
2021//! Attempts to close any openned file.
2022//! @returns
2023//! kSM_Ready
2024int DataLogger::GoToReadyPlease()
2025{
2026 if (fDebugIsOn)
2027 {
2028 Debug("Going to the Ready state...");
2029 }
2030 if (fNightlyLogFile.is_open())
2031 fNightlyLogFile.close();
2032 if (fNightlyReportFile.is_open())
2033 fNightlyReportFile.close();
2034
2035 if (fRunLogFile.is_open())
2036 fRunLogFile.close();
2037 if (fRunReportFile.is_open())
2038 fRunReportFile.close();
2039
2040#ifdef HAVE_FITS
2041 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2042 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2043 {
2044 if (j->second.nightlyFile.IsOpen())
2045 j->second.nightlyFile.Close();
2046 if (j->second.runFile.IsOpen())
2047 j->second.runFile.Close();
2048 }
2049#ifdef ONE_RUN_FITS_ONLY
2050 if (fRunFitsFile != NULL)
2051 {
2052 delete fRunFitsFile;
2053 fRunFitsFile = NULL;
2054 (fNumSubAndFitsData.numOpenFits)--;
2055 }
2056#endif
2057#endif
2058 if (GetCurrentState() == kSM_Logging)
2059 NotifyOpenedFile("", 0, fOpenedRunFiles);
2060 if (GetCurrentState() == kSM_Logging ||
2061 GetCurrentState() == kSM_WaitingRun ||
2062 GetCurrentState() == kSM_NightlyOpen)
2063 {
2064 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2065 if (fNumSubAndFitsIsOn)
2066 fNumSubAndFits->updateService();
2067 }
2068 return kSM_Ready;
2069}
2070// --------------------------------------------------------------------------
2071//
2072//! Implements the transition towards kSM_WaitingRun
2073//! Does nothing really.
2074//! @returns
2075//! kSM_WaitingRun
2076int DataLogger::NightlyToWaitRunPlease()
2077{
2078 if (fDebugIsOn)
2079 {
2080 Debug("Going to Wait Run Number state...");
2081 }
2082 return kSM_WaitingRun;
2083}
2084
2085void DataLogger::Setup(Configuration& conf)
2086{
2087 //Set the block or allow list
2088 fGreyList.clear();
2089 if (conf.Has("block"))
2090 {
2091 vector<string> vec = conf.Get<vector<string>>("block");
2092 if (vec.size() != 0)
2093 {
2094 fIsBlackList = true;
2095 if (fDebugIsOn)
2096 Debug("Setting BLOCK list:");
2097 }
2098 else if (conf.Has("allow"))
2099 {
2100 vec = conf.Get<vector<string>>("allow");
2101 if (vec.size() != 0)
2102 {
2103 fIsBlackList = false;
2104 if (fDebugIsOn)
2105 Debug("Setting ALLOW list:");
2106 }
2107 }
2108 for (vector<string>::iterator it=vec.begin(); it != vec.end(); it++)
2109 {
2110 fGreyList.insert(*it);
2111 if (fDebugIsOn)
2112 Debug(" " + *it);
2113 }
2114 }
2115}
2116/*
2117void DataLogger::setBlackWhiteList(const std::string& black, bool isBlack)
2118{
2119 if (fDebugIsOn)
2120 {
2121 if (isBlack)
2122 Debug("Setting BLACK list: " + black);
2123 else
2124 Debug("Setting WHITE list: " + black);
2125 }
2126 fGreyList.clear();
2127 stringstream stream(black);
2128
2129 string buffer;
2130 while (getline(stream, buffer, '|')) {
2131 fGreyList.insert(buffer);
2132 }
2133 fIsBlackList = isBlack;
2134}
2135*/
2136// --------------------------------------------------------------------------
2137
2138int RunDim(Configuration &conf)
2139{
2140 WindowLog wout;
2141
2142 //log.SetWindow(stdscr);
2143 if (conf.Has("log"))
2144 if (!wout.OpenLogFile(conf.Get<std::string>("log")))
2145 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
2146
2147 // Start io_service.Run to use the StateMachineImp::Run() loop
2148 // Start io_service.run to only use the commandHandler command detaching
2149 DataLogger logger(wout);
2150 logger.Run(true);
2151
2152 return 0;
2153}
2154
2155void RunThread(DataLogger* logger)
2156{
2157 // This is necessary so that the StateMachine Thread can signal the
2158 // Readline thread to exit
2159 logger->Run(true);
2160 Readline::Stop();
2161}
2162
2163template<class T>
2164int RunShell(Configuration &conf)
2165{
2166 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
2167
2168 WindowLog &win = shell.GetStreamIn();
2169 WindowLog &wout = shell.GetStreamOut();
2170
2171 if (conf.Has("log"))
2172 if (!wout.OpenLogFile(conf.Get<std::string>("log")))
2173 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
2174
2175 DataLogger logger(wout);
2176
2177 logger.Setup(conf);
2178
2179 shell.SetReceiver(logger);
2180
2181 boost::thread t(boost::bind(RunThread, &logger));
2182
2183 shell.Run(); // Run the shell
2184
2185 logger.Stop();
2186
2187 //Wait until the StateMachine has finished its thread
2188 //before returning and destroyinng the dim objects which might
2189 //still be in use.
2190 t.join();
2191
2192 return 0;
2193}
2194
2195/*
2196 Extract usage clause(s) [if any] for SYNOPSIS.
2197 Translators: "Usage" and "or" here are patterns (regular expressions) which
2198 are used to match the usage synopsis in program output. An example from cp
2199 (GNU coreutils) which contains both strings:
2200 Usage: cp [OPTION]... [-T] SOURCE DEST
2201 or: cp [OPTION]... SOURCE... DIRECTORY
2202 or: cp [OPTION]... -t DIRECTORY SOURCE...
2203 */
2204void PrintUsage()
2205{
2206 cout << "\n"
2207 "The data logger connects to all available Dim services and "
2208 "writes them to ascii and fits files.\n"
2209 "\n"
2210 "The default is that the program is started without user interaction. "
2211 "All actions are supposed to arrive as DimCommands. Using the -c "
2212 "option, a local shell can be initialized. With h or help a short "
2213 "help message about the usage can be brought to the screen.\n"
2214 "\n"
2215 "Usage: dataLogger [-c type] [OPTIONS]\n"
2216 " or: dataLogger [OPTIONS]\n";
2217 cout << endl;
2218
2219}
2220
2221void PrintHelp()
2222{
2223 /* Additional help text which is printed after the configuration
2224 options goes here */
2225 cout <<
2226 "\n"
2227 "The block option has priority over the allow, "
2228 "i.e. if both are present, only the block list is kept. "
2229 "If only a server name or service without its server prefix is given "
2230 "then all the services of that server, or all the services that "
2231 "correspond to the given suffix are ignored or considered.\n"
2232 "\n"
2233 "For example, block=DIS_DNS will skip all the services offered by "
2234 "the DIS_DNS server, while block=SERVICE_LIST will skip all the "
2235 "SERVICE_LIST services offered by any server.\n"
2236 "\n"
2237 "The commands offered by the dataLoger are the following: \n";
2238 cout << setw(20) << DataLogger::fConfigDay << " : specify the path where to put the nightly files\n";
2239 cout << setw(20) << DataLogger::fConfigRun << " : specify the path where to put the run files\n";
2240 cout << setw(20) << DataLogger::fConfigRunNumber << " : specify the run number\n";
2241 cout << setw(20) << DataLogger::fConfigLog << " : log a particular message\n";
2242 cout << setw(20) << DataLogger::fTransStart << " : start the nightly logging\n";
2243 cout << setw(20) << DataLogger::fTransStop << " : stop the nightly logging\n";
2244 cout << setw(20) << DataLogger::fTransStartRun << " : start the run logging\n";
2245 cout << setw(20) << DataLogger::fTransStopRun << " : stop the run logging\n";
2246 cout << setw(20) << DataLogger::fTransReset << " : stop any logging and/or recover from an error state\n";
2247 cout << setw(20) << DataLogger::fTransWait << " : go to the wait for run number state\n";
2248 cout << setw(20) << DataLogger::fPrintCommand << " : print the current state of the logger to the shell\n";
2249 cout << setw(20) << DataLogger::fDebugOnOff << " : turn on or off the debug mode\n";
2250 cout << setw(20) << DataLogger::fStatsPeriod << " : set the periodicity of the statistics. 0 disable them\n";
2251 cout << endl;
2252}
2253
2254void SetupConfiguration(Configuration &conf)
2255{
2256 const string n = conf.GetName()+".log";
2257
2258 po::options_description config("Program options");
2259 config.add_options()
2260 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
2261 ("log,l", var<string>(n), "Write log-file")
2262 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
2263 ("block,b", vars<string>(), "Black-list of services")
2264 ("allow,a", vars<string>(), "White-list of services")
2265 ;
2266
2267 conf.AddEnv("dns", "DIM_DNS_NODE");
2268
2269 conf.AddOptions(config);
2270}
2271
2272int main(int argc, const char* argv[])
2273{
2274 Configuration conf(argv[0]);
2275 conf.SetPrintUsage(PrintUsage);
2276 SetupConfiguration(conf);
2277
2278 po::variables_map vm;
2279 try
2280 {
2281 vm = conf.Parse(argc, argv);
2282 }
2283 catch (std::exception &e)
2284 {
2285#if BOOST_VERSION > 104000
2286 po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
2287 if (MO)
2288 cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
2289 else
2290#endif
2291 cout << "Error: " << e.what() << endl;
2292 cout << endl;
2293
2294 return -1;
2295 }
2296
2297 if (conf.HasPrint())
2298 return -1;
2299
2300 if (conf.HasVersion())
2301 {
2302 FACT::PrintVersion(argv[0]);
2303 return -1;
2304 }
2305
2306 if (conf.HasHelp())
2307 {
2308 PrintHelp();
2309 return -1;
2310 }
2311
2312 Dim::Setup(conf.Get<string>("dns"));
2313
2314 try
2315 {
2316 // No console access at all
2317 if (!conf.Has("console"))
2318 return RunDim(conf);
2319
2320 // Console access w/ and w/o Dim
2321 if (conf.Get<int>("console")==0)
2322 return RunShell<LocalShell>(conf);
2323 else
2324 return RunShell<LocalConsole>(conf);
2325 }
2326 catch (std::exception& e)
2327 {
2328 cerr << "Exception: " << e.what() << endl;
2329 return -1;
2330 }
2331
2332 return 0;
2333}
Note: See TracBrowser for help on using the repository browser.