QP/C++  8.0.0
Real-Time Embedded Framework
Loading...
Searching...
No Matches
State Dynamics Viewpoint

Interaction ViewpointTime Viewpoint

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".

Figure SDS-SM: Example Hierarchical State Machine (a toaster oven)

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
Note
The design concerns included in this State Dynamics viewpoint apply to QP/C++ Applications rather than QP/C++ Framework and therefore they are use the UIDs 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.

QHsm Design View

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

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
The following snippet of code shows the complete declaration of the ToasterOven subclass of QP QHsm shown in Figure SDS-SM:

[1] class ToasterOven
[2] : public QP::QHsm {
private:
[3] std::uint32_t m_settings;
std::uint16_t m_temp;
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(doorOpen);
private:
[40] void actionA();
void actionB();
}; // class ToasterOven
QAsmAttr m_temp
Temporary storage for target/act-table etc.
Definition qp.hpp:215
Hierarchical State Machine class (QHsm-style state machine implementation strategy)
Definition qp.hpp:361
Time Event class.
Definition qp.hpp:927
QState(*)(void *const me, QEvt const *const e) QStateHandler
Pointer to a state-handler function.
Definition qp.hpp:168
#define Q_STATE_CAST(handler_)
Definition qp.hpp:456
#define Q_STATE_DECL(state_)
Definition qp.hpp:436

[1] Declaration of class ToasterOven

[2] Class ToasterOven inherits QP QHsm, so it can have a state machine that must be implemented according to the rules specified in the QHsm Design View.

Note
The state machine implementation is identical, whether it inherits QP QHsm, QP QActive, or subclasses of these base classes.

[3] The class can have data members (typically private), which will be accessible inside the state machine as the "extended-state variables".

[4] If the state machine uses state history (Figure SDS-SM [11]), a data member must be provided to remember each state's history.

[10] The class needs to provide a constructor, typically without any parameters.

Note
The role of the constructor is just to initialize the state machine instance to bind it to the top-most initial pseudo-state (see step [5]). Most other data members are initialized in the top-most initial transition, which is taken later, when when the state machine is initialized (see SDS_QA_QHsm_top_init).

[11] The class constructor, must call the superclass' constructor (QHsm_ctor() constructor in this case). The superclass' constructor initializes this state machine to the top-most initial pseudostate declared in step [5].

[12] The class constructor, must call the constructors of its data members, if such constructors exist.

[20] Each state machine must have exactly one initial pseudo-state, which is a member function of the class as shown in the listing. By convention, the initial pseudo-state should be always called initial.

[30] All states are represented as member functions of the sate machine class (hence they are called state-handler functions). Every state-handler function takes the const pointer to the current event as a parameter.

Note
All states are declared the same way regardless of their nesting level.

[40] The class might also provide any number of action functions that will be called in the state machine.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_top_init

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
The following example code shows a top-most initial pseudo-state function with various elements explained in the labeled annotations following the listing.

[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);
}
#define Q_STATE_DEF(subclass_, state_)
Definition qp.hpp:441
#define QS_FUN_DICTIONARY(fun_)
Definition qs_dummy.hpp:72

[1] The top-most pseudostate definition starts with Q_STATE_DEF() macro (see also SDS_QA_QHsm_decl [5]). The macro parameters are the state machine name and the state name.

[2] The top-most initial pseudostate initializes internal variables (see also SDS_QA_QHsm_decl [3])

[3] The top-most initial pseudostate initializes state histories for all states with history (see also SDS_QA_QHsm_hist)

[4] If QS software tracing is used, the top-most initial pseudostate produces function dictionaries for all its state-handler functions

[5] The top-most initial pseudostate must return with the QP QHsm::tran() "tran()" function. The argument of this function is the target state indicated in the diagram (see Figure SDS-SM [1])

Backward Traceability
Forward Traceability

SDS_QA_QHsm_state

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
The following example code shows two state-handler functions with almost identical structure. The first one implements a top-level state. The second one implements a state that is explicitly nested in another state.

Note
The general structure of state-handler functions is the same regardless of their nesting level.
// 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_;
}
std::uint_fast8_t QState
Type returned from state-handler functions.
Definition qp.hpp:165

[1] The state definition starts with Q_STATE_DEF() macro (see also SDS_QA_QHsm_decl [6]). The macro parameters are the state machine name and the state name.

[2] The local status_ variable to return at the end of the state-handler function

[3] The state-handler function is structured as a switch statement that discriminates based on the signal of the current event e->sig (see SDS_QP_QEvt)

[4] the case of the switch implement various state elements explained in the remaining Design Elements in this viewpoint.

[5] The switch must always have the default case, which should be executed without side effects.

[6a] For a state that does not nest in any other state, you need to set the status_ variable to the QP QHsm::super() "super()" function, whereas the argument QP QHsm::QHsm::top() "top()" is the top-most state handler function defined in the QP QHsm base class.

[6b] For a state that explicitly nests in another state, you need to set the status_ variable to the QP QHsm::super() "super()" function, whereas the argument is the superstate of that state.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_entry

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
The following example code shows a state-handler function with an entry action.

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

[1] State with an entry action needs a case-statement on the reserved signal QP QAsm::Q_ENTRY_SIG "Q_ENTRY_SIG".

Note
The case Q_ENTRY_SIG is needed only if a state actually has something to do upon the state entry. If there is nothing to do, the whole case can be just omitted.

[2] Inside the case, you code all the actions to be called upon the entry to the state.

[3] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the entry action has been handled.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_exit

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
The following example code shows a state-handler function with an exit action.

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_;
}
bool disarm() noexcept
Definition qf_time.cpp:183

[1] State with an exit action needs a case statement on the reserved signal QP QAsm::Q_EXIT_SIG "Q_EXIT_SIG"

Note
The case Q_EXIT_SIG is needed only if a state actually has something to do upon the state exit. If there is nothing to do, the whole case can be just omitted.

[2] Inside the case, you code all the actions to be called upon the exit from the state.

[3] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the exit action has been handled.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_nest_init

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
The following example code shows a state-handler function with nested initial transition.

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_;
}

[1] State with a nested initial transition needs a case-statement on the reserved signal QP QAsm::Q_INIT_SIG "Q_INIT_SIG"

[2] Inside the case, you code all the actions to be called upon the initial transition.

[3] The action list must end with setting the status to the QP QHsm::tran() "tran()" function. The argument of this function is the target state indicated in the diagram (see Figure SDS-SM [5])

Attention
The target of the nested initial transition specified with the QP QHsm::tran() "tran()" function must be a substate of the given state.
Backward Traceability
Forward Traceability

SDS_QA_QHsm_tran

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
The following example code shows a state-handler function with a state transition.

Q_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] actionA();
[3] status_ = tran(&doorOpen);
break;
}
. . .
}
return status_;
}

[1] State with a transition needs a case-statement on the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called upon the transition.

[3] The action list must end with setting the status to the QP QHsm::tran() "tran()" function. The argument of this function is the target state indicated in the diagram (see Figure SDS-SM [6])

Backward Traceability
Forward Traceability

SDS_QA_QHsm_intern

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
The following example code shows a state-handler function with an internal transition.

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_;
}
#define Q_EVT_CAST(subclass_)
Definition qp.hpp:453

[1] State with a transition needs a case-statement on the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called upon the transition.

[3] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the internal transition has been handled.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_choice

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
The following example code shows a state-handler function with an internal transition.

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_;
}

[1] State with a choice needs a case-statement for the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called before any guard conditions are evaluated.

[3] The guard condition is represented as a condition of the if statement.

[4] any actions to be executed if the guard evaluates to 'true'.

[5] regular state transition is coded as usual with the QP QHsm::tran() "tran()" function with the target of the transition specified as the argument.

[6] the complementary guard condition to all evaluated before is specified with the else statement.

Note
A choice point might have many outgoing guards, each specified in else if (guard)

[7] any actions to be executed for this guard condition.

[8] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the internal transition has been handled..

Backward Traceability
Forward Traceability

SDS_QA_QHsm_hist

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
The following example code shows an exit action that additionally saves the state history.

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_;
}

[1] State with an exit action needs a case-statement on the reserved signal QP QAsm::Q_EXIT_SIG "Q_EXIT_SIG"

[2] Inside the case, you code all the actions to be called upon the exit from the state.

[3] One of the actions must be to set the history variable for the given state to the currently active state. The QP QHsm base class provides two member functions to obtain the current state:

  • QP QHsm::state() "state()" to obtain the currently active state for deep history
  • QP QHsm::childState() "childState()" to obtain the immediate active child state of the given state for shallow history

[4] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the exit action has been executed.

Backward Traceability
Forward Traceability

SDS_QA_QHsm_hist_tran

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
The following example code shows a transition to state history.

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_;
}

[1] State with a transition to history needs a case-statement for the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called upon the transition.

[3] The action list must end with setting the status to the QP QHsm::tran_hist() "tran_hist()" function with the state history variable of the transition specified as the argument.

Backward Traceability
Forward Traceability

QMsm Design View

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).

Note
The purpose of this section is to describe the design of the generated code for the purpose of its readability and the ability to debug such code, but not to implement it manually.

SDS_QA_QMsm_decl

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
The following snippet of code shows the complete declaration of the ToasterOven subclass of QP QMsm shown in Figure SDS-SM:

[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( 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
#define QM_STATE_DECL(state_)
Definition qp.hpp:460
#define QM_ACTION_DECL(action_)
Definition qp.hpp:466
State object for the QP::QMsm class (QM State Machine)
Definition qp.hpp:181

[1] Declaration of class ToasterOven

[2] Class ToasterOven inherits QP::QMsm, so it can have a state machine that must be implemented according to the rules specified in the QMsm Design View.

Note
The state machine implementation is identical, whether it inherits QP::QMsm, QP::QMActive, or subclasses of these base classes.

[3] The class can have data members (typically private), which will be accessible inside the state machine as the "extended-state variables".

[4] If the state machine uses state history (Figure SDS-SM [11]), a data member must be provided to remember each state's history.

[10] The class needs to provide a constructor, typically without any parameters.

Note
The role of the constructor is just to initialize the state machine instance to bind it to the top-most initial pseudo-state (see step [5]). Most other data members are initialized in the top-most initial transition, which is taken later, when when the state machine is initialized (see SDS_QA_QHsm_top_init).

[11] The class constructor, must call the superclass' constructor (QP::QMsm::QMsm() constructor in this case). The superclass' constructor initializes this state machine to the top-most initial pseudostate declared in step [20].

[12] The class constructor, must call the constructors of its data members, if such constructors exist.

[20] Each state machine must have exactly one initial pseudo-state, which is a member function of the class as shown in the listing. By convention, the initial pseudo-state should be always called initial.

[30-34] States are represented as a group of member functions of the state machine class plus a QP::QMState data structure. The member functions are as follows:

  • [30] state-handler for all transitions (including internal transitions)
  • [31] state action handler for state entry
  • [32] state action handler for state exit
  • [33] state action handler for nested initial transition within this state
  • [34] state object that summarizes the information about this state (const in ROM)
Note
Only state actions actually used by the state are declared. For example, state toasting does not have an exit action, so it does not define the QM_ACTION_DECL(toasting_x) action function.
All states are declared the same way regardless of their nesting level.

[40] The class might also provide any number of action functions that will be called in the state machine.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_top_init

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
The following example code shows a top-most initial pseudo-state function with various elements explained in the labeled annotations following the listing.

[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;
[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_);
}
QState(*)(void *const me) QActionHandler
Pointer to an action-handler function.
Definition qp.hpp:171
#define QM_STATE_DEF(subclass_, state_)
Definition qp.hpp:471
#define Q_ACTION_NULL
Definition qp.hpp:495

[1] The top-most initial pseudostate function signature (see also SDS_QA_QMsm_decl [9])

[2] The top-most initial pseudostate initializes internal variables (see also SDS_QA_QMsm_decl [3])

[3] The top-most initial pseudostate initializes state histories for all states with history (see also SDS_QA_QMsm_hist)

[4] If QS software tracing is used, the top-most initial pseudostate produces function dictionaries for all its state-handler functions

[5-9] The top-most initial pseudostate declares a static and const struct called "tran-action table", which specifies the sequence of actions to execute:

  • [6] the target state of the initial transition
  • [7] the entry action to that target state
  • [8] the nested initial transition in that target state
  • [9] the "zero terminator" that indicates the end of the "tran-action table"
Note
The "tran-action table" speeds up the transition execution because all actions are preconfigured at compile-time rather than being discovered at runtime as in the case of the QHsm-based state machine implementation strategy. Being defined as both `static` and `const`, the "tran-action table" can be allocated in ROM and takes no precious RAM.

[10] The top-most initial pseudostate must end with the top-most initial transition, which is coded with the QM_TRAN_INIT() macro. The argument of this macro is the "tran-action table" specified in steps [5-9] (see also Figure SDS-SM [1])

Backward Traceability
Forward Traceability

SDS_QA_QMsm_state

SDS_QA_QMsm_state : Implementing a state and its nesting.

Description
States in the QMsm-based implementation strategy are const data structures of type QP QMState that store:

  1. superstate of a given state
  2. state-handler function
  3. state entry action function
  4. state exit action function
  5. state initial action function

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
The following example code shows two state data structures and the corresponding state-handler functions. The first one implements a top-level state. The second one implements a state that is explicitly nested in another state.

Note
The general structure of state-handler functions is the same regardless of their nesting level.
// 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

[1-6] The const state data struct for the ToasterOven state "doorClosed" is defined and initialized with:

  • [2a] The superstate of this state (QM_STATE_NULL in case of a top-level state)
  • [2b] The superstate of this state (pointer to the state data struct in case of a state nested in another state)
  • [3] The state-handler function pointer to process application-defined events
  • [4] The action-handler function pointer to execute upon the entry to the state (might be Q_ACTION_NULL)
  • [5] The action-handler function pointer to execute upon the exit from the state (might be Q_ACTION_NULL)
  • [6] The action-handler function pointer to execute upon the nested initial transition in the state (might be Q_ACTION_NULL)

[10] The state-handler function to process application-defined events

[11] The local status_ variable to return at the end of the state-handler function

[12] The state-handler function is structured as a switch statement that discriminates based on the signal of the current event e->sig (see SDS_QP_QEvt).

[13] The switch must always have the default case, which should be executed without side effects.

[14] The default case must set the status_ to the QP QAsm::Q_RET_SUPER "Q_RET_SUPER" value.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_entry

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
The following example code shows a state-handler function with an entry action.

[1] QM_ACTION_DEF(ToasterOven, doorClosed_e) {
[2] // any actions...
[3] return qm_entry(&doorClosed_s);
}
#define QM_ACTION_DEF(subclass_, action_)
Definition qp.hpp:477

[1] The state-entry action function is a member of the state machine class.

[2] The state action function calls all the actions to be executed upon the entry to the state.

[3] The state-entry action function must return the QP QAsm::qm_entry() "qm_entry()" function, with the argument being the state struct corresponding to the state being entered.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_exit

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
The following example code shows a state-handler function with an exit action.

[1] QM_ACTION_DEF(ToasterOven, doorClosed_x) {
[2] // any actions...
// save deep history
[3] hist_doorClosed = stateObj();
[4] return qm_exit(&doorClosed_s);
}

[1] The state-exit action function is a member of the state machine class.

[2] The state action function calls all the actions to be executed upon the exit from the state.

[3] If the state has a history transition, the state-exit action function must set the state history variable.

[4] The state-exit action function must return the QP QAsm::qm_exit() "qm_exit()" function, with the argument being the state struct corresponding to the state being exited.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_nest_init

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
The following example code shows a initial transition function.

[1] QM_ACTION_DEF(ToasterOven, doorClosed_i) {
[2] m_temp = BSP::temp();
static struct {
QP::QMState const *target;
[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_);
}

[1] The initial-transition function has the usual signature of an action-handler.

[2] Any actions on this initial transition are executed

[3-6] The static and const struct called "tran-action table" specifies the sequence of actions to execute:

  • [4] the target state of the initial transition
  • [5] the entry action to that target state
  • [6] the "zero terminator" that indicates the end of the "tran-action table"

[7] The state-exit action function must return the QP QAsm::qm_tran_init() "qm_tran_init()" function. The argument of this macro is the "tran-action table" specified in steps [3-6] (see also Figure SDS-SM [5]).

Backward Traceability
Forward Traceability

SDS_QA_QMsm_tran

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
The following example code shows a state-handler function with a state transition.

QM_STATE_DEF(ToasterOven, doorClosed) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] actionA();
static struct {
QP::QMState const *target;
[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_;
}

[1] State with a transition needs a case-statement on the user-defined signal (transition trigger).

[7] Any actions on this initial transition are executed

[3-7] The static and const struct called "tran-action table" specifies the sequence of actions to execute:

  • [4] the target state of the initial transition
  • [5] the exit action from the source state
  • [6] the entry action to that target state
  • [7] the "zero terminator" that indicates the end of the "tran-action table"

[8] The transition must be requested by means of the QP QAsm::qm_tran() "qm_tran()" function. The argument of this function is the "tran-action table" specified in steps [3-7]

Backward Traceability
Forward Traceability

SDS_QA_QMsm_intern

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
The following example code shows a state-handler function with an internal transition.

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_;
}

[1] State with a transition needs a case-statement on the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called upon the transition.

[3] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the internal transition has been handled.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_choice

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
The following example code shows a state-handler function with an internal transition.

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;
} 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_;
}

[1] State-handler needs a case-statement on the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called before any guard conditions are evaluated.

[3] The guard condition is represented as a condition of the if statement.

[4] any actions to be executed if the guard evaluates to 'true'.

[5] as usual in all QMsm transitions, every transition needs a "tran-action table"

[6] state transition to a regular state is coded as usual with the QP QAsm::qm_tran() "qm_tran()" function with the argument being the "tran-action table".

[7] the complementary guard condition to all evaluated before is specified with the else statement.

Note
A choice point might have many outgoing guards, each specified in else if (guard)

[8] any actions to be executed for this guard condition.

[9] The action list must end with setting the status to the QP QAsm::Q_RET_HANDLED "Q_RET_HANDLED" value, which informs the QP event processor that the internal transition has been handled.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_hist

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
The following example code shows an exit action that additionally saves the state history.

[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);
}

[1] The state-exit action function is a member of the state machine class.

[2] The state action function calls all the actions to be executed upon the exit from the state.

[3] If the state has a history transition, the state-exit action function must set the state history variable. The QP QMsm base class provides two member functions to obtain the current state:

[4] The state-exit action function must return the qm_exit() function, with the argument being the state struct corresponding to the state being entered.

Backward Traceability
Forward Traceability

SDS_QA_QMsm_hist_tran

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
The following example code shows a transition to state history.

QM_STATE_DEF(ToasterOven, doorOpen) {
QP::QState status_;
switch (e->sig) {
. . .
[1] case CLOSE_SIG: {
[2] // any actions...
static struct {
QP::QMState const *target;
[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_;
}

[1] State with a transition to history needs a case-statement for the user-defined signal (transition trigger).

[2] Inside the case, you code all the actions to be called upon the transition.

[3] The static and const struct called "tran-action table" specifies the sequence of actions to execute.

[4] The transition to history must be requested by means of the QP QAsm::qm_tran_hist() "qm_tran_hist()" function. The arguments of this function are the history variable for the given state and the "tran-action table" specified in step [3]

Backward Traceability
Forward Traceability

Interaction ViewpointTime Viewpoint