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.
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.