diff --git a/lib/include/srslte/common/fsm.h b/lib/include/srslte/common/fsm.h index 5d0486e9b..1263f6af9 100644 --- a/lib/include/srslte/common/fsm.h +++ b/lib/include/srslte/common/fsm.h @@ -31,7 +31,7 @@ #include #include -// Helper to print a type name for logging +//! Helper to print the name of a type for logging #if defined(__GNUC__) && !defined(__clang__) template std::string get_type_name() @@ -48,8 +48,16 @@ std::string get_type_name() } #endif +//! This version leverages type deduction. (e.g. get_type_name(var)) +template +std::string get_type_name(const T& t) +{ + return get_type_name(); +} + namespace srslte { +//! When there is no state transition struct same_state { }; @@ -60,26 +68,9 @@ struct state_name_visitor { template void operator()(State&& s) { - name = s.name(); + name = get_type_name(s); } - const char* name = "invalid state"; -}; - -template -struct variant_convert { - template - void operator()(State& s) - { - static_assert(not std::is_same::type, typename std::decay::type>::value, - "State cannot transition to itself.\n"); - if (p != nullptr) { - srslte::get(*v).exit(); - } - v->transit(std::move(s)); - srslte::get(*v).enter(); - } - TargetVariant* v; - PrevState* p; + std::string name = "invalid state"; }; struct fsm_helper { @@ -91,12 +82,51 @@ struct fsm_helper { template using disable_if_fsm_state = typename get_fsm_state_list::template disable_if_can_hold; + template + static auto call_init(FSM* f) -> decltype(f->derived()->do_init()) + { + f->derived()->do_init(); + } + static void call_init(...) {} + template + static auto call_enter(FSM* f, State* s) -> decltype(f->enter(*s)) + { + f->enter(*s); + call_init(s); + } + static void call_enter(...) {} + template + static auto call_exit(FSM* f, State* s) -> decltype(f->exit(*s)) + { + f->exit(*s); + } + static void call_exit(...) {} + + template + struct variant_convert { + variant_convert(FSM* f_, PrevState* p_) : f(f_), p(p_) {} + template + void operator()(State& s) + { + static_assert(not std::is_same::type, typename std::decay::type>::value, + "State cannot transition to itself.\n"); + call_exit(f, &srslte::get(f->states)); + f->states.transit(std::move(s)); + call_enter(f, &srslte::get(f->states)); + } + FSM* f; + PrevState* p; + }; + + template struct enter_visitor { + enter_visitor(FSM* f_) : f(f_) {} template void operator()(State&& s) { - s.do_enter(); + call_enter(f, &s); } + FSM* f; }; //! Stayed in same state @@ -109,7 +139,7 @@ struct fsm_helper { template static void handle_state_change(FSM* f, choice_t* s, PrevState* p) { - fsm_details::variant_convertstates), PrevState> visitor{.v = &f->states, .p = p}; + fsm_details::fsm_helper::variant_convert visitor{f, p}; s->visit(visitor); } //! Simple state transition in FSM @@ -117,11 +147,9 @@ struct fsm_helper { static enable_if_fsm_state handle_state_change(FSM* f, State* s, PrevState* p) { static_assert(not std::is_same::value, "State cannot transition to itself.\n"); - if (p != nullptr) { - srslte::get(f->states).do_exit(); - } + call_exit(f, &srslte::get(f->states)); f->states.transit(std::move(*s)); - srslte::get(f->states).do_enter(); + call_enter(f, &srslte::get(f->states)); } //! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM template @@ -129,7 +157,8 @@ struct fsm_helper { { static_assert(FSM::is_nested, "State is not present in the FSM list of valid states"); if (p != nullptr) { - srslte::get(f->states).do_exit(); + // srslte::get(f->states).do_exit(); + call_exit(f, &srslte::get(f->states)); } handle_state_change(f->parent_fsm()->derived(), s, static_cast(f)); } @@ -181,23 +210,9 @@ struct fsm_helper { } // namespace fsm_details -//! Base class for states and FSMs -class state_t -{ -public: - state_t() = default; - virtual const char* name() const = 0; - void do_enter() { enter(); } - void do_exit() { exit(); } - -protected: - virtual void enter() {} - virtual void exit() {} -}; - //! CRTP Class for all non-nested FSMs template -class fsm_t : public state_t +class fsm_t { protected: using base_t = fsm_t; @@ -206,6 +221,9 @@ protected: { public: using derived_t = Derived; + using Derived::do_init; + using Derived::enter; + using Derived::exit; using Derived::react; using Derived::states; using Derived::unhandled_event; @@ -219,10 +237,10 @@ public: struct state_list : public choice_t { using base_t = choice_t; template - state_list(Args&&... args) : base_t(std::forward(args)...) + state_list(fsm_t* f, Args&&... args) : base_t(std::forward(args)...) { if (not Derived::is_nested) { - fsm_details::fsm_helper::enter_visitor visitor{}; + fsm_details::fsm_helper::enter_visitor visitor{f->derived()}; this->visit(visitor); } } @@ -255,7 +273,7 @@ public: return srslte::get_if(derived()->states); } - const char* get_state_name() const + std::string get_state_name() const { fsm_details::state_name_visitor visitor{}; derived()->states.visit(visitor); @@ -279,13 +297,15 @@ protected: derived_view* derived() { return static_cast(this); } const derived_view* derived() const { return static_cast(this); } - void do_enter() + void do_init() { - enter(); - fsm_details::fsm_helper::enter_visitor visitor{}; + fsm_details::fsm_helper::enter_visitor visitor{derived()}; derived()->states.visit(visitor); } + void enter() {} + void exit() {} + template void unhandled_event(Event&& e) { @@ -327,6 +347,8 @@ public: protected: using parent_fsm_t = ParentFSM; + using fsm_t::enter; + using fsm_t::do_init; ParentFSM* fsm_ptr = nullptr; }; @@ -364,22 +386,11 @@ public: using fsm_t::trigger; // states - struct idle_st : public state_t { - const char* name() const final { return "idle"; } - idle_st(Derived* f_) : fsm(f_) {} - void exit() - { - fsm->launch_counter++; - fsm->log_h->info("Proc %s: Starting run no. %d\n", fsm->derived()->name(), fsm->launch_counter); - } - Derived* fsm; + struct idle_st { }; - struct complete_st : public srslte::state_t { - complete_st(Derived* fsm_, bool success_) : fsm(fsm_), success(success_) {} - const char* name() const final { return "complete"; } - void enter() { fsm->trigger(srslte::proc_complete_ev{success}); } - Derived* fsm; - bool success; + struct complete_st { + complete_st(bool success_) : success(success_) {} + bool success; }; explicit proc_fsm_t(srslte::log_ref log_) : fsm_t(log_) {} @@ -392,20 +403,13 @@ public: trigger(proc_launch_ev(std::forward(args)...)); } - // template - // void trigger(proc_launch_ev e) - // { - // if (not base_t::template is_in_state()) { - // log_h->error("Proc %s: Ignoring launch because procedure is already running\n", base_t::derived()->name()); - // return; - // } - // base_t::trigger(std::move(e)); - // } - // template - // void trigger(T&& t) - // { - // base_t::trigger(std::forward(t)); - // } +protected: + void exit(idle_st& s) + { + launch_counter++; + log_h->info("Starting run no. %d\n", launch_counter); + } + void enter(complete_st& s) { trigger(srslte::proc_complete_ev{s.success}); } private: int launch_counter = 0; diff --git a/lib/test/common/fsm_test.cc b/lib/test/common/fsm_test.cc index 41d38679d..2e311d359 100644 --- a/lib/test/common/fsm_test.cc +++ b/lib/test/common/fsm_test.cc @@ -36,48 +36,29 @@ public: uint32_t idle_enter_counter = 0, state1_enter_counter = 0; uint32_t foo_counter = 0; - const char* name() const override { return "fsm1"; } + // states + struct idle_st { + }; + struct state1 { + }; + fsm1(srslte::log_ref log_) : srslte::fsm_t(log_) {} - // idle state - struct idle_st : public srslte::state_t { - idle_st(fsm1* f_) : f(f_) { f->idle_enter_counter++; } - void enter() final { f->log_h->info("fsm1::%s::enter called\n", name()); } - void exit() final { f->log_h->info("fsm1::%s::exit called\n", name()); } - const char* name() const final { return "idle"; } - fsm1* f; - }; - - // simple state - class state1 : public srslte::state_t - { - public: - state1(fsm1* f_) : f(f_) { f->state1_enter_counter++; } - void enter() final { f->log_h->info("fsm1::%s::enter called\n", name()); } - void exit() final { f->log_h->info("fsm1::%s::exit called\n", name()); } - const char* name() const final { return "state1"; } - fsm1* f; - }; - // this state is another FSM class fsm2 : public srslte::nested_fsm_t { public: - struct state_inner : public srslte::state_t { - const char* name() const final { return "state_inner"; } - state_inner(fsm2* f_) : f(f_) {} - void enter() { f->log_h->info("fsm2::%s::enter called\n", name()); } - // void exit() final { f->log_h->info("fsm2::%s::exit called\n", name()); } - fsm2* f; + // states + struct state_inner { }; fsm2(fsm1* f_) : nested_fsm_t(f_) {} - ~fsm2() { log_h->info("%s being destroyed!", name()); } - void enter() final { log_h->info("%s::enter called\n", name()); } - void exit() { log_h->info("%s::exit called\n", name()); } - const char* name() const final { return "fsm2"; } + ~fsm2() { log_h->info("%s being destroyed!", get_type_name(*this).c_str()); } protected: + void enter(state_inner& s) { log_h->info("fsm1::%s::enter called\n", get_type_name().c_str()); } + void exit(state_inner& s) { log_h->info("fsm1::%s::exit called\n", get_type_name().c_str()); } + // FSM2 transitions auto react(state_inner& s, ev1 e) -> srslte::same_state; auto react(state_inner& s, ev2 e) -> state1; @@ -87,6 +68,20 @@ public: }; protected: + // enter/exit + template + void enter(State& s) + { + log_h->info("%s::enter called\n", get_type_name().c_str()); + } + template + void exit(State& s) + { + log_h->info("%s::exit called\n", get_type_name().c_str()); + } + void enter(idle_st& s); + void enter(state1& s); + // transitions auto react(idle_st& s, ev1 e) -> state1; auto react(state1& s, ev1 e) -> fsm2; @@ -95,9 +90,20 @@ protected: void foo(ev1 e) { foo_counter++; } // list of states - state_list states{idle_st{this}}; + state_list states{this, idle_st{}}; }; +void fsm1::enter(idle_st& s) +{ + log_h->info("%s::enter custom called\n", get_type_name(s).c_str()); + idle_enter_counter++; +} +void fsm1::enter(state1& s) +{ + log_h->info("%s::enter custom called\n", get_type_name(s).c_str()); + state1_enter_counter++; +} + // FSM event handlers auto fsm1::fsm2::react(state_inner& s, ev1 e) -> srslte::same_state { @@ -108,24 +114,24 @@ auto fsm1::fsm2::react(state_inner& s, ev1 e) -> srslte::same_state auto fsm1::fsm2::react(state_inner& s, ev2 e) -> state1 { log_h->info("fsm2::state_inner::react called\n"); - return state1{parent_fsm()}; + return {}; } auto fsm1::react(idle_st& s, ev1 e) -> state1 { - log_h->info("fsm1::%s::react called\n", s.name()); + log_h->info("%s::react called\n", get_type_name(s).c_str()); foo(e); - return state1{this}; + return {}; } auto fsm1::react(state1& s, ev1 e) -> fsm2 { - log_h->info("fsm1::%s::react called\n", s.name()); - return fsm2{this}; + log_h->info("%s::react called\n", get_type_name(s).c_str()); + return {this}; } auto fsm1::react(state1& s, ev2 e) -> srslte::choice_t { - log_h->info("fsm1::%s::react called\n", s.name()); - return idle_st{this}; + log_h->info("%s::react called\n", get_type_name(s).c_str()); + return idle_st{}; } // Static Checks @@ -154,39 +160,39 @@ int test_hsm() log_h->set_level(srslte::LOG_LEVEL_INFO); fsm1 f{log_h}; - TESTASSERT(std::string{f.name()} == "fsm1"); - TESTASSERT(std::string{f.get_state_name()} == "idle"); + TESTASSERT(f.idle_enter_counter == 1); + TESTASSERT(get_type_name(f) == "fsm1"); + TESTASSERT(f.get_state_name() == "fsm1::idle_st"); TESTASSERT(f.is_in_state()); TESTASSERT(f.foo_counter == 0); - TESTASSERT(f.idle_enter_counter == 1); // Moving Idle -> State1 ev1 e; f.trigger(e); - TESTASSERT(std::string{f.get_state_name()} == "state1"); + TESTASSERT(f.get_state_name() == "fsm1::state1"); TESTASSERT(f.is_in_state()); // Moving State1 -> fsm2 f.trigger(e); - TESTASSERT(std::string{f.get_state_name()} == "fsm2"); + TESTASSERT(f.get_state_name() == "fsm1::fsm2"); TESTASSERT(f.is_in_state()); - TESTASSERT(std::string{f.get_state()->get_state_name()} == "state_inner"); + TESTASSERT(f.get_state()->get_state_name() == "fsm1::fsm2::state_inner"); // Fsm2 does not listen to ev1 f.trigger(e); - TESTASSERT(std::string{f.get_state_name()} == "fsm2"); + TESTASSERT(std::string{f.get_state_name()} == "fsm1::fsm2"); TESTASSERT(f.is_in_state()); - TESTASSERT(std::string{f.get_state()->get_state_name()} == "state_inner"); + TESTASSERT(f.get_state()->get_state_name() == "fsm1::fsm2::state_inner"); // Moving fsm2 -> state1 f.trigger(ev2{}); - TESTASSERT(std::string{f.get_state_name()} == "state1"); + TESTASSERT(std::string{f.get_state_name()} == "fsm1::state1"); TESTASSERT(f.is_in_state()); TESTASSERT(f.state1_enter_counter == 2); // Moving state1 -> idle f.trigger(ev2{}); - TESTASSERT(std::string{f.get_state_name()} == "idle"); + TESTASSERT(std::string{f.get_state_name()} == "fsm1::idle_st"); TESTASSERT(f.is_in_state()); TESTASSERT(f.foo_counter == 1); TESTASSERT(f.idle_enter_counter == 2); @@ -203,14 +209,11 @@ struct procevent2 { struct proc1 : public srslte::proc_fsm_t { public: - struct procstate1 : public srslte::state_t { - const char* name() const final { return "procstate1"; } + struct procstate1 { }; proc1(srslte::log_ref log_) : base_t(log_) {} - const char* name() const final { return "proc1"; } - protected: // Transitions auto react(idle_st& s, srslte::proc_launch_ev ev) -> procstate1; @@ -232,17 +235,17 @@ auto proc1::react(idle_st& s, srslte::proc_launch_ev ev) -> procstate1 auto proc1::react(procstate1& s, procevent1 ev) -> complete_st { log_h->info("success!\n"); - return {this, true}; + return {true}; } auto proc1::react(procstate1& s, procevent2 ev) -> complete_st { log_h->info("failure!\n"); - return {this, false}; + return {false}; } auto proc1::react(complete_st& s, srslte::proc_complete_ev ev) -> idle_st { log_h->info("propagate results %s\n", s.success ? "success" : "failure"); - return {this}; + return {}; //{this}; } int test_fsm_proc()