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 QHsm subclass.
Description
The first step in coding a hierarchical state machine based on the QHsm implementation is to declare a subclass of the QHsm or the QActive base class.

Example
The following snippet of code shows the complete declaration of the ToasterOven subclass of QHsm shown in Figure SDS-SM:

[1] typedef struct {
// protected:
[2] QHsm super;
// private:
[3] uint32_t settings;
uint16_t temp;
// private state histories
[4] QStateHandler hist_doorClosed;
[1] } ToasterOven;
// public:
[10] void ToasterOven_ctor(ToasterOven * const me) {
[11] QHsm_ctor(&me->super, Q_STATE_CAST(&ToasterOven_initial));
[12] QTimeEvt_ctorX(&me->te, &me->super, TIMEOUT_SIG, 0U),
}
// protected:
[20] QState ToasterOven_initial(ToasterOven * const me, void const * const par);
[30] QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e);
QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e);
QState ToasterOven_toasting(ToasterOven * const me, QEvt const * const e);
QState ToasterOven_baking(ToasterOven * const me, QEvt const * const e);
QState ToasterOven_off(ToasterOven * const me, QEvt const * const e);
QState ToasterOven_doorOpen(ToasterOven * const me, QEvt const * const e);
// private:
[40] void ToasterOven_actionA(ToasterOven * const me);
void ToasterOven_actionB(ToasterOven * const me);
#define Q_STATE_CAST(handler_)
Perform cast to QStateHandler.
Definition qp.h:519
QState(* QStateHandler)(void *const me, QEvt const *const e)
Pointer to a state-handler function.
Definition qp.h:215
enum QStateRet QState
Type returned from state-handler functions.
Definition qp.h:212
Event class.
Definition qp.h:131
Hierarchical State Machine class (QHsm-style state machine implementation strategy)
Definition qp.h:292
Time Event class.
Definition qp.h:946

[1] Declaration of class ToasterOven

[2] Class ToasterOven inherits 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 QHsm, 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] QState ToasterOven_initial(ToasterOven * const me, void const * const par) {
[2] me->setting = DEFAULT_SETTINGS;
me->temp = BSP_temp();
// state history attributes
[3] me->hist_doorClosed = Q_STATE_CAST(&ToasterOven_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 Q_TRAN(&ToasterOven_doorClosed);
}
#define Q_TRAN(target_)
Take transition to the specified target_ state.
Definition qp.h:492
#define QS_FUN_DICTIONARY(fun_)
Definition qs_dummy.h:72

[1] The top-most initial pseudostate function signature (see also SDS_QA_QHsm_decl [5])

[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 Q_TRAN() macro. The argument of this macro 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] QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e) {
[2] QState status_;
[3] switch (e->sig) {
[4] . . .
[5] default: {
[6a] status_ = Q_SUPER(&QHsm_top);
break;
}
}
[7] return status_;
}
// state explicitly nested in another state
[1] QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
[2] QState status_;
[3] switch (e->sig) {
[4] . . .
[5] default: {
[6b] status_ = Q_SUPER(&ToasterOven_doorClosed);
break;
}
}
[7] return status_;
}
#define Q_SUPER(super_)
Designates the superstate of a given state. Applicable only to QHsm subclasses.
Definition qp.h:502
QSignal sig
Signal of the event (see Event Signal)
Definition qp.h:135

[1] The state function signature (see also SDS_QA_QHsm_decl [6]). The function takes the me pointer (OOP) and a const pointer to the current event e.

[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 Q_TRAN() macro, whereas the argument QHsm_top() is the top-most state handler function defined in the QHsm base class.

[6b] For a state that explicitly nests in another state, you need to set the status_ variable to the Q_TRAN() macro, 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 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 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.

QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
[1] case Q_ENTRY_SIG: {
[2] QTimeEvt_armX(&me->te, 100, 0U);
[3] status_ = Q_HANDLED();
break;
}
. . .
}
return status_;
}
#define Q_HANDLED()
Indicate that an action has been "handled". Applies to entry/exit actions and to internal transitions...
Definition qp.h:507
#define Q_ENTRY_SIG
reserved signal sent to state handler to execute the entry case)
Definition qp.h:480

[1] State with an entry action needs a case-statement on the reserved signal 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 Q_HANDLED() macro, 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 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 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.

QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case Q_EXIT_SIG: {
[2] (void)QTimeEvt_disarm(&me->te);
[3] status_ = Q_HANDLED();
break;
}
. . .
}
return status_;
}
#define Q_EXIT_SIG
reserved signal sent to state handler to execute the exit case)
Definition qp.h:483

[1] State with an exit action needs a case statement on the reserved signal 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 Q_HANDLED() macro, 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 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.

QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case Q_INIT_SIG: {
[2] me->temp = BSP_temp(); // get temperature
[3] status_ = Q_TRAN(&ToasterOven_baking);
break;
}
. . .
}
return status_;
}
#define Q_INIT_SIG
reserved signal sent to state handler to execute the initial transition)
Definition qp.h:486

[1] State with a nested initial transition needs a case-statement on the reserved signal 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 Q_TRAN() macro with the argument being the substate-handler, which informs the QP event processor about the next state and that the nested initial transition has been executed.

Attention
The target of the nested initial transition specified with the macro Q_TRAN() 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.

QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] ToasterOven_actionA(me);
[3] status_ = Q_TRAN(&ToasterOven_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 Q_TRAN() macro. The argument of this macro 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.

QState ToasterOven_off(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case SETUP_SIG: {
[2] me->setup = Q_EVT_CAST(SetupEvt)->setup;
[3] status_ = Q_HANDLED();
break;
}
. . .
}
return status_;
}
#define Q_EVT_CAST(class_)
Perform downcast of an event onto a subclass of QEvt class_
Definition qp.h:516

[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 Q_HANDLED() macro, 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.

QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case TIMEOUT_SIG: {
[2] me->temp = BSP_temp();
[3] if (me->temp >= LIMIT) {
[4] // any actions
[5] status_ = Q_TRAN(&ToasterOven_off);
}
[6] else {
[7] // any actions
[8] status_ = Q_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 Q_TRAN() macro 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 Q_HANDLED() macro, 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.

QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case Q_EXIT_SIG: {
[2] // any exit actions
// save deep history
[3] me->hist_doorClosed = QHsm_state(Q_HSM_UPCAST(me));
[4] status_ = Q_HANDLED();
break;
}
. . .
}
return status_;
}
#define Q_HSM_UPCAST(ptr_)
Perform upcasting from a subclass of QHsm to the base class QHsm.
Definition qp.h:471

[1] State with an exit action needs a case-statement on the reserved signal 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 QHsm base class provides two member functions to obtain the current state:

  • QHsm_state() to obtain the currently active state for deep history
  • QHsm_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 Q_HANDLED() macro, 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.

QState ToasterOven_doorOpen(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case CLOSE_SIG: {
[2] // any actions
[3] status_ = Q_TRAN_HIST(me->hist_doorClosed);
break;
}
. . .
}
return status_;
}
#define Q_TRAN_HIST(hist_)
Take transition to the specified history of a given state. Applicable only to HSMs.
Definition qp.h:497

[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 Q_TRAN_HIST() macro 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 QMsm subclass.
Description
The first step in coding a hierarchical state machine based on the QMsm implementation is to declare a subclass of the QMsm or the QMActive base class.

Example
The following snippet of code shows the complete declaration of the ToasterOven subclass of QMsm shown in Figure SDS-SM:

[1] typedef struct {
// protected:
[2] QMsm super;
// private:
[3] uint32_t settings;
uint16_t temp;
// private state histories
[4] QMState const *hist_doorClosed;
[1] } ToasterOven;
// public:
[10] void ToasterOven_ctor(ToasterOven * const me) {
QHsm_ctor(&me->super, Q_STATE_CAST(&ToasterOven_initial));
QTimeEvt_ctorX(&me->te, &me->super, TIMEOUT_SIG, 0U),
}
// protected:
[20] QState ToasterOven_initial(ToasterOven * const me, void const * const par);
[30] QState ToasterOven_doorClosed (ToasterOven * const me, QEvt const * const e);
[31] QState ToasterOven_doorClosed_e(ToasterOven * const me);
[32] QState ToasterOven_doorClosed_x(ToasterOven * const me);
[33] QState ToasterOven_doorClosed_i(ToasterOven * const me);
[34] extern QMState const ToasterOven_doorClosed_s;
QState ToasterOven_heating (ToasterOven * const me, QEvt const * const e);
QState ToasterOven_heating_e(ToasterOven * const me);
QState ToasterOven_heating_x(ToasterOven * const me);
QState ToasterOven_heating_i(ToasterOven * const me);
extern QMState const ToasterOven_heating_s;
QState ToasterOven_toasting (ToasterOven * const me, QEvt const * const e);
QState ToasterOven_toasting_e(ToasterOven * const me);
extern QMState const ToasterOven_toasting_s;
QState ToasterOven_baking (ToasterOven * const me, QEvt const * const e);
QState ToasterOven_baking_e(ToasterOven * const me);
extern QMState const ToasterOven_baking_s;
QState ToasterOven_off (ToasterOven * const me, QEvt const * const e);
QState ToasterOven_off_e(ToasterOven * const me);
extern QMState const ToasterOven_off_s;
QState ToasterOven_doorOpen (ToasterOven * const me, QEvt const * const e);
QState ToasterOven_doorOpen_e(ToasterOven * const me);
QState ToasterOven_doorOpen_x(ToasterOven * const me);
extern QMState const ToasterOven_doorOpen_s;
// private:
[40] void ToasterOven_actionA(ToasterOven * const me);
void ToasterOven_actionB(ToasterOven * const me);
State object for the QMsm class (QM State Machine)
Definition qp.h:228
Hierarchical State Machine class (QMsm-style state machine implementation strategy)
Definition qp.h:355

[1] Declaration of class ToasterOven

[2] Class ToasterOven inherits 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 QMsm, 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 (QMsm_ctor() 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 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 ToasterOven_toasting does not have an exit action, so it does not define the ToasterOven_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] QState ToasterOven_initial(ToasterOven * const me, void const * const par) {
[2] me->setting = DEFAULT_SETTINGS;
me->temp = BSP_temp();
// state history attributes
[3] me->hist_doorClosed = &ToasterOven_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 {
QMState const *target;
[5] } const tatbl_ = { // tran-action table
[6] &ToasterOven_doorClosed_s, // target state
{
[7] Q_ACTION_CAST(&ToasterOven_doorClosed_e), // entry
[8] Q_ACTION_CAST(&ToasterOven_doorClosed_i), // initial tran.
[9] Q_ACTION_NULL // zero terminator
}
};
[10] return QM_TRAN_INIT(&tatbl_);
}
#define QM_TRAN_INIT(tatbl_)
Macro to call in a QM state-handler when it executes an initial transition. Applicable only to QMsm s...
Definition qp.h:568
#define Q_ACTION_CAST(action_)
Perform cast to QActionHandler.
Definition qp.h:522
QState(* QActionHandler)(void *const me)
Pointer to an action-handler function.
Definition qp.h:218
#define Q_ACTION_NULL
Macro to provide strictly-typed zero-action to terminate action lists in the transition-action-tables...
Definition qp.h:513

[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 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] QMState const ToasterOven_doorClosed_s = {
[2a] QM_STATE_NULL, // superstate (top)
[3] Q_STATE_CAST(&ToasterOven_doorClosed),
[4] Q_ACTION_CAST(&ToasterOven_doorClosed_e),
[5] Q_ACTION_CAST(&ToasterOven_doorClosed_x),
[6] Q_ACTION_CAST(&ToasterOven_doorClosed_i)
};
[10] QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e) {
[11] QState status_;
[12] switch (e->sig) {
. . .
[13] default: {
[14] status_ = QM_SUPER();
break;
}
}
[15] return status_;
}
// state nested in another state
[1] QMState const ToasterOven_off_s = {
[2b] &ToasterOven_doorClosed_s, // superstate doorClosed
[3] Q_STATE_CAST(&ToasterOven_off),
[4] Q_ACTION_CAST(&ToasterOven_off_e),
[5] Q_ACTION_NULL, // no exit action
[6] Q_ACTION_NULL // no initial tran.
};
[10] QState ToasterOven_off(ToasterOven * const me, QEvt const * const e) {
[11] QState status_;
[12] switch (e->sig) {
. . .
[13] default: {
[14] status_ = QM_SUPER();
break;
}
}
[15] return status_;
}
#define QM_SUPER()
Macro to call in a QM state-handler when it designates the superstate to handle an event....
Definition qp.h:586
#define QM_STATE_NULL
Macro to provide strictly-typed zero-state to use for submachines. Applicable to subclasses of QMsm.
Definition qp.h:589

[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 macro QM_SUPER().

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] QState ToasterOven_doorClosed_e(ToasterOven * const me) {
[2] // any actions...
[3] return QM_ENTRY(&ToasterOven_doorClosed_s);
}
#define QM_ENTRY(state_)
Macro to call in a QM action-handler when it executes an entry action. Applicable only to QMsm subcla...
Definition qp.h:535

[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 QM_ENTRY() macro, 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] QState ToasterOven_doorClosed_x(ToasterOven * const me) {
[2] // any actions...
[3] me->hist_doorClosed = QMsm_stateObj(Q_MSM_UPCAST(me));
[4] return QM_EXIT(&ToasterOven_doorClosed_s);
}
#define Q_MSM_UPCAST(ptr_)
Perform upcasting from a subclass of QMsm to the base class QMsm.
Definition qp.h:474
#define QM_EXIT(state_)
Macro to call in a QM action-handler when it executes an exit action. Applicable only to QMsm subclas...
Definition qp.h:547

[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 QM_EXIT() macro, 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] QState ToasterOven_doorClosed_i(ToasterOven * const me) {
[2] me->temp = BSP_temp();
static struct {
QMState const *target;
[3] } const tatbl_ = { // tran-action table
[4] &ToasterOven_off_s, // target state
{
[5] Q_ACTION_CAST(&ToasterOven_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 initial transition function must end with the QM_TRAN_INIT() macro. 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.

QState ToasterOven_doorClosed(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case OPEN_SIG: {
[2] ToasterOven_actionA(me);
static struct {
QMState const *target;
[3] } const tatbl_ = { // tran-action table
[4] &ToasterOven_doorOpen_s, // target state
{
[5] Q_ACTION_CAST(&ToasterOven_doorClosed_x), // exit
[6] Q_ACTION_CAST(&ToasterOven_doorOpen_e), // entry
[7] Q_ACTION_NULL // zero terminator
}
};
[8] status_ = QM_TRAN(&tatbl_);
break;
}
. . .
}
return status_;
}
#define QM_TRAN(tatbl_)
Macro to call in a QM state-handler when it executes a regular transition. Applicable only to QMsm su...
Definition qp.h:563

[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 QM_TRAN() macro. The argument of this macro 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.

QState ToasterOven_off(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case SETUP_SIG: {
[2] me->setup = Q_EVT_CAST(SetupEvt)->setup;
[3] status_ = QM_HANDLED();
break;
}
. . .
}
return status_;
}
#define QM_HANDLED()
Macro to call in a QM state-handler when it handled an event. Applicable only to QMsm subclasses.
Definition qp.h:580

[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 QM_HANDLED() macro, 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.

QState ToasterOven_heating(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case TIMEOUT_SIG: {
[2] me->temp = BSP_temp();
[3] if (me->temp >= LIMIT) {
[4] // any actions...
[5] static struct {
QMState const *target;
} const tatbl_ = { // tran-action table
&ToasterOven_off_s, // target state
{
Q_ACTION_CAST(&ToasterOven_heating_x), // exit
Q_ACTION_CAST(&ToasterOven_off_e), // entry
Q_ACTION_NULL // zero terminator
}
};
[6] status_ = QM_TRAN(&tatbl_);
}
[7] else {
[8] // any actions...
[9] status_ = QM_HANDLED();
}
break;
}
. . .
}
return status_;
}
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:525

[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 QM_TRAN() macro 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 QM_HANDLED() macro, 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] QState ToasterOven_doorClosed_x(ToasterOven * const me) {
[2] // any actions...
// save deep history
[3a] me->hist_doorClosed = QMsm_stateObj(Q_MSM_UPCAST(me));
[3b] me->hist_doorClosed = QMsm_childStateObj(Q_MSM_UPCAST(me),
&TstSM_doorClosed_s);
[4] return QM_EXIT(&ToasterOven_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 QMsm base class provides two member functions to obtain the current state:

  • QMsm_stateObj to obtain the currently active state for deep history
  • QMsm_childStateObj() to obtain the immediate active child state of the given state for shallow history

[4] The state-exit action function must return the QM_EXIT() macro, 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.

QState ToasterOven_doorOpen(ToasterOven * const me, QEvt const * const e) {
QState status_;
switch (e->sig) {
. . .
[1] case CLOSE_SIG: {
[2] // any actions...
static struct {
QMState const *target;
[3] } const tatbl_ = { // tran-action table
&ToasterOven_doorClosed_s, // target state
{
Q_ACTION_CAST(&ToasterOven_doorOpen_x), // exit
Q_ACTION_CAST(&ToasterOven_doorClosed_e), // entry
Q_ACTION_NULL // zero terminator
}
};
[4] status_ = QM_TRAN_HIST(me->hist_doorClosed, &tatbl_);
break;
}
. . .
}
return status_;
}
#define QM_TRAN_HIST(history_, tatbl_)
Macro to call in a QM state-handler when it executes a transition to history. Applicable only to QMsm...
Definition qp.h:573

[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 QM_TRAN_HIST() macro. The arguments of this macro are the history variable for the given state and the "tran-action table" specified in step [3]

Backward Traceability
Forward Traceability

Interaction ViewpointTime Viewpoint