diff --git a/lib/include/srslte/common/fsm.h b/lib/include/srslte/common/fsm.h index b68012bec..38ccd51ee 100644 --- a/lib/include/srslte/common/fsm.h +++ b/lib/include/srslte/common/fsm.h @@ -101,11 +101,25 @@ using fsm_transitions = typename FSM::derived_view::transitions; //! Detection of enter/exit methods of a state. template -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 +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 +inline void call_enter(FSM* f, State* s, Args&&...) +{ + call_enter2(f, s); +} template auto call_exit(FSM* f, State* s) -> decltype(s->exit(f)) { @@ -141,38 +155,47 @@ struct state_traits { using is_subfsm = std::integral_constant()>; //! enter new state. enter is called recursively for subFSMs - static void enter_state(FSM* f, State* s) { enter_(f, s, is_subfsm{}); } + template + 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 - static enable_if_fsm_state transit_state(FSM* f) + template + static enable_if_fsm_state transit_state(FSM* f, const Event& ev) { call_exit(f, &f->states.template get_unchecked()); f->states.template transit(); - state_traits::enter_state(f, &f->states.template get_unchecked()); + state_traits::enter_state(f, &f->states.template get_unchecked(), ev); } - template - static disable_if_fsm_state transit_state(FSM* f) + template + static disable_if_fsm_state transit_state(FSM* f, const Event& ev) { using parent_state_traits = state_traits; call_exit(f, &f->states.template get_unchecked()); - parent_state_traits::template transit_state(get_derived(f->parent_fsm())); + parent_state_traits::template transit_state(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 + static void enter_(FSM* f, State* s, const Event& ev, std::true_type) { using init_type = typename fsm_state_list_type::init_state_t; // set default FSM type get_derived(s)->states.template transit(); // call FSM enter function - call_enter(f, s); + call_enter(f, s, ev); // call initial substate enter state_traits::enter_state( - get_derived(s), &get_derived(s)->states.template get_unchecked()); + get_derived(s), &get_derived(s)->states.template get_unchecked(), ev); } //! In case of State is basic state - static void enter_(FSM* f, State* s, std::false_type) { call_enter(f, s); } + template + 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 > { get_type_name().c_str(), get_type_name().c_str()); // Apply state change operations - state_traits::template transit_state(f); + state_traits::template transit_state(f, ev); } return true; } @@ -294,23 +317,20 @@ public: template + 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(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 >; }; - template - 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; - constexpr static void (Derived::*react_fn)(DestState&, const Event&) = ReactFn; - - constexpr static bool (Derived::*guard_fn)(const Event&) const = GuardFn; + template + struct from_any_state { + using dest_state_t = DestState; + using event_t = Event; + constexpr static bool (Derived::*guard_fn)(const Event&) = GuardFn; template 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(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::enter_state(f->derived(), - &get_unchecked()); + fsm_details::state_traits::enter_state( + f->derived(), &get_unchecked(), std::false_type{}); } } @@ -552,15 +568,23 @@ protected: ParentFSM* fsm_ptr = nullptr; }; -// event -template +/************************** + * Procedure FSM + *************************/ + +template struct proc_launch_ev { - std::tuple args; + T args; +}; - explicit proc_launch_ev(Args&&... args_) : args(std::forward(args_)...) {} +template +struct proc_complete_ev { + Result result; }; -template +struct failure_ev {}; + +template class proc_fsm_t : public fsm_t { using fsm_type = Derived; @@ -574,74 +598,86 @@ public: using fsm_t::trigger; // events - struct reset_ev {}; + template + using launch_ev = srslte::proc_launch_ev; + using complete_ev = srslte::proc_complete_ev; // states struct idle_st { - idle_st() = default; - template - idle_st(bool success_, T&& r) : success(success_), result(std::forward(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().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().c_str(), f->launch_counter); - } + f->log_h->warning( + "FSM \"%s\": No result was set for run no. %d\n", get_type_name().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().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().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(log_) {} - bool is_running() const { return base_t::template is_in_state(); } - - bool is_success() const { return base_t::template get_state()->is_success(); } + bool is_running() const { return not base_t::template is_in_state(); } const Result& get_result() const { - if (is_success()) { - return base_t::template get_state->get_result(); + if (launch_counter > 0 and base_t::template is_in_state()) { + return last_result; } THROW_BAD_ACCESS("in proc_fsm_t::get_result"); } - template - void launch(Args&&... args) + template + void await(OtherFSM* f) { - trigger(proc_launch_ev(std::forward(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 > listening_fsms; }; -// Generic events +template +class proc_wait_st +{ +public: + explicit proc_wait_st(ProcFSM* proc_ptr_) : proc_ptr(proc_ptr_) {} -struct success_ev {}; -struct failure_ev {}; + template + 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}); + proc_ptr->await(f); + } + +private: + ProcFSM* proc_ptr = nullptr; +}; } // namespace srslte diff --git a/lib/test/common/fsm_test.cc b/lib/test/common/fsm_test.cc index c219c4683..ace54f30a 100644 --- a/lib/test/common/fsm_test.cc +++ b/lib/test/common/fsm_test.cc @@ -28,6 +28,15 @@ struct ev1 {}; struct ev2 {}; +std::vector calls; +template +void call_log_helper(State* state, srslte::log_ref& log_h, const char* type) +{ + std::string callname = srslte::get_type_name() + "::" + type; + log_h->info("%s custom called\n", callname.c_str()); + calls.push_back(callname); +} + class fsm1 : public srslte::fsm_t { 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 states{this}; // clang-format off using transitions = transition_table< - // Start Target Event Action + // Start Target Event Action // +------------+-------------+----+----------------------+ - row, + upd, row, row // +------------+-------------+----+----------------------+ @@ -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()); TESTASSERT(f.get_if_current_state()->current_state_name() == "state_inner"); + // Ensure correct call order + TESTASSERT(calls[0] == srslte::get_type_name() + "::enter"); // enter for init state called + TESTASSERT(calls[1] == srslte::get_type_name() + "::action1"); + TESTASSERT(calls[2] == srslte::get_type_name() + "::enter"); + TESTASSERT(calls[3] == srslte::get_type_name() + "::action2"); + TESTASSERT(calls[4] == srslte::get_type_name() + "::exit"); + TESTASSERT(calls[5] == srslte::get_type_name() + "::enter"); // entry is recursive + TESTASSERT(calls[6] == srslte::get_type_name() + "::enter"); + TESTASSERT(calls[7] == srslte::get_type_name() + "::inner_action1"); + TESTASSERT(calls[8] == srslte::get_type_name() + "::inner_action2"); + TESTASSERT(calls[9] == srslte::get_type_name() + "::enter"); + TESTASSERT(calls[10] == srslte::get_type_name() + "::exit"); + TESTASSERT(calls[11] == srslte::get_type_name() + "::exit"); // exit is recursive + TESTASSERT(calls[12] == srslte::get_type_name() + "::enter2"); // differentiates different entry funcs + return SRSLTE_SUCCESS; } @@ -251,52 +285,80 @@ struct procevent1 { struct proc1 : public srslte::proc_fsm_t { public: - struct procstate1 {}; + struct procstate1 { + void enter(proc1* f, const srslte::proc_launch_ev& 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& 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 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, &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 >, + 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& ev) +void proc1::procstate1::enter(proc1* f, const launch_ev& 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 { +public: + struct st1 {}; + struct st2 {}; + using proc1_st = srslte::proc_wait_st; + + 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 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{v}); TESTASSERT(proc.current_state_name() == "procstate1"); - proc.launch(&v); + proc.trigger(srslte::proc_launch_ev{v}); TESTASSERT(proc.current_state_name() == "procstate1"); - proc.trigger(5); + proc.trigger(srslte::proc_launch_ev{5}); TESTASSERT(proc.current_state_name() == "procstate1"); proc.trigger(procevent1{true}); TESTASSERT(proc.current_state_name() == "idle_st"); - TESTASSERT(proc.get_state()->is_success()); - proc.launch(&v); + TESTASSERT(proc.get_result() == 5); + proc.trigger(srslte::proc_launch_ev{v}); TESTASSERT(proc.current_state_name() == "procstate1"); proc.trigger(procevent1{false}); TESTASSERT(proc.current_state_name() == "idle_st"); - TESTASSERT(not proc.get_state()->is_success()); + TESTASSERT(proc.get_result() == 3); + + { + proc_listener_fsm outer_fsm{srslte::logmap::get("TEST"), &proc}; + TESTASSERT(outer_fsm.is_in_state()); + outer_fsm.trigger(6); + TESTASSERT(outer_fsm.is_in_state()); + TESTASSERT(proc.is_running()); + proc.trigger(procevent1{true}); + TESTASSERT(not proc.is_running()); + TESTASSERT(outer_fsm.is_in_state()); + } + + { + proc_listener_fsm outer_fsm{srslte::logmap::get("TEST"), &proc}; + TESTASSERT(outer_fsm.is_in_state()); + proc.trigger(srslte::proc_launch_ev{v}); + TESTASSERT(proc.is_running()); + outer_fsm.trigger(7); + TESTASSERT(outer_fsm.is_in_state()); + 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 + from_any_state< emm_deregistered, power_off_ev > + // +-----------------------------+-------------------------+-----------------------------+ >; // clang-format on }; @@ -463,8 +547,8 @@ struct fsm3 : public srslte::fsm_t { 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 }; diff --git a/srsenb/hdr/stack/rrc/rrc_mobility.h b/srsenb/hdr/stack/rrc/rrc_mobility.h index 40b7ba0e2..c901de137 100644 --- a/srsenb/hdr/stack/rrc/rrc_mobility.h +++ b/srsenb/hdr/stack/rrc/rrc_mobility.h @@ -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 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 }; diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index c3fa9bd54..661963414 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -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);