The State Dynamics Viewpoint explains how QP/C++ Application shall implement hierarchical state machines. This design viewpoint is most important and relevant to QP/C++ Application developers, who's primary activity is designing and ultimately implementing the hierarchical state machines of Active Objects.
This viewpoint consists of two parts:
The design concerns in this viewpoint correspond to the "recipes" for implementing various state machine elements. The state diagram and the table below list the elements and provides links to the design views in the "QHsm-based implementation" and "QMsm-based implementation".
The state machine elements labeled in Figure SDS-SM are as follows:
Label | Design concern | QHsm-based implementation | QMsm-based implementation |
---|---|---|---|
declaring application-level state machine subclass | SDS_QA_QHsm_decl | SDS_QA_QMsm_decl | |
[1] | implementing top-most initial pseudo-state | SDS_QA_QHsm_top_init | SDS_QA_QMsm_top_init |
[2] | implementing states (including states nested in other states) | SDS_QA_QHsm_state | SDS_QA_QMsm_state |
[3] | implementing state entry actions | SDS_QA_QHsm_entry | SDS_QA_QMsm_entry |
[4] | implementing state exit actions | SDS_QA_QHsm_exit | SDS_QA_QMsm_exit |
[5] | implementing nested initial transitions | SDS_QA_QHsm_nest_init | SDS_QA_QMsm_nest_init |
[6] | implementing state transitions | SDS_QA_QHsm_tran | SDS_QA_QMsm_tran |
[7] | implementing internal transitions | SDS_QA_QHsm_intern | SDS_QA_QMsm_intern |
[8] | implementing choice points and guard conditions | SDS_QA_QHsm_choice | SDS_QA_QMsm_choice |
[9] | Guard condition with internal transition | SDS_QA_QHsm_choice | SDS_QA_QMsm_choice |
[10] | Guard condition with regular transition | SDS_QA_QHsm_choice | SDS_QA_QMsm_choice |
[11] | implementing state history | SDS_QA_QHsm_hist | SDS_QA_QMsm_hist |
[12] | implementing transition to state history | SDS_QA_QHsm_hist_tran | SDS_QA_QMsm_hist_tran |
SDS_QA_...
(with the component part set to QA
rather than QP
).The model kind used to illustrate the State Dynamics Viewpoint is the UML state diagram shown in Figure SDS-SM and Figure SDS-SM (static views). These state diagrams depict hierarchical state machines (for a toaster oven) with labeled elements, which are subsequently elaborated in various traceable Design Views.
This section presents the hierarchical state machine implementation strategy "optimized for manual coding", which means that changing a single element in the state machine design (e.g., nesting of the state hierarchy) should require changing only a single matching element in the implementation. This strategy imposes restrictions on the implementation, but does not mean that the code must be written manually. In the presence of a modeling tool, such code can also be generated automatically and most of the code presented in this section has indeed been generated by the QM modeling tool↑ (from the state diagram presented in Figure SDS-SM).
SDS_QA_QHsm_decl : Declaring QP::QHsm subclass. |
---|
Description The first step in coding a hierarchical state machine based on the QP QHsm implementation is to declare a subclass of the QP QHsm or the QP QActive base class. |
Example [1] class ToasterOven
[2] : public QP::QHsm {
private:
[3] std::uint32_t m_settings;
std::uint16_t m_temp;
QP::QTimeEvt m_te;
protected:
[4] QP::QStateHandler hist_doorClosed;
public:
[10] ToasterOven()
[11] : QP::QHsm(Q_STATE_CAST(&initial)),
[12] te(this, TIMEOUT_SIG, 0U)
{}
protected:
[20] Q_STATE_DECL(initial);
[30] Q_STATE_DECL(doorClosed);
Q_STATE_DECL(heating);
Q_STATE_DECL(toasting);
Q_STATE_DECL(baking);
Q_STATE_DECL(off);
Q_STATE_DECL(doorOpen);
private:
[40] void actionA();
void actionB();
}; // class ToasterOven
Hierarchical State Machine class (QHsm-style state machine implementation strategy) Definition qp.hpp:361 QState(*)(void *const me, QEvt const *const e) QStateHandler Pointer to a state-handler function. Definition qp.hpp:168
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_top_init : Implementing the top-most initial pseudo-state. |
---|
Description The top-most initial pseudostate is a member function of the sate machine class (see Figure SDS-SM [1]). This pseudostate defines the top-most initial transition, which will be taken when the state machine is initialized (see SDS_QP_QAsm interface init() function). |
Example [1] Q_STATE_DEF(ToasterOven, initial) {
[2] m_setting = DEFAULT_SETTINGS;
m_temp = BSP::temp();
// state history attributes
[3] hist_doorClosed = &off;
[4] QS_FUN_DICTIONARY(&ToasterOven::doorClosed);
QS_FUN_DICTIONARY(&ToasterOven::heating);
QS_FUN_DICTIONARY(&ToasterOven::toasting);
QS_FUN_DICTIONARY(&ToasterOven::baking);
QS_FUN_DICTIONARY(&ToasterOven::off);
QS_FUN_DICTIONARY(&ToasterOven::doorOpen);
[5] return tran(&doorClosed);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_state : Implementing a state and its nesting. |
---|
Description States are member functions of the sate machine class (see Figure SDS-SM [2]). State-handler functions are called during the process of dispatching events. Their purpose is to perform your specific actions and then to tell the %QP event processor what your state-handler has just done. For example, if your state-handler performs a state transition, it executes some actions and then it calls the special Q_TRAN() macro, where it specifies the target state of this state transition. The state-handler then returns the status from the Q_TRAN() macro, and through this return value it informs the dispatch operation what needs to be done with the state machine. Based on this information, the event-processor might decide to call this or other state-handler functions to process the same current event. |
Example
// top-level state
[1] Q_STATE_DEF(ToasterOven, doorClosed) {
[2] QP::QState status_;
[3] switch (e->sig) {
[4] . . .
[5] default: {
[6a] status_ = super(&top);
break;
}
}
[7] return status_;
}
// state explicitly nested in another state
[1] Q_STATE_DEF(ToasterOven, heating) {
[2] QP::QState status_;
[3] switch (e->sig) {
[4] . . .
[5] default: {
[6b] status_ = super(&doorClosed);
break;
}
}
[7] return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_entry : Implementing state entry action. |
---|
Description State entry action (see Figure SDS-SM [3]) is coded as special case for the reserved signal QP Q_ENTRY_SIG. The QP event processor executes the entry action during a state transition processing by calling the state-handler function passing in the reserved event with the QP Q_ENTRY_SIG. Please note that the entry action has no access to the original event that triggered the state transition. |
Example Q_STATE_DEF(ToasterOven, heating) {
QP::QState status_;
switch (e->sig) {
[1] case Q_ENTRY_SIG: {
[2] m_te.armX(100, 0U);
[3] status_ = Q_RET_HANDLED;
break;
}
. . .
}
return status_;
}
void armX(std::uint32_t const nTicks, std::uint32_t const interval=0U) noexcept Definition qf_time.cpp:98
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_exit : Implementing state exit action. |
---|
Description State exit action (see Figure SDS-SM [4]) is coded as special case on the reserved signal QP Q_EXIT_SIG. The QP event processor executes the exit action during a state transition processing by calling the state-handler function passing in the reserved event with the QP Q_EXIT_SIG. Please note that the entry action has no access to the original event that triggered the state transition. |
Example Q_STATE_DEF(ToasterOven, heating) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case Q_EXIT_SIG: {
[2] m_te.disarm();
[3] status_ = Q_RET_HANDLED;
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_nest_init : Implementing nested initial transition. |
---|
Description Nested initial transition (see Figure SDS-SM [5]) in a state is coded as special case for the reserved signal QP Q_INIT_SIG. If a state has no nested initial transition, the whole case can be just omitted. |
Example Q_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case Q_INIT_SIG: {
[2] m_temp = BSP::temp();
[3] status_ = tran(&off);
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_tran : Implementing state transition. |
---|
Description A state transition (see Figure SDS-SM [6]) is coded as special case for the user-defined signal (transition trigger). |
Example Q_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] actionA();
[3] status_ = tran(&doorOpen);
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_intern : Implementing internal transition. |
---|
Description An internal transition (see Figure SDS-SM [7]) is coded as special case for the user-defined signal (transition trigger). |
Example Q_STATE_DEF(ToasterOven, off) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case SETUP_SIG: {
[2] m_setup = Q_EVT_CAST(SetupEvt)->setup;
[3] status_ = Q_RET_HANDLED;
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_choice : Implementing choice point and guard conditions. |
---|
Description A choice point with guard conditions (see Figure SDS-SM [8]) is coded as special case for the user-defined signal (transition trigger). |
Example Q_STATE_DEF(ToasterOven, heating) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case TIMEOUT_SIG: {
[2] m_temp = BSP::temp();
[3] if (m_temp >= LIMIT) {
[4] // any action
[5] status_ = tran(&off);
}
[6] else {
[7] // any action
[8] status_ = Q_RET_HANDLED;
}
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_hist : Implementing state history. |
---|
Description State history (see Figure SDS-SM [11]) requires remembering the most recent active substate when the given state is exited, which is accomplished in the exit action from the state coded as already described in SDS_QA_QHsm_exit. |
Example Q_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case Q_EXIT_SIG: {
[2] // any exit actions
// save deep history
[3] hist_doorClosed = state();
[4] status_ = Q_RET_HANDLED;
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QHsm_hist_tran : Implementing transition to history. |
---|
Description Transition to state history (see Figure SDS-SM [12]) is coded similar as a regular state transition, but takes the transition with the |
Example Q_STATE_DEF(ToasterOven, doorOpen) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case CLOSE_SIG: {
[2] // any actions
[3] status_ = tran_hist(hist_doorClosed);
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
This section presents the hierarchical state machine implementation strategy "optimized for automatic code generation" (see SRS_QP_SM_21), which means the implementation may contain some redundant information to improve the speed of the state machine execution at the expense of larger code and data (typically in ROM).
This strategy requires a code generator, such as the QM modeling tool↑ . Indeed, the presented code in the Design Views below has been automatically generated by QM (from the state diagram presented in Figure SDS-SM).
SDS_QA_QMsm_decl : Declaring QP::QMsm subclass. |
---|
Description The first step in coding a hierarchical state machine based on the QP QMsm implementation is to declare a subclass of the QP QMsm or the QP QMActive base class. |
Example [1] class ToasterOven
[2] : public QP::QMsm {
private:
[3] std::uint32_t m_settings;
std::uint16_t m_temp;
QP::QTimeEvt std::te;
protected:
[4] QP::QMState const *hist_doorClosed;
protected:
[10] QM_STATE_DECL( initial);
[11] QM_STATE_DECL( doorClosed);
[12] QM_ACTION_DECL(doorClosed_e);
[13] QM_ACTION_DECL(doorClosed_x);
[14] QM_ACTION_DECL(doorClosed_i);
QM_STATE_DECL( heating);
QM_ACTION_DECL(heating_e);
QM_ACTION_DECL(heating_x);
QM_ACTION_DECL(heating_i);
QM_STATE_DECL( toasting);
QM_ACTION_DECL(toasting_e);
QM_STATE_DECL( baking);
QM_ACTION_DECL(baking_e);
QM_STATE_DECL( off);
QM_ACTION_DECL(off_e);
QM_STATE_DECL( doorOpen);
QM_ACTION_DECL(doorOpen_e);
QM_ACTION_DECL(doorOpen_x);
private:
[20] void actionA();
[21] void actionB();
public:
[30] ToasterOven();
}; // class ToasterOven
Hierarchical State Machine class (QMsm-style state machine implementation strategy) Definition qp.hpp:391
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_top_init : Implementing the top-most initial pseudo-state. |
---|
Description The top-most initial pseudostate is a member function of the sate machine class (see Figure SDS-SM [1]). This pseudostate defines the top-most initial transition, which will be taken when the state machine is initialized (see SDS_QP_QAsm interface init() function). |
Example [1] QM_STATE_DEF(ToasterOven, initial) {
[2] m_setting = DEFAULT_SETTINGS;
m_temp = BSP::temp();
// state history attributes
[3] hist_doorClosed = &off_s;
[4] QS_FUN_DICTIONARY(&ToasterOven::doorClosed);
QS_FUN_DICTIONARY(&ToasterOven::off);
QS_FUN_DICTIONARY(&ToasterOven::heating);
QS_FUN_DICTIONARY(&ToasterOven::doorOpen);
QS_FUN_DICTIONARY(&ToasterOven::sm_heating_toasting);
QS_FUN_DICTIONARY(&ToasterOven::sm_heating_baking);
QS_FUN_DICTIONARY(&ToasterOven::sm_heating);
static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
[5] } const tatbl_ = { // tran-action table
[6] &doorClosed_s, // target state
{
[7] &doorClosed_e, // entry
[8] &doorClosed_i, // initial tran.
[9] Q_ACTION_NULL // zero terminator
}
};
[10] return qm_tran_init(&tatbl_);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_state : Implementing a state and its nesting. |
---|
Description
The QP event processor uses this information to perform state transitions according to the hierarchical state machine semantics specified in SRS_QP_SM_30. Specifically, the action functions are called through the "tran-action tables" to exit the previous state configuration and enter the next state configuration. |
Example
// top-level state
[1] QP::QMState const ToasterOven::doorClosed_s = {
[2a] QM_STATE_NULL, // superstate (top)
[3] &ToasterOven::doorClosed,
[4] &ToasterOven::doorClosed_e,
[5] &ToasterOven::doorClosed_x,
[6] &ToasterOven::doorClosed_i
};
[10] QM_STATE_DEF(ToasterOven, doorClosed) {
[11] QP::QState status_;
[12] switch (e->sig) {
. . .
[13] default: {
[14] status_ = Q_RET_SUPER;
break;
}
}
return status_;
}
// state nested in another state
[1] QP::QMState const ToasterOven::off_s = {
[2b] &ToasterOven::doorClosed_s, // superstate
[3] &ToasterOven::off,
[4] &ToasterOven::off_e,
[5] Q_ACTION_NULL, // no exit action
[6] Q_ACTION_NULL // no initial tran.
};
[10] QM_STATE_DEF(ToasterOven, off) {
[11] QP::QState status_;
[12] switch (e->sig) {
. . .
[13] default: {
[14] status_ = Q_RET_SUPER;
break;
}
}
return status_;
}
#define QM_STATE_NULL Macro to provide strictly-typed zero-state to use for submachines. Applicable to subclasses of QP::QM... Definition qp.hpp:492
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_entry : Implementing state entry action. |
---|
Description State entry action (see Figure SDS-SM [3]) is coded as a separate member function of the state machine class. The QP event processor executes this state-entry action function through the "tran-action table" when a state transition is taken. Please note that the state-entry action function has no access to the original event that triggered the state transition. |
Example [1] QM_ACTION_DEF(ToasterOven, doorClosed_e) {
[2] // any actions...
[3] return qm_entry(&doorClosed_s);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_exit : Implementing state exit action. |
---|
Description State exit action (see Figure SDS-SM [3]) is coded as a separate member function of the state machine class. The QP event processor executes this state-exit action function through the "tran-action table" when a state transition is taken. Please note that the state-exit action function has no access to the original event that triggered the state transition. |
Example [1] QM_ACTION_DEF(ToasterOven, doorClosed_x) {
[2] // any actions...
// save deep history
[3] hist_doorClosed = stateObj();
[4] return qm_exit(&doorClosed_s);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_nest_init : Implementing nested initial transition. |
---|
Description Nested initial transition (see Figure SDS-SM [5]) is coded as a separate member function of the state machine class. The QP event processor executes this initial transition action function through the "tran-action table" when a state transition is taken. Please note that the initial transition function has no access to the original event that triggered the state transition. |
Example [1] QM_ACTION_DEF(ToasterOven, doorClosed_i) {
[2] m_temp = BSP::temp();
static struct {
QP::QMState const *target;
QP::QActionHandler act[2];
[3] } const tatbl_ = { // tran-action table
[4] &off_s, // target state
{
[5] &off_e, // entry
[6] Q_ACTION_NULL // zero terminator
}
};
[7] return qm_tran_init(&tatbl_);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_tran : Implementing state transition. |
---|
Description A state transition (see Figure SDS-SM [6]) is coded as special case in the state-handler function. |
Example QM_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] actionA();
static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
[3] } const tatbl_ = { // tran-action table
[4] &doorOpen_s, // target state
{
[5] &doorClosed_x, // exit
[6] &doorOpen_e, // entry
[7] Q_ACTION_NULL // zero terminator
}
};
[8] status_ = qm_tran(&tatbl_);
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_intern : Implementing internal transition. |
---|
Description An internal transition (see Figure SDS-SM [7]) is coded as special case-statement in the state-handler function. |
Example QM_STATE_DEF(ToasterOven, off) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case SETUP_SIG: {
[2] m_setup = Q_EVT_CAST(SetupEvt)->setup;
[3] status_ = Q_RET_HANDLED;
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_choice : Implementing choice point and guard conditions. |
---|
Description A choice point with guard conditions (see Figure SDS-SM [8]) is coded as special case for the user-defined signal (transition trigger). |
Example QM_STATE_DEF(ToasterOven, heating) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case TIMEOUT_SIG: {
[2] m_temp = BSP::temp();
[3] if (m_temp >= LIMIT) {
[4] // any actions...
[5] static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
} const tatbl_ = { // tran-action table
&off_s, // target state
{
&heating_x, // exit
&off_e, // entry
Q_ACTION_NULL // zero terminator
}
};
[6] status_ = qm_tran(&tatbl_);
}
[7] else {
[8] // any actions...
[9] status_ = Q_RET_HANDLED;
}
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_hist : Implementing state history. |
---|
Description State history (see Figure SDS-SM [11]) requires remembering the most recent active substate when the given state is exited, which is accomplished in the exit action from the state coded as already described in SDS_QA_QMsm_exit. |
Example [1] QM_ACTION_DEF(ToasterOven, doorClosed_x) {
[2] // any actions...
// save deep history
[3a] hist_doorClosed = stateObj();
[3b] hist_doorClosed = childStateObj(&doorClosed_s);
[4] return qm_exit(&doorClosed_s);
}
|
Backward Traceability
|
Forward Traceability |
SDS_QA_QMsm_hist_tran : Implementing transition to history. |
---|
Description Transition to state history (see Figure SDS-SM [12]) is coded similar as a regular state transition, but uses a different macro to take the transition QM_TRAN_HIST(). |
Example QM_STATE_DEF(ToasterOven, doorOpen) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case CLOSE_SIG: {
[2] // any actions...
static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
[3] } const tatbl_ = { // tran-action table
&doorClosed_s, // target state
{
&doorOpen_x, // exit
&doorClosed_e, // entry
Q_ACTION_NULL // zero terminator
}
};
[4] status_ = qm_tran_hist(hist_doorClosed, &tatbl_);
break;
}
. . .
}
return status_;
}
|
Backward Traceability
|
Forward Traceability |