QP/C  8.0.3
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_ENTRY_DEPTH_ ((int_fast8_t)4)
42
43//! @cond INTERNAL
44
45// top-state object for QMsm-style state machines
46static struct QMState const l_msm_top_s = {
47 (struct QMState *)0,
48 Q_STATE_CAST(0),
52};
53
54#ifdef Q_SPY
55// helper macro to trace state action (entry/exit)
56#define QS_STATE_ACT_(rec_, state_) \
57 QS_CRIT_ENTRY(); \
58 QS_BEGIN_PRE((rec_), qsId) \
59 QS_OBJ_PRE(me); \
60 QS_FUN_PRE(state_); \
61 QS_END_PRE() \
62 QS_CRIT_EXIT()
63
64// internal helper macro to top-most init
65#define QS_TOP_INIT_(rec_, trg_) \
66 QS_CRIT_ENTRY(); \
67 QS_BEGIN_PRE((rec_), qsId) \
68 QS_TIME_PRE(); \
69 QS_OBJ_PRE(me); \
70 QS_FUN_PRE(trg_); \
71 QS_END_PRE() \
72 QS_CRIT_EXIT()
73
74// internal helper macro to trace transition segment
75#define QS_TRAN_SEG_(rec_, src_, trg_) \
76 QS_CRIT_ENTRY(); \
77 QS_BEGIN_PRE((rec_), qsId) \
78 QS_OBJ_PRE(me); \
79 QS_FUN_PRE(src_); \
80 QS_FUN_PRE(trg_); \
81 QS_END_PRE() \
82 QS_CRIT_EXIT()
83
84// internal helper macro to trace transition begin/end
85#define QS_TRAN0_(rec_, trg_) \
86 QS_CRIT_ENTRY(); \
87 QS_BEGIN_PRE((rec_), qsId) \
88 QS_TIME_PRE(); \
89 QS_SIG_PRE(e->sig); \
90 QS_OBJ_PRE(me); \
91 QS_FUN_PRE(trg_); \
92 QS_END_PRE() \
93 QS_CRIT_EXIT()
94
95// internal helper macro to trace regular transition
96#define QS_TRAN_END_(rec_, src_, trg_) \
97 QS_CRIT_ENTRY(); \
98 QS_BEGIN_PRE((rec_), qsId) \
99 QS_TIME_PRE(); \
100 QS_SIG_PRE(e->sig); \
101 QS_OBJ_PRE(me); \
102 QS_FUN_PRE(src_); \
103 QS_FUN_PRE(trg_); \
104 QS_END_PRE() \
105 QS_CRIT_EXIT()
106
107#else
108#define QS_STATE_ACT_(rec_, state_) ((void)0)
109#define QS_TOP_INIT_(rec_, trg_) ((void)0)
110#define QS_TRAN_SEG_(rec_, src_, trg_) ((void)0)
111#define QS_TRAN0_(rec_, trg_) ((void)0)
112#define QS_TRAN_END_(rec_, src_, trg_) ((void)0)
113#endif
114
115//! @endcond
116
117//============================================================================
118//! @private @memberof QMsm
119static QState QMsm_execTatbl_(
120 QAsm * const me,
121 QMTranActTable const * const tatbl,
122 uint_fast8_t const qsId);
123
124//! @private @memberof QMsm
125static void QMsm_exitToTranSource_(
126 QAsm * const me,
127 QMState const * const cs,
128 QMState const * const ts,
129 uint_fast8_t const qsId);
130
131//! @private @memberof QMsm
132static QState QMsm_enterHistory_(
133 QAsm * const me,
134 QMState const *const hist,
135 uint_fast8_t const qsId);
136
137//............................................................................
138//! @protected @memberof QMsm
139void QMsm_ctor(QMsm * const me,
140 QStateHandler const initial)
141{
142 static struct QAsmVtable const vtable = { // QAsm virtual table
143 &QMsm_init_,
144 &QMsm_dispatch_,
145 &QMsm_isIn_
146#ifdef Q_SPY
147 ,&QMsm_getStateHandler_
148#endif
149 };
150 // do not call the QAsm_ctor() here
151 me->super.vptr = &vtable;
152 me->super.state.obj = &l_msm_top_s; // the current state (top)
153 me->super.temp.fun = initial; // the initial tran. handler
154}
155
156//............................................................................
157//! @private @memberof QMsm
159 QAsm * const me,
160 void const * const e,
161 uint_fast8_t const qsId)
162{
163#ifndef Q_SPY
164 Q_UNUSED_PAR(qsId);
165#endif
166
169 Q_REQUIRE_INCRIT(200, (me->vptr != (struct QAsmVtable *)0)
170 && (me->temp.fun != Q_STATE_CAST(0))
171 && (me->state.obj == &l_msm_top_s));
172 QF_CRIT_EXIT();
173
174 // execute the top-most initial tran.
175 QState r = (*me->temp.fun)(me, Q_EVT_CAST(QEvt));
176
178 // the top-most initial tran. must be taken
180 QF_CRIT_EXIT();
181
182 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
184
185 // set state to the last tran. target
186 me->state.obj = me->temp.tatbl->target;
187
188 // drill down into the state hierarchy with initial transitions...
189 while (r >= Q_RET_TRAN_INIT) {
190 // execute the tran. table
191 r = QMsm_execTatbl_(me, me->temp.tatbl, qsId);
192 }
193
194 QS_TOP_INIT_(QS_QEP_INIT_TRAN, me->state.obj->stateHandler);
195}
196
197//............................................................................
198//! @private @memberof QMsm
200 QAsm * const me,
201 QEvt const * const e,
202 uint_fast8_t const qsId)
203{
204#ifndef Q_SPY
205 Q_UNUSED_PAR(qsId);
206#endif
207
208 QMState const *s = me->state.obj; // store the current state
209 QMState const *t = s;
210
214 (e != (QEvt *)0)
215 && (s != (QMState *)0));
216 QF_CRIT_EXIT();
217
218 QS_TRAN0_(QS_QEP_DISPATCH, s->stateHandler);
219
220 // scan the state hierarchy up to the top state...
222 while (t != (QMState *)0) {
223 r = (*t->stateHandler)(me, e); // call state handler function
224
225 if (r >= Q_RET_HANDLED) { // event handled? (the most frequent case)
226 break; // done scanning the state hierarchy
227 }
228#ifdef Q_SPY
229 if (r == Q_RET_UNHANDLED) { // event unhandled due to a guard?
231 QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId)
232 QS_SIG_PRE(e->sig);
233 QS_OBJ_PRE(me);
235 QS_END_PRE()
236 QS_CRIT_EXIT();
237 }
238#endif
239 t = t->superstate; // advance to the superstate
240 }
241
242 if (r >= Q_RET_TRAN) { // any kind of tran. taken?
244 // the tran. source state must not be NULL
245 Q_ASSERT_INCRIT(330, t != (QMState *)0);
246 QF_CRIT_EXIT();
247
248#ifdef Q_SPY
249 QMState const * const ts = t; // tran. source for QS tracing
250#endif // Q_SPY
251
252 if (r == Q_RET_TRAN_HIST) { // was it tran. to history?
253 QMState const * const hist = me->state.obj; // save history
254 me->state.obj = s; // restore the original state
255
256 QS_TRAN_SEG_(QS_QEP_TRAN_HIST,
257 t->stateHandler, hist->stateHandler);
258
259 // save the tran-action table before it gets clobbered
260 struct QMTranActTable const * const tatbl = me->temp.tatbl;
261 QMsm_exitToTranSource_(me, s, t, qsId);
262 (void)QMsm_execTatbl_(me, tatbl, qsId);
263 r = QMsm_enterHistory_(me, hist, qsId);
264 s = me->state.obj;
265 t = s; // set target to the current state
266 }
267
268 while (r >= Q_RET_TRAN) {
269 // save the tran-action table before it gets clobbered
270 struct QMTranActTable const * const tatbl = me->temp.tatbl;
271 me->temp.obj = (QMState *)0; // clear
272 QMsm_exitToTranSource_(me, s, t, qsId);
273 r = QMsm_execTatbl_(me, tatbl, qsId);
274 s = me->state.obj;
275 t = s; // set target to the current state
276 }
277
278 QS_TRAN_END_(QS_QEP_TRAN, ts->stateHandler, s->stateHandler);
279 }
280#ifdef Q_SPY
281 else if (r == Q_RET_HANDLED) { // was the event handled?
283 // internal tran. source can't be NULL
284 Q_ASSERT_INCRIT(380, t != (QMState *)0);
285 QF_CRIT_EXIT();
286
287 QS_TRAN0_(QS_QEP_INTERN_TRAN, t->stateHandler);
288 }
289 else if (t == (QMState *)0) { // event bubbled to the 'top' state?
290 QS_TRAN0_(QS_QEP_IGNORED, s->stateHandler);
291 }
292#endif // Q_SPY
293 else {
294 // empty
295 }
296}
297
298//............................................................................
299//! @private @memberof QMsm
301 QAsm * const me,
302 QStateHandler const stateHndl)
303{
304 bool inState = false; // assume that this SM is not in 'state'
305
306 QMState const *s = me->state.obj;
307 while (s != (QMState *)0) {
308 if (s->stateHandler == stateHndl) { // match found?
309 inState = true;
310 break;
311 }
312 s = s->superstate; // advance to the superstate
313 }
314
315 return inState;
316}
317
318//............................................................................
319#ifdef Q_SPY
320//! @public @memberof QMsm
322 return me->state.obj->stateHandler;
323}
324#endif // def Q_SPY
325
326//............................................................................
327//! @public @memberof QMsm
328QMState const * QMsm_childStateObj(QMsm const * const me,
329 QMState const * const parent)
330{
331 QMState const *s = me->super.state.obj; // start with current state
332 QMState const *child = s;
333 bool isFound = false; // assume the child NOT found
334
335 while (s != (QMState *)0) {
336 if (s == parent) {
337 isFound = true; // child is found
338 break;
339 }
340 child = s;
341 s = s->superstate;
342 }
345 Q_ASSERT_INCRIT(590, isFound);
346 QF_CRIT_EXIT();
347
348#ifdef Q_UNSAFE
349 Q_UNUSED_PAR(isFound);
350#endif
351
352 return child; // return the child
353}
354
355//............................................................................
356//! @private @memberof QMsm
358 QAsm * const me,
359 QMTranActTable const * const tatbl,
360 uint_fast8_t const qsId)
361{
362#ifndef Q_SPY
363 Q_UNUSED_PAR(qsId);
364#endif
365
368 // precondition:
369 // - the tran-action table pointer must not be NULL
370 Q_REQUIRE_INCRIT(600, tatbl != (struct QMTranActTable *)0);
371 QF_CRIT_EXIT();
372
373 QState r = Q_RET_NULL;
374 QActionHandler const *a = &tatbl->act[0];
375 while (*a != Q_ACTION_CAST(0)) {
376 r = (*(*a))(me); // call the action through the 'a' pointer
377 ++a;
378#ifdef Q_SPY
379 if (r == Q_RET_ENTRY) {
380 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, me->temp.obj->stateHandler);
381 }
382 else if (r == Q_RET_EXIT) {
383 QS_STATE_ACT_(QS_QEP_STATE_EXIT, me->temp.obj->stateHandler);
384 }
385 else if (r == Q_RET_TRAN_INIT) {
386 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
387 tatbl->target->stateHandler,
389 }
390 else {
391 // empty
392 }
393#endif // Q_SPY
394 }
396 Q_ASSERT_INCRIT(690, *a == Q_ACTION_CAST(0));
397 QF_CRIT_EXIT();
398
399 me->state.obj = (r >= Q_RET_TRAN)
400 ? me->temp.tatbl->target
401 : tatbl->target;
402 return r;
403}
404
405//............................................................................
406//! @private @memberof QMsm
408 QAsm * const me,
409 QMState const * const cs,
410 QMState const * const ts,
411 uint_fast8_t const qsId)
412{
413#ifndef Q_SPY
414 Q_UNUSED_PAR(qsId);
415#endif
417
418 // exit states from the current state to the tran. source state
419 QMState const *s = cs;
420 while (s != ts) {
421 // exit action provided in state 's'?
422 if (s->exitAction != Q_ACTION_CAST(0)) {
423 // execute the exit action
424 (void)(*s->exitAction)(me);
425
426 QS_STATE_ACT_(QS_QEP_STATE_EXIT, me->temp.obj->stateHandler);
427 }
428 s = s->superstate; // advance to the superstate
429 }
430}
431
432//............................................................................
433//! @private @memberof QMsm
435 QAsm * const me,
436 QMState const *const hist,
437 uint_fast8_t const qsId)
438{
439#ifndef Q_SPY
440 Q_UNUSED_PAR(qsId);
441#endif
442
443 // record the entry path from current state to history
444 QMState const *epath[QMSM_MAX_ENTRY_DEPTH_];
445 QMState const *s = hist;
446 int_fast8_t i = 0; // tran. entry path index & fixed upper loop bound
447 while ((s != me->state.obj) && (i < QMSM_MAX_ENTRY_DEPTH_)) {
448 if (s->entryAction != Q_ACTION_CAST(0)) {
449 epath[i] = s;
450 ++i;
451 }
452 s = s->superstate;
453 }
457 QF_CRIT_EXIT();
458
459 // retrace the entry path in reverse (desired) order...
460 // NOTE: i the fixed upper loop bound
461 for (i = i - 1; i >= 0; --i) {
462 // run entry action in epath[i]
463 (void)(*epath[i]->entryAction)(me);
464
465 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, epath[i]->stateHandler);
466 }
467
468 me->state.obj = hist; // set current state to the tran. target
469
470 // initial tran. present?
471 QState r = Q_RET_NULL;
472 if (hist->initAction != Q_ACTION_CAST(0)) {
473 r = (*hist->initAction)(me); // execute the tran. action
474
475 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
477 }
478 return r;
479}
480
#define QMSM_MAX_ENTRY_DEPTH_
maximum depth of entry levels in a MSM for tran. to history
#define Q_RET_UNHANDLED
Definition qp.h:187
#define Q_RET_NULL
Definition qp.h:198
#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:205
#define Q_RET_TRAN
Definition qp.h:201
#define Q_RET_EXIT
Definition qp.h:195
#define Q_STATE_CAST(handler_)
Perform cast to QStateHandler.
Definition qp.h:172
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:173
#define Q_RET_TRAN_INIT
Definition qp.h:202
#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:186
QState(* QActionHandler)(void *const me)
Pointer to an action-handler function.
Definition qp.h:146
#define Q_RET_ENTRY
Definition qp.h:194
#define Q_RET_HANDLED
Definition qp.h:190
Sample QP/C port.
QS/C dummy public interface.
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.h:149
#define QS_CRIT_STAT
Definition qs_dummy.h:156
#define QS_SIG_PRE(sig_)
Definition qs_dummy.h:147
#define QS_FUN_PRE(fun_)
Definition qs_dummy.h:150
#define QS_CRIT_EXIT()
Definition qs_dummy.h:158
#define QS_END_PRE()
Definition qs_dummy.h:141
#define QS_CRIT_ENTRY()
Definition qs_dummy.h:157
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.h:140
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:39
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section)
Definition qsafe.h:49
#define QF_CRIT_EXIT()
Definition qsafe.h:43
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section)
Definition qsafe.h:86
#define QF_CRIT_STAT
Definition qsafe.h:35
Abstract State Machine class (state machine interface)
Definition qp.h:178
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OO)
Definition qp.h:179
union QAsmAttr state
Current state (pointer to the current state-handler function)
Definition qp.h:180
union QAsmAttr temp
Temporary storage for target/act-table etc.
Definition qp.h:181
Virtual table for the QAsm class.
Definition qp.h:221
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:311
QAsm super
Definition qp.h:312
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:434
static void QMsm_exitToTranSource_(QAsm *const me, QMState const *const cs, QMState const *const ts, uint_fast8_t const qsId)
Exit the current state up to the explicit transition source.
Definition qep_msm.c:407
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:158
static QState QMsm_execTatbl_(QAsm *const me, QMTranActTable const *const tatbl, uint_fast8_t const qsId)
Execute transition-action table.
Definition qep_msm.c:357
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:300
QStateHandler QMsm_getStateHandler_(QAsm *const me)
Implementation of getting the state handler in a QMsm subclass.
Definition qep_msm.c:321
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:328
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:199
QMTranActTable const * tatbl
Definition qp.h:168
QStateHandler fun
Definition qp.h:165
struct QMState const * obj
Definition qp.h:169