updated fsm to allow enter methods to receive event that caused them

master
Francisco Paisana 5 years ago
parent 6f5da19312
commit e550bf726d

@ -101,11 +101,25 @@ using fsm_transitions = typename FSM::derived_view::transitions;
//! Detection of enter/exit methods of a state.
template <typename FSM, typename State>
auto call_enter(FSM* f, State* s) -> decltype(s->enter(f))
auto call_enter2(FSM* f, State* s) -> decltype(s->enter(f))
{
s->enter(f);
}
inline void call_enter(...) {}
inline void call_enter2(...)
{
// do nothing
}
template <typename FSM, typename State, typename Event>
auto call_enter(FSM* f, State* s, const Event& ev) -> decltype(s->enter(f, ev))
{
// pass event to enter method
s->enter(f, ev);
}
template <typename FSM, typename State, typename... Args>
inline void call_enter(FSM* f, State* s, Args&&...)
{
call_enter2(f, s);
}
template <typename FSM, typename State>
auto call_exit(FSM* f, State* s) -> decltype(s->exit(f))
{
@ -141,38 +155,47 @@ struct state_traits {
using is_subfsm = std::integral_constant<bool, ::srslte::fsm_details::is_subfsm<State>()>;
//! enter new state. enter is called recursively for subFSMs
static void enter_state(FSM* f, State* s) { enter_(f, s, is_subfsm{}); }
template <typename Event>
static void enter_state(FSM* f, State* s, const Event& ev)
{
enter_(f, s, ev, is_subfsm{});
}
//! Change state. If DestState is not a state of FSM, call same function for parentFSM recursively
template <typename DestState>
static enable_if_fsm_state<FSM, DestState> transit_state(FSM* f)
template <typename DestState, typename Event>
static enable_if_fsm_state<FSM, DestState> transit_state(FSM* f, const Event& ev)
{
call_exit(f, &f->states.template get_unchecked<State>());
f->states.template transit<DestState>();
state_traits<FSM, DestState>::enter_state(f, &f->states.template get_unchecked<DestState>());
state_traits<FSM, DestState>::enter_state(f, &f->states.template get_unchecked<DestState>(), ev);
}
template <typename DestState>
static disable_if_fsm_state<FSM, DestState> transit_state(FSM* f)
template <typename DestState, typename Event>
static disable_if_fsm_state<FSM, DestState> transit_state(FSM* f, const Event& ev)
{
using parent_state_traits = state_traits<typename FSM::parent_t::derived_view, typename FSM::derived_t>;
call_exit(f, &f->states.template get_unchecked<State>());
parent_state_traits::template transit_state<DestState>(get_derived(f->parent_fsm()));
parent_state_traits::template transit_state<DestState>(get_derived(f->parent_fsm()), ev);
}
private:
//! In case of State is a subFSM
static void enter_(FSM* f, State* s, std::true_type)
template <typename Event>
static void enter_(FSM* f, State* s, const Event& ev, std::true_type)
{
using init_type = typename fsm_state_list_type<State>::init_state_t;
// set default FSM type
get_derived(s)->states.template transit<init_type>();
// call FSM enter function
call_enter(f, s);
call_enter(f, s, ev);
// call initial substate enter
state_traits<typename State::derived_view, init_type>::enter_state(
get_derived(s), &get_derived(s)->states.template get_unchecked<init_type>());
get_derived(s), &get_derived(s)->states.template get_unchecked<init_type>(), ev);
}
//! In case of State is basic state
static void enter_(FSM* f, State* s, std::false_type) { call_enter(f, s); }
template <typename Event>
static void enter_(FSM* f, State* s, const Event& ev, std::false_type)
{
call_enter(f, s, ev);
}
};
//! Trigger Event reaction for the first Row for which the Guard passes
@ -203,7 +226,7 @@ struct apply_first_guard_pass<FSM, type_list<First, Rows...> > {
get_type_name<dest_state>().c_str(),
get_type_name<event_type>().c_str());
// Apply state change operations
state_traits<FSM, src_state>::template transit_state<dest_state>(f);
state_traits<FSM, src_state>::template transit_state<dest_state>(f, ev);
}
return true;
}
@ -294,23 +317,20 @@ public:
template <typename SrcState,
typename DestState,
typename Event,
void (Derived::*ReactFn)(SrcState&, DestState&, const Event&) = nullptr,
bool (Derived::*GuardFn)(SrcState&, const Event&) const = nullptr>
void (Derived::*ReactFn)(SrcState&, const Event&) = nullptr,
bool (Derived::*GuardFn)(SrcState&, const Event&) = nullptr>
struct row {
using src_state_t = SrcState;
using dest_state_t = DestState;
using event_t = Event;
constexpr static void (Derived::*react_fn)(SrcState&, DestState&, const Event&) = ReactFn;
constexpr static bool (Derived::*guard_fn)(SrcState&, const Event&) const = GuardFn;
using src_state_t = SrcState;
using dest_state_t = DestState;
using event_t = Event;
constexpr static void (Derived::*react_fn)(SrcState&, const Event&) = ReactFn;
constexpr static bool (Derived::*guard_fn)(SrcState&, const Event&) = GuardFn;
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::get_state_recursive<dest_state_t, derived_view>(f);
if (react_fn != nullptr) {
(f->*react_fn)(s, *d, ev);
(f->*react_fn)(s, ev);
}
return true;
}
@ -321,26 +341,22 @@ public:
using is_match = std::is_same<type_list<SrcState2, Event2>, type_list<src_state_t, event_t> >;
};
template <typename DestState,
template <typename SrcState,
typename Event,
void (Derived::*ReactFn)(DestState&, const Event&) = nullptr,
bool (Derived::*GuardFn)(const Event&) const = nullptr>
struct from_any_state {
using dest_state_t = DestState;
using event_t = Event;
void (Derived::*ReactFn)(SrcState&, const Event&) = nullptr,
bool (Derived::*GuardFn)(SrcState&, const Event&) = nullptr>
using upd = row<SrcState, SrcState, Event, ReactFn, GuardFn>;
constexpr static void (Derived::*react_fn)(DestState&, const Event&) = ReactFn;
constexpr static bool (Derived::*guard_fn)(const Event&) const = GuardFn;
template <typename DestState, typename Event, bool (Derived::*GuardFn)(const Event&) = nullptr>
struct from_any_state {
using dest_state_t = DestState;
using event_t = Event;
constexpr static bool (Derived::*guard_fn)(const Event&) = GuardFn;
template <typename SrcState>
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::get_state_recursive<dest_state_t, derived_view>(f);
if (react_fn != nullptr) {
(f->*react_fn)(*d, ev);
}
return true;
}
return false;
@ -367,8 +383,8 @@ public:
{
if (not Derived::is_nested) {
// If Root FSM, call initial state enter method
fsm_details::state_traits<derived_view, init_state_t>::enter_state(f->derived(),
&get_unchecked<init_state_t>());
fsm_details::state_traits<derived_view, init_state_t>::enter_state(
f->derived(), &get_unchecked<init_state_t>(), std::false_type{});
}
}
@ -552,15 +568,23 @@ protected:
ParentFSM* fsm_ptr = nullptr;
};
// event
template <typename... Args>
/**************************
* Procedure FSM
*************************/
template <typename T>
struct proc_launch_ev {
std::tuple<Args...> args;
T args;
};
explicit proc_launch_ev(Args&&... args_) : args(std::forward<Args>(args_)...) {}
template <typename Result>
struct proc_complete_ev {
Result result;
};
template <typename Derived, typename Result = std::true_type>
struct failure_ev {};
template <typename Derived, typename Result = bool>
class proc_fsm_t : public fsm_t<Derived>
{
using fsm_type = Derived;
@ -574,74 +598,86 @@ public:
using fsm_t<Derived>::trigger;
// events
struct reset_ev {};
template <typename Arg>
using launch_ev = srslte::proc_launch_ev<Arg>;
using complete_ev = srslte::proc_complete_ev<Result>;
// states
struct idle_st {
idle_st() = default;
template <typename T>
idle_st(bool success_, T&& r) : success(success_), result(std::forward<T>(r)), value_set(true)
{}
void enter(Derived* f)
{
if (f->launch_counter > 0) {
f->log_h->info("FSM \"%s\": Finished run no. %d %s\n",
get_type_name<Derived>().c_str(),
f->launch_counter,
is_success() ? "successfully" : "with an error");
if (not is_result_set()) {
f->log_h->error(
"FSM \"%s\": No result was set for run no. %d\n", get_type_name<Derived>().c_str(), f->launch_counter);
}
f->log_h->warning(
"FSM \"%s\": No result was set for run no. %d\n", get_type_name<Derived>().c_str(), f->launch_counter);
}
}
void enter(Derived* f, const complete_ev& ev)
{
f->log_h->info("FSM \"%s\": Finished run no. %d\n", get_type_name<Derived>().c_str(), f->launch_counter);
f->last_result = ev.result;
for (auto& func : f->listening_fsms) {
func(ev);
}
f->listening_fsms.clear();
}
void exit(Derived* f)
{
value_set = false;
success = false;
f->launch_counter++;
f->log_h->info("FSM \"%s\": Starting run no. %d\n", get_type_name<Derived>().c_str(), f->launch_counter);
}
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<Derived>(log_) {}
bool is_running() const { return base_t::template is_in_state<idle_st>(); }
bool is_success() const { return base_t::template get_state<idle_st>()->is_success(); }
bool is_running() const { return not base_t::template is_in_state<idle_st>(); }
const Result& get_result() const
{
if (is_success()) {
return base_t::template get_state<idle_st>->get_result();
if (launch_counter > 0 and base_t::template is_in_state<idle_st>()) {
return last_result;
}
THROW_BAD_ACCESS("in proc_fsm_t::get_result");
}
template <typename... Args>
void launch(Args&&... args)
template <typename OtherFSM>
void await(OtherFSM* f)
{
trigger(proc_launch_ev<Args...>(std::forward<Args>(args)...));
if (is_running()) {
listening_fsms.push_back([f](const complete_ev& ev) { return f->trigger(ev); });
} else {
f->trigger(last_result);
}
}
private:
int launch_counter = 0;
int launch_counter = 0;
Result last_result = {};
std::vector<std::function<void(const complete_ev& ev)> > listening_fsms;
};
// Generic events
template <typename ProcFSM>
class proc_wait_st
{
public:
explicit proc_wait_st(ProcFSM* proc_ptr_) : proc_ptr(proc_ptr_) {}
struct success_ev {};
struct failure_ev {};
template <typename FSM, typename Ev>
void enter(FSM* f, const Ev& ev)
{
if (proc_ptr->is_running()) {
f->get_log()->error("Unable to launch proc1\n");
f->trigger(typename ProcFSM::complete_ev{false});
}
proc_ptr->trigger(srslte::proc_launch_ev<Ev>{ev});
proc_ptr->await(f);
}
private:
ProcFSM* proc_ptr = nullptr;
};
} // namespace srslte

@ -28,6 +28,15 @@
struct ev1 {};
struct ev2 {};
std::vector<std::string> calls;
template <typename State>
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());
calls.push_back(callname);
}
class fsm1 : public srslte::fsm_t<fsm1>
{
public:
@ -39,7 +48,9 @@ public:
void enter(fsm1* f);
};
struct state1 {
void enter(fsm1* f);
void enter(fsm1* f) {}
void enter(fsm1* f, const ev1& ev);
void enter(fsm1* f, const ev2& ev);
void exit(fsm1* f);
};
@ -53,13 +64,13 @@ public:
struct state_inner {
void enter(fsm2* f)
{
f->log_h->info("fsm1::%s::enter called\n", srslte::get_type_name(*this).c_str());
call_log_helper(this, f->log_h, "enter");
f->parent_fsm()->inner_enter_counter++;
}
};
struct state_inner2 {
void enter(fsm2* f) { f->log_h->info("fsm1::%s::enter called\n", srslte::get_type_name(*this).c_str()); }
void exit(fsm2* f) { f->log_h->info("fsm1::%s::exit called\n", srslte::get_type_name(*this).c_str()); }
void enter(fsm2* f) { call_log_helper(this, f->log_h, "enter"); }
void exit(fsm2* f) { call_log_helper(this, f->log_h, "exit"); }
};
explicit fsm2(fsm1* f_) : nested_fsm_t(f_) {}
@ -67,19 +78,22 @@ public:
fsm2& operator=(fsm2&&) = default;
~fsm2() { log_h->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"); }
private:
void inner_action1(state_inner& s, state_inner& d, const ev1& e);
void inner_action2(state_inner& s, state_inner2& d, const ev2& e);
void inner_action3(state_inner2& s, state1& d, const ev2& e);
void inner_action1(state_inner& s, const ev1& e);
void inner_action2(state_inner& s, const ev2& e);
void inner_action3(state_inner2& s, const ev2& e);
protected:
// list of states
state_list<state_inner, state_inner2> states{this};
// clang-format off
using transitions = transition_table<
// Start Target Event Action
// Start Target Event Action
// +------------+-------------+----+----------------------+
row<state_inner, state_inner, ev1, &fsm2::inner_action1 >,
upd<state_inner, ev1, &fsm2::inner_action1 >,
row<state_inner, state_inner2, ev2, &fsm2::inner_action2 >,
row<state_inner2, state1, ev2, &fsm2::inner_action3 >
// +------------+-------------+----+----------------------+
@ -88,9 +102,9 @@ public:
};
private:
void action1(idle_st& s, state1& d, const ev1& e);
void action2(state1& s, fsm2& d, const ev1& e);
void action3(state1& s, idle_st& d, const ev2& e);
void action1(idle_st& s, const ev1& e);
void action2(state1& s, const ev1& e);
void action3(state1& s, const ev2& e);
protected:
void foo(ev1 e) { foo_counter++; }
@ -102,59 +116,64 @@ protected:
using transitions = transition_table<
// Start Target Event Action
// +------------+-------------+----+------------------+
row< idle_st, state1, ev1, &fsm1::action1>,
row< state1, fsm2, ev1, &fsm1::action2>,
row< state1, idle_st, ev2, &fsm1::action3>
// +------------+-------------+----+--------------------+
row< idle_st, state1, ev1, &fsm1::action1 >,
row< state1, fsm2, ev1, &fsm1::action2 >,
row< state1, idle_st, ev2, &fsm1::action3 >
// +------------+-------------+----+------------------+
>;
// clang-format on
};
void fsm1::idle_st::enter(fsm1* f)
{
f->log_h->info("%s::enter custom called\n", srslte::get_type_name(*this).c_str());
call_log_helper(this, f->log_h, "enter");
f->idle_enter_counter++;
}
void fsm1::state1::enter(fsm1* f)
void fsm1::state1::enter(fsm1* f, const ev1& ev)
{
call_log_helper(this, f->log_h, "enter");
f->state1_enter_counter++;
}
void fsm1::state1::enter(fsm1* f, const ev2& ev)
{
f->log_h->info("%s::enter custom called\n", srslte::get_type_name(*this).c_str());
call_log_helper(this, f->log_h, "enter2");
f->state1_enter_counter++;
}
void fsm1::state1::exit(fsm1* f)
{
f->log_h->info("%s::exit custom called\n", srslte::get_type_name(*this).c_str());
call_log_helper(this, f->log_h, "exit");
}
// FSM event handlers
void fsm1::fsm2::inner_action1(state_inner& s, state_inner& d, const ev1& e)
void fsm1::fsm2::inner_action1(state_inner& s, const ev1& e)
{
log_h->info("fsm2::state_inner::react called\n");
call_log_helper(this, log_h, "inner_action1");
}
void fsm1::fsm2::inner_action2(state_inner& s, state_inner2& d, const ev2& e)
void fsm1::fsm2::inner_action2(state_inner& s, const ev2& e)
{
log_h->info("fsm2::state_inner::react called\n");
call_log_helper(this, log_h, "inner_action2");
}
void fsm1::fsm2::inner_action3(state_inner2& s, state1& d, const ev2& e)
void fsm1::fsm2::inner_action3(state_inner2& s, const ev2& e)
{
log_h->info("fsm2::state_inner2::react called\n");
}
void fsm1::action1(idle_st& s, state1& d, const ev1& e)
void fsm1::action1(idle_st& s, const ev1& e)
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
call_log_helper(this, log_h, "action1");
foo(e);
}
void fsm1::action2(state1& s, fsm2& d, const ev1& ev)
void fsm1::action2(state1& s, const ev1& ev)
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
call_log_helper(this, log_h, "action2");
}
void fsm1::action3(state1& s, idle_st& d, const ev2& ev)
void fsm1::action3(state1& s, const ev2& ev)
{
log_h->info("%s::react called\n", srslte::get_type_name(s).c_str());
call_log_helper(this, log_h, "action3");
}
// Static Checks
@ -240,6 +259,21 @@ int test_hsm()
TESTASSERT(f.is_in_state<fsm1::fsm2>());
TESTASSERT(f.get_if_current_state<fsm1::fsm2>()->current_state_name() == "state_inner");
// Ensure correct call order
TESTASSERT(calls[0] == srslte::get_type_name<fsm1::idle_st>() + "::enter"); // enter for init state called
TESTASSERT(calls[1] == srslte::get_type_name<fsm1>() + "::action1");
TESTASSERT(calls[2] == srslte::get_type_name<fsm1::state1>() + "::enter");
TESTASSERT(calls[3] == srslte::get_type_name<fsm1>() + "::action2");
TESTASSERT(calls[4] == srslte::get_type_name<fsm1::state1>() + "::exit");
TESTASSERT(calls[5] == srslte::get_type_name<fsm1::fsm2>() + "::enter"); // entry is recursive
TESTASSERT(calls[6] == srslte::get_type_name<fsm1::fsm2::state_inner>() + "::enter");
TESTASSERT(calls[7] == srslte::get_type_name<fsm1::fsm2>() + "::inner_action1");
TESTASSERT(calls[8] == srslte::get_type_name<fsm1::fsm2>() + "::inner_action2");
TESTASSERT(calls[9] == srslte::get_type_name<fsm1::fsm2::state_inner2>() + "::enter");
TESTASSERT(calls[10] == srslte::get_type_name<fsm1::fsm2::state_inner2>() + "::exit");
TESTASSERT(calls[11] == srslte::get_type_name<fsm1::fsm2>() + "::exit"); // exit is recursive
TESTASSERT(calls[12] == srslte::get_type_name<fsm1::state1>() + "::enter2"); // differentiates different entry funcs
return SRSLTE_SUCCESS;
}
@ -251,52 +285,80 @@ struct procevent1 {
struct proc1 : public srslte::proc_fsm_t<proc1, int> {
public:
struct procstate1 {};
struct procstate1 {
void enter(proc1* f, const srslte::proc_launch_ev<int>& ev);
};
proc1(srslte::log_ref log_) : base_t(log_) {}
explicit proc1(srslte::log_ref log_) : base_t(log_) {}
protected:
// Transitions
void init(idle_st& s, procstate1& d, const srslte::proc_launch_ev<int*>& ev);
void handle_success(procstate1& s, idle_st& d, const procevent1& ev);
void handle_failure(procstate1& s, idle_st& d, const procevent1& ev);
void handle_success(procstate1& s, const procevent1& ev);
void handle_failure(procstate1& s, const procevent1& ev);
bool is_success(procstate1& s, const procevent1& ev) const { return ev.is_success; }
bool is_success(procstate1& s, const procevent1& ev) { return ev.is_success; }
bool is_failure(procstate1& s, const procevent1& ev) const { return not ev.is_success; }
bool is_failure(procstate1& s, const procevent1& ev) { return not ev.is_success; }
state_list<idle_st, procstate1> states{this, idle_st{}, procstate1{}};
// clang-format off
using transitions = transition_table<
// Start Target Event Action Guard (optional)
// +------------+-------------+----------------------------+------------------------+--------------------+
row< idle_st, procstate1, srslte::proc_launch_ev<int*>, &proc1::init >,
row< procstate1, idle_st, procevent1, &proc1::handle_success, &proc1::is_success >,
row< procstate1, idle_st, procevent1, &proc1::handle_failure, &proc1::is_failure >
// +------------+-------------+----------------------------+------------------------+--------------------+
// Start Target Event Action Guard (optional)
// +------------+-------------+----------------+------------------------+--------------------+
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 >
// +------------+-------------+----------------+------------------------+--------------------+
>;
// clang-format on
};
void proc1::init(idle_st& s, procstate1& d, const srslte::proc_launch_ev<int*>& ev)
void proc1::procstate1::enter(proc1* f, const launch_ev<int>& ev)
{
log_h->info("started!\n");
f->log_h->info("started!\n");
}
void proc1::handle_success(procstate1& s, idle_st& d, const procevent1& ev)
void proc1::handle_success(procstate1& s, const procevent1& ev)
{
log_h->info("success!\n");
d = {true, 5};
trigger(complete_ev{5});
}
void proc1::handle_failure(procstate1& s, idle_st& d, const procevent1& ev)
void proc1::handle_failure(procstate1& s, const procevent1& ev)
{
log_h->info("failure!\n");
d = {false, 3};
trigger(complete_ev{3});
}
struct proc_listener_fsm : public srslte::fsm_t<proc_listener_fsm> {
public:
struct st1 {};
struct st2 {};
using proc1_st = srslte::proc_wait_st<proc1>;
explicit proc_listener_fsm(srslte::log_ref log_, proc1* proc_ptr_) :
base_t(log_),
states(this, st1{}, st2{}, proc1_st{proc_ptr_})
{}
protected:
bool is_success(proc1_st& s, const proc1::complete_ev& ev) { return ev.result; }
// clang-format off
state_list<st1, st2, proc1_st > states;
using f = proc_listener_fsm;
using transitions = transition_table<
// Start Target Event Action Guard (optional)
// +--------------+--------------+-----------------------+------------------------+-------------------+
row< st1, proc1_st, int >,
row< proc1_st, st2, proc1::complete_ev, nullptr, &f::is_success >,
row< proc1_st, st1, proc1::complete_ev >
// +--------------+--------------+-----------------------+------------------------+-------------------+
>;
// clang-format on
};
int test_fsm_proc()
{
proc1 proc{srslte::logmap::get("PROC")};
@ -305,20 +367,41 @@ int test_fsm_proc()
int v = 2;
TESTASSERT(proc.current_state_name() == "idle_st");
proc.launch(&v);
proc.trigger(srslte::proc_launch_ev<int>{v});
TESTASSERT(proc.current_state_name() == "procstate1");
proc.launch(&v);
proc.trigger(srslte::proc_launch_ev<int>{v});
TESTASSERT(proc.current_state_name() == "procstate1");
proc.trigger(5);
proc.trigger(srslte::proc_launch_ev<int>{5});
TESTASSERT(proc.current_state_name() == "procstate1");
proc.trigger(procevent1{true});
TESTASSERT(proc.current_state_name() == "idle_st");
TESTASSERT(proc.get_state<proc1::idle_st>()->is_success());
proc.launch(&v);
TESTASSERT(proc.get_result() == 5);
proc.trigger(srslte::proc_launch_ev<int>{v});
TESTASSERT(proc.current_state_name() == "procstate1");
proc.trigger(procevent1{false});
TESTASSERT(proc.current_state_name() == "idle_st");
TESTASSERT(not proc.get_state<proc1::idle_st>()->is_success());
TESTASSERT(proc.get_result() == 3);
{
proc_listener_fsm outer_fsm{srslte::logmap::get("TEST"), &proc};
TESTASSERT(outer_fsm.is_in_state<proc_listener_fsm::st1>());
outer_fsm.trigger(6);
TESTASSERT(outer_fsm.is_in_state<proc_listener_fsm::proc1_st>());
TESTASSERT(proc.is_running());
proc.trigger(procevent1{true});
TESTASSERT(not proc.is_running());
TESTASSERT(outer_fsm.is_in_state<proc_listener_fsm::st2>());
}
{
proc_listener_fsm outer_fsm{srslte::logmap::get("TEST"), &proc};
TESTASSERT(outer_fsm.is_in_state<proc_listener_fsm::st1>());
proc.trigger(srslte::proc_launch_ev<int>{v});
TESTASSERT(proc.is_running());
outer_fsm.trigger(7);
TESTASSERT(outer_fsm.is_in_state<proc_listener_fsm::st1>());
TESTASSERT(proc.is_running());
}
return SRSLTE_SUCCESS;
}
@ -379,7 +462,8 @@ 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>
from_any_state< emm_deregistered, power_off_ev >
// +-----------------------------+-------------------------+-----------------------------+
>;
// clang-format on
};
@ -463,8 +547,8 @@ struct fsm3 : public srslte::fsm_t<fsm3> {
fsm3() : base_t(srslte::log_ref{"TEST"}) {}
protected:
void handle_ev1(st1& s, st2& d, const ev1& ev) { trigger(ev2{}); }
void handle_ev2(st2& s, st1& d, const ev2& ev)
void handle_ev1(st1& s, const ev1& ev) { trigger(ev2{}); }
void handle_ev2(st2& s, const ev2& ev)
{
if (s.counter < 2) {
trigger(ev1{});
@ -478,6 +562,7 @@ protected:
// +------------------------+-------------------------+-------------------+--------------------+
row< st1, st2, ev1, &fsm3::handle_ev1>,
row< st2, st1, ev2, &fsm3::handle_ev2>
// +------------------------+-------------------------+-------------------+--------------------+
>;
// clang-format on
};

@ -149,7 +149,7 @@ private:
const cell_ctxt_dedicated* source_cell_ctxt = nullptr;
uint16_t last_temp_crnti = SRSLTE_INVALID_RNTI;
void enter(rrc_mobility* f);
void enter(rrc_mobility* f, const ho_meas_report_ev& meas_report);
};
struct s1_target_ho_st {
uint32_t target_cell_id;
@ -158,42 +158,39 @@ private:
ho_meas_report_ev report;
struct wait_ho_req_ack_st {
void enter(s1_source_ho_st* f);
void enter(s1_source_ho_st* f, const ho_meas_report_ev& ev);
};
struct status_transfer_st {
void enter(s1_source_ho_st* f);
bool is_ho_cmd_sent = false;
};
explicit s1_source_ho_st(rrc_mobility* parent_) : base_t(parent_) {}
private:
void handle_ho_cmd(wait_ho_req_ack_st& s, status_transfer_st& d, const srslte::unique_byte_buffer_t& container);
bool send_ho_cmd(wait_ho_req_ack_st& s, const srslte::unique_byte_buffer_t& container);
protected:
using fsm = s1_source_ho_st;
state_list<wait_ho_req_ack_st, status_transfer_st> states{this};
// clang-format off
using transitions = transition_table<
// Start Target Event Action
// +-------------------+------------------+------------------------------+---------------------------+
from_any_state< idle_st, srslte::failure_ev >,
row< wait_ho_req_ack_st, status_transfer_st, srslte::unique_byte_buffer_t, &fsm::handle_ho_cmd >
// +-------------------+------------------+------------------------------+--------- -----------------+
// 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 >
// +-------------------+------------------+------------------------------+---------+---------------------+
>;
// clang-format on
};
// FSM guards
bool needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_report) const;
bool needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_report) const;
bool needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_report);
bool needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_report);
// FSM transition handlers
void handle_s1_meas_report(idle_st& s, s1_source_ho_st& d, const ho_meas_report_ev& meas_report);
void handle_intraenb_meas_report(idle_st& s, intraenb_ho_st& d, const ho_meas_report_ev& meas_report);
void handle_crnti_ce(intraenb_ho_st& s, intraenb_ho_st& d, const user_crnti_upd_ev& ev);
void handle_recfg_complete(intraenb_ho_st& s, idle_st& d, const recfg_complete_ev& ev);
void handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev);
void handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev);
protected:
// states
@ -207,12 +204,14 @@ protected:
using fsm = rrc_mobility;
// clang-format off
using transitions = transition_table<
// Start Target Event Action Guard (optional)
// +---------------+----------------+--------------------+---------------------------------+-------------------------+
row< idle_st, s1_source_ho_st, ho_meas_report_ev, &fsm::handle_s1_meas_report, &fsm::needs_s1_ho >,
row< idle_st, intraenb_ho_st, ho_meas_report_ev, &fsm::handle_intraenb_meas_report, &fsm::needs_intraenb_ho >,
row< intraenb_ho_st, intraenb_ho_st, user_crnti_upd_ev, &fsm::handle_crnti_ce >,
row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >
// Start Target Event Action Guard
// +---------------+----------------+--------------------+---------------------------+-------------------------+
row< idle_st, s1_source_ho_st, ho_meas_report_ev, nullptr, &fsm::needs_s1_ho >,
row< idle_st, intraenb_ho_st, ho_meas_report_ev, nullptr, &fsm::needs_intraenb_ho >,
// +---------------+----------------+--------------------+---------------------------+-------------------------+
upd< intraenb_ho_st, user_crnti_upd_ev, &fsm::handle_crnti_ce >,
row< intraenb_ho_st, idle_st, recfg_complete_ev, &fsm::handle_recfg_complete >
// +---------------+----------------+--------------------+---------------------------+-------------------------+
>;
// clang-format on
};

@ -837,7 +837,7 @@ bool rrc::ue::rrc_mobility::start_enb_status_transfer()
* rrc_mobility FSM methods
*************************************/
bool rrc::ue::rrc_mobility::needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_result) const
bool rrc::ue::rrc_mobility::needs_s1_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
return false;
@ -845,7 +845,7 @@ bool rrc::ue::rrc_mobility::needs_s1_ho(idle_st& s, const ho_meas_report_ev& mea
return rrc_details::eci_to_enbid(meas_result.target_eci) != rrc_enb->cfg.enb_id;
}
bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_result) const
bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_ev& meas_result)
{
if (rrc_ue->get_state() != RRC_STATE_REGISTERED) {
return false;
@ -857,29 +857,24 @@ bool rrc::ue::rrc_mobility::needs_intraenb_ho(idle_st& s, const ho_meas_report_e
return rrc_ue->get_ue_cc_cfg(UE_PCELL_CC_IDX)->cell_cfg.cell_id != cell_id;
}
void rrc::ue::rrc_mobility::handle_s1_meas_report(idle_st& s, s1_source_ho_st& d, const ho_meas_report_ev& meas_report)
{
Info("Starting S1 Handover of rnti=0x%x to 0x%x.\n", rrc_ue->rnti, meas_report.target_eci);
d.report = meas_report;
}
/*************************************
* s1_source_ho subFSM methods
*************************************/
void rrc::ue::rrc_mobility::s1_source_ho_st::wait_ho_req_ack_st::enter(s1_source_ho_st* f)
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->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);
if (not success) {
f->trigger(srslte::failure_ev{});
}
}
void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cmd(wait_ho_req_ack_st& s,
status_transfer_st& d,
const srslte::unique_byte_buffer_t& container)
bool rrc::ue::rrc_mobility::s1_source_ho_st::send_ho_cmd(wait_ho_req_ack_st& s,
const srslte::unique_byte_buffer_t& container)
{
d.is_ho_cmd_sent = false;
/* unpack RRC HOCmd struct and perform sanity checks */
asn1::rrc::ho_cmd_s rrchocmd;
{
@ -887,13 +882,13 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cmd(wait_ho_req_ack_st&
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");
return;
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");
return;
return false;
}
/* unpack DL-DCCH message containing the RRCRonnectionReconf (with MobilityInfo) to be sent to the UE */
@ -903,34 +898,32 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::handle_ho_cmd(wait_ho_req_ack_st&
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");
return;
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");
return;
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");
return;
return false;
}
/* Send HO Command to UE */
if (not parent_fsm()->rrc_ue->send_dl_dcch(&dl_dcch_msg)) {
return;
return false;
}
d.is_ho_cmd_sent = true;
log_h->info("HandoverCommand of rnti=0x%x handled successfully.\n", parent_fsm()->rrc_ue->rnti);
return true;
}
void rrc::ue::rrc_mobility::s1_source_ho_st::status_transfer_st::enter(s1_source_ho_st* f)
{
if (not is_ho_cmd_sent) {
f->trigger(srslte::failure_ev{});
}
f->log_h->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)?
/* Start S1AP eNBStatusTransfer Procedure */
@ -943,23 +936,19 @@ void rrc::ue::rrc_mobility::s1_source_ho_st::status_transfer_st::enter(s1_source
* intraENB Handover sub-FSM
************************************************************************************************/
void rrc::ue::rrc_mobility::handle_intraenb_meas_report(idle_st& s,
intraenb_ho_st& d,
const ho_meas_report_ev& meas_report)
void rrc::ue::rrc_mobility::intraenb_ho_st::enter(rrc_mobility* f, const ho_meas_report_ev& meas_report)
{
uint32_t cell_id = rrc_details::eci_to_cellid(meas_report.target_eci);
d.target_cell = rrc_enb->cell_common_list->get_cell_id(cell_id);
d.source_cell_ctxt = rrc_ue->cell_ded_list.get_ue_cc_idx(UE_PCELL_CC_IDX);
if (d.target_cell == nullptr) {
rrc_log->error("The target cell_id=0x%x was not found in the list of eNB cells\n", cell_id);
uint32_t cell_id = rrc_details::eci_to_cellid(meas_report.target_eci);
target_cell = f->rrc_enb->cell_common_list->get_cell_id(cell_id);
source_cell_ctxt = f->rrc_ue->cell_ded_list.get_ue_cc_idx(UE_PCELL_CC_IDX);
if (target_cell == nullptr) {
f->log_h->error("The target cell_id=0x%x was not found in the list of eNB cells\n", cell_id);
f->trigger(srslte::failure_ev{});
return;
}
Info("Starting intraeNB Handover of rnti=0x%x to 0x%x.\n", rrc_ue->rnti, meas_report.target_eci);
}
f->log_h->info("Starting intraeNB Handover of rnti=0x%x to 0x%x.\n", f->rrc_ue->rnti, meas_report.target_eci);
void rrc::ue::rrc_mobility::intraenb_ho_st::enter(rrc_mobility* f)
{
if (target_cell == nullptr) {
f->trigger(srslte::failure_ev{});
return;
@ -1000,7 +989,7 @@ void rrc::ue::rrc_mobility::intraenb_ho_st::enter(rrc_mobility* f)
}
}
void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, intraenb_ho_st& d, const user_crnti_upd_ev& ev)
void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, const user_crnti_upd_ev& ev)
{
rrc_log->info("UE performing handover updated its temp-crnti=0x%x to rnti=0x%x\n", ev.temp_crnti, ev.crnti);
bool is_first_crnti_ce = s.last_temp_crnti == SRSLTE_INVALID_RNTI;
@ -1025,7 +1014,7 @@ void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, intraenb_ho_st& d
}
}
void rrc::ue::rrc_mobility::handle_recfg_complete(intraenb_ho_st& s, idle_st& d, const recfg_complete_ev& ev)
void rrc::ue::rrc_mobility::handle_recfg_complete(intraenb_ho_st& s, const recfg_complete_ev& ev)
{
rrc_log->info(
"User rnti=0x%x successfully handovered to cell_id=0x%x\n", rrc_ue->rnti, s.target_cell->cell_cfg.cell_id);

Loading…
Cancel
Save