tsm 0.1.0
Loading...
Searching...
No Matches
tsm.h
Go to the documentation of this file.
1#pragma once
2#include <functional>
3#include <tuple>
4#include <type_traits>
5#include <utility>
6#include <variant>
7
8#ifdef __FREE_RTOS__
9#include "FreeRTOS.h"
10#include "semphr.h"
11#include "task.h"
12#else
13#include <condition_variable>
14#include <mutex>
15#include <thread>
16#endif // __FREE_RTOS__
17
18#ifdef __linux__
19#include <cstdio> // perror
20#include <sched.h>
21#include <sys/mman.h>
22#include <time.h>
23#endif
24
25namespace tsm {
26namespace detail {
27
28// Apply a wrapper to a tuple of types
29template<template<class> class Wrapper, typename Tuple>
31
32template<template<class> class Wrapper, typename... Ts>
33struct wrap_type_impl<Wrapper, std::tuple<Ts...>> {
34 using type = std::tuple<typename Wrapper<Ts>::type...>;
35};
36
37template<template<class> class Wrapper, typename Tuple>
39
40// Append unique type to a tuple
41template<typename T, typename Tuple>
43
44template<typename T>
45struct append_unique_impl<T, std::tuple<>> {
46 using type = std::tuple<T>;
47};
48
49template<typename T, typename... Ts>
50struct append_unique_impl<T, std::tuple<Ts...>> {
51 // Add T to the tuple if it is not found among Ts..., otherwise, leave as
52 // is.
53 using type = std::conditional_t<!(std::is_same_v<T, Ts> || ...),
54 std::tuple<Ts..., T>,
55 std::tuple<Ts...>>;
56};
57
58template<typename T, typename Tuple>
60
61// Remove duplicates from a tuple
62// Base case for empty tuple, just provides a type 'type' equal to Us
63template<typename Ts, typename Us>
65
66// Recursive case: tries to append T to Us if it's not already there and
67// recurses with rest of Ts
68template<typename... Us>
69struct unique_tuple_impl<std::tuple<>, std::tuple<Us...>> {
70 using type = std::tuple<Us...>;
71};
72
73// Recursive case: tries to append T to Us if it's not already there and
74// recurses with rest of Ts
75template<typename T, typename... Ts, typename... Us>
76struct unique_tuple_impl<std::tuple<T, Ts...>, std::tuple<Us...>> {
77 using type =
78 typename unique_tuple_impl<std::tuple<Ts...>,
79 append_unique<T, std::tuple<Us...>>>::type;
80};
81
82// Public interface, defaults to starting with an empty unique set (Us)
83template<typename Ts, typename Us = std::tuple<>>
87
88template<typename Ts>
90
91// Rename tuple to variant
92template<typename Tuple>
94
95template<typename... Ts>
96struct tuple_to_variant_impl<std::tuple<Ts...>> {
97 using type = std::variant<Ts...>;
98};
99
100// Pull out the Ts... from a tuple and create a std::
101template<typename Tuple>
103
104// Event
105struct Event {};
106
107template<typename T>
109 using type = T;
110};
111
112// Transition
113// Dummy action and guard - hopefully, these will be optimized away
114
115template<typename From, typename Event, typename To>
117 using from = From;
118 using event = Event;
119 using to = To;
120};
121
122template<typename From,
123 typename Event,
124 typename To,
125 auto Action = []() {},
126 auto Guard = []() { return true; }>
127struct Transition : BaseTransition<From, Event, To> {
128 Transition() = default;
129 ~Transition() = default;
130 using action_t = decltype(Action);
131 using guard_t = decltype(Guard);
132 static constexpr action_t action = Action;
133 static constexpr guard_t guard = Guard;
134};
135
137 int ticks_{0};
138};
139
140template<typename From,
141 typename To,
142 auto Action = []() {},
143 auto Guard = []() { return true; }>
145 : Transition<From, ClockTickEvent, To, Action, Guard> {};
146
147// get_states from TransitionTable
148template<typename Tuple, typename... Ts>
150
151// Base case: No more transitions to process, just return the accumulated
152// states.
153template<typename Tuple>
154struct get_states_impl<Tuple> {
155 using type = Tuple;
156};
157
158// Recursive case: Process the first Transition, then recurse on the rest.
159template<typename Tuple, typename First, typename... Rest>
160struct get_states_impl<Tuple, First, Rest...> {
161 // Accumulate 'from' state
163 // Accumulate 'to' state
165
166 // Recurse with the updated tuple and the remaining transitions
167 using type = typename get_states_impl<to_states, Rest...>::type;
168};
169
170template<typename... Ts>
172
173template<typename... Ts>
174struct get_states<std::tuple<Ts...>> {
175 using type = typename get_states_impl<std::tuple<>, Ts...>::type;
176};
177
178template<typename... Ts>
179using get_states_t = typename get_states<Ts...>::type;
180
181// TransitionMap
182
183// Forward declaration for transition_map_helper with a default index parameter
184// starting from 0
185template<typename From,
186 typename Event,
187 typename TransitionsTuple,
188 size_t Index = 0,
189 typename = void>
191
192// Helper trait to check if a transition matches 'From' and 'Event'
193template<typename Transition, typename From, typename Event>
194struct is_transition_match : std::false_type {};
195
196template<typename From,
197 typename Event,
198 typename To,
199 auto Action,
200 auto Guard>
201struct is_transition_match<Transition<From, Event, To, Action, Guard>,
202 From,
203 Event> : std::true_type {};
204
205// Iteration through the transitions
206template<typename From, typename Event, typename... Transitions, size_t Index>
208 From,
209 Event,
210 std::tuple<Transitions...>,
211 Index,
212 std::enable_if_t<(Index < sizeof...(Transitions))>> {
214 std::tuple_element_t<Index, std::tuple<Transitions...>>;
215 static constexpr bool match =
217
218 using type = typename std::conditional_t<
219 match,
221 typename transition_map_helper<From,
222 Event,
223 std::tuple<Transitions...>,
224 Index + 1>::type>;
225};
226
227// Base case: Reached the end of the tuple without finding a match
228template<typename From, typename Event, typename... Transitions, size_t Index>
230 From,
231 Event,
232 std::tuple<Transitions...>,
233 Index,
234 std::enable_if_t<!(Index < sizeof...(Transitions))>> {
235 using type = void; // Return void if no transition matches
236};
237
238// Wrapper to start the search
239template<typename From, typename Event, typename Transitions>
243
244// find transition
245template<typename From, typename Event, typename Transitions>
248
249// variable template to check for the existence of a valid transition
250template<typename State, typename Event, typename Transitions>
251inline constexpr bool has_valid_transition_v =
252 !std::is_same_v<typename TransitionMap<State, Event, Transitions>::type,
253 void>;
254
255// SFINAE test for exit method
256template<typename T, typename Event, typename = void>
257struct has_exit : std::false_type {};
258
259template<typename T, typename Event>
260struct has_exit<T,
261 Event,
262 std::void_t<std::disjunction<
263 std::is_invocable<decltype(&T::exit), T>,
264 std::is_invocable<decltype(&T::exit), T, T&>,
265 std::is_invocable<decltype(&T::exit), T, Event>>>>
266 : std::true_type {};
267
268template<typename T, typename Event>
269inline constexpr bool has_exit_v = has_exit<T, Event>::value;
270
271// SFINAE test for entry method
272template<typename T, typename Event, typename = void>
273struct has_entry : std::false_type {};
274
275template<typename T, typename Event>
276struct has_entry<T,
277 Event,
278 std::void_t<std::disjunction<
279 std::is_invocable<decltype(&T::entry), T>,
280 std::is_invocable<decltype(&T::entry), T&>,
281 std::is_invocable<decltype(&T::entry), T, Event>>>>
282 : std::true_type {};
283
284template<typename T, typename Event>
286
287// SFINAE test for guard method
288template<typename T, typename Event, typename = void>
289struct has_guard : std::false_type {};
290
291template<typename T, typename Event>
292struct has_guard<T,
293 Event,
294 std::void_t<std::disjunction<
295 std::is_invocable<decltype(&T::guard), T>,
296 std::is_invocable<decltype(&T::guard), T&>,
297 std::is_invocable<decltype(&T::guard), T, Event>>>>
298 : std::true_type {};
299
300template<typename T, typename Event>
302
303// SFINAE test for action method
304template<typename T, typename Event, typename = void>
305struct has_action : std::false_type {};
306
307template<typename T, typename Event>
308struct has_action<T,
309 Event,
310 std::void_t<std::disjunction<
311 std::is_invocable<decltype(&T::action), T>,
312 std::is_invocable<decltype(&T::action), T&>,
313 std::is_invocable<decltype(&T::action), T, Event>>>>
314 : std::true_type {};
315
316template<typename T, typename Event>
318
319// Trait to check for the presence of T::transitions
320template<typename, typename = std::void_t<>>
321struct has_transitions : std::false_type {};
322
323template<typename T>
324struct has_transitions<T, std::void_t<typename T::transitions>>
325 : std::true_type {};
326
327template<typename T>
329
330template<typename T>
332
333// Define a trait to check for the presence of State::handle method for a given
334// Event and Context
335template<typename State,
336 typename Event,
337 typename Context,
338 typename = std::void_t<>>
339struct has_handle_method : std::false_type {};
340
341template<typename State, typename Event, typename Context>
343 State,
344 Event,
345 Context,
346 std::void_t<decltype(std::declval<State>().handle(std::declval<Context&>(),
347 std::declval<Event>()))>>
348 : std::true_type {};
349
350// Helper variable template
351template<typename State, typename Event, typename Context>
352inline constexpr bool has_handle_method_v =
354
355// Trait to check for the presence of T::is_hsm
356template<typename, typename = std::void_t<>>
357struct is_hsm_trait : std::false_type {};
358
359template<typename T>
360struct is_hsm_trait<T, std::void_t<decltype(T::is_hsm)>> : std::true_type {};
361
362template<typename T>
364
365template<typename T>
367
368// Define a helper to check for 'from' and 'to' types in transitions,
369template<typename T>
370inline constexpr bool is_state_trait_v =
371 std::conjunction_v<has_transitions<T>, std::negation<is_hsm_trait<T>>>;
372
373template<typename T, typename = void>
374struct is_clocked_hsm : std::false_type {};
375
376template<typename T>
377struct is_clocked_hsm<T, std::void_t<decltype(T::is_clocked_hsm)>>
378 : std::true_type {};
379
380template<typename T>
382
383// Hsm
384template<typename T, typename transitions = typename T::transitions>
385struct Hsm : T {
386 static constexpr bool is_hsm = true;
388 using HsmType = type; // alias for policy classes
389 using initial_state = typename std::tuple_element_t<0, transitions>::from;
391
393 : current_state_(&std::get<initial_state>(states_)) {}
394
395 // for rvalue reference and copy
396 template<typename Event>
397 bool handle(Event&& e) {
398 // using Event = std::decay_t<Evt>;
399 // Event e = std::forward<Evt>(event);
400 return std::visit(
401 [this, &e](auto* state) {
402 using State = std::decay_t<decltype(*state)>;
403 // check Event is same as decayed e
404 bool handled = false;
405 // if current_state is a state machine, call handle on it
406 if constexpr (is_hsm_trait_t<State>::value) {
407 handled = state->handle(std::forward<Event>(e));
408 }
409 if (!handled) {
410 // Does State implement handle for Event?
411 if constexpr (has_valid_transition_v<State,
412 std::decay_t<Event>,
413 transitions>) {
414 using transition = find_transition_t<State,
415 std::decay_t<Event>,
416 transitions>;
418 state, static_cast<Event&&>(e));
419 handled = true;
420 }
421 }
422 return handled;
423 },
425 }
426
427 template<typename Event, typename State>
428 void entry(Event&& e, State* state) noexcept {
429 if constexpr (has_entry_v<State, Event>) {
430 if constexpr (std::is_invocable_v<decltype(&State::entry),
431 State*,
432 T&,
433 decltype(e)>) {
434 state->entry(static_cast<T&>(*this), std::forward<Event>(e));
435 } else if constexpr (std::is_invocable_v<decltype(&State::entry),
436 State*,
437 T&>) {
438 state->entry(static_cast<T&>(*this));
439 } else if constexpr (std::is_invocable_v<decltype(&State::entry),
440 State*>) {
441 state->entry();
442 }
443 }
444 }
445
446 template<typename Event, typename State>
447 void exit(Event&& e, State* state) noexcept {
448 if constexpr (has_exit_v<State, Event>) {
449 if constexpr (std::is_invocable_v<decltype(&State::exit),
450 State*,
451 T&,
452 decltype(e)>) {
453 state->exit(static_cast<T&>(*this), std::forward<Event>(e));
454 } else if constexpr (std::is_invocable_v<decltype(&State::exit),
455 State*,
456 T&>) {
457 state->exit(static_cast<T&>(*this));
458 } else if constexpr (std::is_invocable_v<decltype(&State::exit),
459 State*>) {
460 state->exit();
461 }
462 }
463 }
464 // Check Guard
465 template<typename Tn,
466 typename Event = typename Tn::event,
467 typename State = typename Tn::from>
469 if constexpr (has_guard_v<State, Event>) {
470 if constexpr (std::is_invocable_v<decltype(&State::guard),
471 State*,
472 T&,
473 decltype(e)>) {
474 return state->guard(static_cast<T&>(*this),
475 std::forward<Event>(e));
476 } else if constexpr (std::is_invocable_v<decltype(&State::guard),
477 State*,
478 T&>) {
479 return state->guard(static_cast<T&>(*this));
480 } else if constexpr (std::is_invocable_v<decltype(&State::guard),
481 State*>) {
482 return state->guard();
483 }
484 } else if constexpr (std::is_member_function_pointer_v<decltype(Tn::guard)>) {
485 auto& ctx = static_cast<T&>(*this);
486 return std::invoke(Tn::guard, ctx);
487 }
488 return true;
489 }
490
491 // Perform action
492 template<typename Tn,
493 typename Event = typename Tn::event,
494 typename State = typename Tn::from>
496 if constexpr (has_action_v<State, Event>) {
497 if constexpr (std::is_invocable_v<decltype(&State::action),
498 State*,
499 T&,
500 decltype(e)>) {
501 state->action(static_cast<T&>(*this), std::forward<Event>(e));
502 } else if constexpr (std::is_invocable_v<decltype(&State::action),
503 State*,
504 T&>) {
505 state->action(static_cast<T&>(*this));
506 } else if constexpr (std::is_invocable_v<decltype(&State::action),
507 State*>) {
508 state->action();
509 }
510 }
511 else if constexpr (std::is_member_function_pointer_v<decltype(Tn::action)>) {
512 auto& ctx = static_cast<T&>(*this);
513 std::invoke(Tn::action, ctx);
514 }
515 }
516
517 template<typename transition,
518 typename State = typename transition::from,
519 typename Event = typename transition::event>
520 std::enable_if_t<!has_handle_method_v<State, Event, T>, void>
521 handle_transition(typename transition::from* state, Event&& e) {
522 // Assume TransitionMap provides the matching transition
523 if (!this->check_guard<transition>(std::forward<Event>(e), state)) {
524 return;
525 }
526
527 this->template exit<Event, State>(std::forward<Event>(e), state);
528
529 // Optional Action
530 this->perform_action<transition>(std::forward<Event>(e), state);
531
532 using to = typename transition::to;
533
534 // switch to the new state
535 current_state_ = &std::get<to>(states_);
536
537 this->template entry<Event, to>(std::forward<Event>(e),
538 *std::get_if<to*>(&current_state_));
539 }
540
541 template<typename transition,
542 typename State = typename transition::from,
543 typename Event = typename transition::event>
544 std::enable_if_t<has_handle_method_v<State, Event, T>, void>
546
547 // A true gives permission to transition
548 if (!state->handle(static_cast<T&>(*this), std::forward<Event>(e))) {
549 return;
550 }
551
552 using to = typename transition::to;
553
554 // switch to the new state
555 current_state_ = &std::get<to>(states_);
556
557 this->template entry<Event, to>(std::forward<Event>(e),
558 *std::get_if<to*>(&current_state_));
559 }
560
561 template<typename State>
563 current_state_ = &std::get<State>(states_);
564 }
565
567 transitions transitions_;
569};
570
571template<typename T, typename = void>
572struct make_hsm;
573
574template<typename T>
575struct make_hsm<T, std::enable_if_t<!is_state_trait_v<T>>> {
576 using type = T;
577};
578
579template<typename T>
581
582// Recursively wrap states in HSMs if they are state traits
583template<typename T>
585 using from = typename T::from;
586 using event = typename T::event;
587 using to = typename T::to;
588
589 using wrap_from =
590 std::conditional_t<is_state_trait_v<from>, make_hsm_t<from>, from>;
591 using wrap_to =
592 std::conditional_t<is_state_trait_v<to>, make_hsm_t<to>, to>;
593
595};
596
597template<typename T>
599
600template<typename... Ts>
602
603template<typename... Ts>
604struct wrap_transitions<std::tuple<Ts...>> {
605 using type = std::tuple<wrap_transition_t<Ts>...>;
606};
607
608template<typename... Ts>
609using wrap_transitions_t = typename wrap_transitions<Ts...>::type;
610
611// Clocked HSM - react to events after a certain time period
612// This is a wrapper around HSM that adds a tick method to the HSM
613template<typename T, template<typename> class Policy = make_hsm_t>
614struct ClockedHsm : Policy<T> {
616 using HsmType = typename Policy<T>::type;
617
618 constexpr static bool is_clocked_hsm = true;
619
620 bool tick() { return this->handle(tick_event_); }
621
622 template<typename Event>
623 bool handle(Event e = Event()) {
624 return HsmType::handle(e);
625 }
626
629 return HsmType::handle(e);
630 }
632};
633
634// Define the wrapper for state traits to convert them into HSMs including their
635// transitions
636template<typename T>
637struct make_hsm<T, std::enable_if_t<is_state_trait_v<T>>> {
638 // shadow the transitions with the wrapped transitions
640
642};
643
644// Orthogonal HSM
645template<typename... Hsms>
647 static constexpr bool is_hsm = true;
649
650 template<typename Event>
651 void entry(Event e = Event()) {
652 std::apply([e](auto&... hsm) { (hsm.entry(e), ...); }, hsms_);
653 }
654
655 template<typename Event>
656 void exit(Event e = Event()) {
657 std::apply([e](auto&... hsm) { (hsm.exit(e), ...); }, hsms_);
658 }
659
660 template<typename Event>
661 bool handle(Event e = Event()) {
662 return std::apply([e](auto&... hsm) { return (hsm.handle(e) && ...); },
663 hsms_);
664 }
665
666 std::tuple<Hsms...> hsms_;
667};
668
669// A thread safe event queue. Any thread can call add_event if it has a pointer
670// to the event queue. The call to nextEvent is a blocking call
671template<typename Event, typename LockType, typename ConditionVarType>
674
675 virtual ~EventQueueT() { stop(); }
676
677 public:
678 // Block until you get an event
680 std::unique_lock<LockType> lock(eventQueueMutex_);
681 cvEventAvailable_.wait(
682 lock, [this] { return (!this->empty() || this->interrupt_); });
683 if (interrupt_) {
684 return Event();
685 }
686 const Event e = std::move(front());
687 pop_front();
688 return e;
689 }
690
691 void add_event(Event&& e) {
692 if (interrupt_) {
693 return;
694 }
695 std::lock_guard<LockType> lock(eventQueueMutex_);
696 push_back(std::forward<Event>(e));
697 cvEventAvailable_.notify_all();
698 }
699
700 void stop() {
701 interrupt_ = true;
702 cvEventAvailable_.notify_all();
703 // Log the events that are going to get dumped if the queue is not
704 // empty
705 }
706
707 bool interrupted() { return interrupt_; }
708
709 protected:
710 bool empty() { return push_index_ == pop_index_; }
711
712 Event const& front() { return data_[pop_index_]; }
713
714 void pop_front() {
715 if (!empty()) {
716 pop_index_ = (pop_index_ + 1) % data_.size();
717 }
718 }
719
720 bool push_back(Event const& e) {
721 if ((push_index_ + 1) % data_.size() != pop_index_) {
722 data_[push_index_] = e;
723 push_index_ = (push_index_ + 1) % data_.size();
724 return true;
725 }
726 return false;
727 }
728
729 private:
730 LockType eventQueueMutex_;
731 ConditionVarType cvEventAvailable_;
732 bool interrupt_{};
733 size_t push_index_{ 0 };
734 size_t pop_index_{ 0 };
735 std::array<Event, 50> data_;
736};
737
738#ifdef __FREE_RTOS__
739constexpr int MaxEvents = 10; // Define your own queue size
740
742 public:
744 // Create the FreeRTOS mutex
745 this->mutex = xSemaphoreCreateMutex();
746 }
747
749 // Delete the FreeRTOS mutex
750 vSemaphoreDelete(this->mutex);
751 }
752
753 void lock() {
754 // Wait forever for the mutex to become available
755 xSemaphoreTake(this->mutex, portMAX_DELAY);
756 }
757
758 bool try_lock() {
759 // Try to take the mutex without blocking
760 return xSemaphoreTake(this->mutex, 0) == pdTRUE;
761 }
762
763 void unlock() {
764 // Release the mutex
765 xSemaphoreGive(this->mutex);
766 }
767
768 // Return the native handle of the mutex
769 SemaphoreHandle_t native_handle() { return this->mutex; }
770
771 private:
772 SemaphoreHandle_t mutex;
773};
774
776 public:
778 : semaphore_(xSemaphoreCreateBinary())
779 , waitersCount_(0) {
780 // Ensure the semaphore is in a non-signalled state initially.
781 xSemaphoreTake(semaphore_, 0);
782 }
783
784 ~FreeRTOSConditionVariable() { vSemaphoreDelete(semaphore_); }
785
786 template<typename Lock>
787 void wait(Lock& lock) {
788 // Increment the count of waiters.
789 UBaseType_t oldISRState = taskENTER_CRITICAL_FROM_ISR();
790 ++waitersCount_;
791 taskEXIT_CRITICAL_FROM_ISR(oldISRState);
792
793 // Unlock the external mutex and wait for notification.
794 lock.unlock();
795 xSemaphoreTake(semaphore_, portMAX_DELAY);
796
797 // Decrement the count of waiters.
798 oldISRState = taskENTER_CRITICAL_FROM_ISR();
799 --waitersCount_;
800 taskEXIT_CRITICAL_FROM_ISR(oldISRState);
801
802 // Re-acquire the mutex before exiting.
803 lock.lock();
804 }
805
806 void notify_one() {
807 if (waitersCount_ > 0) { // Check if there are any waiters.
808 xSemaphoreGive(semaphore_); // Wake up one waiter.
809 }
810 }
811
812 void notify_all() {
813 while (waitersCount_ > 0) { // Keep waking all waiters.
814 xSemaphoreGive(semaphore_);
815 // Small delay to allow other tasks to respond to the semaphore.
816 vTaskDelay(pdMS_TO_TICKS(10));
817 }
818 }
819
820 private:
821 SemaphoreHandle_t semaphore_;
822 volatile UBaseType_t waitersCount_;
823};
824
825// C++ 11 compatible clock with FreeRTOS tick count.
827 using duration = std::chrono::milliseconds;
828 using period = duration::period;
829 using rep = duration::rep;
830 using time_point = std::chrono::time_point<FreeRTOSClock>;
831
832 static constexpr bool is_steady = true;
833
834 static time_point now() noexcept {
835 TickType_t ticks = xTaskGetTickCount();
836 // Convert ticks to milliseconds (note: configTICK_RATE_HZ is the number
837 // of ticks per second)
838 auto durationSinceEpoch =
839 std::chrono::milliseconds(ticks * (1000 / configTICK_RATE_HZ));
840 return time_point(duration(durationSinceEpoch));
841 }
842};
843
844template<typename Event>
846
847template<typename HsmType, typename Events>
849 public:
851 // using EventQueue = FreeRTOSEventQueue<Event>; // Adapted for FreeRTOS
853
856 interrupt_ = pdFALSE;
857 xTaskCreate(reinterpret_cast<TaskFunction_t>(taskCallback),
858 "ThreadedPolicyTask",
859 configMINIMAL_STACK_SIZE,
860 this,
861 tskIDLE_PRIORITY,
862 &smTaskHandle);
863 }
864
866 interrupt_ = pdTRUE;
867 // Proper FreeRTOS task deletion if needed, ensuring clean-up.
868 if (smTaskHandle != nullptr) {
869 vTaskDelete(smTaskHandle);
870 smTaskHandle = nullptr;
871 }
872 }
873
874 void send_event(const Event& event) { eventQueue.add_event(event); }
875
876 protected:
878 TaskHandle_t smTaskHandle{};
880 BaseType_t interrupt_;
881
882 static void StepTask(void* pvParameters) {
883 auto* policy = static_cast<ThreadedExecutionPolicy*>(pvParameters);
884 policy->step();
885 }
886
887 void step() {
888 while (!interrupt_) {
889 processEvent();
890 }
891 }
892
894 Event const& nextEvent = eventQueue.next_event();
895 if (!eventQueue.interrupted()) {
896 std::visit([this](auto const& e) { return this->handle(e); },
897 nextEvent);
898 }
899 }
900};
901
902#else // __FREE_RTOS__ is not defined
903
904template<typename Event>
905using EventQueue = EventQueueT<Event, std::mutex, std::condition_variable_any>;
906
907// C++ 11 compatible accurate clock (nanosecond precision). This is a drop-in
908// replacement for Clock types in std::chrono
909struct AccurateClock {
910 using duration = std::chrono::nanoseconds;
911 using period = duration::period;
912 using rep = duration::rep;
913 using time_point = std::chrono::time_point<AccurateClock>;
914
915 static constexpr bool is_steady = true;
916
917 static time_point now() noexcept {
918 struct timespec ts;
919 clock_gettime(CLOCK_MONOTONIC, &ts);
920 auto durationSinceEpoch = std::chrono::seconds(ts.tv_sec) +
921 std::chrono::nanoseconds(ts.tv_nsec);
922 return time_point(duration(durationSinceEpoch));
923 }
924};
925
926#endif
927
928// Forward declarations
929template<typename HsmType, typename = void>
931
932template<typename Transition>
934
935template<typename TransitionsTuple>
937
938// Specialize for transitions tuple
939template<typename... Transitions>
940struct aggregate_events<std::tuple<Transitions...>> {
941 using type = decltype(std::tuple_cat(
943};
944
945// Base case for empty tuple
946template<>
947struct aggregate_events<std::tuple<>> {
948 using type = std::tuple<>;
949};
950
951// Extract events from a single transition
952template<typename Transition>
954 using Event = std::tuple<typename Transition::event>;
957 using ToEvents =
959 using type = decltype(std::tuple_cat(Event{}, FromEvents{}, ToEvents{}));
960};
961
962// Main structure for extracting events from an HSM
963template<typename HsmType>
964struct get_events_from_hsm<HsmType,
965 std::enable_if_t<has_transitions_v<HsmType>>> {
966 using Transitions = typename HsmType::transitions;
968};
969
970// Fallback for non-HSM types
971template<typename HsmType>
972struct get_events_from_hsm<HsmType,
973 std::enable_if_t<!has_transitions_v<HsmType>>> {
974 using type = std::tuple<>;
975};
976
977// Helper alias
978template<typename HsmType>
981
982// Single threaded execution policy
983template<typename Context, template<typename> class Policy = make_hsm_t>
984struct SingleThreadedExecutionPolicy : Policy<Context> {
986 using HsmType = typename Policy<Context>::type;
988
989 bool step() {
990 // This is a blocking wait
991 Event const& nextEvent = eventQueue_.next_event();
992 // go down the Hsm hierarchy to handle the event as that is the
993 // "most active state"
994 return std::visit(
995 [this](auto&& e) -> bool { return HsmType::handle(e); }, nextEvent);
996 }
997
998 void send_event(Event&& event) {
999 eventQueue_.add_event(std::forward<Event>(event));
1000 }
1001
1002 private:
1003 EventQueue<Event> eventQueue_;
1004 bool interrupt_{};
1005};
1006
1007// Asynchronous execution policy
1008template<typename Context, template<typename> class Policy = make_hsm_t>
1009struct ThreadedExecutionPolicy : Policy<Context> {
1011 using HsmType = typename Policy<Context>::type;
1013
1014 void start() {
1015 smThread_ = std::thread([this] {
1016 while (!interrupt_) {
1017 this->process_event();
1018 }
1019 });
1020 }
1021
1022 void stop() {
1023 eventQueue_.stop();
1024 interrupt_ = true;
1025 if (smThread_.joinable()) {
1026 smThread_.join();
1027 }
1028 }
1029
1031
1032 void send_event(Event&& event) {
1033 eventQueue_.add_event(std::forward<Event>(event));
1034 }
1035
1036 protected:
1037 std::thread smThread_;
1040
1042 // This is a blocking wait
1043 Event const& nextEvent = eventQueue_.next_event();
1044 if (!eventQueue_.interrupted()) {
1045 std::visit([this](auto const& e) { return this->handle(e); },
1046 nextEvent);
1047 }
1048 }
1049};
1050
1057template<typename LockType, typename ConditionVarType>
1059 void notify() {
1060 std::unique_lock<std::mutex> lock(smBusyMutex_);
1061 notified_ = true;
1062 cv_.notify_all();
1063 }
1064
1065 void wait() {
1066 std::unique_lock<std::mutex> lock(smBusyMutex_);
1067 cv_.wait(lock, [this] { return this->notified_ == true; });
1068 notified_ = false;
1069 }
1070
1071 private:
1072 LockType smBusyMutex_;
1073 ConditionVarType cv_;
1074 bool notified_{};
1075};
1076
1077// Preserving for historical reasons
1079 void add_callback(std::function<void()>&& cb) {
1080 if (cb != nullptr) {
1081 cbs_.push_back(cb);
1082 }
1083 }
1084
1085 void notify() {
1086 for (auto const& cb : cbs_) {
1087 cb();
1088 }
1089 }
1090
1091 private:
1092 std::vector<std::function<void()>> cbs_;
1093};
1094
1095#ifdef __FREE_RTOS__
1098#else
1100#endif
1101
1108template<typename Observer,
1109 typename Context,
1110 template<typename> class Policy = ThreadedExecutionPolicy>
1112 : public Policy<Context>
1113 , public Observer {
1115 using HsmType = typename Policy<Context>::type;
1116 using HsmType::interrupt_;
1117 using HsmType::process_event;
1118 using HsmType::smThread_;
1119
1120 void start() {
1121 smThread_ = std::thread([this] {
1122 while (!interrupt_) {
1123 Observer::notify();
1124 process_event();
1125 }
1126 });
1127 }
1128 virtual ~ThreadedExecWithObserver() = default;
1129};
1130
1135template<typename Context>
1138
1140template<typename Clock = std::chrono::steady_clock,
1141 typename Duration = typename Clock::duration>
1142struct Timer {
1143 using ClockType = Clock;
1144 using DurationType = Duration;
1145 void start() {
1146 start_time_ = Clock::now();
1147 started_ = true;
1148 }
1149
1150 Duration elapsed() const {
1151 if (!started_) {
1152 return Duration(0);
1153 }
1154 auto now = Clock::now();
1155 auto interval = now - start_time_;
1156 return std::chrono::duration_cast<Duration>(interval);
1157 }
1158
1159 template<typename ToDuration = Duration>
1160 Duration elapsed(ToDuration since) const {
1161 auto now = Clock::now();
1162 if constexpr (std::is_same<ToDuration, Duration>::value) {
1163 return std::chrono::duration_cast<Duration>(now - since);
1164 } else {
1165 return std::chrono::duration_cast<ToDuration>(now - since);
1166 }
1167 }
1168 // template function to convert to a different Duration
1169 template<typename ToDuration>
1170 ToDuration elapsed() const {
1171 return std::chrono::duration_cast<ToDuration>(elapsed());
1172 }
1173
1174 bool started() const { return started_; }
1175 void reset() { start_time_ = Clock::now(); }
1176 void stop() { started_ = false; }
1177
1178 protected:
1179 typename Clock::time_point start_time_;
1180 bool started_{ false };
1181};
1182
1183// useful for profiling
1184template<typename Clock = std::chrono::steady_clock,
1185 typename Duration = typename Clock::duration>
1186struct IntervalTimer : public Timer<Clock, Duration> {
1187 // Return the interval since the previous call to this function, typecast
1188 // to the Duration type
1189 Duration interval() {
1190 if (!this->started()) {
1191 this->start();
1192 return Duration(0);
1193 }
1194 auto now = Clock::now();
1195 auto interval = now - this->start_time_;
1196 this->start_time_ = now;
1197 return std::chrono::duration_cast<Duration>(interval);
1198 }
1199};
1200
1201#ifdef __linux__
1202// Periodic Timer
1203// Lock up the calling thread for a period of time. Accuracy is determined by
1204// real-time scheduler settings and OS scheduling policies
1205template<typename Clock = AccurateClock,
1206 typename Duration = typename Clock::duration>
1207struct PeriodicSleepTimer : public Timer<Clock, Duration> {
1208 PeriodicSleepTimer(Duration period = Duration(1))
1209 : period_(period) {}
1211
1212 void wait() {
1213 // calculate time elapsed since last callback
1214 auto remaining = period_ - Timer<Clock, Duration>::elapsed();
1215 // nanosleep for remaining time
1216 // Convert duration to timespec
1217 struct timespec ts;
1218 ts.tv_sec =
1219 std::chrono::duration_cast<std::chrono::seconds>(remaining).count();
1220 ts.tv_nsec =
1221 std::chrono::duration_cast<std::chrono::nanoseconds>(remaining)
1222 .count();
1223 // remaining time if -1
1224 struct timespec remaining_ts;
1225 while (nanosleep(&ts, &remaining_ts) == EINTR) {
1226 ts = remaining_ts;
1227 }
1228 // ensure that callback finishes within the period
1230 }
1231
1232 Duration get_period() const { return period_; }
1233
1234 protected:
1235 Duration period_;
1236};
1237
1238// Real-time execution policy
1241 RealtimeConfigurator(int priority, std::array<int, 4> affinity)
1242 : PROCESS_PRIORITY(priority)
1243 , CPU_AFFINITY(affinity) {}
1244
1246 // Set thread to real-time priority
1247 struct sched_param param;
1248 param.sched_priority = PROCESS_PRIORITY - 3;
1249 if (pthread_setschedparam(pthread_self(), SCHED_RR, &param) != 0) {
1250 perror("pthread_setschedparam");
1251 }
1252
1253 // Set CPU affinity
1254 cpu_set_t cpuset;
1255 CPU_ZERO(&cpuset);
1256 for (auto cpu : CPU_AFFINITY) {
1257 CPU_SET(cpu, &cpuset);
1258 }
1259
1260 if (pthread_setaffinity_np(
1261 pthread_self(), sizeof(cpu_set_t), &cpuset) != 0) {
1262 perror("sched_setaffinity");
1263 }
1264
1265 // Lock memory
1266 if (mlockall(MCL_CURRENT | MCL_FUTURE) == -1) {
1267 perror("mlockall failed");
1268 }
1269 }
1270
1271 template<typename Fn>
1272 std::thread real_time_thread(Fn fn) {
1273 return std::thread([this, fn] {
1274 this->config_realtime_thread();
1275 fn();
1276 });
1277 }
1278
1279 std::thread make_real_time(std::thread&& t) {
1281 return std::move(t);
1282 }
1283
1284 protected:
1286 std::array<int, 4> CPU_AFFINITY{ 0, 1, 2, 3 };
1287};
1288
1289// Real-time execution policy - set cpu isolation in grub
1290template<typename Context,
1291 template<typename> class Policy = ThreadedExecutionPolicy>
1293 : Policy<Context>
1296 using HsmType = typename Policy<Context>::type;
1297 using HsmType::interrupt_;
1298 using HsmType::process_event;
1299 using HsmType::smThread_;
1300
1301 void start() {
1302 smThread_ = RealtimeConfigurator::real_time_thread([this] {
1303 while (!interrupt_) {
1304 process_event();
1305 }
1306 });
1307 }
1308
1309 virtual ~RealtimeExecutionPolicy() = default;
1310};
1311
1312// Periodic execution policy
1313template<typename Context,
1314 template<typename> class Policy = ThreadedExecutionPolicy,
1315 typename PeriodicTimer = PeriodicSleepTimer<std::chrono::steady_clock,
1316 std::chrono::milliseconds>>
1318 : Policy<Context>
1319 , PeriodicTimer {
1321 using HsmType = typename Policy<Context>::type;
1322 using TimerType = PeriodicTimer;
1323 using HsmType::interrupt_;
1324 using HsmType::send_event;
1325
1326 void start() {
1327 PeriodicTimer::start();
1329
1330 eventThread_ = std::thread([this] {
1331 while (!interrupt_) {
1332 PeriodicTimer::wait();
1334 this->send_event(tick_event_);
1335 }
1336 });
1337 }
1338
1339 void stop() {
1341 if (eventThread_.joinable()) {
1342 eventThread_.join();
1343 }
1344 }
1346
1347 int get_ticks() { return tick_event_.ticks_; }
1348
1349 protected:
1350 std::thread eventThread_;
1352};
1353
1354// Periodic Real-time execution policy
1355template<typename Context,
1356 template<typename> class Policy = ThreadedExecutionPolicy,
1357 typename PeriodicTimer = PeriodicSleepTimer<std::chrono::steady_clock,
1358 std::chrono::milliseconds>>
1361 , Policy<Context>
1362 , PeriodicTimer {
1363 using type =
1365 using TimerType = PeriodicTimer;
1366 using HsmType = typename Policy<Context>::type;
1367 using HsmType::interrupt_;
1368 using HsmType::process_event;
1369 using HsmType::send_event;
1370 using HsmType::smThread_;
1371
1372 void start() {
1373 PeriodicTimer::start();
1374 smThread_ = RealtimeConfigurator::real_time_thread([this] {
1375 while (!interrupt_) {
1376 this->process_event();
1377 }
1378 });
1379
1381 while (!interrupt_) {
1382 PeriodicTimer::wait();
1384 this->send_event(tick_event_);
1385 }
1386 });
1387 }
1388
1389 void stop() {
1391 if (eventThread_.joinable()) {
1392 eventThread_.join();
1393 }
1394 }
1396
1397 int get_ticks() { return tick_event_.ticks_; }
1398
1399 protected:
1400 std::thread eventThread_;
1402};
1403
1404// Concurrent HSMs
1405template<typename... Hsms>
1407 static constexpr bool is_hsm = true;
1409
1410 template<typename Event>
1411 void entry(Event e = Event()) {
1412 std::apply([e](auto&... hsm) { (hsm.entry(e), ...); }, hsms_);
1413 }
1414
1415 template<typename Event>
1416 void exit(Event&& e) {
1417 std::apply([e](auto&... hsm) { (hsm.exit(e), ...); }, hsms_);
1418 }
1419
1420 template<typename Event>
1421 void send_event(Event&& e) {
1422 return std::apply(
1423 // send event to all HSMs
1424 [e](auto&... hsm) { (hsm.send_event(e), ...); },
1425 hsms_);
1426 }
1427
1428 // assume hsms can be `tick`ed
1429
1430 void tick() {
1431 std::apply([](auto&... hsm) { (hsm.tick(), ...); }, hsms_);
1432 }
1433
1434 std::tuple<Hsms...> hsms_;
1435};
1436
1437// Each HSM in the concurrent HSM is wrapped with a ThreadedExecutionPolicy
1438template<template<typename> class Policy = ThreadedExecutionPolicy,
1439 typename... Ts>
1443
1444template<template<typename> class Policy = ThreadedExecutionPolicy,
1445 typename... Ts>
1446using make_concurrent_hsm_t = typename make_concurrent_hsm<Policy, Ts...>::type;
1447
1448#endif // __linux__
1449
1450} // namespace detail
1451
1477template<typename Context,
1478 template<class> class Policy = detail::SingleThreadedExecutionPolicy>
1479using SingleThreadedHsm = Policy<Context>;
1480
1491template<typename Context,
1492 template<class> class Policy = detail::ThreadedExecutionPolicy>
1493using ThreadedHsm = Policy<Context>;
1494
1510template<typename Context,
1511 template<class> class Policy = detail::PeriodicExecutionPolicy>
1512using PeriodicHsm = Policy<Context>;
1513
1525template<typename Context,
1526 template<class> class Policy = detail::RealtimePeriodicExecutionPolicy>
1527using RealtimePeriodicHsm = Policy<Context>;
1528
1529// Real-time state machine. This state machine is driven by a periodic timer.
1530template<typename Context,
1531 template<class> class Policy = detail::RealtimeExecutionPolicy>
1532using RealtimeHsm = Policy<Context>;
1533
1534// Concurrent Hsm
1535template<template<typename> class Policy = detail::ThreadedExecutionPolicy,
1536 typename... Contexts>
1538
1539} // namespace tsm
tsm::EventQueueT< tsm::Event, std::mutex > EventQueue
Definition EventQueue.cpp:8
FreeRTOSConditionVariable()
Definition tsm.h:777
~FreeRTOSConditionVariable()
Definition tsm.h:784
void notify_all()
Definition tsm.h:812
void wait(Lock &lock)
Definition tsm.h:787
void notify_one()
Definition tsm.h:806
Definition tsm.h:741
bool try_lock()
Definition tsm.h:758
SemaphoreHandle_t native_handle()
Definition tsm.h:769
void lock()
Definition tsm.h:753
FreeRTOSMutex()
Definition tsm.h:743
void unlock()
Definition tsm.h:763
~FreeRTOSMutex()
Definition tsm.h:748
typename Policy< Context >::type HsmType
Definition tsm.h:1011
static void StepTask(void *pvParameters)
Definition tsm.h:882
void stop()
Definition tsm.h:1022
tuple_to_variant_t< Events > Event
Definition tsm.h:850
ThreadedExecutionPolicy()
Definition tsm.h:854
BaseType_t interrupt_
Definition tsm.h:880
void process_event()
Definition tsm.h:893
TaskHandle_t smTaskHandle
Definition tsm.h:878
EventQueue eventQueue
Definition tsm.h:879
std::thread smThread_
Definition tsm.h:1037
virtual ~ThreadedExecutionPolicy()
Definition tsm.h:865
void(*)(ThreadedExecutionPolicy *) TaskCallback
Definition tsm.h:852
void start()
Definition tsm.h:1014
TaskCallback taskCallback
Definition tsm.h:877
void send_event(const Event &event)
Definition tsm.h:874
void step()
Definition tsm.h:887
EventQueue< Event > eventQueue_
Definition tsm.h:1038
void send_event(Event &&event)
Definition tsm.h:1032
constexpr bool has_entry_v
Definition tsm.h:285
constexpr bool has_guard_v
Definition tsm.h:301
typename get_states< Ts... >::type get_states_t
Definition tsm.h:179
constexpr bool is_clocked_hsm_v
Definition tsm.h:381
unique_tuple_t< typename get_events_from_hsm< HsmType >::type > get_events_t
Definition tsm.h:980
constexpr bool is_hsm_trait_v
Definition tsm.h:366
constexpr bool has_exit_v
Definition tsm.h:269
typename wrap_type_impl< Wrapper, Tuple >::type wrap_type
Definition tsm.h:38
constexpr bool is_state_trait_v
Definition tsm.h:370
constexpr bool has_transitions_v
Definition tsm.h:331
typename make_hsm< T >::type make_hsm_t
Definition tsm.h:580
constexpr bool has_action_v
Definition tsm.h:317
typename is_hsm_trait< T >::type is_hsm_trait_t
Definition tsm.h:363
typename wrap_transitions< Ts... >::type wrap_transitions_t
Definition tsm.h:609
constexpr int MaxEvents
Definition tsm.h:739
constexpr bool has_valid_transition_v
Definition tsm.h:251
typename wrap_transition< T >::type wrap_transition_t
Definition tsm.h:598
constexpr bool has_handle_method_v
Definition tsm.h:352
typename make_concurrent_hsm< Policy, Ts... >::type make_concurrent_hsm_t
Definition tsm.h:1446
typename unique_tuple< Ts >::type unique_tuple_t
Definition tsm.h:89
typename tuple_to_variant_impl< Tuple >::type tuple_to_variant_t
Definition tsm.h:102
typename has_transitions< T >::type has_transitions_t
Definition tsm.h:328
typename TransitionMap< From, Event, Transitions >::type find_transition_t
Definition tsm.h:247
typename append_unique_impl< T, Tuple >::type append_unique
Definition tsm.h:59
Definition tsm.h:25
detail::make_concurrent_hsm_t< Policy, Contexts... > ConcurrentHsm
Definition tsm.h:1537
Policy< Context > SingleThreadedHsm
Definition tsm.h:1479
Policy< Context > ThreadedHsm
Definition tsm.h:1493
Policy< Context > RealtimeHsm
Definition tsm.h:1532
Policy< Context > RealtimePeriodicHsm
Definition tsm.h:1527
Policy< Context > PeriodicHsm
Definition tsm.h:1512
Definition tsm.h:116
From from
Definition tsm.h:117
To to
Definition tsm.h:119
Definition tsm.h:1058
void wait()
Definition tsm.h:1065
void notify()
Definition tsm.h:1059
Definition tsm.h:1078
void add_callback(std::function< void()> &&cb)
Definition tsm.h:1079
void notify()
Definition tsm.h:1085
Definition tsm.h:136
int ticks_
Definition tsm.h:137
Definition tsm.h:614
ClockTickEvent tick_event_
Definition tsm.h:631
bool handle(Event e=Event())
Definition tsm.h:623
bool tick()
Definition tsm.h:620
bool handle(ClockTickEvent &e)
Definition tsm.h:627
typename Policy< T >::type HsmType
Definition tsm.h:616
Definition tsm.h:145
void exit(Event &&e)
Definition tsm.h:1416
std::tuple< Hsms... > hsms_
Definition tsm.h:1434
void send_event(Event &&e)
Definition tsm.h:1421
void entry(Event e=Event())
Definition tsm.h:1411
void tick()
Definition tsm.h:1430
static constexpr bool is_hsm
Definition tsm.h:1407
Definition tsm.h:672
bool interrupted()
Definition tsm.h:707
void add_event(Event &&e)
Definition tsm.h:691
bool empty()
Definition tsm.h:710
void stop()
Definition tsm.h:700
bool push_back(Event const &e)
Definition tsm.h:720
void pop_front()
Definition tsm.h:714
Event next_event()
Definition tsm.h:679
Event const & front()
Definition tsm.h:712
virtual ~EventQueueT()
Definition tsm.h:675
Definition tsm.h:105
Definition tsm.h:826
std::chrono::milliseconds duration
Definition tsm.h:827
static constexpr bool is_steady
Definition tsm.h:832
duration::rep rep
Definition tsm.h:829
static time_point now() noexcept
Definition tsm.h:834
duration::period period
Definition tsm.h:828
std::chrono::time_point< FreeRTOSClock > time_point
Definition tsm.h:830
Definition tsm.h:385
transitions transitions_
Definition tsm.h:567
std::enable_if_t< has_handle_method_v< State, Event, T >, void > handle_transition(State *state, Event &&e)
Definition tsm.h:545
bool check_guard(Event &&e, State *state)
Definition tsm.h:468
void perform_action(Event &&e, State *state)
Definition tsm.h:495
void current_state()
Definition tsm.h:562
typename std::tuple_element_t< 0, transitions >::from initial_state
Definition tsm.h:389
Hsm()
Definition tsm.h:392
std::enable_if_t<!has_handle_method_v< State, Event, T >, void > handle_transition(typename transition::from *state, Event &&e)
Definition tsm.h:521
get_states_t< transitions > States
Definition tsm.h:390
Hsm< T, transitions > type
Definition tsm.h:387
static constexpr bool is_hsm
Definition tsm.h:386
tuple_to_variant_t< wrap_type< std::add_pointer, States > > current_state_
Definition tsm.h:568
void entry(Event &&e, State *state) noexcept
Definition tsm.h:428
bool handle(Event &&e)
Definition tsm.h:397
States states_
Definition tsm.h:566
void exit(Event &&e, State *state) noexcept
Definition tsm.h:447
Definition tsm.h:1186
Duration interval()
Definition tsm.h:1189
void exit(Event e=Event())
Definition tsm.h:656
static constexpr bool is_hsm
Definition tsm.h:647
bool handle(Event e=Event())
Definition tsm.h:661
void entry(Event e=Event())
Definition tsm.h:651
std::tuple< Hsms... > hsms_
Definition tsm.h:666
PeriodicTimer TimerType
Definition tsm.h:1322
std::thread eventThread_
Definition tsm.h:1350
ClockTickEvent tick_event_
Definition tsm.h:1351
virtual ~PeriodicExecutionPolicy()
Definition tsm.h:1345
int get_ticks()
Definition tsm.h:1347
void start()
Definition tsm.h:1326
typename Policy< Context >::type HsmType
Definition tsm.h:1321
void stop()
Definition tsm.h:1339
Definition tsm.h:1207
void start()
Definition tsm.h:1210
PeriodicSleepTimer(Duration period=Duration(1))
Definition tsm.h:1208
Duration get_period() const
Definition tsm.h:1232
Duration period_
Definition tsm.h:1235
void wait()
Definition tsm.h:1212
std::array< int, 4 > CPU_AFFINITY
Definition tsm.h:1286
RealtimeConfigurator(int priority, std::array< int, 4 > affinity)
Definition tsm.h:1241
std::thread make_real_time(std::thread &&t)
Definition tsm.h:1279
std::thread real_time_thread(Fn fn)
Definition tsm.h:1272
int PROCESS_PRIORITY
Definition tsm.h:1285
void config_realtime_thread()
Definition tsm.h:1245
void start()
Definition tsm.h:1301
typename Policy< Context >::type HsmType
Definition tsm.h:1296
virtual ~RealtimeExecutionPolicy()=default
PeriodicTimer TimerType
Definition tsm.h:1365
virtual ~RealtimePeriodicExecutionPolicy()
Definition tsm.h:1395
typename Policy< Context >::type HsmType
Definition tsm.h:1366
ClockTickEvent tick_event_
Definition tsm.h:1401
std::thread eventThread_
Definition tsm.h:1400
void send_event(Event &&event)
Definition tsm.h:998
tuple_to_variant_t< get_events_t< HsmType > > Event
Definition tsm.h:987
typename Policy< Context >::type HsmType
Definition tsm.h:986
typename Policy< Context >::type HsmType
Definition tsm.h:1115
void start()
Definition tsm.h:1120
virtual ~ThreadedExecWithObserver()=default
Helper to implement a Timed Execution Policy.
Definition tsm.h:1142
Clock ClockType
Definition tsm.h:1143
void start()
Definition tsm.h:1145
Duration DurationType
Definition tsm.h:1144
ToDuration elapsed() const
Definition tsm.h:1170
Clock::time_point start_time_
Definition tsm.h:1179
bool started_
Definition tsm.h:1180
bool started() const
Definition tsm.h:1174
Duration elapsed(ToDuration since) const
Definition tsm.h:1160
void reset()
Definition tsm.h:1175
Duration elapsed() const
Definition tsm.h:1150
void stop()
Definition tsm.h:1176
Definition tsm.h:240
typename transition_map_helper< From, Event, Transitions >::type type
Definition tsm.h:241
Definition tsm.h:127
decltype(Guard) guard_t
Definition tsm.h:131
decltype(Action) action_t
Definition tsm.h:130
static constexpr action_t action
Definition tsm.h:132
static constexpr guard_t guard
Definition tsm.h:133
decltype(std::tuple_cat(typename get_events_from_transition< Transitions >::type{}...)) type
Definition tsm.h:942
std::tuple<> type
Definition tsm.h:948
Definition tsm.h:936
std::conditional_t<!(std::is_same_v< T, Ts >||...), std::tuple< Ts..., T >, std::tuple< Ts... > > type
Definition tsm.h:55
std::tuple< T > type
Definition tsm.h:46
typename aggregate_events< Transitions >::type type
Definition tsm.h:967
typename get_events_from_hsm< typename Transition::from >::type FromEvents
Definition tsm.h:956
std::tuple< typename Transition::event > Event
Definition tsm.h:954
decltype(std::tuple_cat(Event{}, FromEvents{}, ToEvents{})) type
Definition tsm.h:959
typename get_events_from_hsm< typename Transition::to >::type ToEvents
Definition tsm.h:958
typename get_states_impl< std::tuple<>, Ts... >::type type
Definition tsm.h:175
append_unique< typename First::from, Tuple > from_states
Definition tsm.h:162
append_unique< typename First::to, from_states > to_states
Definition tsm.h:164
typename get_states_impl< to_states, Rest... >::type type
Definition tsm.h:167
Tuple type
Definition tsm.h:155
Definition tsm.h:149
Definition tsm.h:171
Definition tsm.h:305
Definition tsm.h:273
Definition tsm.h:257
Definition tsm.h:289
Definition tsm.h:339
Definition tsm.h:321
Definition tsm.h:374
Definition tsm.h:357
wrap_transitions_t< typename T::transitions > transitions
Definition tsm.h:639
Definition tsm.h:572
typename std::conditional_t< match, CurrentTransition, typename transition_map_helper< From, Event, std::tuple< Transitions... >, Index+1 >::type > type
Definition tsm.h:224
std::variant< Ts... > type
Definition tsm.h:97
typename unique_tuple_impl< std::tuple< Ts... >, append_unique< T, std::tuple< Us... > > >::type type
Definition tsm.h:79
Definition tsm.h:84
typename unique_tuple_impl< Ts, Us >::type type
Definition tsm.h:85
Definition tsm.h:584
typename T::to to
Definition tsm.h:587
std::conditional_t< is_state_trait_v< from >, make_hsm_t< from >, from > wrap_from
Definition tsm.h:590
std::conditional_t< is_state_trait_v< to >, make_hsm_t< to >, to > wrap_to
Definition tsm.h:592
typename T::from from
Definition tsm.h:585
typename T::event event
Definition tsm.h:586
std::tuple< wrap_transition_t< Ts >... > type
Definition tsm.h:605
Definition tsm.h:601
std::tuple< typename Wrapper< Ts >::type... > type
Definition tsm.h:34
Definition tsm.h:30
Definition tsm.h:108
T type
Definition tsm.h:109