fsm refactor.

- avoid triggering directly a subfsm
- improved metafunctions
- other cleanups
This commit is contained in:
Francisco Paisana 2020-08-18 13:33:23 +01:00
parent 2d55691173
commit 7cd4f45d62
5 changed files with 161 additions and 123 deletions

View File

@ -46,11 +46,25 @@ namespace srslte {
//! Forward declarations
template <typename Derived>
class fsm_t;
class base_fsm_t;
template <typename Derived, typename ParentFSM>
class composite_fsm_t;
//! Check if type T is an FSM
template <typename T>
using is_fsm = std::is_base_of<base_fsm_t<T>, T>;
//! Check if type T is a composite FSM
template <typename T, typename TCheck = void>
struct is_composite_fsm : public std::false_type {};
template <typename T>
struct is_composite_fsm<T, typename std::enable_if<is_fsm<T>::value>::type> {
const static bool value = T::is_nested;
};
namespace fsm_details {
//! Meta-function to filter transition list <Rows...> by <Event, SrcState> types
//! Meta-function to filter transition_list<Rows...> by <Event, SrcState> types
template <class Event, class SrcState, class...>
struct filter_transition_type;
template <class Event, class SrcState, class... Rows>
@ -75,34 +89,16 @@ struct state_name_visitor {
std::string name = "invalid";
};
//! Enable/Disable meta-function if <State> is part of <FSM> state list
//! Enable/Disable SFINAE meta-function to check if <State> is part of <FSM> state list
template <typename FSM, typename State, typename T = void>
using enable_if_fsm_state = typename std::enable_if<FSM::template can_hold_state<State>(), T>::type;
template <typename FSM, typename State, typename T = void>
using disable_if_fsm_state = typename std::enable_if<not FSM::template can_hold_state<State>(), T>::type;
template <typename FSM>
constexpr bool is_fsm()
{
return std::is_base_of<fsm_t<FSM>, FSM>::value;
}
using enable_if_subfsm = typename std::enable_if<is_composite_fsm<FSM>::value>::type;
template <typename FSM>
constexpr typename std::enable_if<is_fsm<FSM>(), bool>::type is_subfsm()
{
return FSM::is_nested;
}
template <typename FSM>
constexpr typename std::enable_if<not is_fsm<FSM>(), bool>::type is_subfsm()
{
return false;
}
template <typename FSM>
using enable_if_subfsm = typename std::enable_if<is_subfsm<FSM>()>::type;
template <typename FSM>
using disable_if_subfsm = typename std::enable_if<not is_subfsm<FSM>()>::type;
using disable_if_subfsm = typename std::enable_if<not is_composite_fsm<FSM>::value>::type;
//! Metafunction to determine if FSM can hold given State type
template <typename FSM>
@ -163,7 +159,7 @@ template <typename FSM, typename State>
struct state_traits {
static_assert(FSM::template can_hold_state<State>(), "FSM type does not hold provided State\n");
using state_t = State;
using is_subfsm = std::integral_constant<bool, ::srslte::fsm_details::is_subfsm<State>()>;
using is_subfsm = std::integral_constant<bool, ::srslte::is_composite_fsm<State>::value>;
//! enter new state. enter is called recursively for subFSMs
template <typename Event>
@ -279,7 +275,7 @@ struct trigger_visitor {
enable_if_subfsm<CurrentState> operator()(CurrentState& s)
{
// Enter here for SubFSMs
result = s.trigger(std::forward<Event>(ev));
result = s.process_event(std::forward<Event>(ev));
if (not result) {
result = call_react(s);
}
@ -300,29 +296,24 @@ struct trigger_visitor {
} // namespace fsm_details
template <typename Derived, typename ParentFSM>
class nested_fsm_t;
//! CRTP Class for all non-nested FSMs
template <typename Derived>
class fsm_t
class base_fsm_t
{
protected:
using base_t = fsm_t<Derived>;
template <typename SubFSM>
using subfsm_t = nested_fsm_t<SubFSM, Derived>;
public:
using derived_t = Derived;
//! get access to derived protected members from the base
class derived_view : public Derived
{
public:
using derived_t = Derived;
// propagate user fsm methods
using Derived::states;
using typename Derived::transitions;
};
template <typename... Rows>
using transition_table = type_list<Rows...>;
//! Params of a state transition
template <typename SrcState,
typename DestState,
@ -358,7 +349,7 @@ public:
using upd = row<SrcState, SrcState, Event, ReactFn, GuardFn>;
template <typename DestState, typename Event, bool (Derived::*GuardFn)(const Event&) = nullptr>
struct from_any_state {
struct to_state {
using dest_state_t = DestState;
using event_t = Event;
constexpr static bool (Derived::*guard_fn)(const Event&) = GuardFn;
@ -376,11 +367,6 @@ public:
using is_match = std::is_same<Event2, event_t>;
};
template <typename... Rows>
using transition_table = type_list<Rows...>;
static const bool is_nested = false;
//! Struct used to store FSM states
template <typename... States>
struct state_list : public std::tuple<States...> {
@ -389,7 +375,7 @@ public:
static_assert(not type_list_contains<Derived, States...>(), "An FSM cannot contain itself as state\n");
template <typename... Args>
state_list(fsm_t<Derived>* f, Args&&... args) : tuple_base_t(std::forward<Args>(args)...)
state_list(base_fsm_t<Derived>* f, Args&&... args) : tuple_base_t(std::forward<Args>(args)...)
{
if (not Derived::is_nested) {
// If Root FSM, call initial state enter method
@ -440,26 +426,6 @@ public:
size_t current_idx = 0;
};
explicit fsm_t(srslte::log_ref log_) : log_h(log_) {}
// Push Events to FSM
template <typename Ev>
bool trigger(Ev&& e)
{
if (trigger_locked) {
scheduled_event(std::forward<Ev>(e), typename std::is_lvalue_reference<Ev>::type{});
return false;
}
trigger_locked = true;
bool ret = process_event(std::forward<Ev>(e));
while (not pending_events.empty()) {
pending_events.front()();
pending_events.pop_front();
}
trigger_locked = false;
return ret;
}
template <typename State>
bool is_in_state() const
{
@ -495,7 +461,57 @@ public:
template <typename State>
constexpr static bool can_hold_state()
{
return fsm_details::fsm_state_list_type<fsm_t<Derived> >::template can_hold_type<State>();
return fsm_details::fsm_state_list_type<base_fsm_t<Derived> >::template can_hold_type<State>();
}
protected:
// Access to CRTP derived class
derived_view* derived() { return static_cast<derived_view*>(this); }
const derived_view* derived() const { return static_cast<const derived_view*>(this); }
template <typename Ev>
bool process_event(Ev&& e)
{
fsm_details::trigger_visitor<derived_view, Ev> visitor{derived(), std::forward<Ev>(e)};
srslte::visit(visitor, derived()->states);
return visitor.result;
}
};
template <typename Derived, typename ParentFSM>
class composite_fsm_t;
//! CRTP Class for all non-nested FSMs
template <typename Derived>
class fsm_t : public base_fsm_t<Derived>
{
protected:
using base_t = fsm_t<Derived>;
template <typename SubFSM>
using subfsm_t = composite_fsm_t<SubFSM, Derived>;
public:
static const bool is_nested = false;
explicit fsm_t(srslte::log_ref log_) : log_h(log_) {}
// Push Events to FSM
template <typename Ev>
bool trigger(Ev&& e)
{
if (trigger_locked) {
scheduled_event(std::forward<Ev>(e), typename std::is_lvalue_reference<Ev>::type{});
return false;
}
trigger_locked = true;
bool ret = process_event(std::forward<Ev>(e));
while (not pending_events.empty()) {
pending_events.front()();
pending_events.pop_front();
}
trigger_locked = false;
return ret;
}
void set_fsm_event_log_level(srslte::LOG_LEVEL_ENUM e) { fsm_event_log_level = e; }
@ -527,53 +543,55 @@ public:
}
protected:
// Access to CRTP derived class
derived_view* derived() { return static_cast<derived_view*>(this); }
const derived_view* derived() const { return static_cast<const derived_view*>(this); }
using base_fsm_t<Derived>::derived;
using base_fsm_t<Derived>::process_event;
template <typename Ev>
bool process_event(Ev&& e)
{
fsm_details::trigger_visitor<derived_view, Ev> visitor{derived(), std::forward<Ev>(e)};
srslte::visit(visitor, derived()->states);
return visitor.result;
}
template <typename Ev>
void scheduled_event(Ev&& e, std::true_type)
void scheduled_event(Ev&& e, std::true_type t)
{
pending_events.emplace_back([this, e]() { process_event(e); });
}
template <typename Ev>
void scheduled_event(Ev&& e, std::false_type)
void scheduled_event(Ev&& e, std::false_type t)
{
pending_events.emplace_back(std::bind([this](Ev& e) { process_event(std::move(e)); }, std::move(e)));
}
srslte::log_ref log_h;
srslte::LOG_LEVEL_ENUM fsm_event_log_level = LOG_LEVEL_INFO;
bool trigger_locked = false;
std::list<srslte::move_callback<void()> > pending_events;
srslte::log_ref log_h;
srslte::LOG_LEVEL_ENUM fsm_event_log_level = LOG_LEVEL_INFO;
bool trigger_locked = false;
std::deque<srslte::move_callback<void()> > pending_events;
};
template <typename Derived, typename ParentFSM>
class nested_fsm_t : public fsm_t<Derived>
class composite_fsm_t : public base_fsm_t<Derived>
{
public:
using base_t = nested_fsm_t<Derived, ParentFSM>;
using base_t = composite_fsm_t<Derived, ParentFSM>;
using parent_t = ParentFSM;
static const bool is_nested = true;
explicit nested_fsm_t(ParentFSM* parent_fsm_) : fsm_t<Derived>(parent_fsm_->get_log()), fsm_ptr(parent_fsm_) {}
nested_fsm_t(nested_fsm_t&&) = default;
nested_fsm_t& operator=(nested_fsm_t&&) = default;
explicit composite_fsm_t(ParentFSM* parent_fsm_) : fsm_ptr(parent_fsm_) {}
composite_fsm_t(composite_fsm_t&&) noexcept = default;
composite_fsm_t& operator=(composite_fsm_t&&) noexcept = default;
// Get pointer to outer FSM in case of HSM
// Get pointer to outer FSM in case of HFSM
const parent_t* parent_fsm() const { return fsm_ptr; }
parent_t* parent_fsm() { return fsm_ptr; }
srslte::log_ref get_log() const { return parent_fsm()->get_log(); }
// Push Events to root FSM
template <typename Ev>
bool trigger(Ev&& e)
{
return parent_fsm()->trigger(std::forward<Ev>(e));
}
// Push events to this subFSM
using base_fsm_t<Derived>::process_event;
protected:
using parent_fsm_t = ParentFSM;

View File

@ -30,7 +30,7 @@ struct ev2 {};
std::vector<std::string> calls;
template <typename State>
void call_log_helper(State* state, srslte::log_ref& log_h, const char* type)
void call_log_helper(State* state, srslte::log_ref log_h, const char* type)
{
std::string callname = srslte::get_type_name<State>() + "::" + type;
log_h->info("%s custom called\n", callname.c_str());
@ -64,22 +64,22 @@ public:
struct state_inner {
void enter(fsm2* f)
{
call_log_helper(this, f->log_h, "enter");
call_log_helper(this, f->get_log(), "enter");
f->parent_fsm()->inner_enter_counter++;
}
};
struct state_inner2 {
void enter(fsm2* f) { call_log_helper(this, f->log_h, "enter"); }
void exit(fsm2* f) { call_log_helper(this, f->log_h, "exit"); }
void enter(fsm2* f) { call_log_helper(this, f->get_log(), "enter"); }
void exit(fsm2* f) { call_log_helper(this, f->get_log(), "exit"); }
};
explicit fsm2(fsm1* f_) : nested_fsm_t(f_) {}
explicit fsm2(fsm1* f_) : composite_fsm_t(f_) {}
fsm2(fsm2&&) = default;
fsm2& operator=(fsm2&&) = default;
~fsm2() { log_h->info("%s being destroyed!", get_type_name(*this).c_str()); }
~fsm2() { get_log()->info("%s being destroyed!", get_type_name(*this).c_str()); }
void enter(fsm1* f) { call_log_helper(this, f->log_h, "enter"); }
void exit(fsm1* f) { call_log_helper(this, f->log_h, "exit"); }
void enter(fsm1* f) { call_log_helper(this, get_log(), "enter"); }
void exit(fsm1* f) { call_log_helper(this, get_log(), "exit"); }
private:
void inner_action1(state_inner& s, const ev1& e);
@ -147,17 +147,17 @@ void fsm1::state1::exit(fsm1* f)
// FSM event handlers
void fsm1::fsm2::inner_action1(state_inner& s, const ev1& e)
{
call_log_helper(this, log_h, "inner_action1");
call_log_helper(this, get_log(), "inner_action1");
}
void fsm1::fsm2::inner_action2(state_inner& s, const ev2& e)
{
call_log_helper(this, log_h, "inner_action2");
call_log_helper(this, get_log(), "inner_action2");
}
void fsm1::fsm2::inner_action3(state_inner2& s, const ev2& e)
{
log_h->info("fsm2::state_inner2::react called\n");
get_log()->info("fsm2::state_inner2::react called\n");
}
void fsm1::action1(idle_st& s, const ev1& e)
@ -181,8 +181,8 @@ void fsm1::action3(state1& s, const ev2& ev)
namespace srslte {
namespace fsm_details {
static_assert(is_fsm<fsm1>(), "invalid metafunction\n");
static_assert(is_subfsm<fsm1::fsm2>(), "invalid metafunction\n");
static_assert(is_fsm<fsm1>::value, "invalid metafunction\n");
static_assert(is_composite_fsm<fsm1::fsm2>::value, "invalid metafunction\n");
static_assert(type_list_size(typename filter_transition_type<ev1, fsm1::idle_st, fsm_transitions<fsm1> >::type{}) > 0,
"invalid filter metafunction\n");
static_assert(
@ -308,7 +308,7 @@ protected:
row< idle_st, procstate1, launch_ev<int> >,
upd< procstate1, procevent1, &proc1::handle_success, &proc1::is_success >,
upd< procstate1, procevent1, &proc1::handle_failure, &proc1::is_failure >,
from_any_state< idle_st, complete_ev >
to_state< idle_st, complete_ev >
// +------------+-------------+----------------+------------------------+--------------------+
>;
// clang-format on
@ -462,7 +462,7 @@ protected:
row< emm_ta_updating_initiated, emm_registered, tau_outcome_ev >,
row< emm_ta_updating_initiated, emm_deregistered, tau_reject_other_cause_ev >,
row< emm_deregistered_initiated, emm_deregistered, detach_accept_ev >,
from_any_state< emm_deregistered, power_off_ev >
to_state< emm_deregistered, power_off_ev >
// +-----------------------------+-------------------------+-----------------------------+
>;
// clang-format on
@ -541,18 +541,29 @@ struct fsm3 : public srslte::fsm_t<fsm3> {
struct st1 {};
struct st2 {
int counter = 0;
void enter(fsm3* fsm) { counter++; }
void enter(fsm3* fsm)
{
counter++;
fsm->events.push_back(fsm->current_state_name());
}
};
fsm3() : base_t(srslte::log_ref{"TEST"}) {}
std::vector<std::string> events;
protected:
void handle_ev1(st1& s, const ev1& ev) { trigger(ev2{}); }
void handle_ev1(st1& s, const ev1& ev)
{
trigger(ev2{});
events.push_back(current_state_name() + "::action"); // still in st1
}
void handle_ev2(st2& s, const ev2& ev)
{
if (s.counter < 2) {
trigger(ev1{});
}
events.push_back(current_state_name() + "::action"); // still in st2
}
state_list<st1, st2> states{this};
@ -560,8 +571,8 @@ protected:
using transitions = transition_table<
// Start Target Event Action
// +------------------------+-------------------------+-------------------+--------------------+
row< st1, st2, ev1, &fsm3::handle_ev1>,
row< st2, st1, ev2, &fsm3::handle_ev2>
row< st1, st2, ev1, &fsm3::handle_ev1 >,
row< st2, st1, ev2, &fsm3::handle_ev2 >
// +------------------------+-------------------------+-------------------+--------------------+
>;
// clang-format on
@ -570,11 +581,20 @@ protected:
int test_fsm_self_trigger()
{
fsm3 fsm;
TESTASSERT(fsm.events.empty());
TESTASSERT(fsm.is_in_state<fsm3::st1>());
fsm.trigger(ev1{});
TESTASSERT(fsm.is_in_state<fsm3::st1>());
TESTASSERT(fsm.events.size() == 6);
TESTASSERT(fsm.events[0] == "st1::action");
TESTASSERT(fsm.events[1] == "st2");
TESTASSERT(fsm.events[2] == "st2::action");
TESTASSERT(fsm.events[3] == "st1::action");
TESTASSERT(fsm.events[4] == "st2");
TESTASSERT(fsm.events[5] == "st2::action");
return SRSLTE_SUCCESS;
}

View File

@ -189,12 +189,12 @@ private:
state_list<wait_ho_req_ack_st, status_transfer_st> states{this};
// clang-format off
using transitions = transition_table<
// Start Target Event Action Guard
// +-------------------+------------------+------------------------------+---------+---------------------+
from_any_state< idle_st, srslte::failure_ev >,
row< wait_ho_req_ack_st, status_transfer_st, srslte::unique_byte_buffer_t, nullptr, &fsm::send_ho_cmd >,
row< wait_ho_req_ack_st, idle_st , srslte::unique_byte_buffer_t >
// +-------------------+------------------+------------------------------+---------+---------------------+
// Start Target Event Action Guard
// +-------------------+------------------+------------------------------+---------+---------------------+
to_state< idle_st, srslte::failure_ev >,
row< wait_ho_req_ack_st, status_transfer_st, srslte::unique_byte_buffer_t, nullptr, &fsm::send_ho_cmd >,
row< wait_ho_req_ack_st, idle_st , srslte::unique_byte_buffer_t >
// +-------------------+------------------+------------------------------+---------+---------------------+
>;
// clang-format on
};

View File

@ -1017,8 +1017,8 @@ bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_e
void rrc::ue::rrc_mobility::s1_source_ho_st::wait_ho_req_ack_st::enter(s1_source_ho_st* f, const ho_meas_report_ev& ev)
{
f->log_h->console("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
f->log_h->info("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
f->get_log()->console("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
f->get_log()->info("Starting S1 Handover of rnti=0x%x to 0x%x.\n", f->parent_fsm()->rrc_ue->rnti, ev.target_eci);
f->report = ev;
bool success = f->parent_fsm()->start_ho_preparation(f->report.target_eci, f->report.meas_obj->meas_obj_id, false);
@ -1035,14 +1035,14 @@ bool rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st&
{
asn1::cbit_ref bref(container->msg, container->N_bytes);
if (rrchocmd.unpack(bref) != asn1::SRSASN_SUCCESS) {
log_h->warning("Unpacking of RRC HOCommand was unsuccessful\n");
log_h->warning_hex(container->msg, container->N_bytes, "Received container:\n");
get_log()->warning("Unpacking of RRC HOCommand was unsuccessful\n");
get_log()->warning_hex(container->msg, container->N_bytes, "Received container:\n");
return false;
}
}
if (rrchocmd.crit_exts.type().value != c1_or_crit_ext_opts::c1 or
rrchocmd.crit_exts.c1().type().value != ho_cmd_s::crit_exts_c_::c1_c_::types_opts::ho_cmd_r8) {
log_h->warning("Only handling r8 Handover Commands\n");
get_log()->warning("Only handling r8 Handover Commands\n");
return false;
}
@ -1052,18 +1052,18 @@ bool rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st&
asn1::cbit_ref bref(&rrchocmd.crit_exts.c1().ho_cmd_r8().ho_cmd_msg[0],
rrchocmd.crit_exts.c1().ho_cmd_r8().ho_cmd_msg.size());
if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS) {
log_h->warning("Unpacking of RRC DL-DCCH message with HO Command was unsuccessful.\n");
get_log()->warning("Unpacking of RRC DL-DCCH message with HO Command was unsuccessful.\n");
return false;
}
}
if (dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1 or
dl_dcch_msg.msg.c1().type().value != dl_dcch_msg_type_c::c1_c_::types_opts::rrc_conn_recfg) {
log_h->warning("HandoverCommand is expected to contain an RRC Connection Reconf message inside\n");
get_log()->warning("HandoverCommand is expected to contain an RRC Connection Reconf message inside\n");
return false;
}
asn1::rrc::rrc_conn_recfg_s& reconf = dl_dcch_msg.msg.c1().rrc_conn_recfg();
if (not reconf.crit_exts.c1().rrc_conn_recfg_r8().mob_ctrl_info_present) {
log_h->warning("HandoverCommand is expected to have mobility control subfield\n");
get_log()->warning("HandoverCommand is expected to have mobility control subfield\n");
return false;
}
@ -1077,7 +1077,7 @@ bool rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st&
void rrc::ue::rrc_mobility::s1_source_ho_st::status_transfer_st::enter(s1_source_ho_st* f)
{
f->log_h->info("HandoverCommand of rnti=0x%x handled successfully.\n", f->parent_fsm()->rrc_ue->rnti);
f->get_log()->info("HandoverCommand of rnti=0x%x handled successfully.\n", f->parent_fsm()->rrc_ue->rnti);
// TODO: Do anything with MeasCfg info within the Msg (e.g. update ue_var_meas)?

View File

@ -60,7 +60,7 @@ void phy_controller::in_sync()
trigger(in_sync_ev{});
}
phy_controller::selecting_cell::selecting_cell(phy_controller* parent_) : nested_fsm_t(parent_)
phy_controller::selecting_cell::selecting_cell(phy_controller* parent_) : composite_fsm_t(parent_)
{
wait_in_sync_timer = parent_fsm()->task_sched.get_unique_timer();
}