/** \page tutorial_page QP/C Tutorial This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg This Tutorial presents an example project implemented entirely with the QP/C event-driven platform using UML state machines and the event-driven paradigm. The example application is an interactive "Fly 'n' Shoot"-type game. My aim in this section is to show the essential elements of the method in a real, nontrivial program, but without getting bogged down in details, rules, and exceptions. At this point, I am not trying to be complete or even precise, although this example is meant to show a good design and the recommended coding style. I don't assume that you know much about UML state machines, the UML notation, or the event-driven programming. I will either briefly introduce the concepts, as needed, or refer you to the the \ref PSiCC2 book for more details. The example "Fly 'n' Shoot" game is based on the "Quickstart" application provided in source code with the ARM Cortex-M3 LM3S811 evaluation kit (see \ref F2s2 "Figure 2-2") from Luminary Micro (http://www.luminarymicro.com). I was trying to make the "Fly 'n' Shoot" example behave quite similarly to the original Luminary Micro "Quickstart" application, so that you can directly compare the event-driven approach with the traditional solution to essentially the same problem specification. - \subpage installing - \subpage lets_play - \subpage main_function - \subpage design - \subpage active_objects - \subpage events - \subpage coding_hsm - \subpage execution - \subpage tracing - \subpage comparison - \subpage summary \note The standard QP/C distribution contains two versions of the game. A DOS version is provided for the standard Windows-based PC so that you don't need any special embedded board to play the game and experiment with the code. Also provided is an embedded version for the inexpensive ARM Corterx-M3-based LM3S811 evaluation kit from Luminary Micro. Both the PC and the ARM-Cortex versions use the exact same source code for all application components and differ only in the Board Support Package (BSP). Next: \ref installing \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page installing 1. Installing QP/C and Building QP Libraries and Applications This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref tutorial_page \n Next: \ref lets_play QP/C is distributed in a simple platform-independent ZIP file, or in a self-extracting Windows executable. Either way, installing QP requires simply decompressing the provided archive into a directory of your choice (e.g., \c <qpc> for QP/C). The Section \ref files_page describes the directories and files included in the standard QP/C distribution. Specifically to the "Fly 'n' Shoot" example, the companion code contains two versions of the game. I provide a DOS version for the standard Windows-based PC (see \ref F2s1 "Figure 2-1") so that you don't need any special embedded board to play the game and experiment with the code. \note I've chosen the legacy 16-bit DOS platform because it allows programming a standard PC at the bare-metal level. Without leaving your desktop, you can work with interrupts, directly manipulate CPU registers, and directly access the I/O space. No other modern 32-bit development environment for the standard PC allows this much so easily. The ubiquitous PC running under DOS (or a DOS console within any variant of Windows) is as close as it gets to emulate embedded software development on the commodity 80x86 hardware. Additionally, you can use free, mature tools, such as the Open Watcom C/C++ compiler. I also provide an embedded version for the inexpensive ARM Corterx-M3-based ARM Cortex-M3 LM3S811 evaluation kit from Luminary Micro (see \ref F2s2 "Figure 2-2"). Both the PC and ARM-Cortex versions use the exact same source code for all application components and differ only in the Board Support Package (BSP). \section building_lib 1.1 Building QP Libraries \note The pre-compiled QP libraries are provided in the standard QP distribution (see \ref files_page), so you can start experimenting with all examples without building the QP libraries. However, if you want to re-build the QP libraries, this section provides the details. \ref F1s1 "Figure 1-1" illustrates the steps required to build the QF library. The process of building other QP components, such as QEP or QK, is essentially identical. The key point of the design is that all platform-independent QF source files include the same \c qf_port.h header file as the application source files (see \ref F1s1 "Figure 1-1"). At this point you can clearly see that the Platfrom Abstraction Layer (PAL) provided in QP plays the dual role of facilitating the porting of QP as well as using it in the applications. \ref F1s1 "Figure 1-1" shows also that every QP component, such as QF, can contain a platform-specific source file (\c qf_port.c in this case). The platform-specific source file is optional and many ports don't require it. \anchor F1s1 \image html Fig8.02.jpg "Figure 1-1 Building the QF library." The standard QP ports often contain a simple \c make.bat script or a \c Makefile for building all the QP libraries for the port. You typically can choose the build configuration by providing a target to the \c make.bat script or to the \c Makefile. The default target is "dbg". Other possible targets are "rel", and "spy". The following table summarizes the targets accepted by the \c make.bat scripts or the \c Makefiles.
 Build Configuration  Build Command
 Debug  make
 Release  make rel
 Spy  make spy
\note All QP components are designed to be deployed in fine-granularity object libraries. QP libraries allow the linker to eliminate any unreferenced QP code at link time, which results in automatic scaling of every QP component for a wide range of applications. This approach eliminates the need to manually configure and recompile the QP source code for each application at hand. \section building_app 1.2 Building QP Applications \note The standard QP distribution contains pre-compiled examples (see \ref files_page), so you can start experimenting with all examples without building them. However, if you want to re-build the QP examples, this section provides the details. \ref F1s2 "Figure 1-2" shows the process of building a QP application. Each QP component requires inclusion of only one platform-specific header file and linking one platform-specific library. For example, to use the QF real-time framework, you need to include the \c qf_port.h header file and you need to link the \c qf.lib library file from the specific QP port directory. It really doesn't get any simpler than that. \anchor F1s2 \image html Fig8.01.jpg "Figure 1-2 Building a QP-based Application." The QP port you are using is determined by the directory branch in which the \c qf_port.h header file and the QF library file are located. Section \ref files_page shows some examples of such port directories. Typically you need to instruct the C/C++ compiler to include header files from the specific QP port directory and also from the platform-independent include directory \c <qpc>\\include\\. I strongly discourage hard-coding full path-names of the include files in your source code. You should simply include the QP port header file (#include "qf_port.h") without any path. Then you specify to the compiler to search the QP port directory for include files, typically through the -I option. Prev: \ref tutorial_page \n Next: \ref lets_play \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page lets_play 2. Let's Play This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref installing \n Next: \ref main_function The following description of the "Fly 'n' Shoot" game serves the dual purpose of explaining how to play the game and as the problem specification for the purpose of designing and implementing the software later in this Tutorial. To accomplish these two goals I need to be quite detailed, so please bear with me. Your objective in the game is to navigate a space ship through an endless horizontal tunnel with mines. Any collision with the tunnel or the mine destroys the ship. You can move the ship up and down with UP-arrow and DOWN-arrow keys on the PC (see \ref F2s1 "Figure 2-1") or the potentiometer wheel on the LM3S811 board (see \ref F2s2 "Figure 2-2"). You can also fire a missile to destroy the mines in the tunnel by pressing the SPACE-bar on the PC or the User button on the LM3S811 board. Score accumulates for survival (at the rate of 30 points per second) and destroying the mines. The game lasts for only one ship. The game starts in a demo mode, where the tunnel walls scroll at the normal pace from right to left and the "Press Button" text flashes in the middle of the screen. You need to generate the "fire missile" event for the game to begin (press SPACE-bar on the PC and the User Button on the LM3S811 board). You can have only one missile in flight at a time, so trying to fire a missile while it is already flying has no effect. Hitting the tunnel wall with the missile brings you no points, but you earn extra points for destroying the mines. The game has two types of mines with different behavior. In the original Luminary "Quickstart" application both types of mines behave the same, but I wanted to demonstrate how state machines can elegantly handle differently behaving mines. Mine type-1 is small, but can be destroyed by hitting any of its pixels with the missile. You earn 25 points for destroying a mine type- Mine type-2 is bigger, but is nastier in that the missile can destroy it only by hitting its center, not any of the "tentacles". Of course, the ship is vulnerable to the whole mine. You earn 45 points for destroying a mine type 2. When your crash the ship, either by hitting a wall or a mine, the game ends and displays the flashing "Game Over" text as well as your final score. After 5 seconds of flashing, the "Game Over" screen changes back to the demo screen, where the game waits to be started again. Additionally the application contains a screen saver because the OLED display of the original LM3S811 board has burn-in characteristics similar to a CRT. The screen saver only becomes active if 20 seconds elapse in the demo mode without starting the game (i.e., the screen saver never appears during game play). The screen saver is a simple random-pixel-type, rather than the "Game of Life" algorithm used in the original Luminary "Quickstart" application. I've decided to simplify this aspect of the implementation, because the more elaborate pixel-mixing algorithm does not contribute any new or interesting behavior. After a minute of running the screen saver, the display turns blank and only a single random pixel shows on the screen. Again, this is a little difference from the original "Quickstart" application, which instead blanks the screen and starts flashing the User LED. I've changed this behavior because I have a better purpose for the User LED (to visualize the activity of the idle loop). \section DOS Running the DOS Version The "Fly 'n' Shoot" sample code for the DOS version (in C++) is located in \c <qpc>\\examples\\80x86\\dos\\watcom\\l\\game\\, directory, where \c <qpc> stands for the installation directory you chose to install the QP/C software. The compiled executable is provided, so you can run the game on any Windows-based PC by simply double-clicking on the executable game.exe located in the directory \c <qpc>\\examples\\80x86\\dos\\watcom\\l\\game\\dbg\\. \anchor F2s1 \image html Fig1.01.jpg "Figure 2-1 The Fly 'n' Shoot game running in a DOS window under Windows XP." The first screen you see is the game running in the demo mode with the text "Push Button" flashing in the middle of the display. At the top of the display you see a legend of keystrokes recognized by the application. You need to hit the SPACE key to start playing the game. Please press the ESC key to cleanly exit the application. If you run "Fly 'n' Shoot" in a window under Microsoft Windows, the animation effects in the game might appear a little jumpy, especially when compared to the ARM-Cortex version of the same game. You can make the application execute significantly smoother if you switch to the full-screen mode by pressing and holding the Alt key and then pressing the Enter key. You go back to the window mode by the same Alt-Enter key combination. As you can see in \ref F2s1 "Figure 2-1", the DOS version uses simply the standard VGA text mode to emulate the OLED display of the LM3S811 board. The lower part of the DOS screen is used as a matrix of 80x16 character-wide "pixels", which is a little less than the 96x16 pixels of the OLED display, but is still good enough to play the game. I specifically avoid employing any fancier graphics in this early example because I have a bigger fish to fry for you than to worry about the irrelevant complexities of programming graphics. My main goal is to make it easy for you to understand the event-driven code and experiment with it. To this end, I chose the Open Watcom toolset to build this example as well as several other examples in this book. Open Watcom it is available under a OSI-certified open source license that permits free commercial and non-commercial use. You can download Open Watcom C/C++ toolset for DOS from ftp://ftp.openwatcom.org/. Please select the \c open-watcom-c-dos-1.8.exe installer. Ready to print documentation in PDF format is also available from http://www.openwatcom.org/index.php/Manuals. The Open Watcom C/C++ toolset for DOS is distributed as a Windows installer. After you download the \c open-watcom-c-dos-1.8.exe file, please launch the installer and follow the instructions it provides. \note I strongly recommend that you install the Open Watcom toolset into the directory \c C:\\tools\\watcom\\. That way, you will be able to use directly the provided make scripts. If you choose to install Open Watcom into a different location, you can still use the make scripts supplied with the QP distribution, but you need to define the \c WATCOM environment variable. You should not install Open Watcom in the standard "Prgram Files" directory or any directory name with a space. To experinment with the "Fly 'n' Shoot" game code you can use any code editor to modify the source code. Then you re-build the application by means of the supplied \c make.bat script, which is located in the directory \c <qpc>\\examples\\80x86\\dos\\watcom\\l\\game\\. In the next section, I describe briefly how to run the embedded version of the game. If you are not interested in the ARM-Cortex version, please feel free to skip to the following Section \ref main_function, where I start explaining the application code. \section Cortex Running the ARM-Cortex Version In contrast to the "Fly 'n' Shoot" version for DOS running in the ancient real mode of the 80x86 processor, the exact same source code runs on one of the most modern processors in the industry: the ARM-Cortex. \anchor F2s2 \image html Fig1.02.jpg "Figure 2-2 The Fly 'n' Shoot game running on the ARM Cortex-M3 LM3S811 evaluation board." The sample code for the ARM Cortex-M3 LM3S811 board is located in \c <qpc>\\examples\\arm-cortex\\vanilla\\iar\\game-ev-lm3s811\\ directory, where \c <qpc> stands for the root directory you chose to install the accompanying software. The code for the ARM-Cortex kit has been compiled with the 32KB-limited Kickstart edition of the IAR Embedded Workbench for ARM (IAR EWARM) v 5.11, which is provided with the ARM Cortex-M3 EKI-LM3S811 kit. You can also download this software free of charge directly from IAR Systems (http://www.iar.com), after filling out an online registration. The installation of IAR EWARM is quite straightforward, as the software comes with the installation utility. You also need to install the USB drivers for the hardware debugger built into the LM3S811 board, as described in the documentation of the ARM Cortex-M3 LM3S811 kit. \note I strongly recommend that you install the IAR EWARM toolset into the directory \c C:\\tools\\iar\\arm_ks_5.11 That way, you will be able to use directly the provided EWARM workspace files and make scripts. Before you program the "Fly 'n' Shoot" game to the LM3S811 board, you might want to play a little with the original "Quickstart" application that comes pre-programmed with the LM3S811 kit. To program the "Fly 'n' Shoot" game to the flash memory of the LM3S811 board, you first connect the LM3S811 board to your PC with the USB cable provided in the kit and make sure that the Power LED is on (see \ref F2s2 "Figure 2-2"). Next, you need to launch the IAR Embedded Workbench and open the workspace game-ev-lm3s81eww located in \c <qpc>\\examples\\arm-cortex\\vanilla\\iar\\game-ev-lm3s811\\ directory. At this point your screen should look similar to the screenshot shown in \ref F2s3 "Figure 2-3". The game-ev-lm3s811 project is set up to use the LMI FTDI debugger, which is the piece of hardware integrated on the LM3S811 board (see \ref F2s2 "Figure 2-2"). You can verify this setup by opening the "Options" dialog box via the Project->Options menu. Within the "Options" dialog box, you need to select the Debugger category in the panel on the left. While you are at it, you could also verify that the flash loading is enabled by selecting the "Download" tab. The checked "Use flash loader(s)" checkbox means that the flash loader application provided by IAR will be first loaded to the RAM of the MCU, and this application will program the flash with the image of your application. To start the flash programming process, select the Project->Debug menu, or simply click on the Debug button (see \ref F2s3 "Figure 2-3") in the toolbar. The IAR Workbench should respond by showing the flash programming progress bar for several seconds, as shown in \ref F2s3 "Figure 2-3". Once the flash programming completes, the IAR EWARM switches to the IAR C-Spy debugger and the program should stop at the entry to main(). You can start playing the game either by hitting the "Go" button in the debugger, or you can close the debugger and reset the board by pressing the Reset button. Either way, "Fly 'n' Shoot" game is now permanently programmed into the LM3S811 board and will start automatically upon every power up. \anchor F2s3 \image html Fig1.03.jpg "Figure 2-3 Loading the Fly 'n' Shoot game into the flash of LM3S811 MCU with IAR EWARM IDE" The IAR Embedded Workbench environment allows you to experiment with the "Fly 'n' Shoot" code very easily. You can edit the files and recompile the application at a click of a button (F7). The only caveat is that the first time after the installation of the IAR toolset you need to build the Luminary Micro driver library for the LM3S811 MCU from the sources. You accomplish this by loading the workspace ek-lm3s81eww located in the directory \c <IAR-EWARM>\\ARM\\examples\\Luminary\\Cortex-M3\\boards\\ek-lm3s811, where <IAR-EWARM> stands for the directory name where you've installed the IAR toolset. In the ev-lm3s81eww workspace, you select the "driverlib - Debug" project from the drop-down list at the top of the Workspace panel, and then press F7 to build the library. Prev: \ref installing \n Next: \ref main_function \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page main_function 3. The main() Function This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref lets_play \n Next: \ref design Perhaps the best place to start the explanation of the "Fly 'n' Shoot" application code is the main() function, located in the file \c 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 \c main.c file is shown in \ref L3s1 "Listing 3-1" \note To explain code listings, I place numbers in parentheses at the interesting lines in the left margin of the listing. I then use these labels in the left margin of the explanation section that immediately follows the listing. Occasionally, to unambiguously refer to a line of a particular listing from sections of text other than the explanation section, I use the full reference consisting of the listing number followed by the label. For example, \ref L3s1 "Listing 3-1"(21) refers to the label (21) in \ref L3s1 "Listing 3-1" \anchor L3s1 Listing 3-1 The file main.c of the "Fly 'n' Shoot" game application. \code (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 */ } \endcode \li (1) The "Fly 'n' Shoot" game is an example of an application implemented with the QP event-driven platform. Every application C-file that uses QP must include the qp_port.h header file. This header file contains the specific adaptation of QP to the given processor, operating system, and compiler, which is called a port. Each QP port is located in a separate directory and the C compiler finds the right qp_port.h header file through the include search path provided to the compiler (typically via the -I compiler option). That way I don't need to change the application source code to recompile it for a different processor or compiler. I only need to instruct the compiler to look in a different QP port directory for the qp_port.h header file. For example, the DOS version includes the qp_port.h header file from the directory \c <qpc>\\ports\\80x86\\dos\\watcom\\l\\, and the ARM-Cortex version from the directory \c <qpc>\\ports\\arm-cortex\\vanilla\\iar\\. \li (2) The bsp.h header file contains the interface to the Board Support Package and is located in the application directory. \li (3) The game.h header file contains the declarations of events and other facilities shared among the components of the application. I will discuss this header file in the upcoming Section \ref events. This header file is located in the application directory. 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. \li (4-6) The application must provide storage for the event queues of all active objects used in the application. Here the storage is provided at compile time through the statically allocated arrays of immutable (const) pointers to events, because QF event queues hold just pointers to events, not events themselves. Events are represented as instances of the QEvent structure declared in the qp_port.h header file. Each event queue of an active object can have a different size and you need to decide this size based on your knowledge of the application. I discuss the event queues in Chapters 6 and 7 of \ref PSiCC2. \li (7-8) The application must also provide storage for event pools that the framework uses for fast and deterministic dynamic allocation of events. Each event pool manages can provide only fixed-size memory blocks. To avoid wasting memory by using oversized blocks for small events, the QF framework can manage up to three event pools of different block sizes (for small, medium, and large events). The "Fly 'n' Shoot" application uses only two out of the three possible event pools (the small and medium pools). 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 \ref PSiCC2. \li (9) The "Fly 'n' Shoot" application uses the publish-subscribe event delivery mechanism supported by QF, so it needs to provide the storage for the subscriber lists. The subscriber lists remembers which active objects have subscribed to which events. The size of the subscriber database depends on both the number of published events, which is specified in the MAX_PUB_SIG constant found in the game.h header file, and the maximum number of active objects allowed in the system, which is determined by the QF configuration parameter #QF_MAX_ACTIVE. \li (10-12) These functions perform an early initialization of the active objects in the system. They play the role of static "constructors", which in C you need to invoke explicitly. (C++ calls such static constructors implicitly before entering \c main()). \li (13) The function \c BSP_init() initializes the board and is defined in the bsp.c file. \li (14) The function QF_init() initializes the QF component and the underlying RTOS/kernel, if such software is used. You need to call QF_init() before you invoke any of QF services. \li (15-16) The function QF_poolInit() initializes the event pools. The parameters of this function are the pointer to the event pool storage, the size of this storage, and the block-size of this pool. You can call this function up to three times to initialize up to three event pools. The subsequent calls to QF_poolInit() must be made in the increasing order of block sizes. For instance, the small block-size pool must be initialized before the medium block-size pool. \li (17) The function QF_poolInit() initializes the publish-subscribe event delivery mechanism of QF. The parameters of this function are the pointer to the subscriber-list array and the dimension of this array. 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. \li (18-20) The function QActive_start() tells the QF framework to start managing an active object as part of the application. The function takes the following parameters: the pointer to the active object structure, the priority of the active object, the pointer to its event queue, the dimension (length) of that queue, and three other parameters that I explain in Chapter 7 of \ref PSiCC2, since they are not relevant at this point. The active object priorities in QF are numbered from 1 to #QF_MAX_ACTIVE, inclusive, where a higher priority number denotes higher urgency of the active object. The constant #QF_MAX_ACTIVE is defined in the QF port header file qf_port.h and currently cannot exceed 63. 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 \c Ship_ctor() and the pointer \c AO_Ship. \li (21) At this point, you have provided to the framework all the storage and information it needs to manage your application. The last thing you must do is to call the function QF_run() to pass the control to the framework. 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 \c main(). In an embedded system, such as the ARM-Cortex board, QF_run() runs forever or till the power is removed, whichever comes first. \note For best cross-platform portability, the source code uses consistently the UNIX end-of-line convention (lines are terminated with LF only, 0xA character). This convention seems to be working for all C/C++ compilers and cross-compilers, including legacy DOS-era tools. In contrast, the DOS/Windows end-of-line convention (lines terminated with the CR,LF, or 0xD,0xA pair of characters), is known to cause problems on UNIX-like platforms, especially in the multi-line preprocessor macros. Prev: \ref lets_play \n Next: \ref design \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page design 4. Designing an Event-Driven Application This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref main_function \n Next: \ref active_objects To proceed further with the explanation of the "Fly 'n' Shoot" application, I need to step up to the design level. At this point I need to explain how the application has been decomposed into the active objects, and how these objects exchange events to collectively deliver the functionality of the "Fly 'n' Shoot" game. In general, the decomposition of a problem into active objects is not trivial. As usual in any decomposition, your goal is to achieve possibly loose coupling among the active object components (ideally no sharing of any resources), and you also strive for minimizing the communication in terms of the frequency and size of exchanged events. In the case of the "Fly 'n' Shoot" game, I need first to identify all objects with reactive behavior (i.e. with a state machine). I applied the simplest object-oriented technique of identifying objects, which is to pick the frequently used nouns in the problem specification. From Section \ref lets_play, I identified Ship, Missile, Mines, and Tunnel. However, not every state machine in the system needs to be an active object (with a separate task context, an event queue, and a unique priority level), and merging them is a valid option when performance or space is needed. As an example of this idea, I ended up merging the Mines into the Tunnel active object, whereas I preserved the Mines as independent state machine components of the Tunnel active object. By doing so I applied the "Orthogonal Component" design pattern described in Chapter 5 of \ref PSiCC2. The next step in the event-driven application design is assigning responsibilities and resources to the identified active objects. The general design strategy for avoiding sharing of resources is to encapsulate each resource inside a dedicated active object and to let that object manage the resource for the rest of the application. That way, instead of sharing the resource directly, the rest of the application shares the dedicated active object via events. So, for example, I decided to put the Tunnel active object in charge of the display. Other active objects and state machine components, such as Ship, Missile and Mines, don't draw on the display directly, but rather send events to the Tunnel object with the request to render the Ship, Missile, or Mine bitmaps at the provided (x, y) coordinates of the display. With some understanding of the responsibilities and resource allocations to active object I can move on to devising the various scenarios of event exchanges among the objects. Perhaps the best instrument to aid the thinking process at this stage is the UML sequence diagram, such as the diagram depicted in \ref F4s1 "Figure 4-1". This particular sequence diagram shows the most common event exchange scenarios in the "Fly 'n' Shoot" game (the primary use cases, if you will). The explanation section immediately following the diagram illuminates the interesting points. \note A UML sequence diagram like \ref F4s1 "Figure 4-1" has two dimensions. Horizontally arranged boxes represent the various objects participating in the scenario whereas heavy boarders indicate active objects. As usual in the UML, the object name in underlined. Time flows down the page along the vertical dashed lines descending from the objects. Events are represented as horizontal arrows originating from the sending object and terminating at the receiving object. Optionally, thin rectangles around instance lines indicate focus of control. \anchor F4s1 \image html Fig1.04.jpg "Figure 4-1 The sequence diagram of the Fly 'n' Shoot game." \li (1) The \c TIME_TICK is the most important event in the game. This event is generated by the QF framework from the system time tick interrupt at a rate of 30 times per second, which is needed to drive a smooth animation of the display. Because the \c TIME_TICK event is of interest to virtually all objects in the application, it is published by the framework to all active objects. (The publish-subscribe event delivery in QF is described in Chapter 6 of \ref PSiCC2.) \li (2) Upon reception of the \c TIME_TICK event, the Ship object advances its position by one step and posts the event SHIP_IMG(x, y, bmp) to the Tunnel object. The SHIP_IMG event has parameters x and y, which are the coordinates of the Ship on the display, as well as the bitmap number bmp to draw at these coordinates. \li (3) The Missile object is not in flight yet, so it simply ignores the \c TIME_TICK event this time. \li (4) The Tunnel object performs the heaviest lifting for the \c TIME_TICK event. First, Tunnel redraws the entire display from the current frame buffer. This action performed 30 times per second provides the illusion of animation of the display. Next, the Tunnel clears the frame buffer and starts filling it up again for the next time frame. The Tunnel advances the tunnel walls by one step and copies the walls to the frame buffer. The Tunnel also dispatches the \c TIME_TICK event to all its Mine state machine components. \li (5) Each Mine advances its position by one step and posts the MINE_IMG(x, y, bmp) event to the Tunnel to render the appropriate Mine bitmap at the position (x, y) in the current frame buffer. Mines of type 1 send the bitmap number MINE1_BMP, while mines of type 2 send \c MINE2_BMP. \li (6) Upon reception of the SHIP_IMG(x, y, bmp) event from the Ship, the Tunnel object renders the specified bitmap in the frame buffer and checks for any collision between the ship bitmap and the tunnel walls. Tunnel also dispatches the original SHIP_IMG(x, y, bmp) event to all active Mines. \li (7) Each Mine determines if the Ship is in collision with that Mine. \li (8) The \c PLAYER_TRIGGER event is generated when the Player reliably presses the button (button press is debounced). This event is published by the QF framework and is delivered to the Ship and Tunnel objects, which both subscribe to the \c PLAYER_TRIGGER event. \li (9) Ship generates the MISSILE_FIRE(x, y) event to the Missile object. The parameters of this event are the current (x, y) coordinates of the Ship, which are the starting point for the Missile. \li (10) Tunnel receives the published \c PLAYER_TRIGGER event as well because Tunnel occasionally needs to start the game or terminate the screen saver mode based upon this stimulus. \li (11) Missile reacts to the MISSILE_FIRE(x, y) event by starting to fly, whereas it sets its initial position from the (x, y) event parameters delivered from the Ship. \li (12) This time around, the \c TIME_TICK event arrives while Missile is in flight. Missile posts the MISSILE_IMG(x, y, bmp) event to the Table. \li (13) Table renders the Missile bitmap in the current frame buffer and dispatches the MISSILE_IMG(x, y, bmp) event to all the Mines to let the Mines test for the collision with the Missile. This determination depends on the type of the Mine. In this scenario a particular Mine[n] object detects a hit and posts the HIT_MINE(score) event to the Missile. The Mine provides the score earned for destroying this particular mine as the parameter of this event. \li (14) Missile handles the HIT_MINE(score) event by becoming immediately ready to launch again and lets the Mine do the exploding. As I decided to make the Ship responsible for the scorekeeping, the Missile also generates the DESTROYED_MINE(score) event to the Ship, to report the score for destroying the Mine. \li (15) Upon reception of the DESTROYED_MINE(score) event, the Ship updates the score reported by the Missile. \li (16) The Ship object handles the PLAYER_SHIP_MOVE(x, y) event by updating its position from the event parameters. \li (17) When the Tunnel object handles the SHIP_IMG(x, y, bmp_id) event next time around, it detects a collision between the Ship and the tunnel wall. In that case it posts the event \c HIT_WALL to the Ship. \li (18) The Ship responds to the \c HIT_WALL event by transitioning to the "exploding" state. Even though the sequence diagram in \ref F4s1 "Figure 4-1" shows merely some selected scenarios of the "Fly 'n' Shoot" game, I hope that the explanations give you a big picture of how the application works. More importantly, you should start getting the general idea about the thinking process that goes into designing an event-driven system with active objects and events. Prev: \ref main_function \n Next: \ref active_objects \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page active_objects 5. Elaborating State Machines of Active Objects This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref design \n Next: \ref events I hope that the analysis of the sequence diagram in \ref F4s1 "Figure 4-1" makes it clear that actions performed by an active object depend as much on the events it receives, as on the internal mode of the object. For example, the Missile active object handles the \c TIME_TICK event very differently when the Missile is in flight (\ref F4s1 "Figure 4-1"(12)) compared to the time when it is not (\ref F4s1 "Figure 4-1"(3)). The best known mechanism of handling such modal behavior is through state machines because a state machine makes the behavior explicitly dependent on both the event and the state of an object. In Chapter 2 of \ref PSiCC2 I introduce UML state machine concepts more thoroughly. In this section, I give a cursory explanation of the state machines associated with each object in the "Fly 'n' Shoot" game. \section missile 5.1 The Missile Active Object I start with the Missile state machine shown in \ref F5s1 "Figure 5-1", because it turns out to be the simplest one. The explanation section immediately following the diagram illuminates the interesting points. \note A UML state diagram like \ref F5s1 "Figure 5-1" preserves the general form of the traditional state transition diagrams, where states are represented as nodes and transitions as arcs connecting the nodes. In the UML notation the state nodes are represented as rectangles with rounded corners. The name of the state appears in bold type in the name compartment at the top of the state. Optionally, right below the name, a state can have an internal transition compartment separated from the name by a horizontal line. The internal transition compartment can contain entry actions (actions following the reserved symbol "entry"), exit actions (actions following the reserved symbol "exit"), and other internal transitions (e.g., those triggered by \c TIME_TICK in \ref F5s1 "Figure 5-1"(3)). State transitions are represented as arrows originating at the boundary of the source state and pointing to the boundary of the target state. At a minimum, a transition must be labeled with the triggering event. Optionally, the trigger can be followed by event parameters, a guard, and a list of actions. \anchor F5s1 \image html Fig1.05.jpg "Figure 5-1 Missile state machine diagram." \li (1) The state transition originating at the black ball is called the initial transition. Such transition designates the first active state after the state machine object is created. An initial transition can have associated actions, which in the UML notation are enlisted after the forward slash "/". In this particular case, the Missile state machine starts in the "armed" state and the actions executed upon the initialization consist of subscribing to the event \c TIME_TICK. Subscribing to an event means that the framework will deliver the specified event to the Missile active object every time the event is published to the framework. Chapter 7 of \ref PSiCC2 describes the implementation of the publish-subscribe event delivery in QF. \li (2) The arrow labeled with the MISSILE_FIRE(x, y) event denotes a state transition, that is, change of state from "armed" to "flying". The MISSILE_FIRE(x, y) event is generated by the Ship object when the Player triggers the Missile (see the sequence diagram in \ref F4s1 "Figure 4-1"). In the \c MISSILE_FIRE event, Ship provides Missile with the initial coordinates in the event parameters (x, y). \note The UML intentionally does not specify the notation for actions. In practice, the actions are often written in the programming language used for coding the particular state machine. In all state diagrams in this book, I assume the C programming language. Furthermore, in the C expressions I refer to the data members associated with the state machine object through the me-> prefix and to the event parameters through the e-> prefix. For example, the action me->x = e->x; means that the internal data member \c x of the Missile active object is assigned the value of the event parameter \c x. \li (3) The event name \c TIME_TICK enlisted in the compartment below the state name denotes an internal transition. Internal transitions are simple reactions to events performed without a change of state. An internal transition, as well as a regular transition, can have a guard condition, enclosed in square brackets. Guard condition is a Boolean expression evaluated at runtime. If the guard evaluates to TRUE, the transition is taken. Otherwise, the transition is not taken and no actions enlisted after the forward slash "/" are executed. In this particular case, the guard condition checks whether the x-coordinate propagated by the Missile speed is still visible on the screen. If so, the actions are executed. These actions include propagation of the Missile position by one step and posting the \c MISSILE_IMG event with the current Missile position and the \c MISSILE_BMP bitmap number to the Tunnel active object. Direct event posting to an active object is accomplished by the QF function QActive_postFIFO(), which I discuss in Chapter 7 of \ref PSiCC2. \li (4) The same event \c TIME_TICK with the [else] guard denotes a regular state transition with the guard condition complementary to the other occurrence of the \c TIME_TICK event in the same state. In this case, the \c TIME_TICK transition to "armed" is taken if the Missile object flies out of the screen. \li (5) The event HIT_MINE(score) triggers another transition to the "armed" state. The action associated with this transition posts the \c DESTROYED_MINE event with the parameter e->score to the Ship object, to report destroying the mine. \li (6) The event \c HIT_WALL triggers a transition to the "exploding" state, with the purpose of animating the explosion bitmaps on the display. \li (7) The label "entry" denotes the entry action to be executed unconditionally upon the entry to the "exploding" state. This action consists of clearing explosion counter (me->exp_ctr) member of the Missile object. \li (8) The \c TIME_TICK internal transition is guarded by the condition that the explosion does not scroll off the screen, and that the explosion counter is lower than 16. The actions executed include propagation of the explosion position and posting the \c EXPLOSION_IMG event to the Tunnel active object. Please note that the bitmap of the explosion changes as the explosion counter gets bigger. \li (6) The \c TIME_TICK regular transition with the complementary guard changes the state back to the "armed" state. This transition is taken after the animation of the explosion completes. \section ship 5.2 The Ship Active Object The state machine of the Ship active object is shown in \ref F5s2 "Figure 5-2". This state machine introduces the profound concept of hierarchical state nesting. The power of state nesting derives from the fact that it is designed to eliminate repetitions that otherwise would have to occur. One of the main responsibilities of the Ship active object is to maintain the current position of the Ship. On the original LM3S811 board, this position is determined by the potentiometer wheel (see \ref F2s2 "Figure 2-2"). The PLAYER_SHIP_MOVE(x, y) event is generated whenever the wheel position changes, as shown in the sequence diagram (\ref F4s1 "Figure 4-1"). The Ship object must always keep track of the wheel position, which means that all states of the Ship state machine must handle the PLAYER_SHIP_MOVE(x, y) event. In the traditional finite state machine (FSM) formalism, you would need to repeat the Ship position update from the PLAYER_SHIP_MOVE(x, y) event in every state. But such repetitions would bloat the state machine and, more importantly, would represent multiple points of maintenance both in the diagram and the code. Such repetitions go against the DRY principle (Don't Repeat Yourself), which is vital for flexible and maintainable code. Hierarchical state nesting remedies the problem. Consider the state "active" that surrounds all other states in \ref F5s2 "Figure 5-2". The high-level "active" state is called the superstate and is abstract in that the state machine cannot be in this state directly, but only in one of the states nested within, which are called the substates of "active". The UML semantics associated with state nesting prescribes that any event is first handled in the context of the currently active substate. If the substate cannot handle the event, the state machine attempts to handle the event in the context of the next-level superstate. Of course, state nesting in UML is not limited to just one level and the simple rule of processing events applies recursively to any level of nesting. Specifically to the Ship state machine diagram shown in \ref F5s2 "Figure 5-2", suppose that the event PLAYER_SHIP_MOVE(x, y) arrives when the state machine is in the "parked" state. The "parked" state does not handle the PLAYER_SHIP_MOVE(x, y) event. In the traditional finite state machine this would be the end of story—the PLAYER_SHIP_MOVE(x, y) event would be silently discarded. However, the state machine in \ref F5s2 "Figure 5-2" has another layer of the "active" superstate. Per the semantics of state nesting, this higher-level superstate handles the PLAYER_SHIP_MOVE(x, y) event, which is exactly what's needed. The same exact argumentation applies for any other substate of the "active" superstate, such as "flying" or "exploding", because none of these substates handle the PLAYER_SHIP_MOVE(x, y) event. Instead, the "active" superstate handles the event in one single place, without repetitions. \anchor F5s2 \image html Fig1.06.jpg "Figure 5-2 Ship state machine diagram." \li (1) Upon the initial transition, the Ship state machine enters the "active" superstate and subscribes to events \c TIME_TICK and \c PLAYER_TRIGGER. \li (2) At each level of nesting a superstate can have a private initial transition that designates the active substate after the superstate is entered directly. Here the initial transition of state "active" designates the substate "parked" as the initial active substate. \li (3) The "active" superstate handles the PLAYER_SHIP_MOVE(x, y) event as an internal transition in which it updates the internal data members me->x and me->y from the event parameters e->x and e->y, respectively. \li (4) The TAKE_OFF event triggers transition to "flying". This event is generated by the Tunnel object when the Player starts the game (see the description of the game in Section \ref lets_play). \li (5) The entry actions to "flying" include clearing the me->score data member and posting the event \c SCORE with the event parameter me->score to the Tunnel active object. \li (6) The \c TIME_TICK internal transition causes posting the event \c SHIP_IMG with current Ship position and the \c SHIP_BMP bitmap number to the Tunnel active object. Additionally, the score is incremented for surviving another time tick. Finally, when the score is "round" (divisible by 10) it is also posted to the Tunnel active object. This decimation of the \c SCORE event is performed just to reduce the bandwidth of the communication, because the Tunnel active object only needs to give an approximation of the running score tally to the user. \li (7) The \c PLAYER_TRIIGGER internal transition causes posting the event \c MISSILE_FIRE with current Ship position to the Missile active object. The parameters (me->x, me->y) provide the Missile with the initial position from the Ship. \li (8) The DESTROYED_MINE(score) internal transition causes update of the score kept by the Ship. The score is not posted to the Table at this point, because the next \c TIME_TICK will send the "rounded" score, which is good enough for giving the Player the score approximation. \li (9) The \c HIT_WALL event triggers transition to "exploding" \li (10) The HIT_MINE(type) event also triggers transition to "exploding" \li (11) The "exploding" state of the Ship state machine is very similar to the "exploding" state of Missile (see \ref F5s1 "Figure 5-1"(7-9)). \li (12) The TIME_TICK[else] transition is taken when the Ship finishes exploding. Upon this transition, the Ship object posts the event GAME_OVER(me->score) to the Tunnel active object to terminate the game and display the final score to the Player. \section tunnel 5.3 The Tunnel Active Object The Tunnel active object has the most complex state machine, which is shown in \ref F5s3 "Figure 5-3". Unlike the previous state diagrams, the diagram in \ref F5s3 "Figure 5-3" shows only the high-level of abstraction and omits a lot of details such as most entry/exit actions, internal transitions, guard conditions, or actions on transitions. Such a "zoomed out" view is always legal in the UML, because UML allows you to choose the level of detail that you want to include in your diagram. The Tunnel state machine uses state hierarchy more extensively than the Ship state machine in \ref F5s2 "Figure 5-2". The explanation section immediately following \ref F5s3 "Figure 5-3" illuminates the new uses of state nesting as well as the new elements not explained yet in the other state diagrams. \anchor F5s3 \image html Fig1.07.jpg "Figure 5-3 Tunnel state machine diagram." \li (1) An initial transition can target a substate at any level of state hierarchy, not necessarily just the next-lower level. Here the top-most initial transition goes down two levels to the substate "demo". \li (2) The superstate "active" handles the \c PLAYER_QUIT event as a transition to the final state (see explanation of element (3)). Please note that the PLAYER_QUIT transition applies to all substates directly or transitively nested in the "active" superstate. Because a state transition always involves execution of all exit actions from the states, the high-level PLAYER_QUIT transition guarantees the proper cleanup that is specific to the current state context, whichever substate happens to be active at the time when the \c PLAYER_QUIT event arrives. \li (3) The final state is indicated in the UML notation as the bull's-eye symbol and typically indicates destruction of the state machine object. In this case, the \c PLAYER_QUIT event indicates termination of the game. \li (4) The MINE_DISABLED(mine_id) event is handled at the high level of the "active" state, which means that this internal transition applies to the whole sub-machine nested inside the "active" superstate. (See also the discussion of Mine object in the next section.) \li (5) The entry action to the "demo" state starts the screen time event (timer) me->screenTimeEvt to expire in 20 seconds. Time events are allocated by the application, but they are managed by the QF framework. QF provides functions to arm a time event, such as QTimeEvt_postIn() for one-shot timeout, and QTimeEvt_postEvery() for periodic time events. Arming a time event is in effect telling the QF framework, for instance, "Give me a nudge in 20 seconds". QF then posts the time event (the event me->screenTimeEvt in this case) to the active object after the requested number of clock ticks. Chapters 6 and 7 of \ref PSiCC2 talk about time events in detail. \li (6) The exit action from the "demo" state disarms the me->screenTimeEvt time event. This cleanup is necessary when the state can be exited by a different event than the time event, such as the \c PLAYER_TRIGGER transition. \li (7) The \c SCREEN_TIMEOUT transition to "screen_saver" is triggered by the expiration of the me->screenTimeEvt time event. The signal \c SCREEN_TIMEOUT is assigned to this time event upon initialization and cannot be changed later. \li (8) The transition triggered by \c PLAYER_TRIGGER applies equally to the two substates of the "screen_saver" superstate. \section mines 5.4 The Mine Components Mines are also modeled as hierarchical state machines, but are not active objects. Instead, Mines are components of the Tunnel active object and share its event queue and priority level. The Tunnel active object communicates with the Mine components synchronously by directly dispatching events to them via the function QHsm_dispatch(). Mines communicate with Tunnel and all other active objects asynchronously by posting events to their event queues via the function QActive_postFIFO(). \note Active objects exchange events asynchronously, meaning that the sender of the event merely posts the event to the event queue of the recipient active object without waiting for the completion of the event processing. In contrast, synchronous event processing corresponds to a function call (e.g., QHsm_dispatch()), which processes the event in the caller's thread of execution. \anchor F5s4 \image html Fig1.08.jpg "Figure 5-4 The Table active object manages two types of Mines." As shown in \ref F5s4 "Figure 5-4", Tunnel maintains the data member mines[], which is an array of pointers to hierarchical state machines (QHsm *). Each of these pointers can point either to a Mine1 object, a Mine2 object, or NULL, if the entry is unused. Please note that Tunnel "knows" the Mines only as generic state machines (pointers to the QHsm structure defined in QP). Tunnel dispatches events to Mines uniformly, without differentiating between different types of Mines. Still, each Mine state machine handles the events it its specific way. For example, Mine type 2 checks for collision with the Missile differently than with the Ship while Mine type 1 handles both identically. \note The last point is actually very interesting. Dispatching the same event to different Mine objects results in different behavior, specific to the type of the Mine, which in OOP is known as polymorphism. I'll have more to say about this in Chapter 3 of \ref PSiCC2. Each Mine object is fairly autonomous. The Mine maintains its own position and is responsible for informing the Tunnel object whenever the Mine gets destroyed or scrolls out of the display. This information is vital for the Tunnel object so that it can keep track of the unused Mines. \ref F5s5 "Figure 5-5" shows a hierarchical state machine of Mine2 state machine. Mine1 is very similar, except that it uses the same bitmap for testing collisions with the Missile and the Ship. \anchor F5s5 \image html Fig1.09.jpg "Figure 5-5 Mine2 state machine diagram." \li (1) The Mine starts in the "unused" state. \li (2) The Tunnel object plants a Mine by dispatching the MINE_PLANT(x, y) event to the Mine. The Tunnel provides the (x, y) coordinates as the original position of the Mine. \li (3) When the Mine scrolls off the display the state machine transitions to "unused". \li (4) When the Mine hits the Ship the state machine transitions to "unused". \li (5) When the Mine scrolls finishes exploding the state machine transitions to "unused". \li (6) When the Mine is recycled by the Tunnel object the state machine transitions to "unused". \li (7) The exit action in the "unused" state posts the MINE_DISABLDED(mine_id) event to the Tunnel active object. Through this event, the Mine informs the Tunnel that it's becoming disabled, so that Tunnel can update its mines[] array (see also \ref F5s4 "Figure 5-4"(4)). The mine_id parameter of the event becomes the index into the mines[] array. Please note that generating the MINE_DISABLDED(mine_id) event in the exit action from "used" is much safer and more maintainable than repeating this action in each individual transition (3), (4), (5), and (6). Prev: \ref design \n Next: \ref events \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page events 6. Defining Event Signals and Event Parameters This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref active_objects \n Next: \ref coding_hsm The key events in the "Fly 'n' Shoot" game have been identified in the sequence diagram in \ref F4s1 "Figure 4-1". Other events have been invented during the state machine design stage. In any case, you must have noticed that events consist really of two parts. The part of the event called the signal conveys the type of the occurrence (what happened). For example, the \c TIME_TICK signal conveys the arrival of a time tick, while \c PLAYER_SHIP_MOVE signal conveys that the player wants to move the Ship. An event can also contain additional quantitative information about the occurrence in form of event parameters. For example, the \c PLAYER_SHIP_MOVE signal is accompanied by the parameters (x, y) that contain the quantitative information as to where exactly to move the Ship. In QP, events are represented as instances of the QEvent structure provided by the framework. Specifically, the QEvent structure contains the member sig, to represent the signal of that event. Event parameters are added in the process of inheritance, as described in the sidebar \ref derivation. \section enumerating 6.1 Enumerating Event Signals and Defining Event Parameters 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 \ref L6s1 "Listing 6-1". The explanation section immediately following the listing illuminates the interesting points. \anchor L6s1 Listing 6-1 Signals, event structures, and active object interfaces defined in file game.h. \code (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 */ /* insert other published signals here ... */ (3) MAX_PUB_SIG, /* the last published signal */ 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 */ /* insert other signals here ... */ (4) MAX_SIG /* the last signal (keep always last) */ }; (5) typedef struct ObjectPosEvtTag { (6) QEvent super; /* extend the QEvent class */ (7) uint8_t x; /* the x-position of the object */ (8) uint8_t y; /* new y-position of the object */ } ObjectPosEvt; typedef struct ObjectImageEvtTag { QEvent super; /* extend the QEvent class */ uint8_t x; /* the x-position of the object */ int8_t y; /* the y-position of the object */ uint8_t bmp; /* the bitmap ID representing the object */ } ObjectImageEvt; typedef struct MineEvtTag { QEvent super; /* extend the QEvent class */ uint8_t id; /* the ID of the Mine */ } MineEvt; typedef struct ScoreEvtTag { QEvent super; /* extend the QEvent class */ uint16_t score; /* the current score */ } ScoreEvt; /* opaque pointers to active objects in the application */ (9) extern QActive * const AO_Tunnel; (10) extern QActive * const AO_Ship; (11) extern QActive * const AO_Missile; /* active objects' "constructors" */ (12) void Tunnel_ctor(void); (13) void Ship_ctor(void); (14) void Missile_ctor(void); \endcode \li (1) In QP, signals of events are simply enumerated constants. Placing all signals in a single enumeration is particularly convenient to avoid inadvertent overlap in the numerical values of different signals. \li (2) The application-level signals do not start from zero but rather are offset by the constant #Q_USER_SIG. This is because QP reserves the lowest few signals for the internal use and provides the constant #Q_USER_SIG as an offset from which user-level signals can start. Please also note that by convention, I attach the suffix \c _SIG to all signals so that I can easily distinguish signals from other constants. I drop the suffix \c _SIG in the state diagrams to reduce the clutter. \li (3) The constant \c MAX_PUB_SIG delimits the published signals from the rest. The publish-subscribe event delivery mechanism consumes some RAM, which is proportional to the number of published signals. I save some RAM by providing the lower limit of published signals to QP (\c MAX_PUB_SIG) rather than maximum of all signals used in the application. (See also \ref L3s1 "Listing 3-1"(9)). \li (4) The last enumeration \c MAX_SIG indicates the maximum of all signals used in the application. \li (5) The event structure \c ObjectPosEvt defines a "class" of events that convey the object's position on the display in the event parameters. \li (6) The structure \c ObjectPosEvt derives from the base structure QEvent, as explained in the sidebar \ref derivation. \li (7-8) The structure \c ObjectPosEvt adds parameters \c x and \c y, which are coordinates of the object on the display. \li (9-11) These global pointers represent active objects in the application and are used for posting events directly to active objects. Because the pointers can be initialized at compile time, I like to declare them const, sot that they can be placed in ROM. The active object pointers are "opaque", because they cannot access the whole active object, but only the part inherited from the QActive structure. I'll have more to say about this in the next section. \li (12-14) These functions perform an early initialization of the active objects in the system. They play the role of static "constructors", which in C you need to call explicitly, typically at the beginning of main() (see also \ref L3s1 "Listing 3-1"(10-12)). \section generating 6.2 Generating, Posting, and Publishing Events The QF framework supports two types of asynchronous event exchange: -# The simple mechanism of direct event posting supported through the functions QActive_postFIFO() and QActive_postLIFO(), where the producer of an event directly posts the event to the event queue of the consumer active object. -# A more sophisticated publish-subscribe event delivery mechanism supported through the functions QF_publish() and QActive_subscribe(), where the producers of the events "publish" them to the framework, and the framework then delivers the events to all active objects that had "subscribed" to these events. In QF, any part of the system can produce events, not necessarily only the active objects. For example, interrupt service routines (ISRs) or device drivers can also produce events. On the other hand, only active objects can consume events, because only active objects have event queues. \note QF also provides "raw" thread-safe event queues (struct QEQueue), which can consume events as well. These "raw" thread-safe queues cannot block and are intended to deliver events to ISRs or device drivers. Please refer to Chapter 7 of \ref PSiCC2 for more details. The most important characteristic of event management in QF is that the framework passes around only pointers to events, not the events themselves. QF never copies the events by value ("zero-copy" policy); even in case of publishing events that often involves multicasting the same event to multiple subscribers. The actual event instances are either constant events statically allocated at compile time, or dynamic events allocated at runtime from one of the event pools that the framework manages. \ref L6s2 "Listing 6-2" provides examples of publishing static events and posting dynamic events from the interrupt service routines (ISRs) of the "Fly 'n' Shoot" version for the ARM-Cortex board (file \c <qpc>\\examples\\arm-cortex\\vanilla\\iar\\game-ev-lm3s811\\bsp.c). In the upcoming Section \ref coding_hsm you will see other examples of event posting from active objects in the state machine code. \anchor L6s2 Listing 6-2 Generating, posting , and publishing events from the ISRs in bsp.c for the ARM-Cortex board. \code (1) void ISR_SysTick(void) { (2) static QEvent const tickEvt = { TIME_TICK_SIG, 0 }; (3) QF_publish(&tickEvt); /* publish the tick event to all subscribers */ (4) QF_tick(); /* process all armed time events */ } /*..........................................................................*/ (5) void ISR_ADC(void) { static uint32_t adcLPS = 0; /* Low-Pass-Filtered ADC reading */ static uint32_t wheel = 0; /* the last wheel position */ unsigned long tmp; ADCIntClear(ADC_BASE, 3); /* clear the ADC interrupt */ (6) ADCSequenceDataGet(ADC_BASE, 3, &tmp); /* read the data from the ADC */ /* 1st order low-pass filter: time constant ~= 2^n samples * TF = (1/2^n)/(z-((2^n - 1)/2^n)), * e.g., n=3, y(k+1) = y(k) - y(k)/8 + x(k)/8 => y += (x - y)/8 */ (7) adcLPS += (((int)tmp - (int)adcLPS + 4) >> 3); /* Low-Pass-Filter */ /* compute the next position of the wheel */ (8) tmp = (((1 << 10) - adcLPS)*(BSP_SCREEN_HEIGHT - 2)) >> 10; if (tmp != wheel) { /* did the wheel position change? */ (9) ObjectPosEvt *ope = Q_NEW(ObjectPosEvt, PLAYER_SHIP_MOVE_SIG); (10) ope->x = (uint8_t)GAME_SHIP_X; /* x-position is fixed */ (11) ope->y = (uint8_t)tmp; (12) QActive_postFIFO(AO_ship, (QEvent *)ope); /* post to the Ship AO */ wheel = tmp; /* save the last position of the wheel */ } . . . } \endcode \li (1) In the case of the ARM-Cortex board, the function ISR_SysTick() services the system clock tick ISR generated by the ARM-Cortex system tick timer. \li (2) The \c TIME_TICK event never changes, so it can be statically allocated just once. This event is declared as const, which means that it can be placed in ROM. The initializer list for this event consists of the signal \c TIME_TICK_SIG followed by zero. This zero informs the QF framework that this event is static and should never be recycled to an event pool. \li (3) The ISR calls the framework function QF_publish(), which takes the pointer to the tickEvt event to deliver to all subscribers. \li (4) The ISR calls the function QF_tick(), in which it the framework manages the armed time events. \li (5) The function \c ISR_ADC() services the ADC conversions, which ultimately deliver the position of the Ship. \li (6) The ISR reads the data from the ADC. \li (7-8) A low-pass filter is applied to the raw ADC reading and the potentiometer wheel position is computed. \li (9) The QF macro Q_NEW(ObjectPosEvt, PLAYER_SHIP_MOVE_SIG) dynamically allocates an instance of the ObjectPosEvt event from an event pool managed by QF. The macro also performs the association between the signal \c PLAYER_SHIP_MOVE_SIG and the allocated event. The Q_NEW() macro returns the pointer to the allocated event. \note The PLAYER_SHIP_MOVE(x, y) event is an example of an event with changing parameters. In general, such an event cannot be allocated statically (like the \c TIME_TICK event at label (2)) because it can change asynchronously next time the ISR executes. Some active objects in the system might still be referring to the event via a pointer, so the event should not be changing. Dynamic event allocation of QF solves all such concurrency issues, because every time a new event is allocated. QF then recycles the dynamic events, after it determines that all active objects are done with accessing the events. \li (10-11) The \c x and \c y parameters of the event are assigned. \li (12) The dynamic event is posted directly to the Ship active object. Prev: \ref active_objects \n Next: \ref coding_hsm \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page coding_hsm 7. Coding Hierarchical State Machines This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref events \n Next: \ref execution Contrary to widespread misconceptions, you don't need big design automation tools to translate hierarchical state machines (UML statecharts) into efficient and highly maintainable C or C++. This section explains how to hand-code the Ship state machine from \ref F5s2 "Figure 5-2" with the help of the QF real-time framework and the QEP hierarchical processor, which is also part of the QP event-driven platform. Once you know how to code this state machine, you know how to code them all. The source code for the Ship state machine is found in the file \c ship.c located either in the DOS version or the ARM-Cortex version of the "Fly 'n' Shoot" game. I break the explanation of this file into three steps. \section step1 7.1 Step 1: Defining the Ship Structure In the first step you define the Ship data structure. Just like in case of events, you use inheritance to derive the Ship structure from the framework structure QActive (see the sidebar \ref derivation). Creating this inheritance relationship ties the Ship structure to the QF framework. The main responsibility of the QActive base structure is to store the information about the current active state of the state machine, as well as the event queue and priority level of the Ship active object. In fact, QActive itself derives from a simpler QEP structure QHsm that represents just the current active state of a hierarchical state machine. On top of that information, almost every state machine must also store other "extended-state" information. For example, the Ship object is responsible for maintaining the Ship position as well as the score accumulated in the game. You supply this additional information by means of data members enlisted after the base structure member super, as shown in \ref L7s1 "Listing 7-1". \anchor L7s1 Listing 7-1 Deriving the Ship structure in file ship.c. \code (1) #include "qp_port.h" /* the QP port */ (2) #include "bsp.h" /* Board Support Package */ (3) #include "game.h" /* this application */ /* local objects -----------------------------------------------------------*/ (4) typedef struct ShipTag { (5) QActive super; /* derive from the QActive struct */ (6) uint8_t x; /* x-coordinate of the Ship position on the display */ (7) uint8_t y; /* y-coordinate of the Ship position on the display */ (8) uint8_t exp_ctr; /* explosion counter, used to animate explosions */ (9) uint16_t score; /* running score of the game */ (10) } Ship; /* the typedef-ed name for the Ship struct */ /* state handler functions... */ (11) static QState Ship_active (Ship *me, QEvent const *e); (12) static QState Ship_parked (Ship *me, QEvent const *e); (13) static QState Ship_flying (Ship *me, QEvent const *e); (14) static QState Ship_exploding(Ship *me, QEvent const *e); (15) static QState Ship_initial (Ship *me, QEvent const *e); (16) static Ship l_ship; /* the sole instance of the Ship active object */ /* global objects ----------------------------------------------------------*/ (17) QActive * const AO_ship = (QActive *)&l_ship; /* opaque pointer to Ship AO */ \endcode \li (1) Every application-level C-file that uses the QP platform must include the \c qp_port.h header file. \li (2) The \c bsp.h header file contains the interface to the Board Support Package. \li (3) The \c game.h header file contains the declarations of events and other facilities shared among the components of the application \li (see \ref L6s1 "Listing 6-1"). \li (4) This structure defines the Ship active object. \note I like to keep active objects, and indeed all state machine objects (such as Mines), strictly encapsulated. Therefore, I don't put the state machine structure definitions in header files but rather define them right in the implementation file, such as ship.c. That way, I can be sure that the internal data members of the Ship structure are not known to any other parts of the application. \li (5) The Ship active object structure derives from the framework structure QActive, as described in the sidebar \ref derivation. \li (6-7) The x and y data members represent the position of the Ship on the display. \li (8) The exp_ctr member is used for pacing the explosion animation (see also the "exploding" state in the Ship state diagram in \ref F5s2 "Figure 5-2"). \li (9) The score member stores the accumulated score in the game. \li (10) I use the typedef to define the shorter name Ship equivalent to struct ShipTag. \li (11-14) These four functions are called state-handler functions because they correspond one-to-one to the states of the Ship state machine shown in \ref F5s2 "Figure 5-2". For example, the \c Ship_active() function represents the "active" state. The QEP event processor calls the state handler functions to realize the UML semantics of state machine execution. \c <qpc>\\include\\qep.h. All state handler functions have the same signature. A state handler function takes the state machine pointer and the event pointer as arguments, and returns the status of the operation back to the QEP event processor, for example whether the event was handled or not. The return type QState is typedef-ed to \c uint8_t in the header file \c <qpc>\\include\\qep.h. \note I use a simple naming convention to strengthen the association between the structures and the functions designed to operate on these structures. First, I name the functions by combining the typedef'ed structure name with the name of the operation (e.g., \c Ship_active). Second, I always place the pointer to the structure as the first argument of the associated function and I always name this argument me (e.g., Ship_active(Ship *me, ...)). \li (16) In addition to state handler functions, every state machine must declare the initial pseudostate, which QEP invokes to execute the top-most initial transition (see \ref F5s2 "Figure 5-2"(1)). The initial pseudostate handler has signature identical to the regular state handler function. \li (17) In this line I statically allocate the storage for the Ship active object. Please note that the object l_ship is defined static, so that it is accessible only locally at the file scope of the ship.c file. \li (18) In this line I define and initialize the global pointer AO_Ship to the Ship active object (see also \ref L6s1 "Listing 6-1"(10)). This pointer is "opaque", because it treats the Ship object as the generic QActive base structure, rather than the specific Ship structure. The power of an "opaque" pointer is that it allows me to completely hide the definition of the Ship structure and make it inaccessible to the rest of the application. Still, the other application components can access the Ship object to post events directly to it via the QActive_postFIFO(QActive *me, QEvent const *e) function. \section step2 7.2 Step 2: Initializing the State Machine The state machine initialization is divided into the following two steps for increased flexibility and better control of the initialization timeline: -# The state machine "constructor"; and -# The top-most initial transition. The state machine "constructor", such as \c Ship_ctor(), intentionally does not execute the top-most initial transition defined in the initial pseudostate because at that time some vital objects can be missing and critical hardware might not be properly initialized yet3. Instead, the state machine "constructor" merely puts the state machine in the initial pseudostate. Later, the user code must trigger the top-most initial transition explicitly, which happens actually inside the function QActive_start() (see \ref L3s1 "Listing 3-11"(18-20)). \ref L7s2 "Listing 7-2" shows the instantiation (the "constructor" function) and initialization (the initial pseudostate) of the Ship active object. \anchor L7s2 Listing 7-2 Instantiation and Initialization of the Ship active object in ship.c. \code (1) void Ship_ctor(void) { /* instantiation */ (2) Ship *me = &l_ship; (3) QActive_ctor(&me->super, (QStateHandler)&Ship_initial); (4) me->x = GAME_SHIP_X; (5) me->y = GAME_SHIP_Y; } /*..........................................................................*/ (6) QState Ship_initial(Ship *me, QEvent const *e) { /* initialization */ (7) QActive_subscribe((QActive *)me, TIME_TICK_SIG); (8) QActive_subscribe((QActive *)me, PLAYER_TRIGGER_SIG); (9) return Q_TRAN(&Ship_active); /* top-most initial transition */ } \endcode \li (1) The global function Ship_ctor() is prototyped in game.h and called at the beginning of main(). \li (2) The "me" pointer points to the statically allocated Ship object (see \ref L7s1 "Listing 7-1"(16)). \li (3) Every derived structure is responsible for initializing the part inherited from the base structure. The "constructor" QActive_ctor() puts the state machine in the initial pseudostate &Ship_initial. (see \ref L6s1 "Listing 6-1"(15)). \li (4-5) The Ship position is initialized. \li (6) The Ship_initial() function defines the top-most initial transition in the Ship state machine (see \ref F5s2 "Figure 5-2"(1)). \li (7-8) The Ship active object subscribes to signals \c TIME_TICK_SIG and \c PLAYER_TRIGGER_SIG, as specified in the state diagram in \ref F5s2 "Figure 5-2"(1). \li (9) The initial state "active" is specified by invoking the QP macro Q_TRAN(). \note The macro #Q_TRAN() must always follow the return statement. \section step3 7.3 Step 3: Defining State Handler Functions In the last step, you actually code the Ship state machine by implementing one state at a time as a state handler function. To determine what elements belong the any given state handler function, you follow around the state's boundary in the diagram (\ref F5s2 "Figure 5-2"). You need to implement all transitions originating at the boundary, any entry and exit actions defined in the state, as well as all internal transitions enlisted directly in the state. Additionally, if there is an initial transition embedded directly in the state, you need to implement it as well. Take for example the state "flying" shown in \ref F5s2 "Figure 5-2". This state has an entry action and two transitions originating at its boundary: \c HIT_WALL and HIT_MINE(type), as well as three internal transitions \c TIME_TICK, \c PLAYER_TRIGGER, and DESTROYED_MINE(score). The "flying" state nests inside the "active" superstate. \ref L7s3 "Listing 7-3" shows two state handler functions of the Ship state machine from \ref F5s2 "Figure 5-2". The state handler functions correspond to the states "active" and "flying", respectively. The explanation section immediately following the listing highlights the important implementation techniques. \anchor L7s3 Listing 7-3 State handler functions for states "active" and "flying" in ship.c. \code (1) QState Ship_active(Ship *me, QEvent const *e) { (2) switch (e->sig) { (3) case Q_INIT_SIG: { /* nested initial transition */ (4) /* any actions associated with the initial transition */ (5) return Q_TRAN(&Ship_parked); } (6) case PLAYER_SHIP_MOVE_SIG: { (7) me->x = ((ObjectPosEvt const *)e)->x; (8) me->y = ((ObjectPosEvt const *)e)->y; (9) return Q_HANDLED(); } } (10) return Q_SUPER(&QHsm_top); /* return the superstate */ } /*..........................................................................*/ QState Ship_flying(Ship *me, QEvent const *e) { switch (e->sig) { (11) case Q_ENTRY_SIG: { (12) ScoreEvt *sev; me->score = 0; /* reset the score */ (13) sev = Q_NEW(ScoreEvt, SCORE_SIG); (14) sev->score = me->score; (15) QActive_postFIFO(AO_Tunnel, (QEvent *)sev); (16) return Q_HANDLED(); } case TIME_TICK_SIG: { /* tell the Tunnel to draw the Ship and test for hits */ ObjectImageEvt *oie = Q_NEW(ObjectImageEvt, SHIP_IMG_SIG); oie->x = me->x; oie->y = me->y; oie->bmp = SHIP_BMP; QActive_postFIFO(AO_Tunnel, (QEvent *)oie); ++me->score; /* increment the score for surviving another tick */ if ((me->score % 10) == 0) { /* is the score "round"? */ ScoreEvt *sev = Q_NEW(ScoreEvt, SCORE_SIG); sev->score = me->score; QActive_postFIFO(AO_Tunnel, (QEvent *)sev); } return Q_HANDLED(); } case PLAYER_TRIGGER_SIG: { /* trigger the Missile */ ObjectPosEvt *ope = Q_NEW(ObjectPosEvt, MISSILE_FIRE_SIG); ope->x = me->x; ope->y = me->y + SHIP_HEIGHT - 1; QActive_postFIFO(AO_Missile, (QEvent *)ope); return Q_HANDLED(); } case DESTROYED_MINE_SIG: { me->score += ((ScoreEvt const *)e)->score; /* the score will be sent to the Tunnel by the next TIME_TICK */ return Q_HANDLED(); } (17) case HIT_WALL_SIG: (18) case HIT_MINE_SIG: { (19) /* any actions associated with the transition */ (20) return Q_TRAN(&Ship_exploding); } } (21) return Q_SUPER(&Ship_active); /* return the superstate */ } \endcode \li (1) Each state handler must have the same signature, that is, it must take two parameters: the state machine pointer "me" and the pointer to QEvent. The keyword const before the '*' in the event pointer declaration means that the event pointed to by that pointer cannot be changed inside the state handler function (i.e., the event is read-only). A state handler function must return QState, which conveys the status of the event handling to the QEP event processor. \li (2) Typically, every state handler is structured as a switch statement that discriminates based on the signal of the event signal e->sig. \li (3) This line of code pertains to the nested initial transition \ref F5s2 "Figure 5-2"(2). QEP provides a reserved signal #Q_INIT_SIG that the framework passes to the state handler function when it wants to execute the initial transition. \li (4) You can enlist any actions associated with this initial transition (none in this particular case). \li (5) You designate the target substate with the Q_TRAN() macro. This macro must always follow the return statement, through which the state handler function informs the QEP event processor that the transition has been taken. \note The initial transition must necessarily target a direct or transitive substate of a given state. An initial transition cannot target a peer state or go up in state hierarchy to higher-level states, which in the UML would represent a "malformed" state machine. \li (6) This line of code pertains to the internal transition PLAYER_SHIP_MOVE_SIG(x, y) in \ref F5s2 "Figure 5-2"(3). \li (7-8) You access the data members of the Ship state machine via the "me" argument of the state handler function. You access the event parameters via the "e" argument. You need to cast the event pointer from the generic QEvent base class to the specific event structure expected for the PLAYER_SHIP_MOVE_SIG, which is ObjectPosEvt in this case. \note The association between the event signal and event structure (event parameters) is established at the time the event is generated. All recipients of that event must know about this association to perform the cast to the correct event structure. \li (9) You terminate the case statement with "return Q_HANDLED()", which informs the QEP event processor that the event has been handled (but no transition has been taken). \li (10) The final return from a state handler function designates the superstate of that state by means of the QEP macro #Q_SUPER(). The final return statement from a state handler function represents the single point of maintenance for changing the nesting level of a given state. The state "active" in \ref F5s2 "Figure 5-2" has no explicit superstate, which means that it is implicitly nested in the "top" state. The "top" state is a UML concept that denotes the ultimate root of the state hierarchy in a hierarchical state machine. QEP provides the "top" state as a state handler function QHsm_top(), and therefore the Ship_active() state handler returns the pointer &QHsm_top. \note In C and C++, a pointer-to-function QHsm_top() can be written either as QHsm_top, or &QHsm_top. Even though the notation QHsm_top is more succinct, I prefer adding the ampersand explicitly, to leave absolutely no doubt that I mean a pointer-to-function &QHsm_top. \li (11) This line of code pertains to the entry action into state "flying" (\ref F5s2 "Figure 5-2"(5)). QEP provides a reserved signal ::Q_ENTRY_SIG that the framework passes to the state handler function when it wants to execute the entry actions. \li (12) The entry action to "flying" posts the SCORE event to the Tunnel active object (\ref F5s2 "Figure 5-2"(5)). This line defines a temporary pointer to the event structure ScoreEvt. \li (13) The QF macro Q_NEW(ScoreEvt, SCORE_SIG) dynamically allocates an instance of the ScoreEvt from an event pool managed by QF. The macro also performs the association between the signal SCORE_SIG and the allocated event. The #Q_NEW() macro returns the pointer to the allocated event. \li (14) The score parameter of the ScoreEvt is set from the state machine member me->score. \li (15) The SCORE(me->score) event is posted directly to the Tunnel active object by means of the QP function QActive_postFIFO(). The arguments of this function are the recipient active object (AO_Tunnel in this case) and the pointer to the event (the temporary pointer sev in this case). \li (16) You terminate the case statement with "return Q_HANDLED()", which informs QEP that the entry actions have been handled. \li (17-18) These two lines of code pertain to the state transitions from "flying" to "exploding" (\ref F5s2 "Figure 5-2"(9, 10)). \li (19) You can enlist any actions associated with the transition (none in this particular case). \li (20) You designate the target of the transition with the #Q_TRAN() macro. \li (21) The final return from a state handler function designates the superstate of that state. The state "flying" in \ref F5s2 "Figure 5-2" nests in the state "active", so the state handler Ship_flying() returns the pointer &Ship_active wrapped with the macro Q_SUPER(). When implementing state handler functions you need to keep in mind that the QEP event processor is in charge here rather than your code. QEP will invoke a state handler function for various reasons: for hierarchical event processing, for execution of entry and exit actions, for triggering initial transitions, or even just to elicit the superstate of a given state handler. Therefore, you should not assume that a state handler would be invoked only for processing signals enlisted in the case statements. You should avoid any code outside the switch statement, especially code that would have side effects. Prev: \ref events \n Next: \ref execution \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page execution 8. Using the Built-in Real-Time Kernels and Third-Party RTOSes This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref coding_hsm \n Next: \ref tracing As you saw in \ref L3s1 "Listing 3-1"(21), the \c main() function eventually gives control to the event-driven framework by calling QF_run() to execute the application. In this section, I briefly explain how QF allocates the CPU cycles to various tasks within the system and what options you have in choosing the execution model. \section using_vanilla 8.1 Simple Non-Preemptive "Vanilla" Kernel In the simplest configuration, the "Fly 'n' Shoot" game executes under the simple cooperative "vanilla" kernel, which is provided in the QP. The "vanilla" kernel operates by constantly polling all event queues of active objects in an endless loop. The kernel always selects the highest-priority active object ready to run, which is the highest-priority active object with a non-empty event queue. \note The "vanilla" kernel is so simple that many commercial real-time frameworks don't even call it a kernel . Instead, this configuration is simply referred to as "without an RTOS". However, if you want to understand what it means to execute active objects "without an RTOS" and what execution profile you can expect in this case, you need to realize that a simple cooperative vanilla kernel is indeed involved. The interrupt service routines (ISRs) can preempt the execution of active objects at any time, but due to the simplistic nature of the "vanilla" kernel, every ISR returns to exactly the preemption point. If the ISR posts or publishes an event to any active object, the processing of this event won't start until the current RTC step completes. The maximum time an event for the highest-priority active object can be delayed this way is called the task-level response. With the non-preemptive "vanilla" kernel, the task-level response is equal to the longest RTC step of all active objects in the system. Please note that the task-level response of the "vanilla" kernel is still a lot better than the traditional "superloop" (a.k.a., main+ISRs) architecture. I'll have more to say about this in the upcoming Section \ref comparison, where I compare the event-driven "Fly 'n' Shoot" example to the traditionally structured "Quickstart" application. The task-level response of the simple "vanilla" kernel turns out to be adequate for surprisingly many applications, because state machines by nature handle events quickly without a need to busy-wait for events. (A state machine simply runs-to-completion and becomes dormant until another event arrives). Please also note that often you can make the task-level response as fast as you need by breaking up longer RTC steps into shorter ones (e.g., by using the "Reminder" state pattern described in Chapter 5 of \ref PSiCC2. \section using_QK 8.2 The QK Preemptive Kernel In some cases, breaking up long RTC steps into short enough pieces might be very difficult, and consequently the task-level response of the non-preemptive "vanilla" kernel might be too long. An example system could be a GPS receiver. Such a receiver performs a lot of floating point number crunching on a fixed-point CPU to calculate the GPS position. At the same time, the GPS receiver must track the GPS satellite signals, which involves closing control loops in sub-millisecond intervals. It turns out that it's not easy to break up the position-fix computation into short enough RTC steps to allow reliable signal tracking. But the RTC semantics of state machine execution does not mean that a state machine has to monopolize the CPU for the duration of the RTC step. A preemptive kernel can perform a context switch in the middle of the long RTC step to allow a higher-priority active object to run. As long as the active objects don't share resources they can run concurrently and complete their RTC steps independently. The QP event-driven platform includes a tiny, fully preemptive, priority-based real-time kernel component called QK, which is specifically designed for processing events in the RTC fashion. Configuring QP to use the preemptive QK kernel is very easy, but as with any fully preemptive kernel you must be very careful with any resources shared among active objects5. The "Fly 'n' Shoot" example has been purposely designed to avoid any resource sharing among active objects, so the application code does not need to change at all to run on top of the QK preemptive kerel, or any other preemptive kernel or RTOS for that matter. The accompanying code contains the "Fly 'n' Shoot" example with QK in the following directory: <qpc>\\examples\\80x86\\qk\\watcom\\l\\game\\. You can execute this example in a DOS-console on any standard Windows-based PC. \section using_RTOS 8.3 Traditional OS/RTOS QP can also work with a traditional operating system (OS), such as Windows or Linux, MicroC/OS-II or virtually any real-time operating system (RTOS) to take advantage of the existing device drivers, communication stacks, and other middleware. The accompanying code contains the "Fly 'n' Shoot" example with MicroC/OS-II in the following directory: <qpc>\\examples\\80x86\\ucos2\\watcom\\l\\game\\. You can execute this example in a DOS-console on any standard Windows-based PC. QP contains a platform abstraction layer (PAL), which makes adapting QP to virtually any operating system easy. The carefully designed PAL allows tight integration with the underlying OS/RTOS by reusing any provided facilities for interrupt management, message queues, and memory partitions. I cover porting QP in Chapter 8 of \ref PSiCC2. Prev: \ref coding_hsm \n Next: \ref tracing \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page tracing 9. Using Software Tracing for Testing and Debugging This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref execution \n Next: \ref comparison A running application built of active objects is a highly structured affair where all important system interactions funnel through the real-time framework (QF) and the state-machine engine (QEP). This offers a unique opportunity to use software tracing techniques to gain unprecedented insight into the entire system. Software tracing is a method for obtaining diagnostic information in a live environment without the need to stop the application to get the system feedback. In a nutshell, software tracing is similar to peppering the code with \c printf() statements for logging and debugging, except that mature software tracing techniques are much less intrusive and more selective than the primitive \n printf(). Due to the inversion of a control, software tracing is particularly effective and powerful in combination with the event-driven frameworks. The QP event-driven platorm contains the sophisticated software tracing system called Quantum Spy (QS). The QS trace data can be thorough enough to produce complete sequence diagrams and detailed state machine activity for all state machines in the system. You can selectively monitor all event exchanges, event queues, event pools, and time events because all these elements are controlled by the framework. Additionally, if you use one of the kernels built into QP (the vanilla kernel or the preemptive QK kernel), you can obtain all the data available to a traditional RTOS as well, such as context switches and mutex activity To show you how software tracing works in practice I present an example of a software tracing session. I use the application. All versions of the "Fly 'n' Shoot" game contain the QS instrumentation. The tracing instrumentation becomes active when you build the "Spy" configuration. \note To build the "Spy" configuration in DOS version, please execute the \c make.bat script with the \c spy argument (\c make \c spy). To buld the "Spy" configuration in the ARM-Cortex version, please select the "Spy" configuration in the IAR EWARM IDE (see \ref F2s3 "Figure 2-3"). \ref F9s1 "Figure 9-1" shows how to collect the software trace data from the DOS version of the "Fly 'n' Shoot" application, which is located in the directory \\\examples\\80x86\\dos\\watcom\\l\\dpp\\spy\\dpp.exe. You can re-build the "Spy" configuration by executing \c make \c spy from the command line. You need to run the \c GAME.EXE executable on a target PC with a serial port. You connect the serial port of the target machine to the serial port of a Windows or a Linux host workstation via a NULL-modem cable. \anchor F9s1 \image html Fig11.02.jpg "Figure 9-1 Collecting software trace data from a 80x86 target." Actually, all versions of "Fly 'n' Shoot" applications included in the standard QP distribution are instrumented for software tracing and I encourage you to try them all. For example, you can collect trace data from the LM3S811 board (see \ref F9s2 "Figure 9-2"). The LM3S811 board sends the QS trace data through the UART0 connected to the Virtual COM Port (VCP) provided by the USB debugger, so the QSPY host application can conveniently receive the trace data on the host PC. No additional serial cable is needed. \anchor F9s2 \image html Fig11.01.jpg "Figure 9-2 Collecting software trace data from the LM3S811 board." On the host workstation, you need to start the QSPY host application that decompresses and visualizes the QS trace data. The Windows executable of the QSPY host application is located in the directory <qpc>\\tools\\qspy\\win32\\vc2005\\Release\\qspy.exe. Assuming that this directory is your current directory, or is in your path, you invoke this console application by typing the following command at the command prompt: \code qspy -c COM1 -b 115200 \endcode The first command-line parameter -c COM1 tells the QSPY host application to receive the trace data from COM1. If your target is connected to a different COM port, you need to adjust the COM number. The second parameter configures the baud rate of the serial port to 115200. Section \ref qspy_command explains the comman-line paratmeters of the QSPY host application. \note In the particular case of a Windows PC, you can use the same machine as the target and the host at the same time. You need to use a machine with two serial ports, which you connect with a NULL modem cable. You can use one serial port for the DPP target application running in a DOS-window and the other for the QSPY host application. You might also use a Linux host machine. In case of Linux, you must first build the executable by running the \c Makefile located in the directory <qpc>/tools/qspy/linux/gnu/. You invoke the Linux executable by typing the following command at the command prompt: \code qspy -c /dev/ttyS0 -b 115200 \endcode The first parameter -c /dev/ttyS0 tells the QSPY application to receive the trace data from the ttyS0 serial device. If you connected a different serial port to the target, you need to adjust the ttyS number. If everything is connected correctly, the QSPY host application should produce the human-readable output of the trace data to the screen. Please refer to \ref qspy_page explains the human-readable format as well as importing the trace data to MATLAB®. Prev: \ref execution \n Next: \ref comparison \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page comparison 10. Comparison to the Traditional Approach This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref tracing \n Next: \ref summary The "Fly 'n' Shoot" game behaves intentionally almost identically to the "Quickstart" application provided in source code with the Luminary Micro ARM Cortex-M3 LM3S811 evaluation kit (http://www.luminarymicro.com). In this section I'd like to compare the traditional approach represented by the "Quickstart" application with the state machine-based solution exemplified in the "Fly 'n' Shoot" game. \ref F10s1 "Figure 10-1"(a) shows schematically the flowchart of the "Quickstart" application, while \ref F10s1 "Figure 10-1"(b) shows the flowchart of the "Fly 'n' Shoot" game running on top of the cooperative "vanilla" kernel. At the highest level, the flowcharts are similar in that they both consist of an endless loop surrounding the entire processing. But the internal structure of the main loop is very different in the two cases. As indicated by the heavy lines in the flowcharts, the "Quickstart" application spends most of its time in the tight "event loops" designed to busy-wait for certain events, such as the screen update event. In contrast, the "Fly 'n' Shoot" application spends most of its time right in the main loop. The QP framework dispatches any available event to the appropriate state machine that handles the event and returns quickly to the main loop without ever waiting for events internally. \anchor F10s1 \image html Fig1.11.jpg "Figure 10-1 The control flow in the "Quickstart" application (a), and the Fly 'n' Shoot example (b). The heavy lines represent the most frequently exercised paths through the code." The "Quickstart" application has much more convoluted flow of control than the "Fly 'n' Shoot" example, because the traditional solution is very specific to the problem at hand while the state-machine approach is generic. The "Quickstart" application is structured very much like a traditional sequential program that tries to stay in control from the beginning to the end. From time to time, the application pauses to busy-wait for a certain event, whereas the code is generally not ready to handle any other events than the one it chooses to wait for. All this contributes to the inflexibility of the design. Adding new events is hard because the whole structure of the intervening code is designed to accept only very specific events and would need to change dramatically to accommodate new events. Also, while busy-waiting for the screen update event (equivalent to the \c TIME_TICK event in "Fly 'n' Shoot" example) the application is really not responsive to any other events. The task-level response is hard to characterize and generally depends on the event type. The timing established by the hard-coded waiting for the existing events might not work well for new events. In contrast, the "Fly 'n' Shoot" application has a much simpler control flow that is purely event-driven and completely generic (see \ref F10s1 "Figure 10-1"(b)). The context of each active object component is represented as the current state of a state machine, rather than as a certain place in the code. That way, hanging in tight "event loops" around certain locations in the code corresponding to the current context is unnecessary. Instead, a state machine remembers the context very efficiently as a small data item (the state-variable, see Chapter 3 of \ref PSiCC2). After processing of each event the state machine can return to the common event loop that is designed generically to handle all kinds of events. For every event, the state machine naturally picks up where it left off and moves on to the next state, if necessary. Adding new events is easy in this design, because a state machine is responsive to any event at any time. An event-driven, state-machine-based application is incomparably more flexible and resilient to change than the traditional one. \note The generic event loop can also very easily detect the situation when no events are available, in which case the QP framework calls the QF_onIdle() function (see \ref F10s1 "Figure 10-1"(b)). This callback function is designed to be customized by the application and is the ideal place to put the CPU in a low-power sleep mode to conserve the power. In contrast, the traditional approach does not offer any single place to transition to the low-power sleep mode, and consequently is much less friendly for implementing truly low-power designs. Prev: \ref tracing \n Next: \ref summary \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page summary 11. Summary This QP/C Tutorial is adapted from Chapter 1 of \ref PSiCC2\n by Miro Samek, the founder and president of Quantum Leaps, LLC. \image html qp_tutorial.jpg Prev: \ref comparison If you've never done event-driven programming before, the internal structure of the "Fly 'n' Shoot" game must certainly represent a big paradigm shift for you. In fact, I hope that it actually blows your mind, because otherwise I'm not sure that you really appreciate the complete reversal of control of an event-driven program compared to the traditional sequential code. This reversal of control, known as the "Hollywood Principle" (don't call us, we'll call you), baffles many newcomers, who often find it "mind-boggling", "backwards", or "weird". My main goal in this Tutorial was just to introduce you to the event-driven paradigm and the modern state machines to convince you that these powerful concepts aren't particularly hard to implement directly in C or C++. Indeed, I hope you noticed that the actual coding of the nontrivial "Fly 'n' Shoot" game wasn't a big deal at all. All you needed to know was just a few cookie-cutter rules for coding state machines and familiarity with a few framework services for implementing the actions. Wile the coding turned out to be essentially a non-issue; the bulk of the programming effort was spent on the design of the application. At this point, I hope that the "Fly 'n' Shoot" example helps you to get the big picture of how the method works. Under the event driven model, the program structure is divided into two rough groups: events and state machine components (active objects). An event represents the occurrence of something interesting. A state machine codifies the reactions to the events, which generally depend both on the nature of the event and on the state of the component. While events often originate from the outside of your program, such as time ticks or button presses in the "Fly 'n' Shoot" game, events can also be generated internally by the program itself. For example the Mine components generate notification events when they detect a collision with the Missile or the Ship. An event-driven program executes by constantly checking for possible events and, when an event is detected, dispatching the event to the appropriate state machine component (see \ref F10s1 "Figure 10-1"(b)). In order for this approach to work, the events must be checked continuously and frequently. This implies that the state machines must execute quickly, so that the program can get back to checking for events. In order to meet this requirement, a state machine cannot go into a condition where it is busy-waiting for some long or indeterminate time. The most common example of this would be a while loop inside a state-handler function, where the condition for termination was not under program control, for instance the button press. This kind of program structure, an indefinite loop, is referred to as "blocking" code6, and you saw examples of it in the "Quickstart" application (see \ref F10s1 "Figure 10-1"(a)). In order for the event driven programming model to work, you must only write "non-blocking" code. Finally, the "Fly 'n' Shoot" example demonstrates the use of the event-driven platform called QP, which is a collection of components for building event-driven application. The QF real-time framework component framework embodies the "Hollywood principle", by calling the application code, not the other way around. Such arrangement is very typical for event-driven systems and application frameworks similar to QF are at the heart of virtually every design automation tool on the market today. The QF framework operates in the "Fly 'n' Shoot" game in its simplest configuration, in which QF runs on a bare-metal target processor without any operating system. QF can also be configured to work with the build-in preemptive real-time kernel called QK (see Chapter 10 of \ref PSiCC2), or can be easily ported to almost any traditional OS or RTOS (see Chapter 8 of \ref PSiCC2). In fact, you can view the QF framework itself as a high-level, event-driven, real-time operating system. Prev: \ref comparison \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */ /** \page derivation Encapsulation and Single Inheritance in C Inheritance is the ability to derive new structures based on existing structures in order to reuse and organize code. You can implement single inheritance in C very simply by literally embedding the base structure as the first member of the derived structure. For example, \ref FA1 "Figure 1"(a) shows the structure ScoreEvt derived from the base structure QEvent by embedding the QEvent instance as the first member of ScoreEvt. To make this idiom better stand out, I always name the base structure member super. \anchor FA1 \image html FA1.jpg "Figure 1 (a) Derivation of structures in C, (b) memory alignment, and (c) the UML class diagram." As shown in \ref FA1 "Figure 1"(b), such nesting of structures always aligns the data member super at the beginning of every instance of the derived structure. In particular, this alignment lets you treat a pointer to the derived ScoreEvt structure as a pointer to the QEvent base structure. Consequently, you can always safely pass a pointer to ScoreEvt to any C function that expects a pointer to QEvent. (To be strictly correct in C, you should explicitly cast this pointer. In OOP such casting is called upcasting and is always safe.) Therefore, all functions designed for the QEvent structure are automatically available to the ScoreEvt structure as well as other structures derived from QEvent. \ref FA1 "Figure 1"(c) shows the UML class diagram depicting the inheritance relationship between ScoreEvt and QEvent structures. QP uses single inheritance quite extensively not just for derivation of events with parameters, but also for derivation of state machines and active objects. Of course, the C++ version of QP uses the native C++ support for class inheritance rather than "derivation of structures". \image html logo_ql_TM.jpg Copyright © 2002-2011 Quantum Leaps, LLC. All Rights Reserved.\n http://www.state-machine.com */