QP/C  8.0.4
Real-Time Event Framework
Loading...
Searching...
No Matches
qep_msm.c
Go to the documentation of this file.
1//============================================================================
2// QP/C Real-Time Event Framework (RTEF)
3//
4// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
5//
6// Q u a n t u m L e a P s
7// ------------------------
8// Modern Embedded Software
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
11//
12// This software is dual-licensed under the terms of the open-source GNU
13// General Public License (GPL) or under the terms of one of the closed-
14// source Quantum Leaps commercial licenses.
15//
16// Redistributions in source code must retain this top-level comment block.
17// Plagiarizing this software to sidestep the license obligations is illegal.
18//
19// NOTE:
20// The GPL does NOT permit the incorporation of this code into proprietary
21// programs. Please contact Quantum Leaps for commercial licensing options,
22// which expressly supersede the GPL and are designed explicitly for
23// closed-source distribution.
24//
25// Quantum Leaps contact information:
26// <www.state-machine.com/licensing>
27// <info@state-machine.com>
28//============================================================================
29#define QP_IMPL // this is QP implementation
30#include "qp_port.h" // QP port
31#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
32#ifdef Q_SPY // QS software tracing enabled?
33 #include "qs_port.h" // QS port
34 #include "qs_pkg.h" // QS facilities for pre-defined trace records
35#else
36 #include "qs_dummy.h" // disable the QS software tracing
37#endif // Q_SPY
38
39Q_DEFINE_THIS_MODULE("qep_msm")
40
41#define QMSM_MAX_NEST_DEPTH_ ((int_fast8_t)6)
42#define QMSM_MAX_TRAN_LENGTH_ ((int_fast8_t)(2*QMSM_MAX_NEST_DEPTH_))
43
44//! @cond INTERNAL
45
46// top-state object for QMsm-style state machines
47static struct QMState const l_msm_top_s = {
48 (struct QMState *)0,
49 Q_STATE_CAST(0),
53};
54
55#ifdef Q_SPY
56// helper macro to trace state action (entry/exit)
57#define QS_STATE_ACT_(rec_, state_) \
58 QS_CRIT_ENTRY(); \
59 QS_BEGIN_PRE((rec_), qsId) \
60 QS_OBJ_PRE(me); \
61 QS_FUN_PRE(state_); \
62 QS_END_PRE() \
63 QS_CRIT_EXIT()
64
65// internal helper macro to top-most init
66#define QS_TOP_INIT_(rec_, trg_) \
67 QS_CRIT_ENTRY(); \
68 QS_BEGIN_PRE((rec_), qsId) \
69 QS_TIME_PRE(); \
70 QS_OBJ_PRE(me); \
71 QS_FUN_PRE(trg_); \
72 QS_END_PRE() \
73 QS_CRIT_EXIT()
74
75// internal helper macro to trace transition segment
76#define QS_TRAN_SEG_(rec_, src_, trg_) \
77 QS_CRIT_ENTRY(); \
78 QS_BEGIN_PRE((rec_), qsId) \
79 QS_OBJ_PRE(me); \
80 QS_FUN_PRE(src_); \
81 QS_FUN_PRE(trg_); \
82 QS_END_PRE() \
83 QS_CRIT_EXIT()
84
85// internal helper macro to trace transition begin/end
86#define QS_TRAN0_(rec_, trg_) \
87 QS_CRIT_ENTRY(); \
88 QS_BEGIN_PRE((rec_), qsId) \
89 QS_TIME_PRE(); \
90 QS_SIG_PRE(e->sig); \
91 QS_OBJ_PRE(me); \
92 QS_FUN_PRE(trg_); \
93 QS_END_PRE() \
94 QS_CRIT_EXIT()
95
96// internal helper macro to trace regular transition
97#define QS_TRAN_END_(rec_, src_, trg_) \
98 QS_CRIT_ENTRY(); \
99 QS_BEGIN_PRE((rec_), qsId) \
100 QS_TIME_PRE(); \
101 QS_SIG_PRE(e->sig); \
102 QS_OBJ_PRE(me); \
103 QS_FUN_PRE(src_); \
104 QS_FUN_PRE(trg_); \
105 QS_END_PRE() \
106 QS_CRIT_EXIT()
107
108#else
109#define QS_STATE_ACT_(rec_, state_) ((void)0)
110#define QS_TOP_INIT_(rec_, trg_) ((void)0)
111#define QS_TRAN_SEG_(rec_, src_, trg_) ((void)0)
112#define QS_TRAN0_(rec_, trg_) ((void)0)
113#define QS_TRAN_END_(rec_, src_, trg_) ((void)0)
114#endif
115
116//! @endcond
117
118//============================================================================
119static QState QMsm_execTatbl_(
120 QAsm * const me,
121 QMTranActTable const * const tatbl,
122 uint_fast8_t const qsId);
123
124static void QMsm_exitToTranSource_(
125 QAsm * const me,
126 QMState const * const curr_state,
127 QMState const * const tran_source,
128 uint_fast8_t const qsId);
129
130static QState QMsm_enterHistory_(
131 QAsm * const me,
132 QMState const *const hist,
133 uint_fast8_t const qsId);
134
135//............................................................................
136//! @protected @memberof QMsm
137void QMsm_ctor(QMsm * const me,
138 QStateHandler const initial)
139{
140 static struct QAsmVtable const vtable = { // QAsm virtual table
141 &QMsm_init_,
142 &QMsm_dispatch_,
143 &QMsm_isIn_
144#ifdef Q_SPY
145 ,&QMsm_getStateHandler_
146#endif
147 };
148 // do not call the QAsm_ctor() here
149 me->super.vptr = &vtable;
150 me->super.state.obj = &l_msm_top_s; // the current state (top)
151 me->super.temp.fun = initial; // the initial tran. handler
152}
153
154//............................................................................
155//! @private @memberof QMsm
157 QAsm * const me,
158 void const * const e,
159 uint_fast8_t const qsId)
160{
161#ifndef Q_SPY
162 Q_UNUSED_PAR(qsId);
163#endif
164
165 Q_REQUIRE_LOCAL(200, me->temp.fun != Q_STATE_CAST(0));
166 Q_REQUIRE_LOCAL(210, me->state.obj == &l_msm_top_s);
167
168 // execute the top-most initial tran.
169 QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt));
170
171 // the top-most initial tran. must be taken
173 Q_ASSERT_LOCAL(250, me->temp.tatbl != (struct QMTranActTable *)0);
174
176 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
178
179 // set state to the last tran. target
180 me->state.obj = me->temp.tatbl->target;
181
182 // drill down into the state hierarchy with initial transitions...
183 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
184 do {
185 --lbound; // fixed loop bound
186 Q_INVARIANT_LOCAL(280, lbound >= 0);
187 r = QMsm_execTatbl_(me, me->temp.tatbl, qsId);
188 } while (r >= Q_RET_TRAN_INIT);
189
190 QS_TOP_INIT_(QS_QEP_INIT_TRAN, me->state.obj->stateHandler);
191
192#ifndef Q_UNSAFE
193 // establish stable state configuration
194 me->temp.uint = (uintptr_t)~me->state.uint;
195#endif
196}
197
198//............................................................................
199//! @private @memberof QMsm
201 QAsm * const me,
202 QEvt const * const e,
203 uint_fast8_t const qsId)
204{
205#ifndef Q_SPY
206 Q_UNUSED_PAR(qsId);
207#endif
208
209 Q_INVARIANT_LOCAL(300, me->state.uint == (uintptr_t)(~me->temp.uint));
210
211 Q_REQUIRE_LOCAL(310, e != (QEvt *)0);
212
213 QMState const *s = me->state.obj; // the current state
214 QMState const * const t = s; // store the current state for later
216 QS_TRAN0_(QS_QEP_DISPATCH, s->stateHandler);
217
218 // scan the state hierarchy up to the top state...
220 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
221 do {
222 --lbound; // fixed loop bound
223 Q_INVARIANT_LOCAL(340, lbound >= 0);
224
225 r = (*s->stateHandler)(me, e); // call state handler function
226 if (r >= Q_RET_HANDLED) { // event handled? (the most frequent case)
227 break; // done scanning the state hierarchy
228 }
229#ifdef Q_SPY
230 if (r == Q_RET_UNHANDLED) { // event unhandled due to a guard?
232 QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId)
233 QS_SIG_PRE(e->sig);
234 QS_OBJ_PRE(me);
235 QS_FUN_PRE(s->stateHandler);
236 QS_END_PRE()
237 QS_CRIT_EXIT();
238 }
239#endif
240 s = s->superstate; // advance to the superstate
241 } while (s != (QMState *)0);
242
243 if (s == (QMState *)0) { // event bubbled to the 'top' state?
244#ifdef Q_SPY
245 QS_TRAN0_(QS_QEP_IGNORED, t->stateHandler);
246#endif // Q_SPY
247 }
248 else if (r >= Q_RET_TRAN) { // any kind of tran. taken?
249#ifdef Q_SPY
250 QMState const * const ts = s; // tran. source for QS tracing
251#endif // Q_SPY
252
253 if (r == Q_RET_TRAN) {
254 struct QMTranActTable const * const tatbl = me->temp.tatbl;
255 QMsm_exitToTranSource_(me, t, s, qsId);
256 r = QMsm_execTatbl_(me, tatbl, qsId);
257#ifdef Q_SPY
258 s = me->state.obj;
259#endif // Q_SPY
260 }
261 else if (r == Q_RET_TRAN_HIST) { // was it tran. to history?
262 QMState const * const hist = me->state.obj; // save history
263 me->state.obj = t; // restore the original state
264
265 QS_TRAN_SEG_(QS_QEP_TRAN_HIST,
266 s->stateHandler, hist->stateHandler);
267
268 // save the tran-action table before it gets clobbered
269 struct QMTranActTable const * const tatbl = me->temp.tatbl;
270 QMsm_exitToTranSource_(me, t, s, qsId);
271 (void)QMsm_execTatbl_(me, tatbl, qsId);
272 r = QMsm_enterHistory_(me, hist, qsId);
273#ifdef Q_SPY
274 s = me->state.obj;
275#endif // Q_SPY
276 }
277 else {
278 // empty
279 }
280
281 lbound = QMSM_MAX_NEST_DEPTH_;
282 while (r == Q_RET_TRAN_INIT) { // initial tran. in the target?
283
284 r = QMsm_execTatbl_(me, me->temp.tatbl, qsId);
285#ifdef Q_SPY
286 s = me->state.obj;
287#endif // Q_SPY
288
289 --lbound; // fixed loop bound
290 Q_INVARIANT_LOCAL(360, lbound >= 0);
291 }
292
293 QS_TRAN_END_(QS_QEP_TRAN, ts->stateHandler, s->stateHandler);
294 }
295#ifdef Q_SPY
296 else if (r == Q_RET_HANDLED) { // was the event handled?
297 QS_TRAN0_(QS_QEP_INTERN_TRAN, s->stateHandler);
298 }
299#endif // Q_SPY
300 else {
301 // empty
302 }
303
304#ifndef Q_UNSAFE
305 // establish stable state configuration
306 me->temp.uint = (uintptr_t)~me->state.uint;
307#endif
308}
309
310//............................................................................
311//! @private @memberof QMsm
313 QAsm * const me,
314 QMTranActTable const * const tatbl,
315 uint_fast8_t const qsId)
316{
317#ifndef Q_SPY
318 Q_UNUSED_PAR(qsId);
319#endif
320
321 Q_REQUIRE_LOCAL(400, tatbl != (struct QMTranActTable *)0);
322
324 QState r = Q_RET_NULL;
325 QActionHandler const *a = &tatbl->act[0];
326 int_fast8_t lbound = QMSM_MAX_TRAN_LENGTH_;
327 while (*a != Q_ACTION_CAST(0)) {
328 r = (*(*a))(me); // call the action through the 'a' pointer
329 ++a;
330
331#ifdef Q_SPY
332 if (r == Q_RET_ENTRY) {
333 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, me->temp.obj->stateHandler);
334 }
335 else if (r == Q_RET_EXIT) {
336 QS_STATE_ACT_(QS_QEP_STATE_EXIT, me->temp.obj->stateHandler);
337 }
338 else if (r == Q_RET_TRAN_INIT) {
339 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
340 tatbl->target->stateHandler,
342 }
343 else {
344 // empty
345 }
346#endif // Q_SPY
347
348 --lbound; // fixed loop bound
349 Q_INVARIANT_LOCAL(480, lbound >= 0);
350 }
351
352 me->state.obj = (r >= Q_RET_TRAN)
353 ? me->temp.tatbl->target
354 : tatbl->target;
355 return r;
356}
357
358//............................................................................
359//! @private @memberof QMsm
361 QAsm * const me,
362 QMState const * const curr_state,
363 QMState const * const tran_source,
364 uint_fast8_t const qsId)
365{
366#ifndef Q_SPY
367 Q_UNUSED_PAR(qsId);
368#endif
370
371 // exit states from the current state to the tran. source state
372 QMState const *s = curr_state;
373 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
374 while (s != tran_source) {
375 if (s->exitAction != Q_ACTION_CAST(0)) { // exit action provided?
376 (void)(*s->exitAction)(me); // execute the exit action
377 QS_STATE_ACT_(QS_QEP_STATE_EXIT, me->temp.obj->stateHandler);
378 }
379 s = s->superstate; // advance to the superstate
380
381 --lbound; // fixed loop bound
382 Q_INVARIANT_LOCAL(580, lbound >= 0);
383 }
384}
385
386//............................................................................
387//! @private @memberof QMsm
389 QAsm * const me,
390 QMState const *const hist,
391 uint_fast8_t const qsId)
392{
393#ifndef Q_SPY
394 Q_UNUSED_PAR(qsId);
395#endif
396
397 // record the entry path from current state to history
398 QMState const *epath[QMSM_MAX_NEST_DEPTH_];
399 QMState const *s = hist;
400 int_fast8_t i = -1; // entry path index (one below [0])
401 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
402 while (s != me->state.obj) {
403 if (s->entryAction != Q_ACTION_CAST(0)) {
404 ++i;
406 epath[i] = s;
407 }
408 s = s->superstate;
409
410 --lbound; // fixed loop bound
411 Q_INVARIANT_LOCAL(620, lbound >= 0);
412 }
413
415 // retrace the entry path in reverse (desired) order...
416 // NOTE: i the fixed loop bound
417 for (; i >= 0; --i) {
418 (void)(*epath[i]->entryAction)(me); // enter epath[i]
419 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, epath[i]->stateHandler);
420 }
421
422 me->state.obj = hist; // set current state to the tran. target
423
424 // initial tran. present?
425 QState r = Q_RET_NULL;
426 if (hist->initAction != Q_ACTION_CAST(0)) {
427 r = (*hist->initAction)(me); // execute the tran. action
428 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
430 }
431 return r;
432}
433
434//............................................................................
435//! @private @memberof QMsm
437 QAsm * const me,
438 QStateHandler const stateHndl)
439{
440 bool inState = false; // assume that this SM is not in 'state'
441 QMState const *s = me->state.obj;
442 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
443 while (s != (QMState *)0) {
444 if (s->stateHandler == stateHndl) { // match found?
445 inState = true;
446 break;
447 }
448 s = s->superstate; // advance to the superstate
449
450 --lbound; // fixed loop bound
451 Q_INVARIANT_LOCAL(740, lbound >= 0);
452 }
453
454 return inState;
455}
456
457//............................................................................
458#ifdef Q_SPY
459//! @public @memberof QMsm
461 return me->state.obj->stateHandler;
462}
463#endif // def Q_SPY
464
465//............................................................................
466//! @public @memberof QMsm
467QMState const * QMsm_childStateObj(QMsm const * const me,
468 QMState const * const parent)
469{
470 QMState const *s = me->super.state.obj; // start with current state
471 QMState const *child = s;
472 bool isFound = false; // assume the child NOT found
473 int_fast8_t lbound = QMSM_MAX_NEST_DEPTH_;
474 while (s != (QMState *)0) {
475 if (s == parent) {
476 isFound = true; // child is found
477 break;
478 }
479 child = s;
480 s = s->superstate;
481
482 --lbound; // fixed loop bound
483 Q_INVARIANT_LOCAL(840, lbound >= 0);
484 }
485 Q_ENSURE_LOCAL(890, isFound);
486
487#ifdef Q_UNSAFE
488 Q_UNUSED_PAR(isFound);
489#endif
490
491 return child;
492}
#define QMSM_MAX_TRAN_LENGTH_
maximum length of transition-action array
#define QMSM_MAX_NEST_DEPTH_
maximum depth of state nesting in a QMsm (including the top level)
#define Q_RET_UNHANDLED
Definition qp.h:188
#define Q_RET_NULL
Definition qp.h:199
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:94
#define Q_RET_TRAN_HIST
Definition qp.h:206
#define Q_RET_TRAN
Definition qp.h:202
#define Q_RET_EXIT
Definition qp.h:196
#define Q_STATE_CAST(handler_)
Perform cast to QStateHandler.
Definition qp.h:173
QState(* QStateHandler)(void *const me, QEvt const *const e)
Pointer to a state-handler function.
Definition qp.h:145
#define Q_ACTION_CAST(action_)
Perform cast to QActionHandler.
Definition qp.h:174
#define Q_RET_TRAN_INIT
Definition qp.h:203
#define Q_EVT_CAST(class_)
Perform downcast of an event onto a subclass of QEvt class_
Definition qp.h:139
uint_fast8_t QState
Type returned from state-handler functions.
Definition qp.h:144
#define Q_RET_SUPER
Definition qp.h:187
QState(* QActionHandler)(void *const me)
Pointer to an action-handler function.
Definition qp.h:146
#define Q_RET_ENTRY
Definition qp.h:195
#define Q_RET_HANDLED
Definition qp.h:191
Sample QP/C port.
#define QS_CRIT_STAT
Definition qs.h:432
@ QS_QEP_STATE_INIT
an initial transition was taken in a state
Definition qs.h:64
@ QS_QEP_TRAN_HIST
a tran. to history was taken
Definition qs.h:142
@ QS_QEP_STATE_EXIT
a state was exited
Definition qs.h:63
@ QS_QEP_INIT_TRAN
the top-most initial transition was taken
Definition qs.h:65
@ QS_QEP_INTERN_TRAN
an internal transition was taken
Definition qs.h:66
@ QS_QEP_STATE_ENTRY
a state was entered
Definition qs.h:62
@ QS_QEP_UNHANDLED
an event was un-handled due to a guard
Definition qs.h:70
@ QS_QEP_TRAN
a regular transition was taken
Definition qs.h:67
@ QS_QEP_DISPATCH
an event was dispatched (begin of RTC step)
Definition qs.h:69
@ QS_QEP_IGNORED
an event was ignored (silently discarded)
Definition qs.h:68
#define QS_CRIT_EXIT()
Definition qs.h:440
#define QS_CRIT_ENTRY()
Definition qs.h:436
QS/C package-scope interface.
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define Q_REQUIRE_LOCAL(id_, expr_)
Definition qsafe.h:99
#define Q_ENSURE_LOCAL(id_, expr_)
Definition qsafe.h:104
#define Q_ASSERT_LOCAL(id_, expr_)
Definition qsafe.h:72
#define Q_INVARIANT_LOCAL(id_, expr_)
Definition qsafe.h:109
Abstract State Machine class (state machine interface)
Definition qp.h:179
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OO)
Definition qp.h:180
union QAsmAttr state
Current state (pointer to the current state-handler function)
Definition qp.h:181
union QAsmAttr temp
Temporary storage for target/act-table etc.
Definition qp.h:182
Virtual table for the QAsm class.
Definition qp.h:222
Event class.
Definition qp.h:112
QSignal sig
Signal of the event (see Event Signal)
Definition qp.h:113
State object for the QMsm class (QM State Machine)
Definition qp.h:151
struct QMState const * superstate
Definition qp.h:152
QActionHandler const entryAction
Definition qp.h:154
QActionHandler const initAction
Definition qp.h:156
QStateHandler const stateHandler
Definition qp.h:153
Transition-Action Table for the QMsm State Machine.
Definition qp.h:159
QActionHandler const act[1]
Definition qp.h:161
QMState const * target
Definition qp.h:160
Hierarchical State Machine class (QMsm-style state machine implementation strategy)
Definition qp.h:312
QAsm super
Definition qp.h:313
static QState QMsm_enterHistory_(QAsm *const me, QMState const *const hist, uint_fast8_t const qsId)
Enter history of a composite state.
Definition qep_msm.c:388
static void QMsm_exitToTranSource_(QAsm *const me, QMState const *const curr_state, QMState const *const tran_source, uint_fast8_t const qsId)
Exit the current state up to the explicit transition source.
Definition qep_msm.c:360
void QMsm_init_(QAsm *const me, void const *const e, uint_fast8_t const qsId)
Implementation of the top-most initial transition in QMsm.
Definition qep_msm.c:156
static QState QMsm_execTatbl_(QAsm *const me, QMTranActTable const *const tatbl, uint_fast8_t const qsId)
Execute transition-action table.
Definition qep_msm.c:312
bool QMsm_isIn_(QAsm *const me, QStateHandler const stateHndl)
Tests if a given state is part of the current active state configuration.
Definition qep_msm.c:436
QStateHandler QMsm_getStateHandler_(QAsm *const me)
Implementation of getting the state handler in a QMsm subclass.
Definition qep_msm.c:460
QMState const * QMsm_childStateObj(QMsm const *const me, QMState const *const parent)
Obtain the current active child state of a given parent in QMsm.
Definition qep_msm.c:467
void QMsm_dispatch_(QAsm *const me, QEvt const *const e, uint_fast8_t const qsId)
Implementation of dispatching events to a QMsm.
Definition qep_msm.c:200
uintptr_t uint
Definition qp.h:170
QMTranActTable const * tatbl
Definition qp.h:168
QStateHandler fun
Definition qp.h:165
struct QMState const * obj
Definition qp.h:169