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

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