QP/C++  7.0.1
Real-Time Embedded Framework
qep_msm.cpp
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//============================================================================
30
31#define QP_IMPL // this is QP implementation
32#include "qep_port.hpp" // QEP port
33#ifdef Q_SPY // QS software tracing enabled?
34 #include "qs_port.hpp" // QS port
35 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
36#else
37 #include "qs_dummy.hpp" // disable the QS software tracing
38#endif // Q_SPY
39#include "qassert.h" // QP embedded systems-friendly assertions
40
41// unnamed namespace for local definitions with internal linkage
42namespace {
43
44Q_DEFINE_THIS_MODULE("qep_msm")
45
46} // unnamed namespace
47
48namespace QP {
49
50//============================================================================
51QMState const QMsm::msm_top_s = {
52 nullptr,
53 nullptr,
54 nullptr,
55 nullptr,
56 nullptr
57};
58
59//============================================================================
74QMsm::QMsm(QStateHandler const initial) noexcept
75 : QHsm(initial)
76{
77 m_state.obj = &msm_top_s;
78 m_temp.fun = initial;
79}
80
81//============================================================================
92void QMsm::init(void const * const e, std::uint_fast8_t const qs_id) {
94
97 Q_REQUIRE_ID(200, (m_temp.fun != nullptr)
98 && (m_state.obj == &msm_top_s));
99
100 // execute the top-most initial tran.
101 QState r = (*m_temp.fun)(this, Q_EVT_CAST(QEvt));
102
103 // initial tran. must be taken
104 Q_ASSERT_ID(210, r == Q_RET_TRAN_INIT);
105
107 QS_OBJ_PRE_(this); // this state machine object
108 QS_FUN_PRE_(m_state.obj->stateHandler); // source handler
109 QS_FUN_PRE_(m_temp.tatbl->target->stateHandler); // target handler
111
112 // set state to the last tran. target
114
115 // drill down into the state hierarchy with initial transitions...
116 do {
117 r = execTatbl_(m_temp.tatbl, qs_id); // execute the tran-action table
118 } while (r >= Q_RET_TRAN_INIT);
119
121 QS_TIME_PRE_(); // time stamp
122 QS_OBJ_PRE_(this); // this state machine object
123 QS_FUN_PRE_(m_state.obj->stateHandler); // the new current state
125
126 static_cast<void>(qs_id); // unused parameter (if Q_SPY not defined)
127}
128
129//============================================================================
139void QMsm::init(std::uint_fast8_t const qs_id) {
140 QMsm::init(nullptr, qs_id);
141}
142
143//============================================================================
154void QMsm::dispatch(QEvt const * const e, std::uint_fast8_t const qs_id) {
155 QMState const *s = m_state.obj; // store the current state
156 QMState const *t = s;
157 QState r;
159
161 Q_REQUIRE_ID(300, s != nullptr);
162
164 QS_TIME_PRE_(); // time stamp
165 QS_SIG_PRE_(e->sig); // the signal of the event
166 QS_OBJ_PRE_(this); // this state machine object
167 QS_FUN_PRE_(s->stateHandler); // the current state handler
169
170 // scan the state hierarchy up to the top state...
171 do {
172 r = (*t->stateHandler)(this, e); // call state handler function
173
174 // event handled? (the most frequent case)
175 if (r >= Q_RET_HANDLED) {
176 break; // done scanning the state hierarchy
177 }
178 // event unhandled and passed to the superstate?
179 else if (r == Q_RET_SUPER) {
180 t = t->superstate; // advance to the superstate
181 }
182 // event unhandled and passed to a submachine superstate?
183 else if (r == Q_RET_SUPER_SUB) {
184 t = m_temp.obj; // current host state of the submachie
185 }
186 // event unhandled due to a guard?
187 else if (r == Q_RET_UNHANDLED) {
188
190 QS_SIG_PRE_(e->sig); // the signal of the event
191 QS_OBJ_PRE_(this); // this state machine object
192 QS_FUN_PRE_(t->stateHandler); // the current state
194
195 t = t->superstate; // advance to the superstate
196 }
197 else {
198 // no other return value should be produced
199 Q_ERROR_ID(310);
200 }
201 } while (t != nullptr);
202
203 // any kind of transition taken?
204 if (r >= Q_RET_TRAN) {
205#ifdef Q_SPY
206 QMState const * const ts = t; // transition source for QS tracing
207
208 // the transition source state must not be nullptr
209 Q_ASSERT_ID(320, ts != nullptr);
210#endif // Q_SPY
211
212 do {
213 // save the transition-action table before it gets clobbered
214 QMTranActTable const * const tatbl = m_temp.tatbl;
215 QHsmAttr tmp; // temporary to save intermediate values
216
217 // was TRAN, TRAN_INIT, or TRAN_EP taken?
218 if (r <= Q_RET_TRAN_EP) {
219 exitToTranSource_(s, t, qs_id);
220 r = execTatbl_(tatbl, qs_id);
221 s = m_state.obj;
222 }
223 // was a transition segment to history taken?
224 else if (r == Q_RET_TRAN_HIST) {
225 tmp.obj = m_state.obj; // save history
226 m_state.obj = s; // restore the original state
227 exitToTranSource_(s, t, qs_id);
228 static_cast<void>(execTatbl_(tatbl, qs_id));
229 r = enterHistory_(tmp.obj, qs_id);
230 s = m_state.obj;
231 }
232 // was a transition segment to an exit point taken?
233 else if (r == Q_RET_TRAN_XP) {
234 tmp.act = m_state.act; // save XP action
235 m_state.obj = s; // restore the original state
236 r = (*tmp.act)(this); // execute the XP action
237 if (r == Q_RET_TRAN) { // XP -> TRAN ?
238#ifdef Q_SPY
239 tmp.tatbl = m_temp.tatbl; // save m_temp
240#endif // Q_SPY
241 exitToTranSource_(s, t, qs_id);
242 // take the tran-to-XP segment inside submachine
243 static_cast<void>(execTatbl_(tatbl, qs_id));
244 s = m_state.obj;
245#ifdef Q_SPY
246 m_temp.tatbl = tmp.tatbl; // restore m_temp
247#endif // Q_SPY
248 }
249 else if (r == Q_RET_TRAN_HIST) { // XP -> HIST ?
250 tmp.obj = m_state.obj; // save the history
251 m_state.obj = s; // restore the original state
252#ifdef Q_SPY
253 s = m_temp.obj; // save m_temp
254#endif // Q_SPY
255 exitToTranSource_(m_state.obj, t, qs_id);
256 // take the tran-to-XP segment inside submachine
257 static_cast<void>(execTatbl_(tatbl, qs_id));
258#ifdef Q_SPY
259 m_temp.obj = s; // restore me->temp
260#endif // Q_SPY
261 s = m_state.obj;
262 m_state.obj = tmp.obj; // restore the history
263 }
264 else {
265 // TRAN_XP must NOT be followed by any other tran type
266 Q_ASSERT_ID(330, r < Q_RET_TRAN);
267 }
268 }
269 else {
270 // no other return value should be produced
271 Q_ERROR_ID(340);
272 }
273
274 t = s; // set target to the current state
275
276 } while (r >= Q_RET_TRAN);
277
279 QS_TIME_PRE_(); // time stamp
280 QS_SIG_PRE_(e->sig); // the signal of the event
281 QS_OBJ_PRE_(this); // this state machine object
282 QS_FUN_PRE_(ts->stateHandler); // the transition source
283 QS_FUN_PRE_(s->stateHandler); // the new active state
285 }
286
287#ifdef Q_SPY
288 // was the event handled?
289 else if (r == Q_RET_HANDLED) {
290 // internal tran. source can't be nullptr
291 Q_ASSERT_ID(340, t != nullptr);
292
294 QS_TIME_PRE_(); // time stamp
295 QS_SIG_PRE_(e->sig); // the signal of the event
296 QS_OBJ_PRE_(this); // this state machine object
297 QS_FUN_PRE_(t->stateHandler); // the source state
299
300 }
301 // event bubbled to the 'top' state?
302 else if (t == nullptr) {
303
305 QS_TIME_PRE_(); // time stamp
306 QS_SIG_PRE_(e->sig); // the signal of the event
307 QS_OBJ_PRE_(this); // this state machine object
308 QS_FUN_PRE_(s->stateHandler); // the current state
310
311 }
312#endif // Q_SPY
313
314 else {
315 // empty
316 }
317
318 static_cast<void>(qs_id); // unused parameter (if Q_SPY not defined)
319}
320
321//============================================================================
322#ifdef Q_SPY
327 return m_state.obj->stateHandler;
328 }
329#endif
330
331//============================================================================
346 std::uint_fast8_t const qs_id)
347{
348 ;
349 QState r = Q_RET_NULL;
351
353 Q_REQUIRE_ID(400, tatbl != nullptr);
354
355 for (QActionHandler const *a = &tatbl->act[0]; *a != nullptr; ++a) {
356 r = (*(*a))(this); // call the action through the 'a' pointer
357#ifdef Q_SPY
358 if (r == Q_RET_ENTRY) {
359
361 QS_OBJ_PRE_(this); // this state machine object
362 QS_FUN_PRE_(m_temp.obj->stateHandler); // entered state handler
364 }
365 else if (r == Q_RET_EXIT) {
366
368 QS_OBJ_PRE_(this); // this state machine object
369 QS_FUN_PRE_(m_temp.obj->stateHandler); // exited state handler
371 }
372 else if (r == Q_RET_TRAN_INIT) {
373
375 QS_OBJ_PRE_(this); // this state machine object
376 QS_FUN_PRE_(tatbl->target->stateHandler); // source
379 }
380 else if (r == Q_RET_TRAN_EP) {
381
383 QS_OBJ_PRE_(this); // this state machine object
384 QS_FUN_PRE_(tatbl->target->stateHandler); // source
387 }
388 else if (r == Q_RET_TRAN_XP) {
389
391 QS_OBJ_PRE_(this); // this state machine object
392 QS_FUN_PRE_(tatbl->target->stateHandler); // source
395 }
396 else {
397 // empty
398 }
399#endif // Q_SPY
400 }
401
402 static_cast<void>(qs_id); // unused parameter (if Q_SPY not defined)
403
404 m_state.obj = (r >= Q_RET_TRAN)
406 : tatbl->target;
407 return r;
408}
409
410//============================================================================
421 QMState const * const ts,
422 std::uint_fast8_t const qs_id)
423{
424 // exit states from the current state to the tran. source state
425 while (s != ts) {
426 // exit action provided in state 's'?
427 if (s->exitAction != nullptr) {
428 // execute the exit action
429 static_cast<void>((*s->exitAction)(this));
430
433 QS_OBJ_PRE_(this); // this state machine object
434 QS_FUN_PRE_(s->stateHandler); // the exited state handler
436 }
437
438 s = s->superstate; // advance to the superstate
439
440 // reached the top of a submachine?
441 if (s == nullptr) {
442 s = m_temp.obj; // the superstate from QM_SM_EXIT()
443 Q_ASSERT_ID(510, s != nullptr);
444 }
445 }
446 static_cast<void>(qs_id); // unused parameter (if Q_SPY not defined)
447}
448
449//============================================================================
462 std::uint_fast8_t const qs_id)
463{
464 QMState const *s = hist;
465 QMState const *ts = m_state.obj; // transition source
466 QMState const *epath[MAX_ENTRY_DEPTH_];
467
469
471 QS_OBJ_PRE_(this); // this state machine object
472 QS_FUN_PRE_(ts->stateHandler); // source state handler
473 QS_FUN_PRE_(hist->stateHandler); // target state handler
475
476 std::int_fast8_t i = 0; // entry path index
477 while (s != ts) {
478 if (s->entryAction != nullptr) {
480 epath[i] = s;
481 ++i;
482 }
483 s = s->superstate;
484 if (s == nullptr) {
485 ts = s; // force exit from the for-loop
486 }
487 }
488
489 // retrace the entry path in reverse (desired) order...
490 while (i > 0) {
491 --i;
492 // run entry action in epath[i]
493 static_cast<void>((*epath[i]->entryAction)(this));
494
496 QS_OBJ_PRE_(this);
497 QS_FUN_PRE_(epath[i]->stateHandler); // entered state handler
499 }
500
501 m_state.obj = hist; // set current state to the transition target
502
503 // initial tran. present?
504 QState r;
505 if (hist->initAction != nullptr) {
506 r = (*hist->initAction)(this); // execute the transition action
507 }
508 else {
509 r = Q_RET_NULL;
510 }
511
512 static_cast<void>(qs_id); // unused parameter (if Q_SPY not defined)
513 return r;
514}
515
516//============================================================================
529bool QMsm::isInState(QMState const * const st) const noexcept {
530 bool inState = false; // assume that this MSM is not in 'state'
531
532 for (QMState const *s = m_state.obj;
533 s != nullptr;
534 s = s->superstate)
535 {
536 if (s == st) {
537 inState = true; // match found, return 'true'
538 break;
539 }
540 }
541 return inState;
542}
543
544//============================================================================
559QMState const *QMsm::childStateObj(QMState const * const parent)
560 const noexcept
561{
562 QMState const *child = m_state.obj;
563 bool isFound = false; // start with the child not found
564
565 for (QMState const *s = m_state.obj;
566 s != nullptr;
567 s = s->superstate)
568 {
569 if (s == parent) {
570 isFound = true; // child is found
571 break;
572 }
573 else {
574 child = s;
575 }
576 }
577
579 Q_ENSURE_ID(810, isFound);
580#ifdef Q_NASSERT
581 // avoid compiler warning about unused variable
582 static_cast<void>(isFound);
583#endif
584
585 return child; // return the child
586}
587
588} // namespace QP
Hierarchical State Machine base class.
Definition: qep.hpp:249
static constexpr QState Q_RET_SUPER_SUB
event passed to submachine superstate
Definition: qep.hpp:297
static constexpr QState Q_RET_HANDLED
event handled (internal transition)
Definition: qep.hpp:303
friend class QMsm
Definition: qep.hpp:449
static constexpr QState Q_RET_SUPER
event passed to the superstate to handle
Definition: qep.hpp:294
static constexpr QState Q_RET_UNHANDLED
event unhandled due to a guard evaluating to 'false'
Definition: qep.hpp:300
static constexpr QState Q_RET_TRAN_EP
entry-point transition into a submachine
Definition: qep.hpp:324
static constexpr QState Q_RET_TRAN_INIT
initial transition taken
Definition: qep.hpp:321
static constexpr QState Q_RET_NULL
return value without any effect
Definition: qep.hpp:315
static constexpr QState Q_RET_TRAN
regular transition taken
Definition: qep.hpp:318
QHsmAttr m_state
current active state (state-variable)
Definition: qep.hpp:250
static constexpr QState Q_RET_EXIT
state exit action executed
Definition: qep.hpp:312
static constexpr QState Q_RET_TRAN_HIST
transition to history of a given state
Definition: qep.hpp:327
QHsmAttr m_temp
temporary: transition chain, target state, etc.
Definition: qep.hpp:251
static constexpr QState Q_RET_TRAN_XP
exit-point transition out of a submachine
Definition: qep.hpp:330
static constexpr QState Q_RET_ENTRY
state entry action executed
Definition: qep.hpp:309
void exitToTranSource_(QMState const *s, QMState const *const ts, std::uint_fast8_t const qs_id)
Internal helper function to exit current state to transition source.
Definition: qep_msm.cpp:420
static QMState const msm_top_s
the top state object for the QMsm
Definition: qep.hpp:550
QStateHandler getStateHandler() noexcept override
Get the current state handler of the QMsm.
Definition: qep_msm.cpp:326
QState execTatbl_(QMTranActTable const *const tatbl, std::uint_fast8_t const qs_id)
Internal helper function to execute a transition-action table.
Definition: qep_msm.cpp:345
void dispatch(QEvt const *const e, std::uint_fast8_t const qs_id) override
Dispatches an event to a HSM.
Definition: qep_msm.cpp:154
static constexpr std::int_fast8_t MAX_ENTRY_DEPTH_
maximum depth of implemented entry levels for transitions to history
Definition: qep.hpp:547
QMState const * childStateObj(QMState const *const parent) const noexcept
Obtain the current active child state of a given parent (read only)
Definition: qep_msm.cpp:559
QState enterHistory_(QMState const *const hist, std::uint_fast8_t const qs_id)
Internal helper function to enter state history.
Definition: qep_msm.cpp:461
void init(void const *const e, std::uint_fast8_t const qs_id) override
Performs the second step of SM initialization by triggering the top-most initial transition.
Definition: qep_msm.cpp:92
bool isInState(QMState const *const st) const noexcept
Tests if a given state is part of the active state configuration.
Definition: qep_msm.cpp:529
namespace associated with the QP/C++ framework
Definition: exa_native.dox:1
std::uint_fast8_t QState
Type returned from state-handler functions.
Definition: qep.hpp:205
@ QS_QEP_STATE_INIT
an initial transition was taken in a state
Definition: qs.hpp:61
@ QS_QEP_TRAN_HIST
a tran to history was taken
Definition: qs.hpp:137
@ QS_QEP_TRAN_EP
a tran to entry point into a submachine
Definition: qs.hpp:138
@ QS_QEP_STATE_EXIT
a state was exited
Definition: qs.hpp:60
@ QS_QEP_INIT_TRAN
the top-most initial transition was taken
Definition: qs.hpp:62
@ QS_QEP_INTERN_TRAN
an internal transition was taken
Definition: qs.hpp:63
@ QS_QEP_TRAN_XP
a tran to exit point out of a submachine
Definition: qs.hpp:139
@ QS_QEP_STATE_ENTRY
a state was entered
Definition: qs.hpp:59
@ QS_QEP_UNHANDLED
an event was unhandled due to a guard
Definition: qs.hpp:67
@ QS_QEP_TRAN
a regular transition was taken
Definition: qs.hpp:64
@ QS_QEP_DISPATCH
an event was dispatched (begin of RTC step)
Definition: qs.hpp:66
@ QS_QEP_IGNORED
an event was ignored (silently discarded)
Definition: qs.hpp:65
QActionHandler const act[1]
Definition: qep.hpp:576
QMState const * obj
pointer to QMState object
Definition: qep.hpp:224
QMTranActTable const * tatbl
transition-action table
Definition: qep.hpp:225
QActionHandler const entryAction
entry action handler function
Definition: qep.hpp:568
QMState const * superstate
superstate of this state
Definition: qep.hpp:566
QState(*)(void *const me) QActionHandler
Pointer to an action-handler function.
Definition: qep.hpp:211
QMState const * target
Definition: qep.hpp:575
QActionHandler const initAction
init action handler function
Definition: qep.hpp:570
QState(*)(void *const me, QEvt const *const e) QStateHandler
Pointer to state-handler function.
Definition: qep.hpp:208
QActionHandler const exitAction
exit action handler function
Definition: qep.hpp:569
QStateHandler const stateHandler
state handler function
Definition: qep.hpp:567
QStateHandler fun
pointer to a state handler function
Definition: qep.hpp:221
QActionHandler act
pointer to an action-handler function
Definition: qep.hpp:222
State object for the QP::QMsm class (QM State Machine).
Definition: qep.hpp:565
Transition-Action Table for the QP::QMsm State Machine.
Definition: qep.hpp:574
Attribute of for the QHsm class (Hierarchical State Machine).
Definition: qep.hpp:220
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
#define Q_EVT_CAST(class_)
Perform downcast of an event onto a subclass of QEvt class_.
Definition: qep.hpp:93
#define QS_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qs.hpp:746
#define QS_TIME_PRE_()
Definition: qs.hpp:225
Internal (package scope) QS/C++ interface.
#define QS_BEGIN_PRE_(rec_, qs_id_)
Internal QS macro to begin a predefined QS record with critical section.
Definition: qs_pkg.hpp:109
#define QS_OBJ_PRE_(obj_)
Internal QS macro to output object pointer data element.
Definition: qs_pkg.hpp:178
#define QS_FUN_PRE_(fun_)
Internal QS macro to output an unformatted function pointer data element.
Definition: qs_pkg.hpp:199
#define QS_END_PRE_()
Internal QS macro to end a predefined QS record with critical section.
Definition: qs_pkg.hpp:120
QEvt base class.
Definition: qep.hpp:191
QSignal sig
signal of the event instance
Definition: qep.hpp:192