Extract States & Transitions to own header, use in plugins

This commit is contained in:
Alexey Rybalchenko 2019-07-17 11:39:44 +02:00 committed by Dennis Klein
parent 8bb6a9518a
commit 4487b81de8
8 changed files with 206 additions and 308 deletions

View File

@ -28,7 +28,6 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
DESTINATION ${PROJECT_INSTALL_INCDIR}
)
#########
# Tools #
#########
@ -92,10 +91,12 @@ if(BUILD_FAIRMQ OR BUILD_SDK)
set(FSM_PUBLIC_HEADER_FILES
StateMachine.h
States.h
)
set(FSM_SOURCE_FILES
StateMachine.cxx
States.cxx
)
add_library(${target}

View File

@ -491,7 +491,7 @@ class FairMQDevice
public:
bool ChangeState(const fair::mq::Transition transition) { return fStateMachine.ChangeState(transition); }
bool ChangeState(const std::string& transition) { return fStateMachine.ChangeState(fair::mq::StateMachine::GetTransition(transition)); }
bool ChangeState(const std::string& transition) { return fStateMachine.ChangeState(fair::mq::GetTransition(transition)); }
bool ChangeState(const int transition) __attribute__((deprecated("Use ChangeState(const fair::mq::Transition transition).")));
@ -500,7 +500,7 @@ class FairMQDevice
fair::mq::State WaitForNextState();
void WaitForState(fair::mq::State state);
void WaitForState(const std::string& state) { WaitForState(fair::mq::StateMachine::GetState(state)); }
void WaitForState(const std::string& state) { WaitForState(fair::mq::GetState(state)); }
void TransitionTo(const fair::mq::State state);
@ -519,8 +519,8 @@ class FairMQDevice
fair::mq::State GetCurrentState() const { return fStateMachine.GetCurrentState(); }
std::string GetCurrentStateName() const { return fStateMachine.GetCurrentStateName(); }
static std::string GetStateName(const fair::mq::State state) { return fair::mq::StateMachine::GetStateName(state); }
static std::string GetTransitionName(const fair::mq::Transition transition) { return fair::mq::StateMachine::GetTransitionName(transition); }
static std::string GetStateName(const fair::mq::State state) { return fair::mq::GetStateName(state); }
static std::string GetTransitionName(const fair::mq::Transition transition) { return fair::mq::GetTransitionName(transition); }
struct DeviceStateError : std::runtime_error { using std::runtime_error::runtime_error; };

View File

@ -11,135 +11,20 @@
using namespace fair::mq;
using namespace std;
const unordered_map<string, PluginServices::DeviceState> PluginServices::fkDeviceStateStrMap = {
{"OK", DeviceState::Ok},
{"ERROR", DeviceState::Error},
{"IDLE", DeviceState::Idle},
{"INITIALIZING DEVICE", DeviceState::InitializingDevice},
{"INITIALIZED", DeviceState::Initialized},
{"BINDING", DeviceState::Binding},
{"BOUND", DeviceState::Bound},
{"CONNECTING", DeviceState::Connecting},
{"DEVICE READY", DeviceState::DeviceReady},
{"INITIALIZING TASK", DeviceState::InitializingTask},
{"READY", DeviceState::Ready},
{"RUNNING", DeviceState::Running},
{"RESETTING TASK", DeviceState::ResettingTask},
{"RESETTING DEVICE", DeviceState::ResettingDevice},
{"EXITING", DeviceState::Exiting}
};
const unordered_map<PluginServices::DeviceState, string, tools::HashEnum<PluginServices::DeviceState>> PluginServices::fkStrDeviceStateMap = {
{DeviceState::Ok, "OK"},
{DeviceState::Error, "ERROR"},
{DeviceState::Idle, "IDLE"},
{DeviceState::InitializingDevice, "INITIALIZING DEVICE"},
{DeviceState::Initialized, "INITIALIZED"},
{DeviceState::Binding, "BINDING"},
{DeviceState::Bound, "BOUND"},
{DeviceState::Connecting, "CONNECTING"},
{DeviceState::DeviceReady, "DEVICE READY"},
{DeviceState::InitializingTask, "INITIALIZING TASK"},
{DeviceState::Ready, "READY"},
{DeviceState::Running, "RUNNING"},
{DeviceState::ResettingTask, "RESETTING TASK"},
{DeviceState::ResettingDevice, "RESETTING DEVICE"},
{DeviceState::Exiting, "EXITING"}
};
const unordered_map<string, PluginServices::DeviceStateTransition> PluginServices::fkDeviceStateTransitionStrMap = {
{"AUTO", DeviceStateTransition::Auto},
{"INIT DEVICE", DeviceStateTransition::InitDevice},
{"COMPLETE INIT", DeviceStateTransition::CompleteInit},
{"BIND", DeviceStateTransition::Bind},
{"CONNECT", DeviceStateTransition::Connect},
{"INIT TASK", DeviceStateTransition::InitTask},
{"RUN", DeviceStateTransition::Run},
{"STOP", DeviceStateTransition::Stop},
{"RESET TASK", DeviceStateTransition::ResetTask},
{"RESET DEVICE", DeviceStateTransition::ResetDevice},
{"END", DeviceStateTransition::End},
{"ERROR FOUND", DeviceStateTransition::ErrorFound},
};
const unordered_map<PluginServices::DeviceStateTransition, string, tools::HashEnum<PluginServices::DeviceStateTransition>> PluginServices::fkStrDeviceStateTransitionMap = {
{DeviceStateTransition::Auto, "Auto"},
{DeviceStateTransition::InitDevice, "INIT DEVICE"},
{DeviceStateTransition::CompleteInit, "COMPLETE INIT"},
{DeviceStateTransition::Bind, "BIND"},
{DeviceStateTransition::Connect, "CONNECT"},
{DeviceStateTransition::InitTask, "INIT TASK"},
{DeviceStateTransition::Run, "RUN"},
{DeviceStateTransition::Stop, "STOP"},
{DeviceStateTransition::ResetTask, "RESET TASK"},
{DeviceStateTransition::ResetDevice, "RESET DEVICE"},
{DeviceStateTransition::End, "END"},
{DeviceStateTransition::ErrorFound, "ERROR FOUND"},
};
const unordered_map<State, PluginServices::DeviceState, tools::HashEnum<State>> PluginServices::fkDeviceStateMap = {
{State::Ok, DeviceState::Ok},
{State::Error, DeviceState::Error},
{State::Idle, DeviceState::Idle},
{State::InitializingDevice, DeviceState::InitializingDevice},
{State::Initialized, DeviceState::Initialized},
{State::Binding, DeviceState::Binding},
{State::Bound, DeviceState::Bound},
{State::Connecting, DeviceState::Connecting},
{State::DeviceReady, DeviceState::DeviceReady},
{State::InitializingTask, DeviceState::InitializingTask},
{State::Ready, DeviceState::Ready},
{State::Running, DeviceState::Running},
{State::ResettingTask, DeviceState::ResettingTask},
{State::ResettingDevice, DeviceState::ResettingDevice},
{State::Exiting, DeviceState::Exiting}
};
const unordered_map<PluginServices::DeviceState, State> PluginServices::fkStateMap = {
{DeviceState::Ok, State::Ok},
{DeviceState::Error, State::Error},
{DeviceState::Idle, State::Idle},
{DeviceState::InitializingDevice, State::InitializingDevice},
{DeviceState::Initialized, State::Initialized},
{DeviceState::Binding, State::Binding},
{DeviceState::Bound, State::Bound},
{DeviceState::Connecting, State::Connecting},
{DeviceState::DeviceReady, State::DeviceReady},
{DeviceState::InitializingTask, State::InitializingTask},
{DeviceState::Ready, State::Ready},
{DeviceState::Running, State::Running},
{DeviceState::ResettingTask, State::ResettingTask},
{DeviceState::ResettingDevice, State::ResettingDevice},
{DeviceState::Exiting, State::Exiting}
};
const unordered_map<PluginServices::DeviceStateTransition, Transition, tools::HashEnum<PluginServices::DeviceStateTransition>> PluginServices::fkDeviceStateTransitionMap = {
{DeviceStateTransition::Auto, Transition::Auto},
{DeviceStateTransition::InitDevice, Transition::InitDevice},
{DeviceStateTransition::CompleteInit, Transition::CompleteInit},
{DeviceStateTransition::Bind, Transition::Bind},
{DeviceStateTransition::Connect, Transition::Connect},
{DeviceStateTransition::InitTask, Transition::InitTask},
{DeviceStateTransition::Run, Transition::Run},
{DeviceStateTransition::Stop, Transition::Stop},
{DeviceStateTransition::ResetTask, Transition::ResetTask},
{DeviceStateTransition::ResetDevice, Transition::ResetDevice},
{DeviceStateTransition::End, Transition::End},
{DeviceStateTransition::ErrorFound, Transition::ErrorFound}
};
auto PluginServices::ChangeDeviceState(const string& controller, const DeviceStateTransition next) -> bool
{
lock_guard<mutex> lock{fDeviceControllerMutex};
if (!fDeviceController) fDeviceController = controller;
bool result = false;
if (fDeviceController == controller) {
result = fDevice.ChangeState(fkDeviceStateTransitionMap.at(next));
return fDevice.ChangeState(next);
} else {
throw DeviceControlError{tools::ToString(
"Plugin '", controller, "' is not allowed to change device states. ",
"Currently, plugin '", *fDeviceController, "' has taken control."
)};
}
return result;
}
void PluginServices::TransitionDeviceStateTo(const std::string& controller, DeviceState state)
@ -149,7 +34,7 @@ void PluginServices::TransitionDeviceStateTo(const std::string& controller, Devi
if (!fDeviceController) fDeviceController = controller;
if (fDeviceController == controller) {
fDevice.TransitionTo(fkStateMap.at(state));
fDevice.TransitionTo(state);
} else {
throw DeviceControlError{tools::ToString(
"Plugin '", controller, "' is not allowed to change device states. ",

View File

@ -10,6 +10,7 @@
#define FAIR_MQ_PLUGINSERVICES_H
#include <fairmq/Tools.h>
#include <fairmq/States.h>
#include <FairMQDevice.h>
#include <fairmq/ProgOptions.h>
#include <fairmq/Properties.h>
@ -58,41 +59,8 @@ class PluginServices
PluginServices(const PluginServices&) = delete;
PluginServices operator=(const PluginServices&) = delete;
/// See https://github.com/FairRootGroup/FairRoot/blob/dev/fairmq/docs/Device.md#13-state-machine
enum class DeviceState : int
{
Ok,
Error,
Idle,
InitializingDevice,
Initialized,
Binding,
Bound,
Connecting,
DeviceReady,
InitializingTask,
Ready,
Running,
ResettingTask,
ResettingDevice,
Exiting
};
enum class DeviceStateTransition : int // transition event between DeviceStates
{
Auto,
InitDevice,
CompleteInit,
Bind,
Connect,
InitTask,
Run,
Stop,
ResetTask,
ResetDevice,
End,
ErrorFound
};
using DeviceState = fair::mq::State;
using DeviceStateTransition = fair::mq::Transition;
// Control API
@ -100,29 +68,26 @@ class PluginServices
/// @param state to convert
/// @return DeviceState enum entry
/// @throw std::out_of_range if a string cannot be resolved to a DeviceState
static auto ToDeviceState(const std::string& state) -> DeviceState { return fkDeviceStateStrMap.at(state); }
static auto ToDeviceState(const std::string& state) -> DeviceState { return GetState(state); }
/// @brief Convert string to DeviceStateTransition
/// @param transition to convert
/// @return DeviceStateTransition enum entry
/// @throw std::out_of_range if a string cannot be resolved to a DeviceStateTransition
static auto ToDeviceStateTransition(const std::string& transition) -> DeviceStateTransition { return fkDeviceStateTransitionStrMap.at(transition); }
static auto ToDeviceStateTransition(const std::string& transition) -> DeviceStateTransition { return GetTransition(transition); }
/// @brief Convert DeviceState to string
/// @param state to convert
/// @return string representation of DeviceState enum entry
static auto ToStr(DeviceState state) -> std::string { return fkStrDeviceStateMap.at(state); }
static auto ToStr(DeviceState state) -> std::string { return GetStateName(state); }
/// @brief Convert DeviceStateTransition to string
/// @param transition to convert
/// @return string representation of DeviceStateTransition enum entry
static auto ToStr(DeviceStateTransition transition) -> std::string { return fkStrDeviceStateTransitionMap.at(transition); }
friend auto operator<<(std::ostream& os, const DeviceState& state) -> std::ostream& { return os << ToStr(state); }
friend auto operator<<(std::ostream& os, const DeviceStateTransition& transition) -> std::ostream& { return os << ToStr(transition); }
static auto ToStr(DeviceStateTransition transition) -> std::string { return GetTransitionName(transition); }
/// @return current device state
auto GetCurrentDeviceState() const -> DeviceState { return fkDeviceStateMap.at(static_cast<fair::mq::State>(fDevice.GetCurrentState())); }
auto GetCurrentDeviceState() const -> DeviceState { return fDevice.GetCurrentState(); }
/// @brief Become device controller
/// @param controller id
@ -171,7 +136,7 @@ class PluginServices
auto SubscribeToDeviceStateChange(const std::string& subscriber, std::function<void(DeviceState /*newState*/)> callback) -> void
{
fDevice.SubscribeToStateChange(subscriber, [&,callback](fair::mq::State newState){
callback(fkDeviceStateMap.at(newState));
callback(newState);
});
}
@ -310,14 +275,6 @@ class PluginServices
/// @brief Decreases logging verbosity, or sets it to highest if it is already lowest
auto CycleLogVerbosityDown() -> void { Logger::CycleVerbosityDown(); }
static const std::unordered_map<std::string, DeviceState> fkDeviceStateStrMap;
static const std::unordered_map<DeviceState, std::string, tools::HashEnum<DeviceState>> fkStrDeviceStateMap;
static const std::unordered_map<std::string, DeviceStateTransition> fkDeviceStateTransitionStrMap;
static const std::unordered_map<DeviceStateTransition, std::string, tools::HashEnum<DeviceStateTransition>> fkStrDeviceStateTransitionMap;
static const std::unordered_map<fair::mq::State, DeviceState, tools::HashEnum<fair::mq::State>> fkDeviceStateMap;
static const std::unordered_map<DeviceState, fair::mq::State> fkStateMap;
static const std::unordered_map<DeviceStateTransition, fair::mq::Transition, tools::HashEnum<DeviceStateTransition>> fkDeviceStateTransitionMap;
private:
fair::mq::ProgOptions& fConfig;
FairMQDevice& fDevice;

View File

@ -6,7 +6,7 @@
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include "StateMachine.h"
#include <fairmq/StateMachine.h>
#include <fairmq/Tools.h>
// Increase maximum number of boost::msm states (default is 10)
@ -25,10 +25,8 @@
#include <atomic>
#include <condition_variable>
#include <chrono>
#include <array>
#include <unordered_map>
#include <mutex>
#include <stdexcept>
using namespace std;
using namespace boost::msm;
@ -76,80 +74,6 @@ struct RESET_DEVICE_E { static string Name() { return "RESET_DEVICE"; } static
struct END_E { static string Name() { return "END"; } static Transition Type() { return Transition::End; } };
struct ERROR_FOUND_E { static string Name() { return "ERROR_FOUND"; } static Transition Type() { return Transition::ErrorFound; } };
static array<string, 15> stateNames =
{
{
"OK",
"Error",
"IDLE",
"INITIALIZING_DEVICE",
"INITIALIZED",
"BINDING",
"BOUND",
"CONNECTING",
"DEVICE_READY",
"INITIALIZING_TASK",
"READY",
"RUNNING",
"RESETTING_TASK",
"RESETTING_DEVICE",
"EXITING"
}
};
static array<string, 12> transitionNames =
{
{
"AUTO",
"INIT_DEVICE",
"COMPLETE_INIT",
"BIND",
"CONNECT",
"INIT_TASK",
"RUN",
"STOP",
"RESET_TASK",
"RESET_DEVICE",
"END",
"ERROR_FOUND"
}
};
static map<string, State> stateNumbers =
{
{ "OK", State::Ok },
{ "Error", State::Error },
{ "IDLE", State::Idle },
{ "INITIALIZING_DEVICE", State::InitializingDevice },
{ "INITIALIZED", State::Initialized },
{ "BINDING", State::Binding },
{ "BOUND", State::Bound },
{ "CONNECTING", State::Connecting },
{ "DEVICE_READY", State::DeviceReady },
{ "INITIALIZING_TASK", State::InitializingTask },
{ "READY", State::Ready },
{ "RUNNING", State::Running },
{ "RESETTING_TASK", State::ResettingTask },
{ "RESETTING_DEVICE", State::ResettingDevice },
{ "EXITING", State::Exiting }
};
static map<string, Transition> transitionNumbers =
{
{ "AUTO", Transition::Auto },
{ "INIT_DEVICE", Transition::InitDevice },
{ "COMPLETE_INIT", Transition::CompleteInit },
{ "BIND", Transition::Bind },
{ "CONNECT", Transition::Connect },
{ "INIT_TASK", Transition::InitTask },
{ "RUN", Transition::Run },
{ "STOP", Transition::Stop },
{ "RESET_TASK", Transition::ResetTask },
{ "RESET_DEVICE", Transition::ResetDevice },
{ "END", Transition::End },
{ "ERROR_FOUND", Transition::ErrorFound }
};
// defining the boost MSM state machine
struct Machine_ : public state_machine_def<Machine_>
{
@ -369,7 +293,7 @@ try {
return false;
}
} else {
LOG(state) << "Transition " << transitionNames.at(static_cast<int>(transition)) << " incoming, but another state transition is already ongoing.";
LOG(state) << "Transition " << GetTransitionName(transition) << " incoming, but another state transition is already ongoing.";
return false;
}
} catch (exception& e) {
@ -461,9 +385,3 @@ void StateMachine::ProcessWork()
throw;
}
}
string StateMachine::GetStateName(const State state) { return stateNames.at(static_cast<int>(state)); }
string StateMachine::GetTransitionName(const Transition transition) { return transitionNames.at(static_cast<int>(transition)); }
State StateMachine::GetState(const string& state) { return stateNumbers.at(state); }
Transition StateMachine::GetTransition(const string& transition) { return transitionNumbers.at(transition); }

View File

@ -9,15 +9,13 @@
#ifndef FAIRMQSTATEMACHINE_H_
#define FAIRMQSTATEMACHINE_H_
#include <fairmq/States.h>
#include <fairlogger/Logger.h>
#include <string>
#include <memory>
#include <functional>
#include <condition_variable>
#include <ostream>
#include <queue>
#include <mutex>
#include <stdexcept>
namespace fair
@ -25,41 +23,6 @@ namespace fair
namespace mq
{
enum class State : int
{
Ok,
Error,
Idle,
InitializingDevice,
Initialized,
Binding,
Bound,
Connecting,
DeviceReady,
InitializingTask,
Ready,
Running,
ResettingTask,
ResettingDevice,
Exiting
};
enum class Transition : int
{
Auto,
InitDevice,
CompleteInit,
Bind,
Connect,
InitTask,
Run,
Stop,
ResetTask,
ResetDevice,
End,
ErrorFound
};
class StateMachine
{
public:
@ -89,20 +52,12 @@ class StateMachine
void ProcessWork();
static std::string GetStateName(const State);
static std::string GetTransitionName(const Transition);
static State GetState(const std::string& state);
static Transition GetTransition(const std::string& transition);
struct ErrorStateException : std::runtime_error { using std::runtime_error::runtime_error; };
private:
std::shared_ptr<void> fFsm;
};
inline std::ostream& operator<<(std::ostream& os, const State& state) { return os << StateMachine::GetStateName(state); }
inline std::ostream& operator<<(std::ostream& os, const Transition& transition) { return os << StateMachine::GetTransitionName(transition); }
} // namespace mq
} // namespace fair

116
fairmq/States.cxx Normal file
View File

@ -0,0 +1,116 @@
/********************************************************************************
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#include <fairmq/States.h>
#include <array>
#include <unordered_map>
using namespace std;
namespace fair
{
namespace mq
{
array<string, 15> stateNames =
{
{
"OK",
"ERROR",
"IDLE",
"INITIALIZING DEVICE",
"INITIALIZED",
"BINDING",
"BOUND",
"CONNECTING",
"DEVICE READY",
"INITIALIZING TASK",
"READY",
"RUNNING",
"RESETTING TASK",
"RESETTING DEVICE",
"EXITING"
}
};
unordered_map<string, State> states =
{
{ "OK", State::Ok },
{ "ERROR", State::Error },
{ "IDLE", State::Idle },
{ "INITIALIZING DEVICE", State::InitializingDevice },
{ "INITIALIZED", State::Initialized },
{ "BINDING", State::Binding },
{ "BOUND", State::Bound },
{ "CONNECTING", State::Connecting },
{ "DEVICE READY", State::DeviceReady },
{ "INITIALIZING TASK", State::InitializingTask },
{ "READY", State::Ready },
{ "RUNNING", State::Running },
{ "RESETTING TASK", State::ResettingTask },
{ "RESETTING DEVICE", State::ResettingDevice },
{ "EXITING", State::Exiting }
};
array<string, 12> transitionNames =
{
{
"AUTO",
"INIT DEVICE",
"COMPLETE INIT",
"BIND",
"CONNECT",
"INIT TASK",
"RUN",
"STOP",
"RESET TASK",
"RESET DEVICE",
"END",
"ERROR FOUND"
}
};
unordered_map<string, Transition> transitions =
{
{ "AUTO", Transition::Auto },
{ "INIT DEVICE", Transition::InitDevice },
{ "COMPLETE INIT", Transition::CompleteInit },
{ "BIND", Transition::Bind },
{ "CONNECT", Transition::Connect },
{ "INIT TASK", Transition::InitTask },
{ "RUN", Transition::Run },
{ "STOP", Transition::Stop },
{ "RESET TASK", Transition::ResetTask },
{ "RESET DEVICE", Transition::ResetDevice },
{ "END", Transition::End },
{ "ERROR FOUND", Transition::ErrorFound }
};
string GetStateName(const State state)
{
return stateNames.at(static_cast<int>(state));
}
string GetTransitionName(const Transition transition)
{
return transitionNames.at(static_cast<int>(transition));
}
State GetState(const string& state)
{
return states.at(state);
}
Transition GetTransition(const string& transition)
{
return transitions.at(transition);
}
} // namespace mq
} // namespace fair

66
fairmq/States.h Normal file
View File

@ -0,0 +1,66 @@
/********************************************************************************
* Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* *
* This software is distributed under the terms of the *
* GNU Lesser General Public Licence (LGPL) version 3, *
* copied verbatim in the file "LICENSE" *
********************************************************************************/
#ifndef FAIRMQSTATES_H_
#define FAIRMQSTATES_H_
#include <string>
#include <ostream>
namespace fair
{
namespace mq
{
enum class State : int
{
Ok,
Error,
Idle,
InitializingDevice,
Initialized,
Binding,
Bound,
Connecting,
DeviceReady,
InitializingTask,
Ready,
Running,
ResettingTask,
ResettingDevice,
Exiting
};
enum class Transition : int
{
Auto,
InitDevice,
CompleteInit,
Bind,
Connect,
InitTask,
Run,
Stop,
ResetTask,
ResetDevice,
End,
ErrorFound
};
std::string GetStateName(const State);
std::string GetTransitionName(const Transition);
State GetState(const std::string& state);
Transition GetTransition(const std::string& transition);
inline std::ostream& operator<<(std::ostream& os, const State& state) { return os << GetStateName(state); }
inline std::ostream& operator<<(std::ostream& os, const Transition& transition) { return os << GetTransitionName(transition); }
} // namespace mq
} // namespace fair
#endif /* FAIRMQSTATES_H_ */