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

Last change on this file since 10496 was 10489, checked in by lyard, 14 years ago
Added a number of subscriptions service and better error handling
File size: 49.5 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="DailyOpen"]
17 w [label="WaitingRun"]
18 l [label="Logging"]
19 b [label="BadDailyconfig" color="red"]
20 c [label="BadRunConfig" color="red"]
21
22 e -> r
23 r -> e
24 r -> d
25 r -> b
26 d -> w
27 d -> r
28 w -> r
29 l -> r
30 l -> w
31 b -> d
32 w -> c
33 w -> l
34 b -> r
35 c -> r
36 c -> l
37 }
38 \enddot
39
40 @todo
41 - Retrieve also the messages, not only the infos
42 */
43 //****************************************************************
44#include "Event.h"
45#include "Time.h"
46#include "StateMachineDim.h"
47#include "WindowLog.h"
48#include "Configuration.h"
49#include "ServiceList.h"
50#include "Converter.h"
51#include "MessageImp.h"
52#include "LocalControl.h"
53#include "DimDescriptionService.h"
54
55#include "Description.h"
56
57//for getting stat of opened files
58#include <unistd.h>
59//for getting disk free space
60#include <sys/statvfs.h>
61//for getting files sizes
62#include <sys/stat.h>
63
64#define HAS_FITS
65//#define ONE_RUN_FITS_ONLY
66
67#include <fstream>
68
69#include <boost/bind.hpp>
70#include <boost/thread.hpp>
71
72#ifdef HAS_FITS
73#include "Fits.h"
74#endif
75
76//Dim structures
77struct DataLoggerStats {
78 long long sizeWritten;
79 long long freeSpace;
80 long writingRate;
81};
82
83struct NumSubAndFitsType {
84 int numSubscriptions;
85 int numOpenFits;
86};
87//For debugging DIM's services
88class MyService
89{
90public:
91 MyService(){};
92 MyService(std::string, std::string, void*, int){};
93 MyService(std::string, const char*){};
94 void updateService(){};
95 void updateService(void*, int){};
96 void setQuality(int){};
97};
98class DataLogger : public StateMachineDim, DimInfoHandler
99{
100public:
101 /// The list of existing states specific to the DataLogger
102 enum
103 {
104 kSM_DailyOpen = 20, ///< Daily file openned and writing
105 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
106 kSM_Logging = 40, ///< both files openned and writing
107 kSM_BadDailyConfig = 0x101, ///< the folder specified for daily logging does not exist or has bad permissions
108 kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number
109 } localstates_t;
110
111 DataLogger(std::ostream &out);
112 ~DataLogger();
113
114private:
115 //Define all the data structure specific to the DataLogger here
116 /// ofstream for the dailyLogfile
117 std::ofstream fDailyLogFile;
118 /// ofstream for the run-specific Log file
119 std::ofstream fRunLogFile;
120
121 /// ofstream for the daily report file
122 std::ofstream fDailyReportFile;
123 /// ofstream for the run-specific report file
124 std::ofstream fRunReportFile;
125 /// base path of the dailyfile
126 std::string fDailyFileName;
127 ///base path of the run file
128 std::string fRunFileName;
129 ///run number (-1 means no run number specified)
130 int fRunNumber;
131 ///Current Service Quality
132 int fQuality;
133 ///Modified Julian Date
134 double fMjD;
135
136 ///Define all the static names
137 static const char* fConfigDay;
138 static const char* fConfigRun;
139 static const char* fConfigRunNumber;
140 static const char* fConfigLog;
141 static const char* fTransStart;
142 static const char* fTransStop;
143 static const char* fTransStartRun;
144 static const char* fTransStopRun;
145 static const char* fTransReset;
146 static const char* fTransWait;
147 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
148
149 //overloading of DIM's infoHandler function
150 void infoHandler();
151
152 ///for obtaining the name of the existing services
153 ServiceList fServiceList;
154
155
156 ///A std pair to store both the DimInfo name and the actual DimInfo pointer
157// typedef std::pair<std::string, DimStampedInfo*> subscriptionType;
158 ///All the services to which we've subscribed to. Sorted by server name
159// std::map<const std::string, std::vector<subscriptionType > > fServiceSubscriptions;
160
161 ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file
162 struct SubscriptionType
163 {
164#ifdef HAS_FITS
165 ///daily FITS output file
166 Fits dailyFile;
167 ///run-specific FITS output file
168 Fits runFile;
169#endif
170 ///the actual dimInfo pointer
171 DimStampedInfo* dimInfo;
172 ///the converter for outputting the data according to the format
173 Converter* fConv;
174 ///the number of existing handlers to this structure.
175 ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor
176 int* numCopies;
177 void operator = (const SubscriptionType& other)
178 {
179#ifdef HAS_FITS
180 dailyFile = other.dailyFile;
181 runFile = other.runFile;
182#endif
183 dimInfo = other.dimInfo;
184 numCopies = other.numCopies;
185 fConv = other.fConv;
186 (*numCopies)++;
187 }
188 SubscriptionType(const SubscriptionType& other)
189 {
190#ifdef HAS_FITS
191 dailyFile = other.dailyFile;
192 runFile = other.runFile;
193#endif
194 dimInfo = other.dimInfo;
195 numCopies = other.numCopies;
196 fConv = other.fConv;
197 (*numCopies)++;
198 }
199 SubscriptionType(DimStampedInfo* info)
200 {
201 dimInfo = info;
202 fConv = NULL;
203 numCopies = new int(1);
204 }
205 SubscriptionType()
206 {
207 dimInfo = NULL;
208 fConv = NULL;
209 numCopies = new int(1);
210 }
211 ~SubscriptionType()
212 {
213 if (numCopies)
214 (*numCopies)--;
215 if (numCopies)
216 if (*numCopies < 1)
217 {
218 if (dimInfo)
219 delete dimInfo;
220#ifdef HAS_FITS
221 if (dailyFile.IsOpen())
222 dailyFile.Close();
223 if (runFile.IsOpen())
224 runFile.Close();
225#endif
226 if (numCopies)
227 delete numCopies;
228 delete fConv;
229 fConv = NULL;
230 dimInfo = NULL;
231 numCopies = NULL;
232 }
233 }
234 };
235 typedef std::map<const std::string, std::map<std::string, SubscriptionType>> SubscriptionsListType;
236 ///All the services to which we have subscribed to, sorted by server name.
237 SubscriptionsListType fServiceSubscriptions;
238
239
240 ///Reporting method for the services info received
241 void ReportPlease(DimInfo* I, SubscriptionType& sub);
242
243 ///Configuration of the daily file path
244 int ConfigureDailyFileName(const Event& evt);
245 ///Configuration fo the file name
246 int ConfigureRunFileName(const Event& evt);
247 ///DEPREC - configuration of the run number
248 int ConfigureRunNumber(const Event& evt);
249 ///logging method for the messages
250 int LogMessagePlease(const Event& evt);
251 ///checks whether or not the current info being treated is a run number
252 void CheckForRunNumber(DimInfo* I);
253 /// start transition
254 int StartPlease();
255 ///from waiting to logging transition
256 int StartRunPlease();
257 /// from logging to waiting transition
258 int StopRunPlease();
259 ///stop and reset transition
260 int GoToReadyPlease();
261 ///from dailyOpen to waiting transition
262 int DailyToWaitRunPlease();
263#ifdef HAS_FITS
264 ///Open fits files
265 void OpenFITSFilesPlease(SubscriptionType& sub);
266 ///Write data to FITS files
267 void WriteToFITS(SubscriptionType& sub);
268 ///Allocate the buffers required for fits
269 void AllocateFITSBuffers(SubscriptionType& sub);
270
271#ifdef ONE_RUN_FITS_ONLY
272 ///FITS file for runs. only one, hence dealt with in the dataLogger itself
273 FITS* fRunFitsFile;
274#endif //one_run_fits_only
275#endif//has_fits
276public:
277 ///checks with fServiceList whether or not the services got updated
278 bool CheckForServicesUpdate();
279
280private:
281 ///monitoring notification loop
282 void ServicesMonitoring();
283 ///services notification thread
284 boost::thread fMonitoringThread;
285 ///end of the monitoring
286 bool fContinueMonitoring;
287 ///required for accurate monitoring
288 std::map<std::string, long long> fFileSizesMap;
289 std::string fFullDailyLogFileName;
290 std::string fFullDailyReportFileName;
291 std::string fFullRunLogFileName;
292 std::string fFullRunReportFileName;
293 long long fBaseSizeDaily;
294 long long fPreviousSize;
295 long long fBaseSizeRun;
296 ///Service for opened files
297 DimDescribedService* fOpenedDailyFiles;
298 DimDescribedService* fOpenedRunFiles;
299 DimDescribedService* fNumSubAndFits;
300 NumSubAndFitsType fNumSubAndFitsData;
301// char* fDimBuffer;
302 inline void NotifyOpenedFile(std::string name, int type, DimService* service);
303 inline void NotifyOpenedFile(std::string , int , MyService* ){}
304
305}; //DataLogger
306
307//static members initialization
308//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 ?
309const char* DataLogger::fConfigDay = "CONFIG_DAY";
310const char* DataLogger::fConfigRun = "CONFIG_RUN";
311const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER";
312const char* DataLogger::fConfigLog = "LOG";
313const char* DataLogger::fTransStart = "START";
314const char* DataLogger::fTransStop = "STOP";
315const char* DataLogger::fTransStartRun = "START_RUN";
316const char* DataLogger::fTransStopRun = "STOP_RUN";
317const char* DataLogger::fTransReset = "RESET";
318const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER";
319const char* DataLogger::fRunNumberInfo = "RUN_NUMBER";
320
321
322void DataLogger::ServicesMonitoring()
323{
324 //create the DIM service
325 int dataSize = 2*sizeof(long long) + sizeof(long);
326
327 DataLoggerStats statVar;
328 statVar.sizeWritten = 0;
329 statVar.freeSpace = 0;
330 statVar.writingRate = 0;
331
332 struct statvfs vfs;
333 if (!statvfs(fDailyFileName.c_str(), &vfs))
334 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
335 else
336 statVar.freeSpace = -1;
337// DimDescribedService srvc((GetName()+"/STATS").c_str(), "x:2;l:1", &statVar, dataSize,//static_cast<void*>(data), dataSize,
338// "Add description here");
339 DimDescribedService srvc ("DATA_LOGGER/STATS", "X:2;L:1", &statVar, dataSize, "Add description here");
340 double deltaT = 1;
341 fPreviousSize = 0;
342 bool statWarning = false;
343 //loop-wait for broadcast
344 while (fContinueMonitoring)
345 {
346 sleep(deltaT);
347 //update the fits files sizes
348#ifdef HAS_FITS
349 SubscriptionsListType::iterator x;
350 std::map<std::string, SubscriptionType>::iterator y;
351 bool runFileDone = false;
352 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
353 {
354 for (y=x->second.begin(); y != x->second.end(); y++)
355 {
356 if (y->second.runFile.IsOpen() && !runFileDone)
357 {
358 fFileSizesMap[y->second.runFile.fFileName] = y->second.runFile.GetWrittenSize();
359 runFileDone = true;
360 }
361 if (y->second.dailyFile.IsOpen())
362 fFileSizesMap[y->second.dailyFile.fFileName] = y->second.dailyFile.GetWrittenSize();
363 }
364 }
365#endif
366 struct stat st;
367 //gather log and report files sizes on disk
368 if (fDailyLogFile.is_open())
369 {
370 stat(fFullDailyLogFileName.c_str(), &st);
371 fFileSizesMap[fFullDailyLogFileName] = st.st_size;
372 }
373 if (fDailyReportFile.is_open())
374 {
375 stat(fFullDailyReportFileName.c_str(), &st);
376 fFileSizesMap[fFullDailyReportFileName] = st.st_size;
377 }
378 if (fRunLogFile.is_open())
379 {
380 stat(fFullRunLogFileName.c_str(), &st);
381 fFileSizesMap[fFullRunLogFileName] = st.st_size;
382 }
383 if (fRunReportFile.is_open())
384 {
385 stat(fFullRunReportFileName.c_str(), &st);
386 fFileSizesMap[fFullRunReportFileName] = st.st_size;
387 }
388
389 if (!statvfs(fDailyFileName.c_str(), &vfs))
390 {
391 statVar.freeSpace = vfs.f_bsize*vfs.f_bavail;
392 statWarning = false;
393 }
394 else
395 {
396 std::stringstream str;
397 str << "Unable to retrieve stats for " << fDailyFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
398 if (!statWarning)
399 Error(str);
400 statWarning = true;
401 statVar.freeSpace = -1;
402 }
403
404 //sum up all the file sizes. past and present
405 statVar.sizeWritten = 0;
406 for (std::map<std::string, long long>::iterator it=fFileSizesMap.begin(); it != fFileSizesMap.end(); it++)
407 statVar.sizeWritten += it->second;
408 statVar.sizeWritten -= fBaseSizeDaily;
409 statVar.sizeWritten -= fBaseSizeRun;
410 //FIXME get the actual time elapsed
411 statVar.writingRate = (statVar.sizeWritten - fPreviousSize)/deltaT;
412 fPreviousSize = statVar.sizeWritten;
413 if (statVar.writingRate != 0) //if data has been written
414 {
415//std::cout << "rate: " << statVar.writingRate << std::endl;
416 srvc.updateService(&statVar, dataSize);//static_cast<void*>(data), dataSize);
417 }
418 }
419}
420
421
422// --------------------------------------------------------------------------
423//
424//! Default constructor. The name of the machine is given DATA_LOGGER
425//! and the state is set to kSM_Ready at the end of the function.
426//
427//!Setup the allows states, configs and transitions for the data logger
428//
429DataLogger::DataLogger(std::ostream &out) : StateMachineDim(out, "DATA_LOGGER")
430{
431 //initialize member data
432 fDailyFileName = ".";//"/home/lyard/log";//
433 fRunFileName = ".";//"/home/lyard/log";
434 fRunNumber = 12345;
435#ifdef HAS_FITS
436#ifdef ONE_RUN_FITS_ONLY
437 fRunFitsFile = NULL;
438#endif
439#endif
440 //Give a name to this machine's specific states
441 AddStateName(kSM_DailyOpen, "DailyFileOpen", "Add description here");
442 AddStateName(kSM_WaitingRun, "WaitForRun", "Add description here");
443 AddStateName(kSM_Logging, "Logging", "Add description here");
444 AddStateName(kSM_BadDailyConfig, "ErrDailyFolder", "Add description here");
445 AddStateName(kSM_BadRunConfig, "ErrRunFolder", "Add description here");
446
447 /*Add the possible transitions for this machine*/
448 AddTransition(kSM_DailyOpen, fTransStart, kSM_Ready, kSM_BadDailyConfig)
449 (boost::bind(&DataLogger::StartPlease, this))
450 ("start the daily logging. daily file location must be specified already");
451
452 AddTransition(kSM_Ready, fTransStop, kSM_DailyOpen, kSM_WaitingRun, kSM_Logging)
453 (boost::bind(&DataLogger::GoToReadyPlease, this))
454 ("stop the data logging");
455
456 AddTransition(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig)
457 (boost::bind(&DataLogger::StartRunPlease, this))
458 ("start the run logging. run file location must be specified already.");
459
460 AddTransition(kSM_WaitingRun, fTransStopRun, kSM_Logging)
461 (boost::bind(&DataLogger::StopRunPlease, this))
462 ("");
463
464 AddTransition(kSM_Ready, fTransReset, kSM_Error, kSM_BadDailyConfig, kSM_BadRunConfig, kSM_Error)
465 (boost::bind(&DataLogger::GoToReadyPlease, this))
466 ("transition to exit error states. dunno if required or not, would close the daily file if already openned.");
467
468 AddTransition(kSM_WaitingRun, fTransWait, kSM_DailyOpen)
469 (boost::bind(&DataLogger::DailyToWaitRunPlease, this));
470
471 /*Add the possible configurations for this machine*/
472
473 AddConfiguration(fConfigDay, "C", kSM_Ready, kSM_BadDailyConfig)
474 (boost::bind(&DataLogger::ConfigureDailyFileName, this, _1))
475 ("configure the daily file location. cannot be done before the file is actually opened");
476
477 AddConfiguration(fConfigRun, "C", kSM_Ready, kSM_BadDailyConfig, kSM_DailyOpen, kSM_WaitingRun, kSM_BadRunConfig)
478 (boost::bind(&DataLogger::ConfigureRunFileName, this, _1))
479 ("configure the run file location. cannot be done before the file is actually opened, and not in a dailly related error.");
480
481 //Provide a logging command
482 //I get the feeling that I should be going through the EventImp
483 //instead of DimCommand directly, mainly because the commandHandler
484 //is already done in StateMachineImp.cc
485 //Thus I'll simply add a configuration, which I will treat as the logging command
486 AddConfiguration(fConfigLog, "C", kSM_DailyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig)
487 (boost::bind(&DataLogger::LogMessagePlease, this, _1));
488
489 fServiceList.SetHandler(this);
490 CheckForServicesUpdate();
491
492 //start the monitoring service
493 fContinueMonitoring = true;
494 fMonitoringThread = boost::thread(boost::bind(&DataLogger::ServicesMonitoring, this));
495 fBaseSizeDaily = 0;
496 fBaseSizeRun = 0;
497
498 //gives the entire buffer size. Otherwise, dim overwrites memory at bizarre locations if smaller size is given at creation time.
499// fOpenedFiles = new DimDescribedService((GetName()+"/FILENAME").c_str(), "i:1;C", static_cast<void*>(fDimBuffer), 256, "Add description here");
500 fOpenedDailyFiles = new DimDescribedService((GetName() + "/FILENAME_DAILY").c_str(), const_cast<char*>(""), "Add description here");//"i:1;C", static_cast<void*>(fDimBuffer), 256);
501 fOpenedRunFiles = new DimDescribedService((GetName() + "/FILENAME_RUN").c_str(), const_cast<char*>(""), "Add description here");
502 fOpenedDailyFiles->setQuality(0);
503 fOpenedRunFiles->setQuality(0);
504 fOpenedDailyFiles->updateService();
505 fOpenedRunFiles->updateService();
506 fNumSubAndFitsData.numSubscriptions = 0;
507 fNumSubAndFitsData.numOpenFits = 0;
508 fNumSubAndFits = new DimDescribedService((GetName() + "/NUM_SUBS").c_str(), "I:2", &fNumSubAndFitsData, sizeof(NumSubAndFitsType), "Add description here");
509
510}
511// --------------------------------------------------------------------------
512//
513//! Checks for changes in the existing services.
514//! Any new service will be added to the service list, while the ones which disappeared are removed.
515//! @todo
516//! add the configuration (using the conf class ?)
517//
518//FIXME The service must be udpated so that I get the first notification. This should not be
519bool DataLogger::CheckForServicesUpdate()
520{
521 bool serviceUpdated = false;
522 //get the current server list
523 const std::vector<std::string> serverList = fServiceList.GetServerList();
524 //first let's remove the servers that may have disapeared
525 //can't treat the erase on maps the same way as for vectors. Do it the safe way instead
526 std::vector<std::string> toBeDeleted;
527 for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++)
528 {
529 std::vector<std::string>::const_iterator givenServers;
530 for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++)
531 if (cListe->first == *givenServers)
532 break;
533 if (givenServers == serverList.end())//server vanished. Remove it
534 {
535 toBeDeleted.push_back(cListe->first);
536 serviceUpdated = true;
537 }
538
539 }
540 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
541 fServiceSubscriptions.erase(*it);
542 //now crawl through the list of servers, and see if there was some updates
543 for (std::vector<std::string>::const_iterator i=serverList.begin(); i!=serverList.end();i++)
544 {
545 //skip the two obvious excluded services
546 if ((i->find("DIS_DNS") != std::string::npos) ||
547 (i->find("DATA_LOGGER") != std::string::npos))
548 continue;
549 //find the current server in our subscription list
550 SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i);
551 //get the service list of the current server
552 std::vector<std::string> cServicesList = fServiceList.GetServiceList(*i);
553 if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions
554 { //then check and update our list of subscriptions
555 //first, remove the services that may have dissapeared.
556 std::map<std::string, SubscriptionType>::iterator serverSubs;
557 std::vector<std::string>::const_iterator givenSubs;
558 toBeDeleted.clear();
559 for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++)
560 {
561 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
562 if (serverSubs->first == *givenSubs)
563 break;
564 if (givenSubs == cServicesList.end())
565 {
566 toBeDeleted.push_back(serverSubs->first);
567 serviceUpdated = true;
568 }
569 }
570 for (std::vector<std::string>::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++)
571 cSubs->second.erase(*it);
572 //now check for new services
573 for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++)
574 {
575 if (*givenSubs == "SERVICE_LIST")
576 continue;
577 if (cSubs->second.find(*givenSubs) == cSubs->second.end())
578 {//service not found. Add it
579 cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast<char*>(""), this);
580 serviceUpdated = true;
581 }
582 }
583 }
584 else //server not found in our list. Create its entry
585 {
586 fServiceSubscriptions[*i] = std::map<std::string, SubscriptionType>();
587 std::map<std::string, SubscriptionType>& liste = fServiceSubscriptions[*i];
588 for (std::vector<std::string>::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++)
589 {
590 if (*j == "SERVICE_LIST")
591 continue;
592 liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast<char*>(""), this);
593 serviceUpdated = true;
594 }
595 }
596 }
597 return serviceUpdated;
598}
599// --------------------------------------------------------------------------
600//
601//! Destructor
602//
603DataLogger::~DataLogger()
604{
605 //release the services subscriptions
606 fServiceSubscriptions.clear();
607 //exit the monitoring loop
608 fContinueMonitoring = false;
609// delete[] fDimBuffer;
610 fMonitoringThread.join();
611 //close the files
612 if (fDailyLogFile.is_open())
613 fDailyLogFile.close();
614 if (fDailyReportFile.is_open())
615 fDailyReportFile.close();
616 if (fRunLogFile.is_open())
617 fRunLogFile.close();
618 if (fRunReportFile.is_open())
619 fRunReportFile.close();
620 delete fOpenedDailyFiles;
621 delete fOpenedRunFiles;
622 delete fNumSubAndFits;
623//TODO notify that all files were closed
624#ifdef HAS_FITS
625#ifdef ONE_RUN_FITS_ONLY
626 if (fRunFitsFile != NULL)
627 delete fRunFitsFile;
628 fRunFitsFile = NULL;
629#endif
630#endif
631}
632
633// --------------------------------------------------------------------------
634//
635//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
636//
637void DataLogger::infoHandler()
638{
639 DimInfo* I = getInfo();
640 SubscriptionsListType::iterator x;
641 std::map<std::string, SubscriptionType>::iterator y;
642 if (I==NULL)
643 {
644 if (CheckForServicesUpdate())
645 {
646 //services were updated. Notify
647 fNumSubAndFitsData.numSubscriptions = 0;
648 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
649 fNumSubAndFitsData.numSubscriptions += x->second.size();
650 fNumSubAndFits->updateService();
651 }
652 return;
653 }
654 //check if the service pointer corresponds to something that we subscribed to
655 //this is a fix for a bug that provides bad Infos when a server starts
656 bool found = false;
657 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
658 {//find current service is subscriptions
659 for (y=x->second.begin(); y!=x->second.end();y++)
660 if (y->second.dimInfo == I)
661 {
662 found = true;
663 break;
664 }
665 if (found)
666 break;
667 }
668 if (!found)
669 return;
670 if (I->getSize() <= 0)
671 return;
672 //check that the message has been updated by something, i.e. must be different from its initial value
673 if (I->getTimestamp() == 0)
674 return;
675
676 CheckForRunNumber(I);
677 ReportPlease(I, y->second);
678
679}
680
681// --------------------------------------------------------------------------
682//
683//! Checks whether or not the current info is a run number.
684//! If so, then remember it. A run number is required to open the run-log file
685//! @param I
686//! the current DimInfo
687//
688void DataLogger::CheckForRunNumber(DimInfo* I)
689{
690 if (strstr(I->getName(), fRunNumberInfo) != NULL)
691 {//assumes that the run number is an integer
692 //TODO check the format here
693 fRunNumber = I->getInt();
694 }
695}
696
697// --------------------------------------------------------------------------
698//
699//! write infos to log files.
700//! @param I
701//! The current DimInfo
702//
703void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub)
704{
705 //should we log or report this info ? (i.e. is it a message ?)
706 bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL));
707
708 //TODO add service exclusion
709
710 if (!fDailyReportFile.is_open())
711 return;
712
713 //create the converter for that service
714 if (sub.fConv == NULL)
715 {
716 sub.fConv = new Converter(Out(), I->getFormat());
717 if (!sub.fConv)
718 {
719 std::stringstream str;
720 str << "Couldn't properly parse the format... service " << sub.dimInfo->getName() << " ignored.";
721 Error(str);
722 return;
723 }
724 }
725
726 //construct the header
727 std::stringstream header;
728 Time cTime(I->getTimestamp(), I->getTimestampMillisecs()*1000);
729 fQuality = I->getQuality();
730 fMjD = cTime.Mjd();
731
732 if (isItaReport)
733 {
734 //write text header
735 header << I->getName() << " " << fQuality << " ";
736 header << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
737 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
738 header << cTime.ms() << " " << I->getTimestamp() << " ";
739
740 std::string text;
741 try
742 {
743 text = sub.fConv->GetString(I->getData(), I->getSize());
744 }
745 catch (const std::runtime_error &e)
746 {
747 Out() << kRed << e.what() << endl;
748 std::stringstream str;
749 str << "Could not properly parse the data for service " << sub.dimInfo->getName();
750 str << " reason: " << e.what() << ". Entry ignored";
751 Error(str);
752 return;
753 }
754
755 if (text.empty())
756 {
757 std::stringstream str;
758 str << "Service " << sub.dimInfo->getName() << " sent an empty string";
759 Info(str);
760 return;
761 }
762 //replace bizarre characters by white space
763 replace(text.begin(), text.end(), '\n', '\\');
764 replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
765
766 //write entry to daily report
767 try
768 {
769 if (fDailyReportFile.is_open())
770 fDailyReportFile << header.str() << text << std::endl;
771 }
772 catch (std::exception e)
773 {
774 std::stringstream str;
775 str << "Error while writing to daily report file: " << e.what();
776 Error(str);
777 }
778 //write entry to run-report
779 try
780 {
781 if (fRunReportFile.is_open())
782 fRunReportFile << header.str() << text << std::endl;
783 }
784 catch (std::exception e)
785 {
786 std::stringstream str;
787 str << "Error while writing to run report file: " << e.what();
788 Error(str);
789 }
790 }
791 else
792 {//write entry to both daily and run logs
793 std::string n = I->getName();
794 std::stringstream msg;
795 msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString();
796 try
797 {
798 MessageImp dailyMess(fDailyLogFile);
799 dailyMess.Write(cTime, msg.str().c_str(), fQuality);
800 }
801 catch (std::exception e)
802 {
803 std::stringstream str;
804 str << "Error while writing to daily log file: " << e.what();
805 Error(str);
806 }
807 if (fRunLogFile.is_open())
808 {
809 try
810 {
811 MessageImp runMess(fRunLogFile);
812 runMess.Write(cTime, msg.str().c_str(), fQuality);
813 }
814 catch (std::exception e)
815 {
816 std::stringstream str;
817 str << "Error while writing to run log file: " << e.what();
818 Error(str);
819 }
820 }
821 }
822
823#ifdef HAS_FITS
824 if (!sub.dailyFile.IsOpen() || !sub.runFile.IsOpen())
825 OpenFITSFilesPlease(sub);
826 WriteToFITS(sub);
827
828#endif
829
830}
831
832// --------------------------------------------------------------------------
833//
834//! write messages to logs.
835//! @param evt
836//! the current event to log
837//! @returns
838//! the new state. Currently, always the current state
839//!
840//! @deprecated
841//! I guess that this function should not be any longer
842//
843//TODO isn't that function not used any longer ? If so I guess that we should get rid of it...
844//Otherwise re-write it properly with the MessageImp class
845int DataLogger::LogMessagePlease(const Event& evt)
846{
847 if (!fDailyLogFile.is_open())
848 return GetCurrentState();
849
850 std::stringstream header;
851 const Time& cTime = evt.GetTime();
852 header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " ";
853 header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " ";
854 header << cTime.ms() << " ";
855
856 const Converter conv(Out(), evt.GetFormat());
857 if (!conv)
858 {
859 Error("Couldn't properly parse the format... ignored.");
860 return GetCurrentState();
861 }
862
863 std::string text;
864 try
865 {
866 text = conv.GetString(evt.GetData(), evt.GetSize());
867 }
868 catch (const std::runtime_error &e)
869 {
870 Out() << kRed << e.what() << endl;
871 Error("Couldn't properly parse the data... ignored.");
872 return GetCurrentState();
873 }
874
875 if (text.empty())
876 return GetCurrentState();
877
878 //replace bizarre characters by white space
879 replace(text.begin(), text.end(), '\n', '\\');
880 replace_if(text.begin(), text.end(), std::ptr_fun<int, int>(&std::iscntrl), ' ');
881
882 if (fDailyLogFile.is_open())
883 fDailyLogFile << header;
884 if (fRunLogFile.is_open())
885 fRunLogFile << header;
886
887 if (fDailyLogFile.is_open())
888 fDailyLogFile << text;
889 if (fRunLogFile.is_open())
890 fRunLogFile << text;
891
892 return GetCurrentState();
893}
894// --------------------------------------------------------------------------
895//
896//! Sets the path to use for the daily log file.
897//! @param evt
898//! the event transporting the path
899//! @returns
900//! currently only the current state.
901//
902int DataLogger::ConfigureDailyFileName(const Event& evt)
903{
904 if (evt.GetText() != NULL)
905 {
906 fDailyFileName = std::string(evt.GetText());
907 Message("New daily folder specified: " + fDailyFileName);
908 }
909 else
910 Error("Empty daily folder given. Please specify a valid path.");
911
912 return GetCurrentState();
913}
914// --------------------------------------------------------------------------
915//
916//! Sets the path to use for the run log file.
917//! @param evt
918//! the event transporting the path
919//! @returns
920//! currently only the current state
921int DataLogger::ConfigureRunFileName(const Event& evt)
922{
923 if (evt.GetText() != NULL)
924 {
925 fRunFileName = std::string(evt.GetText());
926 Message("New Run folder specified: " + fRunFileName);
927 }
928 else
929 Error("Empty daily folder given. Please specify a valid path");
930
931 return GetCurrentState();
932}
933// --------------------------------------------------------------------------
934//
935//! Sets the run number.
936//! @param evt
937//! the event transporting the run number
938//! @returns
939//! currently only the current state
940//TODO remove this function as the run numbers will be distributed through a dedicated service
941int DataLogger::ConfigureRunNumber(const Event& evt)
942{
943 fRunNumber = evt.GetInt();
944 return GetCurrentState();
945}
946// --------------------------------------------------------------------------
947//
948//! Notifies the DIM service that a particular file was opened
949//! @ param name the base name of the opened file, i.e. without path nor extension.
950//! WARNING: use string instead of string& because I pass values that do not convert to string&.
951//! this is not a problem though because file are not opened so often.
952//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
953inline void DataLogger::NotifyOpenedFile(std::string name, int type, DimService* service)
954{
955 service->setQuality(type);
956 service->updateService(const_cast<char*>(name.c_str()));
957}
958// --------------------------------------------------------------------------
959//
960//! Implements the Start transition.
961//! Concatenates the given path for the daily file and the filename itself (based on the day),
962//! and tries to open it.
963//! @returns
964//! kSM_DailyOpen if success, kSM_BadDailyConfig if failure
965int DataLogger::StartPlease()
966{
967 //TODO concatenate the dailyFileName and the formatted date and extension to obtain the full file name
968 Time time;
969 std::stringstream sTime;
970 sTime << time.Y() << "_" << time.M() << "_" << time.D();
971
972 fFullDailyLogFileName = fDailyFileName + '/' + sTime.str() + ".log";
973 fDailyLogFile.open(fFullDailyLogFileName.c_str(), std::ios_base::out | std::ios_base::app);
974 if (errno != 0)
975 {
976 std::stringstream str;
977 str << "Unable to open daily Log " << fFullDailyLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
978 Error(str);
979 }
980 fFullDailyReportFileName = fDailyFileName + '/' + sTime.str() + ".rep";
981 fDailyReportFile.open(fFullDailyReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
982 if (errno != 0)
983 {
984 std::stringstream str;
985 str << "Unable to open daily Report " << fFullDailyReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
986 Error(str);
987 }
988
989 if (!fDailyLogFile.is_open() || !fDailyReportFile.is_open())
990 {
991 //TODO send an error message
992 return kSM_BadDailyConfig;
993 }
994 //get the size of the newly opened file.
995 struct stat st;
996 stat(fFullDailyLogFileName.c_str(), &st);
997 fBaseSizeDaily = st.st_size;
998 stat(fFullDailyReportFileName.c_str(), &st);
999 fBaseSizeDaily += st.st_size;
1000 fFileSizesMap.clear();
1001 fBaseSizeRun = 0;
1002 fPreviousSize = 0;
1003 //notify that files were opened
1004 std::string actualTargetDir;
1005 if (fDailyFileName == ".")
1006 {
1007 char currentPath[FILENAME_MAX];
1008 getcwd(currentPath, sizeof(currentPath));
1009 if (errno != 0)
1010 {
1011 std::stringstream str;
1012 str << "Unable retrieve current path" << ". Reason: " << strerror(errno) << " [" << errno << "]";
1013 Error(str);
1014 }
1015 actualTargetDir = currentPath;
1016 }
1017 else
1018 {
1019 actualTargetDir = fDailyFileName;
1020 }
1021
1022 NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 3, fOpenedDailyFiles);
1023
1024
1025 return kSM_DailyOpen;
1026}
1027
1028#ifdef HAS_FITS
1029// --------------------------------------------------------------------------
1030//
1031//! open if required a the FITS files corresponding to a given subscription
1032//! @param sub
1033//! the current DimInfo subscription being examined
1034void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub)
1035{
1036 std::string serviceName(sub.dimInfo->getName());
1037 for (unsigned int i=0;i<serviceName.size(); i++)
1038 {
1039 if (serviceName[i] == '/')
1040 {
1041 serviceName[i] = '_';
1042 break;
1043 }
1044 }
1045 Time time;
1046 std::stringstream sTime;
1047 sTime << time.Y() << "_" << time.M() << "_" << time.D();
1048 //we open the dailyFile anyway, otherwise this function shouldn't have been called.
1049 if (!sub.dailyFile.IsOpen())
1050 {
1051 std::string partialName = fDailyFileName + '/' + sTime.str() + '_' + serviceName + ".fits";
1052 AllocateFITSBuffers(sub);
1053 //get the size of the file we're about to open
1054 if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1055 {
1056 struct stat st;
1057 if (!stat(partialName.c_str(), &st))
1058 fBaseSizeDaily += st.st_size;
1059 fFileSizesMap[partialName] = 0;
1060 }
1061 sub.dailyFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, Out());
1062 //notify the opening
1063 std::string actualTargetDir;
1064 if (fDailyFileName == ".")
1065 {
1066 char currentPath[FILENAME_MAX];
1067 getcwd(currentPath, sizeof(currentPath));
1068 actualTargetDir = currentPath;
1069 }
1070 else
1071 {
1072 actualTargetDir = fDailyFileName;
1073 }
1074 NotifyOpenedFile(actualTargetDir + '/' + sTime.str(), 4, fOpenedDailyFiles);
1075 fNumSubAndFits->updateService();
1076 }
1077 if (!sub.runFile.IsOpen() && (GetCurrentState() == kSM_Logging))
1078 {//buffer for the run file have already been allocated when doing the daily file
1079 std::stringstream sRun;
1080 sRun << fRunNumber;
1081#ifdef ONE_RUN_FITS_ONLY
1082 std::string partialName = fRunFileName + '/' + sRun.str() + ".fits";//'_' + serviceName + ".fits";
1083 if (fRunFitsFile == NULL)
1084 {
1085#else
1086 std::string partialName = fRunFileName + '/' + sRun.str() + '_' + serviceName + ".fits";
1087#endif
1088 //get the size of the file we're about to open
1089 if (fFileSizesMap.find(partialName) == fFileSizesMap.end())
1090 {
1091 struct stat st;
1092 if (!stat(partialName.c_str(), &st))
1093 fBaseSizeRun += st.st_size;
1094 else
1095 fBaseSizeRun = 0;
1096 fFileSizesMap[partialName] = 0;
1097 }
1098#ifdef ONE_RUN_FITS_ONLY
1099 try
1100 {
1101 fRunFitsFile = new FITS(partialName, RWmode::Write);
1102 (fNumSubAndFitsData.numOpenFits)++;
1103 }
1104 catch (CCfits::FitsError e)
1105 {
1106 std::stringstream str;
1107 str << "Could not open FITS Run file " << partialName << " reason: " << e.message();
1108 Error(str);
1109 fRunFitsFile = NULL;
1110 }
1111#endif
1112 std::string actualTargetDir;
1113 if (fRunFileName == ".")
1114 {
1115 char currentPath[FILENAME_MAX];
1116 getcwd(currentPath, sizeof(currentPath));
1117 actualTargetDir = currentPath;
1118 }
1119 else
1120 {
1121 actualTargetDir = fRunFileName;
1122 }
1123 NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 4, fOpenedRunFiles);// + '_' + serviceName, 4);
1124#ifdef ONE_RUN_FITS_ONLY
1125 }
1126 sub.runFile.Open(partialName, serviceName, fRunFitsFile, &fNumSubAndFitsData.numOpenFits, Out());
1127#else
1128 sub.runFile.Open(partialName, serviceName, NULL, &fNumSubAndFitsData.numOpenFits, Out());
1129#endif //one_run_fits_only
1130 fNumSubAndFits->updateService();
1131
1132 }
1133}
1134// --------------------------------------------------------------------------
1135//
1136void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
1137{
1138 int size = sub.dimInfo->getSize();
1139
1140 //Init the time columns of the file
1141 Description dateDesc(std::string("Time"), std::string("Modified Julian Date"), std::string("MjD"));
1142 sub.dailyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1143 sub.runFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1144
1145 Description QoSDesc("Qos", "Quality of service", "None");
1146 sub.dailyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1147 sub.runFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1148
1149 const Converter::FormatList flist = sub.fConv->GetList();
1150 // Compilation failed
1151 if (flist.empty() || flist.back().first.second!=0)
1152 {
1153 Error("Compilation of format string failed.");
1154 return;
1155 }
1156
1157 //we've got a nice structure describing the format of this service's messages.
1158 //Let's create the appropriate FITS columns
1159 std::vector<std::string> dataFormatsLocal;
1160 for (unsigned int i=0;i<flist.size()-1;i++)
1161 {
1162 std::stringstream dataQualifier;
1163
1164 dataQualifier << flist[i].second.first;
1165 switch (flist[i].first.first->name()[0])
1166 {//TODO handle all the data format cases
1167 case 'c':
1168 dataQualifier << "S";
1169 break;
1170 case 's':
1171 dataQualifier << "I";
1172 break;
1173 case 'i':
1174 dataQualifier << "J";
1175 break;
1176 case 'l':
1177 dataQualifier << "J";
1178 //TODO triple check that in FITS, long = int
1179 break;
1180 case 'f':
1181 dataQualifier << "E";
1182 break;
1183 case 'd':
1184 dataQualifier << "D";
1185 break;
1186 case 'x':
1187 case 'X':
1188 dataQualifier << "K";
1189 break;
1190 case 'S':
1191 //for strings, the number of elements I get is wrong. Correct it
1192 dataQualifier.str(""); //clear
1193 dataQualifier << size-1 << "A";
1194 size = size-1;
1195 break;
1196
1197 default:
1198 Error("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 948.");
1199 };
1200 dataFormatsLocal.push_back(dataQualifier.str());
1201 }
1202
1203 sub.dailyFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1204 sub.runFile.InitDataColumns(fServiceList.GetDescriptions(sub.dimInfo->getName()), dataFormatsLocal, sub.dimInfo->getData(), size);
1205}
1206// --------------------------------------------------------------------------
1207//
1208//! write a dimInfo data to its corresponding FITS files
1209//
1210void DataLogger::WriteToFITS(SubscriptionType& sub)
1211{
1212 //dailyFile status (open or not) already checked
1213 if (sub.dailyFile.IsOpen())
1214 sub.dailyFile.Write(sub.fConv);
1215 if (sub.runFile.IsOpen())
1216 sub.runFile.Write(sub.fConv);
1217}
1218#endif //if has_fits
1219// --------------------------------------------------------------------------
1220//
1221//! Implements the StartRun transition.
1222//! Concatenates the given path for the run file and the filename itself (based on the run number),
1223//! and tries to open it.
1224//! @returns
1225//! kSM_Logging if success, kSM_BadRunConfig if failure.
1226int DataLogger::StartRunPlease()
1227{
1228 //attempt to open run file with current parameters
1229 if (fRunNumber == -1)
1230 return kSM_BadRunConfig;
1231 std::stringstream sRun;
1232 sRun << fRunNumber;
1233 fFullRunLogFileName = fRunFileName + '/' + sRun.str() + ".log";
1234 fRunLogFile.open(fFullRunLogFileName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate
1235 if (errno != 0)
1236 {
1237 std::stringstream str;
1238 str << "Unable to open run Log " << fFullRunLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1239 Error(str);
1240 }
1241 fFullRunReportFileName = fRunFileName + '/' + sRun.str() + ".rep";
1242 fRunReportFile.open(fFullRunReportFileName.c_str(), std::ios_base::out | std::ios_base::app);
1243 if (errno != 0)
1244 {
1245 std::stringstream str;
1246 str << "Unable to open run report " << fFullRunReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1247 Error(str);
1248 }
1249
1250 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1251 {
1252 //TODO send an error message
1253 return kSM_BadRunConfig;
1254 }
1255 //get the size of the newly opened file.
1256 struct stat st;
1257 fBaseSizeRun = 0;
1258 if (fFileSizesMap.find(fFullRunLogFileName) == fFileSizesMap.end())
1259 {
1260 stat(fFullRunLogFileName.c_str(), &st);
1261 if (errno != 0)
1262 {
1263 std::stringstream str;
1264 str << "Unable to stat " << fFullRunLogFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1265 Error(str);
1266 }
1267 else
1268 fBaseSizeRun += st.st_size;
1269 fFileSizesMap[fFullRunLogFileName] = 0;
1270 }
1271 if (fFileSizesMap.find(fFullRunReportFileName) == fFileSizesMap.end())
1272 {
1273 stat(fFullRunReportFileName.c_str(), &st);
1274 if (errno != 0)
1275 {
1276 std::stringstream str;
1277 str << "Unable to stat " << fFullRunReportFileName << ". Reason: " << strerror(errno) << " [" << errno << "]";
1278 Error(str);
1279 }
1280 else
1281 fBaseSizeRun += st.st_size;
1282 fFileSizesMap[fFullRunReportFileName] = 0;
1283 }
1284 std::string actualTargetDir;
1285 if (fRunFileName == ".")
1286 {
1287 char currentPath[FILENAME_MAX];
1288 getcwd(currentPath, sizeof(currentPath));
1289 if (errno != 0)
1290 {
1291 std::stringstream str;
1292 str << "Unable to retrieve the current path" << ". Reason: " << strerror(errno) << " [" << errno << "]";
1293 Error(str);
1294 }
1295 actualTargetDir = currentPath;
1296 }
1297 else
1298 {
1299 actualTargetDir = fRunFileName;
1300 }
1301 NotifyOpenedFile(actualTargetDir + '/' + sRun.str(), 3, fOpenedRunFiles);
1302
1303 return kSM_Logging;
1304}
1305// --------------------------------------------------------------------------
1306//
1307//! Implements the StopRun transition.
1308//! Attempts to close the run file.
1309//! @returns
1310//! kSM_WaitingRun if success, kSM_FatalError otherwise
1311int DataLogger::StopRunPlease()
1312{
1313 if (!fRunLogFile.is_open() || !fRunReportFile.is_open())
1314 return kSM_FatalError;
1315
1316 fRunLogFile.close();
1317 fRunReportFile.close();
1318#ifdef HAS_FITS
1319 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1320 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1321 {
1322 if (j->second.runFile.IsOpen())
1323 j->second.runFile.Close();
1324 }
1325#ifdef ONE_RUN_FITS_ONLY
1326 if (fRunFitsFile != NULL)
1327 {
1328 delete fRunFitsFile;
1329 fRunFitsFile = NULL;
1330 (fNumSubAndFitsData.numOpenFits)--;
1331 }
1332#endif
1333#endif
1334 NotifyOpenedFile("None", 0, fOpenedRunFiles);
1335 fNumSubAndFits->updateService();
1336 return kSM_WaitingRun;
1337
1338}
1339// --------------------------------------------------------------------------
1340//
1341//! Implements the Stop and Reset transitions.
1342//! Attempts to close any openned file.
1343//! @returns
1344//! kSM_Ready
1345int DataLogger::GoToReadyPlease()
1346{
1347 if (fDailyLogFile.is_open())
1348 fDailyLogFile.close();
1349 if (fDailyReportFile.is_open())
1350 fDailyReportFile.close();
1351
1352 if (fRunLogFile.is_open())
1353 fRunLogFile.close();
1354 if (fRunReportFile.is_open())
1355 fRunReportFile.close();
1356
1357#ifdef HAS_FITS
1358 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
1359 for (std::map<std::string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
1360 {
1361 if (j->second.dailyFile.IsOpen())
1362 j->second.dailyFile.Close();
1363 if (j->second.runFile.IsOpen())
1364 j->second.runFile.Close();
1365 }
1366#ifdef ONE_RUN_FITS_ONLY
1367 if (fRunFitsFile != NULL)
1368 {
1369 delete fRunFitsFile;
1370 fRunFitsFile = NULL;
1371 (fNumSubAndFitsData.numOpenFits)--;
1372 }
1373#endif
1374#endif
1375 if (GetCurrentState() == kSM_Logging)
1376 NotifyOpenedFile("None", 0, fOpenedRunFiles);
1377 if (GetCurrentState() == kSM_Logging ||
1378 GetCurrentState() == kSM_WaitingRun ||
1379 GetCurrentState() == kSM_DailyOpen)
1380 {
1381 NotifyOpenedFile("None", 0, fOpenedDailyFiles);
1382 fNumSubAndFits->updateService();
1383 }
1384 return kSM_Ready;
1385}
1386// --------------------------------------------------------------------------
1387//
1388//! Implements the transition towards kSM_WaitingRun
1389//! Does nothing really.
1390//! @returns
1391//! kSM_WaitingRun
1392int DataLogger::DailyToWaitRunPlease()
1393{
1394 return kSM_WaitingRun;
1395}
1396
1397// --------------------------------------------------------------------------
1398
1399int RunDim(Configuration &conf)
1400{
1401 WindowLog wout;
1402
1403 //log.SetWindow(stdscr);
1404 if (conf.Has("log"))
1405 if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1406 wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1407
1408 // Start io_service.Run to use the StateMachineImp::Run() loop
1409 // Start io_service.run to only use the commandHandler command detaching
1410 DataLogger logger(wout);
1411 logger.Run(true);
1412
1413 return 0;
1414}
1415
1416void RunThread(DataLogger* logger)
1417{
1418 // This is necessary so that the StateMachine Thread can signal the
1419 // Readline thread to exit
1420 logger->Run(true);
1421 Readline::Stop();
1422}
1423
1424template<class T>
1425int RunShell(Configuration &conf)
1426{
1427 static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);
1428
1429 WindowLog &win = shell.GetStreamIn();
1430 WindowLog &wout = shell.GetStreamOut();
1431
1432 if (conf.Has("log"))
1433 if (!wout.OpenLogFile(conf.Get<std::string>("log")))
1434 win << kRed << "ERROR - Couldn't open log-file " << conf.Get<std::string>("log") << ": " << strerror(errno) << std::endl;
1435
1436 DataLogger logger(wout);
1437
1438// if (conf.Has("black-list"))
1439// logger.setBlackList(conf.Get<std::string>("black-list"));
1440// else if (conf.Has("white-list"))
1441// logger.setWhiteList(conf.Get<std::string>("white-list"));
1442
1443 shell.SetReceiver(logger);
1444
1445 boost::thread t(boost::bind(RunThread, &logger));
1446
1447 shell.Run(); // Run the shell
1448
1449 logger.Stop();
1450
1451 //Wait until the StateMachine has finished its thread
1452 //before returning and destroyinng the dim objects which might
1453 //still be in use.
1454 t.join();
1455
1456 return 0;
1457}
1458
1459/*
1460 Extract usage clause(s) [if any] for SYNOPSIS.
1461 Translators: "Usage" and "or" here are patterns (regular expressions) which
1462 are used to match the usage synopsis in program output. An example from cp
1463 (GNU coreutils) which contains both strings:
1464 Usage: cp [OPTION]... [-T] SOURCE DEST
1465 or: cp [OPTION]... SOURCE... DIRECTORY
1466 or: cp [OPTION]... -t DIRECTORY SOURCE...
1467 */
1468void PrintUsage()
1469{
1470 cout << "\n"
1471 "The data logger connects to all available Dim services and "
1472 "writes them to ascii and fits files.\n"
1473 "\n"
1474 "Usage: dataLogger [-c type] [OPTIONS]\n"
1475 " or: dataLogger [OPTIONS]\n"
1476 "\n"
1477 "Options:\n"
1478 "The following describes the available commandline options. "
1479 "For further details on how command line option are parsed "
1480 "and in which order which configuration sources are accessed "
1481 "please refer to the class reference of the Configuration class.";
1482 cout << endl;
1483
1484}
1485
1486void PrintHelp()
1487{
1488 cout << "\n"
1489 "The default is that the program is started without user interaction. "
1490 "All actions are supposed to arrive as DimCommands. Using the -c "
1491 "option, a local shell can be initialized. With h or help a short "
1492 "help message about the usuage can be brought to the screen."
1493 << endl;
1494}
1495
1496/*
1497 The first line of the --version information is assumed to be in one
1498 of the following formats:
1499
1500 <version>
1501 <program> <version>
1502 {GNU,Free} <program> <version>
1503 <program> ({GNU,Free} <package>) <version>
1504 <program> - {GNU,Free} <package> <version>
1505
1506 and separated from any copyright/author details by a blank line.
1507
1508 Handle multi-line bug reporting sections of the form:
1509
1510 Report <program> bugs to <addr>
1511 GNU <package> home page: <url>
1512 ...
1513*/
1514void PrintVersion(const char *name)
1515{
1516 cout <<
1517 name << " - "PACKAGE_STRING"\n"
1518 "\n"
1519 "Written by Thomas Bretz et al.\n"
1520 "\n"
1521 "Report bugs to <"PACKAGE_BUGREPORT">\n"
1522 "Home page: "PACKAGE_URL"\n"
1523 "\n"
1524 "Copyright (C) 2011 by the FACT Collaboration.\n"
1525 "This is free software; see the source for copying conditions.\n"
1526 << endl;
1527}
1528
1529
1530void SetupConfiguration(Configuration &conf)
1531{
1532 const string n = conf.GetName()+".log";
1533
1534 po::options_description config("Program options");
1535 config.add_options()
1536 ("dns", var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
1537 ("log,l", var<string>(n), "Write log-file")
1538 ("console,c", var<int>(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
1539 ("black-list,b", var<string>(""), "Black-list of services")
1540 ("white-list,w", var<string>(""), "White-list of services")
1541 ;
1542
1543 conf.AddEnv("dns", "DIM_DNS_NODE");
1544
1545 conf.AddOptions(config);
1546}
1547
1548int main(int argc, const char* argv[])
1549{
1550 Configuration conf(argv[0]);
1551 conf.SetPrintUsage(PrintUsage);
1552 SetupConfiguration(conf);
1553
1554 po::variables_map vm;
1555 try
1556 {
1557 vm = conf.Parse(argc, argv);
1558 }
1559 catch (std::exception &e)
1560 {
1561#if BOOST_VERSION > 104000
1562 po::multiple_occurrences *MO = dynamic_cast<po::multiple_occurrences*>(&e);
1563 if (MO)
1564 cout << "Error: " << e.what() << " of '" << MO->get_option_name() << "' option." << endl;
1565 else
1566#endif
1567 cout << "Error: " << e.what() << endl;
1568 cout << endl;
1569
1570 return -1;
1571 }
1572
1573 if (conf.HasPrint())
1574 return -1;
1575
1576 if (conf.HasVersion())
1577 {
1578 PrintVersion(argv[0]);
1579 return -1;
1580 }
1581
1582 if (conf.HasHelp())
1583 {
1584 PrintHelp();
1585 return -1;
1586 }
1587
1588 setenv("DIM_DNS_NODE", conf.Get<string>("dns").c_str(), 1);
1589
1590 try
1591 {
1592 // No console access at all
1593 if (!conf.Has("console"))
1594 return RunDim(conf);
1595
1596 // Console access w/ and w/o Dim
1597 if (conf.Get<int>("console")==0)
1598 return RunShell<LocalShell>(conf);
1599 else
1600 return RunShell<LocalConsole>(conf);
1601 }
1602 catch (std::exception& e)
1603 {
1604 cerr << "Exception: " << e.what() << endl;
1605 return -1;
1606 }
1607
1608 return 0;
1609}
Note: See TracBrowser for help on using the repository browser.