QP/C++  7.3.4
Real-Time Embedded Framework
Loading...
Searching...
No Matches
State Machines

Purpose and Scope

The behavior of each active object in QP Framework is specified by means of a hierarchical state machine (UML statechart), which is the most effective and elegant technique of describing event-driven behavior. The most important innovation of UML state machines over classical finite state machines (FSMs) is the hierarchical state nesting. The value of state nesting lies in avoiding repetitions, which are inevitable in the traditional "flat" FSM formalism and are the main reason for the "state-transition explosion" in FSMs. The semantics of state nesting allow substates to define only the differences of behavior from the superstates, thus promoting sharing and reusing behavior.

The Quantum Leaps Application Note A Crash Course in UML State Machines introduces the main state machine concepts backed up by examples.

Note
The hallmark of the QP Framework implementation of UML state machines is traceability, which is direct, precise, and unambiguous mapping of every state machine element to human-readable, portable code. Preserving the traceability from requirements through design to code is essential for mission-critical systems, such as medical devices or avionic systems.

This section describes how to implement hierarchical state machines with the QP™/C real-time embedded framework, which is quite a mechanical process consisting of just a few simple rules. (In fact, the process of coding state machines in QP™/C has been automated by the QM model-based design and code-generating tool.)

To focus this discussion, this section uses the Calculator example, located in the directory qpcpp/examples/workstation/calc. This example has been used in the PSiCC2 book (Section 4.6 "Summary of Steps for Implementing HSMs with QEP")

This section explains how to code the following (marked) elements of a hierarchical state machine:

Fragment of the Calculator hierarchical state machine

[1] The top-most initial pseudo-state

[2] A state (nested in the implicit top state)

[3] An entry action to a state

[4] An exit action from a state

[5] An initial transition nested in a state

[6] A regular state transition

[7] A state (substate) nested in another state (superstate)

[8] Even more deeply nested substate

[9] A choice point with a guard

Note
This section describes the QHsm state machine implementation strategy, suitable for manual coding of hierarchical state machines in QP™/C. The alternative QMsm state machine implementation strategy, which QP™/C also supports, is not covered in this section, as the code needs to be generated automatically by the QM modeling tool.

State Machine Declaration

Hierarchical state machines are represented in QP™/C as subclasses of the QHsm abstract base class, which is defined in the header file qpc\include\qep.h. Please note that abstract classes like ::QMsm, ::QActive and ::QMActive are also subclasses of ::QHsm, so their subclasses also can have state machines.

struct Calc {
/* protected */
[1] QHsm super; /* inherit QHsm */
/* private: */
[2] double m_operand1;
uint8_t m_operator;
};
/* public: */
[3] Calc_ctor(Calc * const me) { /* constructor */
[4] QHsm_ctor(&me->super, &Calc_initial) /* superclass' constructor */
}
/* protected: */
[5] QState Calc_initial(Calc * const me, void const * const par);
[6] QState Calc_on(Calc * const me, QEvt const * const e);
QState Calc_ready(Calc * const me, QEvt const * const e);
QState Calc_result(Calc * const me, QEvt const * const e);
QState Calc_begin(Calc * const me, QEvt const * const e);
. . .

[1] Class Calc (Calculator) derives from ::QHsm, so it can have a state machine

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

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

[4] The constructor must call the appropriate superclass' constructor. The superclass' constructor takes the top-most a pointer to the Calc_initial pseudo-state (see step [5]), which binds the state-machine to the class.

[5] Each state machine must have exactly one initial pseudo-state, which by convention should be always called initial. The initial pseudo-state is declared with the shown parameter list.

[6] All other state handlers are declared with the shown signature.

State Machine Definition

The definition of the state machine class is the actual code for your state machine. You need to define (i.e., write the code for) all "state-handler" member functions you declared in the state machine class declaration.

One important aspect to realize about coding "state-handler" functions is that they are always called during the process of dispatching events. The purpose of the "state-handlers" is to perform your specific actions and then to tell the event processor what needs to be done with the state machine. For example, if your "state-handler" performs a state transition, it executes some actions and then it calls the special QHsm_tran_(<target>) function, where it specifies the <target> state of this state transition. The state-handler then returns the status from the tran() function, 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. The following code examples should make all this clearer.

Purpose and Scope