QP/C++  8.0.3
Real-Time Event Framework
Loading...
Searching...
No Matches
qep_msm.cpp
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.hpp" // QP port
31#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
32#ifdef Q_SPY // QS software tracing enabled?
33 #include "qs_port.hpp" // QS port
34 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
35#else
36 #include "qs_dummy.hpp" // disable the QS software tracing
37#endif // Q_SPY
38
39// unnamed namespace for local definitions with internal linkage
40namespace {
41
42Q_DEFINE_THIS_MODULE("qep_msm")
43
44// maximum depth of entry levels in a MSM for tran. to history.
45static constexpr std::int_fast8_t QMSM_MAX_ENTRY_DEPTH_ {4};
46
47//! @cond INTERNAL
48
49// top-state object for QMsm-style state machines
50constexpr QP::QMState l_msm_top_s = {
51 nullptr,
52 nullptr,
53 nullptr,
54 nullptr,
55 nullptr
56};
57
58} // unnamed namespace
59
60#ifdef Q_SPY
61// helper macro to trace state action (entry/exit)
62#define QS_STATE_ACT_(rec_, state_) \
63 QS_CRIT_ENTRY(); \
64 QS_BEGIN_PRE((rec_), qsId) \
65 QS_OBJ_PRE(this); \
66 QS_FUN_PRE(state_); \
67 QS_END_PRE() \
68 QS_CRIT_EXIT()
69
70// internal helper macro to top-most init
71#define QS_TOP_INIT_(rec_, trg_) \
72 QS_CRIT_ENTRY(); \
73 QS_BEGIN_PRE((rec_), qsId) \
74 QS_TIME_PRE(); \
75 QS_OBJ_PRE(this); \
76 QS_FUN_PRE(trg_); \
77 QS_END_PRE() \
78 QS_CRIT_EXIT()
79
80// internal helper macro to trace transition segment
81#define QS_TRAN_SEG_(rec_, src_, trg_) \
82 QS_CRIT_ENTRY(); \
83 QS_BEGIN_PRE((rec_), qsId) \
84 QS_OBJ_PRE(this); \
85 QS_FUN_PRE(src_); \
86 QS_FUN_PRE(trg_); \
87 QS_END_PRE() \
88 QS_CRIT_EXIT()
89
90// internal helper macro to trace transition begin/end
91#define QS_TRAN0_(rec_, trg_) \
92 QS_CRIT_ENTRY(); \
93 QS_BEGIN_PRE((rec_), qsId) \
94 QS_TIME_PRE(); \
95 QS_SIG_PRE(e->sig); \
96 QS_OBJ_PRE(this); \
97 QS_FUN_PRE(trg_); \
98 QS_END_PRE() \
99 QS_CRIT_EXIT()
100
101// internal helper macro to trace regulsr transition
102#define QS_TRAN_END_(rec_, src_, trg_) \
103 QS_CRIT_ENTRY(); \
104 QS_BEGIN_PRE((rec_), qsId) \
105 QS_TIME_PRE(); \
106 QS_SIG_PRE(e->sig); \
107 QS_OBJ_PRE(this); \
108 QS_FUN_PRE(src_); \
109 QS_FUN_PRE(trg_); \
110 QS_END_PRE() \
111 QS_CRIT_EXIT()
112
113#else
114#define QS_STATE_ACT_(rec_, state_) (static_cast<void>(0))
115#define QS_TOP_INIT_(rec_, trg_) (static_cast<void>(0))
116#define QS_TRAN_SEG_(rec_, src_, trg_) (static_cast<void>(0))
117#define QS_TRAN0_(rec_, trg_) (static_cast<void>(0))
118#define QS_TRAN_END_(rec_, src_, trg_) (static_cast<void>(0))
119#endif
120
121//! @endcond
122
123//============================================================================
124namespace QP {
125
126//............................................................................
127QMsm::QMsm(QStateHandler const initial) noexcept
128 : QAsm()
129{
130 m_state.obj = &l_msm_top_s; // the current state (top)
131 m_temp.fun = initial; // the initial tran. handler
132}
133
134//............................................................................
135void QMsm::init(
136 void const * const e,
137 std::uint_fast8_t const qsId)
138{
139#ifndef Q_SPY
140 Q_UNUSED_PAR(qsId);
141#endif
142
146 (m_temp.fun != nullptr)
147 && (m_state.obj == &l_msm_top_s));
148 QF_CRIT_EXIT();
149
150 // execute the top-most initial tran.
151 QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt));
152
154 // the top-most initial tran. must be taken
156 QF_CRIT_EXIT();
157
158 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
159 m_state.obj->stateHandler, m_temp.tatbl->target->stateHandler);
160
161 // set state to the last tran. target
162 m_state.obj = m_temp.tatbl->target;
163
164 // drill down into the state hierarchy with initial transitions...
165 while (r >= Q_RET_TRAN_INIT) {
166 // execute the tran. table
167 r = execTatbl_(m_temp.tatbl, qsId);
168 }
169
170 QS_TOP_INIT_(QS_QEP_INIT_TRAN, m_state.obj->stateHandler);
171}
172
173//............................................................................
174void QMsm::dispatch(
175 QEvt const * const e,
176 std::uint_fast8_t const qsId)
177{
178#ifndef Q_SPY
179 Q_UNUSED_PAR(qsId);
180#endif
181
182 QMState const *s = m_state.obj; // store the current state
183 QMState const *t = s;
184
188 (e != nullptr)
189 && (s != nullptr));
190 QF_CRIT_EXIT();
191
192 QS_TRAN0_(QS_QEP_DISPATCH, s->stateHandler);
193
194 // scan the state hierarchy up to the top state...
196 while (t != nullptr) {
197 r = (*t->stateHandler)(this, e); // call state handler function
198
199 if (r >= Q_RET_HANDLED) { // event handled? (the most frequent case)
200 break; // done scanning the state hierarchy
201 }
202#ifdef Q_SPY
203 if (r == Q_RET_UNHANDLED) { // event unhandled due to a guard?
205 QS_BEGIN_PRE(QS_QEP_UNHANDLED, qsId)
206 QS_SIG_PRE(e->sig);
207 QS_OBJ_PRE(this);
208 QS_FUN_PRE(t->stateHandler);
209 QS_END_PRE()
210 QS_CRIT_EXIT();
211 }
212#endif
213 t = t->superstate; // advance to the superstate
214 }
215
216 if (r >= Q_RET_TRAN) { // any kind of tran. taken?
218 // the tran. source state must not be NULL
219 Q_ASSERT_INCRIT(330, t != nullptr);
220 QF_CRIT_EXIT();
221
222#ifdef Q_SPY
223 QMState const * const ts = t; // tran. source for QS tracing
224#endif // Q_SPY
225
226 if (r == Q_RET_TRAN_HIST) { // was it tran. to history?
227 QMState const * const hist = m_state.obj; // save history
228 m_state.obj = s; // restore the original state
229
230 QS_TRAN_SEG_(QS_QEP_TRAN_HIST,
231 t->stateHandler, hist->stateHandler);
232
233 // save the tran-action table before it gets clobbered
234 QMTranActTable const * const tatbl = m_temp.tatbl;
235 exitToTranSource_(s, t, qsId);
236 static_cast<void>(execTatbl_(tatbl, qsId));
237 r = enterHistory_(hist, qsId);
238 s = m_state.obj;
239 t = s; // set target to the current state
240 }
241
242 while (r >= Q_RET_TRAN) {
243 // save the tran-action table before it gets clobbered
244 QMTranActTable const * const tatbl = m_temp.tatbl;
245 m_temp.obj = nullptr; // clear
246 exitToTranSource_(s, t, qsId);
247 r = execTatbl_(tatbl, qsId);
248 s = m_state.obj;
249 t = s; // set target to the current state
250 }
251
252 QS_TRAN_END_(QS_QEP_TRAN, ts->stateHandler, s->stateHandler);
253 }
254#ifdef Q_SPY
255 else if (r == Q_RET_HANDLED) { // was the event handled?
257 // internal tran. source can't be NULL
258 Q_ASSERT_INCRIT(380, t != nullptr);
259 QF_CRIT_EXIT();
260
261 QS_TRAN0_(QS_QEP_INTERN_TRAN, t->stateHandler);
262 }
263 else if (t == nullptr) { // event bubbled to the 'top' state?
264 QS_TRAN0_(QS_QEP_IGNORED, s->stateHandler);
265 }
266#endif // Q_SPY
267 else {
268 // empty
269 }
270}
271
272//............................................................................
273bool QMsm::isIn(QStateHandler const stateHndl) noexcept {
274 bool inState = false; // assume that this SM is not in 'state'
275
276 QMState const *s = m_state.obj;
277 while (s != nullptr) {
278 if (s->stateHandler == stateHndl) { // match found?
279 inState = true;
280 break;
281 }
282 s = s->superstate; // advance to the superstate
283 }
284
285 return inState;
286}
287
288//............................................................................
289QMState const * QMsm::childStateObj(QMState const * const parentHndl)
290 const noexcept
291{
292 QMState const *s = m_state.obj; // start with current state
293 QMState const *child = s;
294 bool isFound = false; // assume the child NOT found
295
296 while (s != nullptr) {
297 if (s == parentHndl) {
298 isFound = true; // child is found
299 break;
300 }
301 child = s;
302 s = s->superstate;
303 }
306 Q_ASSERT_INCRIT(590, isFound);
307 QF_CRIT_EXIT();
308
309#ifdef Q_UNSAFE
310 Q_UNUSED_PAR(isFound);
311#endif
312
313 return child; // return the child
314}
315
316//............................................................................
318 QMTranActTable const * const tatbl,
319 std::uint_fast8_t const qsId)
320{
321#ifndef Q_SPY
322 Q_UNUSED_PAR(qsId);
323#endif
324
327 // precondition:
328 // - the tran-action table pointer must not be NULL
329 Q_REQUIRE_INCRIT(600, tatbl != nullptr);
330 QF_CRIT_EXIT();
331
332 QState r = Q_RET_NULL;
333 QActionHandler const *a = &tatbl->act[0];
334 while (*a != nullptr) {
335 r = (*(*a))(this); // call the action through the 'a' pointer
336 ++a;
337#ifdef Q_SPY
338 if (r == Q_RET_ENTRY) {
339 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, m_temp.obj->stateHandler);
340 }
341 else if (r == Q_RET_EXIT) {
342 QS_STATE_ACT_(QS_QEP_STATE_EXIT, m_temp.obj->stateHandler);
343 }
344 else if (r == Q_RET_TRAN_INIT) {
345 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
346 tatbl->target->stateHandler,
347 m_temp.tatbl->target->stateHandler);
348 }
349 else {
350 // empty
351 }
352#endif // Q_SPY
353 }
355 Q_ASSERT_INCRIT(690, *a == nullptr);
356 QF_CRIT_EXIT();
357
358 m_state.obj = (r >= Q_RET_TRAN)
359 ? m_temp.tatbl->target
360 : tatbl->target;
361 return r;
362}
363
364//............................................................................
366 QMState const * const cs,
367 QMState const * const ts,
368 std::uint_fast8_t const qsId)
369{
370#ifndef Q_SPY
371 Q_UNUSED_PAR(qsId);
372#endif
374
375 // exit states from the current state to the tran. source state
376 QMState const *s = cs;
377 while (s != ts) {
378 // exit action provided in state 's'?
379 if (s->exitAction != nullptr) {
380 // execute the exit action
381 static_cast<void>((*s->exitAction)(this));
382
383 QS_STATE_ACT_(QS_QEP_STATE_EXIT, m_temp.obj->stateHandler);
384 }
385 s = s->superstate; // advance to the superstate
386 }
387}
388
389//............................................................................
391 QMState const * const hist,
392 std::uint_fast8_t const qsId)
393{
394#ifndef Q_SPY
395 Q_UNUSED_PAR(qsId);
396#endif
397
398 // record the entry path from current state to history
399 QMState const *epath[QMSM_MAX_ENTRY_DEPTH_];
400 QMState const *s = hist;
401 std::int_fast8_t i = 0; // tran. entry path index & fixed upper loop bound
402 while ((s != m_state.obj) && (i < QMSM_MAX_ENTRY_DEPTH_)) {
403 if (s->entryAction != nullptr) {
404 epath[i] = s;
405 ++i;
406 }
407 s = s->superstate;
408 }
411 Q_ASSERT_INCRIT(810, i <= QMSM_MAX_ENTRY_DEPTH_);
412 QF_CRIT_EXIT();
413
414 // retrace the entry path in reverse (desired) order...
415 // NOTE: i the fixed upper loop bound
416 for (i = i - 1; i >= 0; --i) {
417 // run entry action in epath[i]
418 static_cast<void>((*epath[i]->entryAction)(this));
419
420 QS_STATE_ACT_(QS_QEP_STATE_ENTRY, epath[i]->stateHandler);
421 }
422
423 m_state.obj = hist; // set current state to the tran. target
424
425 // initial tran. present?
426 QState r = Q_RET_NULL;
427 if (hist->initAction != nullptr) {
428 r = (*hist->initAction)(this); // execute the tran. action
429
430 QS_TRAN_SEG_(QS_QEP_STATE_INIT,
431 hist->stateHandler, m_temp.tatbl->target->stateHandler);
432 }
433 return r;
434}
435
436//............................................................................
437QMState const * QMsm::topQMState() const noexcept {
438 return &l_msm_top_s;
439}
440
441} // namespace QP
Abstract State Machine class (state machine interface)
Definition qp.hpp:178
static constexpr QState Q_RET_UNHANDLED
Definition qp.hpp:187
static constexpr QState Q_RET_TRAN
Definition qp.hpp:201
static constexpr QState Q_RET_ENTRY
Definition qp.hpp:194
static constexpr QState Q_RET_NULL
Definition qp.hpp:198
QAsmAttr m_temp
Temporary storage for target/act-table etc.
Definition qp.hpp:181
static constexpr QState Q_RET_SUPER
Definition qp.hpp:186
static constexpr QState Q_RET_TRAN_INIT
Definition qp.hpp:202
static constexpr QState Q_RET_TRAN_HIST
Definition qp.hpp:205
static constexpr QState Q_RET_EXIT
Definition qp.hpp:195
QAsmAttr m_state
Current state (pointer to the current state-handler function)
Definition qp.hpp:180
static constexpr QState Q_RET_HANDLED
Definition qp.hpp:190
Event class.
Definition qp.hpp:115
QMState const * childStateObj(QMState const *const parentHndl) const noexcept
QState execTatbl_(QMTranActTable const *const tatbl, std::uint_fast8_t const qsId)
void dispatch(QEvt const *const e, std::uint_fast8_t const qsId) override
Virtual function to dispatch an event to the state machine.
void exitToTranSource_(QMState const *const cs, QMState const *const ts, std::uint_fast8_t const qsId)
QState enterHistory_(QMState const *const hist, std::uint_fast8_t const qsId)
void init(void const *const e, std::uint_fast8_t const qsId) override
Virtual function to take the top-most initial transition in the state machine.
bool isIn(QStateHandler const stateHndl) noexcept override
QMsm(QStateHandler const initial) noexcept
QMState const * topQMState() const noexcept
QP/C++ framework.
Definition qequeue.hpp:36
QState(*)(void *const me) QActionHandler
Pointer to an action-handler function.
Definition qp.hpp:151
std::uint_fast8_t QState
Type returned from state-handler functions.
Definition qp.hpp:146
QState(*)(void *const me, QEvt const *const e) QStateHandler
Pointer to a state-handler function.
Definition qp.hpp:150
#define Q_EVT_CAST(subclass_)
Definition qp.hpp:417
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:94
Sample QP/C++ port.
QS/C++ dummy public interface.
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.hpp:157
#define QS_CRIT_STAT
Definition qs_dummy.hpp:164
#define QS_SIG_PRE(sig_)
Definition qs_dummy.hpp:155
#define QS_FUN_PRE(fun_)
Definition qs_dummy.hpp:158
#define QS_CRIT_EXIT()
Definition qs_dummy.hpp:166
#define QS_END_PRE()
Definition qs_dummy.hpp:149
#define QS_CRIT_ENTRY()
Definition qs_dummy.hpp:165
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.hpp:148
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
State object for the QP::QMsm class (QM State Machine)
Definition qp.hpp:154