/**
* @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 = 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 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) 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) 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 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