Back to Home

Quantum Leaps QP and UML statecharts

UML · C · C ++ · Quantum Leaps · QP · event programming · RTOS · state machine

Quantum Leaps QP and UML statecharts

Foreword


This article, it seems to me, will be of interest to those who are familiar with UML state and sequence diagrams (statecharts diagram and sequence diagram), as well as event-driven programming (event-driven programming).

Introduction


The Quantum Leaps cross-platform QP framework (Quantum Platform) is presented by its creators as a means of developing RTOS in C / C ++. It will significantly increase the development speed and reliability of applications, and also has powerful debugging and logging tools. To all this is added the fact that QP is very flexible and easy: it is divided into many modules, almost each of which can be implemented by itself when building the framework or using the proposed solution; many settings are made during precompilation. A program written using QP is an implementation of a collection of UML statecharts.

QP-components


This article is an implicit review of the QEP module, which is the basis of the framework (QEP implements event processing), and was written with the aim of encouraging further study.

Event Processing System in QP


To implement state diagrams, QP provides a UML-compatible event processing system. But you should pay attention to one circumstance: the framework only supports local transitions, that is, a transition to a substate is not accompanied by an exit event, and a transition to a superstate does not raise an entry event. In UML 2, for compatibility with previous versions, the ability to make external transitions was left. In earlier versions of the language, only external transitions are available: any transition is accompanied by an exit and entry event for the current and target states, respectively.

image

QP offers the following classes to the
developer : QFsm for implementing a single-level finite state machine (without superstates)
QHsm for implementing a hierarchical automaton (with superstates)
The objects of these classes store the current state of the UML diagram, and have a public method void dispatch(const QEvent* e)for handling the event.
QP also provides the QActive class , which by default is an extension of QHsm (can be changed to QFsm ). Objects of this class, called active objects, implement the ability of the machine to work in a separate thread of execution.

An event in QP is an object of the QEvent class or its descendants. Each event is characterized by a signal - a sig field that distinguishes one type of event from another.

A state in QP is a function (or a static method), a pointer to which has the form:,
typedef QState (*QStateHandler)(void *me, QEvent const *e)where me is a context (an automaton object),e - received event.

The framework provides for the state three main ways of processing the event:
  • Q_HANDLED () , if you just need to process the event without making a transition;
  • Q_TRAN (<address of the next state>) - make a transition;
  • Q_SUPER (<superstate address>) - pass to superstate. Available only for QHsm vending machines.

QP also has special events that can be generated during the transition. The order of occurrence of these events and the sequence of actions during the transition are as follows:
  1. atomic transition actions are performed;
  2. exit event ( Q_EXIT_SIG ). Occurs in the current state when exiting it. If, in addition, the superstate is exited, then exit for this superstate occurs immediately after exit for the current one. The exit event can only be processed ( Q_HANDLED );
  3. the machine goes into the target state;
  4. entry event ( Q_ENTRY_SIG ). Occurs when entering a state. This event can only be processed;
  5. event with a signal Q_INIT_SIG (only for QHsm -automats) - an initiating event. It always arises. Serves to make the transition ( Q_TRAN ) to the substate, but in principle it can also be simply processed;

General view of QFsm states:
static QState state1(Fsm* me, const QEvent* e) {
    switch(e->sig) {
        case Q_ENTRY_SIG: {
            /* processing */
            return Q_HANDLED();
        }
        case EVENT1_SIG: {
            /* processing */
            return Q_TRAN(&state2);
        }
        case EVENT2_SIG: {
            /* processing */
            return Q_HANDLED();
        }
        case Q_EXIT_SIG: {
            /* processing */
            return Q_HANDLED();
        }
    }
    return Q_HANDLED();
}

* This source code was highlighted with Source Code Highlighter.


General view of QHsm states:
static QState state1(Hsm* me, const QEvent* e) {
    switch(e->sig) {
        case Q_ENTRY_SIG: {
            /* processing */
            return Q_HANDLED();
        }
        case Q_INIT_SIG: {
            /* processing */
            return Q_TRAN(&state1_substate1); // return Q_HANDLED();
        }
        case EVENT1_SIG: {
            /* processing */
            return Q_TRAN(&state2);
        }
        case EVENT2_SIG: {
            /* processing */
            return Q_HANDLED();
        }
        case Q_EXIT_SIG: {
            /* processing */
            return Q_HANDLED();
        }
    }
    return Q_SUPER(&superstate1);
}

* This source code was highlighted with Source Code Highlighter.


Special events of the type Q_ENTRY_SIG , Q_INIT_SIG , Q_EXIT_SIG exist within the same state and are not delegated to the superstate; it is not necessary to process them.

Example


Consider a system with one active object ( sm ) and one-way impact on it. In this case, you can do without using the multitasking module (QK) and the QActive class . Let an object sm have a state diagram, which is a hierarchical finite state machine. This is how you can implement this task using QP:








// сигналы
enum Signals {
    PROCEED_SIG = Q_USER_SIG,
    CANCEL_SIG,
};

// класс событий, соответствующий сигналу PROCEED_SIG
struct ProceedEvt : public QEvent {
    ProceedEvt(int value = 0) : value(value) { sig = PROCEED_SIG; }
    int value;
};

// класс событий, соответствующий сигналу CANCEL_SIG
struct CancelEvt : public QEvent {
    CancelEvt() { sig = CANCEL_SIG; }
};

// класс иерархического автомата sm
class Hsm : public QHsm {
    public:
        Hsm() : QHsm((QStateHandler)&initial) { init(); }

    private:
        // псевдосостояние initial state
        static QState initial(Hsm* me, const QEvent* e) {
            return Q_TRAN(&superState);
        }

        static QState superState(Hsm* me, const QEvent* e) {
            switch (e->sig) {
                case Q_ENTRY_SIG: {
                    me->count = 10;
                    return Q_HANDLED();
                }
                case Q_INIT_SIG: {
                    return Q_TRAN(&stateA);
                }
                case CANCEL_SIG: {
                    return Q_TRAN(&stateC);
                }
            }

            /* QHsm::top - самое верхнее суперсостояние,
             * которое просто возвращает Q_HANDLED(). */
            return Q_SUPER(&QHsm::top);
        }

        static QState stateA(Hsm* me, const QEvent* e) {
            switch (e->sig) {
                case PROCEED_SIG: {
                    return Q_TRAN(&stateB);
                }
            }
            return Q_SUPER(&superState);
        }

        static QState stateB(Hsm* me, const QEvent* e) {
            switch (e->sig) {
                case PROCEED_SIG: {
                    if (me->count > 1) {
                        me->count *= 2;
                        return Q_TRAN(&stateA);
                    }
                    ++me->count;
                    return Q_HANDLED();
                }
                case Q_EXIT_SIG: {
                    cout << "count = " << me->count << endl;
                    me->count = 0;
                    return Q_HANDLED();
                }
            }
            return Q_SUPER(&superState);
        }

        static QState stateC(Hsm* me, const QEvent* e) {
            switch (e->sig) {
                case PROCEED_SIG: {
                    if (static_cast(e)->value == 1) {
                        return Q_TRAN(&superState);
                    }
                    break;
                }
            }
            return Q_SUPER(&QHsm::top);
        }

        int count;
};

* This source code was highlighted with Source Code Highlighter.


Now create an sm object of class Hsm (see the sequence diagram above) and process a dozen events:
int main(int argc, char* argv[]) {
    Hsm sm; // stateA, count = 10

    for (int i = 0; i < 2; ++i) {
        sm.dispatch(&ProceedEvt());        // stateB, count = 10
        sm.dispatch(&ProceedEvt());        // stateA, count = 0
        sm.dispatch(&ProceedEvt());        // stateB, count = 0
        sm.dispatch(&ProceedEvt());        // stateB, count = 1
        sm.dispatch(&ProceedEvt());        // stateB, count = 2
        sm.dispatch(&ProceedEvt());        // stateA, count = 0
        sm.dispatch(&ProceedEvt());        // stateB, count = 0
        sm.dispatch(&ProceedEvt());        // stateB, count = 1
        sm.dispatch(&ProceedEvt());        // stateB, count = 2
        sm.dispatch(&CancelEvt());        // stateC, count = 2
        sm.dispatch(&CancelEvt());        // stateC, count = 2
        sm.dispatch(&ProceedEvt());        // stateC, count = 2
        sm.dispatch(&ProceedEvt(1));        // stateA, count = 10
    }

    return 0;
}

* This source code was highlighted with Source Code Highlighter.


Conclusion


Other means of UML statecharts: pseudo-states initial, final, history, deep history and others; deferred event processing, the orthogonal component of the state, the hidden state, etc., are quite easily implemented using “state design patterns”, five of which are given in the book, which I will discuss below.

Benefits of QP
  • the principle of use in C is almost no different from C ++;
  • is opensource, source codes are well commented;
  • QP has already been ported to many systems, particularly GNU Linux systems;
  • there is a tool for modeling QM state diagrams from Quantum Leaps;
  • Quantum Leaps products are used by fairly large companies .

About the book

QP comes with a large book entitled “Practical UML Statecharts in C / C ++, Second Edition: Event-Driven Programming for Embedded Systems” from the founder of Quantum Leaps, which is divided into 2 parts: the first talks about how to use the framework, and the second focuses on how it works, how to sharpen QP for your system, how to use the QS tracer (QSpy), and what QP-nano is.

The tutorial also compares the use of the framework, state pattern, and switch method.

The book costs money, but can be found on the Internet for "free download."

References

Read Next