source: trunk/FACT++/src/datalogger.cc@ 15157

Last change on this file since 15157 was 15153, checked in by lyard, 12 years ago
changed close time of files from noon to 30 minutes after sunrise for faster transfer to archive. Also disabled writing of report file.
File size: 83.0 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 // FIXME FIXME: Error states missing...
13 digraph datalogger
14 {
15 node [shape=record, fontname=Helvetica, fontsize=10];
16
17 srt [label="Start" style="rounded"]
18 rdy [label="Ready"]
19 nop [label="NightlyOpen"]
20 wait [label="WaitingRun"]
21 log [label="Logging"]
22
23 //e [label="Error" color="red"];
24 //c [label="BadFolder" color="red"]
25
26
27 cmd_start [label="START" shape="none" height="0"]
28 cmd_stop [label="STOP" shape="none" height="0"]
29 cmd_stopr [label="STOP_RUN_LOGGING" shape="none" height="0"]
30 cmd_startr [label="START_RUN_LOGGING" shape="none" height="0"]
31
32 { rank=same; cmd_startr cmd_stopr }
33 { rank=same; cmd_start cmd_stop }
34
35
36 srt -> rdy
37
38 rdy -> cmd_start [ arrowhead="open" dir="both" arrowtail="tee" weight=10 ]
39 cmd_start -> nop
40
41 nop -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
42 wait -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
43 log -> cmd_stop [ arrowhead="none" dir="both" arrowtail="inv" ]
44 cmd_stop -> rdy
45
46 wait -> cmd_stopr [ arrowhead="none" dir="both" arrowtail="inv" ]
47 log -> cmd_stopr [ arrowhead="none" dir="both" arrowtail="inv" ]
48 cmd_stopr -> nop
49
50 nop -> cmd_startr [ arrowhead="none" dir="both" arrowtail="inv" weight=10 ]
51 rdy -> cmd_startr [ arrowhead="none" dir="both" arrowtail="inv" ]
52 cmd_startr -> wait [ weight=10 ]
53
54
55 wait -> log
56 log -> wait
57 }
58 \enddot
59
60 For questions or bug report, please contact Etienne Lyard (etienne.lyard@unige.ch) or Thomas Bretz.
61 */
62 //****************************************************************
63#include <unistd.h> //for getting stat of opened files
64//#include <sys/statvfs.h> //for getting disk free space
65//#include <sys/stat.h> //for getting files sizes
66#include <fstream>
67#include <functional>
68
69#include <boost/filesystem.hpp>
70
71#include "Dim.h"
72#include "Event.h"
73#include "StateMachineDim.h"
74#include "LocalControl.h"
75#include "Configuration.h"
76#include "Converter.h"
77#include "DimWriteStatistics.h"
78
79#include "Description.h"
80//#include "DimNetwork.h"
81
82#ifdef HAVE_FITS
83#include "Fits.h"
84#endif
85
86#include "DimState.h"
87
88#include <libnova/solar.h>
89#include <libnova/rise_set.h>
90
91//Dim structures
92///distributes the number of opened subscriptions and fits files
93struct NumSubAndFitsType {
94 uint32_t numSubscriptions;
95 uint32_t numOpenFits;
96};
97///distributes which files were opened.
98struct OpenFileToDim {
99 uint32_t code;
100 char fileName[FILENAME_MAX];
101};
102
103///Run number record. Used to keep track of which run numbers are still active
104struct RunNumberType {
105
106 ///the actual run number
107 int32_t runNumber;
108 ///the time at which the run number was received
109 Time time;
110 ///default constructor
111 RunNumberType()
112 {
113 runNumber = 0;
114 }
115 ///default destructor
116 ~RunNumberType()
117 {
118
119 }
120};
121
122EventImp nullEventImp;
123///Dim subscription type. Stores all the relevant info to handle a Dim subscription
124struct SubscriptionType
125{
126#ifdef HAVE_FITS
127 ///Nightly FITS output file
128 Fits nightlyFile;
129#endif
130 ///the server
131 string server;
132 ///the service
133 string service;
134 ///the converter for outputting the data according to the format
135 shared_ptr<Converter> fConv;
136 ///the original format string. So that we can check if format is changing over time
137 string format;
138 ///the current run number used by this subscription
139 int32_t runNumber;
140 ///time of the latest received event
141 Time lastReceivedEvent;
142 ///whether or not the fits buffer was allocated already
143 bool fitsBufferAllocated;
144 ///the actual dimInfo pointer (must be the last in the list to ensure
145 /// that it is the first which is deleted -- and consequently none of
146 /// the other members can still be in use in an infoHandler)
147 //DIM_REPLACE
148 //shared_ptr<DimStampedInfo> dimInfo;
149 unsigned int index;
150 ///counter to know if format has changed during operations
151 unsigned int increment;
152
153 ///Dim info constructor
154 //DIM_REPLACE
155// SubscriptionType(DimStampedInfo* info=NULL)
156 SubscriptionType()
157 {
158 fConv = shared_ptr<Converter>();
159 runNumber = 0;
160 lastReceivedEvent = Time::None;
161 fitsBufferAllocated = false;
162 // Should be the last instantiated to make sure that all other
163 // variables which might be used are already initialized
164 //DIM_REPLACE
165 //dimInfo = shared_ptr<DimStampedInfo>(info);
166 index = 0;
167 increment = 0;
168 }
169 ///default destructor
170 ~SubscriptionType()
171 {
172 }
173};
174
175class DataLogger : public StateMachineDim
176//DIM_REPLACE
177//, DimServiceInfoListImp
178{
179public:
180 /// The list of existing states specific to the DataLogger
181 enum
182 {
183 kSM_NightlyOpen = 20, ///< Nightly file openned and writing
184 kSM_WaitingRun = 30, ///< waiting for the run number to open the run file
185 kSM_Logging = 40, ///< both files openned and writing
186 kSM_BadFolder = 0x101, ///< the folder specified for Nightly logging does not exist or has bad permissions
187 kSM_RunWriteError = 0x103, ///< Denotes that an error occured while writing a run file (text or fits).
188 kSM_DailyWriteError = 0x103, ///< Denots that an error occured while writing a daily file (text or fits).
189 } localstates_t;
190
191 DataLogger(ostream &out);
192 ~DataLogger();
193
194 int EvalOptions(Configuration& conf);
195
196private:
197 /************************************************
198 * MEMBER VARIABLES
199 ************************************************/
200 /// ofstream for the NightlyLogfile
201 ofstream fNightlyLogFile;
202 /// ofstream for the Nightly report file
203 ofstream fNightlyReportFile;
204 /// base path of files
205 string fFilePath;
206 ///run numbers
207 list<RunNumberType> fRunNumber;
208 ///old run numbers time-out delay (in seconds)
209 uint32_t fRunNumberTimeout;
210 ///previous run number. to check if changed while logging
211 int fPreviousRunNumber;
212 ///Current Service Quality
213 int fQuality;
214 ///Modified Julian Date
215 double fMjD;
216 ///for obtaining the name of the existing services
217// ServiceList fServiceList;
218 typedef map<const string, map<string, SubscriptionType> > SubscriptionsListType;
219 ///All the services to which we have subscribed to, sorted by server name.
220 SubscriptionsListType fServiceSubscriptions;
221 ///full name of the nightly log file
222 string fFullNightlyLogFileName;
223 ///full name of the nightly report file
224 string fFullNightlyReportFileName;
225 ///variable to track when the statistic were last calculated
226// Time fPreviousStatsUpdateTime;
227 Time fPreviousOldRunNumberCheck;
228 ///boolean to know whether we should close and reopen daily files or not
229 bool fDailyFileDayChangedAlready;
230
231 DimWriteStatistics fFilesStats;
232
233 ///map and mutex for storing services description
234 map<string, vector<Description> > fServiceDescriptionList;
235 mutex fMutex;
236 int HandleDescriptions(DimDescriptions* desc);
237 vector<Description> GetDescription(const string& server, const string& service);
238private:
239 /***************************************************
240 * DIM INFO HANDLER
241 ***************************************************/
242 //overloading of DIM's infoHandler function
243 int infoCallback(const EventImp& evt, unsigned int infoIndex);
244
245 Time GetSunRise(const Time &time=Time());
246
247 /***************************************************
248 * TRANSITION FUNCTIONS
249 ***************************************************/
250 ///Reporting method for the services info received
251 void Report(const EventImp& evt, SubscriptionType& sub);
252
253 ///Configuration of the nightly file path
254 int ConfigureFilePath(const Event& evt);
255 ///print the current state of the dataLogger
256 int PrintState(const Event& evt);
257 ///checks whether or not the current info being treated is a run number
258 void CheckForRunNumber(const EventImp& evt, unsigned int index);
259 /// start transition
260 int Start();
261 ///from waiting to logging transition
262 //int StartRun();
263 // from logging to waiting transition
264 int StopRunLogging();
265 ///stop and reset transition
266 int GoToReady();
267 ///from NightlyOpen to waiting transition
268 int NightlyToWaitRun();
269 ///from wait for run number to nightly open
270 int BackToNightlyOpen();
271#ifdef HAVE_FITS
272 ///Open fits files
273 void OpenFITSFiles(SubscriptionType& sub);
274 ///Write data to FITS files
275 void WriteToFITS(SubscriptionType& sub, const void* data);
276 ///Allocate the buffers required for fits
277 void AllocateFITSBuffers(SubscriptionType& sub);
278#endif//has_fits
279
280 /***************************************
281 * DIM SERVICES PROVIDED BY THE DATA LOGGER
282 ***************************************/
283 ///monitoring notification loop
284 void ServicesMonitoring();
285 inline void NotifyOpenedFile(const string &name, int type, DimDescribedService* service);
286 ///Service for opened files
287 DimDescribedService* fOpenedNightlyFiles;
288 DimDescribedService* fOpenedRunFiles;
289 DimDescribedService* fNumSubAndFits;
290 NumSubAndFitsType fNumSubAndFitsData;
291
292 ///Service for broadcasting subscription status
293 DimDescribedService* fCurrentSubscription;
294 ///Number of seconds since the last update of the subscribed list
295 int fCurrentSubscriptionUpdateRate;
296 ///The last time in seconds of the day when the service was update
297 Time fLastSubscriptionUpdate;
298 ///update the service
299 void updateSubscriptionList();
300 ///set the duration between two updates. a zero or negative value disables the service updates
301 int setSubscriptionListUpdateTimeLapse(const Event& evt);
302 /***************************************************
303 * DATA LOGGER's CONFIGURATION STUFF
304 ***************************************************/
305 ///black/white listing
306 set<string> fBlackList;
307 set<string> fWhiteList;
308 ///list of services to be grouped
309 set<string> fGrouping;
310 ///configuration flags
311 bool fDebugIsOn;
312 bool fOpenedFilesIsOn;
313 bool fNumSubAndFitsIsOn;
314 //functions for controlling the services behavior
315 int SetDebugOnOff(const Event& evt);
316 int SetStatsPeriod(const Event& evt);
317 int SetOpenedFilesOnOff(const Event& evt);
318 int SetNumSubsAndFitsOnOff(const Event& evt);
319 int SetRunTimeoutDelay(const Event& evt);
320
321 ///boolean to prevent DIM update while desctructing the dataLogger
322 bool fDestructing;
323 /***************************************************
324 * UTILITIES
325 ***************************************************/
326 ///vectors to keep track of opened Fits files, for grouping purposes.
327 map<string, vector<string> > fOpenedNightlyFits;
328 ///creates a group fits file based on a list of files to be grouped
329 void CreateFitsGrouping(map<string, vector<string> >& filesToGroup);
330
331 bool OpenStreamImp(ofstream &stream, const string &filename, bool mightbeopen);
332 bool OpenStream(shared_ptr<ofstream> stream, const string &filename);
333 ///Open the relevant text files related to a particular run
334 int OpenRunFile(RunNumberType& run);
335 ///add a new run number
336 void AddNewRunNumber(int64_t newRun, Time time);
337 std::vector<int64_t> previousRunNumbers;
338 ///removes the oldest run number, and close the relevant files.
339 void RemoveOldestRunNumber();
340 ///retrieves the size of a file
341 off_t GetFileSize(const string&);
342 ///Get the digits of year, month and day for filenames and paths
343 void GetYearMonthDayForFiles(unsigned short& year, unsigned short& month, unsigned short& day);
344 ///Appends the relevant year month day to a given path
345 void AppendYearMonthDaytoPath(string& path);
346 ///Form the files path
347 string CompileFileNameWithPath(const string &path, const string &service, const string & extension);
348 ///Form the file names only
349 string CompileFileName(const string& service, const string& extension, const Time& time=Time()) const;
350 ///Check whether service is in black and/or white list
351 bool ShouldSubscribe(const string& server, const string& service);
352 ///Subscribe to a given server and service
353// EventImp& SubscribeTo(const string& server, const string& service);
354 ///Open a text file and checks for ofstream status
355 bool OpenTextFile(ofstream& stream, const string& name);
356 ///Checks if the input osftream is in error state, and if so close it.
357 bool CheckForOfstreamError(ofstream& out, bool isDailyStream);
358 ///Goes to Write error states
359 void GoToRunWriteErrorState();
360 void GoToNightlyWriteErrorState();
361 ///Checks if a given path exist
362 bool DoesPathExist(string path);
363 ///Check if old run numbers can be trimmed, and if so, do it
364 void TrimOldRunNumbers();
365 ///Create a given directory
366 bool CreateDirectory(string path);
367 /***************************************************
368 * INHERITED FROM DimServiceInfoList
369 ***************************************************/
370 ///Add a new server subscription
371 void AddServer(const string& server);
372 ///Add a new service subscription
373 void AddService(const Service& svc);
374 ///Remove a given service subscription
375 //FIXME unused
376 void RemoveService(const string, const string, bool);
377 ///Remove all the services associated with a given server
378 //FIXME unused
379 void RemoveAllServices(const string&);
380 ///pointer to the dim's subscription that should distribute the run numbers.
381 //DIM_REPLACE
382 //DimInfo* fRunNumberService;
383 unsigned int fRunNumberService;
384 /***************************************************
385 * Overwritten from MessageImp
386 ***************************************************/
387 vector<string> backLogBuffer;
388 bool shouldBackLog;
389 bool fShouldAutoStart;
390 bool fAutoStarted;
391
392 //Current day variable. Used to close nightly files when night changes
393 Time fCurrentDay;
394 Time lastFlush;
395
396 DimDnsServiceList fDimList;
397 vector<DimDescriptions*> fServerDescriptionsList;
398
399 //counter for keeping tracker of services
400 unsigned int servicesCounter;
401public:
402 int Write(const Time &time, const std::string &txt, int qos=kMessage);
403
404}; //DataLogger
405
406
407/**
408 * @brief the two methods below were copied from StateMachineDimControl.cc
409 *
410 */
411int DataLogger::HandleDescriptions(DimDescriptions* desc)
412{
413 fMutex.lock();
414 for (auto it=desc->descriptions.begin(); it != desc->descriptions.end(); it++) {
415 if (fDebugIsOn)
416 {
417 Debug("Adding description for service: " + it->front().name);
418 }
419 fServiceDescriptionList[it->front().name].assign(it->begin(), it->end());
420 }
421 fMutex.unlock();
422
423 return GetCurrentState();
424}
425/**
426 * UPDATE SUBSCRIPTION LIST. Updates the subscription list service if enough time has passed.
427 * Otherwise does nothing
428 */
429void DataLogger::updateSubscriptionList()
430{
431 if (fCurrentSubscriptionUpdateRate <= 0) return;
432 Time timeNow;
433 //if less than the update rate time has passed, just return
434 if (timeNow - fLastSubscriptionUpdate < boost::posix_time::seconds(fCurrentSubscriptionUpdateRate))
435 return;
436 //TODO remove me !
437// cout << "Updating subscription list with: " << endl;
438
439 fLastSubscriptionUpdate = timeNow;
440
441 //update service !
442 ostringstream output;
443 for (auto serverIt=fServiceSubscriptions.begin();serverIt!=fServiceSubscriptions.end(); serverIt++)
444 {
445 if (serverIt->first == "DATA_LOGGER")
446 continue;
447 for (auto serviceIt=serverIt->second.begin(); serviceIt!=serverIt->second.end(); serviceIt++)
448 {
449 output << serverIt->first << "/" << serviceIt->first << ",";
450 if (serviceIt->second.lastReceivedEvent != Time::None)
451 output << (timeNow - serviceIt->second.lastReceivedEvent).total_seconds();
452 else
453 output << "-1";
454 output << "\n";
455 }
456 }
457//TODO remove me !
458//cout << output.str();
459 fCurrentSubscription->setData(output.str().c_str(), output.str().size()+1);
460 fCurrentSubscription->setQuality(0);
461 fCurrentSubscription->Update();
462}
463int DataLogger::setSubscriptionListUpdateTimeLapse(const Event& evt)
464{
465 fCurrentSubscriptionUpdateRate = evt.GetInt();
466
467 return GetCurrentState();
468}
469vector<Description> DataLogger::GetDescription(const string& server, const string& service)
470{
471 const lock_guard<mutex> guard(fMutex);
472 const auto it = fServiceDescriptionList.find(server+"/"+service);
473 return it==fServiceDescriptionList.end()?vector<Description>():it->second;
474}
475// --------------------------------------------------------------------------
476//
477//! Overwritten write function. This way we directly log the datalogger's messages, without going through dim's dns,
478//! thus increasing robustness.
479//! @param time: see MessageImp class param
480//! @param txt: see MessageImp class param
481//! @param qos: see MessageImp class param
482//! @return see MessageImp class param
483//
484int DataLogger::Write(const Time&time, const std::string& txt, int qos)
485{
486 ostringstream ss;
487 ss << "datalogger: " << txt;
488 if (fNightlyLogFile.is_open())
489 {
490 MessageImp mimp(fNightlyLogFile);
491 mimp.Write(time, ss.str(), qos);
492 }
493 else if (shouldBackLog)
494 {
495 ostringstream str;
496 MessageImp mimp(str);
497 mimp.Write(time, ss.str(), qos);
498 backLogBuffer.push_back(str.str());
499 }
500 return StateMachineDim::Write(time, ss.str(), qos);
501}
502// --------------------------------------------------------------------------
503//
504//! Check if a given path exists
505//! @param path the path to be checked
506//! @return whether or not the creation has been successfull
507//
508bool DataLogger::CreateDirectory(string path)
509{
510 try
511 {
512 DimWriteStatistics::CreateDirectory(path);
513 return true;
514 }
515 catch (const runtime_error &e)
516 {
517 Error(e.what());
518 return false;
519 }
520}
521// --------------------------------------------------------------------------
522//
523//! Check if a given path exists
524//! @param path the path to be checked
525//! @return whether or not the given path exists
526//
527bool DataLogger::DoesPathExist(string path)
528{
529 return DimWriteStatistics::DoesPathExist(path, *this);
530}
531
532
533void DataLogger::AddServer(const string& server)
534{
535 Info("Got request to add server " + server );
536 if (server != "DIS_DNS")
537 {
538 for (auto it=fServerDescriptionsList.begin(); it != fServerDescriptionsList.end(); it++)
539 if ((*it)->server == server)
540 {
541 if (fDebugIsOn)
542 {
543 ostringstream str;
544 str << "Already got description for server " << server << ". Ignoring." << endl;
545 Debug(str.str());
546 return;
547 }
548 }
549 DimDescriptions* d = new DimDescriptions(server);
550 d->SetCallbackDescriptions(bind(&DataLogger::HandleDescriptions, this, d));
551 d->Subscribe(*this);
552 fServerDescriptionsList.push_back(d);
553 }
554
555}
556
557// --------------------------------------------------------------------------
558//
559//! Add a new service subscription
560//! @param server the server for which the subscription should be created
561//! @param service the service for which the subscription should be created
562//! @param isCmd whether this is a Dim Command or not. Commands are not logged
563//
564void DataLogger::AddService(const Service& svc)
565{
566 const string& serverr = svc.server;
567 //FIX in order to get rid of the '+' that sometimes makes it all the way to me
568 string server = serverr;
569 if (server.size() > 0 && server[0] == '+')
570 {
571 server = server.substr(1);
572 Warn("Got a service beginning with +. This is not supposed to happen");
573 }
574// server = server.substr(1);
575
576 const string& service = svc.service;
577 const bool isCmd = svc.iscmd;
578
579 //dataLogger does not subscribe to commands
580 if (isCmd)
581 return;
582
583 Info("Got request to add service: "+server+"/"+service);
584
585 //check the given subscription against black and white lists
586 if (!ShouldSubscribe(server, service))
587 return;
588
589 map<string, SubscriptionType> &list = fServiceSubscriptions[server];
590
591 if (list.find(service) != list.end())
592 {
593 if (list[service].format != svc.format)
594 {
595 if (list[service].nightlyFile.IsOpen())
596 {
597 string fileName = list[service].nightlyFile.GetName();
598 if (fileName == "")
599 {
600 Error("Something went wrong while dealing with new format of "+server+"/"+service+" file tagged as open but filename is empty. Aborting");
601 return;
602 }
603 list[service].nightlyFile.Close();
604 list[service].increment++;
605 Warn("Format of "+server+"/"+service+" has changed. Closing "+fileName);
606/* string fileNameWithoutFits = fileName.substr(0, fileName.size()-4);
607 int counter=0;
608 while (counter < 100)
609 {
610 ostringstream newFileName;
611 newFileName << fileNameWithoutFits << counter << ".fits";
612 ifstream testStream(newFileName.str());
613 if (!testStream) //fileName available
614 {
615 rename(fileName.c_str(), newFileName.str().c_str());
616 break;
617 }
618 counter++;
619 }
620 if (counter==100)
621 Error("Could not rename "+fileName+" after 100 trials (because of format change). Aborting");
622*/
623 //reallocate the fits buffer...
624 list[service].fitsBufferAllocated = false;
625 }
626 list[service].fConv = shared_ptr<Converter>(new Converter(Out(), svc.format));
627 list[service].format = svc.format;
628 }
629 if (fDebugIsOn)
630 Debug("Service " + server + "/" + service + " is already in the dataLogger's list... ignoring update.");
631 return;
632 }
633 //DIM_REPLACE
634// list[service].dimInfo.reset(SubscribeTo(server, service));
635 if (fDebugIsOn)
636 Debug("Subscribing to service "+server+"/"+service);
637 Subscribe(server + "/" + service)
638 (bind(&DataLogger::infoCallback, this, placeholders::_1, servicesCounter));
639 list[service].server = server;
640 list[service].service = service;
641 list[service].format = svc.format;
642 list[service].index = servicesCounter;
643 fNumSubAndFitsData.numSubscriptions++;
644 //check if this is the run numbers service
645 if ((server == "FAD_CONTROL") && (service == "START_RUN"))
646 fRunNumberService = servicesCounter;
647 servicesCounter++;
648 Info("Added subscription to " + server + "/" + service);
649}
650// --------------------------------------------------------------------------
651//
652//! Remove a given service subscription
653//! @param server the server for which the subscription should be removed
654//! @param service the service that should be removed
655//! @param isCmd whether or not this is a command
656//
657void DataLogger::RemoveService(string server, string service, bool isCmd)
658{
659
660 Info("Got request to remove service: "+server+"/"+service);
661 if (fDestructing)//this function is called by the super class, after the destructor has deleted its own subscriptions
662 return;
663//FIXME unused
664 return;
665
666 if (isCmd)
667 return;
668
669 if (fServiceSubscriptions.find(server) == fServiceSubscriptions.end())
670 {
671 Error("Request to remove service "+service+" from server "+server+", but service not found.");
672 return;
673 }
674
675 if (fServiceSubscriptions[server].erase(service) != 1)
676 {
677 //check the given subscription against black and white lists
678 if (!ShouldSubscribe(server, service))
679 return;
680
681 Error("Subscription "+server+"/"+service+" could not be removed as it is not present");
682 return;
683 }
684 fNumSubAndFitsData.numSubscriptions--;
685
686 if ((server == "FAD_CONTROL") && (service == "START_RUN"))
687 fRunNumberService = 0;
688
689 Info("Removed subscription to " + server + "/" + service);
690}
691// --------------------------------------------------------------------------
692//
693//! Remove all the services associated with a given server
694//! @param server the server for which all the services should be removed
695//
696void DataLogger::RemoveAllServices(const string& server)
697{
698 Info("Got request for removing all services from: "+server);
699 if (fServiceSubscriptions.find(server)==fServiceSubscriptions.end())
700 {
701 Warn("Request to remove all services, but corresponding server " + server + " not found.");
702 return;
703 }
704//FIXME unused
705 return;
706 fNumSubAndFitsData.numSubscriptions -= fServiceSubscriptions[server].size();
707
708 fServiceSubscriptions[server].clear();
709 fServiceSubscriptions.erase(server);
710
711 if (server == "FAD_CONTROL")
712 fRunNumberService = 0;
713
714 if (fDebugIsOn)
715 Debug("Removed all subscriptions to " + server + "/");
716}
717
718// --------------------------------------------------------------------------
719//
720//! Checks if the given ofstream is in error state and if so, close it
721//! @param out the ofstream that should be checked
722//
723bool DataLogger::CheckForOfstreamError(ofstream& out, bool isDailyStream)
724{
725 if (out.good())
726 return true;
727
728 Error("An error occured while writing to a text file. Closing it");
729 if (out.is_open())
730 out.close();
731 if (isDailyStream)
732 GoToNightlyWriteErrorState();
733 else
734 GoToRunWriteErrorState();
735
736 return false;
737}
738
739bool DataLogger::OpenStreamImp(ofstream &stream, const string &filename, bool mightbeopen)
740{
741 if (stream.is_open())
742 {
743 if (!mightbeopen)
744 Error(filename+" was already open when trying to open it.");
745 return mightbeopen;
746 }
747
748 errno = 0;
749 stream.open(filename.c_str(), ios_base::out | ios_base::app);
750 if (!stream /*|| errno!=0*/)
751 {
752 ostringstream str;
753 str << "ofstream::open() failed for '" << filename << "': " << strerror(errno) << " [errno=" << errno << "]";
754 Error(str);
755 return false;
756 }
757
758 if (!stream.is_open())
759 {
760 Error("File "+filename+" not open as it ought to be.");
761 return false;
762 }
763
764 Info("Opened: "+filename);
765
766 return true;
767}
768
769bool DataLogger::OpenStream(shared_ptr<ofstream> stream, const string &filename)
770{
771 return OpenStreamImp(*stream, filename, false);
772}
773
774// --------------------------------------------------------------------------
775//
776//! Open a text file and checks for error code
777//! @param stream the ofstream for which the file should be opened
778//! @name the file name
779//
780bool DataLogger::OpenTextFile(ofstream& stream, const string& name)
781{
782 return OpenStreamImp(stream, name, true);
783}
784
785// --------------------------------------------------------------------------
786//
787//! Create a new dim subscription to a given server and service
788//! @param server the server name
789//! @param service the service name
790//
791/*EventImp& DataLogger::SubscribeTo(const string& server, const string& service)
792{
793
794 //DIM_REPLACE
795 //return new DimStampedInfo((server + "/" + service).c_str(), (void*)NULL, 0, this);
796 EventImp& newSubscription = Subscribe(server + "/" + service);
797 newSubscription.bind(&infoHandler, this, placeholders::_1);
798 return newSubscription;
799}*/
800// --------------------------------------------------------------------------
801//
802//! Check whether a service should be subscribed to, based on the black/white list entries
803//! @param server the server name associated with the service being checked
804//! @param service the service name associated with the service being checked
805//
806bool DataLogger::ShouldSubscribe(const string& server, const string& service)
807{
808 if ((fBlackList.find(server + "/") != fBlackList.end()) ||
809 (fBlackList.find(server + "/" + service) != fBlackList.end()) ||
810 (fBlackList.find("/" + service) != fBlackList.end()))
811 {
812 if (fWhiteList.size()>0 &&
813 (fWhiteList.find(server + "/" + service) != fWhiteList.end()))
814 {
815 if (fDebugIsOn)
816 Debug("White list saved service " + server + "/" + service + " from blacklisting");
817 return true;
818 }
819 if (fDebugIsOn)
820 Debug("Blacklist banned service " + server + "/" + service);
821 return false;
822 }
823 return true;
824}
825// --------------------------------------------------------------------------
826//
827//! Compiles a file name
828//! @param path the base path where to put the file
829//! @param time the time at which the file is created
830//! @param service the service name, if any
831//! @param extension the extension to add, if any
832//
833string DataLogger::CompileFileName(const string& service, const string& extension, const Time& time) const
834{
835 ostringstream str;
836
837 const Time ftime(time);//removed this as already done by nightAsInt: -boost::posix_time::hours(12));
838 str << ftime.NightAsInt();
839
840 if (!service.empty())
841 str << '.' << service;
842
843 if (!extension.empty())
844 str << "." << extension;
845
846 return str.str();
847}
848
849string DataLogger::CompileFileNameWithPath(const string& path, const string& service, const string& extension)
850{
851 ostringstream str;
852
853 const Time time;
854
855 //calculate time suitable for naming files.
856 //fCurrentDay is 30 minutes after upcoming sunrise. So just use 12 hours before then
857 const Time ftime = fCurrentDay-boost::posix_time::hours(12);
858
859 //output it
860 str << path << ftime.GetAsStr("/%Y/%m/%d");
861
862 //check if target directory exist
863 if (!DoesPathExist(str.str()))
864 CreateDirectory(str.str());
865
866 str << '/' << CompileFileName(service, extension, fCurrentDay);
867
868 return str.str();
869
870
871}
872
873// --------------------------------------------------------------------------
874//
875//!retrieves the size on disk of a file
876//! @param fileName the full file name for which the size on disk should be retrieved
877//! @return the size of the file on disk, in bytes. 0 if the file does not exist or if an error occured
878//
879off_t DataLogger::GetFileSize(const string& fileName)
880{
881 return DimWriteStatistics::GetFileSizeOnDisk(fileName, *this);
882}
883
884// --------------------------------------------------------------------------
885//
886//! Removes the oldest run number and closes the fits files that should be closed
887//! Also creates the fits grouping file
888//
889void DataLogger::RemoveOldestRunNumber()
890{
891 if (fDebugIsOn)
892 {
893 ostringstream str;
894 str << "Removing run number " << fRunNumber.front().runNumber;
895 Debug(str);
896 }
897 //remove the entry
898 fRunNumber.pop_front();
899}
900
901// --------------------------------------------------------------------------
902//
903//! Default constructor. The name of the machine is given DATA_LOGGER
904//! and the state is set to kSM_Ready at the end of the function.
905//
906//!Setup the allows states, configs and transitions for the data logger
907//
908DataLogger::DataLogger(ostream &out) : StateMachineDim(out, "DATA_LOGGER"),
909 fFilesStats("DATA_LOGGER", *this)
910{
911 shouldBackLog = true;
912
913 servicesCounter=1;
914
915 //initialize member data
916 fFilePath = ".";
917
918 fDimList.Subscribe(*this);
919 fDimList.SetCallbackServerAdd(bind(&DataLogger::AddServer, this, placeholders::_1));
920 fDimList.SetCallbackServiceAdd(bind(&DataLogger::AddService, this, placeholders::_1));
921
922 //calculate time "centered" around noon instead of midnight
923 const Time timeNow;
924// const Time nowMinusTwelve = timeNow-boost::posix_time::hours(12);
925 //the "current day" is actually the next closing time of nightly files
926 //the next closing time is 30 minutes after upcoming sunrise.
927 //If we are within 30 minutes after sunrise, closing time is soon
928 fCurrentDay = GetSunRise(Time()-boost::posix_time::minutes(30)) + boost::posix_time::minutes(30);//(int)(nowMinusTwelve.Mjd());//nowMinusTwelve.M()*31 + nowMinusTwelve.D();//assume 31 days per month. we do not really care, only want unique number per day of the year
929 lastFlush = Time();
930
931 //Give a name to this machine's specific states
932 AddStateName(kSM_NightlyOpen, "NightlyFileOpen", "The summary files for the night are open.");
933 AddStateName(kSM_WaitingRun, "WaitForRun", "The summary files for the night are open and we wait for a run to be started.");
934 AddStateName(kSM_Logging, "Logging", "The summary files for the night and the files for a single run are open.");
935 AddStateName(kSM_BadFolder, "ErrInvalidFolder", "The folder for the files is not invalid.");
936 AddStateName(kSM_DailyWriteError, "ErrDailyWrite", "An error occured while writing to a daily (and run) file.");
937 AddStateName(kSM_RunWriteError, "ErrRunWrite", "An error occured while writing to a run file.");
938
939 // Add the possible transitions for this machine
940 AddEvent("START", kSM_Ready, kSM_BadFolder)
941 (bind(&DataLogger::Start, this))
942 ("Start the nightly logging. Nightly file location must be specified already");
943
944 AddEvent("STOP", kSM_NightlyOpen, kSM_WaitingRun, kSM_Logging, kSM_DailyWriteError, kSM_RunWriteError)
945 (bind(&DataLogger::GoToReady, this))
946 ("Stop all data logging, close all files.");
947
948 AddEvent("RESET", kSM_Error, kSM_BadFolder, kSM_DailyWriteError, kSM_RunWriteError)
949 (bind(&DataLogger::GoToReady, this))
950 ("Transition to exit error states. Closes the any open file.");
951
952 AddEvent("START_RUN_LOGGING", /*kSM_Logging,*/ kSM_NightlyOpen, kSM_Ready)
953 (bind(&DataLogger::NightlyToWaitRun, this))
954 ("Go to waiting for run number state. In this state with any received run-number a new file is opened.");
955
956 AddEvent("STOP_RUN_LOGGING", kSM_WaitingRun, kSM_Logging)
957 (bind(&DataLogger::BackToNightlyOpen, this))
958 ("Go from the wait for run to nightly open state.");
959
960 // Provide a print command
961 AddEvent("PRINT_INFO")
962 (bind(&DataLogger::PrintState, this, placeholders::_1))
963 ("Print information about the internal status of the data logger.");
964
965
966 OpenFileToDim fToDim;
967 fToDim.code = 0;
968 fToDim.fileName[0] = '\0';
969
970 fOpenedNightlyFiles = new DimDescribedService(GetName() + "/FILENAME_NIGHTLY", "I:1;C", fToDim,
971 "Path and base name used for the nightly files."
972 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
973 "|Name[string]:path and base file name");
974
975 fOpenedRunFiles = new DimDescribedService(GetName() + "/FILENAME_RUN", "I:1;C", fToDim,
976 "Path and base name used for the run files."
977 "|Type[int]:type of open files (1=log, 2=rep, 4=fits)"
978 "|Name[string]:path and base file name");
979
980 fNumSubAndFitsData.numSubscriptions = 0;
981 fNumSubAndFitsData.numOpenFits = 0;
982 fNumSubAndFits = new DimDescribedService(GetName() + "/NUM_SUBS", "I:2", fNumSubAndFitsData,
983 "Num. open files + num. subscribed services"
984 "|NSubAndOpenFiles[int]:Num. of subs and open files");
985
986 //services parameters
987 fDebugIsOn = false;
988 fOpenedFilesIsOn = true;
989 fNumSubAndFitsIsOn = true;
990
991 string emptyString="";
992 //Subscription list service
993 fCurrentSubscription = new DimDescribedService(GetName() + "/SUBSCRIPTIONS", "C", emptyString.c_str(),
994 "List of all the services subscribed by datalogger, except the ones provided by itself."
995 "|Liste[string]:list of logged services and the delay in seconds since last update");
996 fCurrentSubscriptionUpdateRate = 60; //by default, 1 minute between each update
997 fLastSubscriptionUpdate = timeNow;
998
999 // provide services control commands
1000 AddEvent("SET_DEBUG_MODE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1001 (bind(&DataLogger::SetDebugOnOff, this, placeholders::_1))
1002 ("Switch debug mode on or off. Debug mode prints information about every service written to a file."
1003 "|Enable[bool]:Enable of disable debug mode (yes/no).");
1004
1005 AddEvent("SET_STATISTICS_UPDATE_INTERVAL", "S:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1006 (bind(&DataLogger::SetStatsPeriod, this, placeholders::_1))
1007 ("Interval in which the data-logger statistics service (STATS) is updated."
1008 "|Interval[ms]:Value in milliseconds (<=0: no update).");
1009
1010 AddEvent("ENABLE_FILENAME_SERVICES", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1011 (bind(&DataLogger::SetOpenedFilesOnOff ,this, placeholders::_1))
1012 ("Switch service which distributes information about the open files on or off."
1013 "|Enable[bool]:Enable of disable filename services (yes/no).");
1014
1015 AddEvent("ENABLE_NUMSUBS_SERVICE", "B:1", kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun, kSM_Ready)
1016 (bind(&DataLogger::SetNumSubsAndFitsOnOff, this, placeholders::_1))
1017 ("Switch the service which distributes information about the number of subscriptions and open files on or off."
1018 "|Enable[bool]:Enable of disable NUM_SUBS service (yes/no).");
1019
1020 AddEvent("SET_RUN_TIMEOUT", "L:1", kSM_Ready, kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun)
1021 (bind(&DataLogger::SetRunTimeoutDelay, this, placeholders::_1))
1022 ("Set the timeout delay for old run numbers."
1023 "|timeout[min]:Time out in minutes after which files for expired runs are closed.");
1024 //Provide access to the duration between two updates of the service list
1025 AddEvent("SET_SERVICE_LIST_UPDATE_INTERVAL", "I:1", kSM_Ready, kSM_NightlyOpen, kSM_Logging, kSM_WaitingRun)
1026 (bind(&DataLogger::setSubscriptionListUpdateTimeLapse, this, placeholders::_1))
1027 ("Set the min interval between two services-list updates."
1028 "|duration[sec]:The interval between two updates, in seconds.");
1029
1030 fDestructing = false;
1031
1032 fPreviousOldRunNumberCheck = Time().Mjd();
1033
1034 fDailyFileDayChangedAlready = true;
1035 fRunNumberTimeout = 60000; //default run-timeout set to 1 minute
1036 fRunNumber.push_back(RunNumberType());
1037 fRunNumber.back().runNumber = -1;
1038 fRunNumber.back().time = Time();
1039 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
1040 NotifyOpenedFile("", 0, fOpenedRunFiles);
1041
1042 fRunNumberService = 0;
1043
1044 fShouldAutoStart = false;
1045 fAutoStarted = false;
1046
1047
1048 if(fDebugIsOn)
1049 {
1050 Debug("DataLogger Init Done.");
1051 }
1052}
1053
1054// --------------------------------------------------------------------------
1055//
1056//! Destructor
1057//
1058DataLogger::~DataLogger()
1059{
1060 if (fDebugIsOn)
1061 Debug("DataLogger destruction starts");
1062
1063 //this boolean should not be required anymore
1064 fDestructing = true;
1065
1066 //now clear the services subscriptions
1067 dim_lock();
1068 fServiceSubscriptions.clear();
1069 dim_unlock();
1070
1071 //clear any remaining run number (should remain only one)
1072 while (fRunNumber.size() > 0)
1073 {
1074 RemoveOldestRunNumber();
1075 }
1076 //go to the ready state. i.e. close all files, run-wise first
1077 GoToReady();
1078
1079 Info("Will soon close the daily log file");
1080
1081 delete fOpenedNightlyFiles;
1082 delete fOpenedRunFiles;
1083 delete fNumSubAndFits;
1084 delete fCurrentSubscription;
1085
1086 if (fNightlyLogFile.is_open())//this file is the only one that has not been closed by GoToReady
1087 {
1088 fNightlyLogFile << endl;
1089 fNightlyLogFile.close();
1090 }
1091
1092 for (auto it=fServerDescriptionsList.begin(); it!= fServerDescriptionsList.end(); it++)
1093 delete *it;
1094
1095 if (fDebugIsOn)
1096 Debug("DataLogger desctruction ends");
1097}
1098
1099// --------------------------------------------------------------------------
1100//
1101//! checks if old run numbers should be trimmed and if so, do it
1102//
1103void DataLogger::TrimOldRunNumbers()
1104{
1105 const Time cTime = Time();
1106
1107 if (cTime - fPreviousOldRunNumberCheck < boost::posix_time::milliseconds(fRunNumberTimeout))
1108 return;
1109
1110 while (fRunNumber.size() > 1 && (cTime - fRunNumber.back().time) > boost::posix_time::milliseconds(fRunNumberTimeout))
1111 {
1112 RemoveOldestRunNumber();
1113 }
1114 fPreviousOldRunNumberCheck = cTime;
1115}
1116// --------------------------------------------------------------------------
1117//
1118//! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them
1119//
1120int DataLogger::infoCallback(const EventImp& evt, unsigned int subIndex)
1121{
1122// if (fDebugIsOn)
1123// {
1124// ostringstream str;
1125// str << "Got infoCallback called with service index= " << subIndex;
1126// Debug(str.str());
1127// }
1128
1129 if ((GetCurrentState() == kSM_Ready) && (!fAutoStarted) && fShouldAutoStart)
1130 {
1131 fAutoStarted = true;
1132 SetCurrentState(Start());
1133 }
1134 else
1135 {
1136 if (GetCurrentState() > kSM_Ready)
1137 fAutoStarted = true;
1138 }
1139
1140
1141 //check if the service pointer corresponds to something that we subscribed to
1142 //this is a fix for a bug that provides bad Infos when a server starts
1143 bool found = false;
1144 SubscriptionsListType::iterator x;
1145 map<string, SubscriptionType>::iterator y;
1146 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1147 {//find current service is subscriptions
1148 //Edit: this should be useless now... remove it sometimes ?
1149 for (y=x->second.begin(); y!=x->second.end();y++)
1150 if (y->second.index == subIndex)
1151 {
1152 found = true;
1153 break;
1154 }
1155 if (found)
1156 break;
1157 }
1158
1159 if (!found && fDebugIsOn)
1160 {
1161 ostringstream str;
1162 str << "Service " << evt.GetName() << " not found in subscriptions" << endl;
1163 Debug(str.str());
1164 }
1165 if (!found)
1166 return GetCurrentState();
1167
1168
1169 if (evt.GetSize() == 0 && fDebugIsOn)
1170 {
1171 ostringstream str;
1172 str << "Got 0 size for " << evt.GetName() << endl;
1173 Debug(str.str());
1174 }
1175 if (evt.GetSize() == 0)
1176 return GetCurrentState();
1177
1178 if (evt.GetFormat() == "" && fDebugIsOn)
1179 {
1180 ostringstream str;
1181 str << "Got no format for " << evt.GetName() << endl;
1182 Debug(str.str());
1183 }
1184 if (evt.GetFormat() == "")
1185 return GetCurrentState();
1186
1187// cout.precision(20);
1188// cout << "Orig timestamp: " << Time(I->getTimestamp(), I->getTimestampMillisecs()*1000).Mjd() << endl;
1189 // FIXME: Here we have to check if we have received the
1190 // service with the run-number.
1191 // CheckForRunNumber(I); has been removed because we have to
1192 // subscribe to this service anyway and hence we have the pointer
1193 // (no need to check for the name)
1194 CheckForRunNumber(evt, subIndex);
1195
1196 Report(evt, y->second);
1197
1198 //remove old run numbers
1199 TrimOldRunNumbers();
1200
1201 return GetCurrentState();
1202}
1203
1204// --------------------------------------------------------------------------
1205//
1206//! Add a new active run number
1207//! @param newRun the new run number
1208//! @param time the time at which the new run number was issued
1209//
1210void DataLogger::AddNewRunNumber(int64_t newRun, Time time)
1211{
1212
1213 if (newRun > 0xffffffff)
1214 {
1215 Error("New run number too large, out of range. Ignoring.");
1216 return;
1217 }
1218 for (std::vector<int64_t>::const_iterator it=previousRunNumbers.begin(); it != previousRunNumbers.end(); it++)
1219 {
1220 if (*it == newRun)
1221 {
1222 Error("Newly provided run number has already been used (or is still in use). Going to error state");
1223 SetCurrentState(kSM_BadFolder);
1224 return;
1225 }
1226 }
1227 if (fDebugIsOn)
1228 {
1229 ostringstream str;
1230 str << "Adding new run number " << newRun << " issued at " << time;
1231 Debug(str);
1232 }
1233 //Add new run number to run number list
1234 fRunNumber.push_back(RunNumberType());
1235 fRunNumber.back().runNumber = int32_t(newRun);
1236 fRunNumber.back().time = time;
1237
1238 if (fDebugIsOn)
1239 {
1240 ostringstream str;
1241 str << "The new run number is: " << fRunNumber.back().runNumber;
1242 Debug(str);
1243 }
1244 if (GetCurrentState() != kSM_Logging && GetCurrentState() != kSM_WaitingRun )
1245 return;
1246
1247 if (newRun > 0 && GetCurrentState() == kSM_WaitingRun)
1248 SetCurrentState(kSM_Logging);
1249 if (newRun < 0 && GetCurrentState() == kSM_Logging)
1250 SetCurrentState(kSM_WaitingRun);
1251}
1252// --------------------------------------------------------------------------
1253//
1254//! Checks whether or not the current info is a run number.
1255//! If so, then remember it. A run number is required to open the run-log file
1256//! @param I
1257//! the current DimInfo
1258//
1259void DataLogger::CheckForRunNumber(const EventImp& evt, unsigned int index)
1260{
1261 if (index != fRunNumberService)
1262 return;
1263// int64_t newRun = reinterpret_cast<const uint64_t*>(evt.GetData())[0];
1264 AddNewRunNumber(evt.GetXtra(), evt.GetTime());
1265}
1266// --------------------------------------------------------------------------
1267//
1268//! Get SunRise. Copied from drivectrl.cc
1269//! Used to know when to close and reopen files
1270//!
1271Time DataLogger::GetSunRise(const Time &time)
1272{
1273 const double lon = -(17.+53./60+26.525/3600);
1274 const double lat = 28.+45./60+42.462/3600;
1275
1276 ln_lnlat_posn observer;
1277 observer.lng = lon;
1278 observer.lat = lat;
1279
1280 // This caluclates the sun-rise of the next day after 12:00 noon
1281 ln_rst_time sun_day;
1282 if (ln_get_solar_rst(time.JD(), &observer, &sun_day)==1)
1283 {
1284 Fatal("GetSunRise reported the sun to be circumpolar!");
1285 return Time(Time::none);
1286 }
1287
1288 if (Time(sun_day.rise)>=time)
1289 return Time(sun_day.rise);
1290
1291 if (ln_get_solar_rst(time.JD()+0.5, &observer, &sun_day)==1)
1292 {
1293 Fatal("GetSunRise reported the sun to be circumpolar!");
1294 return Time(Time::none);
1295 }
1296
1297 return Time(sun_day.rise);
1298}
1299
1300// --------------------------------------------------------------------------
1301//
1302//! write infos to log files.
1303//! @param I
1304//! The current DimInfo
1305//! @param sub
1306//! The dataLogger's subscription corresponding to this DimInfo
1307//
1308void DataLogger::Report(const EventImp& evt, SubscriptionType& sub)
1309{
1310 const string fmt(evt.GetFormat());
1311
1312 const bool isItaReport = fmt!="C";
1313
1314 if (!fNightlyLogFile.is_open())
1315 return;
1316
1317 if (fDebugIsOn && string(evt.GetName())!="DATA_LOGGER/MESSAGE")
1318 {
1319 ostringstream str;
1320 str << "Logging " << evt.GetName() << " [" << evt.GetFormat() << "] (" << evt.GetSize() << ")";
1321 Debug(str);
1322 }
1323
1324 //
1325 // Check whether we should close and reopen daily text files or not
1326 // calculate time "centered" around noon instead of midnight
1327 // if number of days has changed, then files should be closed and reopenned.
1328 const Time timeNow;
1329// const Time nowMinusTwelve = timeNow-boost::posix_time::hours(12);
1330// int newDayNumber = (int)(nowMinusTwelve.Mjd());
1331
1332 //also check if we should flush the nightly files
1333 if (lastFlush < timeNow-boost::posix_time::minutes(1))
1334 {
1335 lastFlush = timeNow;
1336 SubscriptionsListType::iterator x;
1337 map<string, SubscriptionType>::iterator y;
1338 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1339 {//find current service is subscriptions
1340 for (y=x->second.begin(); y!=x->second.end();y++)
1341 if (y->second.nightlyFile.IsOpen())
1342 {
1343 y->second.nightlyFile.Flush();
1344 }
1345 }
1346 if (fDebugIsOn)
1347 Debug("Just flushed nightly fits files to the disk");
1348 }
1349 //check if we should close and reopen the nightly files
1350 if (timeNow > fCurrentDay)//GetSunRise(fCurrentDay)+boost::posix_time::minutes(30)) //if we went past 30 minutes after sunrise
1351 {
1352 //set the next closing time. If we are here, we have passed 30 minutes after sunrise.
1353 fCurrentDay = GetSunRise(timeNow-boost::posix_time::minutes(30))+boost::posix_time::minutes(30);
1354 //crawl through the subcriptions and close any open nightly file
1355 SubscriptionsListType::iterator x;
1356 map<string, SubscriptionType>::iterator y;
1357 for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++)
1358 {//find current service is subscriptions
1359 for (y=x->second.begin(); y!=x->second.end();y++)
1360 if (y->second.nightlyFile.IsOpen())
1361 {
1362 y->second.nightlyFile.Close();
1363 }
1364 }
1365
1366 if (fDebugIsOn)
1367 Debug("Day have changed! Closing and reopening nightly files");
1368
1369 fNightlyLogFile << endl;
1370 fNightlyLogFile.close();
1371 fNightlyReportFile.close();
1372
1373 Info("Closed: "+fFullNightlyLogFileName);
1374 Info("Closed: "+fFullNightlyReportFileName);
1375
1376 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
1377 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
1378 {
1379 GoToReady();
1380 SetCurrentState(kSM_BadFolder);
1381 return;
1382 }
1383 fNightlyLogFile << endl;
1384
1385 fFullNightlyReportFileName = CompileFileNameWithPath(fFilePath, "", "rep");
1386 if (!OpenTextFile(fNightlyReportFile, fFullNightlyReportFileName))
1387 {
1388 GoToReady();
1389 SetCurrentState(kSM_BadFolder);
1390 return;
1391 }
1392 }
1393 //create the converter for that service
1394 if (!sub.fConv)
1395 {
1396 sub.fConv = shared_ptr<Converter>(new Converter(Out(), evt.GetFormat()));
1397 if (!sub.fConv->valid())
1398 {
1399 ostringstream str;
1400 str << "Couldn't properly parse the format... service " << evt.GetName() << " ignored.";
1401 Error(str);
1402 return;
1403 }
1404 }
1405 //construct the header
1406 ostringstream header;
1407 const Time cTime(evt.GetTime());
1408 fQuality = evt.GetQoS();
1409
1410 //update subscription last received time
1411 sub.lastReceivedEvent = cTime;
1412 //update subscription list service if required
1413 updateSubscriptionList();
1414
1415 fMjD = cTime.Mjd() ? cTime.Mjd()-40587 : 0;
1416
1417 if (isItaReport)
1418 {
1419//DISABLED REPORT WRITING BY THOMAS REQUEST
1420 //write text header
1421/* string serviceName = (sub.service == "MESSAGE") ? "" : "_"+sub.service;
1422 header << sub.server << serviceName << " " << fQuality << " ";
1423 header << evt.GetTime() << " ";
1424
1425 string text;
1426 try
1427 {
1428 text = sub.fConv->GetString(evt.GetData(), evt.GetSize());
1429 }
1430 catch (const runtime_error &e)
1431 {
1432 ostringstream str;
1433 str << "Parsing service " << evt.GetName();
1434 str << " failed: " << e.what() << " removing the subscription to " << sub.server << "/" << sub.service;
1435 Warn(str);
1436 //remove this subscription from the list.
1437 //because these operators use references to elements, and because they're supposed here to erase these objects on the way, I'm not too sure... so duplicate the names !
1438 RemoveService(sub.server, sub.service, false);
1439 return;
1440 }
1441
1442 if (text.empty())
1443 {
1444 ostringstream str;
1445 str << "Service " << evt.GetName() << " sent an empty string";
1446 Info(str);
1447 return;
1448 }
1449 //replace bizarre characters by white space
1450 replace(text.begin(), text.end(), '\n', '\\');
1451 replace_if(text.begin(), text.end(), ptr_fun<int, int>(&iscntrl), ' ');
1452
1453 //write entry to Nightly report
1454 if (fNightlyReportFile.is_open())
1455 {
1456 fNightlyReportFile << header.str() << text << endl;
1457 if (!CheckForOfstreamError(fNightlyReportFile, true))
1458 return;
1459 }
1460*/
1461#ifdef HAVE_FITS
1462 //check if the last received event was before noon and if current one is after noon.
1463 //if so, close the file so that it gets reopened.
1464// sub.lastReceivedEvent = cTime;
1465 if (!sub.nightlyFile.IsOpen())
1466 if (GetCurrentState() != kSM_Ready)
1467 OpenFITSFiles(sub);
1468 WriteToFITS(sub, evt.GetData());
1469#endif
1470 }
1471 else
1472 {//write entry to Nightly log
1473 vector<string> strings;
1474 try
1475 {
1476 strings = sub.fConv->ToStrings(evt.GetData());
1477 }
1478 catch (const runtime_error &e)
1479 {
1480 ostringstream str;
1481 str << "Parsing service " << evt.GetName();
1482 str << " failed: " << e.what() << " removing the subscription for now.";
1483 Error(str);
1484 //remove this subscription from the list.
1485 //because these operators use references to elements, and because they're supposed here to erase these objects on the way, I'm not too sure... so duplicate the names !
1486 RemoveService(sub.server, sub.service, false);
1487 return;
1488 }
1489 if (strings.size() > 1)
1490 {
1491 ostringstream err;
1492 err << "There was more than one string message in service " << evt.GetName() << " going to fatal error state";
1493 Error(err.str());
1494 }
1495 ostringstream msg;
1496 string serviceName = (sub.service == "MESSAGE") ? "" : "_"+sub.service;
1497 msg << sub.server << serviceName << ": " << strings[0];
1498
1499 if (fNightlyLogFile.is_open())
1500 {
1501 MessageImp(fNightlyLogFile).Write(cTime, msg.str().c_str(), fQuality);
1502 if (!CheckForOfstreamError(fNightlyLogFile, true))
1503 return;
1504 }
1505
1506// sub.lastReceivedEvent = cTime;
1507 if (!sub.nightlyFile.IsOpen())
1508 if (GetCurrentState() != kSM_Ready)
1509 OpenFITSFiles(sub);
1510 WriteToFITS(sub, evt.GetData());
1511 }
1512}
1513
1514// --------------------------------------------------------------------------
1515//
1516//! print the dataLogger's current state. invoked by the PRINT command
1517//! @param evt
1518//! the current event. Not used by the method
1519//! @returns
1520//! the new state. Which, in that case, is the current state
1521//!
1522int DataLogger::PrintState(const Event& )
1523{
1524 Message("------------------------------------------");
1525 Message("------- DATA LOGGER CURRENT STATE --------");
1526 Message("------------------------------------------");
1527
1528 //print the path configuration
1529#if BOOST_VERSION < 104600
1530 Message("File path: " + boost::filesystem::system_complete(boost::filesystem::path(fFilePath)).directory_string());
1531#else
1532 Message("File path: " + boost::filesystem::system_complete(boost::filesystem::path(fFilePath)).parent_path().string());
1533#endif
1534
1535 //print active run numbers
1536 ostringstream str;
1537 //timeout value
1538 str << "Timeout delay for old run numbers: " << fRunNumberTimeout << " ms";
1539 Message(str);
1540 str.str("");
1541 str << "Active Run Numbers:";
1542 for (list<RunNumberType>::const_iterator it=fRunNumber.begin(); it!=fRunNumber.end(); it++)
1543 str << " " << it->runNumber;
1544 if (fRunNumber.size()==0)
1545 str << " <none>";
1546 Message(str);
1547
1548 //print all the open files.
1549 Message("------------ OPEN FILES ----------------");
1550 if (fNightlyLogFile.is_open())
1551 Message("Nightly log-file: "+fFullNightlyLogFileName);
1552
1553 if (fNightlyReportFile.is_open())
1554 Message("Nightly report-file: "+fFullNightlyReportFileName);
1555
1556 const DimWriteStatistics::Stats statVar = fFilesStats.GetTotalSizeWritten();
1557 // /*const bool statWarning =*/ calculateTotalSizeWritten(statVar, true);
1558#ifdef HAVE_FITS
1559 str.str("");
1560 str << "Number of open FITS files: " << fNumSubAndFitsData.numOpenFits;
1561 Message(str);
1562 // FIXME: Print list of open FITS files
1563#else
1564 Message("FITS output disabled at compilation");
1565#endif
1566 Message("----------------- STATS ------------------");
1567 if (fFilesStats.GetUpdateInterval()>0)
1568 {
1569 str.str("");
1570 str << "Statistics are updated every " << fFilesStats.GetUpdateInterval() << " ms";
1571 Message(str);
1572 }
1573 else
1574 Message("Statistics updates are currently disabled.");
1575 str.str("");
1576 str << "Total Size written: " << statVar.sizeWritten/1000 << " kB";
1577 Message(str);
1578 str.str("");
1579 str << "Disk free space: " << statVar.freeSpace/1000000 << " MB";
1580 Message(str);
1581
1582 Message("------------ DIM SUBSCRIPTIONS -----------");
1583 str.str("");
1584 str << "There are " << fNumSubAndFitsData.numSubscriptions << " active DIM subscriptions.";
1585 Message(str);
1586 for (map<const string, map<string, SubscriptionType> >::const_iterator it=fServiceSubscriptions.begin(); it!= fServiceSubscriptions.end();it++)
1587 {
1588 Message("Server "+it->first);
1589 for (map<string, SubscriptionType>::const_iterator it2=it->second.begin(); it2!=it->second.end(); it2++)
1590 Message(" -> "+it2->first);
1591 }
1592 Message("--------------- BLOCK LIST ---------------");
1593 for (set<string>::const_iterator it=fBlackList.begin(); it != fBlackList.end(); it++)
1594 Message(" -> "+*it);
1595 if (fBlackList.size()==0)
1596 Message(" <empty>");
1597
1598 Message("--------------- ALLOW LIST ---------------");
1599 for (set<string>::const_iterator it=fWhiteList.begin(); it != fWhiteList.end(); it++)
1600 Message(" -> "+*it);
1601 if (fWhiteList.size()==0)
1602 Message(" <empty>");
1603
1604 Message("-------------- GROUPING LIST -------------");
1605 Message("The following servers and/or services will");
1606 Message("be grouped into a single fits file:");
1607 for (set<string>::const_iterator it=fGrouping.begin(); it != fGrouping.end(); it++)
1608 Message(" -> "+*it);
1609 if (fGrouping.size()==0)
1610 Message(" <no grouping>");
1611
1612 Message("------------------------------------------");
1613 Message("-------- END OF DATA LOGGER STATE --------");
1614 Message("------------------------------------------");
1615
1616 return GetCurrentState();
1617}
1618
1619// --------------------------------------------------------------------------
1620//
1621//! turn debug mode on and off
1622//! @param evt
1623//! the current event. contains the instruction string: On, Off, on, off, ON, OFF, 0 or 1
1624//! @returns
1625//! the new state. Which, in that case, is the current state
1626//!
1627int DataLogger::SetDebugOnOff(const Event& evt)
1628{
1629 const bool backupDebug = fDebugIsOn;
1630
1631 fDebugIsOn = evt.GetBool();
1632
1633 if (fDebugIsOn == backupDebug)
1634 Message("Debug mode was already in the requested state.");
1635
1636 ostringstream str;
1637 str << "Debug mode is now " << fDebugIsOn;
1638 Message(str);
1639
1640 fFilesStats.SetDebugMode(fDebugIsOn);
1641
1642 return GetCurrentState();
1643}
1644// --------------------------------------------------------------------------
1645//
1646//! set the statistics update period duration. 0 disables the statistics
1647//! @param evt
1648//! the current event. contains the new duration.
1649//! @returns
1650//! the new state. Which, in that case, is the current state
1651//!
1652int DataLogger::SetStatsPeriod(const Event& evt)
1653{
1654 fFilesStats.SetUpdateInterval(evt.GetShort());
1655 return GetCurrentState();
1656}
1657// --------------------------------------------------------------------------
1658//
1659//! set the opened files service on or 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::SetOpenedFilesOnOff(const Event& evt)
1666{
1667 const bool backupOpened = fOpenedFilesIsOn;
1668
1669 fOpenedFilesIsOn = evt.GetBool();
1670
1671 if (fOpenedFilesIsOn == backupOpened)
1672 Message("Opened files service mode was already in the requested state.");
1673
1674 ostringstream str;
1675 str << "Opened files service mode is now " << fOpenedFilesIsOn;
1676 Message(str);
1677
1678 return GetCurrentState();
1679}
1680
1681// --------------------------------------------------------------------------
1682//
1683//! set the number of subscriptions and opened fits on and off
1684//! @param evt
1685//! the current event. contains the instruction string. similar to setdebugonoff
1686//! @returns
1687//! the new state. Which, in that case, is the current state
1688//!
1689int DataLogger::SetNumSubsAndFitsOnOff(const Event& evt)
1690{
1691 const bool backupSubs = fNumSubAndFitsIsOn;
1692
1693 fNumSubAndFitsIsOn = evt.GetBool();
1694
1695 if (fNumSubAndFitsIsOn == backupSubs)
1696 Message("Number of subscriptions service mode was already in the requested state");
1697
1698 ostringstream str;
1699 str << "Number of subscriptions service mode is now " << fNumSubAndFitsIsOn;
1700 Message(str);
1701
1702 return GetCurrentState();
1703}
1704// --------------------------------------------------------------------------
1705//
1706//! set the timeout delay for old run numbers
1707//! @param evt
1708//! the current event. contains the timeout delay long value
1709//! @returns
1710//! the new state. Which, in that case, is the current state
1711//!
1712int DataLogger::SetRunTimeoutDelay(const Event& evt)
1713{
1714 if (evt.GetUInt() == 0)
1715 {
1716 Error("Timeout delays for old run numbers must be greater than 0... ignored.");
1717 return GetCurrentState();
1718 }
1719
1720 if (fRunNumberTimeout == evt.GetUInt())
1721 Message("New timeout for old run numbers is same value as previous one.");
1722
1723 fRunNumberTimeout = evt.GetUInt();
1724
1725 ostringstream str;
1726 str << "Timeout delay for old run numbers is now " << fRunNumberTimeout << " ms";
1727 Message(str);
1728
1729 return GetCurrentState();
1730}
1731
1732// --------------------------------------------------------------------------
1733//
1734//! Notifies the DIM service that a particular file was opened
1735//! @ param name the base name of the opened file, i.e. without path nor extension.
1736//! WARNING: use string instead of string& because I pass values that do not convert to string&.
1737//! this is not a problem though because file are not opened so often.
1738//! @ param type the type of the opened file. 0 = none open, 1 = log, 2 = text, 4 = fits
1739inline void DataLogger::NotifyOpenedFile(const string &name, int type, DimDescribedService* service)
1740{
1741 if (!fOpenedFilesIsOn)
1742 return;
1743
1744 if (fDebugIsOn)
1745 {
1746 ostringstream str;
1747 str << "Updating " << service->getName() << " file '" << name << "' (type=" << type << ")";
1748 Debug(str);
1749
1750 str.str("");
1751 str << "Num subscriptions: " << fNumSubAndFitsData.numSubscriptions << " Num open FITS files: " << fNumSubAndFitsData.numOpenFits;
1752 Debug(str);
1753 }
1754
1755 if (name.size()+1 > FILENAME_MAX)
1756 {
1757 Error("Provided file name '" + name + "' is longer than allowed file name length.");
1758 return;
1759 }
1760
1761 OpenFileToDim fToDim;
1762 fToDim.code = type;
1763 memcpy(fToDim.fileName, name.c_str(), name.size()+1);
1764
1765 service->setData(reinterpret_cast<void*>(&fToDim), name.size()+1+sizeof(uint32_t));
1766 service->setQuality(0);
1767 service->Update();
1768}
1769// --------------------------------------------------------------------------
1770//
1771//! Implements the Start transition.
1772//! Concatenates the given path for the Nightly file and the filename itself (based on the day),
1773//! and tries to open it.
1774//! @returns
1775//! kSM_NightlyOpen if success, kSM_BadFolder if failure
1776int DataLogger::Start()
1777{
1778 if (fDebugIsOn)
1779 {
1780 Debug("Starting...");
1781 }
1782 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
1783 bool nightlyLogOpen = fNightlyLogFile.is_open();
1784 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
1785 return kSM_BadFolder;
1786 if (!nightlyLogOpen)
1787 fNightlyLogFile << endl;
1788
1789 fFullNightlyReportFileName = CompileFileNameWithPath(fFilePath, "", "rep");
1790 if (!OpenTextFile(fNightlyReportFile, fFullNightlyReportFileName))
1791 {
1792 fNightlyLogFile.close();
1793 Info("Closed: "+fFullNightlyReportFileName);
1794 return kSM_BadFolder;
1795 }
1796
1797 fFilesStats.FileOpened(fFullNightlyLogFileName);
1798 fFilesStats.FileOpened(fFullNightlyReportFileName);
1799 //notify that a new file has been opened.
1800 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
1801 NotifyOpenedFile(baseFileName, 3, fOpenedNightlyFiles);
1802
1803 fOpenedNightlyFits.clear();
1804
1805 return kSM_NightlyOpen;
1806}
1807
1808#ifdef HAVE_FITS
1809// --------------------------------------------------------------------------
1810//
1811//! open if required a the FITS files corresponding to a given subscription
1812//! @param sub
1813//! the current DimInfo subscription being examined
1814void DataLogger::OpenFITSFiles(SubscriptionType& sub)
1815{
1816 string serviceName(sub.server + "_" + sub.service);//evt.GetName());
1817
1818 for (unsigned int i=0;i<serviceName.size(); i++)
1819 {
1820 if (serviceName[i] == '/')
1821 {
1822 serviceName[i] = '_';
1823 break;
1824 }
1825 }
1826 //we open the NightlyFile anyway, otherwise this function shouldn't have been called.
1827 if (!sub.nightlyFile.IsOpen())
1828 {
1829 string incrementedServiceName = serviceName;
1830 if (sub.increment != 0)
1831 {
1832 ostringstream str;
1833 str << "." << sub.increment;
1834 incrementedServiceName += str.str();
1835 }
1836 const string partialName = CompileFileNameWithPath(fFilePath, incrementedServiceName, "fits");
1837
1838 const string fileNameOnly = partialName.substr(partialName.find_last_of('/')+1, partialName.size());
1839 if (!sub.fitsBufferAllocated)
1840 AllocateFITSBuffers(sub);
1841 //get the size of the file we're about to open
1842 if (fFilesStats.FileOpened(partialName))
1843 fOpenedNightlyFits[fileNameOnly].push_back(serviceName);
1844
1845 if (!sub.nightlyFile.Open(partialName, serviceName, &fNumSubAndFitsData.numOpenFits, this, 0))
1846 {
1847 GoToRunWriteErrorState();
1848 return;
1849 }
1850
1851 ostringstream str;
1852 str << "Opened: " << partialName << " (Nfits=" << fNumSubAndFitsData.numOpenFits << ")";
1853 Info(str);
1854
1855 //notify the opening
1856 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
1857 NotifyOpenedFile(baseFileName, 7, fOpenedNightlyFiles);
1858 if (fNumSubAndFitsIsOn)
1859 fNumSubAndFits->Update();
1860 }
1861
1862}
1863// --------------------------------------------------------------------------
1864//
1865//! Allocates the required memory for a given pair of fits files (nightly and run)
1866//! @param sub the subscription of interest.
1867//
1868void DataLogger::AllocateFITSBuffers(SubscriptionType& sub)
1869{
1870 //Init the time columns of the file
1871 Description dateDesc(string("Time"), string("Modified Julian Date"), string("MJD"));
1872 sub.nightlyFile.AddStandardColumn(dateDesc, "1D", &fMjD, sizeof(double));
1873
1874 Description QoSDesc("QoS", "Quality of service", "");
1875 sub.nightlyFile.AddStandardColumn(QoSDesc, "1J", &fQuality, sizeof(int));
1876
1877 // Compilation failed
1878 if (!sub.fConv->valid())
1879 {
1880 Error("Compilation of format string failed.");
1881 return;
1882 }
1883
1884 //we've got a nice structure describing the format of this service's messages.
1885 //Let's create the appropriate FITS columns
1886 const vector<string> dataFormatsLocal = sub.fConv->GetFitsFormat();
1887
1888 ostringstream str;
1889 str << "Initializing data columns for service " << sub.server << "/" << sub.service;
1890 Info(str);
1891 sub.nightlyFile.InitDataColumns(GetDescription(sub.server, sub.service), dataFormatsLocal, this);
1892
1893 sub.fitsBufferAllocated = true;
1894}
1895// --------------------------------------------------------------------------
1896//
1897//! write a dimInfo data to its corresponding FITS files
1898//
1899//FIXME: DO I REALLY NEED THE EVENT IMP HERE ???
1900void DataLogger::WriteToFITS(SubscriptionType& sub, const void* data)
1901{
1902 //nightly File status (open or not) already checked
1903 if (sub.nightlyFile.IsOpen())
1904 {
1905 if (!sub.nightlyFile.Write(*sub.fConv.get(), data))
1906 {
1907 RemoveService(sub.server, sub.service, false);
1908 GoToNightlyWriteErrorState();
1909 return;
1910 }
1911 }
1912}
1913#endif //if has_fits
1914// --------------------------------------------------------------------------
1915//
1916//! Go to Run Write Error State
1917// A write error has occurred. Checks what is the current state and take appropriate action
1918void DataLogger::GoToRunWriteErrorState()
1919{
1920 if ((GetCurrentState() != kSM_RunWriteError) &&
1921 (GetCurrentState() != kSM_DailyWriteError))
1922 SetCurrentState(kSM_RunWriteError);
1923}
1924// --------------------------------------------------------------------------
1925//
1926//! Go to Nightly Write Error State
1927// A write error has occurred. Checks what is the current state and take appropriate action
1928void DataLogger::GoToNightlyWriteErrorState()
1929{
1930 if (GetCurrentState() != kSM_DailyWriteError)
1931 SetCurrentState(kSM_DailyWriteError);
1932}
1933
1934
1935#ifdef HAVE_FITS
1936// --------------------------------------------------------------------------
1937//
1938//! Create a fits group file with all the run-fits that were written (either daily or run)
1939//! @param filesToGroup a map of filenames mapping to table names to be grouped (i.e. a
1940//! single file can contain several tables to group
1941//! @param runNumber the run number that should be used for grouping. 0 means nightly group
1942//
1943void DataLogger::CreateFitsGrouping(map<string, vector<string> > & filesToGroup)
1944{
1945 if (fDebugIsOn)
1946 {
1947 ostringstream str;
1948 str << "Creating fits group for nightly files";
1949 Debug(str);
1950 }
1951 //create the FITS group corresponding to the ending run.
1952 CCfits::FITS* groupFile;
1953 unsigned int numFilesToGroup = 0;
1954 unsigned int maxCharLength = 0;
1955 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it != filesToGroup.end(); it++)
1956 {
1957 //add the number of tables in this file to the total number to group
1958 numFilesToGroup += it->second.size();
1959 //check the length of all the strings to be written, to determine the max string length to write
1960 if (it->first.size() > maxCharLength)
1961 maxCharLength = it->first.size();
1962 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++)
1963 if (jt->size() > maxCharLength)
1964 maxCharLength = jt->size();
1965 }
1966
1967 if (fDebugIsOn)
1968 {
1969 ostringstream str;
1970 str << "There are " << numFilesToGroup << " tables to group";
1971 Debug(str);
1972 }
1973 if (numFilesToGroup <= 1)
1974 {
1975 filesToGroup.clear();
1976 return;
1977 }
1978 const string groupName = CompileFileNameWithPath(fFilePath, "", "fits");
1979
1980 Info("Creating FITS group in: "+groupName);
1981
1982 CCfits::Table* groupTable;
1983
1984 try
1985 {
1986 groupFile = new CCfits::FITS(groupName, CCfits::RWmode::Write);
1987 //setup the column names
1988 ostringstream pathTypeName;
1989 pathTypeName << maxCharLength << "A";
1990 vector<string> names;
1991 vector<string> dataTypes;
1992 names.push_back("MEMBER_XTENSION");
1993 dataTypes.push_back("8A");
1994 names.push_back("MEMBER_URI_TYPE");
1995 dataTypes.push_back("3A");
1996 names.push_back("MEMBER_LOCATION");
1997 dataTypes.push_back(pathTypeName.str());
1998 names.push_back("MEMBER_NAME");
1999 dataTypes.push_back(pathTypeName.str());
2000 names.push_back("MEMBER_VERSION");
2001 dataTypes.push_back("1J");
2002 names.push_back("MEMBER_POSITION");
2003 dataTypes.push_back("1J");
2004
2005 groupTable = groupFile->addTable("GROUPING", numFilesToGroup, names, dataTypes);
2006//TODO handle the case when the logger was stopped and restarted during the same day, i.e. the grouping file must be updated
2007 }
2008 catch (CCfits::FitsException e)
2009 {
2010 ostringstream str;
2011 str << "Creating FITS table GROUPING in " << groupName << ": " << e.message();
2012 Error(str);
2013 return;
2014 }
2015 try
2016 {
2017 groupTable->addKey("GRPNAME", "FACT_RAW_DATA", "Data from the FACT telescope");
2018 }
2019 catch (CCfits::FitsException e)
2020 {
2021 Error("CCfits::Table::addKey failed for 'GRPNAME' in '"+groupName+"-GROUPING': "+e.message());
2022 return;
2023 }
2024 //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.
2025 //use cfitsio routines instead
2026 groupTable->makeThisCurrent();
2027 //create appropriate buffer.
2028 const unsigned int n = 8 + 3 + 2*maxCharLength + 1 + 8; //+1 for trailling character
2029
2030 vector<char> realBuffer(n);
2031
2032 char *startOfExtension = realBuffer.data();
2033 char *startOfURI = realBuffer.data()+8;
2034 char *startOfLocation = realBuffer.data()+8+3;
2035 char *startOfName = realBuffer.data()+8+3+maxCharLength;
2036
2037 strcpy(startOfExtension, "BINTABLE");
2038 strcpy(startOfURI, "URL");
2039
2040 realBuffer[8+3+2*maxCharLength+3] = 1;
2041 realBuffer[8+3+2*maxCharLength+7] = 1;
2042
2043 int i=1;
2044 for (map<string, vector<string> >::const_iterator it=filesToGroup.begin(); it!=filesToGroup.end(); it++)
2045 for (vector<string>::const_iterator jt=it->second.begin(); jt != it->second.end(); jt++, i++)
2046 {
2047 memset(startOfLocation, 0, 2*maxCharLength+1+8);
2048
2049 strcpy(startOfLocation, it->first.c_str());
2050 strcpy(startOfName, jt->c_str());
2051
2052 if (fDebugIsOn)
2053 {
2054 ostringstream str;
2055 str << "Grouping " << it->first << " " << *jt;
2056 Debug(str);
2057 }
2058
2059 int status = 0;
2060 fits_write_tblbytes(groupFile->fitsPointer(), i, 1, 8+3+2*maxCharLength +8,
2061 reinterpret_cast<unsigned char*>(realBuffer.data()), &status);
2062 if (status)
2063 {
2064 char text[30];//max length of cfitsio error strings (from doc)
2065 fits_get_errstatus(status, text);
2066 ostringstream str;
2067 str << "Writing FITS row " << i << " in " << groupName << ": " << text << " (file_write_tblbytes, rc=" << status << ")";
2068 Error(str);
2069 GoToRunWriteErrorState();
2070 delete groupFile;
2071 return;
2072 }
2073 }
2074
2075 filesToGroup.clear();
2076 delete groupFile;
2077}
2078#endif //HAVE_FITS
2079
2080// --------------------------------------------------------------------------
2081//
2082//! Implements the StopRun transition.
2083//! Attempts to close the run file.
2084//! @returns
2085//! kSM_WaitingRun if success, kSM_FatalError otherwise
2086int DataLogger::StopRunLogging()
2087{
2088
2089 if (fDebugIsOn)
2090 {
2091 Debug("Stopping Run Logging...");
2092 }
2093
2094 if (fNumSubAndFitsIsOn)
2095 fNumSubAndFits->Update();
2096
2097 while (fRunNumber.size() > 0)
2098 {
2099 RemoveOldestRunNumber();
2100 }
2101 return kSM_WaitingRun;
2102}
2103// --------------------------------------------------------------------------
2104//
2105//! Implements the Stop and Reset transitions.
2106//! Attempts to close any openned file.
2107//! @returns
2108//! kSM_Ready
2109int DataLogger::GoToReady()
2110{
2111 if (fDebugIsOn)
2112 {
2113 Debug("Going to the Ready state...");
2114 }
2115 if (GetCurrentState() == kSM_Logging || GetCurrentState() == kSM_WaitingRun)
2116 StopRunLogging();
2117
2118 //it may be that dim tries to write a dimInfo while we're closing files. Prevent that
2119 const string baseFileName = CompileFileNameWithPath(fFilePath, "", "");
2120
2121 if (fNightlyReportFile.is_open())
2122 {
2123 fNightlyReportFile.close();
2124 Info("Closed: "+baseFileName+".rep");
2125 }
2126#ifdef HAVE_FITS
2127 for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++)
2128 for (map<string, SubscriptionType>::iterator j = i->second.begin(); j != i->second.end(); j++)
2129 {
2130 if (j->second.nightlyFile.IsOpen())
2131 j->second.nightlyFile.Close();
2132 }
2133#endif
2134 if (GetCurrentState() == kSM_Logging ||
2135 GetCurrentState() == kSM_WaitingRun ||
2136 GetCurrentState() == kSM_NightlyOpen)
2137 {
2138 NotifyOpenedFile("", 0, fOpenedNightlyFiles);
2139 if (fNumSubAndFitsIsOn)
2140 fNumSubAndFits->Update();
2141 }
2142#ifdef HAVE_FITS
2143 CreateFitsGrouping(fOpenedNightlyFits);
2144#endif
2145 return kSM_Ready;
2146}
2147
2148// --------------------------------------------------------------------------
2149//
2150//! Implements the transition towards kSM_WaitingRun
2151//! If current state is kSM_Ready, then tries to go to nightlyOpen state first.
2152//! @returns
2153//! kSM_WaitingRun or kSM_BadFolder
2154int DataLogger::NightlyToWaitRun()
2155{
2156 int cState = GetCurrentState();
2157
2158 if (cState == kSM_Ready)
2159 cState = Start();
2160
2161 if (cState != kSM_NightlyOpen)
2162 return GetCurrentState();
2163
2164 if (fDebugIsOn)
2165 {
2166 Debug("Going to Wait Run Number state...");
2167 }
2168 return kSM_WaitingRun;
2169}
2170// --------------------------------------------------------------------------
2171//
2172//! Implements the transition from wait for run number to nightly open
2173//! Does nothing really.
2174//! @returns
2175//! kSM_WaitingRun
2176int DataLogger::BackToNightlyOpen()
2177{
2178 if (GetCurrentState()==kSM_Logging)
2179 StopRunLogging();
2180
2181 if (fDebugIsOn)
2182 {
2183 Debug("Going to NightlyOpen state...");
2184 }
2185 return kSM_NightlyOpen;
2186}
2187// --------------------------------------------------------------------------
2188//
2189//! Setup Logger's configuration from a Configuration object
2190//! @param conf the configuration object that should be used
2191//!
2192int DataLogger::EvalOptions(Configuration& conf)
2193{
2194 fDebugIsOn = conf.Get<bool>("debug");
2195 fFilesStats.SetDebugMode(fDebugIsOn);
2196
2197 //Set the block or allow list
2198 fBlackList.clear();
2199 fWhiteList.clear();
2200
2201 //Adding entries that should ALWAYS be ignored
2202 fBlackList.insert("DATA_LOGGER/MESSAGE");
2203 fBlackList.insert("DATA_LOGGER/SUBSCRIPTIONS");
2204 fBlackList.insert("/SERVICE_LIST");
2205 fBlackList.insert("DIS_DNS/");
2206
2207 //set the black list, white list and the goruping
2208 const vector<string> vec1 = conf.Vec<string>("block");
2209 const vector<string> vec2 = conf.Vec<string>("allow");
2210 const vector<string> vec3 = conf.Vec<string>("group");
2211
2212 fBlackList.insert(vec1.begin(), vec1.end());
2213 fWhiteList.insert(vec2.begin(), vec2.end());
2214 fGrouping.insert( vec3.begin(), vec3.end());
2215
2216 //set the old run numbers timeout delay
2217 if (conf.Has("run-timeout"))
2218 {
2219 const uint32_t timeout = conf.Get<uint32_t>("run-timeout");
2220 if (timeout == 0)
2221 {
2222 Error("Time out delay for old run numbers must not be 0.");
2223 return 1;
2224 }
2225 fRunNumberTimeout = timeout;
2226 }
2227
2228 //configure the run files directory
2229 if (conf.Has("destination-folder"))
2230 {
2231 const string folder = conf.Get<string>("destination-folder");
2232 if (!fFilesStats.SetCurrentFolder(folder))
2233 return 2;
2234
2235 fFilePath = folder;
2236 fFullNightlyLogFileName = CompileFileNameWithPath(fFilePath, "", "log");
2237 if (!OpenTextFile(fNightlyLogFile, fFullNightlyLogFileName))
2238 return 3;
2239
2240 fNightlyLogFile << endl;
2241 NotifyOpenedFile(fFullNightlyLogFileName, 1, fOpenedNightlyFiles);
2242 for (vector<string>::iterator it=backLogBuffer.begin();it!=backLogBuffer.end();it++)
2243 fNightlyLogFile << *it;
2244 }
2245
2246 shouldBackLog = false;
2247 backLogBuffer.clear();
2248
2249 //configure the interval between statistics updates
2250 if (conf.Has("stats-interval"))
2251 fFilesStats.SetUpdateInterval(conf.Get<int16_t>("stats-interval"));
2252
2253 //configure if the filenames service is on or off
2254 fOpenedFilesIsOn = !conf.Get<bool>("no-filename-service");
2255
2256 //configure if the number of subscriptions and fits files is on or off.
2257 fNumSubAndFitsIsOn = !conf.Get<bool>("no-numsubs-service");
2258 //should we open the daily files at startup ?
2259 if (conf.Has("start-daily-files"))
2260 if (conf.Get<bool>("start-daily-files"))
2261 {
2262 fShouldAutoStart = true;
2263 }
2264 if (conf.Has("service-list-interval"))
2265 fCurrentSubscriptionUpdateRate = conf.Get<int32_t>("service-list-interval");
2266 return -1;
2267}
2268
2269
2270#include "Main.h"
2271
2272// --------------------------------------------------------------------------
2273template<class T>
2274int RunShell(Configuration &conf)
2275{
2276 return Main::execute<T, DataLogger>(conf);//, true);
2277}
2278
2279/*
2280 Extract usage clause(s) [if any] for SYNOPSIS.
2281 Translators: "Usage" and "or" here are patterns (regular expressions) which
2282 are used to match the usage synopsis in program output. An example from cp
2283 (GNU coreutils) which contains both strings:
2284 Usage: cp [OPTION]... [-T] SOURCE DEST
2285 or: cp [OPTION]... SOURCE... DIRECTORY
2286 or: cp [OPTION]... -t DIRECTORY SOURCE...
2287 */
2288void PrintUsage()
2289{
2290 cout << "\n"
2291 "The data logger connects to all available Dim services and "
2292 "writes them to ascii and fits files.\n"
2293 "\n"
2294 "The default is that the program is started without user interaction. "
2295 "All actions are supposed to arrive as DimCommands. Using the -c "
2296 "option, a local shell can be initialized. With h or help a short "
2297 "help message about the usage can be brought to the screen.\n"
2298 "\n"
2299 "Usage: datalogger [-c type] [OPTIONS]\n"
2300 " or: datalogger [OPTIONS]\n";
2301 cout << endl;
2302
2303}
2304// --------------------------------------------------------------------------
2305void PrintHelp()
2306{
2307 /* Additional help text which is printed after the configuration
2308 options goes here */
2309 cout <<
2310 "\n"
2311 "If the allow list has any element, only the servers and/or services "
2312 "specified in the list will be used for subscription. The black list "
2313 "will disable service subscription and has higher priority than the "
2314 "allow list. If the allow list is not present by default all services "
2315 "will be subscribed."
2316 "\n"
2317 "For example, block=DIS_DNS/ will skip all the services offered by "
2318 "the DIS_DNS server, while block=/SERVICE_LIST will skip all the "
2319 "SERVICE_LIST services offered by any server and DIS_DNS/SERVICE_LIST "
2320 "will skip DIS_DNS/SERVICE_LIST.\n"
2321 << endl;
2322
2323 Main::PrintHelp<DataLogger>();
2324}
2325
2326// --------------------------------------------------------------------------
2327void SetupConfiguration(Configuration &conf)
2328{
2329 po::options_description configs("DataLogger options");
2330 configs.add_options()
2331 ("block,b", vars<string>(), "Black-list to block services")
2332 ("allow,a", vars<string>(), "White-list to only allowe certain services")
2333 ("debug,d", po_bool(), "Debug mode. Print clear text of received service reports.")
2334 ("group,g", vars<string>(), "Grouping of services into a single run-Fits")
2335 ("run-timeout", var<uint32_t>(), "Time out delay for old run numbers in milliseconds.")
2336 ("destination-folder", var<string>(), "Base path for the nightly and run files")
2337 ("stats-interval", var<int16_t>(), "Interval in milliseconds for write statistics update")
2338 ("no-filename-service", po_bool(), "Disable update of filename service")
2339 ("no-numsubs-service", po_bool(), "Disable update of number-of-subscriptions service")
2340 ("start-daily-files", po_bool(), "Starts the logger in DailyFileOpen instead of Ready")
2341 ("service-list-interval", var<int32_t>(), "Interval between two updates of the service SUBSCRIPTIONS")
2342 ;
2343
2344 conf.AddOptions(configs);
2345}
2346// --------------------------------------------------------------------------
2347int main(int argc, const char* argv[])
2348{
2349 Configuration conf(argv[0]);
2350 conf.SetPrintUsage(PrintUsage);
2351 Main::SetupConfiguration(conf);
2352 SetupConfiguration(conf);
2353
2354 if (!conf.DoParse(argc, argv, PrintHelp))
2355 return 127;
2356
2357 {
2358 // No console access at all
2359 if (!conf.Has("console"))
2360 return RunShell<LocalStream>(conf);
2361
2362 // Console access w/ and w/o Dim
2363 if (conf.Get<int>("console")==0)
2364 return RunShell<LocalShell>(conf);
2365 else
2366 return RunShell<LocalConsole>(conf);
2367 }
2368
2369
2370 return 0;
2371}
Note: See TracBrowser for help on using the repository browser.