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

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