6. Signals, Events, and Active Objects

This QP-nano 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.

qp_tutorial.jpg

Prev: 5. Elaborating State Machines of Active Objects
Next: 7. Coding Hierarchical State Machines

In QP-nano, event signals are enumerated just like in the full-version QP. The only limitation is that signal values in QP-nano cannot exceed 255, because signals are always represented in a single byte.

In QP-nano, you cannot specify arbitrary event parameters, so you don't derive events as in full-version QP. Instead, all events in QP-nano are simply instances of the QEvent structure, which contains the fixed-size scalar parameter configured according to your definition of Q_PARAM_SIZE (see Listing Listing 3-2(2)). On the other hand, active objects in QP-nano are derived from the QActive base structure, just like they are in the full-version QP (see Encapsulation and Single Inheritance in C). One of the main concerns with respect to active object structures is to keep them encapsulated. In all QP-nano examples, including the "Fly 'n' Shoot" game, I demonstrate a technique to keep the active object structures and state machines completely opaque. I describe this technique in the explanation section following Listing 6-1, which shows the header file game.h included by all components of the "Fly 'n' Shoot" application.

6.1 Enumerating Event Signals and Encapsulating Active Objects

Because events are explicitly shared among most of the application components, it is convenient to declare them in the separate header file game.h shown Listing 6-1. The explanation section immediately following the listing illuminates the interesting points.

Listing 6-1 Signals, event structures, and active object interfaces defined in file game.h.

 (1) enum GameSignals {                              /* signals used in the game */
 (2)     TIME_TICK_SIG = Q_USER_SIG,                  /* published from tick ISR */
         PLAYER_TRIGGER_SIG, /* published by Player (ISR) to trigger the Missile */
         PLAYER_QUIT_SIG,          /* published by Player (ISR) to quit the game */
         GAME_OVER_SIG,          /* published by Ship when it finishes exploding */
         PLAYER_SHIP_MOVE_SIG,  /* posted by Player (ISR) to the Ship to move it */
         BLINK_TIMEOUT_SIG,           /* signal for Tunnel's blink timeout event */
         SCREEN_TIMEOUT_SIG,         /* signal for Tunnel's screen timeout event */
         TAKE_OFF_SIG,    /* from Tunnel to Ship to grant permission to take off */
         HIT_WALL_SIG,            /* from Tunnel to Ship when Ship hits the wall */
         HIT_MINE_SIG,     /* from Mine to Ship or Missile when it hits the mine */
         SHIP_IMG_SIG,     /* from Ship to the Tunnel to draw and check for hits */
         MISSILE_IMG_SIG,  /* from Missile the Tunnel to draw and check for hits */
         MINE_IMG_SIG,            /* sent by Mine to the Tunnel to draw the mine */
         MISSILE_FIRE_SIG,                /* sent by Ship to the Missile to fire */
         DESTROYED_MINE_SIG, /* from Missile to Ship when Missile destroyed Mine */
         EXPLOSION_SIG,     /* from any exploding object to render the explosion */
         MINE_PLANT_SIG,                  /* from Tunnel to the Mine to plant it */
         MINE_DISABLED_SIG,      /* from Mine to Tunnel when it becomes disabled */
         MINE_RECYCLE_SIG,         /* sent by Tunnel to Mine to recycle the mine */
         SCORE_SIG    /* from Ship to Tunnel to adjust game level based on score */
     };

     /* active objects ..........................................................*/
 (3) extern struct TunnelTag  AO_Tunnel;
 (4) extern struct ShipTag    AO_Ship;
 (5) extern struct MissileTag AO_Missile;

 (6) void Tunnel_ctor (void);
 (7) void Ship_ctor   (void);
 (8) void Missile_ctor(uint8_t speed);

     /* common constants and shared helper functions ............................*/
     . . .
Note:
The active object structures (e.g., struct TunnelTag) do not need to be defined globally in the application header file. The QF_active[] array needs only pointers to the active objects (see Listing 3-1(9-11)), which the compiler can resolve without knowing the full definition of the active object structure.

I never declare active object structures globally. Instead, I declare the active object structures in the file scope of the specific active object module (e.g., struct TunnelTag is declared in the tunnel.c file scope). That way, I can be sure that each active object remains fully encapsulated.

Listing 6-2 Generating and posting events from the ISRs in bsp.c for the ARM-Cortex board.

 (1) static void interrupt ISR_tmr(void) { /* 80x86 enters ISRs with int. locked */

 (2)     QF_tick();                             /* process all armed time events */

 (3)     QActive_postISR((QActive *)&AO_Tunnel,  TIME_TICK_SIG, 0);
 (4)     QActive_postISR((QActive *)&AO_Ship,    TIME_TICK_SIG, 0);
 (5)     QActive_postISR((QActive *)&AO_Missile, TIME_TICK_SIG, 0);

         outp(0x20, 0x20);                        /* write EOI to the master PIC */
     }
     . . .
Note:
QP-nano provides different services for ISRs and different for the task level. You can only call two QP-nano functions from interrupts: QF_tick() and QActive_postISR(). Conversely, you should never call these two functions from the task level. This separation of APIs is closely related to the separate interrupt locking policies for tasks and interrupts in QP-nano.

Prev: 5. Elaborating State Machines of Active Objects
Next: 7. Coding Hierarchical State Machines

logo_ql_TM.jpg

Copyright © 2002-2010 Quantum Leaps, LLC. All Rights Reserved.
http://www.quantum-leaps.com

Generated on Tue Mar 16 19:41:33 2010 for QP-nano by  doxygen 1.6.3