You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
7.2 KiB
251 lines
7.2 KiB
/*
|
|
* TinyFSM - Tiny Finite State Machine Processor
|
|
*
|
|
* Copyright (c) 2012-2022 Axel Burri
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
*/
|
|
|
|
/* ---------------------------------------------------------------------
|
|
* Version: 0.3.3
|
|
*
|
|
* API documentation: see "../doc/50-API.md"
|
|
*
|
|
* The official TinyFSM website is located at:
|
|
* https://digint.ch/tinyfsm/
|
|
*
|
|
* Author:
|
|
* Axel Burri <axel@tty0.ch>
|
|
* ---------------------------------------------------------------------
|
|
*/
|
|
|
|
#ifndef TINYFSM_HPP_INCLUDED
|
|
#define TINYFSM_HPP_INCLUDED
|
|
|
|
#ifndef TINYFSM_NOSTDLIB
|
|
#include <type_traits>
|
|
#endif
|
|
|
|
// #include <iostream>
|
|
// #define DBG(str) do { std::cerr << str << std::endl; } while( false )
|
|
// DBG("*** dbg_example *** " << __PRETTY_FUNCTION__);
|
|
|
|
namespace tinyfsm
|
|
{
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
struct Event { };
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
#ifdef TINYFSM_NOSTDLIB
|
|
// remove dependency on standard library (silent fail!).
|
|
// useful in conjunction with -nostdlib option, e.g. if your compiler
|
|
// does not provide a standard library.
|
|
// NOTE: this silently disables all static_assert() calls below!
|
|
template<typename F, typename S>
|
|
struct is_same_fsm { static constexpr bool value = true; };
|
|
#else
|
|
// check if both fsm and state class share same fsmtype
|
|
template<typename F, typename S>
|
|
struct is_same_fsm : std::is_same< typename F::fsmtype, typename S::fsmtype > { };
|
|
#endif
|
|
|
|
template<typename S>
|
|
struct _state_instance
|
|
{
|
|
using value_type = S;
|
|
using type = _state_instance<S>;
|
|
static S value;
|
|
};
|
|
|
|
template<typename S>
|
|
typename _state_instance<S>::value_type _state_instance<S>::value;
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
template<typename F>
|
|
class Fsm
|
|
{
|
|
public:
|
|
|
|
using fsmtype = Fsm<F>;
|
|
using state_ptr_t = F *;
|
|
|
|
static state_ptr_t current_state_ptr;
|
|
|
|
// public, leaving ability to access state instance (e.g. on reset)
|
|
template<typename S>
|
|
static constexpr S & state(void) {
|
|
static_assert(is_same_fsm<F, S>::value, "accessing state of different state machine");
|
|
return _state_instance<S>::value;
|
|
}
|
|
|
|
template<typename S>
|
|
static constexpr bool is_in_state(void) {
|
|
static_assert(is_same_fsm<F, S>::value, "accessing state of different state machine");
|
|
return current_state_ptr == &_state_instance<S>::value;
|
|
}
|
|
|
|
/// state machine functions
|
|
public:
|
|
|
|
// explicitely specialized in FSM_INITIAL_STATE macro
|
|
static void set_initial_state();
|
|
|
|
static void reset() { };
|
|
|
|
static void enter() {
|
|
current_state_ptr->entry();
|
|
}
|
|
|
|
static void start() {
|
|
set_initial_state();
|
|
enter();
|
|
}
|
|
|
|
template<typename E>
|
|
static void dispatch(E const & event) {
|
|
current_state_ptr->react(event);
|
|
}
|
|
|
|
|
|
/// state transition functions
|
|
protected:
|
|
|
|
template<typename S>
|
|
void transit(void) {
|
|
static_assert(is_same_fsm<F, S>::value, "transit to different state machine");
|
|
current_state_ptr->exit();
|
|
current_state_ptr = &_state_instance<S>::value;
|
|
current_state_ptr->entry();
|
|
}
|
|
|
|
template<typename S, typename ActionFunction>
|
|
void transit(ActionFunction action_function) {
|
|
static_assert(is_same_fsm<F, S>::value, "transit to different state machine");
|
|
current_state_ptr->exit();
|
|
// NOTE: do not send events in action_function definisions.
|
|
action_function();
|
|
current_state_ptr = &_state_instance<S>::value;
|
|
current_state_ptr->entry();
|
|
}
|
|
|
|
template<typename S, typename ActionFunction, typename ConditionFunction>
|
|
void transit(ActionFunction action_function, ConditionFunction condition_function) {
|
|
if(condition_function()) {
|
|
transit<S>(action_function);
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename F>
|
|
typename Fsm<F>::state_ptr_t Fsm<F>::current_state_ptr;
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
template<typename... FF>
|
|
struct FsmList;
|
|
|
|
template<> struct FsmList<> {
|
|
static void set_initial_state() { }
|
|
static void reset() { }
|
|
static void enter() { }
|
|
template<typename E>
|
|
static void dispatch(E const &) { }
|
|
};
|
|
|
|
template<typename F, typename... FF>
|
|
struct FsmList<F, FF...>
|
|
{
|
|
using fsmtype = Fsm<F>;
|
|
|
|
static void set_initial_state() {
|
|
fsmtype::set_initial_state();
|
|
FsmList<FF...>::set_initial_state();
|
|
}
|
|
|
|
static void reset() {
|
|
F::reset();
|
|
FsmList<FF...>::reset();
|
|
}
|
|
|
|
static void enter() {
|
|
fsmtype::enter();
|
|
FsmList<FF...>::enter();
|
|
}
|
|
|
|
static void start() {
|
|
set_initial_state();
|
|
enter();
|
|
}
|
|
|
|
template<typename E>
|
|
static void dispatch(E const & event) {
|
|
fsmtype::template dispatch<E>(event);
|
|
FsmList<FF...>::template dispatch<E>(event);
|
|
}
|
|
};
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
template<typename... SS> struct StateList;
|
|
template<> struct StateList<> {
|
|
static void reset() { }
|
|
};
|
|
template<typename S, typename... SS>
|
|
struct StateList<S, SS...>
|
|
{
|
|
static void reset() {
|
|
_state_instance<S>::value = S();
|
|
StateList<SS...>::reset();
|
|
}
|
|
};
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
template<typename F>
|
|
struct MooreMachine : tinyfsm::Fsm<F>
|
|
{
|
|
virtual void entry(void) { }; /* entry actions in some states */
|
|
void exit(void) { }; /* no exit actions */
|
|
};
|
|
|
|
template<typename F>
|
|
struct MealyMachine : tinyfsm::Fsm<F>
|
|
{
|
|
// input actions are modeled in react():
|
|
// - conditional dependent of event type or payload
|
|
// - transit<>(ActionFunction)
|
|
void entry(void) { }; /* no entry actions */
|
|
void exit(void) { }; /* no exit actions */
|
|
};
|
|
|
|
} /* namespace tinyfsm */
|
|
|
|
|
|
#define FSM_INITIAL_STATE(_FSM, _STATE) \
|
|
namespace tinyfsm { \
|
|
template<> void Fsm< _FSM >::set_initial_state(void) { \
|
|
current_state_ptr = &_state_instance< _STATE >::value; \
|
|
} \
|
|
}
|
|
|
|
#endif /* TINYFSM_HPP_INCLUDED */
|
|
|