This QP/C Tutorial is adapted from Chapter 1 of Practical UML Statecharts in C/C++, Second Edition
by Miro Samek, the founder and president of Quantum Leaps, LLC.
Prev: 2. Let's Play
Next: 4. Designing an Event-Driven Application
Perhaps the best place to start the explanation of the "Fly 'n' Shoot" application code is the main() function, located in the file main.c. Unless indicated otherwise in this Tutorial, you can browse the code either in the DOS version, or the ARM-Cortex version, because the application source code is identical in both. The complete main.c file is shown in Listing 3-1
Listing 3-1 The file main.c of the "Fly 'n' Shoot" game application.
(1) #include "qp_port.h" /* the QP port */ (2) #include "bsp.h" /* Board Support Package */ (3) #include "game.h" /* this application */ /* Local-scope objects -----------------------------------------------------*/ (4) static QEvent const * l_missileQueueSto[2]; /* event queue */ (5) static QEvent const * l_shipQueueSto[3]; /* event queue */ (6) static QEvent const * l_tunnelQueueSto[GAME_MINES_MAX + 5]; /* event queue */ (7) static ObjectPosEvt l_smlPoolSto[GAME_MINES_MAX + 8]; /* small-size pool */ (8) static ObjectImageEvt l_medPoolSto[GAME_MINES_MAX + 8]; /* medium-size pool */ (9) static QSubscrList l_subscrSto[MAX_PUB_SIG]; /* publish-subscribe */ /*..........................................................................*/ void main(int argc, char *argv[]) { /* explicitly invoke the active objects' ctors... */ (10) Missile_ctor(); (11) Ship_ctor(); (12) Tunnel_ctor(); (13) BSP_init(argc, argv); /* initialize the Board Support Package */ (14) QF_init(); /* initialize the framework and the underlying RT kernel */ /* initialize the event pools... */ (15) QF_poolInit(l_smlPoolSto, sizeof(l_smlPoolSto), sizeof(l_smlPoolSto[0])); (16) QF_poolInit(l_medPoolSto, sizeof(l_medPoolSto), sizeof(l_medPoolSto[0])); (17) QF_psInit(l_subscrSto, Q_DIM(l_subscrSto)); /* init publish-subscribe */ /* start the active objects... */ (18) QActive_start(AO_Missile,/* global pointer to the Missile active object */ 1, /* priority (lowest) */ l_missileQueueSto, Q_DIM(l_missileQueueSto), /* evt queue */ (void *)0, 0, /* no per-thread stack */ (QEvent *)0); /* no initialization event */ (19) QActive_start(AO_Ship, /* global pointer to the Ship active object */ 2, /* priority */ l_shipQueueSto, Q_DIM(l_shipQueueSto), /* evt queue */ (void *)0, 0, /* no per-thread stack */ (QEvent *)0); /* no initialization event */ (20) QActive_start(AO_Tunnel, /* global pointer to the Tunnel active object */ 3, /* priority */ l_tunnelQueueSto, Q_DIM(l_tunnelQueueSto), /* evt queue */ (void *)0, 0, /* no per-thread stack */ (QEvent *)0); /* no initialization event */ (21) QF_run(); /* run the QF application */ }
<qpc>\ports\80x86\dos\watcom\l\, and the ARM-Cortex version from the directory <qpc>\ports\arm-cortex\vanilla\iar\.The QP event-driven platform is a collection of components, such as the QEP event processor that executes state machines according to the UML semantics and the QF real-time framework that implements the active object computing model. Active objects in QF are encapsulated state machines (each with an event queue, a separate task context, and a unique priority) that communicate with one another asynchronously by sending and receiving events, while QF handles all the details of thread-safe event exchange and queuing. Within an active object, the events are processed by the QEP event processor sequentially in a run-to-completion (RTC) fashion, meaning that processing of one event must necessarily complete before processing the next event.
The QF real-time framework supports two event delivery mechanisms: the simple direct event posting to active objects, and the more advanced mechanism called publish-subscribe that decouples event producers from the consumers. In the publish-subscribe mechanism, active objects subscribe to events by the framework. Event producers publish the events to the framework. Upon each publication request, the framework delivers the event to all active objects that had subscribed to that event type. One obvious implication of publish-subscribe is that the framework must store the subscriber information, whereas it must be possible to handle multiple subscribers to any give event type. The event delivery mechanisms are described in Chapters 6 and 7 of Practical UML Statecharts in C/C++, Second Edition.
main()).BSP_init() initializes the board and is defined in the bsp.c file.The utility macro Q_DIM(a) provides the dimension of a one-dimensional array a[] computed as sizeof(a)/sizeof(a[0]), which is a compile-time constant. The use of this macro simplifies the code because it allows me to eliminate many define constants that otherwise I would need to provide for the dimensions of various arrays. I can simply hard-code the dimension right in the definition of an array, which is the only place that I specify it. I then use the macro Q_DIM() whenever I need this dimension in the code.
I like to keep the code and data of every active object strictly encapsulated within its own C-file. For example, all code and data for the active object Ship are encapsulated in the file ship.c, with the external interface consisting of the function Ship_ctor() and the pointer AO_Ship.
After the call to QF_run() the framework is in full control. The framework executes the application by calling your code, not the other way around. The function QF_run() never returns the control back to main(). In the DOS version of the "Fly 'n' Shoot" game, you can terminate the application by pressing the ESC key, in which case QF_run() exits to DOS, but not to main(). In an embedded system, such as the ARM-Cortex board, QF_run() runs forever or till the power is removed, whichever comes first.
Prev: 2. Let's Play
Next: 4. Designing an Event-Driven Application
Copyright © 2002-2010 Quantum Leaps, LLC. All Rights Reserved.
http://www.state-machine.com
1.6.3