diff --git a/yasmin/include/yasmin/state_machine.hpp b/yasmin/include/yasmin/state_machine.hpp index e67fc96..fd64463 100644 --- a/yasmin/include/yasmin/state_machine.hpp +++ b/yasmin/include/yasmin/state_machine.hpp @@ -16,6 +16,7 @@ #ifndef YASMIN_STATE_MACHINE_HPP #define YASMIN_STATE_MACHINE_HPP +#include <atomic> #include <functional> #include <map> #include <memory> @@ -40,14 +41,16 @@ namespace yasmin { */ class StateMachine : public State { - // Type definitions for callback functions + /// Alias for a callback function executed before running the state machine. using StartCallbackType = std::function<void( std::shared_ptr<yasmin::blackboard::Blackboard>, const std::string &, const std::vector<std::string> &)>; + /// Alias for a callback function executed before changing the state. using TransitionCallbackType = std::function<void( std::shared_ptr<yasmin::blackboard::Blackboard>, const std::string &, const std::string &, const std::string &, const std::vector<std::string> &)>; + /// Alias for a callback function executed after running the state machine. using EndCallbackType = std::function<void( std::shared_ptr<yasmin::blackboard::Blackboard>, const std::string &, const std::vector<std::string> &)>; @@ -225,20 +228,27 @@ class StateMachine : public State { std::string to_string(); private: - std::map<std::string, std::shared_ptr<State>> states; ///< Map of states - std::map<std::string, std::map<std::string, std::string>> - transitions; ///< Map of transitions - std::string start_state; ///< Name of the start state - std::string current_state; ///< Name of the current state - std::unique_ptr<std::mutex> - current_state_mutex; ///< Mutex for current state access - - std::vector<std::pair<StartCallbackType, std::vector<std::string>>> - start_cbs; ///< Start callbacks + /// Map of states + std::map<std::string, std::shared_ptr<State>> states; + /// Map of transitions + std::map<std::string, std::map<std::string, std::string>> transitions; + /// Name of the start state + std::string start_state; + /// Name of the current state + std::string current_state; + /// Mutex for current state access + std::unique_ptr<std::mutex> current_state_mutex; + + /// Flag to indicate if the state machine has been validated + std::atomic_bool validated{false}; + + /// Start callbacks executed before the state machine + std::vector<std::pair<StartCallbackType, std::vector<std::string>>> start_cbs; + /// Transition callbacks executed before changing the state std::vector<std::pair<TransitionCallbackType, std::vector<std::string>>> - transition_cbs; ///< Transition callbacks - std::vector<std::pair<EndCallbackType, std::vector<std::string>>> - end_cbs; ///< End callbacks + transition_cbs; + /// End callbacks executed before the state machine + std::vector<std::pair<EndCallbackType, std::vector<std::string>>> end_cbs; }; } // namespace yasmin diff --git a/yasmin/src/yasmin/state_machine.cpp b/yasmin/src/yasmin/state_machine.cpp index eeb5832..262db59 100644 --- a/yasmin/src/yasmin/state_machine.cpp +++ b/yasmin/src/yasmin/state_machine.cpp @@ -85,6 +85,9 @@ void StateMachine::add_state(std::string name, std::shared_ptr<State> state, if (this->start_state.empty()) { this->set_start_state(name); } + + // Mark state machine as no validated + this->validated.store(false); } void StateMachine::add_state(std::string name, std::shared_ptr<State> state) { @@ -194,6 +197,11 @@ void StateMachine::validate() { YASMIN_LOG_DEBUG("Validating state machine '%s'", this->to_string().c_str()); + if (this->validated.load()) { + YASMIN_LOG_DEBUG("State machine '%s' has already been validated", + this->to_string().c_str()); + } + // Check initial state if (this->start_state.empty()) { throw std::runtime_error("No initial state set"); @@ -260,6 +268,9 @@ void StateMachine::validate() { "' not registered as outcome or state"); } } + + // State machine has been validated + this->validated.store(true); } std::string diff --git a/yasmin/yasmin/state_machine.py b/yasmin/yasmin/state_machine.py index 4cd0289..a8a61e6 100644 --- a/yasmin/yasmin/state_machine.py +++ b/yasmin/yasmin/state_machine.py @@ -54,6 +54,10 @@ def __init__(self, outcomes: Set[str]) -> None: self.__current_state: str = None ## A threading lock to manage access to the current state. self.__current_state_lock: Lock = Lock() + + ## A flag indicating whether the state machine has been validated. + self._validated: bool = False + ## A list of callbacks to call when the state machine starts. self.__start_cbs: List[ Tuple[Callable[[Blackboard, str, List[Any]], None], List[Any]] @@ -119,6 +123,9 @@ def add_state( if not self._start_state: self.set_start_state(name) + ## Mark state machine as no validated + self._validated = False + def set_start_state(self, state_name: str) -> None: """ Sets the initial state for the state machine. @@ -276,6 +283,9 @@ def validate(self) -> None: """ yasmin.YASMIN_LOG_DEBUG(f"Validating state machine '{self}'") + if self._validated: + yasmin.YASMIN_LOG_DEBUG("State machine '{self}' has already been validated") + # terminal outcomes terminal_outcomes = [] @@ -323,6 +333,9 @@ def validate(self) -> None: f"State machine outcome '{o}' not registered as outcome neither state" ) + # State machine has been validated + self._validated = True + def execute(self, blackboard: Blackboard) -> str: """ Executes the state machine starting from the initial state.