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

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