Update state machine

- Split INITIALIZING state into Init+Bind+Connect
 - Remove PAUSE state
 - Convert state/transitions to enum classes (CamelCase)
 - Transition to a state only once previous handler is complete
 - Add CompleteInit transition to notify Initializing state
   that config updates are complete
 - Deprecate WaitForEndOfState(transition) in favor of
   WaitForState(state)/WaitForNextState()
 - Update tests/plugins to new APIs
 - Deprecate CheckCurrentState() in favor of NewStatePending()
This commit is contained in:
Alexey Rybalchenko
2019-02-07 13:38:11 +01:00
committed by Dennis Klein
parent 5e71d09e4d
commit fc94342db8
33 changed files with 1322 additions and 1515 deletions

View File

@@ -1,132 +1,107 @@
/********************************************************************************
* Copyright (C) 2017 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH *
* Copyright (C) 2014 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 FAIR_MQ_STATEMACHINE_H
#define FAIR_MQ_STATEMACHINE_H
#ifndef FAIRMQSTATEMACHINE_H_
#define FAIRMQSTATEMACHINE_H_
#include <utility>
#include <FairMQLogger.h>
#include <fairmq/Tools.h>
#include <fairmq/EventManager.h>
#include <deque>
#include "FairMQLogger.h"
#include <string>
#include <memory>
#include <functional>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unordered_map>
#include <ostream>
#include <queue>
#include <mutex>
#include <stdexcept>
namespace fair
{
namespace mq
{
/**
* @class StateMachine StateMachine.h <fairmq/StateMachine.h>
* @brief Implements the state machine for FairMQ devices
*
* See https://github.com/FairRootGroup/FairRoot/blob/dev/fairmq/docs/Device.md#13-state-machine
*/
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:
enum class State : int
{
Ok,
Error,
Idle,
InitializingDevice,
DeviceReady,
InitializingTask,
Ready,
Running,
ResettingTask,
ResettingDevice,
Exiting
};
enum class StateTransition : int // transition event between States
{
InitDevice,
InitTask,
Run,
Stop,
ResetTask,
ResetDevice,
End,
ErrorFound,
Automatic
};
/// @brief Convert string to State
/// @param state to convert
/// @return State enum entry
/// @throw std::out_of_range if a string cannot be resolved to a State
static auto ToState(const std::string& state) -> State { return fkStateStrMap.at(state); }
/// @brief Convert string to StateTransition
/// @param transition to convert
/// @return StateTransition enum entry
/// @throw std::out_of_range if a string cannot be resolved to a StateTransition
static auto ToStateTransition(const std::string& transition) -> StateTransition { return fkStateTransitionStrMap.at(transition); }
/// @brief Convert State to string
/// @param state to convert
/// @return string representation of State enum entry
static auto ToStr(State state) -> std::string { return fkStrStateMap.at(state); }
/// @brief Convert StateTransition to string
/// @param transition to convert
/// @return string representation of StateTransition enum entry
static auto ToStr(StateTransition transition) -> std::string { return fkStrStateTransitionMap.at(transition); }
friend auto operator<<(std::ostream& os, const State& state) -> std::ostream& { return os << ToStr(state); }
friend auto operator<<(std::ostream& os, const StateTransition& transition) -> std::ostream& { return os << ToStr(transition); }
StateMachine();
virtual ~StateMachine();
struct IllegalTransition : std::runtime_error { using std::runtime_error::runtime_error; };
bool ChangeState(const Transition transition);
bool ChangeState(const std::string& transition) { return ChangeState(GetTransition(transition)); }
struct StateChange : Event<State> {};
struct StateQueued : Event<State> {};
auto SubscribeToStateChange(const std::string& subscriber, std::function<void(typename StateChange::KeyType newState, State lastState)> callback) -> void { fCallbacks.Subscribe<StateChange, State>(subscriber, callback); }
auto UnsubscribeFromStateChange(const std::string& subscriber) -> void { fCallbacks.Unsubscribe<StateChange, State>(subscriber); }
auto SubscribeToStateQueued(const std::string& subscriber, std::function<void(typename StateQueued::KeyType newState, State lastState)> callback) -> void { fCallbacks.Subscribe<StateQueued, State>(subscriber, callback); }
auto UnsubscribeFromStateQueued(const std::string& subscriber) -> void { fCallbacks.Unsubscribe<StateQueued, State>(subscriber); }
void SubscribeToStateChange(const std::string& key, std::function<void(const State)> callback);
void UnsubscribeFromStateChange(const std::string& key);
auto GetCurrentState() const -> State { std::lock_guard<std::mutex> lock{fMutex}; return fState; }
auto GetCurrentErrorState() const -> State { std::lock_guard<std::mutex> lock{fMutex}; return fErrorState; }
auto GetLastQueuedState() const -> State { std::lock_guard<std::mutex> lock{fMutex}; return fNextStates.back(); }
void HandleStates(std::function<void(const State)> callback);
void StopHandlingStates();
auto ChangeState(StateTransition transition) -> void;
void SubscribeToNewTransition(const std::string& key, std::function<void(const Transition)> callback);
void UnsubscribeFromNewTransition(const std::string& key);
auto Run() -> void;
auto Reset() -> void;
bool NewStatePending() const;
void WaitForPendingState() const;
bool WaitForPendingStateFor(const int durationInMs) const;
auto NextStatePending() -> bool;
State GetCurrentState() const;
std::string GetCurrentStateName() const;
void Start();
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);
private:
State fState;
State fErrorState;
std::deque<State> fNextStates;
EventManager fCallbacks;
std::shared_ptr<void> fFsm;
};
static const std::unordered_map<std::string, State> fkStateStrMap;
static const std::unordered_map<State, std::string, tools::HashEnum<State>> fkStrStateMap;
static const std::unordered_map<std::string, StateTransition> fkStateTransitionStrMap;
static const std::unordered_map<StateTransition, std::string, tools::HashEnum<StateTransition>> fkStrStateTransitionMap;
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); }
mutable std::mutex fMutex;
std::condition_variable fNewState;
} // namespace mq
} // namespace fair
static auto Transition(const State currentState, const StateTransition transition) -> State;
}; /* class StateMachine */
} /* namespace mq */
} /* namespace fair */
#endif /* FAIR_MQ_STATEMACHINE_H */
#endif /* FAIRMQSTATEMACHINE_H_ */