diff --git a/lib/include/srslte/common/fsm.h b/lib/include/srslte/common/fsm.h index 4bd7530b9..d59cc22d3 100644 --- a/lib/include/srslte/common/fsm.h +++ b/lib/include/srslte/common/fsm.h @@ -51,7 +51,7 @@ struct filter_transition_type > { using type = type_list<>; }; -//! Visitor to get a state's name string +//! Visitor to get current state's name string struct state_name_visitor { template void operator()(State&& s) @@ -91,184 +91,180 @@ using enable_if_subfsm = typename std::enable_if()>::type; template using disable_if_subfsm = typename std::enable_if()>::type; -struct fsm_helper { - //! Metafunction to determine if FSM can hold given State type - template - using fsm_state_list_type = decltype(std::declval().states); - template - using fsm_transitions = typename FSM::derived_view::transitions; +//! Metafunction to determine if FSM can hold given State type +template +using fsm_state_list_type = decltype(std::declval().states); +template +using fsm_transitions = typename FSM::derived_view::transitions; - //! Call FSM/State enter method. If subfsm, call enter of subfsm init state as well - template - static enable_if_subfsm call_enter(FSM* f, State* s) +template +using state_enter_t = decltype(std::declval().enter(std::declval())); +template +using state_exit_t = decltype(std::declval().exit(std::declval())); + +//! Find State in FSM recursively (e.g. find State in FSM,FSM::parentFSM,FSM::parentFSM::parentFSM,...) +template +static auto get_state_recursive(FSM* f) -> enable_if_fsm_state +{ + return &f->states.template get_unchecked(); +} + +struct derived_access { + template + static typename T::derived_view* get(T* f) + { + return f->derived(); + } + template + static typename T::parent_t::derived_view* get_parent(T* f) + { + return f->parent_fsm()->derived(); + } +}; + +template +static auto get_state_recursive(FSM* f) -> disable_if_fsm_state +{ + static_assert(FSM::is_nested, "State is not present in the FSM list of valid states"); + return get_state_recursive(derived_access::get_parent(f)); +} + +//! Helper type for FSM state-related operations +template +struct state_traits { + static_assert(FSM::template can_hold_state(), "FSM type does not hold provided State\n"); + using state_t = State; + using is_subfsm = std::integral_constant()>; + using has_enter = type_utils::is_detected; + using has_exit = type_utils::is_detected; + + //! enter new state. enter is called recursively for subFSMs + static void enter_state(FSM* f, State* s) { enter_(f, s, is_subfsm{}); } + //! Change state. If DestState is not a state of FSM, call same function for parentFSM recursively + template + static enable_if_fsm_state transit_state(FSM* f) + { + exit_if_exists_(f, &f->states.template get_unchecked(), has_exit{}); + f->states.template transit(); + state_traits::enter_state(f, &f->states.template get_unchecked()); + } + template + static disable_if_fsm_state transit_state(FSM* f) + { + exit_if_exists_(f, &f->states.template get_unchecked(), has_exit{}); + state_traits::template transit_state( + derived_access::get_parent(f)); + } + +private: + //! In case of State is a subFSM + static void enter_(FSM* f, State* s, std::true_type) { using init_type = typename fsm_state_list_type::init_state_t; // set default FSM type - s->derived()->states.template transit(); + derived_access::get(s)->states.template transit(); // call FSM enter function - f->enter(*s); + enter_if_exists_(f, s, has_enter{}); // call initial substate enter - call_enter(s->derived(), &s->derived()->states.template get_unchecked()); + state_traits::enter_state( + derived_access::get(s), &derived_access::get(s)->states.template get_unchecked()); } + //! In case of State is basic state + static void enter_(FSM* f, State* s, std::false_type) { enter_if_exists_(f, s, has_enter{}); } + static void enter_if_exists_(FSM* f, State* s, std::true_type) { f->enter(*s); } + static void enter_if_exists_(FSM* f, State* s, std::false_type) {} + static void exit_if_exists_(FSM* f, State* s, std::true_type) { f->exit(*s); } + static void exit_if_exists_(FSM* f, State* s, std::false_type) {} +}; - template - static disable_if_subfsm call_enter(FSM* f, State* s) +//! Trigger Event reaction for the first Row for which the Guard passes +template +struct apply_first_guard_pass; + +template +struct apply_first_guard_pass > { + template + static bool trigger(FSM* f, SrcState& s, const typename First::event_t& ev) { - f->enter(*s); - } + using src_state = SrcState; + using dest_state = typename First::dest_state_t; + using event_type = typename First::event_t; - //! Simple state transition in FSM - template - static auto handle_state_transition(FSM* f) -> enable_if_fsm_state - { - f->exit(f->states.template get_unchecked()); - f->states.template transit(); - call_enter(f, &f->states.template get_unchecked()); - } - - //! State not present in current FSM. Attempt state transition in parent FSM in the case of NestedFSM - template - static auto handle_state_transition(FSM* f) -> disable_if_fsm_state - { - static_assert(FSM::is_nested, "State is not present in the FSM list of valid states"); - f->exit(f->states.template get_unchecked()); - handle_state_transition( - f->parent_fsm()->derived()); - } - - template - static auto get_state_recursive(FSM* f) -> enable_if_fsm_state - { - return &f->states.template get_unchecked(); - } - - template - static auto get_state_recursive(FSM* f) -> disable_if_fsm_state - { - static_assert(FSM::is_nested, "State is not present in the FSM list of valid states"); - return get_state_recursive(f->parent_fsm()->derived()); - } - - //! Trigger in first Row for which the Guard passes - template - struct apply_first_guard_pass; - - template - struct apply_first_guard_pass > { - template - static bool trigger(FSM* f, SrcState& s, const typename First::event_t& ev) - { - using src_state = SrcState; - using dest_state = typename First::dest_state_t; - using event_type = typename First::event_t; - - bool triggered = First::react(f, s, ev); - if (triggered) { - // Log Transition - if (std::is_same::value) { - f->log_h->info("FSM \"%s\": Event \"%s\" updated state \"%s\"\n", - get_type_name().c_str(), - get_type_name().c_str(), - get_type_name().c_str()); - } else { - f->log_h->info("FSM \"%s\": Transition %s-->%s (cause: %s)", - get_type_name().c_str(), - get_type_name().c_str(), - get_type_name().c_str(), - get_type_name().c_str()); - // Apply state change operations - handle_state_transition(f); - } - return true; + bool triggered = First::react(f, s, ev); + if (triggered) { + // Log Transition + if (std::is_same::value) { + f->log_fsm_activity("FSM \"%s\": Event \"%s\" updated state \"%s\"\n", + get_type_name().c_str(), + get_type_name().c_str(), + get_type_name().c_str()); + } else { + f->log_fsm_activity("FSM \"%s\": Transition detected - %s -> %s (cause: %s)", + get_type_name().c_str(), + get_type_name().c_str(), + get_type_name().c_str(), + get_type_name().c_str()); + // Apply state change operations + state_traits::template transit_state(f); } - return apply_first_guard_pass >::trigger(f, s, ev); + return true; } + return apply_first_guard_pass >::trigger(f, s, ev); + } +}; - // template - // bool test_react(FSM *f, SrcState &s, const typename First::event_t &ev) { - // using dest_state = typename First::dest_state_t; - // if (First::guard == nullptr or (f->*First::guard)(ev)) { - // // Guard Passed. Apply react method - // dest_state *d = get_state_recursive(f); - // if (First::react != nullptr) { - // (f->*First::react)(*d, ev); - // } - // return true; - // } - // return false; - // } - // - // template<> - // bool - // test_react(FSM *f, typename First::src_state_t &s, const typename First::event_t - // &ev) { - // using dest_state = typename First::dest_state_t; - // - // if (First::guard == nullptr or (f->*First::guard)(s, ev)) { - // // Guard Passed. Apply react method - // dest_state *d = get_state_recursive(f); - // if (First::react != nullptr) { - // (f->*First::react)(s, *d, ev); - // } - // return true; - // } - // return false; - // } - }; +template +struct apply_first_guard_pass > { + template + static bool trigger(FSM* f, SrcState& s, const Event& ev) + { + f->log_fsm_activity("FSM \"%s\": Unhandled event caught: \"%s\"\n", + get_type_name().c_str(), + get_type_name().c_str()); + return false; + } +}; - template - struct apply_first_guard_pass > { - template - static bool trigger(FSM* f, SrcState& s, const Event& ev) - { - f->log_fsm_activity("FSM \"%s\": Unhandled event caught: \"%s\"\n", - get_type_name().c_str(), - get_type_name().c_str()); - return false; - } - }; +//! Trigger Event, that will result in a state transition +template +struct trigger_visitor { + using event_t = typename std::decay::type; - //! Trigger Event, that will result in a state transition - template - struct trigger_visitor { - using event_t = typename std::decay::type; + trigger_visitor(FSM* f_, Event&& ev_) : f(f_), ev(std::forward(ev_)) {} - trigger_visitor(FSM* f_, Event&& ev_) : f(f_), ev(std::forward(ev_)) {} + /** + * @brief Trigger visitor callback for the current state. + * @description tries to find an fsm::trigger method in case the current state is a subfsm. If it does not + * find it, searches for a react(current_state&, dest_state&, event) method at the current level + * Stores True in "result" if state changed. False otherwise + */ + template + disable_if_subfsm operator()(CurrentState& s) + { + result = call_react(s); + } - /** - * @brief Trigger visitor callback for the current state. - * @description tries to find an fsm::trigger method in case the current state is a subfsm. If it does not - * find it, searches for a react(current_state&, dest_state&, event) method at the current level - * Stores True in "result" if state changed. False otherwise - */ - template - disable_if_subfsm operator()(CurrentState& s) - { + template + enable_if_subfsm operator()(CurrentState& s) + { + // Enter here for SubFSMs + result = s.trigger(std::forward(ev)); + if (not result) { result = call_react(s); } + } - template - enable_if_subfsm operator()(CurrentState& s) - { - // Enter here for SubFSMs - result = s.trigger(std::forward(ev)); - if (not result) { - result = call_react(s); - } - } + template + bool call_react(SrcState& s) + { + using trigger_list = + typename filter_transition_type::type; + return apply_first_guard_pass::trigger(f, s, ev); + } - template - bool call_react(SrcState& s) - { - using trigger_list = - typename filter_transition_type::type; - return apply_first_guard_pass::trigger(f, s, ev); - } - - FSM* f; - Event ev; - bool result = false; - }; + FSM* f; + Event ev; + bool result = false; }; } // namespace fsm_details @@ -285,13 +281,12 @@ protected: template using subfsm_t = nested_fsm_t; +public: //! get access to derived protected members from the base class derived_view : public Derived { public: using derived_t = Derived; - using derived_t::base_t::enter; - using derived_t::base_t::exit; // propagate user fsm methods using Derived::enter; using Derived::exit; @@ -317,7 +312,7 @@ protected: static bool react(derived_view* f, src_state_t& s, const event_t& ev) { if (guard_fn == nullptr or (f->*guard_fn)(s, ev)) { - dest_state_t* d = fsm_details::fsm_helper::get_state_recursive(f); + dest_state_t* d = fsm_details::get_state_recursive(f); if (react_fn != nullptr) { (f->*react_fn)(s, *d, ev); } @@ -346,7 +341,7 @@ protected: static bool react(derived_view* f, SrcState& s, const event_t& ev) { if (guard_fn == nullptr or (f->*guard_fn)(ev)) { - dest_state_t* d = fsm_details::fsm_helper::get_state_recursive(f); + dest_state_t* d = fsm_details::get_state_recursive(f); if (react_fn != nullptr) { (f->*react_fn)(*d, ev); } @@ -362,7 +357,6 @@ protected: template using transition_table = type_list; -public: static const bool is_nested = false; //! Struct used to store FSM states @@ -377,7 +371,8 @@ public: { if (not Derived::is_nested) { // If Root FSM, call initial state enter method - fsm_details::fsm_helper::call_enter(f->derived(), &get_unchecked()); + fsm_details::state_traits::enter_state(f->derived(), + &get_unchecked()); } } @@ -429,7 +424,7 @@ public: template bool trigger(Ev&& e) { - fsm_details::fsm_helper::trigger_visitor visitor{derived(), std::forward(e)}; + fsm_details::trigger_visitor visitor{derived(), std::forward(e)}; srslte::visit(visitor, derived()->states); return visitor.result; } @@ -469,33 +464,13 @@ public: template constexpr static bool can_hold_state() { - return fsm_details::fsm_helper::fsm_state_list_type >::template can_hold_type(); + return fsm_details::fsm_state_list_type >::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; - - // Access to CRTP derived class - derived_view* derived() { return static_cast(this); } - - const derived_view* derived() const { return static_cast(this); } - - template - void enter(State& s) - { - // do nothing by default - } - - template - void exit(State& s) - { - // do nothing by default - } - //! Log FSM activity method, e.g. state transitions template void log_fsm_activity(const char* format, Args&&... args) @@ -518,6 +493,17 @@ protected: } } +protected: + friend struct fsm_details::derived_access; + + // Access to CRTP derived class + derived_view* derived() { return static_cast(this); } + + const derived_view* derived() const { return static_cast(this); } + + void enter() {} + void exit() {} + srslte::log_ref log_h; srslte::LOG_LEVEL_ENUM fsm_event_log_level = LOG_LEVEL_INFO; }; @@ -539,19 +525,11 @@ public: protected: using parent_fsm_t = ParentFSM; - using fsm_t::enter; using fsm_t::exit; ParentFSM* fsm_ptr = nullptr; }; -template -struct proc_complete_ev { - proc_complete_ev(bool success_) : success(success_) {} - - bool success; -}; - // event template struct proc_launch_ev { @@ -565,12 +543,9 @@ 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::enter; - using fsm_t::exit; public: using base_t = proc_fsm_t; @@ -581,14 +556,34 @@ public: // states struct idle_st { - bool success = false; - Result result = {}; + idle_st() = default; + template + idle_st(bool success_, T&& r) : success(success_), result(std::forward(r)), value_set(true) + {} + + bool is_result_set() const { return value_set; } + bool is_success() const { return value_set and success; } + const Result& get_result() const { return result; } + + private: + bool success = false, value_set = false; + Result result = {}; }; explicit proc_fsm_t(srslte::log_ref log_) : fsm_t(log_) {} bool is_running() const { return base_t::template is_in_state(); } + bool is_success() const { return base_t::template get_state()->is_success(); } + + const Result& get_result() const + { + if (is_success()) { + return base_t::template get_state->get_result(); + } + THROW_BAD_ACCESS("in proc_fsm_t::get_result"); + } + template void launch(Args&&... args) { @@ -599,26 +594,24 @@ protected: void enter(idle_st& s) { if (launch_counter > 0) { - log_h->info("Finished run no. %d %s\n", launch_counter, s.success ? "successfully" : "with an error"); + log_h->info("FSM \"%s\": Finished run no. %d %s\n", + get_type_name().c_str(), + launch_counter, + s.is_success() ? "successfully" : "with an error"); + if (not s.is_result_set()) { + log_h->error( + "FSM \"%s\": No result was set for run no. %d\n", get_type_name().c_str(), launch_counter); + } } } void exit(idle_st& s) { + s = {}; launch_counter++; log_h->info("FSM \"%s\": Starting run no. %d\n", get_type_name().c_str(), launch_counter); } - bool is_success() const { return base_t::template get_state()->success; } - - const Result& get_result() const - { - if (is_success()) { - return base_t::template get_state->result; - } - THROW_BAD_ACCESS("in proc_fsm_t::get_result"); - } - private: int launch_counter = 0; }; diff --git a/lib/include/srslte/common/type_utils.h b/lib/include/srslte/common/type_utils.h index fd31faa4f..9f46e73c5 100644 --- a/lib/include/srslte/common/type_utils.h +++ b/lib/include/srslte/common/type_utils.h @@ -209,6 +209,37 @@ struct filter { using type = type_list<>; }; +/************************** + * Detect Pattern + *************************/ + +template +using voider = void; + +template class Op, class... Args> +struct detector { + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector >, Op, Args...> { + using value_t = std::true_type; + using type = Op; +}; + +template