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