#include "Dim.h" #include "Event.h" #include "Shell.h" #include "StateMachineDim.h" #include "Connection.h" #include "Configuration.h" #include "Console.h" #include "Converter.h" #include "DimServiceInfoList.h" #include "tools.h" #include "LocalControl.h" #include "HeadersFTM.h" #include "HeadersFAD.h" namespace ba = boost::asio; namespace bs = boost::system; namespace dummy = ba::placeholders; using namespace std; // ------------------------------------------------------------------------ #include "DimDescriptionService.h" // ------------------------------------------------------------------------ class StateMachineMCP : public StateMachineDim, public DimInfoHandler { /* int Wrap(boost::function f) { f(); return T::GetCurrentState(); } boost::function Wrapper(boost::function func) { return bind(&StateMachineMCP::Wrap, this, func); }*/ private: enum states_t { kStateDimNetworkNA = 1, kStateDisconnected, kStateConnecting, kStateConnected, kStateIdle, kStateReadyForDataTaking, kStateConfiguring1, kStateConfiguring2, kStateConfiguring3, kStateConfigured, }; DimServiceInfoList fNetwork; pair fStatusDim; pair fStatusFTM; pair fStatusFAD; pair fStatusLog; DimStampedInfo fDim; DimStampedInfo fFTM; DimStampedInfo fFAD; DimStampedInfo fLog; pair GetNewState(DimStampedInfo &info) const { const bool disconnected = info.getSize()==0; // Make sure getTimestamp is called _before_ getTimestampMillisecs const int tsec = info.getTimestamp(); const int tms = info.getTimestampMillisecs(); return make_pair(Time(tsec, tms*1000), disconnected ? -2 : info.getQuality()); } void infoHandler() { DimInfo *curr = getInfo(); // get current DimInfo address if (!curr) return; if (curr==&fFTM) { fStatusFTM = GetNewState(fFTM); return; } if (curr==&fFAD) { fStatusFAD = GetNewState(fFAD); return; } if (curr==&fLog) { fStatusLog = GetNewState(fLog); return; } if (curr==&fDim) { fStatusDim = GetNewState(fDim); fStatusDim.second = curr->getSize()==4 ? curr->getInt() : 0; return; } } bool CheckEventSize(size_t has, const char *name, size_t size) { if (has==size) return true; ostringstream msg; msg << name << " - Received event has " << has << " bytes, but expected " << size << "."; Fatal(msg); return false; } int SetVerbosity(const EventImp &) { /* if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1)) return T::kSM_FatalError; fFSC.SetVerbose(evt.GetBool()); */ return GetCurrentState(); } void PrintState(const pair &state, const char *server) { const State rc = fNetwork.GetState(server, state.second); Out() << state.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << server << ": "; Out() << rc.name << "[" << rc.index << "]"; Out() << kReset << " - " << kBlue << rc.comment << endl; } int Print() { Out() << fStatusDim.first.GetAsStr("%H:%M:%S.%f").substr(0, 12) << " - "; Out() << kBold << "DIM_DNS: "; if (fStatusDim.second==0) Out() << "Offline" << endl; else Out() << "V" << fStatusDim.second/100 << 'r' << fStatusDim.second%100 << endl; PrintState(fStatusFTM, "FTM_CONTROL"); PrintState(fStatusFAD, "FAD_CONTROL"); PrintState(fStatusLog, "DATA_LOGGER"); return GetCurrentState(); } int GetReady() { return GetCurrentState(); } int StopRun(const EventImp &) { if (fStatusFTM.second==FTM::kTakingData) Dim::SendCommand("FTM_CONTROL/STOP_RUN"); // FIXME: Do step 2 only when FTM is stopped if (fStatusFAD.second==FAD::kConnected) { Dim::SendCommand("FAD_CONTROL/ENABLE_TRIGGER_LINE", bool(false)); Dim::SendCommand("FAD_CONTROL/ENABLE_CONTINOUS_TRIGGER", bool(false)); } return GetCurrentState(); } int Reset(const EventImp &) { fRunType = ""; return kStateIdle; /* // FIMXE: Handle error states! if (fStatusLog.second>=20)//kSM_NightlyOpen Dim::SendCommand("DATA_LOGGER/STOP"); if (fStatusLog.second==0) Dim::SendCommand("DATA_LOGGER/WAIT_FOR_RUN_NUMBER"); if (fStatusFAD.second==FAD::kConnected) { Dim::SendCommand("FAD_CONTROL/ENABLE_TRIGGER_LINE", bool(false)); Dim::SendCommand("FAD_CONTROL/ENABLE_CONTINOUS_TRIGGER", bool(false)); } if (fStatusFTM.second==FTM::kTakingData) Dim::SendCommand("FTM_CONTROL/STOP"); return GetCurrentState(); */ } uint64_t fMaxTime; uint64_t fNumEvents; string fRunType; int StartRun(const EventImp &evt) { fMaxTime = evt.Get(); fNumEvents = evt.Get(8); fRunType = evt.Ptr(16); ostringstream str; str << "Starting configuration '" << fRunType << "' for new run"; if (fNumEvents>0 || fMaxTime>0) str << " ["; if (fNumEvents>0) str << fNumEvents << " events"; if (fNumEvents>0 && fMaxTime>0) str << " / "; if (fMaxTime>0) str << fMaxTime << "s"; if (fNumEvents>0 || fMaxTime>0) str << "]"; Message(str); return kStateConfiguring1; } void ConfigureFAD() { struct Value { uint64_t time; uint64_t nevts; char type[]; }; const size_t len = sizeof(Value)+fRunType.length()+1; char *buf = new char[len]; Value *val = reinterpret_cast(buf); val->time = fMaxTime; val->nevts = fNumEvents; strcpy(val->type, fRunType.c_str()); Dim::SendCommand("FAD_CONTROL/CONFIGURE", buf, len); delete buf; } int Execute() { // Dispatch (execute) at most one handler from the queue. In contrary // to run_one(), it doesn't wait until a handler is available // which can be dispatched, so poll_one() might return with 0 // handlers dispatched. The handlers are always dispatched/executed // synchronously, i.e. within the call to poll_one() //poll_one(); if (fStatusDim.second==0) return kStateDimNetworkNA; if (fStatusFTM.second >= FTM::kConnected && fStatusFAD.second >= FAD::kConnected && fStatusLog.second >= kSM_Ready) { if (GetCurrentState()==kStateConfiguring1) { if (fStatusLog.second!=30/*kSM_WaitForRun*/) Dim::SendCommand("DATA_LOGGER/WAIT_FOR_RUN_NUMBER"); Dim::SendCommand("FTM_CONTROL/CONFIGURE", fRunType); return kStateConfiguring2; } if (GetCurrentState()==kStateConfiguring2) { if ((fStatusFTM.second != FTM::kConfiguring2 && fStatusFTM.second != FTM::kConfigured) || fStatusLog.second != 30/*kSM_WaitForRun*/) return GetCurrentState(); Message("Starting FAD"); ConfigureFAD(); return kStateConfiguring3; } if (GetCurrentState()==kStateConfiguring3) { if (fStatusFTM.second != FTM::kConfigured || fStatusFAD.second != FAD::kConfigured) return GetCurrentState(); Message("Starting Trigger (FTM)"); Dim::SendCommand("FTM_CONTROL/START_RUN"); return kStateConfigured; } if (GetCurrentState()==kStateConfigured) return GetCurrentState(); return kStateIdle; } /* if (fStatusFTM.second >= FTM::kConnected && fStatusFAD.second >= FAD::kConnected && fStatusLog.second >= kSM_Ready) return kStateIdle; */ if (fStatusFTM.second >-2 && fStatusFAD.second >-2 && fStatusLog.second >-2) return kStateConnected; if (fStatusFTM.second >-2 || fStatusFAD.second >-2 || fStatusLog.second >-2) return kStateConnecting; return kStateDisconnected; } public: StateMachineMCP(ostream &out=cout) : StateMachineDim(out, "MCP"), fStatusDim(make_pair(Time(), -2)), fStatusFTM(make_pair(Time(), -2)), fStatusFAD(make_pair(Time(), -2)), fStatusLog(make_pair(Time(), -2)), fDim("DIS_DNS/VERSION_NUMBER", (void*)NULL, 0, this), fFTM("FTM_CONTROL/STATE", (void*)NULL, 0, this), fFAD("FAD_CONTROL/STATE", (void*)NULL, 0, this), fLog("DATA_LOGGER/STATE", (void*)NULL, 0, this) { // ba::io_service::work is a kind of keep_alive for the loop. // It prevents the io_service to go to stopped state, which // would prevent any consecutive calls to run() // or poll() to do nothing. reset() could also revoke to the // previous state but this might introduce some overhead of // deletion and creation of threads and more. // State names AddStateName(kStateDimNetworkNA, "DimNetworkNotAvailable", "."); AddStateName(kStateDisconnected, "Disconnected", "."); AddStateName(kStateConnecting, "Connecting", "."); AddStateName(kStateConnected, "Connected", "."); AddStateName(kStateIdle, "Idle", "."); AddStateName(kStateReadyForDataTaking, "ReadyForDataTaking", "."); AddStateName(kStateConfiguring1, "Configuring1", "."); AddStateName(kStateConfiguring2, "Configuring2", "."); AddStateName(kStateConfiguring3, "Configuring3", "."); AddStateName(kStateConfigured, "Configured", "."); AddEvent("START", "X:2;C")//, kStateIdle) (bind(&StateMachineMCP::StartRun, this, placeholders::_1)) (""); AddEvent("STOP") (bind(&StateMachineMCP::StopRun, this, placeholders::_1)) (""); AddEvent("RESET", kStateConfiguring1, kStateConfiguring2, kStateConfigured) (bind(&StateMachineMCP::Reset, this, placeholders::_1)) (""); // Verbosity commands AddEvent("SET_VERBOSE", "B:1") (bind(&StateMachineMCP::SetVerbosity, this, placeholders::_1)) ("set verbosity state" "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data"); AddEvent("PRINT") (bind(&StateMachineMCP::Print, this)) (""); } int EvalOptions(Configuration &) { //SetEndpoint(conf.Get("addr")); //fFSC.SetVerbose(!conf.Get("quiet")); return -1; } }; // ------------------------------------------------------------------------ #include "Main.h" /* void RunThread(StateMachineImp *io_service) { // This is necessary so that the StateMachien Thread can signal the // Readline to exit io_service->Run(); Readline::Stop(); } */ /* template int RunDim(Configuration &conf) { WindowLog wout; ReadlineColor::PrintBootMsg(wout, conf.GetName(), false); if (conf.Has("log")) if (!wout.OpenLogFile(conf.Get("log"))) wout << kRed << "ERROR - Couldn't open log-file " << conf.Get("log") << ": " << strerror(errno) << endl; // Start io_service.Run to use the StateMachineImp::Run() loop // Start io_service.run to only use the commandHandler command detaching StateMachineMCP io_service(wout); if (!io_service.EvalConfiguration(conf)) return -1; io_service.Run(); return 0; } */ template int RunShell(Configuration &conf) { return Main(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) << endl; StateMachineMCP io_service(wout); if (!io_service.EvalConfiguration(conf)) return -1; shell.SetReceiver(io_service); boost::thread t(bind(RunThread, &io_service)); // boost::thread t(bind(&StateMachineMCP::Run, &io_service)); if (conf.Has("cmd")) { const vector v = conf.Get>("cmd"); for (vector::const_iterator it=v.begin(); it!=v.end(); it++) shell.ProcessLine(*it); } if (conf.Has("exec")) { const vector v = conf.Get>("exec"); for (vector::const_iterator it=v.begin(); it!=v.end(); it++) shell.Execute(*it); } if (conf.Get("quit")) shell.Stop(); shell.Run(); // Run the shell io_service.Stop(); // Signal Loop-thread to stop // io_service.Close(); // Obsolete, done by the destructor // Wait until the StateMachine has finished its thread // before returning and destroying the dim objects which might // still be in use. t.join(); return 0; */ } void SetupConfiguration(Configuration &conf) { const 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") // ("no-dim,d", po_bool(), "Disable dim services") ("console,c", var(), "Use console (0=shell, 1=simple buffered, X=simple unbuffered)") ("cmd", vars(), "Execute one or more commands at startup") ("exec,e", vars(), "Execute one or more scrips at startup") ("quit,q", po_switch(), "Quit after startup"); ; /* po::options_description control("FTM control options"); control.add_options() ("addr,a", var("localhost:5000"), "Network address of FTM") ("quiet,q", po_bool(), "Disable printing contents of all received messages (except dynamic data) in clear text.") ; */ conf.AddEnv("dns", "DIM_DNS_NODE"); conf.AddOptions(config); // conf.AddOptions(control); } /* Extract usage clause(s) [if any] for SYNOPSIS. Translators: "Usage" and "or" here are patterns (regular expressions) which are used to match the usage synopsis in program output. An example from cp (GNU coreutils) which contains both strings: Usage: cp [OPTION]... [-T] SOURCE DEST or: cp [OPTION]... SOURCE... DIRECTORY or: cp [OPTION]... -t DIRECTORY SOURCE... */ void PrintUsage() { cout << "The ftmctrl controls the FSC (FACT Slow Control) board.\n" "\n" "The default is that the program is started without user intercation. " "All actions are supposed to arrive as DimCommands. Using the -c " "option, a local shell can be initialized. With h or help a short " "help message about the usuage can be brought to the screen.\n" "\n" "Usage: fscctrl [-c type] [OPTIONS]\n" " or: fscctrl [OPTIONS]\n"; cout << endl; } void PrintHelp() { /* Additional help text which is printed after the configuration options goes here */ /* cout << "bla bla bla" << endl << endl; cout << endl; cout << "Environment:" << endl; cout << "environment" << endl; cout << endl; cout << "Examples:" << endl; cout << "test exam" << endl; cout << endl; cout << "Files:" << endl; cout << "files" << endl; cout << endl; */ } int main(int argc, const char* argv[]) { Configuration conf(argv[0]); conf.SetPrintUsage(PrintUsage); SetupConfiguration(conf); po::variables_map vm; try { vm = conf.Parse(argc, argv); } #if BOOST_VERSION > 104000 catch (po::multiple_occurrences &e) { cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl; return -1; } #endif catch (exception& e) { cerr << "Program options invalid due to: " << e.what() << endl; return -1; } if (conf.HasVersion() || conf.HasPrint()) return -1; if (conf.HasHelp()) { PrintHelp(); return -1; } Dim::Setup(conf.Get("dns")); //try { // No console access at all if (!conf.Has("console")) { // if (conf.Get("no-dim")) // return RunShell(conf); // else return RunShell(conf); } // Cosole access w/ and w/o Dim /* if (conf.Get("no-dim")) { if (conf.Get("console")==0) return RunShell(conf); else return RunShell(conf); } else */ { 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; }