29#ifndef ETL_FSM_INCLUDED
30#define ETL_FSM_INCLUDED
38#include "message_router.h"
43 #include "type_list.h"
56#if !defined(ETL_FSM_STATE_ID_TYPE)
65#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION)
69 #include "private/fsm_fwd_decl_cpp03.h"
79 fsm_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
92 fsm_null_state_exception(string_type file_name_, numeric_type line_number_)
93 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:null state", ETL_FSM_FILE_ID
"A"), file_name_, line_number_)
105 fsm_state_id_exception(string_type file_name_, numeric_type line_number_)
106 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:state id", ETL_FSM_FILE_ID
"B"), file_name_, line_number_)
118 fsm_state_list_exception(string_type file_name_, numeric_type line_number_)
119 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:state list", ETL_FSM_FILE_ID
"C"), file_name_, line_number_)
131 fsm_state_list_order_exception(string_type file_name_, numeric_type line_number_)
132 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:state list order", ETL_FSM_FILE_ID
"D"), file_name_, line_number_)
144 fsm_not_started(string_type file_name_, numeric_type line_number_)
145 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:not started", ETL_FSM_FILE_ID
"F"), file_name_, line_number_)
159 fsm_reentrant_transition_forbidden(string_type file_name_, numeric_type line_number_)
160 :
etl::fsm_exception(ETL_ERROR_TEXT(
"fsm:reentrant calls to start/receive/etc. forbidden", ETL_FSM_FILE_ID
"G"), file_name_, line_number_)
165 namespace private_fsm
167 template <
typename T =
void>
168 class ifsm_state_helper
177 static ETL_CONSTANT
fsm_state_id_t Pass_To_Parent = No_State_Change - 1U;
180 static ETL_CONSTANT
fsm_state_id_t Self_Transition = No_State_Change - 2U;
183 template <
typename T>
184 ETL_CONSTANT
fsm_state_id_t ifsm_state_helper<T>::No_State_Change;
186 template <
typename T>
189 template <
typename T>
190 ETL_CONSTANT
fsm_state_id_t ifsm_state_helper<T>::Self_Transition;
194 template <
size_t Id,
typename...>
195 struct check_ids : etl::true_type
199 template <
size_t Id,
typename TState0,
typename... TRest>
200 struct check_ids<Id, TState0, TRest...>
201 :
etl::integral_constant< bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
220 : is_locked(transition_guard_flag)
256 template <
typename... TStates>
263 ETL_STATIC_ASSERT((private_fsm::check_ids<0, TStates...>::value),
"State IDs must be 0..N-1 and in order");
264 ETL_STATIC_ASSERT(
sizeof...(TStates) > 0,
"At least one state is required");
265 ETL_STATIC_ASSERT(
sizeof...(TStates) < private_fsm::ifsm_state_helper<>::No_State_Change,
266 "State IDs mst be less than ifsm_state::No_State_Change");
271 static ETL_CONSTEXPR
size_t size()
273 return sizeof...(TStates);
279 template <
typename TState>
288 template <
typename TState>
289 const TState&
get()
const
305 etl::tuple<TStates...> storage{};
327#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION)
329 friend class fsm_state;
331 #include "private/fsm_friend_decl_cpp03.h"
349 state.p_parent =
this;
351 if (p_default_child == ETL_NULLPTR)
353 p_default_child = &state;
361 template <
typename TSize>
364 p_active_child = ETL_NULLPTR;
365 p_default_child = ETL_NULLPTR;
367 for (TSize i = 0; i <
size; ++i)
380 : state_id(state_id_)
381 , p_context(ETL_NULLPTR)
382 , p_parent(ETL_NULLPTR)
383 , p_active_child(ETL_NULLPTR)
384 , p_default_child(ETL_NULLPTR)
405 return No_State_Change;
407 virtual void on_exit_state() {}
410 void set_fsm_context(
etl::fsm& context)
412 p_context = &context;
443 using imessage_router::receive;
448 fsm(etl::message_router_id_t
id)
449 : imessage_router(id)
450 , p_state(ETL_NULLPTR)
451 , state_list(ETL_NULLPTR)
452 , number_of_states(0U)
453 , is_processing_state_change(false)
461 template <
typename TSize>
464 state_list = p_states;
474 state_list[i]->set_fsm_context(*
this);
483 template <
typename... TStates>
484 void set_states(etl::fsm_state_pack<TStates...>& state_pack)
486 state_list = state_pack.get_state_list();
491 state_list[i]->set_fsm_context(*
this);
503 virtual void start(
bool call_on_enter_state =
true)
510 p_state = state_list[0];
513 if (call_on_enter_state)
519 p_last_state = p_state;
520 next_state_id = p_state->on_enter_state();
521 if (next_state_id != ifsm_state::No_State_Change)
524 p_state = state_list[next_state_id];
526 }
while (p_last_state != p_state);
542 process_state_change(next_state_id);
559 return process_state_change(new_state_id);
563 return ifsm_state::No_State_Change;
567 using imessage_router::accepts;
584 return p_state->get_state_id();
610 return p_state != ETL_NULLPTR;
618 virtual void reset(
bool call_on_exit_state =
false)
624 p_state->on_exit_state();
627 p_state = ETL_NULLPTR;
632 bool is_null_router() const ETL_OVERRIDE
638 bool is_producer() const ETL_OVERRIDE
644 bool is_consumer() const ETL_OVERRIDE
654 return (next_state_id != p_state->get_state_id()) && (next_state_id != ifsm_state::No_State_Change)
655 && (next_state_id != ifsm_state::Self_Transition);
661 return (next_state_id == ifsm_state::Self_Transition);
669 if (is_self_transition(next_state_id))
671 p_state->on_exit_state();
672 next_state_id = p_state->on_enter_state();
675 if (have_changed_state(next_state_id))
681 p_state->on_exit_state();
682 p_state = p_next_state;
684 next_state_id = p_state->on_enter_state();
686 if (have_changed_state(next_state_id))
689 p_next_state = state_list[next_state_id];
691 }
while (p_next_state != p_state);
694 return p_state->get_state_id();
697 etl::ifsm_state* p_state;
698 etl::ifsm_state** state_list;
700 bool is_processing_state_change;
708#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION)
712 template <
typename TContext,
typename TDerived,
etl::fsm_state_id_t STATE_ID_,
typename... TMessageTypes>
717 using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
721 using message_types = etl::type_list<TMessageTypes...>;
722 using sorted_message_types = etl::type_list_sort_t<message_types, etl::compare_message_id_less>;
724 static_assert(etl::type_list_is_unique<message_types>::value,
"All TMessageTypes must be unique");
725 static_assert(etl::type_list_all_of<message_types, etl::is_message_type>::value,
726 "All TMessageTypes must satisfy the condition etl::is_message_type");
727 static_assert(etl::index_sequence_is_unique<message_id_sequence>::value,
"All message IDs must be unique");
740 TContext& get_fsm_context()
const
742 return static_cast<TContext&
>(ifsm_state::get_fsm_context());
747 static constexpr size_t Number_Of_Messages =
sizeof...(TMessageTypes);
748 static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t<sorted_message_types, 0>::ID;
750 static_assert(Number_Of_Messages > 0,
"Zero messages");
755 template <
size_t Index,
bool Last = (Index + 1U >= Number_Of_Messages)>
756 struct contiguous_impl;
758 template <
size_t Index>
759 struct contiguous_impl<Index, true> : etl::true_type
763 template <
size_t Index>
764 struct contiguous_impl<Index, false>
765 :
etl::bool_constant< (etl::type_list_type_at_index_t<sorted_message_types, Index>::ID + 1U
766 == etl::type_list_type_at_index_t<sorted_message_types, Index + 1U>::ID)
767 && contiguous_impl<Index + 1U>::value>
773 static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value;
779 using message_dispatch_table_t =
etl::array<handler_ptr,
795 if (
id >= Message_Id_Start)
797 const size_t index = get_dispatch_index_from_message_id(
id);
801 if (index < Number_Of_Messages)
805 if (new_state_id != Pass_To_Parent)
815 return (p_parent !=
nullptr) ? p_parent->process_event(message) :
static_cast<TDerived*
>(
this)->on_event_unknown(message);
822 template <
typename TMessage>
825 return derived.on_event(
static_cast<const TMessage&
>(msg));
833 template <
size_t Index>
834 static constexpr handler_ptr get_message_handler()
836 return &call_on_event< etl::type_list_type_at_index_t<sorted_message_types, Index>>;
843 template <
size_t... Indices>
844 static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence<Indices...>)
846 return message_dispatch_table_t{{get_message_handler<Indices>()...}};
854 template <
size_t Index>
857 return etl::type_list_type_at_index_t<sorted_message_types, Index>::ID;
864 template <
size_t... Indices>
865 static constexpr message_id_table_t make_message_id_table(etl::index_sequence<Indices...>)
867 return message_id_table_t{{get_message_id_from_index<Indices>()...}};
879 if ETL_IF_CONSTEXPR (Message_Ids_Are_Contiguous)
882 return static_cast<size_t>(
id - Message_Id_Start);
888 size_t right = Number_Of_Messages;
892 size_t mid = (left + right) / 2;
894 if (message_id_table[mid] ==
id)
898 else if (message_id_table[mid] <
id)
909 return Number_Of_Messages;
918 return message_dispatch_table[index](
static_cast<TDerived&
>(*this), msg);
925 static ETL_INLINE_VAR
constexpr message_dispatch_table_t message_dispatch_table =
926 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_dispatch_table(
927 etl::make_index_sequence< etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
933 static ETL_INLINE_VAR
constexpr message_id_table_t message_id_table =
934 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_id_table(
935 etl::make_index_sequence< etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
938 #if ETL_USING_CPP11 && !ETL_USING_CPP17
939 template <
typename TContext,
typename TDerived,
etl::fsm_state_id_t STATE_ID_,
typename... TMessageTypes>
940 constexpr const typename etl::fsm_state< TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table_t
941 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table;
943 template <
typename TContext,
typename TDerived,
etl::fsm_state_id_t STATE_ID_,
typename... TMessageTypes>
944 constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table_t
945 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table;
949 template <
typename TContext,
typename TDerived,
etl::fsm_state_id_t STATE_ID_,
typename... TMessageTypes>
955 template <
typename TContext,
typename TDerived, etl::fsm_state_
id_t STATE_ID_>
960 using message_id_sequence = etl::index_sequence<>;
964 using message_types = etl::type_list<>;
965 using sorted_message_types = etl::type_list<>;
978 TContext& get_fsm_context()
const
980 return static_cast<TContext&
>(ifsm_state::get_fsm_context());
988 return (p_parent !=
nullptr) ? p_parent->process_event(message) :
static_cast<TDerived*
>(
this)->on_event_unknown(message);
993 #include "private/fsm_cpp03.h"
Base exception class for FSM.
Definition fsm.h:76
Exception for message received but not started.
Definition fsm.h:141
Exception for null state pointer.
Definition fsm.h:89
Exception for invalid state id.
Definition fsm.h:102
Exception for incompatible state list.
Definition fsm.h:115
Exception for incompatible order state list.
Definition fsm.h:128
The FSM class.
Definition fsm.h:439
etl::fsm_state_id_t get_state_id() const
Gets the current state id.
Definition fsm.h:581
void receive(const etl::imessage &message) ETL_OVERRIDE
Top level message handler for the FSM.
Definition fsm.h:534
virtual void start(bool call_on_enter_state=true)
Definition fsm.h:503
fsm(etl::message_router_id_t id)
Constructor.
Definition fsm.h:448
virtual void reset(bool call_on_exit_state=false)
Definition fsm.h:618
bool accepts(etl::message_id_t) const ETL_OVERRIDE
Definition fsm.h:573
etl::fsm_state_id_t transition_to(etl::fsm_state_id_t new_state_id)
Invoke a state transition.
Definition fsm.h:553
void set_states(etl::ifsm_state **p_states, TSize size)
Definition fsm.h:462
const ifsm_state & get_state() const
Gets a const reference to the current state interface.
Definition fsm.h:599
ifsm_state & get_state()
Gets a reference to the current state interface.
Definition fsm.h:590
bool is_started() const
Checks if the FSM has been started.
Definition fsm.h:608
Interface class for FSM states.
Definition fsm.h:316
void add_child_state(etl::ifsm_state &state)
Definition fsm.h:346
void set_child_states(etl::ifsm_state **state_list, TSize size)
Definition fsm.h:362
etl::fsm_state_id_t get_state_id() const
Gets the id for this state.
Definition fsm.h:337
ifsm_state(etl::fsm_state_id_t state_id_)
Constructor.
Definition fsm.h:379
virtual ~ifsm_state()
Destructor.
Definition fsm.h:391
friend class etl::fsm
Allows ifsm_state functions to be private.
Definition fsm.h:320
This is the base of all message routers.
Definition message_router.h:141
~fsm_reentrancy_guard() ETL_NOEXCEPT
Definition fsm.h:230
fsm_reentrancy_guard(bool &transition_guard_flag)
Definition fsm.h:219
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
Definition exception.h:59
Definition integral_limits.h:517
uint_least8_t message_id_t
Allow alternative type for message id.
Definition message_types.h:40
ETL_CONSTEXPR TContainer::size_type size(const TContainer &container)
Definition iterator.h:1434
T & get(array< T, Size > &a)
Definition array.h:1158
uint_least8_t fsm_state_id_t
Allow alternative type for state id.
Definition fsm.h:57
Definition type_traits.h:97
integral_constant
Definition type_traits.h:67