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

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