diff --git a/include/cpp_event_framework/Statemachine.hxx b/include/cpp_event_framework/Statemachine.hxx index 9ad4c1f..beeb544 100644 --- a/include/cpp_event_framework/Statemachine.hxx +++ b/include/cpp_event_framework/Statemachine.hxx @@ -448,6 +448,7 @@ public: { assert(impl_ != nullptr); // Most probably you forgot to call Init() current_state_ = &kInTransition; + initial_.clear(); EnterStatesFromDownTo(nullptr, initial); } @@ -713,33 +714,45 @@ private: return state->initial_; } + void ExitState(StatePtr state) + { + if (on_state_exit_ != nullptr) + { + on_state_exit_(*this, *state); + } + + if (state->on_exit_ != nullptr) + { + (impl_->*state->on_exit_)(); + } + } + void ExitStatesFromUpTo(StatePtr from, StatePtr top) { const auto* state = from; - while (state != top) + if (state == top) + { + ExitState(state); + } + else { - // Save history state - if (state->parent_ != nullptr) + while (state != top) { - if ((state->parent_->flags_ & EFlags::kHistory) != EFlags::kNone) + // Save history state + if (state->parent_ != nullptr) { - SetInitialState(state->parent_, state); + if ((state->parent_->flags_ & EFlags::kHistory) != EFlags::kNone) + { + SetInitialState(state->parent_, state); + } } - } - - if (on_state_exit_ != nullptr) - { - on_state_exit_(*this, *state); - } - if (state->on_exit_ != nullptr) - { - (impl_->*state->on_exit_)(); - } + ExitState(state); - state = state->parent_; - }; + state = state->parent_; + }; + } } void EnterState(StateRef state) const @@ -769,7 +782,10 @@ private: void EnterStatesFromDownTo(StatePtr top, StatePtr target) { - EnterStatesFromDownToRecursive(top, target->parent_); + if (top != target) + { + EnterStatesFromDownToRecursive(top, target->parent_); + } if (GetInitialState(target) != nullptr) { diff --git a/test/Statemachine_unittest.cxx b/test/Statemachine_unittest.cxx index e0782e7..5ddf482 100644 --- a/test/Statemachine_unittest.cxx +++ b/test/Statemachine_unittest.cxx @@ -25,6 +25,10 @@ class EvtTurnOff : public cpp_event_framework::NextSignal { }; +class EvtSelfTransition : public cpp_event_framework::NextSignal +{ +}; + class StatemachineImpl; class Fsm : public cpp_event_framework::Statemachine { @@ -177,6 +181,16 @@ class StatemachineImpl off_entry_called_ = false; CheckAllFalse(); + fsm_.React(EvtSelfTransition::MakeShared()); + assert(fsm_.CurrentState() == &Fsm::kOff); + assert(off_entry_called_ == true); + off_entry_called_ = false; + assert(off_exit_called_ == true); + off_exit_called_ = false; + assert(on_recall_event_called_ == true); + on_recall_event_called_ = false; + CheckAllFalse(); + fsm_.React(EvtTurnOn::MakeShared()); assert(off_exit_called_ == true); assert(on_entry_called_ == true); @@ -207,10 +221,18 @@ class StatemachineImpl assert(fsm_.CurrentState() == &Fsm::kRedYellow); CheckAllFalse(); + fsm_.React(EvtSelfTransition::MakeShared()); + assert(fsm_.CurrentState() == &Fsm::kRedYellow); + CheckAllFalse(); + fsm_.React(EvtGoGreen::MakeShared()); assert(fsm_.CurrentState() == &Fsm::kGreen); CheckAllFalse(); + fsm_.React(EvtGoRed::MakeShared()); + assert(fsm_.CurrentState() == &Fsm::kRed); + CheckAllFalse(); + fsm_.React(EvtTurnOff::MakeShared()); assert(on_exit_called_ == true); assert(off_entry_called_ == true); @@ -267,6 +289,9 @@ Fsm::Transition Fsm::FsmOffHandler(ImplPtr /*impl*/, Event event) case EvtGoYellow::kId: // fall through case EvtGoRed::kId: return DeferEvent(); + case EvtSelfTransition::kId: + // Self transition, entry + exit must be called + return TransitionTo(kOff); default: return UnhandledEvent(); } @@ -280,6 +305,8 @@ Fsm::Transition Fsm::FsmOnHandler(ImplPtr /*impl*/, Event event) return TransitionTo(kOff); case EvtTurnOn::kId: return NoTransition(); + case EvtGoRed::kId: + return TransitionTo(kRed); default: return UnhandledEvent(); } @@ -293,6 +320,8 @@ Fsm::Transition Fsm::FsmGreenHandler(ImplPtr /*impl*/, Event event) return TransitionTo(kYellow); case EvtGoGreen::kId: return NoTransition(); + case EvtSelfTransition::kId: + return TransitionTo(kGreen); default: return UnhandledEvent(); } @@ -308,6 +337,8 @@ Fsm::Transition Fsm::FsmYellowHandler(ImplPtr /*impl*/, Event event) return TransitionTo(kRed, kYellowRedTransitionActions); case EvtGoYellow::kId: return NoTransition(); + case EvtSelfTransition::kId: + return TransitionTo(kYellow); default: return UnhandledEvent(); } @@ -321,6 +352,8 @@ Fsm::Transition Fsm::FsmRedHandler(ImplPtr /*impl*/, Event event) return TransitionTo(kRedYellow); case EvtGoRed::kId: return NoTransition(); + case EvtSelfTransition::kId: + return TransitionTo(kRed); default: return UnhandledEvent(); } @@ -334,6 +367,8 @@ Fsm::Transition Fsm::FsmRedYellowHandler(ImplPtr /*impl*/, Event event) return TransitionTo(kGreen, &Fsm::Impl::Walk); case EvtGoYellow::kId: return NoTransition(); + case EvtSelfTransition::kId: + return TransitionTo(kRedYellow); default: return UnhandledEvent(); }