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
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.
I start the design with just two states:
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
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
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
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.