diff --git a/lib/include/srslte/common/fsm.h b/lib/include/srslte/common/fsm.h index ed1329870..5d0486e9b 100644 --- a/lib/include/srslte/common/fsm.h +++ b/lib/include/srslte/common/fsm.h @@ -31,9 +31,25 @@ #include #include +// Helper to print a type name for logging +#if defined(__GNUC__) && !defined(__clang__) +template +std::string get_type_name() +{ + static const char* s = __PRETTY_FUNCTION__; + static const char* pos1 = strchr(s, '=') + 2; + return std::string{pos1, strchr(pos1, ';')}; +} +#else +template +std::string get_type_name() +{ + return "anonymous"; +} +#endif + namespace srslte { -// using same_state = mpark::monostate; struct same_state { }; @@ -156,7 +172,7 @@ struct fsm_helper { s->trigger(std::move(ev)); } //! No trigger or react method found. Do nothing - void call_trigger2(...) {} + void call_trigger2(...) { f->unhandled_event(std::move(ev)); } FSM* f; Event ev; @@ -184,6 +200,7 @@ template class fsm_t : public state_t { protected: + using base_t = fsm_t; // get access to derived protected members from the base class derived_view : public Derived { @@ -191,6 +208,8 @@ protected: using derived_t = Derived; using Derived::react; using Derived::states; + using Derived::unhandled_event; + using Derived::base_t::unhandled_event; }; public: @@ -214,6 +233,8 @@ public: } }; + explicit fsm_t(srslte::log_ref log_) : log_h(log_) {} + // Push Events to FSM template void trigger(Ev&& e) @@ -248,6 +269,9 @@ public: return fsm_details::fsm_helper::get_fsm_state_list >::template can_hold_type(); } + void set_fsm_event_log_level(srslte::LOG_LEVEL_ENUM e) { fsm_event_log_level = e; } + srslte::log_ref get_log() const { return log_h; } + protected: friend struct fsm_details::fsm_helper; @@ -261,6 +285,30 @@ protected: fsm_details::fsm_helper::enter_visitor visitor{}; derived()->states.visit(visitor); } + + template + void unhandled_event(Event&& e) + { + switch (fsm_event_log_level) { + case LOG_LEVEL_DEBUG: + log_h->debug("Unhandled event caught: \"%s\"\n", get_type_name().c_str()); + break; + case LOG_LEVEL_INFO: + log_h->info("Unhandled event caught: \"%s\"\n", get_type_name().c_str()); + break; + case LOG_LEVEL_WARNING: + log_h->warning("Unhandled event caught: \"%s\"\n", get_type_name().c_str()); + break; + case LOG_LEVEL_ERROR: + log_h->error("Unhandled event caught: \"%s\"\n", get_type_name().c_str()); + break; + default: + break; + } + } + + srslte::log_ref log_h; + srslte::LOG_LEVEL_ENUM fsm_event_log_level = LOG_LEVEL_DEBUG; }; template @@ -271,7 +319,7 @@ public: using parent_t = ParentFSM; static const bool is_nested = true; - explicit nested_fsm_t(ParentFSM* parent_fsm_) : fsm_ptr(parent_fsm_) {} + explicit nested_fsm_t(ParentFSM* parent_fsm_) : fsm_t(parent_fsm_->get_log()), fsm_ptr(parent_fsm_) {} // Get pointer to outer FSM in case of HSM const parent_t* parent_fsm() const { return fsm_ptr; } @@ -301,6 +349,15 @@ class proc_fsm_t : public fsm_t { using fsm_type = Derived; using fsm_t::derived; + friend struct fsm_details::fsm_helper; + +protected: + using fsm_t::log_h; + using fsm_t::unhandled_event; + void unhandled_event(srslte::proc_launch_ev e) + { + log_h->warning("Unhandled event \"launch\" caught when procedure is already running\n"); + } public: using base_t = proc_fsm_t; @@ -325,12 +382,10 @@ public: bool success; }; - explicit proc_fsm_t(srslte::log_ref log_) : log_h(log_) {} + explicit proc_fsm_t(srslte::log_ref log_) : fsm_t(log_) {} bool is_running() const { return base_t::template is_in_state(); } - srslte::log_ref log_h; - template void launch(Args&&... args) { diff --git a/lib/test/common/fsm_test.cc b/lib/test/common/fsm_test.cc index 47156c975..41d38679d 100644 --- a/lib/test/common/fsm_test.cc +++ b/lib/test/common/fsm_test.cc @@ -22,8 +22,6 @@ #include "srslte/common/fsm.h" #include "srslte/common/test_common.h" -srslte::log_ref test_log{"TEST"}; - ///////////////////////////// // Events @@ -39,23 +37,26 @@ public: uint32_t foo_counter = 0; const char* name() const override { return "fsm1"; } + fsm1(srslte::log_ref log_) : srslte::fsm_t(log_) {} // idle state struct idle_st : public srslte::state_t { - idle_st(fsm1* f) { f->idle_enter_counter++; } - void enter() final { test_log->info("fsm1::%s::enter called\n", name()); } - void exit() final { test_log->info("fsm1::%s::exit called\n", name()); } + 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->state1_enter_counter++; } - void enter() final { test_log->info("fsm1::%s::enter called\n", name()); } - void exit() final { test_log->info("fsm1::%s::exit called\n", name()); } + 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 @@ -64,13 +65,16 @@ public: public: struct state_inner : public srslte::state_t { const char* name() const final { return "state_inner"; } - void enter() { test_log->info("fsm2::%s::enter called\n", name()); } - void exit() final { test_log->info("fsm2::%s::exit called\n", name()); } + 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; }; fsm2(fsm1* f_) : nested_fsm_t(f_) {} - void enter() final { test_log->info("%s::enter called\n", name()); } - void exit() { test_log->info("%s::exit called\n", name()); } + ~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"; } protected: @@ -79,7 +83,7 @@ public: auto react(state_inner& s, ev2 e) -> state1; // list of states - state_list states; + state_list states{this}; }; protected: @@ -97,30 +101,30 @@ protected: // FSM event handlers auto fsm1::fsm2::react(state_inner& s, ev1 e) -> srslte::same_state { - test_log->info("fsm2::state_inner::react called\n"); + log_h->info("fsm2::state_inner::react called\n"); return {}; } auto fsm1::fsm2::react(state_inner& s, ev2 e) -> state1 { - test_log->info("fsm2::state_inner::react called\n"); + log_h->info("fsm2::state_inner::react called\n"); return state1{parent_fsm()}; } auto fsm1::react(idle_st& s, ev1 e) -> state1 { - test_log->info("fsm1::%s::react called\n", s.name()); + log_h->info("fsm1::%s::react called\n", s.name()); foo(e); return state1{this}; } auto fsm1::react(state1& s, ev1 e) -> fsm2 { - test_log->info("fsm1::%s::react called\n", s.name()); + log_h->info("fsm1::%s::react called\n", s.name()); return fsm2{this}; } auto fsm1::react(state1& s, ev2 e) -> srslte::choice_t { - test_log->info("fsm1::%s::react called\n", s.name()); + log_h->info("fsm1::%s::react called\n", s.name()); return idle_st{this}; } @@ -145,9 +149,11 @@ static_assert(fsm1::can_hold_state(), "can hold state method faile int test_hsm() { - test_log->set_level(srslte::LOG_LEVEL_INFO); + srslte::log_ref log_h{"HSM"}; + log_h->prepend_string("HSM: "); + log_h->set_level(srslte::LOG_LEVEL_INFO); - fsm1 f; + fsm1 f{log_h}; TESTASSERT(std::string{f.name()} == "fsm1"); TESTASSERT(std::string{f.get_state_name()} == "idle"); TESTASSERT(f.is_in_state()); @@ -212,6 +218,9 @@ protected: auto react(procstate1& s, procevent2 ev) -> complete_st; auto react(complete_st& s, srslte::proc_complete_ev ev) -> idle_st; + // example of uncaught event handling + void unhandled_event(int e) { log_h->info("I dont know how to handle an \"int\" event\n"); } + state_list states{this}; }; @@ -238,10 +247,14 @@ auto proc1::react(complete_st& s, srslte::proc_complete_ev ev) -> idle_st int test_fsm_proc() { - proc1 proc{srslte::logmap::get("TEST")}; - int v = 2; + proc1 proc{srslte::logmap::get("PROC")}; + proc.get_log()->prepend_string("Proc1: "); + proc.get_log()->set_level(srslte::LOG_LEVEL_INFO); + proc.set_fsm_event_log_level(srslte::LOG_LEVEL_INFO); + int v = 2; proc.launch(&v); proc.launch(&v); + proc.trigger(5); proc.trigger(procevent1{}); proc.launch(&v); proc.trigger(procevent2{}); @@ -249,11 +262,16 @@ int test_fsm_proc() return SRSLTE_SUCCESS; } +/////////////////////////// + int main() { - srslte::logmap::get("TEST")->set_level(srslte::LOG_LEVEL_INFO); - // TESTASSERT(test_hsm() == SRSLTE_SUCCESS); + srslte::log_ref testlog{"TEST"}; + testlog->set_level(srslte::LOG_LEVEL_INFO); + TESTASSERT(test_hsm() == SRSLTE_SUCCESS); + testlog->info("TEST \"hsm\" finished successfully\n\n"); TESTASSERT(test_fsm_proc() == SRSLTE_SUCCESS); + testlog->info("TEST \"proc\" finished successfully\n\n"); return SRSLTE_SUCCESS; }