From 9ba658460e350ebfed72ef6b0cad1a03ae7b6b69 Mon Sep 17 00:00:00 2001 From: Michael Haberler Date: Sun, 2 Jan 2022 23:17:34 +0100 Subject: [PATCH 1/2] FSM.add_transition(): add optional predicate function argument the transition will fire only iff predicate returns true. Default is true. --- Fsm.cpp | 98 +++++++++++++++++++++++++++++---------------------------- Fsm.h | 46 +++++++++++++++------------ 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/Fsm.cpp b/Fsm.cpp index 9b932ca..6ad86a5 100644 --- a/Fsm.cpp +++ b/Fsm.cpp @@ -17,24 +17,24 @@ State::State(void (*on_enter)(), void (*on_state)(), void (*on_exit)()) -: on_enter(on_enter), - on_state(on_state), - on_exit(on_exit) + : on_enter(on_enter), + on_state(on_state), + on_exit(on_exit) { } -Fsm::Fsm(State* initial_state) -: m_current_state(initial_state), - m_transitions(NULL), - m_num_transitions(0), - m_num_timed_transitions(0), - m_initialized(false) +Fsm::Fsm(State *initial_state) + : m_current_state(initial_state), + m_transitions(NULL), + m_num_transitions(0), + m_num_timed_transitions(0), + m_initialized(false) { } -Fsm::~Fsm() +Fsm::~Fsm() { free(m_transitions); free(m_timed_transitions); @@ -43,64 +43,63 @@ Fsm::~Fsm() } -void Fsm::add_transition(State* state_from, State* state_to, int event, - void (*on_transition)()) -{ +void Fsm::add_transition(State *state_from, State *state_to, int event, + void (*on_transition)(), predicate precondition) { if (state_from == NULL || state_to == NULL) return; Transition transition = Fsm::create_transition(state_from, state_to, event, - on_transition); + on_transition, precondition); m_transitions = (Transition*) realloc(m_transitions, (m_num_transitions + 1) - * sizeof(Transition)); + * sizeof(Transition)); m_transitions[m_num_transitions] = transition; m_num_transitions++; } - -void Fsm::add_timed_transition(State* state_from, State* state_to, - unsigned long interval, void (*on_transition)()) -{ +void Fsm::add_timed_transition(State *state_from, State *state_to, + unsigned long interval, void (*on_transition)(), + predicate precondition) { if (state_from == NULL || state_to == NULL) return; Transition transition = Fsm::create_transition(state_from, state_to, 0, - on_transition); + on_transition, precondition); TimedTransition timed_transition; timed_transition.transition = transition; timed_transition.start = 0; timed_transition.interval = interval; - m_timed_transitions = (TimedTransition*) realloc( + m_timed_transitions = (TimedTransition *)realloc( m_timed_transitions, (m_num_timed_transitions + 1) * sizeof(TimedTransition)); m_timed_transitions[m_num_timed_transitions] = timed_transition; m_num_timed_transitions++; } - -Fsm::Transition Fsm::create_transition(State* state_from, State* state_to, - int event, void (*on_transition)()) -{ +Fsm::Transition Fsm::create_transition(State *state_from, State *state_to, + int event, void (*on_transition)(), + predicate precondition) { Transition t; t.state_from = state_from; t.state_to = state_to; t.event = event; t.on_transition = on_transition; + t.precondition = precondition; return t; } void Fsm::trigger(int event) { - if (m_initialized) + if (m_initialized) { // Find the transition with the current state and given event. - for (int i = 0; i < m_num_transitions; ++i) + for (int i = 0; i < m_num_transitions; ++i) { if (m_transitions[i].state_from == m_current_state && - m_transitions[i].event == event) - { + m_transitions[i].event == event && + m_transitions[i].precondition(m_current_state, + m_transitions[i].state_to)) { Fsm::make_transition(&(m_transitions[i])); return; } @@ -108,20 +107,22 @@ void Fsm::trigger(int event) } } -void Fsm::check_timed_transitions() +void Fsm::check_timed_transitions() { - for (int i = 0; i < m_num_timed_transitions; ++i) + for (int i = 0; i < m_num_timed_transitions; ++i) { - TimedTransition* transition = &m_timed_transitions[i]; - if (transition->transition.state_from == m_current_state) + TimedTransition *transition = &m_timed_transitions[i]; + if (transition->transition.state_from == m_current_state) { - if (transition->start == 0) + if (transition->start == 0) { transition->start = millis(); - } - else{ + } + else { unsigned long now = millis(); - if (now - transition->start >= transition->interval) + if ((now - transition->start >= transition->interval) && + transition->transition.precondition( + m_current_state, transition->transition.state_to)) { Fsm::make_transition(&(transition->transition)); transition->start = 0; @@ -131,25 +132,25 @@ void Fsm::check_timed_transitions() } } -void Fsm::run_machine() +void Fsm::run_machine() { // first run must exec first state "on_enter" - if (!m_initialized) + if (!m_initialized) { m_initialized = true; if (m_current_state->on_enter != NULL) m_current_state->on_enter(); } - + if (m_current_state->on_state != NULL) m_current_state->on_state(); - + Fsm::check_timed_transitions(); } -void Fsm::make_transition(Transition* transition) +void Fsm::make_transition(Transition *transition) { - + // Execute the handlers in the correct order. if (transition->state_from->on_exit != NULL) transition->state_from->on_exit(); @@ -159,16 +160,17 @@ void Fsm::make_transition(Transition* transition) if (transition->state_to->on_enter != NULL) transition->state_to->on_enter(); - + m_current_state = transition->state_to; - //Initialice all timed transitions from m_current_state + // Initialice all timed transitions from m_current_state unsigned long now = millis(); - for (int i = 0; i < m_num_timed_transitions; ++i) + for (int i = 0; i < m_num_timed_transitions; ++i) { - TimedTransition* ttransition = &m_timed_transitions[i]; + TimedTransition *ttransition = &m_timed_transitions[i]; if (ttransition->transition.state_from == m_current_state) ttransition->start = now; } - } + +bool _enabled(const State *from, const State *to) { return true; }; diff --git a/Fsm.h b/Fsm.h index afbb3dd..ac200ec 100644 --- a/Fsm.h +++ b/Fsm.h @@ -18,13 +18,13 @@ #if defined(ARDUINO) && ARDUINO >= 100 - #include +#include #else - #include +#include #endif -struct State +struct State { State(void (*on_enter)(), void (*on_state)(), void (*on_exit)()); void (*on_enter)(); @@ -32,18 +32,23 @@ struct State void (*on_exit)(); }; +bool _enabled(const State *from, const State *to); -class Fsm +typedef bool (*predicate)(const State *, const State *); + +class Fsm { public: - Fsm(State* initial_state); + Fsm(State *initial_state); ~Fsm(); - void add_transition(State* state_from, State* state_to, int event, - void (*on_transition)()); + void add_transition(State *state_from, State *state_to, int event, + void (*on_transition)(), + predicate precondition = _enabled); - void add_timed_transition(State* state_from, State* state_to, - unsigned long interval, void (*on_transition)()); + void add_timed_transition(State *state_from, State *state_to, + unsigned long interval, void (*on_transition)(), + predicate precondition = _enabled); void check_timed_transitions(); @@ -51,32 +56,33 @@ class Fsm void run_machine(); private: - struct Transition + struct Transition { - State* state_from; - State* state_to; + State *state_from; + State *state_to; int event; void (*on_transition)(); - + predicate precondition; }; - struct TimedTransition + struct TimedTransition { Transition transition; unsigned long start; unsigned long interval; }; - static Transition create_transition(State* state_from, State* state_to, - int event, void (*on_transition)()); + static Transition create_transition(State *state_from, State *state_to, + int event, void (*on_transition)(), + predicate precondition); - void make_transition(Transition* transition); + void make_transition(Transition *transition); private: - State* m_current_state; - Transition* m_transitions; + State *m_current_state; + Transition *m_transitions; int m_num_transitions; - TimedTransition* m_timed_transitions; + TimedTransition *m_timed_transitions; int m_num_timed_transitions; bool m_initialized; }; From 7ec8dc2fc77ff842c5f78a2fc3fcd9d6503d6ff9 Mon Sep 17 00:00:00 2001 From: Michael Haberler Date: Sun, 2 Jan 2022 23:37:15 +0100 Subject: [PATCH 2/2] examples/predicate: demonstrate transition predicates a transition guarded with a predicate will only fire if the predicate returns true. In this example, transitions will start firing only after 5s post startup. --- .../light_switch_with_predicate.ino | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 examples/using_predicates/light_switch_with_predicate.ino diff --git a/examples/using_predicates/light_switch_with_predicate.ino b/examples/using_predicates/light_switch_with_predicate.ino new file mode 100644 index 0000000..ff2e42f --- /dev/null +++ b/examples/using_predicates/light_switch_with_predicate.ino @@ -0,0 +1,67 @@ +#include "Fsm.h" + +// State machine variables +#define FLIP_LIGHT_SWITCH 1 + +State state_light_on(on_light_on_enter, NULL, &on_light_on_exit); +State state_light_off(on_light_off_enter, NULL, &on_light_off_exit); +Fsm fsm(&state_light_off); + +bool transition_enabled(const State *, const State *) +{ + return millis() > 5000; +} + +// Transition callback functions +void on_light_on_enter() +{ + Serial.println("Entering LIGHT_ON"); +} + +void on_light_on_exit() +{ + Serial.println("Exiting LIGHT_ON"); +} + +void on_light_off_enter() +{ + Serial.println("Entering LIGHT_OFF"); +} + +void on_light_off_exit() +{ + Serial.println("Exiting LIGHT_OFF"); +} + +void on_trans_light_on_light_off() +{ + Serial.println("Transitioning from LIGHT_ON to LIGHT_OFF"); +} + +void on_trans_light_off_light_on() +{ + Serial.println("Transitioning from LIGHT_OFF to LIGHT_ON"); +} + +// standard arduino functions +void setup() +{ + Serial.begin(9600); + + fsm.add_transition(&state_light_on, &state_light_off, + FLIP_LIGHT_SWITCH, + &on_trans_light_on_light_off); + fsm.add_transition(&state_light_off, &state_light_on, + FLIP_LIGHT_SWITCH, + &on_trans_light_off_light_on, + transition_enabled); +} + +void loop() +{ + // No "fsm.run_machine()" call needed as no "on_state" funcions or timmed transitions exists + delay(2000); + fsm.trigger(FLIP_LIGHT_SWITCH); + delay(2000); + fsm.trigger(FLIP_LIGHT_SWITCH); +}