QP/C  7.0.0
Real-Time Embedded Framework
qep_hsm.c
Go to the documentation of this file.
1/*============================================================================
2* QP/C Real-Time Embedded Framework (RTEF)
3* Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
4*
5* SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
6*
7* This software is dual-licensed under the terms of the open source GNU
8* General Public License version 3 (or any later version), or alternatively,
9* under the terms of one of the closed source Quantum Leaps commercial
10* licenses.
11*
12* The terms of the open source GNU General Public License version 3
13* can be found at: <www.gnu.org/licenses/gpl-3.0>
14*
15* The terms of the closed source Quantum Leaps commercial licenses
16* can be found at: <www.state-machine.com/licensing>
17*
18* Redistributions in source code must retain this top-level comment block.
19* Plagiarizing this software to sidestep the license obligations is illegal.
20*
21* Contact information:
22* <www.state-machine.com>
23* <info@state-machine.com>
24============================================================================*/
34#define QP_IMPL /* this is QP implementation */
35#include "qep_port.h" /* QEP port */
36#include "qassert.h" /* QP embedded systems-friendly assertions */
37#ifdef Q_SPY /* QS software tracing enabled? */
38 #include "qs_port.h" /* QS port */
39 #include "qs_pkg.h" /* QS facilities for pre-defined trace records */
40#else
41 #include "qs_dummy.h" /* disable the QS software tracing */
42#endif /* Q_SPY */
43
44Q_DEFINE_THIS_MODULE("qep_hsm")
45
46/*==========================================================================*/
48
49/*==========================================================================*/
51enum {
58};
59
66static QEvt const QEP_reservedEvt_[] = {
67 { (QSignal)QEP_EMPTY_SIG_, 0U, 0U },
68 { (QSignal)Q_ENTRY_SIG, 0U, 0U },
69 { (QSignal)Q_EXIT_SIG, 0U, 0U },
70 { (QSignal)Q_INIT_SIG, 0U, 0U }
71};
72
74static inline QState QHsm_trig_(QHsm * const me,
75 QStateHandler const state, QSignal sig)
76{
77 return (*state)(me, &QEP_reservedEvt_[sig]);
78}
79
80#ifdef Q_SPY
81 #define QEP_EXIT_(state_, qs_id_) do { \
82 if ((*(state_))(me, &QEP_reservedEvt_[Q_EXIT_SIG]) \
83 == (QState)Q_RET_HANDLED) { \
84 QS_BEGIN_PRE_(QS_QEP_STATE_EXIT, (qs_id_)) \
85 QS_OBJ_PRE_(me); \
86 QS_FUN_PRE_(state_); \
87 QS_END_PRE_() \
88 } \
89 } while (false)
90
91 #define QEP_ENTER_(state_, qs_id_) do { \
92 if ((*(state_))(me, &QEP_reservedEvt_[Q_ENTRY_SIG]) \
93 == (QState)Q_RET_HANDLED) { \
94 QS_BEGIN_PRE_(QS_QEP_STATE_ENTRY, (qs_id_)) \
95 QS_OBJ_PRE_(me); \
96 QS_FUN_PRE_(state_); \
97 QS_END_PRE_() \
98 } \
99 } while (false)
100
102 static int_fast8_t QHsm_tran_(QHsm * const me,
104 uint_fast8_t const qs_id);
105#else
106 #define QEP_EXIT_(state_, dummy_) \
107 ((void)(*(state_))(me, &QEP_reservedEvt_[Q_EXIT_SIG]))
108
109 #define QEP_ENTER_(state_, dummy_) \
110 ((void)(*(state_))(me, &QEP_reservedEvt_[Q_ENTRY_SIG]))
111
112 static int_fast8_t QHsm_tran_(QHsm * const me,
114#endif /* Q_SPY */
115
116/*==========================================================================*/
139void QHsm_ctor(QHsm * const me, QStateHandler initial) {
140 static struct QHsmVtable const vtable = { /* QHsm virtual table */
141 &QHsm_init_,
143#ifdef Q_SPY
145#endif
146 };
147 me->vptr = &vtable;
149 me->temp.fun = initial;
150}
151
152/*==========================================================================*/
166#ifdef Q_SPY
167void QHsm_init_(QHsm * const me, void const * const e,
168 uint_fast8_t const qs_id)
169#else
170void QHsm_init_(QHsm * const me, void const * const e)
171#endif
172{
173 QStateHandler t = me->state.fun;
174
179 Q_REQUIRE_ID(200, (me->vptr != (struct QHsmVtable *)0)
180 && (me->temp.fun != Q_STATE_CAST(0))
181 && (t == Q_STATE_CAST(&QHsm_top)));
182
183 /* execute the top-most initial tran. */
184 QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt));
185
186 /* the top-most initial transition must be taken */
187 Q_ASSERT_ID(210, r == (QState)Q_RET_TRAN);
188
191 QS_OBJ_PRE_(me); /* this state machine object */
192 QS_FUN_PRE_(t); /* the source state */
193 QS_FUN_PRE_(me->temp.fun); /* the target of the initial transition */
195
196 /* drill down into the state hierarchy with initial transitions... */
197 do {
198 QStateHandler path[QHSM_MAX_NEST_DEPTH_]; /* tran entry path array */
199 int_fast8_t ip = 0; /* tran entry path index */
200
201 path[0] = me->temp.fun;
202 (void)QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
203 while (me->temp.fun != t) {
204 ++ip;
206 path[ip] = me->temp.fun;
207 (void)QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
208 }
209 me->temp.fun = path[0];
210
211 /* nested initial transition, drill into the target hierarchy... */
212 do {
213 QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
214 --ip;
215 } while (ip >= 0);
216
217 t = path[0]; /* current state becomes the new source */
218
219 r = QHsm_trig_(me, t, Q_INIT_SIG); /* execute initial transition */
220
221#ifdef Q_SPY
222 if (r == (QState)Q_RET_TRAN) {
224 QS_OBJ_PRE_(me); /* this state machine object */
225 QS_FUN_PRE_(t); /* the source state */
226 QS_FUN_PRE_(me->temp.fun); /* target of the initial tran. */
228 }
229#endif /* Q_SPY */
230
231 } while (r == (QState)Q_RET_TRAN);
232
234 QS_TIME_PRE_(); /* time stamp */
235 QS_OBJ_PRE_(me); /* this state machine object */
236 QS_FUN_PRE_(t); /* the new active state */
238
239 me->state.fun = t; /* change the current active state */
240 me->temp.fun = t; /* mark the configuration as stable */
241}
242
243/*==========================================================================*/
261QState QHsm_top(void const * const me, QEvt const * const e) {
262 (void)me; /* suppress the "unused parameter" compiler warning */
263 (void)e; /* suppress the "unused parameter" compiler warning */
264 return (QState)Q_RET_IGNORED; /* the top state ignores all events */
265}
266
267/*==========================================================================*/
285#ifdef Q_SPY
286void QHsm_dispatch_(QHsm * const me, QEvt const * const e,
287 uint_fast8_t const qs_id)
288#else
289void QHsm_dispatch_(QHsm * const me, QEvt const * const e)
290#endif
291{
292 QStateHandler t = me->state.fun;
294
298 Q_REQUIRE_ID(400, (t != Q_STATE_CAST(0))
299 && (t == me->temp.fun));
300
302 QS_TIME_PRE_(); /* time stamp */
303 QS_SIG_PRE_(e->sig); /* the signal of the event */
304 QS_OBJ_PRE_(me); /* this state machine object */
305 QS_FUN_PRE_(t); /* the current state */
307
309 QState r;
310 /* process the event hierarchically... */
311 do {
312 s = me->temp.fun;
313 r = (*s)(me, e); /* invoke state handler s */
314
315 if (r == (QState)Q_RET_UNHANDLED) { /* unhandled due to a guard? */
316
318 QS_SIG_PRE_(e->sig); /* the signal of the event */
319 QS_OBJ_PRE_(me); /* this state machine object */
320 QS_FUN_PRE_(s); /* the current state */
322
323 r = QHsm_trig_(me, s, QEP_EMPTY_SIG_); /* find superstate of s */
324 }
325 } while (r == (QState)Q_RET_SUPER);
326
327 /* regular transition taken? */
329 if (r >= (QState)Q_RET_TRAN) {
331
332 path[0] = me->temp.fun; /* save the target of the transition */
333 path[1] = t;
334 path[2] = s;
335
336 /* exit current state to transition source s... */
338 for (; t != s; t = me->temp.fun) {
339 if (QHsm_trig_(me, t, Q_EXIT_SIG) == (QState)Q_RET_HANDLED) {
341 QS_OBJ_PRE_(me); /* this state machine object */
342 QS_FUN_PRE_(t); /* the exited state */
344
345 /* find superstate of t */
346 (void)QHsm_trig_(me, t, QEP_EMPTY_SIG_);
347 }
348 }
349
350 int_fast8_t ip;
351
352#ifdef Q_SPY
353 ip = QHsm_tran_(me, path, qs_id);
354 if (r == (QState)Q_RET_TRAN_HIST) {
356 QS_OBJ_PRE_(me); /* this state machine object */
357 QS_FUN_PRE_(t); /* the source of the transition */
358 QS_FUN_PRE_(path[0]); /* the target of the tran. to history */
360 }
361#else
362 ip = QHsm_tran_(me, path);
363#endif /* Q_SPY */
364
365 /* execute state entry actions in the desired order... */
367 for (; ip >= 0; --ip) {
368 QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
369 }
370
371 t = path[0]; /* stick the target into register */
372 me->temp.fun = t; /* update the next state */
373
374 /* while nested initial transition... */
376 while (QHsm_trig_(me, t, Q_INIT_SIG) == (QState)Q_RET_TRAN) {
377
379 QS_OBJ_PRE_(me); /* this state machine object */
380 QS_FUN_PRE_(t); /* the source (pseudo)state */
381 QS_FUN_PRE_(me->temp.fun); /* the target of the transition */
383
384 ip = 0;
385 path[0] = me->temp.fun;
386
387 /* find superstate */
388 (void)QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
389
390 while (me->temp.fun != t) {
391 ++ip;
392 path[ip] = me->temp.fun;
393 /* find superstate */
394 (void)QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
395 }
396 me->temp.fun = path[0];
397
398 /* entry path must not overflow */
400
401 /* retrace the entry path in reverse (correct) order... */
402 do {
403 QEP_ENTER_(path[ip], qs_id); /* enter path[ip] */
404 --ip;
405 } while (ip >= 0);
406
407 t = path[0]; /* current state becomes the new source */
408 }
409
411 QS_TIME_PRE_(); /* time stamp */
412 QS_SIG_PRE_(e->sig); /* the signal of the event */
413 QS_OBJ_PRE_(me); /* this state machine object */
414 QS_FUN_PRE_(s); /* the source of the transition */
415 QS_FUN_PRE_(t); /* the new active state */
417 }
418
419#ifdef Q_SPY
420 else if (r == (QState)Q_RET_HANDLED) {
421
423 QS_TIME_PRE_(); /* time stamp */
424 QS_SIG_PRE_(e->sig); /* the signal of the event */
425 QS_OBJ_PRE_(me); /* this state machine object */
426 QS_FUN_PRE_(s); /* the source state */
428
429 }
430 else {
431
433 QS_TIME_PRE_(); /* time stamp */
434 QS_SIG_PRE_(e->sig); /* the signal of the event */
435 QS_OBJ_PRE_(me); /* this state machine object */
436 QS_FUN_PRE_(me->state.fun); /* the current state */
438
439 }
440#endif /* Q_SPY */
441
442 me->state.fun = t; /* change the current active state */
443 me->temp.fun = t; /* mark the configuration as stable */
444}
445
446/*==========================================================================*/
464#ifdef Q_SPY
465static int_fast8_t QHsm_tran_(QHsm * const me,
467 uint_fast8_t const qs_id)
468#else
469static int_fast8_t QHsm_tran_(QHsm * const me,
471#endif
472{
473 int_fast8_t ip = -1; /* transition entry path index */
474 QStateHandler t = path[0];
475 QStateHandler const s = path[2];
477
478 /* (a) check source==target (transition to self)... */
479 if (s == t) {
480 QEP_EXIT_(s, qs_id); /* exit the source */
481 ip = 0; /* enter the target */
482 }
483 else {
484 /* find superstate of target */
485 (void)QHsm_trig_(me, t, QEP_EMPTY_SIG_);
486
487 t = me->temp.fun;
488
489 /* (b) check source==target->super... */
490 if (s == t) {
491 ip = 0; /* enter the target */
492 }
493 else {
494 /* find superstate of src */
495 (void)QHsm_trig_(me, s, QEP_EMPTY_SIG_);
496
497 /* (c) check source->super==target->super... */
498 if (me->temp.fun == t) {
499 QEP_EXIT_(s, qs_id); /* exit the source */
500 ip = 0; /* enter the target */
501 }
502 else {
503 /* (d) check source->super==target... */
504 if (me->temp.fun == path[0]) {
505 QEP_EXIT_(s, qs_id); /* exit the source */
506 }
507 else {
508 /* (e) check rest of source==target->super->super..
509 * and store the entry path along the way
510 */
511 int_fast8_t iq = 0; /* indicate that LCA not found */
512 ip = 1; /* enter target and its superstate */
513 path[1] = t; /* save the superstate of target */
514 t = me->temp.fun; /* save source->super */
515
516 /* find target->super->super... */
517 QState r = QHsm_trig_(me, path[1], QEP_EMPTY_SIG_);
518 while (r == (QState)Q_RET_SUPER) {
519 ++ip;
520 path[ip] = me->temp.fun; /* store the entry path */
521 if (me->temp.fun == s) { /* is it the source? */
522 iq = 1; /* indicate that LCA found */
523
524 /* entry path must not overflow */
525 Q_ASSERT_ID(510,
527 --ip; /* do not enter the source */
528 r = (QState)Q_RET_HANDLED; /* terminate loop */
529 }
530 /* it is not the source, keep going up */
531 else {
532 r = QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
533 }
534 }
535
536 /* the LCA not found yet? */
537 if (iq == 0) {
538
539 /* entry path must not overflow */
541
542 QEP_EXIT_(s, qs_id); /* exit the source */
543
544 /* (f) check the rest of source->super
545 * == target->super->super...
546 */
547 iq = ip;
548 r = (QState)Q_RET_IGNORED; /* LCA NOT found */
549 do {
550 if (t == path[iq]) { /* is this the LCA? */
551 r = (QState)Q_RET_HANDLED; /* LCA found */
552 ip = iq - 1; /* do not enter LCA */
553 iq = -1; /* cause termintion of the loop */
554 }
555 else {
556 --iq; /* try lower superstate of target */
557 }
558 } while (iq >= 0);
559
560 /* LCA not found? */
561 if (r != (QState)Q_RET_HANDLED) {
562 /* (g) check each source->super->...
563 * for each target->super...
564 */
565 r = (QState)Q_RET_IGNORED; /* keep looping */
566 do {
567 /* exit t unhandled? */
568 if (QHsm_trig_(me, t, Q_EXIT_SIG)
570 {
572 QS_OBJ_PRE_(me);
573 QS_FUN_PRE_(t);
575
576 (void)QHsm_trig_(me, t, QEP_EMPTY_SIG_);
577 }
578 t = me->temp.fun; /* set to super of t */
579 iq = ip;
580 do {
581 /* is this LCA? */
582 if (t == path[iq]) {
583 /* do not enter LCA */
584 ip = (int_fast8_t)(iq - 1);
585 iq = -1; /* break out of inner loop */
586 /* break out of outer loop */
588 }
589 else {
590 --iq;
591 }
592 } while (iq >= 0);
593 } while (r != (QState)Q_RET_HANDLED);
594 }
595 }
596 }
597 }
598 }
599 }
600 return ip;
601}
602
603/*==========================================================================*/
604#ifdef Q_SPY
606 return me->state.fun;
607}
608#endif
609
610/*==========================================================================*/
628bool QHsm_isIn(QHsm * const me, QStateHandler const state) {
629
631 Q_REQUIRE_ID(600, me->temp.fun == me->state.fun);
632
633 bool inState = false; /* assume that this HSM is not in 'state' */
634
635 /* scan the state hierarchy bottom-up */
636 QState r;
637 do {
638 /* do the states match? */
639 if (me->temp.fun == state) {
640 inState = true; /* 'true' means that match found */
641 r = (QState)Q_RET_IGNORED; /* break out of the loop */
642 }
643 else {
644 r = QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
645 }
646 } while (r != (QState)Q_RET_IGNORED); /* QHsm_top() state not reached */
647 me->temp.fun = me->state.fun; /* restore the stable state configuration */
648
649 return inState; /* return the status */
650}
651
652/*==========================================================================*/
678 QStateHandler const parent)
679{
680 QStateHandler child = me->state.fun; /* start with the current state */
681 bool isFound = false; /* start with the child not found */
682
683 /* establish stable state configuration */
684 me->temp.fun = me->state.fun;
685 QState r;
686 do {
687 /* is this the parent of the current child? */
688 if (me->temp.fun == parent) {
689 isFound = true; /* child is found */
690 r = (QState)Q_RET_IGNORED; /* break out of the loop */
691 }
692 else {
693 child = me->temp.fun;
694 r = QHsm_trig_(me, me->temp.fun, QEP_EMPTY_SIG_);
695 }
696 } while (r != (QState)Q_RET_IGNORED); /* QHsm_top() state not reached */
697 me->temp.fun = me->state.fun; /* establish stable state configuration */
698
700 Q_ENSURE_ID(810, isFound);
701#ifdef Q_NASSERT
702 (void)isFound; /* avoid compiler warning about unused variable */
703#endif
704
705 return child; /* return the child */
706}
707
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:102
#define Q_ASSERT_ID(id_, test_)
Definition: qassert.h:135
#define Q_ENSURE_ID(id_, test_)
Definition: qassert.h:271
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:252
@ Q_RET_HANDLED
Definition: qep.h:578
@ Q_RET_IGNORED
Definition: qep.h:579
@ Q_RET_TRAN
Definition: qep.h:589
@ Q_RET_UNHANDLED
Definition: qep.h:575
@ Q_RET_SUPER
Definition: qep.h:573
@ Q_RET_TRAN_HIST
Definition: qep.h:594
#define QP_VERSION_STR
Definition: qep.h:48
#define Q_STATE_CAST(handler_)
Definition: qep.h:196
QState(* QStateHandler)(void *const me, QEvt const *const e)
Definition: qep.h:176
#define Q_EVT_CAST(class_)
Definition: qep.h:158
uint_fast8_t QState
Definition: qep.h:170
@ Q_INIT_SIG
Definition: qep.h:690
@ Q_EXIT_SIG
Definition: qep.h:689
@ Q_ENTRY_SIG
Definition: qep.h:688
uint16_t QSignal
Definition: qep.h:97
static QEvt const QEP_reservedEvt_[]
Definition: qep_hsm.c:66
#define QEP_EXIT_(state_, qs_id_)
Definition: qep_hsm.c:81
#define QEP_ENTER_(state_, qs_id_)
Definition: qep_hsm.c:91
@ QHSM_MAX_NEST_DEPTH_
Definition: qep_hsm.c:57
@ QEP_EMPTY_SIG_
Definition: qep_hsm.c:52
static QState QHsm_trig_(QHsm *const me, QStateHandler const state, QSignal sig)
Definition: qep_hsm.c:74
char const QP_versionStr[7]
Definition: qep_hsm.c:47
#define QS_CRIT_STAT_
Definition: qs.h:509
#define QS_TIME_PRE_()
Definition: qs.h:220
@ QS_QEP_STATE_INIT
Definition: qs.h:60
@ QS_QEP_TRAN_HIST
Definition: qs.h:136
@ QS_QEP_STATE_EXIT
Definition: qs.h:59
@ QS_QEP_INIT_TRAN
Definition: qs.h:61
@ QS_QEP_INTERN_TRAN
Definition: qs.h:62
@ QS_QEP_UNHANDLED
Definition: qs.h:66
@ QS_QEP_TRAN
Definition: qs.h:63
@ QS_QEP_DISPATCH
Definition: qs.h:65
@ QS_QEP_IGNORED
Definition: qs.h:64
Internal (package scope) QS/C interface.
#define QS_BEGIN_PRE_(rec_, qs_id_)
Definition: qs_pkg.h:114
#define QS_OBJ_PRE_(obj_)
Definition: qs_pkg.h:179
#define QS_FUN_PRE_(fun_)
Definition: qs_pkg.h:182
#define QS_SIG_PRE_(sig_)
Definition: qs_pkg.h:172
#define QS_END_PRE_()
Definition: qs_pkg.h:126
Definition: qep.h:119
QSignal sig
Definition: qep.h:123
Definition: qep.h:253
bool QHsm_isIn(QHsm *const me, QStateHandler const state)
Definition: qep_hsm.c:628
struct QHsmVtable const * vptr
Definition: qep.h:254
void QHsm_ctor(QHsm *const me, QStateHandler initial)
Definition: qep_hsm.c:139
static int_fast8_t QHsm_tran_(QHsm *const me, QStateHandler path[QHSM_MAX_NEST_DEPTH_], uint_fast8_t const qs_id)
Definition: qep_hsm.c:465
union QHsmAttr state
Definition: qep.h:255
union QHsmAttr temp
Definition: qep.h:256
void QHsm_init_(QHsm *const me, void const *const e, uint_fast8_t const qs_id)
Definition: qep_hsm.c:167
QState QHsm_top(void const *const me, QEvt const *const e)
Definition: qep_hsm.c:261
QStateHandler QHsm_getStateHandler_(QHsm *const me)
Definition: qep_hsm.c:605
void QHsm_dispatch_(QHsm *const me, QEvt const *const e, uint_fast8_t const qs_id)
Definition: qep_hsm.c:286
QStateHandler QHsm_childState(QHsm *const me, QStateHandler const parent)
Definition: qep_hsm.c:677
QStateHandler fun
Definition: qep.h:220