Skyward boardcore
Loading...
Searching...
No Matches
HSM.h
Go to the documentation of this file.
1/* Copyright (c) 2015-2018 Skyward Experimental Rocketry
2 * Authors: Matteo Piazzolla, Alain Carlucci, Luca Erbetta
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23// This work is derived from https://www.state-machine.com by Miro Samek.
24
25#pragma once
26
27#include <assert.h>
28#include <events/Event.h>
29#include <events/EventHandler.h>
30#include <utils/Debug.h>
32
33#include "ActiveObject.h"
34
35namespace Boardcore
36{
37
38static constexpr int8_t HSM_MAX_NEST_DEPTH = 5;
39
48
49template <class T>
50class HSM : public EventHandler
51{
52public:
53 typedef State (T::*StateHandler)(const Event&);
54
60 HSM(StateHandler initialState,
61 unsigned int stacksize = miosix::STACK_DEFAULT_FOR_PTHREAD,
62 miosix::Priority priority = miosix::MAIN_PRIORITY)
63 : EventHandler(stacksize, priority)
64 {
65 state = &T::state_top;
66 temp = initialState;
67 }
68
69 virtual ~HSM() {};
70
71 bool start() override
72 {
73 init();
74
75 return EventHandler::start();
76 }
77
84 {
85 temp = nextState;
86 return TRAN;
87 }
88
95 {
96 temp = superState;
97 return SUPER;
98 }
99
106 bool testState(StateHandler testState) { return this->state == testState; }
107
108 // Internal pointers representing the state o the HSM. If state == temp,
109 // then "the configuration is stable".
112
113protected:
120 void handleEvent(const Event& event) override
121 {
122 // NOTE: here state == temp
123
124 StateHandler target = this->state; // Current state
125 StateHandler source;
126 State retState;
127
128 // Process the event hierarchically until it is either HANDLED, IGNORED
129 // or TRAN
130 do
131 {
132 // Save the current state that will handled the event
133 source = this->temp;
134
135 // Forward the event to the current state
136 retState = (static_cast<T*>(this)->*source)(event);
137
138 // If the event was unhandled by the current state, forward the
139 // event to the parent state. EV_EMPTY will result in a transition
140 // to the direct parent state, this temp will change
141 if (retState == UNHANDLED)
142 retState = (static_cast<T*>(this)->*source)({EV_EMPTY});
143
144 // Here the return state won't be UNHANDLED. If the return state is
145 // SUPER then it means that the event triggered a state change
146 // upwards in the hierarchy. This means that the event has to be
147 // handled by the parent, thus the loop continues until the event is
148 // either HANDLED, IGNORED or TRAN
149
150 // If the return value is TRAN, then it means that the event was
151 // handled and a transition to another state occurred. Thus temp
152 // will contain a new value
153
154 // If the return value is SUPER, then temp will contain the parent
155 // state that will handle the event
156 } while (retState == SUPER);
157
158 // NOTE: Here temp could be unchanged or could contain a new state that
159 // the machine transitioned to. If this is the case then retState will
160 // be TRAN. Otherwise the current state doesn't need to be changed
161
162 // If the event triggered a transition to another state, then the state
163 // need to be changed. If this is the case then temp contains the new
164 // state to transition to
165 if (retState == TRAN)
166 {
167 StateHandler path[HSM_MAX_NEST_DEPTH];
168 int8_t index = -1;
169 int8_t tempIndex = 0;
170
171 // NOTE: temp contains the new state to transition to
172 // NOTE: target is the initial state
173
174 // The order is such that the new state is further in the path
175 path[0] = this->temp; // Next state
176 path[1] = target; // Initial state
177
178 // NOTE: source contains the state that handled the event and that
179 // triggered the state change
180
181 // Exit from every state from the initial state until the one that
182 // handled the event. The loop stops before exiting the source
183 // state (the one that handled the event)
184 while (target != source)
185 {
186 // Exit from the current target state. The target starts from
187 // the initial state
188
189 // If the state exit is handled, then find the super state
190 if ((static_cast<T*>(this)->*target)({EV_EXIT}) == HANDLED)
191 (void)(static_cast<T*>(this)->*target)({EV_EMPTY});
192
193 target = this->temp;
194 // Now target is the super state of the previous target
195 }
196
197 // NOTE: At this point EV_EXIT has been called on the initial state
198 // and on every super state until the state that handled the event
199
200 // Save in target the next state
201 target = path[0];
202
203 // NOTE: At this point:
204 // target is the state to transition to
205 // source is the state that handled the event
206
207 // (A) If the source (state that handled the event) and the target
208 // (the next state) are the same, then this is a transition to
209 // itself
210 if (source == target) // source == target
211 {
212 // Exit the source state
213 (static_cast<T*>(this)->*source)({EV_EXIT});
214
215 // Refer to the next state
216 index = 0;
217 }
218 else
219 {
220 // Otherwise find the superstate of the target (the next state)
221 (static_cast<T*>(this)->*target)({EV_EMPTY});
222
223 // The superstate now becomes the target
224 target = this->temp;
225
226 // (B) Check if the superstate of the target is the state that
227 // handled the event
228 if (source == target) // source = target->super
229 {
230 // Enter the target
231 index = 0;
232 }
233 else
234 {
235 // Find the superstate of the source
236 (static_cast<T*>(this)->*source)({EV_EMPTY});
237
238 // (C) Check if the superstate of the source is the
239 // superstate of the target
240 if (this->temp == target) // source->super == target->super
241 {
242 // Exit the source and enter the target
243 (static_cast<T*>(this)->*source)({EV_EXIT});
244 index = 0;
245 }
246 // (D) Check if the superstate of the source is the target
247 else if (this->temp == path[0]) // source->super = target
248 {
249 // Exit the source
250 (static_cast<T*>(this)->*source)({EV_EXIT});
251 }
252 // (E) Check the rest
253 else // source == target->...->super
254 {
255 // Store the path along the way
256
257 tempIndex = 0;
258 index = 1;
259
260 path[1] = target;
261
262 // path[0] is target
263 // path[1] is target->super
264
265 // Save source->super
266 target = this->temp;
267
268 // Find target->super->super
269 retState =
270 (static_cast<T*>(this)->*path[1])({EV_EMPTY});
271
272 // EV_EMPTY will result in SUPER unless state_top is
273 // reached
274 while (retState == SUPER)
275 {
276 index++;
277
278 // Store the current state
279 path[index] = this->temp;
280
281 // Is it the source?
282 if (this->temp == source)
283 {
284 // LCa (leas common ancestor) found
285 tempIndex = 1;
286
287 // Path must not overflow
288 D(assert(index < (int8_t)HSM_MAX_NEST_DEPTH));
289
290 // Do not enter the source and terminate
291 index--;
292 retState = HANDLED;
293 }
294 else
295 {
296 // It is not the source, keep going up
297 retState =
298 (static_cast<T*>(this)->*temp)({EV_EMPTY});
299 }
300 }
301
302 // Here path will contain the path from the target to
303 // the source
304
305 if (tempIndex == 0)
306 {
307 // the LCA not found yet?
308
309 // Entry path must not overflow
310 D(assert(index < HSM_MAX_NEST_DEPTH));
311
312 // Exit the source
313 (static_cast<T*>(this)->*source)({EV_EXIT});
314
315 // (F) Check the rest of source->super ==
316 // target->super->super...
317
318 // Show that LCA was not found
319 tempIndex = index;
320 retState = IGNORED;
321
322 do
323 {
324 // Is this the LCA?
325 if (target == path[tempIndex])
326 {
327 // Indicate LCA was found
328 retState = HANDLED;
329
330 // Do not enter the LCA and terminate
331 index = tempIndex - 1;
332 tempIndex = -1;
333 }
334 else
335 {
336 // Try lower superstate of target
337 tempIndex--;
338 }
339 } while (tempIndex >= 0);
340
341 // LCA not found yet?
342 if (retState != HANDLED)
343 {
344 // (G) check each source->super->... for each
345 // target->super...
346 retState = IGNORED;
347
348 do
349 {
350 // Exit target unhandled?
351 if ((static_cast<T*>(this)->*target)(
352 {EV_EXIT}) == HANDLED)
353 {
354 (static_cast<T*>(this)->*target)(
355 {EV_EMPTY});
356 }
357
358 // Set to super of target
359 target = this->temp;
360 tempIndex = index;
361
362 do
363 {
364 // Is this LCA?
365 if (target == path[tempIndex])
366 {
367 // Do not enter LCA, break inner and
368 // break outer
369 index = tempIndex - 1;
370 tempIndex = -1;
371 retState = HANDLED;
372 }
373 else
374 {
375 tempIndex--;
376 }
377 } while (tempIndex >= 0);
378 } while (retState != HANDLED);
379 }
380 }
381 }
382 }
383 }
384
385 // Travel the path in reverse
386 for (; index >= 0; index--)
387 (static_cast<T*>(this)->*path[index])({EV_ENTRY});
388
389 // Stick the target into register and update the next state
390 target = path[0];
391 this->temp = target;
392
393 /* drill into the target hierarchy... */
394 while ((static_cast<T*>(this)->*target)({EV_INIT}) == TRAN)
395 {
396 index = (int8_t)0;
397 path[0] = this->temp;
398
399 /* find superstate */
400 (static_cast<T*>(this)->*temp)({EV_EMPTY});
401
402 while (this->temp != target)
403 {
404 ++index;
405 path[index] = this->temp;
406 /*find superstate */
407 (static_cast<T*>(this)->*temp)({EV_EMPTY});
408 }
409 this->temp = path[0];
410 /* entry path must not overflow */
411 D(assert(index < (int8_t)HSM_MAX_NEST_DEPTH));
412
413 /* retrace the entry path in reverse (correct) order... */
414 do
415 {
416 (static_cast<T*>(this)->*path[index])({EV_ENTRY});
417 --index;
418 } while (index >= (int8_t)0);
419
420 target = path[0];
421 }
422 }
423
424 // Change the current active state, mark the configuration as stable
425 this->state = target;
426 this->temp = target;
427
428 // At the end, state and temp are equals. This means that on every call
429 // of handleEvent, state == temp
430 }
431
432 State state_top(const Event&) { return IGNORED; }
433
434private:
439 void init()
440 {
441 // NOTE: Here state = state_top != state
442
443 StateHandler target = this->state;
444 State retState;
445
446 // NOTE: EV_EMPTY requires the state to move to the parent state with
447 // tranSuper
448
449 // At start, temp is a fictitious state and handling EV_EMPTY will
450 // transition to the real initial state
451 // State s __attribute__((unused)) =
452 // (static_cast<T*>(this)->*temp)({EV_EMPTY});
453 // TODO: Check lines above!
454
455 // This is to assert that the first state given to the constructor
456 // transition to the initial state
457 // D(assert(s == TRAN));s
458
459 // NOTE: At this point, temp is the initial state
460
461 do
462 {
463 StateHandler statePath[HSM_MAX_NEST_DEPTH];
464 int8_t index = 0;
465
466 // Save the current state
467 statePath[0] = this->temp;
468
469 // Transition to the next parent state
470 (void)(static_cast<T*>(this)->*temp)({EV_EMPTY});
471
472 // Find the state hierarchy from the current state to state_top.
473 // This will loop until temp is hsp_top (target is initialized to
474 // state_top in the constructor) or the maximum allowed depth is
475 // reached
476 while (this->temp != target && index < HSM_MAX_NEST_DEPTH - 1)
477 {
478 index++;
479
480 // Save the current state at the current index
481 statePath[index] = this->temp;
482
483 // Transition to the next parent state
484 (void)(static_cast<T*>(this)->*temp)({EV_EMPTY});
485
486 // This is useless given the while condition
487 D(assert(index < HSM_MAX_NEST_DEPTH));
488 }
489
490 // NOTE: At this point in statePath we have the state hierarchy from
491 // the initial state up to state_top (or the nest limit)
492
493 // Reset the current state to the initial state
494 this->temp = statePath[0];
495
496 // Forward EV_ENTRY to every state in the current hierarchy from the
497 // top state down to the initial state
498 do
499 {
500 (void)(static_cast<T*>(this)->*statePath[index])({EV_ENTRY});
501 index--;
502 } while (index >= 0);
503
504 // Save the initial state
505 target = statePath[0];
506
507 // Forward EV_INIT to the initial state
508 retState = (static_cast<T*>(this)->*temp)({EV_INIT});
509
510 // If EV_INIT triggered a transition to another state, the whole
511 // process needs to be repeated.
512 } while (retState == TRAN);
513
514 this->state = target;
515 this->temp = target;
516 }
517};
518
519} // namespace Boardcore
#define D(x)
Definition Debug.h:57
virtual bool start()
Start the thread associated with this active object.
HSM(StateHandler initialState, unsigned int stacksize=miosix::STACK_DEFAULT_FOR_PTHREAD, miosix::Priority priority=miosix::MAIN_PRIORITY)
Definition HSM.h:60
State state_top(const Event &)
Definition HSM.h:432
State(T::* StateHandler)(const Event &)
Definition HSM.h:53
void handleEvent(const Event &event) override
Makes the current state handle the event and changes the state accordingly.
Definition HSM.h:120
State transition(StateHandler nextState)
Performs a transition to the specified state.
Definition HSM.h:83
virtual ~HSM()
Definition HSM.h:69
bool testState(StateHandler testState)
Test if the state machine is in the specified state.
Definition HSM.h:106
StateHandler temp
Definition HSM.h:111
State tranSuper(StateHandler superState)
Performs a transaction to the specified super state.
Definition HSM.h:94
bool start() override
Start the thread associated with this active object.
Definition HSM.h:71
StateHandler state
Definition HSM.h:110
This file includes all the types the logdecoder script will decode.
@ EV_EMPTY
Definition Event.h:36
@ EV_ENTRY
Definition Event.h:34
@ EV_EXIT
Definition Event.h:35
@ EV_INIT
Definition Event.h:37
uint8_t Event
Definition Event.h:30
State
Definition HSM.h:41
@ IGNORED
Definition HSM.h:44
@ UNHANDLED
Event unhandled.
Definition HSM.h:43
@ TRAN
A transition to another state was taken.
Definition HSM.h:45
@ SUPER
A transition to a parent state was taken.
Definition HSM.h:46
@ HANDLED
Event handled.
Definition HSM.h:42