|
High Quality/High Productivity Software Development Method for Event-Driven Software Programs
This article describes a software development method that I found to be very productive and resulting in software
that is far more reliable than that written with other methods. The
method is based heavily on the Cleanroom Software Engineering technology
as described by Prowell et. al. The article first describes
the method, several possible extensions, and concludes with the
description of a set of C++ class templates that allow effective
implementation of the method.
Review of event-driven programming
Event-driven programming is required in all
situations in which a software program reacts to external events. For
computers with a graphical user interface those events are mouse
movements, mouse clicks, keyboard activity, and possibly buttons on
peripherals (like the "scan" button on a scanner). Embedded
systems may have a front panel with buttons and switches to which the
software reacts.
In all event-driven programs, a particular
section of code is executed in response to a particular external event.
This section of code is often referred to as an event handler.
Furthermore, it is only in response to events that any code is
executed in a typical event-driven program (in most cases the system can
generate internal events based on a timer, and in networked
environments, events can also be created when data arrive or are
requested on the network).
In order for the system to fulfill its desired
function, the code that is run in response to an event may need to
behave in different ways, depending on the events that have taken place
previously. Consider this example: My cell phone has a "menu"
button. Once I click it, the menu is displayed. The menu will be
displayed for a period of time. If I don't press another button during
that time, the cell phone is automatically switching back to its default
display. My cell phone might create a time tick event every second and switch
back to the default display after 30 seconds. During the first 29 runs
of the time tick event handler, the display stays the same, yet at the 30th
run, the time tick event handler switches the display back to its default
display. Since the time tick events are the only ones that are happening
during that time (we assume that its a slow day and no one called me in
those 30 seconds...), it is the time tick event handler which must do the
work. The action of the time tick event handler therefore depends on up to 30 events
in the past (my menu button press, and 29 time tick events).
Cleanroom Software engineering method
At the heart of the cleanroom software engineering
method lies the recognition that a complete analysis of all possible
events with all possible previous event sequences, and an assigned
action to each analysis point completely describes the behavior of the software.
While this statement is mathematically true, by
itself it is of no help. Consider 5 possible events that can occur. The
possible permutations of a sequence of n events is 5n! This
is open-ended, because in most systems n can be large (every time I move
the mouse across my computer screen, it creates hundreds of mouse move
events).
However, often times a sequence of events is
equivalent to a shorter sequence of events. When I drag the mouse across
my screen, receiving two mouse move events in sequence may be equivalent
to receiving only one mouse move event, because there is really nothing
that changes as far as my word processor program is concerned (unless
with that last mouse move I move the cursor over a button, and the
cursor now must change into a pointer).
An example
Going through a full-blown example in detail is
beyond the scope of this article. We will limit ourselves to a trivial
example. However, a more complex example is provided for download.
Consider a device similar to the one sketched
here:

A serial input of 8 bit quantities is delivered
somehow. The device has two push buttons: Open, and Close, two 7-segment
digit displays and an error light.
The software for our example receives a stream of
bytes on "serial in". These bytes can be one of the following: ASCII codes
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, a, b, c, d, e, f, space,
tab, carriage-return, line-feed. Digits and letters are interpreted as
two-digit hexa-decimal numbers, the other keys are for separating these
numbers. For example the input may look
like this:
aAba 0a 1b 2d
0 134f
The output should be: AA/BA/0A/1B/2D/00/13/4F
If any ASCII codes not listed above are received
on "serial in," the software should indicate error and ignore all further input
sequences
One-digit hexadecimal numbers at the input are
allowed. Their upper digit is considered 0.
Input bytes can create different events to
process, depending on their value:
- ASCII codes
0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,a,b,c,d,e,f create an input that we
label D (for digit)
- space, tab, carriage-return, line-feed create
an input that we label S (for separator)
- all other values create an input E (for error)
Before any data are received, an Open button must be
pushed.
To stop reception of further data on serial in,
the Close button must be pushed.
The output of the software is as follows:
When the first byte of a hexadecimal number is
received, it is placed into the low digit of the display. The high digit
of the display is set to zero.
When the second byte of a hexadecimal number is
received, the following actions take place: The digit currently
displayed in the low digit is moved to the high digit, and the
newly-received number is placed into the low digit.
When the software has detected an error, it
indicates this with the error output. The high and low digits of the
display are switched off.
When the close input is received, any existing
error indication is turned off. The two digits of the display are
switched off.
With these specifications, we can declare the
following input events and resulting output operations. Each input and
output gets a symbol (note that we have pre-pended the "o_" to
output symbols to avoid confusion between input and output)
Inputs:
| O |
Open |
| D |
Data |
| S |
Separator |
| C |
Close |
| E |
Error on input (Unrecognized character) |
Outputs:
| o_L |
Set low digit (also reset high digit to 0) |
| o_H |
Set high digit |
| o_E1 |
Set error indicator |
| o_E0 |
Reset error indicator |
| o_C |
Clear digit display |
An input sequence analysis gives the following
table:
| Sequence |
Sequence Equivalent |
Output |
Comment |
| O |
|
|
|
| D |
nil |
|
Ignore data when not open |
| S |
nil |
|
Ignore data when not open |
| C |
nil |
|
|
| E |
nil |
|
Since E can only come from erroneous input on
serial port, ignore while not open |
| |
|
|
|
| OO |
O |
|
Open already. Ignore |
| OD |
|
o_L |
|
| OS |
O |
|
|
| OC |
nil |
o_E0,o_C |
|
| OE |
|
o_E1,o_C |
|
| |
|
|
|
| OEO |
E |
|
|
| OED |
E |
|
|
| OES |
E |
|
|
| OEC |
nil |
o_E0,o_C |
|
| OEE |
E |
|
|
| |
|
|
|
| ODO |
OD |
|
Open already. Ignore |
| ODD |
O |
o_H |
|
| ODS |
O |
|
|
| ODC |
nil |
o_E0,o_C |
|
| ODE |
E |
o_E1,o_C |
|
| |
|
|
|
This is the complete description of the behavior.
A state diagram can be drawn directly from the sequence analysis table.
Inputs are drawn as arrows, and labeled with the input name, as well as
the output actions that are to be taken when the input occurs. States are
drawn as bubbles. Inputs that do not change the state, and do not create
output are omitted from the graph.

Implementation in Java
The example is implemented as a simulation in the Java applet below. On the left
side are the buttons to enter a hexadecimal digit, as well as two button
to enter a white space and a carriage return. Two more buttons are
provided to simulate the behavior of unrecognized ASCII codes (comma and
period).
On the right hand side are two buttons to simulate open and close. Above
are two fields for display. The left of them simulates the display of
the hexadecimal number. The right simulates the error light by
displaying "Error!" (your browser must have Java enabled for
this to work).
(this is a Java applet. Click on the buttons to play with it)
Some variation on the basic methodology
- control/data path separation
Frequently (especially in communication
applications), the software has two tasks: It has to move
"packets" of data from one place to another, and has to keep
track of the control status (for example: opening a communication channel,
closing, recovering from a lost connection, treating packets that arrive
out of sequence, retransmitting lost or damaged packets, etc). It would make
no sense to treat every byte in the data packet as input data. Rather,
if there is control information enclosed in the packet (for example in
a packet header, or as check sum at the end of the packet), the control
data must be extracted first, and then presented as input events to the
control software.
- input abstraction
In the example above, it is likely that the
"real" event that happens is the reception of an ASCII
character. However, the raw character is not shown in the list of event
inputs. Instead of treating '0','1',etc. as distinct inputs, they were
grouped with the D input. Similarly, all separator characters (space,
tab, etc.) were lumped into a single input, as were all the other
characters into the E input group. This results in a reduction of input
events without losing any functionality. The grouping would likely be
done in a pre-processing step of the "character received"
event.
- event cascading
In the example of the cell phone display above, a simple approach to
event analysis, in which a sequence is explicitly written out up to the
30th time tick event would become immense and impractical. An alternate
approach is to provide a counter. This counter is decremented each time
a time event is received and its current value is not 0. When the
counter decrements from 1 to 0, it creates an event (lets call it the
timeout event). When the menu button is pressed, the counter is set to
30. If no suitable event happens within the next 30 seconds, a timeout
event is being created. If any event happens (for example if I press a
button to move through the menu) the counter is set to 30 again. During
the sequence analysis after the menu button press event, any number of
time events only cause the counter to be decremented, and the sequence
is marked as identical to the current sequence without the timeout event
(of course, in each step in the sequence analysis "timeout" must now be
considered as an additional event).
A family class templates for small event-driven
software systems
Component Software, Inc. has developed a set of
class templates that allow a direct implementation of the sequence
analysis table into software. The family consists of three class
templates:
- The input sequencer
- The controller
- The processor
The classes interact with each other as shown
below.

The input sequencer is optional and is used with
event cascading when the processor issues events of its own. These
events are stored in the sequencer's event queue and will be processed
subsequently.
The controller keeps track of the event sequence
that has been executed so far, and it receives events as they occur. It
will cause calls into the processor depending on the past event sequence
and the current event. A more specific implementation (a table-based
controller) is provided as a class template. It allows the direct
encoding of the sequence analysis table.
The processor is driven by the controller and
implements the output actions that need to be performed. Typically the
actions are kept simple. Event cascading is supported when the processor
has access to the input sequencer.
Example source code
Source code for the java applet further above is available, as well as the implementation of a base64
encoder and decoder, based on the Controller/Processor C++ class family described above.
Get the source code for Windows (zip) or Linux/Unix (gz).
Bibliography
Prowell et. al. Cleanroom Software Engineering,
Addison Wesley Longman ISBN 0-201-85480-5
|