Add tinyfsm, start converting core functions to an FSM-based event loop
This commit is contained in:
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* 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 */
|
||||
Reference in New Issue
Block a user