Example 4: Generating C for Digital Wristwatch Applications
The last example illustrates C code generation from state machines. There are several ways to implement a state machine code. For a more detailed description of possible solutions see Miro Samek's book Practical Statecharts in C/C++
. The best solution for a given situation depends of course on such things as the target language and platform, requirements for the size and efficiency of the code, etc.
This digital wristwatch example's architecture consists of a set of applications for displaying the current time, stopwatch, alarm etc. The modeling language focuses on capturing the static elements of the watch application, such as buttons, time units, icons, etc., and on capturing the functionality using a kind of state machine. It extends the traditional state transition diagram semantics with domain-specific concepts and constraints. For example, each state has a link to a display function that presents the time, state transitions are triggered only by pressing one button once, arithmetic operations are limited to those relevant for operating on time units, etc. Figure 4
is an example application model for showing and editing the current time, and related operations. Listing 1
contains the corresponding generated C code, slightly abridged to save space.
|Figure 4. Watch Application Design: The figure shows the design for a watch application that can display and edit the current time.|
|Editor's Note: The line numbers in parentheses in the following two paragraphs refer to Listing 1.
Defining the mapping process from model to code is reasonably painless. The generator goes through the design and creates enumerations (enums) that will act as unique labels for states and buttons (lines 12). These are then initialized in lines 45. Next the generator outputs a boilerplate runWatch()
function, common to all applications. For each input event, runWatch()
invokes the main behavioral part of the application, handleEvent()
(lines 1666). In handleEvent()
, what to do and where to go to next depends on the preceding state, and what the input event was. The generator thus reads the state transition diagram and implements it using a simple nested switch statement. The generator produces this by iterating over the states in the model; for each state it iterates over the transitions leaving that state.
A transition can also trigger actions. The example model needs to support only basic time units for arithmetic operations. Setting time variables is a simple assignment; but rolling a digit pair up or down is actually somewhat complicateddifferent time units roll around to zero at different numbers. Because many applications need such a function, you'd probably move that code out of the per-application code into a framework component if you were manually coding such applications. While you could generate it inline each time it is needed, it seems better to keep the overall code small and conceptually neater by making it into a function; then, when it needs such a function, the code generator needs only to produce a call for it (line 35).
Though being more complex than the voice menu example, this case still does not require much work on the framework side. Some framework code or components are needed to provide some low-level services for behavioral part of the implementation (e.g. the implementation of the roll function) but for the structural part, we rely entirely on the C language (e.g. using a plain switch-case to implement the state machine). As in the previous example, the model captures the essence of the system, which reduces the complexity of the code generator.