QP/C  7.0.0
Real-Time Embedded Framework
qep_msm.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============================================================================*/
32#define QP_IMPL /* this is QP implementation */
33#include "qep_port.h" /* QEP port */
34#include "qassert.h" /* QP embedded systems-friendly assertions */
35#ifdef Q_SPY /* QS software tracing enabled? */
36 #include "qs_port.h" /* QS port */
37 #include "qs_pkg.h" /* QS facilities for pre-defined trace records */
38#else
39 #include "qs_dummy.h" /* disable the QS software tracing */
40#endif /* Q_SPY */
41
42Q_DEFINE_THIS_MODULE("qep_msm")
43
44/*==========================================================================*/
48#define QMSM_MAX_ENTRY_DEPTH_ 4
49
50static struct QMState const l_msm_top_s = {
51 (struct QMState *)0,
52 Q_STATE_CAST(0),
56};
57
58#ifdef Q_SPY
62 static QState QMsm_execTatbl_(QHsm * const me,
63 struct QMTranActTable const *tatbl,
64 uint_fast8_t const qs_id);
65
69 static void QMsm_exitToTranSource_(QHsm * const me, QMState const *cs,
70 QMState const *ts,
71 uint_fast8_t const qs_id);
72
76 static QState QMsm_enterHistory_(QHsm * const me,
77 QMState const * const hist,
78 uint_fast8_t const qs_id);
79#else
80 static QState QMsm_execTatbl_(QHsm * const me,
81 struct QMTranActTable const *tatbl);
82 static void QMsm_exitToTranSource_(QHsm * const me, QMState const *cs,
83 QMState const *ts);
84 static QState QMsm_enterHistory_(QHsm * const me,
85 QMState const * const hist);
86#endif /* Q_SPY */
87
88/*==========================================================================*/
114void QMsm_ctor(QMsm * const me, QStateHandler initial) {
115 static struct QHsmVtable const vtable = { /* QHsm virtual table */
116 &QMsm_init_,
118#ifdef Q_SPY
120#endif
121 };
122 /* do not call the QHsm_ctor() here */
123 me->super.vptr = &vtable;
124 me->super.state.obj = &l_msm_top_s; /* the current state (top) */
125 me->super.temp.fun = initial; /* the initial transition handler */
126}
127
128/*==========================================================================*/
141#ifdef Q_SPY
142void QMsm_init_(QHsm * const me, void const * const e,
143 uint_fast8_t const qs_id)
144#else
145void QMsm_init_(QHsm * const me, void const * const e)
146#endif
147{
149
154 Q_REQUIRE_ID(200, (me->vptr != (struct QHsmVtable *)0)
155 && (me->temp.fun != Q_STATE_CAST(0))
156 && (me->state.obj == &l_msm_top_s));
157
158 /* execute the top-most initial tran. */
159 QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt));
160
161 /* the top-most initial transition must be taken */
163
165 QS_OBJ_PRE_(me); /* this state machine object */
166 QS_FUN_PRE_(me->state.obj->stateHandler); /* source state */
167 QS_FUN_PRE_(me->temp.tatbl->target->stateHandler); /* target state */
169
170 /* set state to the last tran. target */
171 me->state.obj = me->temp.tatbl->target;
172
173 /* drill down into the state hierarchy with initial transitions... */
174 /* execute the tran. table */
175 do {
176#ifdef Q_SPY
177 r = QMsm_execTatbl_(me, me->temp.tatbl, qs_id);
178#else
179 r = QMsm_execTatbl_(me, me->temp.tatbl);
180#endif
181 } while (r >= (QState)Q_RET_TRAN_INIT);
182
184 QS_TIME_PRE_(); /* time stamp */
185 QS_OBJ_PRE_(me); /* this state machine object */
186 QS_FUN_PRE_(me->state.obj->stateHandler); /* the new current state */
188}
189
190/*==========================================================================*/
205#ifdef Q_SPY
206void QMsm_dispatch_(QHsm * const me, QEvt const * const e,
207 uint_fast8_t const qs_id)
208#else
209void QMsm_dispatch_(QHsm * const me, QEvt const * const e)
210#endif
211{
212 QMState const *s = me->state.obj; /* store the current state */
213 QMState const *t = s;
214 QState r;
216
218 Q_REQUIRE_ID(300, s != (QMState *)0);
219
221 QS_TIME_PRE_(); /* time stamp */
222 QS_SIG_PRE_(e->sig); /* the signal of the event */
223 QS_OBJ_PRE_(me); /* this state machine object */
224 QS_FUN_PRE_(s->stateHandler); /* the current state handler */
226
227 /* scan the state hierarchy up to the top state... */
228 do {
229 r = (*t->stateHandler)(me, e); /* call state handler function */
230
231 /* event handled? (the most frequent case) */
232 if (r >= (QState)Q_RET_HANDLED) {
233 break; /* done scanning the state hierarchy */
234 }
235 /* event unhandled and passed to the superstate? */
236 else if (r == (QState)Q_RET_SUPER) {
237 t = t->superstate; /* advance to the superstate */
238 }
239 /* event unhandled and passed to a submachine superstate? */
240 else if (r == (QState)Q_RET_SUPER_SUB) {
241 t = me->temp.obj; /* current host state of the submachie */
242 }
243 /* event unhandled due to a guard? */
244 else if (r == (QState)Q_RET_UNHANDLED) {
245
247 QS_SIG_PRE_(e->sig); /* the signal of the event */
248 QS_OBJ_PRE_(me); /* this state machine object */
249 QS_FUN_PRE_(t->stateHandler); /* the current state */
251
252 t = t->superstate; /* advance to the superstate */
253 }
254 else {
255 /* no other return value should be produced */
256 Q_ERROR_ID(310);
257 }
258 } while (t != (QMState *)0);
259
260
261 /* any kind of transition taken? */
262 if (r >= (QState)Q_RET_TRAN) {
263#ifdef Q_SPY
264 QMState const * const ts = t; /* transition source for QS tracing */
265
266 /* the transition source state must not be NULL */
267 Q_ASSERT_ID(320, ts != (QMState *)0);
268#endif /* Q_SPY */
269
270 do {
271 /* save the transition-action table before it gets clobbered */
272 struct QMTranActTable const * const tatbl = me->temp.tatbl;
273 union QHsmAttr tmp; /* temporary to save intermediate values */
274
275 /* was TRAN, TRAN_INIT, or TRAN_EP taken? */
276 if (r <= (QState)Q_RET_TRAN_EP) {
277#ifdef Q_SPY
278 QMsm_exitToTranSource_(me, s, t, qs_id);
279 r = QMsm_execTatbl_(me, tatbl, qs_id);
280#else
281 QMsm_exitToTranSource_(me, s, t);
282 r = QMsm_execTatbl_(me, tatbl);
283#endif
284 s = me->state.obj;
285 }
286 /* was a transition segment to history taken? */
287 else if (r == (QState)Q_RET_TRAN_HIST) {
288 tmp.obj = me->state.obj; /* save history */
289 me->state.obj = s; /* restore the original state */
290#ifdef Q_SPY
291 QMsm_exitToTranSource_(me, s, t, qs_id);
292 (void)QMsm_execTatbl_(me, tatbl, qs_id);
293 r = QMsm_enterHistory_(me, tmp.obj, qs_id);
294#else
295 QMsm_exitToTranSource_(me, s, t);
296 (void)QMsm_execTatbl_(me, tatbl);
297 r = QMsm_enterHistory_(me, tmp.obj);
298#endif
299 s = me->state.obj;
300 }
301 /* was a transition segment to an exit point taken? */
302 else if (r == (QState)Q_RET_TRAN_XP) {
303 tmp.act = me->state.act; /* save XP action */
304 me->state.obj = s; /* restore the original state */
305 r = (*tmp.act)(me); /* execute the XP action */
306 if (r == (QState)Q_RET_TRAN) { /* XP -> TRAN ? */
307#ifdef Q_SPY
308 tmp.tatbl = me->temp.tatbl; /* save me->temp */
309 QMsm_exitToTranSource_(me, s, t, qs_id);
310 /* take the tran-to-XP segment inside submachine */
311 (void)QMsm_execTatbl_(me, tatbl, qs_id);
312#else
313 QMsm_exitToTranSource_(me, s, t);
314 /* take the tran-to-XP segment inside submachine */
315 (void)QMsm_execTatbl_(me, tatbl);
316#endif /* Q_SPY */
317 s = me->state.obj;
318#ifdef Q_SPY
319 me->temp.tatbl = tmp.tatbl; /* restore me->temp */
320#endif /* Q_SPY */
321 }
322 else if (r == (QState)Q_RET_TRAN_HIST) { /* XP -> HIST ? */
323 tmp.obj = me->state.obj; /* save the history */
324 me->state.obj = s; /* restore the original state */
325#ifdef Q_SPY
326 s = me->temp.obj; /* save me->temp */
327 QMsm_exitToTranSource_(me, me->state.obj, t, qs_id);
328 /* take the tran-to-XP segment inside submachine */
329 (void)QMsm_execTatbl_(me, tatbl, qs_id);
330#else
331 QMsm_exitToTranSource_(me, me->state.obj, t);
332 /* take the tran-to-XP segment inside submachine */
333 (void)QMsm_execTatbl_(me, tatbl);
334#endif /* Q_SPY */
335#ifdef Q_SPY
336 me->temp.obj = s; /* restore me->temp */
337#endif /* Q_SPY */
338 s = me->state.obj;
339 me->state.obj = tmp.obj; /* restore the history */
340 }
341 else {
342 /* TRAN_XP must NOT be followed by any other tran type */
343 Q_ASSERT_ID(330, r < (QState)Q_RET_TRAN);
344 }
345 }
346 else {
347 /* no other return value should be produced */
348 Q_ERROR_ID(340);
349 }
350
351 t = s; /* set target to the current state */
352
353 } while (r >= (QState)Q_RET_TRAN);
354
356 QS_TIME_PRE_(); /* time stamp */
357 QS_SIG_PRE_(e->sig); /* the signal of the event */
358 QS_OBJ_PRE_(me); /* this state machine object */
359 QS_FUN_PRE_(ts->stateHandler); /* the transition source */
360 QS_FUN_PRE_(s->stateHandler); /* the new active state */
362 }
363
364#ifdef Q_SPY
365 /* was the event handled? */
366 else if (r == (QState)Q_RET_HANDLED) {
367 /* internal tran. source can't be NULL */
368 Q_ASSERT_ID(340, t != (QMState *)0);
369
371 QS_TIME_PRE_(); /* time stamp */
372 QS_SIG_PRE_(e->sig); /* the signal of the event */
373 QS_OBJ_PRE_(me); /* this state machine object */
374 QS_FUN_PRE_(t->stateHandler); /* the source state */
376
377 }
378 /* event bubbled to the 'top' state? */
379 else if (t == (QMState *)0) {
380
382 QS_TIME_PRE_(); /* time stamp */
383 QS_SIG_PRE_(e->sig); /* the signal of the event */
384 QS_OBJ_PRE_(me); /* this state machine object */
385 QS_FUN_PRE_(s->stateHandler); /* the current state */
387
388 }
389#endif /* Q_SPY */
390 else {
391 /* empty */
392 }
393}
394
395/*==========================================================================*/
396#ifdef Q_SPY
398 return me->state.obj->stateHandler;
399}
400#endif
401
402/*==========================================================================*/
419#ifdef Q_SPY
420static QState QMsm_execTatbl_(QHsm * const me,
421 struct QMTranActTable const *tatbl,
422 uint_fast8_t const qs_id)
423#else
424static QState QMsm_execTatbl_(QHsm * const me,
425 struct QMTranActTable const *tatbl)
426#endif
427{
430
432 Q_REQUIRE_ID(400, tatbl != (struct QMTranActTable *)0);
433
434 for (QActionHandler const *a = &tatbl->act[0];
435 *a != Q_ACTION_CAST(0);
436 ++a)
437 {
438 r = (*(*a))(me); /* call the action through the 'a' pointer */
439#ifdef Q_SPY
440 if (r == (QState)Q_RET_ENTRY) {
441
443 QS_OBJ_PRE_(me); /* this state machine object */
444 QS_FUN_PRE_(me->temp.obj->stateHandler);/*entered state */
446 }
447 else if (r == (QState)Q_RET_EXIT) {
448
450 QS_OBJ_PRE_(me); /* this state machine object */
451 QS_FUN_PRE_(me->temp.obj->stateHandler); /* exited state */
453 }
454 else if (r == (QState)Q_RET_TRAN_INIT) {
455
457 QS_OBJ_PRE_(me); /* this state machine object */
458 QS_FUN_PRE_(tatbl->target->stateHandler); /* source */
459 QS_FUN_PRE_(me->temp.tatbl->target->stateHandler);/* target */
461 }
462 else if (r == (QState)Q_RET_TRAN_EP) {
463
465 QS_OBJ_PRE_(me); /* this state machine object */
466 QS_FUN_PRE_(tatbl->target->stateHandler); /* source */
467 QS_FUN_PRE_(me->temp.tatbl->target->stateHandler);/* target */
469 }
470 else if (r == (QState)Q_RET_TRAN_XP) {
471
473 QS_OBJ_PRE_(me); /* this state machine object */
474 QS_FUN_PRE_(tatbl->target->stateHandler); /* source */
475 QS_FUN_PRE_(me->temp.tatbl->target->stateHandler);/* target */
477 }
478 else {
479 /* empty */
480 }
481#endif /* Q_SPY */
482 }
483
484 me->state.obj = (r >= (QState)Q_RET_TRAN)
485 ? me->temp.tatbl->target
486 : tatbl->target;
487 return r;
488}
489
490/*==========================================================================*/
503#ifdef Q_SPY
504static void QMsm_exitToTranSource_(QHsm * const me, QMState const *cs,
505 QMState const *ts,
506 uint_fast8_t const qs_id)
507#else
508static void QMsm_exitToTranSource_(QHsm * const me, QMState const *cs,
509 QMState const *ts)
510#endif
511{
512 QMState const *s = cs;
513
514 /* exit states from the current state to the tran. source state */
515 while (s != ts) {
516 /* exit action provided in state 's'? */
517 if (s->exitAction != Q_ACTION_CAST(0)) {
519
520 (void)(*s->exitAction)(me); /* execute the exit action */
521
523 QS_OBJ_PRE_(me); /* this state machine object */
524 QS_FUN_PRE_(s->stateHandler); /* the exited state handler */
526 }
527
528 s = s->superstate; /* advance to the superstate */
529
530 if (s == (QMState *)0) { /* reached the top of a submachine? */
531 s = me->temp.obj; /* the superstate from QM_SM_EXIT() */
532 Q_ASSERT_ID(510, s != (QMState *)0); /* must be valid */
533 }
534 }
535}
536
537/*==========================================================================*/
552#ifdef Q_SPY
553static QState QMsm_enterHistory_(QHsm * const me, QMState const *const hist,
554 uint_fast8_t const qs_id)
555#else
556static QState QMsm_enterHistory_(QHsm * const me, QMState const *const hist)
557#endif
558{
559 QMState const *s = hist;
560 QMState const *ts = me->state.obj; /* transition source */
561 QMState const *epath[QMSM_MAX_ENTRY_DEPTH_];
563
565 QS_OBJ_PRE_(me); /* this state machine object */
566 QS_FUN_PRE_(ts->stateHandler); /* source state handler */
567 QS_FUN_PRE_(hist->stateHandler); /* target state handler */
569
570 int_fast8_t i = 0; /* transition entry path index */
571 while (s != ts) {
572 if (s->entryAction != Q_ACTION_CAST(0)) {
574 epath[i] = s;
575 ++i;
576 }
577 s = s->superstate;
578 if (s == (QMState *)0) {
579 ts = s; /* force exit from the for-loop */
580 }
581 }
582
583 /* retrace the entry path in reverse (desired) order... */
584 while (i > 0) {
585 --i;
586 (void)(*epath[i]->entryAction)(me); /* run entry action in epath[i] */
587
589 QS_OBJ_PRE_(me);
590 QS_FUN_PRE_(epath[i]->stateHandler); /* entered state handler */
592 }
593
594 me->state.obj = hist; /* set current state to the transition target */
595
596 /* initial tran. present? */
597 QState r;
598 if (hist->initAction != Q_ACTION_CAST(0)) {
599 r = (*hist->initAction)(me); /* execute the transition action */
600 }
601 else {
602 r = (QState)Q_RET_NULL;
603 }
604 return r;
605}
606
607/*==========================================================================*/
623bool QMsm_isInState(QMsm const * const me, QMState const * const state) {
624 bool inState = false; /* assume that this MSM is not in 'state' */
625
626 for (QMState const *s = me->super.state.obj;
627 s != (QMState *)0;
628 s = s->superstate)
629 {
630 if (s == state) {
631 inState = true; /* match found, return 'true' */
632 break;
633 }
634 }
635 return inState;
636}
637
638/*==========================================================================*/
657QMState const *QMsm_childStateObj(QHsm const * const me,
658 QMState const * const parent)
659{
660 QMState const *child = me->state.obj;
661 bool isFound = false; /* start with the child not found */
662
663 for (QMState const *s = me->state.obj;
664 s != (QMState *)0;
665 s = s->superstate)
666 {
667 if (s == parent) {
668 isFound = true; /* child is found */
669 break;
670 }
671 else {
672 child = s;
673 }
674 }
675
677 Q_ENSURE_ID(810, isFound);
678#ifdef Q_NASSERT
679 (void)isFound; /* avoid compiler warning about unused variable */
680#endif
681
682 return child; /* return the child */
683}
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
#define Q_ERROR_ID(id_)
Definition: qassert.h:187
@ Q_RET_ENTRY
Definition: qep.h:582
@ Q_RET_HANDLED
Definition: qep.h:578
@ Q_RET_TRAN_XP
Definition: qep.h:595
@ Q_RET_TRAN_INIT
Definition: qep.h:590
@ Q_RET_TRAN
Definition: qep.h:589
@ Q_RET_UNHANDLED
Definition: qep.h:575
@ Q_RET_SUPER
Definition: qep.h:573
@ Q_RET_NULL
Definition: qep.h:586
@ Q_RET_TRAN_HIST
Definition: qep.h:594
@ Q_RET_SUPER_SUB
Definition: qep.h:574
@ Q_RET_TRAN_EP
Definition: qep.h:591
@ Q_RET_EXIT
Definition: qep.h:583
#define Q_STATE_CAST(handler_)
Definition: qep.h:196
QState(* QStateHandler)(void *const me, QEvt const *const e)
Definition: qep.h:176
#define Q_ACTION_CAST(action_)
Definition: qep.h:207
#define Q_EVT_CAST(class_)
Definition: qep.h:158
uint_fast8_t QState
Definition: qep.h:170
QState(* QActionHandler)(void *const me)
Definition: qep.h:179
#define QMSM_MAX_ENTRY_DEPTH_
Definition: qep_msm.c:48
static struct QMState const l_msm_top_s
Definition: qep_msm.c:50
#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_TRAN_EP
Definition: qs.h:137
@ 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_TRAN_XP
Definition: qs.h:138
@ QS_QEP_STATE_ENTRY
Definition: qs.h:58
@ 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
struct QHsmVtable const * vptr
Definition: qep.h:254
union QHsmAttr state
Definition: qep.h:255
union QHsmAttr temp
Definition: qep.h:256
Definition: qep.h:442
struct QMState const * superstate
Definition: qep.h:443
QActionHandler const entryAction
Definition: qep.h:445
QActionHandler const initAction
Definition: qep.h:447
QActionHandler const exitAction
Definition: qep.h:446
QStateHandler const stateHandler
Definition: qep.h:444
QActionHandler const act[1]
Definition: qep.h:454
struct QMState const * target
Definition: qep.h:453
Definition: qep.h:425
QHsm super
Definition: qep.h:426
QStateHandler QMsm_getStateHandler_(QHsm *const me)
Definition: qep_msm.c:397
static void QMsm_exitToTranSource_(QHsm *const me, QMState const *cs, QMState const *ts, uint_fast8_t const qs_id)
Definition: qep_msm.c:504
void QMsm_init_(QHsm *const me, void const *const e, uint_fast8_t const qs_id)
Definition: qep_msm.c:142
void QMsm_dispatch_(QHsm *const me, QEvt const *const e, uint_fast8_t const qs_id)
Definition: qep_msm.c:206
QMState const * QMsm_childStateObj(QHsm const *const me, QMState const *const parent)
Definition: qep_msm.c:657
static QState QMsm_enterHistory_(QHsm *const me, QMState const *const hist, uint_fast8_t const qs_id)
Definition: qep_msm.c:553
bool QMsm_isInState(QMsm const *const me, QMState const *const state)
Definition: qep_msm.c:623
void QMsm_ctor(QMsm *const me, QStateHandler initial)
Definition: qep_msm.c:114
static QState QMsm_execTatbl_(QHsm *const me, struct QMTranActTable const *tatbl, uint_fast8_t const qs_id)
Definition: qep_msm.c:420
Definition: qep.h:219
QMTranActTable const * tatbl
Definition: qep.h:224
QStateHandler fun
Definition: qep.h:220
struct QMState const * obj
Definition: qep.h:223
QActionHandler act
Definition: qep.h:221