|
@@ -0,0 +1,319 @@
|
|
|
+/**
|
|
|
+ * @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 <http://www.gnu.org/licenses/>.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP
|
|
|
+#define ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP
|
|
|
+
|
|
|
+#include <artis-star/common/event/Value.hpp>
|
|
|
+
|
|
|
+#include <map>
|
|
|
+#include <memory>
|
|
|
+#include <vector>
|
|
|
+
|
|
|
+namespace artis::addons::utils {
|
|
|
+
|
|
|
+template<class Time, class Types, class Parameters, class StateMachineTypes>
|
|
|
+class RootStateMachine;
|
|
|
+
|
|
|
+template<class Time, class Types>
|
|
|
+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 <InternalEvent> internals;
|
|
|
+ std::vector <ExternalEvent> 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<class Machine>
|
|
|
+ struct State : AbstractState {
|
|
|
+ std::shared_ptr <Machine> _machine;
|
|
|
+
|
|
|
+ State(int id, const std::shared_ptr <Machine> &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 = 0;
|
|
|
+
|
|
|
+ virtual bool no_event() const = 0;
|
|
|
+
|
|
|
+ virtual Events output(const typename Time::type & /* t */) const = 0;
|
|
|
+
|
|
|
+ virtual ~AbstractTransition() = default;
|
|
|
+ };
|
|
|
+
|
|
|
+ template<class Machine>
|
|
|
+ struct Transition : AbstractTransition {
|
|
|
+ std::shared_ptr <Machine> _machine;
|
|
|
+
|
|
|
+ Transition(const std::shared_ptr <Machine> &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 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<StateMachine<Types>> &machine) = 0;
|
|
|
+
|
|
|
+ const std::unique_ptr <AbstractState> ¤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<AbstractState>(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<AbstractTransition>(transition), next_state));
|
|
|
+ }
|
|
|
+
|
|
|
+ // without event
|
|
|
+ Events transition(const typename Time::type &t) {
|
|
|
+ typename std::map<int, _transitions_type>::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<std::unique_ptr<AbstractTransition>, 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<int, _transitions_type>::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<std::unique_ptr<AbstractTransition>, int> &e) {
|
|
|
+ if (e.first->guard(t) 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<int, _transitions_type>::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<std::unique_ptr<AbstractTransition>, int> &e) {
|
|
|
+ if (e.first->guard(t) 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<std::pair<std::unique_ptr < AbstractTransition>, int >> _transitions_type;
|
|
|
+
|
|
|
+ std::map<int, std::unique_ptr<AbstractState>> _states;
|
|
|
+ std::map<int, _transitions_type> _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 Time, class Types, class StateType>
|
|
|
+class StateMachine : public AbstractStateMachine<Time, Types> {
|
|
|
+public:
|
|
|
+ StateMachine(const typename Types::root_state_machine_type &root, StateType state)
|
|
|
+ : AbstractStateMachine<Time, Types>(root), _state(state) {}
|
|
|
+
|
|
|
+ virtual ~StateMachine() = default;
|
|
|
+
|
|
|
+ const StateType &state_() const { return _state; }
|
|
|
+
|
|
|
+ StateType &state_() { return _state; }
|
|
|
+
|
|
|
+protected:
|
|
|
+ StateType _state;
|
|
|
+};
|
|
|
+
|
|
|
+}
|
|
|
+#endif //ARTIS_STAR_ADDONS_UTILS_STATE_MACHINE_HPP
|