summaryrefslogtreecommitdiff
path: root/lib/tinyfsm/include
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-05-19 21:21:27 +1000
committerjacqueline <me@jacqueline.id.au>2023-05-19 21:21:27 +1000
commita6ab1504058304012791281f9eb42c262745888f (patch)
treef82379cd1e66a8ae2f1afbae5cf083a8ab7acc53 /lib/tinyfsm/include
parentb320a6a863cf1c10dc79254af41f573730935564 (diff)
downloadtangara-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.hpp251
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 */