diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-05-19 21:21:27 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-05-19 21:21:27 +1000 |
| commit | a6ab1504058304012791281f9eb42c262745888f (patch) | |
| tree | f82379cd1e66a8ae2f1afbae5cf083a8ab7acc53 /lib/tinyfsm/include | |
| parent | b320a6a863cf1c10dc79254af41f573730935564 (diff) | |
| download | tangara-fw-a6ab1504058304012791281f9eb42c262745888f.tar.gz | |
Add tinyfsm, start converting core functions to an FSM-based event loop
Diffstat (limited to 'lib/tinyfsm/include')
| -rw-r--r-- | lib/tinyfsm/include/tinyfsm.hpp | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/lib/tinyfsm/include/tinyfsm.hpp b/lib/tinyfsm/include/tinyfsm.hpp new file mode 100644 index 00000000..5a11f8ae --- /dev/null +++ b/lib/tinyfsm/include/tinyfsm.hpp @@ -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 */ |
