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