Embedded Template Library 1.0
Loading...
Searching...
No Matches
fsm.h
1/******************************************************************************
2The MIT License(MIT)
3
4Embedded Template Library.
5https://github.com/ETLCPP/etl
6https://www.etlcpp.com
7
8Copyright(c) 2017 John Wellbelove
9
10Permission is hereby granted, free of charge, to any person obtaining a copy
11of this software and associated documentation files(the "Software"), to deal
12in the Software without restriction, including without limitation the rights
13to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14copies of the Software, and to permit persons to whom the Software is
15furnished to do so, subject to the following conditions :
16
17The above copyright notice and this permission notice shall be included in all
18copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26SOFTWARE.
27******************************************************************************/
28
29#ifndef ETL_FSM_INCLUDED
30#define ETL_FSM_INCLUDED
31
32#include "platform.h"
33#include "array.h"
34#include "error_handler.h"
35#include "exception.h"
36#include "integral_limits.h"
37#include "largest.h"
38#include "message_router.h"
39#include "nullptr.h"
40#include "user_type.h"
41#if ETL_USING_CPP11
42 #include "tuple.h"
43 #include "type_list.h"
44#endif
46#include <stdint.h>
47
48#include "private/minmax_push.h"
49
50namespace etl
51{
52 class fsm;
53 class hfsm;
54
56#if !defined(ETL_FSM_STATE_ID_TYPE)
57 typedef uint_least8_t fsm_state_id_t;
58#else
59 typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t;
60#endif
61
62 // For internal FSM use.
63 typedef etl::larger_type<etl::message_id_t>::type fsm_internal_id_t;
64
65#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
66 template <typename, typename, etl::fsm_state_id_t, typename...>
67 class fsm_state;
68#else
69 #include "private/fsm_fwd_decl_cpp03.h"
70#endif
71
72 //***************************************************************************
74 //***************************************************************************
75 class fsm_exception : public etl::exception
76 {
77 public:
78
79 fsm_exception(string_type reason_, string_type file_name_, numeric_type line_number_)
80 : etl::exception(reason_, file_name_, line_number_)
81 {
82 }
83 };
84
85 //***************************************************************************
87 //***************************************************************************
88 class fsm_null_state_exception : public etl::fsm_exception
89 {
90 public:
91
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_)
94 {
95 }
96 };
97
98 //***************************************************************************
100 //***************************************************************************
101 class fsm_state_id_exception : public etl::fsm_exception
102 {
103 public:
104
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_)
108 }
109 };
110
111 //***************************************************************************
113 //***************************************************************************
114 class fsm_state_list_exception : public etl::fsm_exception
115 {
116 public:
117
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_)
120 {
121 }
122 };
123
124 //***************************************************************************
126 //***************************************************************************
127 class fsm_state_list_order_exception : public etl::fsm_exception
128 {
129 public:
130
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_)
133 {
134 }
135 };
136
137 //***************************************************************************
139 //***************************************************************************
140 class fsm_not_started : public etl::fsm_exception
141 {
142 public:
143
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_)
146 {
147 }
148 };
149
150 //***************************************************************************
154 //***************************************************************************
155 class fsm_reentrant_transition_forbidden : public etl::fsm_exception
156 {
157 public:
158
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_)
161 {
162 }
163 };
164
165 namespace private_fsm
166 {
167 template <typename T = void>
168 class ifsm_state_helper
169 {
170 public:
171
172 // Pass this whenever no state change is desired.
173 // The highest unsigned value of fsm_state_id_t.
174 static ETL_CONSTANT fsm_state_id_t No_State_Change = etl::integral_limits<fsm_state_id_t>::max;
175
176 // Pass this when this event also needs to be passed to the parent.
177 static ETL_CONSTANT fsm_state_id_t Pass_To_Parent = No_State_Change - 1U;
178
179 // Pass this when this event should trigger a self transition.
180 static ETL_CONSTANT fsm_state_id_t Self_Transition = No_State_Change - 2U;
181 };
182
183 template <typename T>
184 ETL_CONSTANT fsm_state_id_t ifsm_state_helper<T>::No_State_Change;
185
186 template <typename T>
187 ETL_CONSTANT fsm_state_id_t ifsm_state_helper<T>::Pass_To_Parent;
188
189 template <typename T>
190 ETL_CONSTANT fsm_state_id_t ifsm_state_helper<T>::Self_Transition;
191
192#if ETL_USING_CPP11
193 // Compile-time: TState::ID must equal its index in the type list (0..N-1)
194 template <size_t Id, typename...>
195 struct check_ids : etl::true_type
196 {
197 };
198
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>
202 {
203 };
204#endif
205
206 //***************************************************************************
210 //***************************************************************************
212 {
213 public:
214
215 //*******************************************
218 //*******************************************
219 fsm_reentrancy_guard(bool& transition_guard_flag)
220 : is_locked(transition_guard_flag)
221 {
223 is_locked = true;
224 }
225
226 //*******************************************
228 /// Releases lock on reentrancy.
229 //*******************************************
231 {
232 is_locked = false;
233 }
234
235 private:
236
237 // Reference to the flag signifying a lock on the state machine.
238 bool& is_locked;
239
240 // Copy & move semantics disabled since this is a guard.
242 fsm_reentrancy_guard& operator=(fsm_reentrancy_guard const&) ETL_DELETE;
243#if ETL_USING_CPP11
245 fsm_reentrancy_guard& operator=(fsm_reentrancy_guard&&) ETL_DELETE;
246#endif
247 };
248 } // namespace private_fsm
249
250 class ifsm_state;
251
252#if ETL_USING_CPP11
253 //***************************************************************************
255 //***************************************************************************
256 template <typename... TStates>
257 class fsm_state_pack
258 {
259 public:
260
261 friend class etl::fsm;
262
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");
267
268 //*********************************
269 // The number of states.
270 //*********************************
271 static ETL_CONSTEXPR size_t size()
272 {
273 return sizeof...(TStates);
274 }
275
276 //*********************************
278 //*********************************
279 template <typename TState>
280 TState& get()
281 {
282 return etl::get<TState>(storage);
283 }
284
285 //*********************************
286 /// Gets a const reference to the state.
287 //*********************************
288 template <typename TState>
289 const TState& get() const
290 {
291 return etl::get<TState>(storage);
292 }
293
294 private:
295
296 //*********************************
298 //*********************************
299 etl::ifsm_state** get_state_list()
300 {
301 return &states[0];
302 }
303
305 etl::tuple<TStates...> storage{};
306
308 etl::ifsm_state* states[sizeof...(TStates)]{&etl::get<TStates>(storage)...};
309 };
310#endif
311
312 //***************************************************************************
314 //***************************************************************************
316 {
317 public:
318
320 friend class etl::fsm;
321 friend class etl::hfsm;
322
323 using private_fsm::ifsm_state_helper<>::No_State_Change;
324 using private_fsm::ifsm_state_helper<>::Pass_To_Parent;
325 using private_fsm::ifsm_state_helper<>::Self_Transition;
326
327#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
328 template <typename, typename, etl::fsm_state_id_t, typename...>
329 friend class fsm_state;
330#else
331 #include "private/fsm_friend_decl_cpp03.h"
332#endif
333
334 //*******************************************
336 //*******************************************
338 {
339 return state_id;
340 }
341
342 //*******************************************
343 /// Adds a child to this state.
344 /// Only of use when part of an HFSM.
345 //*******************************************
347 {
348 ETL_ASSERT(state.p_parent == ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
349 state.p_parent = this;
350
351 if (p_default_child == ETL_NULLPTR)
352 {
353 p_default_child = &state;
354 }
355 }
356
357 //*******************************************
360 //*******************************************
361 template <typename TSize>
362 void set_child_states(etl::ifsm_state** state_list, TSize size)
363 {
364 p_active_child = ETL_NULLPTR;
365 p_default_child = ETL_NULLPTR;
366
367 for (TSize i = 0; i < size; ++i)
368 {
369 ETL_ASSERT(state_list[i] != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
370 add_child_state(*state_list[i]);
371 }
372 }
373
374 protected:
375
376 //*******************************************
378 //*******************************************
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)
385 {
386 }
387
388 //*******************************************
390 //*******************************************
391 virtual ~ifsm_state() {}
392
393 //*******************************************
394 etl::fsm& get_fsm_context() const
395 {
396 return *p_context;
397 }
398
399 private:
400
401 virtual fsm_state_id_t process_event(const etl::imessage& message) = 0;
402
403 virtual fsm_state_id_t on_enter_state()
404 {
405 return No_State_Change;
406 } // By default, do nothing.
407 virtual void on_exit_state() {} // By default, do nothing.
408
409 //*******************************************
410 void set_fsm_context(etl::fsm& context)
411 {
412 p_context = &context;
413 }
414
415 // The state id.
416 const etl::fsm_state_id_t state_id;
417
418 // A pointer to the FSM context.
419 etl::fsm* p_context;
420
421 // A pointer to the parent.
422 ifsm_state* p_parent;
423
424 // A pointer to the active child.
425 ifsm_state* p_active_child;
426
427 // A pointer to the default active child.
428 ifsm_state* p_default_child;
429
430 // Disabled.
431 ifsm_state(const ifsm_state&) ETL_DELETE;
432 ifsm_state& operator=(const ifsm_state&) ETL_DELETE;
433 };
434
435 //***************************************************************************
437 //***************************************************************************
439 {
440 public:
441
442 friend class etl::hfsm;
443 using imessage_router::receive;
444
445 //*******************************************
447 //*******************************************
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)
455 }
456
457 //*******************************************
460 //*******************************************
461 template <typename TSize>
462 void set_states(etl::ifsm_state** p_states, TSize size)
463 {
464 state_list = p_states;
465 number_of_states = etl::fsm_state_id_t(size);
466
467 ETL_ASSERT(number_of_states > 0, ETL_ERROR(etl::fsm_state_list_exception));
468 ETL_ASSERT(number_of_states < ifsm_state::No_State_Change, ETL_ERROR(etl::fsm_state_list_exception));
469
470 for (etl::fsm_state_id_t i = 0; i < size; ++i)
471 {
472 ETL_ASSERT(state_list[i] != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
473 ETL_ASSERT(state_list[i]->get_state_id() == i, ETL_ERROR(etl::fsm_state_list_order_exception));
474 state_list[i]->set_fsm_context(*this);
475 }
476 }
477
478#if ETL_USING_CPP11
479 //*******************************************
482 //*******************************************
483 template <typename... TStates>
484 void set_states(etl::fsm_state_pack<TStates...>& state_pack)
485 {
486 state_list = state_pack.get_state_list();
487 number_of_states = etl::fsm_state_id_t(state_pack.size());
488
489 for (etl::fsm_state_id_t i = 0; i < number_of_states; ++i)
490 {
491 state_list[i]->set_fsm_context(*this);
492 }
493 }
494#endif
495
496 //*******************************************
500
502 //*******************************************
503 virtual void start(bool call_on_enter_state = true)
504 {
505 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
506
507 // Can only be started once.
508 if (!is_started())
509 {
510 p_state = state_list[0];
511 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
512
513 if (call_on_enter_state)
514 {
515 etl::fsm_state_id_t next_state_id;
516 etl::ifsm_state* p_last_state;
517
518 do {
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)
522 {
523 ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
524 p_state = state_list[next_state_id];
525 }
526 } while (p_last_state != p_state);
527 }
528 }
529 }
530
531 //*******************************************
533 //*******************************************
534 void receive(const etl::imessage& message) ETL_OVERRIDE
535 {
536 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
537
538 if (is_started())
539 {
540 etl::fsm_state_id_t next_state_id = p_state->process_event(message);
541
542 process_state_change(next_state_id);
543 }
544 else
545 {
546 ETL_ASSERT_FAIL(ETL_ERROR(etl::fsm_not_started));
547 }
548 }
549
550 //*******************************************
552 //*******************************************
554 {
555 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
556
557 if (is_started())
558 {
559 return process_state_change(new_state_id);
560 }
561 else
562 {
563 return ifsm_state::No_State_Change;
564 }
565 }
566
567 using imessage_router::accepts;
568
569 //*******************************************
572 //*******************************************
573 bool accepts(etl::message_id_t) const ETL_OVERRIDE
574 {
575 return true;
576 }
577
578 //*******************************************
580 //*******************************************
582 {
583 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
584 return p_state->get_state_id();
585 }
586
587 //*******************************************
589 //*******************************************
591 {
592 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
593 return *p_state;
594 }
595
596 //*******************************************
598 //*******************************************
599 const ifsm_state& get_state() const
600 {
601 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
602 return *p_state;
603 }
604
605 //*******************************************
607 //*******************************************
608 bool is_started() const
609 {
610 return p_state != ETL_NULLPTR;
611 }
612
613 //*******************************************
617 //*******************************************
618 virtual void reset(bool call_on_exit_state = false)
619 {
620 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
621
622 if (is_started() && call_on_exit_state)
623 {
624 p_state->on_exit_state();
625 }
626
627 p_state = ETL_NULLPTR;
628 }
629
630 //********************************************
631 ETL_DEPRECATED
632 bool is_null_router() const ETL_OVERRIDE
633 {
634 return false;
635 }
636
637 //********************************************
638 bool is_producer() const ETL_OVERRIDE
639 {
640 return true;
641 }
642
643 //********************************************
644 bool is_consumer() const ETL_OVERRIDE
645 {
646 return true;
647 }
648
649 private:
650
651 //********************************************
652 bool have_changed_state(etl::fsm_state_id_t next_state_id) const
653 {
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);
656 }
657
658 //********************************************
659 bool is_self_transition(etl::fsm_state_id_t next_state_id) const
661 return (next_state_id == ifsm_state::Self_Transition);
662 }
663
664 //*******************************************
666 //*******************************************
667 virtual etl::fsm_state_id_t process_state_change(etl::fsm_state_id_t next_state_id)
668 {
669 if (is_self_transition(next_state_id))
670 {
671 p_state->on_exit_state();
672 next_state_id = p_state->on_enter_state();
673 }
674
675 if (have_changed_state(next_state_id))
676 {
677 ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
678 etl::ifsm_state* p_next_state = state_list[next_state_id];
679
680 do {
681 p_state->on_exit_state();
682 p_state = p_next_state;
683
684 next_state_id = p_state->on_enter_state();
685
686 if (have_changed_state(next_state_id))
687 {
688 ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
689 p_next_state = state_list[next_state_id];
690 }
691 } while (p_next_state != p_state); // Have we changed state again?
692 }
693
694 return p_state->get_state_id();
695 }
696
697 etl::ifsm_state* p_state;
698 etl::ifsm_state** state_list;
699 etl::fsm_state_id_t number_of_states;
700 bool is_processing_state_change;
702
703 };
704
705 //*************************************************************************************************
706 // For C++11 and above.
707 //*************************************************************************************************
708#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
709 //***************************************************************************
710 // The definition for all types.
711 //***************************************************************************
712 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
713 class fsm_state : public ifsm_state
714 {
715 private:
716
717 using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
718
719 public:
720
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>;
723
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");
728
729 static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
730
731 fsm_state()
732 : ifsm_state(STATE_ID)
733 {
734 }
735
736 protected:
737
738 ~fsm_state() {}
739
740 TContext& get_fsm_context() const
741 {
742 return static_cast<TContext&>(ifsm_state::get_fsm_context());
743 }
744
745 private:
746
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;
749
750 static_assert(Number_Of_Messages > 0, "Zero messages");
751
752 //**********************************************
753 // Checks that the message ids are contiguous.
754 //**********************************************
755 template <size_t Index, bool Last = (Index + 1U >= Number_Of_Messages)>
756 struct contiguous_impl;
757
758 template <size_t Index>
759 struct contiguous_impl<Index, true> : etl::true_type
760 {
761 };
762
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>
768 {
769 };
770
771 // The message ids are contiguous if there are 0 or 1 message types, or if
772 // each message id is one greater than the previous message id.
773 static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value;
774
775 using handler_ptr = etl::fsm_state_id_t (*)(TDerived&,
776 const etl::imessage&);
779 using message_dispatch_table_t = etl::array<handler_ptr,
780 Number_Of_Messages>;
783 using message_id_table_t = etl::array<etl::message_id_t,
784 Number_Of_Messages>;
787
788 //********************************************
789 etl::fsm_state_id_t process_event(const etl::imessage& message)
790 {
791 const etl::message_id_t id = message.get_message_id();
792
793 // The IDs are sorted, so an ID less than the first is not handled by this
794 // router.
795 if (id >= Message_Id_Start)
796 {
797 const size_t index = get_dispatch_index_from_message_id(id);
798
799 // If the index is less than Number_Of_Messages, then we have a handler
800 // for this message type, so dispatch it.
801 if (index < Number_Of_Messages)
802 {
803 const etl::fsm_state_id_t new_state_id = dispatch(message, index);
804
805 if (new_state_id != Pass_To_Parent)
806 {
807 return new_state_id;
808 }
809 }
810 }
811
813 // If we get here, then we don't have a handler for this message type, so
814 // pass it to the parent if we have one, otherwise call on_event_unknown.
815 return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
817 }
818
819 //**********************************************
820 // Call for a single message type
821 //**********************************************
822 template <typename TMessage>
823 static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg)
824 {
825 return derived.on_event(static_cast<const TMessage&>(msg));
826 }
827
828 //**********************************************
829 // Get the handler for a single message type at the index in the sorted
830 // type_list. This will be called for each message type to generate the
831 // dispatch table.
832 //**********************************************
833 template <size_t Index>
834 static constexpr handler_ptr get_message_handler()
835 {
836 return &call_on_event< etl::type_list_type_at_index_t<sorted_message_types, Index>>;
837 }
838
839 //**********************************************
840 // Generate the dispatch table at compile time.
841 // This will create an array of handler pointers, one for each message type.
842 //**********************************************
843 template <size_t... Indices>
844 static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence<Indices...>)
845 {
846 return message_dispatch_table_t{{get_message_handler<Indices>()...}};
847 }
848
849 //**********************************************
850 // Get the message id for a single message type at an index in the sorted
851 // type_list. This will be called for each message type to generate the
852 // message id table.
853 //**********************************************
854 template <size_t Index>
855 static constexpr etl::message_id_t get_message_id_from_index()
856 {
857 return etl::type_list_type_at_index_t<sorted_message_types, Index>::ID;
858 }
859
860 //**********************************************
861 // Generate the message id table at compile time.
862 // This will create an array of message ids, one for each message type.
863 //**********************************************
864 template <size_t... Indices>
865 static constexpr message_id_table_t make_message_id_table(etl::index_sequence<Indices...>)
866 {
867 return message_id_table_t{{get_message_id_from_index<Indices>()...}};
868 }
869
870 //**********************************************
871 // Get the dispatch index for a message id.
872 // This will be used at runtime to find the handler for a message id.
873 // If the message ids are contiguous, we can calculate the index directly.
874 // If they are not contiguous, we need to do a binary search. This will
875 // return Number_Of_Messages if the message id is not found.
876 //**********************************************
877 static size_t get_dispatch_index_from_message_id(etl::message_id_t id)
878 {
879 if ETL_IF_CONSTEXPR (Message_Ids_Are_Contiguous)
880 {
881 // The IDs are contiguous, so we can calculate the index directly.
882 return static_cast<size_t>(id - Message_Id_Start);
883 }
884 else
885 {
886 // The IDs are not contiguous, so we need to do a binary search.
887 size_t left = 0;
888 size_t right = Number_Of_Messages;
890 while (left < right)
891 {
892 size_t mid = (left + right) / 2;
893
894 if (message_id_table[mid] == id)
895 {
896 return mid;
897 }
898 else if (message_id_table[mid] < id)
899 {
900 left = mid + 1;
901 }
902 else
903 {
904 right = mid;
905 }
906 }
907 }
908
909 return Number_Of_Messages; // Not found
910 }
911
912 //**********************************************
913 // Dispatch the message to the appropriate handler based on the index in the
914 // dispatch table.
915 //**********************************************
916 etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index)
917 {
918 return message_dispatch_table[index](static_cast<TDerived&>(*this), msg);
919 }
920
921 //**********************************************
922 // The dispatch table is generated at compile time. The dispatch table
923 // contains pointers to the on_receive handlers for each message type.
924 //**********************************************
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>{});
928
929 //**********************************************
930 // The message id table is generated at compile time. The message id table
931 // contains the corresponding message ids for each message type.
932 //**********************************************
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>{});
936 };
937
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;
942
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;
946 #endif
947
949 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
950 ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
951
952 //***************************************************************************
953 // The definition for no messages.
954 //***************************************************************************
955 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_>
956 class fsm_state<TContext, TDerived, STATE_ID_> : public ifsm_state
957 {
958 private:
959
960 using message_id_sequence = etl::index_sequence<>;
961
962 public:
963
964 using message_types = etl::type_list<>;
965 using sorted_message_types = etl::type_list<>;
966
967 static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
968
969 fsm_state()
970 : ifsm_state(STATE_ID)
971 {
972 }
973
974 protected:
975
976 ~fsm_state() {}
977
978 TContext& get_fsm_context() const
979 {
980 return static_cast<TContext&>(ifsm_state::get_fsm_context());
981 }
982
983 private:
984
985 //********************************************
986 etl::fsm_state_id_t process_event(const etl::imessage& message)
987 {
988 return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
989 }
990 };
991
992#else
993 #include "private/fsm_cpp03.h"
994#endif
995} // namespace etl
996
997#include "private/minmax_pop.h"
998
999#endif
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
Definition fsm.h:46
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
Definition hfsm.h:42
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
Definition message.h:73
Definition message.h:92
~fsm_reentrancy_guard() ETL_NOEXCEPT
Definition fsm.h:230
fsm_reentrancy_guard(bool &transition_guard_flag)
Definition fsm.h:219
Definition array.h:85
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
Definition exception.h:59
Definition integral_limits.h:517
Definition largest.h:199
Definition absolute.h:40
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