Design

UML Statecharts at $10.99

By Miro Samek, May 24, 2006

UML-style state machines can help you produce efficient, maintainable, testable systems--and you don't need big UML tools to use them.

Designing a Statechart

Considering the limited capabilities of the Toolstick, I was constrained with the choice of a compelling example. Basically, the Toolstick can only blink its two LEDs (see Figure 1), or at most change the LED intensity using the built-in PWM generators. To me this resembled the operation of a PEdestrian LIght CONtrolled (PELICAN) crossing that I've used before in "Deja Vu".

The PELICAN crossing controller starts with cars enabled (green light for cars) and pedestrians disabled (don't-walk signal for pedestrians). To activate the traffic light change, a pedestrian must push the button at the crossing, which generates the PEDS_WAITING event. In response, the cars get the yellow light, which after a few seconds changes to red light. Next, pedestrians get the walk signal, which shortly thereafter changes to the flashing don't-walk signal. When the don't-walk signal stops flashing, cars get the green light again. After this cycle, the traffic lights don't respond to the PEDS_WAITING button press immediately, although the button "remembers" that it has been pressed. The traffic light controller always gives the cars a minimum of several seconds of green light before repeating the traffic light change cycle. One additional feature (coming late into the project) is that at any time an operator can take the PELICAN crossing offline (by providing the OFF event). In the "offline" mode, the cars get a flashing yellow and pedestrians flashing don't-walk signal. At any time the operator can turn the crossing back online (by providing the ON event).

Due to the hierarchical character of statecharts, you can approach the design from the top down or bottom up. Here, I use a combination of the two.

The limitation on the number of diagrams I can use here does not permit me to show a series of progressively elaborated statecharts, which would be perhaps the most educational method of explaining the design process. Instead, Figure 2 shows the complete PELICAN crossing statechart.

[Click image to view at full size]

Figure 2: Complete PELICAN crossing statechart.

I start the design with just two states: carsEnabled and pedsEnabled. This pair of states realizes the main function of the PELICAN crossing, which is to alternate between enabling cars and enabling pedestrians. Obviously, both states need some substates to realize the details of the specification, but I ignore this at first. At this stage, I only make sure that the design guarantees mutually exclusive access to the crossing, which is the main safety concern here. The exit action from the pedsEnabled state disables pedestrians, and the exit action from carsEnabled disables cars. Now, the UML semantics of state transitions guarantees that these exit actions execute whichever way the states happen to be exited, so I can be sure that the pedestrians have always don't-walk signal outside the pedsEnabled state and cars have the red light outside the carsEnabled state.

In the next step, I concentrate on the internal structure of the pedsEnabled state. The job of the pedsWalk substate is to display the walk signal and to time out. The job of the pedsFlash substate is to turn the don't-walk signal on/off. All actions are triggered by the TIMEOUT events, which are generated by the timer object associated with the state machine. The function QActive_arm() arms the timer for a one-shot delivery of the TIMEOUT event in the specified number of clock ticks. In the substate pedsFlash, the TIMEOUT event triggers two internal transitions and one regular transition leading out of the state. Internal transitions in UML are different from regular transitions, because the internal transitions never cause execution of state exit or entry. Internal transitions have also a distinctive notation that is similar to the entry/exit actions (Figure 2). The two internal transitions in state pedsFlash have different guard conditions (the Boolean expressions in the square brackets), which means that they are enabled only if the conditions in the square brackets evaluate to TRUE. The guard conditions are based in this case on the internal counter pedFlashCtr_ that controls the number of flashes of the don't-walk signal.

Turning to the internal structure of the carsEnabled state, the most interesting problem is to guarantee the minimum green light for cars before enabling pedestrians. Upon entry to the carsGreen substate, the timer is armed to expire after the minimum green light time. When the PEDS_WAITING event arrives before the expiration of this timeout, the active state is carsGreenNoPed, and the state machine transitions to the substate carsGreenPedWait, which has the purpose of "remembering" that a pedestrian is waiting. When the minimum green light time expires in the carsGreenPedWait state, it triggers the TIMEOUT transition to the carsYellow state, which after another timeout transitions out of carsEnabled state to open the crossing to pedestrians. However, if the PEDS_WAITING event does not arrive before the minimum green light timer expires the state machine will be in the carsGreenNoPed state that does not prescribe how to handle the TIMEOUT event. Per the semantics of state nesting, the event is passed to the higher-level state, that is, to carsGreen, which handles the TIMEOUT event in the transition to carsGreenInt (interruptible green light).

At this point, the statechart accomplishes the main functionality of the PELICAN crossing. The design progressed top-down, by gradually elaborating the inner structure of hierarchical states. However, you can also design statecharts in the bottom-up fashion. In fact, this is the best way to add the last feature--the "offline" mode of operation.

The offline mode of operation is added simply by enclosing the whole state machine elaborated in the previous steps inside the superstate operational that handles the transition OFF to the offline state. Note how the state hierarchy ensures that the transition OFF is inherited by all direct or transitive substates of the operational superstate, so regardless in which substate the state machine happens to be, the OFF event always triggers transition to offline. Now, imagine how difficult it would be to make such a last-minute change to a traditional, non-hierarchical FSM.

The PELICAN crossing is ready now, but we still have a big problem of actually generating the external events to the PELICAN state machine, such as PED_WAITING, ON, and OFF. The actual PELICAN crossing hardware provides a push button for generating the PED_WAITING event, as well as the ON/OFF switch to generate the ON/OFF events, but the Toolstick has no external input (Figure 1). For Toolstick, we need to simulate the pedestrian/operator in a separate state machine. This is actually a good opportunity to demonstrate how to combine many state machines that collectively deliver the intended functionality of the application. Refer to the accompanying code for the implementation of the straightforward Pedestrian state machine.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!