/******************************************************************************** * Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * * * * This software is distributed under the terms of the * * GNU Lesser General Public Licence version 3 (LGPL) version 3, * * copied verbatim in the file "LICENSE" * ********************************************************************************/ /** * FairMQStateMachine.h * * @since 2012-10-25 * @author D. Klein, A. Rybalchenko */ #ifndef FAIRMQSTATEMACHINE_H_ #define FAIRMQSTATEMACHINE_H_ #define FAIRMQ_INTERFACE_VERSION 3 #include #include #include #include #include #include // Increase maximum number of boost::msm states (default is 10) // This #define has to be before any msm header includes #define FUSION_MAX_VECTOR_SIZE 20 #include #include #include #include #include #include #include "FairMQLogger.h" namespace msm = boost::msm; namespace mpl = boost::mpl; namespace msmf = boost::msm::front; namespace FairMQFSM { // defining events for the boost MSM state machine struct INIT_DEVICE { std::string name() const { return "INIT_DEVICE"; } }; struct internal_DEVICE_READY { std::string name() const { return "internal_DEVICE_READY"; } }; struct INIT_TASK { std::string name() const { return "INIT_TASK"; } }; struct internal_READY { std::string name() const { return "internal_READY"; } }; struct RUN { std::string name() const { return "RUN"; } }; struct PAUSE { std::string name() const { return "PAUSE"; } }; struct STOP { std::string name() const { return "STOP"; } }; struct RESET_TASK { std::string name() const { return "RESET_TASK"; } }; struct RESET_DEVICE { std::string name() const { return "RESET_DEVICE"; } }; struct internal_IDLE { std::string name() const { return "internal_IDLE"; } }; struct END { std::string name() const { return "END"; } }; struct ERROR_FOUND { std::string name() const { return "ERROR_FOUND"; } }; // deactivate the warning for non-virtual destructor thrown in the boost library #if defined(__clang__) _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wnon-virtual-dtor\"") #elif defined(__GNUC__) || defined(__GNUG__) _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") _Pragma("GCC diagnostic ignored \"-Weffc++\"") #endif // defining the boost MSM state machine struct FairMQFSM_ : public msmf::state_machine_def { public: FairMQFSM_() : fWorkerThread() , fWork() , fWorkAvailableCondition() , fWorkDoneCondition() , fWorkMutex() , fWorkerTerminated(false) , fWorkActive(false) , fWorkAvailable(false) , fState() , fChangeStateMutex() {} // Destructor virtual ~FairMQFSM_() {}; template void on_entry(Event const&, FSM& fsm) { LOG(STATE) << "Starting FairMQ state machine"; fState = IDLE; // start a worker thread to execute user states in. fsm.fWorkerThread = std::thread(&FairMQFSM_::Worker, &fsm); } template void on_exit(Event const&, FSM& /*fsm*/) { LOG(STATE) << "Exiting FairMQ state machine"; } // The list of FSM states struct OK_FSM : public msmf::state<> {}; struct ERROR_FSM : public msmf::terminate_state<> {}; struct IDLE_FSM : public msmf::state<> {}; struct INITIALIZING_DEVICE_FSM : public msmf::state<> {}; struct DEVICE_READY_FSM : public msmf::state<> {}; struct INITIALIZING_TASK_FSM : public msmf::state<> {}; struct READY_FSM : public msmf::state<> {}; struct RUNNING_FSM : public msmf::state<> {}; struct PAUSED_FSM : public msmf::state<> {}; struct RESETTING_TASK_FSM : public msmf::state<> {}; struct RESETTING_DEVICE_FSM : public msmf::state<> {}; struct EXITING_FSM : public msmf::state<> {}; // Define initial states typedef mpl::vector initial_state; // Actions struct IdleFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering IDLE state"; fsm.fState = IDLE; } }; struct InitDeviceFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering INITIALIZING DEVICE state"; fsm.fState = INITIALIZING_DEVICE; std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::InitWrapper, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct DeviceReadyFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering DEVICE READY state"; fsm.fState = DEVICE_READY; } }; struct InitTaskFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering INITIALIZING TASK state"; fsm.fState = INITIALIZING_TASK; fsm.InitTaskWrapper(); // fsm.fInitializingTaskThread = std::thread(&FairMQFSM_::InitTaskWrapper, &fsm); } }; struct ReadyFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering READY state"; fsm.fState = READY; } }; struct RunFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering RUNNING state"; fsm.fState = RUNNING; std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::RunWrapper, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct PauseFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering PAUSED state"; fsm.fState = PAUSED; fsm.Unblock(); std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::Pause, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct ResumeFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering RUNNING state"; fsm.fState = RUNNING; std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::RunWrapper, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct StopFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering READY state"; fsm.fState = READY; fsm.Unblock(); std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } } }; struct InternalStopFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "RUNNING state finished without an external event, entering READY state"; fsm.fState = READY; } }; struct ResetTaskFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering RESETTING TASK state"; fsm.fState = RESETTING_TASK; std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::ResetTaskWrapper, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct ResetDeviceFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering RESETTING DEVICE state"; fsm.fState = RESETTING_DEVICE; std::unique_lock lock(fsm.fWorkMutex); while (fsm.fWorkActive) { fsm.fWorkDoneCondition.wait(lock); } fsm.fWorkAvailable = true; fsm.fWork = std::bind(&FairMQFSM_::ResetWrapper, &fsm); fsm.fWorkAvailableCondition.notify_one(); } }; struct ExitingFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering EXITING state"; fsm.fState = EXITING; // terminate worker thread { std::lock_guard lock(fsm.fWorkMutex); fsm.fWorkerTerminated = true; fsm.fWorkAvailableCondition.notify_one(); } // join the worker thread (executing user states) if (fsm.fWorkerThread.joinable()) { fsm.fWorkerThread.join(); } fsm.Exit(); } }; struct ErrorFoundFct { template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) { LOG(STATE) << "Entering ERROR state"; fsm.fState = ERROR; } }; // actions to be overwritten by derived classes virtual void InitWrapper() {} virtual void Init() {} virtual void InitTaskWrapper() {} virtual void InitTask() {} virtual void RunWrapper() {} virtual void Run() {} virtual void Pause() {} virtual void ResetWrapper() {} virtual void Reset() {} virtual void ResetTaskWrapper() {} virtual void ResetTask() {} virtual void Exit() {} virtual void Unblock() {} // Method to send commands. void Worker() { while (true) { { std::unique_lock lock(fWorkMutex); // Wait for work to be done. while (!fWorkAvailable && !fWorkerTerminated) { fWorkAvailableCondition.wait(lock); } if (fWorkerTerminated) { break; } fWorkActive = true; } fWork(); std::lock_guard lock(fWorkMutex); fWorkActive = false; fWorkAvailable = false; fWorkDoneCondition.notify_one(); } } // Transition table for FairMQFSM struct transition_table : mpl::vector< // Start Event Next Action Guard // +------------------------+----------------------+------------------------+----------------+---------+ msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row, msmf::Row> {}; // Replaces the default no-transition response. template void no_transition(Event const& e, FSM&, int state) { typedef typename msm::back::recursive_get_transition_table::type recursive_stt; typedef typename msm::back::generate_state_set::type all_states; std::string stateName; mpl::for_each>(msm::back::get_state_name(stateName, state)); stateName = stateName.substr(24); std::size_t pos = stateName.find("_FSME"); stateName.erase(pos); if (stateName == "1RUNNING" || stateName == "6DEVICE_READY" || stateName == "0PAUSED" || stateName == "8RESETTING_TASK" || stateName == "0RESETTING_DEVICE") { stateName = stateName.substr(1); } if (stateName != "OK") { LOG(STATE) << "No transition from state " << stateName << " on event " << e.name(); } // LOG(STATE) << "no transition from state " << GetStateName(state) << " (" << state << ") on event " << e.name(); } // backward compatibility to FairMQStateMachine enum State { OK, ERROR, IDLE, INITIALIZING_DEVICE, DEVICE_READY, INITIALIZING_TASK, READY, RUNNING, PAUSED, RESETTING_TASK, RESETTING_DEVICE, EXITING }; std::string GetStateName(int state) const { switch(state) { case OK: return "OK"; case ERROR: return "ERROR"; case IDLE: return "IDLE"; case INITIALIZING_DEVICE: return "INITIALIZING_DEVICE"; case DEVICE_READY: return "DEVICE_READY"; case INITIALIZING_TASK: return "INITIALIZING_TASK"; case READY: return "READY"; case RUNNING: return "RUNNING"; case PAUSED: return "PAUSED"; case RESETTING_TASK: return "RESETTING_TASK"; case RESETTING_DEVICE: return "RESETTING_DEVICE"; case EXITING: return "EXITING"; default: return "requested name for non-existent state..."; } } std::string GetCurrentStateName() const { return GetStateName(fState); } int GetCurrentState() const { return fState; } bool CheckCurrentState(int state) const { if (state == fState) { return true; } else { return false; } } bool CheckCurrentState(std::string state) const { if (state == GetCurrentStateName()) { return true; } else { return false; } } // this is to run certain functions in a separate thread std::thread fWorkerThread; // function to execute user states in a worker thread std::function fWork; std::condition_variable fWorkAvailableCondition; std::condition_variable fWorkDoneCondition; std::mutex fWorkMutex; bool fWorkerTerminated; bool fWorkActive; bool fWorkAvailable; protected: std::atomic fState; std::mutex fChangeStateMutex; }; // reactivate the warning for non-virtual destructor #if defined(__clang__) _Pragma("clang diagnostic pop") #elif defined(__GNUC__) || defined(__GNUG__) _Pragma("GCC diagnostic pop") #endif typedef msm::back::state_machine FairMQFSM; } // namespace FairMQFSM class FairMQStateMachine : public FairMQFSM::FairMQFSM { public: enum Event { INIT_DEVICE, internal_DEVICE_READY, INIT_TASK, internal_READY, RUN, PAUSE, STOP, RESET_TASK, RESET_DEVICE, internal_IDLE, END, ERROR_FOUND }; FairMQStateMachine(); virtual ~FairMQStateMachine(); int GetInterfaceVersion(); int GetEventNumber(std::string event); bool ChangeState(int event); bool ChangeState(std::string event); void WaitForEndOfState(int state); void WaitForEndOfState(std::string state); bool WaitForEndOfStateForMs(int state, int durationInMs); bool WaitForEndOfStateForMs(std::string state, int durationInMs); }; #endif /* FAIRMQSTATEMACHINE_H_ */