qpcpp/qep/source/qhsm_dis.cpp

363 lines
13 KiB
C++
Raw Normal View History

2012-08-14 18:00:48 -04:00
/// \file
2014-04-13 21:35:34 -04:00
/// \brief QP::QHsm::dispatch() and QP::QHsm::hsm_tran() definitions
2012-08-14 18:00:48 -04:00
/// \ingroup qep
2014-04-13 21:35:34 -04:00
/// \cond
///***************************************************************************
/// Product: QEP/C++
/// Last updated for version 5.3.0
/// Last updated on 2014-04-09
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) Quantum Leaps, www.state-machine.com.
///
/// This program is open source software: you can redistribute it and/or
/// modify it under the terms of the GNU General Public License as published
/// by the Free Software Foundation, either version 3 of the License, or
/// (at your option) any later version.
///
/// Alternatively, this program may be distributed and modified under the
/// terms of Quantum Leaps commercial licenses, which expressly supersede
/// the GNU General Public License and are specifically designed for
/// licensees interested in retaining the proprietary status of their code.
///
/// This program is distributed in the hope that it will be useful,
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
/// GNU General Public License for more details.
///
/// You should have received a copy of the GNU General Public License
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
///
/// Contact information:
/// Web: www.state-machine.com
/// Email: info@state-machine.com
///***************************************************************************
/// \endcond
#define QP_IMPL // this is QP implementation
#include "qep_port.h" // QEP port
#include "qep_pkg.h" // QEP internal interface
#ifdef Q_SPY // QS software tracing enabled?
#include "qs_port.h" // include QS port
#else
#include "qs_dummy.h" // disable the QS software tracing
#endif // Q_SPY
2014-04-21 21:48:04 -04:00
#include "qassert.h"
2012-08-14 18:00:48 -04:00
2013-10-10 20:01:51 -04:00
namespace QP {
2012-08-14 18:00:48 -04:00
Q_DEFINE_THIS_MODULE("qhsm_dis")
2014-04-13 21:35:34 -04:00
//****************************************************************************
/// \description
/// Dispatches an event for processing to a hierarchical state machine (HSM).
/// The processing of an event represents one run-to-completion (RTC) step.
///
/// \arguments
/// \arg[in] \c e pointer to the event to be dispatched to the HSM
///
/// \note
/// This state machine must be initialized by calling QP::QHsm::init() exactly
/// __once__ before calling QP::QHsm::dispatch().
///
2012-08-14 18:00:48 -04:00
void QHsm::dispatch(QEvt const * const e) {
2013-10-10 20:01:51 -04:00
QStateHandler t = m_state.fun;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
/// \pre the state configuration must be stable
Q_REQUIRE_ID(100, t == m_temp.fun);
2012-08-14 18:00:48 -04:00
QStateHandler s;
QState r;
QS_CRIT_STAT_
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_DISPATCH, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the current state
2012-08-14 18:00:48 -04:00
QS_END_()
2014-04-13 21:35:34 -04:00
// process the event hierarchically...
do {
2013-10-10 20:01:51 -04:00
s = m_temp.fun;
2014-04-13 21:35:34 -04:00
r = (*s)(this, e); // invoke state handler s
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
// unhandled due to a guard?
if (r == Q_RET_UNHANDLED) {
2012-08-14 18:00:48 -04:00
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_UNHANDLED, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the current state
2012-08-14 18:00:48 -04:00
QS_END_()
2014-04-13 21:35:34 -04:00
r = QEP_TRIG_(s, QEP_EMPTY_SIG_); // find superstate of s
2012-08-14 18:00:48 -04:00
}
} while (r == Q_RET_SUPER);
2014-04-13 21:35:34 -04:00
// transition taken?
if ((r == Q_RET_TRAN) || (r == Q_RET_TRAN_HIST)) {
QStateHandler path[MAX_NEST_DEPTH_];
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
path[0] = m_temp.fun; // save the target of the transition
2012-08-14 18:00:48 -04:00
path[1] = t;
2013-12-30 17:41:15 -05:00
path[2] = s;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
// exit current state to transition source s...
while (t != s) {
// exit handled?
if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED) {
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_STATE_EXIT, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the exited state
2012-08-14 18:00:48 -04:00
QS_END_()
2014-04-13 21:35:34 -04:00
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // find superstate of t
2012-08-14 18:00:48 -04:00
}
2014-04-13 21:35:34 -04:00
t = m_temp.fun; // m_temp.fun holds the superstate
2012-08-14 18:00:48 -04:00
}
2014-04-13 21:35:34 -04:00
int_fast8_t ip = hsm_tran(path); // take the HSM transition
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
// retrace the entry path in reverse (desired) order...
for (; ip >= sf8_0; --ip) {
QEP_ENTER_(path[ip]); // enter path[ip]
2012-08-14 18:00:48 -04:00
}
2014-04-13 21:35:34 -04:00
t = path[0]; // stick the target into register
m_temp.fun = t; // update the next state
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
// drill into the target hierarchy...
2012-08-14 18:00:48 -04:00
while (QEP_TRIG_(t, Q_INIT_SIG) == Q_RET_TRAN) {
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_STATE_INIT, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_OBJ_(this); // this state machine object
QS_FUN_(t); // the source (pseudo)state
QS_FUN_(m_temp.fun); // the target of the transition
2012-08-14 18:00:48 -04:00
QS_END_()
2014-04-13 21:35:34 -04:00
ip = sf8_0;
2013-10-10 20:01:51 -04:00
path[0] = m_temp.fun;
2014-04-13 21:35:34 -04:00
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_); // find superstate
2013-10-10 20:01:51 -04:00
while (m_temp.fun != t) {
2012-08-14 18:00:48 -04:00
++ip;
2013-10-10 20:01:51 -04:00
path[ip] = m_temp.fun;
(void)QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);// find superstate
2012-08-14 18:00:48 -04:00
}
2013-10-10 20:01:51 -04:00
m_temp.fun = path[0];
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
// entry path must not overflow
Q_ASSERT(ip < MAX_NEST_DEPTH_);
// retrace the entry path in reverse (correct) order...
do {
QEP_ENTER_(path[ip]); // enter path[ip]
2012-08-14 18:00:48 -04:00
--ip;
2014-04-13 21:35:34 -04:00
} while (ip >= sf8_0);
2012-08-14 18:00:48 -04:00
t = path[0];
}
2014-04-13 21:35:34 -04:00
#ifdef Q_SPY
if (r == Q_RET_TRAN) {
QS_BEGIN_(QS_QEP_TRAN, QS::priv_.smObjFilter, this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the source of the transition
QS_FUN_(t); // the new active state
QS_END_()
}
else {
QS_BEGIN_(QS_QEP_TRAN_HIST, QS::priv_.smObjFilter, this)
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(s); // the source of the transition
QS_FUN_(t); // the new active state
QS_END_()
}
#endif // Q_SPY
2012-08-14 18:00:48 -04:00
}
2014-04-13 21:35:34 -04:00
// transition not taken
else {
2012-08-14 18:00:48 -04:00
#ifdef Q_SPY
if (r == Q_RET_HANDLED) {
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_INTERN_TRAN, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(m_state.fun);// the state that handled the event
2012-08-14 18:00:48 -04:00
QS_END_()
}
else {
2013-10-10 20:01:51 -04:00
QS_BEGIN_(QS_QEP_IGNORED, QS::priv_.smObjFilter, this)
2014-04-13 21:35:34 -04:00
QS_TIME_(); // time stamp
QS_SIG_(e->sig); // the signal of the event
QS_OBJ_(this); // this state machine object
QS_FUN_(m_state.fun);// the current state
2012-08-14 18:00:48 -04:00
QS_END_()
}
#endif
}
2014-04-13 21:35:34 -04:00
m_state.fun = t; // change the current active state
m_temp.fun = t; // mark the configuration as stable
2012-08-14 18:00:48 -04:00
}
2014-04-13 21:35:34 -04:00
//****************************************************************************
/// \description
/// helper function to execute transition sequence in a hierarchical state
/// machine (HSM).
///
/// \arguments
/// \arg[in,out] \c path array of pointers to state-handler functions
/// to execute the entry actions
///
/// \returns the depth of the entry path stored in the \c path argument.
////
int_fast8_t QHsm::hsm_tran(QStateHandler (&path)[MAX_NEST_DEPTH_]) {
int_fast8_t ip = sf8_n1; // transition entry path index
int_fast8_t iq; // helper transition entry path index
2013-12-30 17:41:15 -05:00
QStateHandler t = path[0];
QStateHandler s = path[2];
QState r;
QS_CRIT_STAT_
2014-04-13 21:35:34 -04:00
// (a) check source==target (transition to self)
if (s == t) {
QEP_EXIT_(s); // exit the source
ip = sf8_0; // cause entering the target
2013-12-30 17:41:15 -05:00
}
else {
2014-04-13 21:35:34 -04:00
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_); // superstate of target
2013-12-30 17:41:15 -05:00
t = m_temp.fun;
2014-04-13 21:35:34 -04:00
// (b) check source==target->super
if (s == t) {
ip = sf8_0; // cause entering the target
2013-12-30 17:41:15 -05:00
}
else {
2014-04-13 21:35:34 -04:00
(void)QEP_TRIG_(s, QEP_EMPTY_SIG_); // superstate of src
// (c) check source->super==target->super
2013-12-30 17:41:15 -05:00
if (m_temp.fun == t) {
2014-04-13 21:35:34 -04:00
QEP_EXIT_(s); // exit the source
ip = sf8_0; // cause entering the target
2013-12-30 17:41:15 -05:00
}
else {
2014-04-13 21:35:34 -04:00
// (d) check source->super==target
2013-12-30 17:41:15 -05:00
if (m_temp.fun == path[0]) {
2014-04-13 21:35:34 -04:00
QEP_EXIT_(s); // exit the source
2013-12-30 17:41:15 -05:00
}
else { // (e) check rest of source==target->super->super..
// and store the entry path along the way
//
2014-04-13 21:35:34 -04:00
iq = sf8_0; // indicate LCA not found
ip = sf8_1; // cause entering target's superst
path[1] = t; // save the superstate of target
t = m_temp.fun; // save source->super
// find target->super->super
2013-12-30 17:41:15 -05:00
r = QEP_TRIG_(path[1], QEP_EMPTY_SIG_);
while (r == Q_RET_SUPER) {
++ip;
2014-04-13 21:35:34 -04:00
path[ip] = m_temp.fun; // store the entry path
// is it the source?
if (m_temp.fun == s) {
iq = sf8_1; // indicate that LCA found
// entry path must not overflow
Q_ASSERT(ip < MAX_NEST_DEPTH_);
--ip; // do not enter the source
r = Q_RET_HANDLED; // terminate the loop
2013-12-30 17:41:15 -05:00
}
2014-04-13 21:35:34 -04:00
// it is not the source, keep going up
else {
2013-12-30 17:41:15 -05:00
r = QEP_TRIG_(m_temp.fun, QEP_EMPTY_SIG_);
}
}
2014-04-13 21:35:34 -04:00
// LCA found yet?
if (iq == sf8_0) {
// entry path must not overflow
Q_ASSERT(ip < MAX_NEST_DEPTH_);
QEP_EXIT_(s); // exit the source
2013-12-30 17:41:15 -05:00
// (f) check the rest of source->super
// == target->super->super...
//
iq = ip;
2014-04-13 21:35:34 -04:00
r = Q_RET_IGNORED; // indicate LCA NOT found
2013-12-30 17:41:15 -05:00
do {
2014-04-13 21:35:34 -04:00
// is this the LCA?
if (t == path[iq]) {
r = Q_RET_HANDLED; // indicate LCA found
// do not enter LCA
ip = static_cast<int_fast8_t>(iq - sf8_1);
iq = sf8_n1; // cause termination of the loop
2013-12-30 17:41:15 -05:00
}
else {
2014-04-13 21:35:34 -04:00
--iq; // try lower superstate of target
2013-12-30 17:41:15 -05:00
}
2014-04-13 21:35:34 -04:00
} while (iq >= sf8_0);
2013-12-30 17:41:15 -05:00
2014-04-13 21:35:34 -04:00
// LCA not found yet?
if (r != Q_RET_HANDLED) {
2013-12-30 17:41:15 -05:00
// (g) check each source->super->...
// for each target->super...
//
2014-04-13 21:35:34 -04:00
r = Q_RET_IGNORED; // keep looping
2013-12-30 17:41:15 -05:00
do {
2014-04-13 21:35:34 -04:00
// exit t unhandled?
2013-12-30 17:41:15 -05:00
if (QEP_TRIG_(t, Q_EXIT_SIG) == Q_RET_HANDLED)
{
QS_BEGIN_(QS_QEP_STATE_EXIT,
QS::priv_.smObjFilter, this)
QS_OBJ_(this);
QS_FUN_(t);
QS_END_()
(void)QEP_TRIG_(t, QEP_EMPTY_SIG_);
}
2014-04-13 21:35:34 -04:00
t = m_temp.fun; // set to super of t
2013-12-30 17:41:15 -05:00
iq = ip;
do {
2014-04-13 21:35:34 -04:00
// is this LCA?
if (t == path[iq]) {
// do not enter LCA
ip =
static_cast<int_fast8_t>(iq - sf8_1);
iq = sf8_n1;// break out of inner loop
r = Q_RET_HANDLED; // break outer loop
2013-12-30 17:41:15 -05:00
}
else {
--iq;
}
2014-04-13 21:35:34 -04:00
} while (iq >= sf8_0);
2013-12-30 17:41:15 -05:00
} while (r != Q_RET_HANDLED);
}
}
}
}
}
}
return ip;
}
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
} // namespace QP
2013-10-10 20:01:51 -04:00