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 typename 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 // Compile-time: TState::ID must equal its index in the type list (0..N-1)
193 template <size_t Id, typename...>
194 struct check_ids : etl::true_type
195 {
196 };
197
198 template <size_t Id, typename TState0, typename... TRest>
199 struct check_ids<Id, TState0, TRest...>
200 : etl::integral_constant< bool, (TState0::STATE_ID == Id) && private_fsm::check_ids<Id + 1, TRest...>::value>
201 {
202 };
203
204 //***************************************************************************
208 //***************************************************************************
210 {
211 public:
212
213 //*******************************************
216 //*******************************************
217 fsm_reentrancy_guard(bool& transition_guard_flag)
218 : is_locked(transition_guard_flag)
219 {
221 is_locked = true;
222 }
223
224 //*******************************************
227 //*******************************************
229 {
230 is_locked = false;
231 }
232
233 private:
234
235 // Reference to the flag signifying a lock on the state machine.
236 bool& is_locked;
237
238 // Copy & move semantics disabled since this is a guard.
239 fsm_reentrancy_guard(fsm_reentrancy_guard const&) ETL_DELETE;
240 fsm_reentrancy_guard& operator=(fsm_reentrancy_guard const&) ETL_DELETE;
241#if ETL_USING_CPP11
242 fsm_reentrancy_guard(fsm_reentrancy_guard&&) ETL_DELETE;
243 fsm_reentrancy_guard& operator=(fsm_reentrancy_guard&&) ETL_DELETE;
244#endif
245 };
246 } // namespace private_fsm
247
248 class ifsm_state;
249
250#if ETL_USING_CPP11
251 //***************************************************************************
253 //***************************************************************************
254 template <typename... TStates>
255 class fsm_state_pack
256 {
257 public:
258
259 friend class etl::fsm;
260
261 ETL_STATIC_ASSERT((private_fsm::check_ids<0, TStates...>::value), "State IDs must be 0..N-1 and in order");
262 ETL_STATIC_ASSERT(sizeof...(TStates) > 0, "At least one state is required");
263 ETL_STATIC_ASSERT(sizeof...(TStates) < private_fsm::ifsm_state_helper<>::No_State_Change,
264 "State IDs mst be less than ifsm_state::No_State_Change");
265
266 //*********************************
267 // The number of states.
268 //*********************************
269 static ETL_CONSTEXPR size_t size()
270 {
271 return sizeof...(TStates);
272 }
273
274 //*********************************
276 //*********************************
277 template <typename TState>
278 TState& get()
279 {
280 return etl::get<TState>(storage);
281 }
282
283 //*********************************
285 //*********************************
286 template <typename TState>
287 const TState& get() const
288 {
289 return etl::get<TState>(storage);
290 }
291
292 private:
293
294 //*********************************
296 //*********************************
297 etl::ifsm_state** get_state_list()
298 {
299 return &states[0];
300 }
301
303 etl::tuple<TStates...> storage{};
304
306 etl::ifsm_state* states[sizeof...(TStates)]{&etl::get<TStates>(storage)...};
307 };
308#endif
309
310 //***************************************************************************
312 //***************************************************************************
314 {
315 public:
316
318 friend class etl::fsm;
319 friend class etl::hfsm;
320
321 using private_fsm::ifsm_state_helper<>::No_State_Change;
322 using private_fsm::ifsm_state_helper<>::Pass_To_Parent;
323 using private_fsm::ifsm_state_helper<>::Self_Transition;
324
325#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
326 template <typename, typename, etl::fsm_state_id_t, typename...>
327 friend class fsm_state;
328#else
329 #include "private/fsm_friend_decl_cpp03.h"
330#endif
331
332 //*******************************************
334 //*******************************************
336 {
337 return state_id;
338 }
339
340 //*******************************************
343 //*******************************************
345 {
346 ETL_ASSERT(state.p_parent == ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
347 state.p_parent = this;
348
349 if (p_default_child == ETL_NULLPTR)
350 {
351 p_default_child = &state;
352 }
353 }
354
355 //*******************************************
358 //*******************************************
359 template <typename TSize>
360 void set_child_states(etl::ifsm_state** state_list, TSize size)
361 {
362 p_active_child = ETL_NULLPTR;
363 p_default_child = ETL_NULLPTR;
364
365 for (TSize i = 0; i < size; ++i)
366 {
367 ETL_ASSERT(state_list[i] != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
368 add_child_state(*state_list[i]);
369 }
370 }
371
372 protected:
373
374 //*******************************************
376 //*******************************************
378 : state_id(state_id_)
379 , p_context(ETL_NULLPTR)
380 , p_parent(ETL_NULLPTR)
381 , p_active_child(ETL_NULLPTR)
382 , p_default_child(ETL_NULLPTR)
383 {
384 }
385
386 //*******************************************
388 //*******************************************
389 virtual ~ifsm_state() {}
390
391 //*******************************************
392 etl::fsm& get_fsm_context() const
393 {
394 return *p_context;
395 }
396
397 private:
398
399 virtual fsm_state_id_t process_event(const etl::imessage& message) = 0;
400
401 virtual fsm_state_id_t on_enter_state()
402 {
403 return No_State_Change;
404 } // By default, do nothing.
405 virtual void on_exit_state() {} // By default, do nothing.
406
407 //*******************************************
408 void set_fsm_context(etl::fsm& context)
409 {
410 p_context = &context;
411 }
412
413 // The state id.
414 const etl::fsm_state_id_t state_id;
415
416 // A pointer to the FSM context.
417 etl::fsm* p_context;
418
419 // A pointer to the parent.
420 ifsm_state* p_parent;
421
422 // A pointer to the active child.
423 ifsm_state* p_active_child;
424
425 // A pointer to the default active child.
426 ifsm_state* p_default_child;
427
428 // Disabled.
429 ifsm_state(const ifsm_state&) ETL_DELETE;
430 ifsm_state& operator=(const ifsm_state&) ETL_DELETE;
431 };
432
433 //***************************************************************************
435 //***************************************************************************
437 {
438 public:
439
440 friend class etl::hfsm;
441 using imessage_router::receive;
442
443 //*******************************************
445 //*******************************************
446 fsm(etl::message_router_id_t id)
447 : imessage_router(id)
448 , p_state(ETL_NULLPTR)
449 , state_list(ETL_NULLPTR)
450 , number_of_states(0U)
451 , is_processing_state_change(false)
452 {
453 }
455 //*******************************************
458 //*******************************************
459 template <typename TSize>
460 void set_states(etl::ifsm_state** p_states, TSize size)
461 {
462 state_list = p_states;
463 number_of_states = etl::fsm_state_id_t(size);
464
465 ETL_ASSERT(number_of_states > 0, ETL_ERROR(etl::fsm_state_list_exception));
466 ETL_ASSERT(number_of_states < ifsm_state::No_State_Change, ETL_ERROR(etl::fsm_state_list_exception));
467
468 for (etl::fsm_state_id_t i = 0; i < size; ++i)
469 {
470 ETL_ASSERT(state_list[i] != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
471 ETL_ASSERT(state_list[i]->get_state_id() == i, ETL_ERROR(etl::fsm_state_list_order_exception));
472 state_list[i]->set_fsm_context(*this);
473 }
474 }
475
476#if ETL_USING_CPP11
477 //*******************************************
480 //*******************************************
481 template <typename... TStates>
482 void set_states(etl::fsm_state_pack<TStates...>& state_pack)
483 {
484 state_list = state_pack.get_state_list();
485 number_of_states = etl::fsm_state_id_t(state_pack.size());
486
487 for (etl::fsm_state_id_t i = 0; i < number_of_states; ++i)
488 {
489 state_list[i]->set_fsm_context(*this);
490 }
491 }
492#endif
493
494 //*******************************************
500 //*******************************************
501 virtual void start(bool call_on_enter_state = true)
502 {
503 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
504
505 // Can only be started once.
506 if (!is_started())
508 p_state = state_list[0];
509 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
510
511 if (call_on_enter_state)
512 {
513 etl::fsm_state_id_t next_state_id;
514 etl::ifsm_state* p_last_state;
515
516 do {
517 p_last_state = p_state;
518 next_state_id = p_state->on_enter_state();
519 if (next_state_id != ifsm_state::No_State_Change)
520 {
521 ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
522 p_state = state_list[next_state_id];
523 }
524 } while (p_last_state != p_state);
525 }
526 }
527 }
528
529 //*******************************************
531 //*******************************************
532 void receive(const etl::imessage& message) ETL_OVERRIDE
533 {
534 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
535
536 if (is_started())
537 {
538 etl::fsm_state_id_t next_state_id = p_state->process_event(message);
539
540 process_state_change(next_state_id);
541 }
542 else
543 {
544 ETL_ASSERT_FAIL(ETL_ERROR(etl::fsm_not_started));
545 }
546 }
547
548 //*******************************************
550 //*******************************************
552 {
553 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
554
555 if (is_started())
556 {
557 return process_state_change(new_state_id);
558 }
559 else
560 {
561 return ifsm_state::No_State_Change;
562 }
563 }
564
565 using imessage_router::accepts;
566
567 //*******************************************
570 //*******************************************
571 bool accepts(etl::message_id_t) const ETL_OVERRIDE
572 {
573 return true;
574 }
575
576 //*******************************************
578 //*******************************************
580 {
581 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
582 return p_state->get_state_id();
583 }
584
585 //*******************************************
587 //*******************************************
589 {
590 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
591 return *p_state;
592 }
593
594 //*******************************************
596 //*******************************************
597 const ifsm_state& get_state() const
598 {
599 ETL_ASSERT(p_state != ETL_NULLPTR, ETL_ERROR(etl::fsm_null_state_exception));
600 return *p_state;
601 }
602
603 //*******************************************
605 //*******************************************
606 bool is_started() const
607 {
608 return p_state != ETL_NULLPTR;
609 }
611 //*******************************************
615 //*******************************************
616 virtual void reset(bool call_on_exit_state = false)
617 {
618 private_fsm::fsm_reentrancy_guard transition_lock(is_processing_state_change);
619
620 if (is_started() && call_on_exit_state)
621 {
622 p_state->on_exit_state();
623 }
624
625 p_state = ETL_NULLPTR;
626 }
627
628 //********************************************
629 ETL_DEPRECATED
630 bool is_null_router() const ETL_OVERRIDE
631 {
632 return false;
633 }
634
635 //********************************************
636 bool is_producer() const ETL_OVERRIDE
637 {
638 return true;
639 }
640
641 //********************************************
642 bool is_consumer() const ETL_OVERRIDE
643 {
644 return true;
645 }
646
647 private:
648
649 //********************************************
650 bool have_changed_state(etl::fsm_state_id_t next_state_id) const
651 {
652 return (next_state_id != p_state->get_state_id()) && (next_state_id != ifsm_state::No_State_Change)
653 && (next_state_id != ifsm_state::Self_Transition);
654 }
655
656 //********************************************
657 bool is_self_transition(etl::fsm_state_id_t next_state_id) const
658 {
659 return (next_state_id == ifsm_state::Self_Transition);
661
662 //*******************************************
664 //*******************************************
665 virtual etl::fsm_state_id_t process_state_change(etl::fsm_state_id_t next_state_id)
666 {
667 if (is_self_transition(next_state_id))
668 {
669 p_state->on_exit_state();
670 next_state_id = p_state->on_enter_state();
671 }
672
673 if (have_changed_state(next_state_id))
674 {
675 ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
676 etl::ifsm_state* p_next_state = state_list[next_state_id];
677
678 do {
679 p_state->on_exit_state();
680 p_state = p_next_state;
681
682 next_state_id = p_state->on_enter_state();
683
684 if (have_changed_state(next_state_id))
685 {
686 ETL_ASSERT_OR_RETURN_VALUE(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception), p_state->get_state_id());
687 p_next_state = state_list[next_state_id];
688 }
689 } while (p_next_state != p_state); // Have we changed state again?
690 }
691
692 return p_state->get_state_id();
693 }
694
695 etl::ifsm_state* p_state;
696 etl::ifsm_state** state_list;
697 etl::fsm_state_id_t number_of_states;
698 bool is_processing_state_change;
701 };
702
703 //*************************************************************************************************
704 // For C++11 and above.
705 //*************************************************************************************************
706#if ETL_USING_CPP11 && !defined(ETL_FSM_FORCE_CPP03_IMPLEMENTATION) // For C++11 and above
707 //***************************************************************************
708 // The definition for all types.
709 //***************************************************************************
710 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
711 class fsm_state : public ifsm_state
712 {
713 private:
714
715 using message_id_sequence = etl::index_sequence<TMessageTypes::ID...>;
716
717 public:
718
719 using message_types = etl::type_list<TMessageTypes...>;
720 using sorted_message_types = etl::type_list_sort_t<message_types, etl::compare_message_id_less>;
721
722 static_assert(etl::type_list_is_unique<message_types>::value, "All TMessageTypes must be unique");
723 static_assert(etl::type_list_all_of<message_types, etl::is_message_type>::value,
724 "All TMessageTypes must satisfy the condition etl::is_message_type");
725 static_assert(etl::index_sequence_is_unique<message_id_sequence>::value, "All message IDs must be unique");
726
727 static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
728
729 fsm_state()
730 : ifsm_state(STATE_ID)
731 {
732 }
733
734 protected:
735
736 ~fsm_state() {}
737
738 TContext& get_fsm_context() const
739 {
740 return static_cast<TContext&>(ifsm_state::get_fsm_context());
741 }
742
743 private:
744
745 static constexpr size_t Number_Of_Messages = sizeof...(TMessageTypes);
746 static constexpr etl::message_id_t Message_Id_Start = etl::type_list_type_at_index_t<sorted_message_types, 0>::ID;
747
748 static_assert(Number_Of_Messages > 0, "Zero messages");
749
750 //**********************************************
751 // Checks that the message ids are contiguous.
752 //**********************************************
753 template <size_t Index, bool Last = (Index + 1U >= Number_Of_Messages)>
754 struct contiguous_impl;
756 template <size_t Index>
757 struct contiguous_impl<Index, true> : etl::true_type
758 {
759 };
760
761 template <size_t Index>
762 struct contiguous_impl<Index, false>
763 : etl::bool_constant< (etl::type_list_type_at_index_t<sorted_message_types, Index>::ID + 1U
764 == etl::type_list_type_at_index_t<sorted_message_types, Index + 1U>::ID)
765 && contiguous_impl<Index + 1U>::value>
766 {
767 };
768
769 // The message ids are contiguous if there are 0 or 1 message types, or if
770 // each message id is one greater than the previous message id.
771 static constexpr bool Message_Ids_Are_Contiguous = (Number_Of_Messages <= 1U) ? true : contiguous_impl<0U>::value;
772
773 using handler_ptr = etl::fsm_state_id_t (*)(TDerived&,
774 const etl::imessage&);
777 using message_dispatch_table_t = etl::array<handler_ptr,
778 Number_Of_Messages>;
781 using message_id_table_t = etl::array<etl::message_id_t,
782 Number_Of_Messages>;
785
786 //********************************************
787 etl::fsm_state_id_t process_event(const etl::imessage& message)
788 {
789 const etl::message_id_t id = message.get_message_id();
790
791 // The IDs are sorted, so an ID less than the first is not handled by this
792 // router.
793 if (id >= Message_Id_Start)
794 {
795 const size_t index = get_dispatch_index_from_message_id(id);
796
797 // If the index is less than Number_Of_Messages, then we have a handler
798 // for this message type, so dispatch it.
799 if (index < Number_Of_Messages)
800 {
801 const etl::fsm_state_id_t new_state_id = dispatch(message, index);
802
803 if (new_state_id != Pass_To_Parent)
804 {
805 return new_state_id;
806 }
807 }
808 }
809
811 // If we get here, then we don't have a handler for this message type, so
812 // pass it to the parent if we have one, otherwise call on_event_unknown.
813 return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
815 }
816
817 //**********************************************
818 // Call for a single message type
819 //**********************************************
820 template <typename TMessage>
821 static etl::fsm_state_id_t call_on_event(TDerived& derived, const imessage& msg)
822 {
823 return derived.on_event(static_cast<const TMessage&>(msg));
824 }
825
826 //**********************************************
827 // Get the handler for a single message type at the index in the sorted
828 // type_list. This will be called for each message type to generate the
829 // dispatch table.
830 //**********************************************
831 template <size_t Index>
832 static constexpr handler_ptr get_message_handler()
833 {
834 return &call_on_event< etl::type_list_type_at_index_t<sorted_message_types, Index>>;
835 }
836
837 //**********************************************
838 // Generate the dispatch table at compile time.
839 // This will create an array of handler pointers, one for each message type.
840 //**********************************************
841 template <size_t... Indices>
842 static constexpr message_dispatch_table_t make_message_dispatch_table(etl::index_sequence<Indices...>)
843 {
844 return message_dispatch_table_t{{get_message_handler<Indices>()...}};
845 }
847 //**********************************************
848 // Get the message id for a single message type at an index in the sorted
849 // type_list. This will be called for each message type to generate the
850 // message id table.
851 //**********************************************
852 template <size_t Index>
853 static constexpr etl::message_id_t get_message_id_from_index()
854 {
855 return etl::type_list_type_at_index_t<sorted_message_types, Index>::ID;
856 }
857
858 //**********************************************
859 // Generate the message id table at compile time.
860 // This will create an array of message ids, one for each message type.
861 //**********************************************
862 template <size_t... Indices>
863 static constexpr message_id_table_t make_message_id_table(etl::index_sequence<Indices...>)
864 {
865 return message_id_table_t{{get_message_id_from_index<Indices>()...}};
866 }
867
868 //**********************************************
869 // Get the dispatch index for a message id.
870 // This will be used at runtime to find the handler for a message id.
871 // If the message ids are contiguous, we can calculate the index directly.
872 // If they are not contiguous, we need to do a binary search. This will
873 // return Number_Of_Messages if the message id is not found.
874 //**********************************************
875 static size_t get_dispatch_index_from_message_id(etl::message_id_t id)
876 {
877 if ETL_IF_CONSTEXPR (Message_Ids_Are_Contiguous)
878 {
879 // The IDs are contiguous, so we can calculate the index directly.
880 return static_cast<size_t>(id - Message_Id_Start);
881 }
882 else
883 {
884 // The IDs are not contiguous, so we need to do a binary search.
885 size_t left = 0;
886 size_t right = Number_Of_Messages;
887
888 while (left < right)
890 size_t mid = (left + right) / 2;
891
892 if (message_id_table[mid] == id)
893 {
894 return mid;
895 }
896 else if (message_id_table[mid] < id)
897 {
898 left = mid + 1;
899 }
900 else
901 {
902 right = mid;
903 }
904 }
905 }
906
907 return Number_Of_Messages; // Not found
908 }
909
910 //**********************************************
911 // Dispatch the message to the appropriate handler based on the index in the
912 // dispatch table.
913 //**********************************************
914 etl::fsm_state_id_t dispatch(const etl::imessage& msg, size_t index)
915 {
916 return message_dispatch_table[index](static_cast<TDerived&>(*this), msg);
917 }
918
919 //**********************************************
920 // The dispatch table is generated at compile time. The dispatch table
921 // contains pointers to the on_receive handlers for each message type.
922 //**********************************************
923 static ETL_INLINE_VAR constexpr message_dispatch_table_t message_dispatch_table =
924 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_dispatch_table(
925 etl::make_index_sequence< etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
926
927 //**********************************************
928 // The message id table is generated at compile time. The message id table
929 // contains the corresponding message ids for each message type.
930 //**********************************************
931 static ETL_INLINE_VAR constexpr message_id_table_t message_id_table =
932 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::make_message_id_table(
933 etl::make_index_sequence< etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::Number_Of_Messages>{});
934 };
935
936 #if ETL_USING_CPP11 && !ETL_USING_CPP17
937 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
938 constexpr const typename etl::fsm_state< TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table_t
939 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_dispatch_table;
940
941 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
942 constexpr const typename etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table_t
943 etl::fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::message_id_table;
944 #endif
945
947 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_, typename... TMessageTypes>
948 ETL_CONSTANT etl::fsm_state_id_t fsm_state<TContext, TDerived, STATE_ID_, TMessageTypes...>::STATE_ID;
949
950 //***************************************************************************
951 // The definition for no messages.
952 //***************************************************************************
953 template <typename TContext, typename TDerived, etl::fsm_state_id_t STATE_ID_>
954 class fsm_state<TContext, TDerived, STATE_ID_> : public ifsm_state
955 {
956 private:
957
958 using message_id_sequence = etl::index_sequence<>;
959
960 public:
961
962 using message_types = etl::type_list<>;
963 using sorted_message_types = etl::type_list<>;
964
965 static ETL_CONSTANT etl::fsm_state_id_t STATE_ID = STATE_ID_;
966
967 fsm_state()
968 : ifsm_state(STATE_ID)
969 {
970 }
971
972 protected:
973
974 ~fsm_state() {}
975
976 TContext& get_fsm_context() const
977 {
978 return static_cast<TContext&>(ifsm_state::get_fsm_context());
979 }
980
981 private:
982
983 //********************************************
984 etl::fsm_state_id_t process_event(const etl::imessage& message)
985 {
986 return (p_parent != nullptr) ? p_parent->process_event(message) : static_cast<TDerived*>(this)->on_event_unknown(message);
987 }
988 };
989
990#else
991 #include "private/fsm_cpp03.h"
992#endif
993} // namespace etl
994
995#include "private/minmax_pop.h"
996
997#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:437
etl::fsm_state_id_t get_state_id() const
Gets the current state id.
Definition fsm.h:579
void receive(const etl::imessage &message) ETL_OVERRIDE
Top level message handler for the FSM.
Definition fsm.h:532
virtual void start(bool call_on_enter_state=true)
Definition fsm.h:501
fsm(etl::message_router_id_t id)
Constructor.
Definition fsm.h:446
virtual void reset(bool call_on_exit_state=false)
Definition fsm.h:616
bool accepts(etl::message_id_t) const ETL_OVERRIDE
Definition fsm.h:571
etl::fsm_state_id_t transition_to(etl::fsm_state_id_t new_state_id)
Invoke a state transition.
Definition fsm.h:551
void set_states(etl::ifsm_state **p_states, TSize size)
Definition fsm.h:460
const ifsm_state & get_state() const
Gets a const reference to the current state interface.
Definition fsm.h:597
ifsm_state & get_state()
Gets a reference to the current state interface.
Definition fsm.h:588
bool is_started() const
Checks if the FSM has been started.
Definition fsm.h:606
Definition hfsm.h:42
Interface class for FSM states.
Definition fsm.h:314
void add_child_state(etl::ifsm_state &state)
Definition fsm.h:344
void set_child_states(etl::ifsm_state **state_list, TSize size)
Definition fsm.h:360
etl::fsm_state_id_t get_state_id() const
Gets the id for this state.
Definition fsm.h:335
ifsm_state(etl::fsm_state_id_t state_id_)
Constructor.
Definition fsm.h:377
virtual ~ifsm_state()
Destructor.
Definition fsm.h:389
friend class etl::fsm
Allows ifsm_state functions to be private.
Definition fsm.h:318
This is the base of all message routers.
Definition message_router.h:138
Definition message.h:75
Definition message.h:94
~fsm_reentrancy_guard() ETL_NOEXCEPT
Definition fsm.h:228
fsm_reentrancy_guard(bool &transition_guard_flag)
Definition fsm.h:217
Definition array.h:88
#define ETL_ASSERT(b, e)
Definition error_handler.h:511
Definition exception.h:59
Definition integral_limits.h:518
Definition largest.h:199
bitset_ext
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:1192
T & get(array< T, Size > &a)
Definition array.h:1161
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
Definition fsm.h:195