/** * @file utils/StateMachine.hpp * @author The ARTIS Development Team * See the AUTHORS or Authors.txt file */ /* * ARTIS - the multimodeling and simulation environment * This file is a part of the ARTIS environment * * Copyright (C) 2013-2023 ULCO http://www.univ-littoral.fr * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP #define ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP #include #include #include #include namespace artis::addons::utils { template class RootStateMachine; template class AbstractStateMachine { public: struct Event { int id; artis::common::event::Value data; }; struct InternalEvent : Event { int machine_id; }; struct ExternalEvent : Event { }; struct Events { std::vector internals; std::vector externals; }; struct AbstractState { int _id; AbstractState(int id) : _id(id) {} virtual typename Time::type ta(const typename Time::type & /* t */) const = 0; virtual ~AbstractState() = default; }; template struct State : AbstractState { std::shared_ptr _machine; State(int id, const std::shared_ptr &machine) : AbstractState(id), _machine(machine) {} typename Time::type ta(const typename Time::type & /* t */) const override { return Time::infinity; } }; struct AbstractTransition { virtual void action(const typename Time::type & /* t */, const artis::common::event::Value & /* value */) = 0; virtual bool event(const typename Time::type & /* t */, int /* event */) = 0; virtual bool guard(const typename Time::type & /* t */, const artis::common::event::Value & /* value */) const = 0; virtual bool no_event() const = 0; virtual Events output(const typename Time::type & /* t */) const = 0; virtual ~AbstractTransition() = default; }; template struct Transition : AbstractTransition { std::shared_ptr _machine; Transition(const std::shared_ptr &machine) : _machine(machine) {} void action(const typename Time::type & /* t */, const artis::common::event::Value & /* value */) override {} bool event(const typename Time::type & /* t */, int /* event */) override { return false; } bool guard(const typename Time::type & /* t */, const artis::common::event::Value & /* value */) const override { return true; } bool no_event() const override { return false; } Events output(const typename Time::type & /* t */) const override { return {}; } }; AbstractStateMachine(const typename Types::root_state_machine_type &root) : _root(root) {} virtual ~AbstractStateMachine() = default; // virtual void build(const std::shared_ptr> &machine) = 0; const std::unique_ptr ¤t_state() const { return _states.at(_current_state); } int current_state(int machine_id) const { return _root.current_state(machine_id); } void initial_state(int state) { _initial_state = state; } bool is_ready(const typename Time::type &t) const { return std::abs(_sigma - (t - _last_time)) < 1e-6; } const typename Types::root_state_machine_type &root() const { return _root; } void start(const typename Time::type &t) { _current_state = _initial_state; _last_time = t; compute_sigma(t); } void state(AbstractState *state) { _states.insert(std::make_pair(state->_id, std::unique_ptr(state))); _transitions.insert(std::make_pair(state->_id, _transitions_type{})); } void switch_transition(int state, int next_state, size_t index, AbstractTransition *transition) { auto it = std::find_if(_transitions[state].begin(), _transitions[state].end(), [next_state](const auto &e) { return e.second == next_state; }); assert(it != _transitions[state].end()); while (it + 1 != _transitions[state].end() and index > 0) { it = std::find_if(it + 1, _transitions[state].end(), [next_state](const auto &e) { return e.second == next_state; }); --index; } assert(it != _transitions[state].end()); it->first.reset(transition); } typename Time::type ta(const typename Time::type & /* t */) const { return _sigma; } void transition(int state, int next_state, AbstractTransition *transition) { _transitions[state].push_back(std::make_pair(std::unique_ptr(transition), next_state)); } // without event Events transition(const typename Time::type &t) { typename std::map::const_iterator it = _transitions.find(_current_state); Events events{}; uint unique = 0; if (it != _transitions.cend()) { AbstractTransition *select = nullptr; int stateID{}; std::for_each(it->second.cbegin(), it->second.cend(), [t, &unique, &select, &stateID]( const std::pair, int> &e) { if (e.first->no_event() and e.first->guard(t, {})) { select = e.first.get(); stateID = e.second; ++unique; } }); assert(unique <= 1); if (select != nullptr) { select->action(t, {}); events = select->output(t); _current_state = stateID; compute_sigma(t); } else { update_sigma(t); } } return events; } // with external event Events transition(const typename Time::type &t, const ExternalEvent &event) { typename std::map::const_iterator it = _transitions.find(_current_state); Events events{}; uint unique = 0; if (it != _transitions.cend()) { AbstractTransition *select = nullptr; int stateID{}; std::for_each(it->second.cbegin(), it->second.cend(), [t, event, &unique, &select, &stateID]( const std::pair, int> &e) { if (e.first->guard(t, event.data) and e.first->event(t, event.id)) { select = e.first.get(); stateID = e.second; ++unique; } }); assert(unique <= 1); if (select != nullptr) { select->action(t, event.data); events = select->output(t); _current_state = stateID; compute_sigma(t); } else { update_sigma(t); } } return events; } // with internal event Events transition(const typename Time::type &t, const InternalEvent &event) { typename std::map::const_iterator it = _transitions.find(_current_state); Events events{}; uint unique = 0; if (it != _transitions.cend()) { AbstractTransition *select = nullptr; int stateID{}; std::for_each(it->second.cbegin(), it->second.cend(), [t, event, &unique, &select, &stateID]( const std::pair, int> &e) { if (e.first->guard(t, event.data) and e.first->event(t, event.id)) { select = e.first.get(); stateID = e.second; ++unique; } }); assert(unique <= 1); if (select != nullptr) { select->action(t, event.data); events = select->output(t); _current_state = stateID; compute_sigma(t); } else { update_sigma(t); } } return events; } void update_sigma(const typename Time::type &t) { _sigma -= t - _last_time; _last_time = t; } private: void compute_sigma(const typename Time::type &t) { _sigma = current_state()->ta(t); _last_time = t; } typedef std::vector, int >> _transitions_type; std::map> _states; std::map _transitions; int _initial_state; const typename Types::root_state_machine_type &_root; int _current_state; typename Time::type _last_time{}; typename Time::type _sigma{}; }; template class StateMachine : public AbstractStateMachine { public: StateMachine(const typename Types::root_state_machine_type &root, StateType state) : AbstractStateMachine(root), _state(state) {} virtual ~StateMachine() = default; const StateType &state_() const { return _state; } StateType &state_() { return _state; } protected: StateType _state; }; } #define DECLARE_STATE_TRANSITION_TYPES(StateMachineClassName) \ typedef typename StateMachineClassName::template State> State_t; \ typedef typename StateMachineClassName::template Transition> Transition_t; #define DEFINE_STATE_MACHINE_STATE(StateClassName, StateID, StateMachineClassName) \ template \ struct StateClassName : StateMachineClassName::State_t { \ StateClassName(const std::shared_ptr> &machine) : \ StateMachineClassName::State_t(StateID, machine) {} \ }; #define DEFINE_STATE_MACHINE_STATE_WITH_NULL_TA(StateClassName, StateID, StateMachineClassName) \ template \ struct StateClassName : StateMachineClassName::State_t { \ StateClassName(const std::shared_ptr> &machine) : \ StateMachineClassName::State_t(StateID, machine) {} \ artis::traffic::core::Time ta(const artis::traffic::core::Time & /* t */) const override { return 0; }\ }; #define DEFINE_STATE_MACHINE_STATE_WITH_TA(StateClassName, StateID, StateMachineClassName) \ template \ struct StateClassName : StateMachineClassName::State_t { \ StateClassName(const std::shared_ptr> &machine) : \ StateMachineClassName::State_t(StateID, machine) {} \ artis::traffic::core::Time ta(const artis::traffic::core::Time & /* t */) const override; \ }; #define DEFINE_STATE_MACHINE_TRANSITION_HEADER(TransitionClassName, StateMachineClassName) \ template \ struct TransitionClassName : StateMachineClassName::Transition_t { \ TransitionClassName( \ const std::shared_ptr> &machine) : \ StateMachineClassName::Transition_t(machine) {} #define ATTRIBUTE(Type, Var) Type Var; #define NO_ATTRIBUTE #define DEFINE_STATE_MACHINE_TRANSITION_FOOTER(Attribute) \ Attribute \ }; #define DEFINE_STATE_MACHINE_TRANSITION_ACTION_true void action(const artis::traffic::core::Time & /* t */, const artis::common::event::Value & /* value */) override; #define DEFINE_STATE_MACHINE_TRANSITION_ACTION_false #define DEFINE_STATE_MACHINE_TRANSITION_EVENT_true bool event(const artis::traffic::core::Time & /* t */, int event) override; #define DEFINE_STATE_MACHINE_TRANSITION_EVENT_false #define DEFINE_STATE_MACHINE_TRANSITION_GUARD_true bool guard(const artis::traffic::core::Time & /* t */, const artis::common::event::Value & /* value */) const override; #define DEFINE_STATE_MACHINE_TRANSITION_GUARD_false #define DEFINE_STATE_MACHINE_TRANSITION_NO_EVENT_true bool no_event() const override { return true; } #define DEFINE_STATE_MACHINE_TRANSITION_NO_EVENT_false #define DEFINE_STATE_MACHINE_TRANSITION_OUTPUT_true(StateMachineClassName) typename StateMachineClassName::Events output(const artis::traffic::core::Time & /* t */) const override; #define DEFINE_STATE_MACHINE_TRANSITION_OUTPUT_false(StateMachineClassName) #define DEFINE_STATE_MACHINE_TRANSITION(TransitionClassName, StateMachineClassName, Attribute, ActionMethod, EventMethod, GuardMethod, NoEventMethod, OutputMethod) \ DEFINE_STATE_MACHINE_TRANSITION_HEADER(TransitionClassName, StateMachineClassName) \ DEFINE_STATE_MACHINE_TRANSITION_ACTION_##ActionMethod \ DEFINE_STATE_MACHINE_TRANSITION_EVENT_##EventMethod \ DEFINE_STATE_MACHINE_TRANSITION_GUARD_##GuardMethod \ DEFINE_STATE_MACHINE_TRANSITION_NO_EVENT_##NoEventMethod \ DEFINE_STATE_MACHINE_TRANSITION_OUTPUT_##OutputMethod(StateMachineClassName) \ DEFINE_STATE_MACHINE_TRANSITION_FOOTER(Attribute) #endif //ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP