📜 ⬆️ ⬇️

Quantum Leaps QP and UML statecharts

Foreword


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

Introduction


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

QP-components

A program written using QP is an implementation of a collection of UML statecharts state diagrams.
The article is an implicit consideration of the QEP module, which is the basis of the framework (QEP implements event processing), and is written with the aim of encouraging further study.
')

Event handling system in QP


For the implementation of state diagrams, QP provides a UML-compatible event processing system. But you should pay attention to one circumstance: only local transitions are supported by the framework, i.e. the transition to the substate is not accompanied by an exit event, and the transition to the superstate does not trigger the entry event. In UML 2, for compatibility with previous versions, it was possible to make external transitions. 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 developer offers the following classes:
QFsm for the implementation of a single-level finite state machine (without superstate)
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 void dispatch ( const QEvent * e ) method void dispatch ( const QEvent * e ) for handling the event.
QP also provides the QActive class, which by default is a QHsm extension (can be changed to QFsm ). Objects of this class, called active objects, realize the possibility of an automaton operating in a separate execution thread.

An event in QP is objects of the QEvent class or its heirs. Each event is characterized by a signal — the sig field, which distinguishes one type of event from another.

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

The framework provides for the state three main ways to handle events:

QP also has special events that can be generated during the transition. The order of occurrence of these events and the sequence of actions when making the transition are as follows:
  1. atomic transition actions are performed;
  2. exit event ( Q_EXIT_SIG ). Occurs in the current state when exiting. If, in addition, there is an exit from the super-state, then an exit for this super-state occurs immediately after the exit for the current one. The exit event can only be processed ( Q_HANDLED );
  3. the machine goes to the target state;
  4. event entry ( Q_ENTRY_SIG ). Occurs when entering the state. This event can only be processed;
  5. an event with a Q_INIT_SIG signal (only for QHsm machines) is an initiating event. Always arises. It serves to make a transition ( Q_TRAN ) to a substate, but in principle it can also be easily 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 type Q_ENTRY_SIG , Q_INIT_SIG , Q_EXIT_SIG exist within one state and are not delegated to the super state, they are not necessary to process them.

Example


Consider a system with one active object ( sm ) and one-sided impact on it. In this case, you can do without the use of the multitasking module (QK) and the QActive class.



Let sm be a state diagram that is a hierarchical finite automaton.



This is how you can accomplish 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< const ProceedEvt*>(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 we will create the sm object of the Hsm class (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


The rest of the UML statecharts are: initial, final, history, deep history and other pseudostates; deferred event processing, the orthogonal state component, the hidden state, and so on — are quite simply implemented with the help of “state design patterns”, five of which are listed in the book, which I will discuss below.

QP benefits

About the book

QP comes with a big book “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 one talks about how to use the framework, and the second is about how it works, how to sharpen a QP for your system, how to use a QS tracer (QSpy), and what a QP-nano is.

Also in the tutorial are comparisons of the use of the framework, the state pattern and the switch method.

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

Links

Source: https://habr.com/ru/post/114239/


All Articles