//**************************************************************** /** @class DataLogger @brief Logs all message and infos between the services This is the main logging class facility. It derives from StateMachineDim and DimInfoHandler. the first parent is here to enforce a state machine behaviour, while the second one is meant to make the dataLogger receive dim services to which it subscribed from. The possible states and transitions of the machine are: \dot digraph datalogger { node [shape=record, fontname=Helvetica, fontsize=10]; e [label="Error" color="red"]; r [label="Ready"] d [label="DailyOpen"] w [label="WaitingRun"] l [label="Logging"] b [label="BadDailyconfig" color="red"] c [label="BadRunConfig" color="red"] e -> r r -> e r -> d r -> b d -> w d -> r w -> r l -> r l -> w b -> d w -> c w -> l b -> r c -> r c -> l } \enddot @todo - Retrieve also the messages, not only the infos */ //**************************************************************** #include "Event.h" #include "Time.h" #include "StateMachineDim.h" #include "WindowLog.h" #include "Configuration.h" #include "ServiceList.h" #include "Converter.h" #include "MessageImp.h" #include "LocalControl.h" //#define HAS_FITS #include #include #ifdef HAS_FITS #include #endif class DataLogger : public StateMachineDim, DimInfoHandler { public: /// The list of existing states specific to the DataLogger enum { kSM_DailyOpen = 20, ///< Daily file openned and writing kSM_WaitingRun = 30, ///< waiting for the run number to open the run file kSM_Logging = 40, ///< both files openned and writing kSM_BadDailyConfig = 0x101, ///< the folder specified for daily logging does not exist or has bad permissions kSM_BadRunConfig = 0x102, ///< the folder specified for the run logging does not exist or has wrong permissions or no run number } localstates_t; DataLogger(std::ostream &out); ~DataLogger(); private: //Define all the data structure specific to the DataLogger here /// ofstream for the dailyLogfile std::ofstream fDailyLogFile; /// ofstream for the run-specific Log file std::ofstream fRunLogFile; /// ofstream for the daily report file std::ofstream fDailyReportFile; /// ofstream for the run-specific report file std::ofstream fRunReportFile; /// base path of the dailyfile std::string fDailyFileName; ///base path of the run file std::string fRunFileName; ///run number (-1 means no run number specified) int fRunNumber; ///Current year short fYear; ///Current Month short fMonth; ///Current Day short fDay; ///Current Hour short fHour; ///Current Minute short fMin; ///Current Second short fSec; ///Current Milliseconds int fMs; ///Current Service Quality int fQuality; ///Modified Julian Date double fMjD; ///Define all the static names static const char* fConfigDay; static const char* fConfigRun; static const char* fConfigRunNumber; static const char* fConfigLog; static const char* fTransStart; static const char* fTransStop; static const char* fTransStartRun; static const char* fTransStopRun; static const char* fTransReset; static const char* fTransWait; static const char* fRunNumberInfo; ///< This is the name of the dimInfo received to specify the run number. It must be updated once the final name will be defined ///Inherited from state machine impl int Execute(); ///Inherited from state machine impl int Transition(const Event& evt); ///Inherited from state machine impl int Configure(const Event& evt); //overloading of DIM's infoHandler function void infoHandler(); ///for obtaining the name of the existing services ServiceList fServiceList; ///A std pair to store both the DimInfo name and the actual DimInfo pointer // typedef std::pair subscriptionType; ///All the services to which we've subscribed to. Sorted by server name // std::map > fServiceSubscriptions; ///A std pair to store both the DimInfo pointer and the corresponding outputted fits file struct SubscriptionType { #ifdef HAS_FITS ///daily FITS output file AstroRootIo dailyFile; ///run-specific FITS output file AstroRootIo runFile; #endif ///the actual dimInfo pointer DimStampedInfo* dimInfo; ///the number of existing handlers to this structure. ///This is required otherwise I MUST handle the deleting of dimInfo outside from the destructor int* numCopies; void operator = (const SubscriptionType& other) { #ifdef HAS_FITS dailyFile = other.dailyFile; runFile = other.runFile; #endif dimInfo = other.dimInfo; numCopies = other.numCopies; (*numCopies)++; } SubscriptionType(const SubscriptionType& other) { #ifdef HAS_FITS dailyFile = other.dailyFile; runFile = other.runFile; #endif dimInfo = other.dimInfo; numCopies = other.numCopies; (*numCopies)++; } SubscriptionType(DimStampedInfo* info) { dimInfo = info; numCopies = new int(1); } SubscriptionType() { dimInfo = NULL; numCopies = new int(1); } ~SubscriptionType() { if (numCopies) (*numCopies)--; if (numCopies) if (*numCopies < 1) { #ifdef HAS_FITS if (dailyFile.IsOpen()) dailyFile.Close(); if (runFile.IsOpen()) runFile.Close(); #endif if (dimInfo) delete dimInfo; if (numCopies) delete numCopies; dimInfo = NULL; numCopies = NULL; } } }; typedef std::map> SubscriptionsListType; ///All the services to which we have subscribed to, sorted by server name. SubscriptionsListType fServiceSubscriptions; ///Reporting method for the services info received void ReportPlease(DimInfo* I, SubscriptionType& sub); ///Configuration of the daily file path int ConfigureDailyFileName(const Event& evt); ///Configuration fo the file name int ConfigureRunFileName(const Event& evt); ///DEPREC - configuration of the run number int ConfigureRunNumber(const Event& evt); ///logging method for the messages int LogMessagePlease(const Event& evt); ///checks whether or not the current info being treated is a run number void CheckForRunNumber(DimInfo* I); /// start transition int StartPlease(); ///from waiting to logging transition int StartRunPlease(); /// from logging to waiting transition int StopRunPlease(); ///stop and reset transition int GoToReadyPlease(); ///from dailyOpen to waiting transition int DailyToWaitRunPlease(); #ifdef HAS_FITS ///Open fits files void OpenFITSFilesPlease(SubscriptionType& sub); ///Write data to FITS files void WriteToFITS(SubscriptionType& sub); ///Allocate the buffers required for fits void AllocateFITSBuffers(SubscriptionType& sub); #endif public: ///checks with fServiceList whether or not the services got updated void CheckForServicesUpdate(); }; //DataLogger //static members initialization //since I do not check the transition/config names any longer, indeed maybe these could be hard-coded... but who knows what will happen in the future ? const char* DataLogger::fConfigDay = "CONFIG_DAY"; const char* DataLogger::fConfigRun = "CONFIG_RUN"; const char* DataLogger::fConfigRunNumber = "CONFIG_RUN_NUMBER"; const char* DataLogger::fConfigLog = "LOG"; const char* DataLogger::fTransStart = "START"; const char* DataLogger::fTransStop = "STOP"; const char* DataLogger::fTransStartRun = "START_RUN"; const char* DataLogger::fTransStopRun = "STOP_RUN"; const char* DataLogger::fTransReset = "RESET"; const char* DataLogger::fTransWait = "WAIT_RUN_NUMBER"; const char* DataLogger::fRunNumberInfo = "RUN_NUMBER"; // -------------------------------------------------------------------------- // //! Default constructor. The name of the machine is given DATA_LOGGER //! and the state is set to kSM_Ready at the end of the function. // //!Setup the allows states, configs and transitions for the data logger // DataLogger::DataLogger(std::ostream &out) : StateMachineDim(out, "DATA_LOGGER") { //initialize member data fDailyFileName = "/home/lyard/log"; fRunFileName = "/home/lyard/log"; fRunNumber = 12345; //Give a name to this machine's specific states AddStateName(kSM_DailyOpen, "DailyFileOpen"); AddStateName(kSM_WaitingRun, "WaitForRun"); AddStateName(kSM_Logging, "Logging"); AddStateName(kSM_BadDailyConfig, "ErrDailyFolder"); AddStateName(kSM_BadRunConfig, "ErrRunFolder"); /*Add the possible transitions for this machine*/ //start the daily logging. daily file location must be specified already AddTransition(kSM_DailyOpen, fTransStart, kSM_Ready, kSM_BadDailyConfig). AssignFunction(boost::bind(&DataLogger::StartPlease, this)); //stop the data logging AddTransition(kSM_Ready, fTransStop, kSM_DailyOpen, kSM_WaitingRun, kSM_Logging). AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this)); //start the run logging. run file location must be specified already. AddTransition(kSM_Logging, fTransStartRun, kSM_WaitingRun, kSM_BadRunConfig). AssignFunction(boost::bind(&DataLogger::StartRunPlease, this)); AddTransition(kSM_WaitingRun, fTransStopRun, kSM_Logging). AssignFunction(boost::bind(&DataLogger::StopRunPlease, this)); //transition to exit error states. dunno if required or not, would close the daily file if already openned. AddTransition(kSM_Ready, fTransReset, kSM_Error, kSM_BadDailyConfig, kSM_BadRunConfig, kSM_Error). AssignFunction(boost::bind(&DataLogger::GoToReadyPlease, this)); AddTransition(kSM_WaitingRun, fTransWait, kSM_DailyOpen). AssignFunction(boost::bind(&DataLogger::DailyToWaitRunPlease, this)); /*Add the possible configurations for this machine*/ //configure the daily file location. cannot be done before the file is actually opened AddConfiguration(fConfigDay, "C", kSM_Ready, kSM_BadDailyConfig). AssignFunction(boost::bind(&DataLogger::ConfigureDailyFileName, this, _1)); //configure the run file location. cannot be done before the file is actually opened, and not in a dailly related error. AddConfiguration(fConfigRun, "C", kSM_Ready, kSM_BadDailyConfig, kSM_DailyOpen, kSM_WaitingRun, kSM_BadRunConfig). AssignFunction(boost::bind(&DataLogger::ConfigureRunFileName, this, _1)); //Provide a logging command //I get the feeling that I should be going through the EventImp //instead of DimCommand directly, mainly because the commandHandler //is already done in StateMachineImp.cc //Thus I'll simply add a configuration, which I will treat as the logging command AddConfiguration(fConfigLog, "C", kSM_DailyOpen, kSM_Logging, kSM_WaitingRun, kSM_BadRunConfig). AssignFunction(boost::bind(&DataLogger::LogMessagePlease, this, _1)); fServiceList.SetHandler(this); CheckForServicesUpdate(); } // -------------------------------------------------------------------------- // //! Checks for changes in the existing services. //! Any new service will be added to the service list, while the ones which disappeared are removed. //! @todo //! add the configuration (using the conf class ?) // void DataLogger::CheckForServicesUpdate() { //get the current server list const std::vector serverList = fServiceList.GetServerList(); //first let's remove the servers that may have disapeared //can't treat the erase on maps the same way as for vectors. Do it the safe way instead std::vector toBeDeleted; for (SubscriptionsListType::iterator cListe = fServiceSubscriptions.begin(); cListe != fServiceSubscriptions.end(); cListe++) { std::vector::const_iterator givenServers; for (givenServers=serverList.begin(); givenServers!= serverList.end(); givenServers++) if (cListe->first == *givenServers) break; if (givenServers == serverList.end())//server vanished. Remove it toBeDeleted.push_back(cListe->first); } for (std::vector::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++) fServiceSubscriptions.erase(*it); //now crawl through the list of servers, and see if there was some updates for (std::vector::const_iterator i=serverList.begin(); i!=serverList.end();i++) { //skip the two obvious excluded services if ((i->find("DIS_DNS") != std::string::npos) || (i->find("DATA_LOGGER") != std::string::npos)) continue; //find the current server in our subscription list SubscriptionsListType::iterator cSubs = fServiceSubscriptions.find(*i); //get the service list of the current server std::vector cServicesList = fServiceList.GetServiceList(*i); if (cSubs != fServiceSubscriptions.end())//if the current server already is in our subscriptions { //then check and update our list of subscriptions //first, remove the services that may have dissapeared. std::map::iterator serverSubs; std::vector::const_iterator givenSubs; toBeDeleted.clear(); for (serverSubs=cSubs->second.begin(); serverSubs != cSubs->second.end(); serverSubs++) { for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++) if (serverSubs->first == *givenSubs) break; if (givenSubs == cServicesList.end()) { toBeDeleted.push_back(serverSubs->first); } } for (std::vector::const_iterator it = toBeDeleted.begin(); it != toBeDeleted.end(); it++) cSubs->second.erase(*it); //now check for new services for (givenSubs = cServicesList.begin(); givenSubs != cServicesList.end(); givenSubs++) { if (*givenSubs == "SERVICE_LIST") continue; if (cSubs->second.find(*givenSubs) == cSubs->second.end()) {//service not found. Add it cSubs->second[*givenSubs].dimInfo = new DimStampedInfo(((*i) + "/" + *givenSubs).c_str(), const_cast(""), this); } } } else //server not found in our list. Create its entry { fServiceSubscriptions[*i] = std::map(); std::map& liste = fServiceSubscriptions[*i]; for (std::vector::const_iterator j = cServicesList.begin(); j!= cServicesList.end(); j++) { if (*j == "SERVICE_LIST") continue; liste[*j].dimInfo = new DimStampedInfo(((*i) + "/" + (*j)).c_str(), const_cast(""), this); } } } } // -------------------------------------------------------------------------- // //! Destructor // DataLogger::~DataLogger() { //close the files if (fDailyLogFile.is_open()) fDailyLogFile.close(); if (fDailyReportFile.is_open()) fDailyReportFile.close(); if (fRunLogFile.is_open()) fRunLogFile.close(); if (fRunReportFile.is_open()) fRunReportFile.close(); //release the services subscriptions fServiceSubscriptions.clear(); } // -------------------------------------------------------------------------- // //! Execute //! Shouldn't be run as we use callbacks instead // int DataLogger::Execute() { //due to the callback mecanism, this function should never be called return kSM_FatalError; switch (GetCurrentState()) { case kSM_Error: case kSM_Ready: case kSM_DailyOpen: case kSM_WaitingRun: case kSM_Logging: case kSM_BadDailyConfig: case kSM_BadRunConfig: return GetCurrentState(); } //this line below should never be hit. It here mainly to remove warnings at compilation return kSM_FatalError; } // -------------------------------------------------------------------------- // //! Shouldn't be run as we use callbacks instead // int DataLogger::Transition(const Event& evt) { //due to the callback mecanism, this function should never be called return kSM_FatalError; switch (evt.GetTargetState()) { case kSM_Ready: /*here we must figure out whether the STOP or RESET command was sent*/ /*close opened files and go back to ready state*/ switch (GetCurrentState()) { case kSM_BadDailyConfig: case kSM_BadRunConfig: case kSM_Error: return GoToReadyPlease(); case kSM_Logging: case kSM_WaitingRun: case kSM_DailyOpen: return GoToReadyPlease(); } break; case kSM_DailyOpen: /*Attempt to open the daily file */ switch (GetCurrentState()) { case kSM_Ready: case kSM_BadDailyConfig: return StartPlease(); } break; case kSM_WaitingRun: /*either close the run file, or just go to the waitingrun state (if coming from daily open*/ switch (GetCurrentState()) { case kSM_DailyOpen: return kSM_WaitingRun; case kSM_Logging: return StopRunPlease(); } break; case kSM_Logging: /*Attempt to open run file */ switch (GetCurrentState()) { case kSM_WaitingRun: case kSM_BadRunConfig: return StartRunPlease(); } break; } //Getting here means that an invalid transition has been asked. //TODO Log an error message //and return the fatal error state return kSM_FatalError; } // -------------------------------------------------------------------------- // //! Shouldn't be run as we use callbacks instead // int DataLogger::Configure(const Event& evt) { //due to the callback mecanism, this function should never be called return kSM_FatalError; switch (evt.GetTargetState()) { case kSM_Ready: case kSM_BadDailyConfig: return ConfigureDailyFileName(evt); break; case kSM_WaitingRun: case kSM_BadRunConfig: return ConfigureRunFileName(evt); break; case kSM_Logging: case kSM_DailyOpen: //TODO check that this is indeed correct return 0;//LogMessagePlease(evt); break; } //Getting here means that an invalid configuration has been asked. //TODO Log an error message //and return the fatal error state return kSM_FatalError; } // -------------------------------------------------------------------------- // //! Inherited from DimInfo. Handles all the Infos to which we subscribed, and log them // void DataLogger::infoHandler() { DimInfo* I = getInfo(); if (I==NULL) { CheckForServicesUpdate(); return; } //check if the service pointer corresponds to something that we subscribed to //this is a fix for a bug that provides bad Infos when a server starts bool found = false; SubscriptionsListType::iterator x; std::map::iterator y; for (x=fServiceSubscriptions.begin(); x != fServiceSubscriptions.end(); x++) {//instead of extracting the server and service names, I crawl through my records. dunno what's faster, but both should work for (y=x->second.begin(); y!=x->second.end();y++) if (y->second.dimInfo == I) { found = true; break; } if (found) break; } if (!found) return; if (I->getSize() <= 0) return; //check that the message has been updated by something, i.e. must be different from its initial value if (I->getTimestamp() == 0) return; CheckForRunNumber(I); ReportPlease(I, y->second); } // -------------------------------------------------------------------------- // //! Checks whether or not the current info is a run number. //! If so, then remember it. A run number is required to open the run-log file //! @param I //! the current DimInfo // void DataLogger::CheckForRunNumber(DimInfo* I) { return; if (strstr(I->getName(), fRunNumberInfo) != NULL) {//assumes that the run number is an integer //TODO check the format here fRunNumber = I->getInt(); } } // -------------------------------------------------------------------------- // //! write infos to log files. //! @param I //! The current DimInfo // void DataLogger::ReportPlease(DimInfo* I, SubscriptionType& sub) { //should we log or report this info ? (i.e. is it a message ?) bool isItaReport = ((strstr(I->getName(), "Message") == NULL) && (strstr(I->getName(), "MESSAGE") == NULL)); // std::ofstream & dailyFile = fDailyReportFile;//isItaReport? fDailyReportFile : fDailyLogFile; // std::ofstream & runFile = fRunReportFile;//isItaReport? fRunReportFile : fRunLogFile; //TODO add service exclusion if (!fDailyReportFile.is_open()) return; //construct the header std::stringstream header; Time cTime((time_t)(I->getTimestamp()), I->getTimestampMillisecs()); //Buffer them for FITS write //TODO this has been replaced by MjD. So I guess that these member variables can go. fYear = cTime.Y(); fMonth = cTime.M(); fDay = cTime.D(); fHour = cTime.h(); fMin = cTime.m(); fSec = cTime.s(); fMs = cTime.ms(); fQuality = I->getQuality(); fMjD = cTime.Mjd(); if (isItaReport) { //write text header header << I->getName() << " " << fQuality << " "; header << fYear << " " << fMonth << " " << fDay << " "; header << fHour << " " << fMin << " " << fSec << " "; header << fMs << " " << I->getTimestamp() << " "; const Converter conv(Out(), I->getFormat()); if (!conv) { Error("Couldn't properly parse the format... ignored."); return; } std::string text; try { text = conv.GetString(I->getData(), I->getSize()); } catch (const std::runtime_error &e) { Out() << kRed << e.what() << endl; Error("Couldn't properly parse the data... ignored."); return; } if (text.empty()) return; //replace bizarre characters by white space replace(text.begin(), text.end(), '\n', '\\'); replace_if(text.begin(), text.end(), std::ptr_fun(&std::iscntrl), ' '); if (fDailyReportFile.is_open()) fDailyReportFile << header.str(); if (fRunReportFile.is_open()) fRunReportFile << header.str(); if (fDailyReportFile.is_open()) fDailyReportFile << text << std::endl; if (fRunReportFile.is_open()) fRunReportFile << text << std::endl; } else { std::string n = I->getName(); std::stringstream msg; msg << n.substr(0, n.find_first_of('/')) << ": " << I->getString(); MessageImp dailyMess(fDailyLogFile); dailyMess.Write(cTime, msg.str().c_str(), fQuality); if (fRunLogFile.is_open()) { MessageImp runMess(fRunLogFile); runMess.Write(cTime, msg.str().c_str(), fQuality); } } #ifdef HAS_FITS if (!sub.dailyFile.IsOpen() || !sub.runFile.IsOpen()) OpenFITSFilesPlease(sub); WriteToFITS(sub); #endif } // -------------------------------------------------------------------------- // //! write messages to logs. //! @param evt //! the current event to log //! @returns //! the new state. Currently, always the current state //! //! @deprecated //! I guess that this function should not be any longer // int DataLogger::LogMessagePlease(const Event& evt) { if (!fDailyLogFile.is_open()) return GetCurrentState(); std::stringstream header; const Time& cTime = evt.GetTime(); header << evt.GetName() << " " << cTime.Y() << " " << cTime.M() << " " << cTime.D() << " "; header << cTime.h() << " " << cTime.m() << " " << cTime.s() << " "; header << cTime.ms() << " "; // std::string text = ToString(evt.GetFormat().c_str(), evt.GetData(), evt.GetSize()); const Converter conv(Out(), evt.GetFormat()); if (!conv) { Error("Couldn't properly parse the format... ignored."); return GetCurrentState(); } std::string text; try { text = conv.GetString(evt.GetData(), evt.GetSize()); } catch (const std::runtime_error &e) { Out() << kRed << e.what() << endl; Error("Couldn't properly parse the data... ignored."); return GetCurrentState(); } if (text.empty()) return GetCurrentState(); //replace bizarre characters by white space replace(text.begin(), text.end(), '\n', '\\'); replace_if(text.begin(), text.end(), std::ptr_fun(&std::iscntrl), ' '); if (fDailyLogFile.is_open()) fDailyLogFile << header; if (fRunLogFile.is_open()) fRunLogFile << header; if (fDailyLogFile.is_open()) fDailyLogFile << text; if (fRunLogFile.is_open()) fRunLogFile << text; return GetCurrentState(); } // -------------------------------------------------------------------------- // //! Sets the path to use for the daily log file. //! @param evt //! the event transporting the path //! @returns //! currently only the current state. // int DataLogger::ConfigureDailyFileName(const Event& evt) { if (evt.GetText() != NULL) fDailyFileName = std::string(evt.GetText()); else Error("Empty daily folder"); return GetCurrentState(); } // -------------------------------------------------------------------------- // //! Sets the path to use for the run log file. //! @param evt //! the event transporting the path //! @returns //! currently only the current state int DataLogger::ConfigureRunFileName(const Event& evt) { if (evt.GetText() != NULL) fRunFileName = std::string(evt.GetText()); else Error("Empty daily folder"); return GetCurrentState(); } // -------------------------------------------------------------------------- // //! Sets the run number. //! @param evt //! the event transporting the run number //! @returns //! currently only the current state int DataLogger::ConfigureRunNumber(const Event& evt) { fRunNumber = evt.GetInt(); return GetCurrentState(); } // -------------------------------------------------------------------------- // //! Implements the Start transition. //! Concatenates the given path for the daily file and the filename itself (based on the day), //! and tries to open it. //! @returns //! kSM_DailyOpen if success, kSM_BadDailyConfig if failure int DataLogger::StartPlease() { //TODO concatenate the dailyFileName and the formatted date and extension to obtain the full file name Time time;//(Time::utc); std::stringstream sTime; sTime << time.Y() << "_" << time.M() << "_" << time.D(); std::string fullName = fDailyFileName + '/' + sTime.str() + ".log"; fDailyLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be "app" instead of "ate" ?? fullName = fDailyFileName + '/' + sTime.str() + ".rep"; fDailyReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); if (!fDailyLogFile.is_open() || !fDailyReportFile.is_open()) { //TODO send an error message return kSM_BadDailyConfig; } return kSM_DailyOpen; } #ifdef HAS_FITS // -------------------------------------------------------------------------- // //! open if required a the FITS files corresponding to a given subscription //! @param sub //! the current DimInfo subscription being examined void DataLogger::OpenFITSFilesPlease(SubscriptionType& sub) { std::string serviceName(sub.dimInfo->getName()); for (unsigned int i=0;igetFormat(); const int size = sub.dimInfo->getSize(); //Init the time columns of the file sub.dailyFile.InitCol("Date", "double", &fMjD); sub.runFile.InitCol("Date", "double", &fMjD); // sub.dailyFile.InitCol("Year", "short", &fYear); // sub.dailyFile.InitCol("Month", "short", &fMonth); // sub.dailyFile.InitCol("Day", "short", &fDay); // sub.dailyFile.InitCol("Hour", "short", &fHour); // sub.dailyFile.InitCol("Minute", "short", &fMin); // sub.dailyFile.InitCol("Second", "short", &fSec); // sub.dailyFile.InitCol("MilliSec", "int", &fMs); sub.dailyFile.InitCol("QoS", "int", &fQuality); // sub.runFile.InitCol("Year", "short", &fYear); // sub.runFile.InitCol("Month", "short", &fMonth); // sub.runFile.InitCol("Day", "short", &fDay); // sub.runFile.InitCol("Hour", "short", &fHour); // sub.runFile.InitCol("Minute", "short", &fMin); // sub.runFile.InitCol("Second", "short", &fSec); // sub.runFile.InitCol("MilliSec", "int", &fMs); sub.runFile.InitCol("QoS", "int", &fQuality); const Converter::FormatList flist = Converter::Compile(Out(), format); // Compilation failed if (fList.empty() || fList.back().first.second!=0) { Error("Compilation of format string failed."); return; } //we've got a nice structure describing the format of this service's messages. //Let's create the appropriate FITS columns for (unsigned int i=0;i(sub.dimInfo->getData()) + flist[i].second.second; colName << "Data" << i; dataQualifier << flist[i].second.first; switch (flist[i].first.first) { case 'c': dataQualifier << "S"; break; case 's': dataQualifier << "I"; break; case 'i': dataQualifier << "J"; break; case 'l': dataQualifier << "J"; //TODO triple check that in FITS, long = int break; case 'f': dataQualifier << "E"; break; case 'd': dataQualifier << "D"; break; case 'x': dataQualifier << "K"; break; case 'S': //for strings, the number of elements I get is wrong. Correct it dataQualifier.str(""); //clear dataQualifier << size-1 << "A"; break; default: Error("THIS SHOULD NEVER BE REACHED. dataLogger.cc ln 962."); }; sub.dailyFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer); sub.runFile.InitCol(colName.str().c_str(), dataQualifier.str().c_str(), dataPointer); } //TODO init the attributes } // -------------------------------------------------------------------------- // //! write a dimInfo data to its corresponding FITS files // void DataLogger::WriteToFITS(SubscriptionType& sub) { //dailyFile status (open or not) already checked if (sub.dailyFile.IsOpen()) sub.dailyFile.Write(); if (sub.runFile.IsOpen()) sub.runFile.Write(); } #endif //if has_fits // -------------------------------------------------------------------------- // //! Implements the StartRun transition. //! Concatenates the given path for the run file and the filename itself (based on the run number), //! and tries to open it. //! @returns //! kSM_Logging if success, kSM_BadRunConfig if failure. int DataLogger::StartRunPlease() { //attempt to open run file with current parameters if (fRunNumber == -1) return kSM_BadRunConfig; std::stringstream sRun; sRun << fRunNumber; std::string fullName = fRunFileName + '/' + sRun.str() + ".log"; fRunLogFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); //maybe should be app instead of ate fullName = fRunFileName + '/' + sRun.str() + ".rep"; fRunReportFile.open(fullName.c_str(), std::ios_base::out | std::ios_base::app); if (!fRunLogFile.is_open() || !fRunReportFile.is_open()) { //TODO send an error message return kSM_BadRunConfig; } return kSM_Logging; } // -------------------------------------------------------------------------- // //! Implements the StopRun transition. //! Attempts to close the run file. //! @returns //! kSM_WaitingRun if success, kSM_FatalError otherwise int DataLogger::StopRunPlease() { if (!fRunLogFile.is_open() || !fRunReportFile.is_open()) return kSM_FatalError; fRunLogFile.close(); fRunReportFile.close(); #ifdef HAS_FITS for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++) for (std::map::iterator j = i->second.begin(); j != i->second.end(); j++) { if (j->second.runFile.IsOpen()) j->second.runFile.Close(); } #endif return kSM_WaitingRun; } // -------------------------------------------------------------------------- // //! Implements the Stop and Reset transitions. //! Attempts to close any openned file. //! @returns //! kSM_Ready int DataLogger::GoToReadyPlease() { if (fDailyLogFile.is_open()) fDailyLogFile.close(); if (fDailyReportFile.is_open()) fDailyReportFile.close(); if (fRunLogFile.is_open()) fRunLogFile.close(); if (fRunReportFile.is_open()) fRunReportFile.close(); #ifdef HAS_FITS for (SubscriptionsListType::iterator i = fServiceSubscriptions.begin(); i != fServiceSubscriptions.end(); i++) for (std::map::iterator j = i->second.begin(); j != i->second.end(); j++) { if (j->second.dailyFile.IsOpen()) j->second.dailyFile.Close(); if (j->second.runFile.IsOpen()) j->second.runFile.Close(); } #endif return kSM_Ready; } // -------------------------------------------------------------------------- // //! Implements the transition towards kSM_WaitingRun //! Does nothing really. //! @returns //! kSM_WaitingRun int DataLogger::DailyToWaitRunPlease() { return kSM_WaitingRun; } // -------------------------------------------------------------------------- int RunDim(Configuration &conf) { WindowLog wout; //log.SetWindow(stdscr); if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"))) wout << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << std::endl; // Start io_service.Run to use the StateMachineImp::Run() loop // Start io_service.run to only use the commandHandler command detaching DataLogger logger(wout); logger.Run(true); return 0; } template int RunShell(Configuration &conf) { static T shell(conf.GetName().c_str(), conf.Get("console")!=1); WindowLog &win = shell.GetStreamIn(); WindowLog &wout = shell.GetStreamOut(); if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"))) win << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << std::endl; DataLogger logger(wout); shell.SetReceiver(logger); logger.SetReady(); shell.Run(); // Run the shell logger.SetNotReady(); return 0; } void SetupConfiguration(Configuration &conf) { const std::string n = conf.GetName()+".log"; po::options_description config("Program options"); config.add_options() ("dns", var("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)") ("log,l", var(n), "Write log-file") ("console,c", var(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)") ; conf.AddEnv("dns", "DIM_DNS_NODE"); conf.AddOptions(config); } int main(int argc, char* argv[]) { Configuration conf(argv[0]); SetupConfiguration(conf); po::variables_map vm; try { vm = conf.Parse(argc, argv); } #if BOOST_VERSION > 104000 catch (po::multiple_occurrences &e) { std::cout << "Error: " << e.what() << " of '" << e.get_option_name() << "' option." << std::endl; std::cout << std::endl; return -1; } #endif catch (std::exception &e) { std::cout << "Error: " << e.what() << std::endl; std::cout << std::endl; return -1; } if (conf.HasHelp() || conf.HasPrint()) return -1; // To allow overwriting of DIM_DNS_NODE set 0 to 1 setenv("DIM_DNS_NODE", conf.Get("dns").c_str(), 1); try { // No console access at all if (!conf.Has("console")) return RunDim(conf); // Cosole access w/ and w/o Dim if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } catch (std::exception& e) { cerr << "Exception: " << e.what() << endl; return -1; } return 0; }