Following up on my previous post, ROC Hockey, where I introduced our new implementation of Hierarchical State Machines on NetKernel I’ve decided to cover the topic of how they are actually used in practice in an ROC system. Since that post we have released the first version of Lang/SCXML urn:com:ten60:netkernel:lang:scxml into the Enterprise Edition repositories. If you want to have a play with it yourself here are the steps:
- launch a NKEE instance
- go to http://localhost:1060/tools/apposite/
- search for and install lang-scxml
- go to http://localhost:1060/tools/ee/license/ and capture the reported Hostname under licensee details
- Log in to NetKernel services portal: https://cs.1060research.com/csp/ and note down your login name and project
- Email me (tab AT 1060research.com) both the hostname, CSP login name and project and I’ll issue you a developer license
- Go to the license page: https://cs.1060research.com/csp/license/ and download your license
- Install the license using http://localhost:1060/tools/ee/license/
- download and install demo module from: http://resources.1060research.com/packages/2013/10/demo.statemachine-tab-2013-10-25.jar
State machines are an approach to modelling and implementing systems that are event based. Events are analogous to requests in resource oriented computing however events don’t have responses - you might say they are fire-and-forget. Often in programming, state of system is scattered across a number of variables and flags and the logic to determine a particular circumstance and then what to do may end up quite convoluted making testing and evolution difficult. In a state machine the essential state is captured by a single variable called “current state” (this is actually a simplification but you get the principle) which can only take on one of a predefined set of values. The current state can only change when an event is received and a transition is defined that the state machine is expecting- corner cases become explicit. State machines are often visualised as graphs.
Example state machine of noisy toy dog (this is implemented in demo module)
An SCXML document requires a root scxml element with default namespace. It is also worth declaring the namespace necessary for issuing sub-requests:
<scxml version="1.0" xmlns:nk="http://1060.org/scxml" xmlns="http://www.w3.org/2005/07/scxml"> </scxml>
Each state is declared as nested state element. The initial state is declared with an attribute in the parent scxml element:
<scxml initial="bark" version="1.0" xmlns:nk="http://1060.org/scxml" xmlns="http://www.w3.org/2005/07/scxml"> <state id="bark"/> <state id="howl"/> <state id="wait"/> </scxml>
Following the example visualized above we can now look at how transitions are added between the three states. Transitions are defined within a state and define a target state to go to when an event is received.
<scxml version="1.0" xmlns:nk="http://1060.org/scxml" xmlns="http://www.w3.org/2005/07/scxml"> <state id="bark"> <transition event="head_button_pressed" target="wait"/> <transition event="5_second_timer" target="howl"/> </state> <state id="howl"> <transition event="howl_completed" target="wait"/> </state> <state id="wait"> <transition event="15_second_timer" target="bark"/> </state> </scxml>
Executable content is the scxml term for configurable actions that can be executed at certain points. The can be executed when we enter a state, exit a state or perform a transition.
<state id="howl"> <onentry> <!-- executable content here to be executed when we enter state howl --> </onentry> <transition event="howl_completed" target="wait"> <!-- executable content here to be executed when we make the transition to wait --> </transition> <onexit> <!-- executable content here to be executed when we exit state howl --> </onexit> </state>
Executable content is executed in the following order (assuming some is defined):
- <onexit> of the state being exited
- <onentry> of the state being entered
Executable content can contain the following tags:
- <if> evaluate an expression and then conditionally evaluate further executable content
- <send> send an internal event back to this state machine
- <subrequest> issue a NetKernel sub-request
Further details of executable content is covered in the W3C Specification.
State Machine Runtime
The state machine runtime follows the Language Runtime pattern. It takes the SCXML definition as an operator argument and the event to be processed as the event argument. In common with all NetKernel language runtimes it doesn’t hold any state. This may seem particularly ironic for a state machine but of course stateless never means “no state anywhere” it means “no state here” and the state is actually stored (SOURCEd and SINKed) in a resource referenced by the state argument. Here is the documentation boiler plate for the runtime:
this is mapped to the frontend 8080 HTTP server with the identifier grammar:
<group>res:/smHello/ <group name="event"> <regex type="anything"/> </group> </group>
Here is the SCXML (i’ve had to move it out of the default namespace due to a limitation of XML literals in nCoDE)
<sc:scxml initial="a" version="1.0" xmlns:nk="http://1060.org/scxml" xmlns:sc="http://www.w3.org/2005/07/scxml"> <sc:state id="a"> <sc:transition event="e" target="b"/> </sc:state> <sc:state id="b"> <sc:transition event="e" target="c"/> </sc:state> <sc:state id="c"> <sc:transition event="e" target="a"/> </sc:state> </sc:scxml>
Issuing a request of http://localhost:8080/smHello/e will advance the state machine a->b->c->a…. and any other event will not.
In this example we return the output from the state machine which isn’t that interesting as usually you’ll be triggering off sub-requests to do useful work but this is what it looks like:
<statemachine> <guid>141EFF89D482AE</guid> <state>b</state> </statemachine>
Hope this post helps demystify how state machines are implemented in NetKernel and don’t forget that if your curious the demo module contains much more exciting stuff, including a full version of the legendary RocHockey (multiplayer air hockey) and a timesheet system that puts at least one major corporations timesheet system to shame.