So what exactly is a Hierarchical State Machine (HSM) anyway? It is a super set of a Finite State Machines(FSM). And like all FSM, they are basically event filters that only react to an event if the state handles it. For a given state, certain events can invoke an action or cause a transition to another state. This is very common design technique to model behavior in an embedded systems. But one of the problems with realizing the state machine in code is that you end up with too many states and too many transitions that makes it complicated and unmanageable for non trivial systems.
Enter the HSM which basically allows states to be nested. The advantages of nesting a state into another state is that the child state doesn't need to handle every event/signal. If the current state doesn't have a handler for it, then it can pass the event to its parent and see if this has a handler for the event/signal. Otherwise that state can continue passing that event up to the state hierarchy. You can think of this as a sort of class inheritance. This offers some big advantages such as:
- More opportunities for refactoring code - similar behavior can be pushed up to the parent
- Less event handlers - Common events can be pushed up to the parent instead of every child state
- Less transitions - Each state doesn't need to handle the transition
- Consistent setup and teardown - Each state handles specific operations, while parent handle common operations
From my perspective, I only care about being able to draw a HSM state machine using some UML diagramming tool and then map it directly into some code. So here is my python implementation of HSM @ https://github.com/howard-chan/HSM.git
Of course for this to be useful, I need an example to demonstrate its capabilities. So using Visual Paradigm as my UML drawing tool, I created this simple HSM model of a camera.
Here we have a camera that really has 4 states (Off, OnShoot, OnDispPlay, OnDispMenu) which are nested into the parent states (On, OnDisp).
Here we see that any state will handle the PWR event. If we are in any one of the child On states (OnShoot, OnDispPlay, OnDispMenu), we see that each child doesn't directly handle the PWR event so it pushes the event up to the parent which does handle the PWR event. In this case, its the On state which will handle the PWR event and cause a transition to the Off state. As the HSM transitions from say the "OnDispMenu" to "Off" state, each of the "exit" event handling are invoked to clean up the state, til it enter the "Off" state which triggers the "entry" even handling to setup the state.
So for the following event sequence fed to the Camera:
# Instantiate Camera
basic = Camera("Canon")
# Turn on the Power
basic.Run(evt.PWR)
# Take a picture
basic.Run(evt.RELEASE)
# Take another picture
basic.Run(evt.RELEASE)
# Playback the photo
basic.Run(evt.MODE)
# Oops, pushed the release button by accident
basic.Run(evt.RELEASE)
# Go to menu settings
basic.Run(evt.MODE)
# Uh oh, low battery
basic.Run(evt.LOWBATT)
# Time to turn it off
basic.Run(evt.PWR)
We get the following output:
Run Canon[Off](evt:PWR)
Tran Canon[Off -> On]
Canon[Off](EXIT)
Exit Low Power Mode
Canon[On](ENTRY)
Open Lens
Canon[On](INIT)
Tran Canon[On -> On.Shoot]
Canon[On.Shoot](ENTRY)
Enable Sensor
Canon[On.Shoot](INIT)
Run Canon[On.Shoot](evt:RELEASE)
CLICK!, save photo
Run Canon[On.Shoot](evt:RELEASE)
CLICK!, save photo
Run Canon[On.Shoot](evt:MODE)
Tran Canon[On.Shoot -> On.Disp.Play]
Canon[On.Shoot](EXIT)
Disable Sensor
Canon[On.Disp](ENTRY)
Turn on LCD
Canon[On.Disp.Play](ENTRY)
Display Pictures
Canon[On.Disp.Play](INIT)
Run Canon[On.Disp.Play](evt:RELEASE)
evt:RELEASE unhandled, passing to Canon[On.Disp]
evt:RELEASE unhandled, passing to Canon[On]
evt:RELEASE unhandled, passing to Canon[:root:]
Unhandled event:RELEASE Canon[<hsm.State instance at 0x00000000058EA708>]
Run Canon[On.Disp.Play](evt:MODE)
Tran Canon[On.Disp.Play -> On.Disp.Menu]
Canon[On.Disp.Play](EXIT)
Canon[On.Disp.Menu](ENTRY)
Display Menu
Canon[On.Disp.Menu](INIT)
Run Canon[On.Disp.Menu](evt:LOWBATT)
evt:LOWBATT unhandled, passing to Canon[On.Disp]
evt:LOWBATT unhandled, passing to Canon[On]
Beep low battery warning
Run Canon[On.Disp.Menu](evt:PWR)
evt:PWR unhandled, passing to Canon[On.Disp]
evt:PWR unhandled, passing to Canon[On]
Tran Canon[On.Disp.Menu -> Off]
Canon[On.Disp.Menu](EXIT)
Canon[On.Disp](EXIT)
Turn off LCD
Canon[On](EXIT)
Close Lens
Canon[Off](ENTRY)
Enter Low Power Mode
Canon[Off](INIT)
Well that is it for now, I'll probably elaborate on how the HSM.py works on my next entry
No comments:
Post a Comment