Skip to content

Latest commit

 

History

History
331 lines (261 loc) · 11.3 KB

SCXML.md

File metadata and controls

331 lines (261 loc) · 11.3 KB

State Machine Cat and SCXML

State Chart XML is a w3c recommendation that aims to be a generic state-machine based execution environment. The core constructs part of it provides a standard way to describe state charts. State machine cat can output core constructs SCXML and read them as well. SCXML looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" initial="meow" version="1.0">
    <state id="eat">
        <transition event="belly full" target="sleep"/>
    </state>
    <state id="sleep">
        <transition event="wake up" target="meow"/>
    </state>
    <state id="meow">
        <transition event="no response from human" target="meow"/>
        <transition event="human gives food" target="eat"/>
        <transition event="human gives toy" target="play"/>
    </state>
    <state id="play">
        <transition event="tired or bored" target="sleep"/>
    </state>
</scxml>

Usage

SCXML output

Both the command line and the online interpreter support scxml output.

  • Command line: smcat --output-type scxml mycoolchart.smcat
  • Online interpreter: pick SCXML from the hamburger menu.

Visualizing SCXML

On the command line tell that you're trying to parse scxml by passing it as an input type: smcat --input-type scxml mycoolcart.scxml. In the online interpreter you can pick SCXML from the hamburger menu under Input type. The atom plugin kicks in when the extension is scxml - so it will just work when you hit the State Machine cat Preview Toggle Preview action.

What is supported?

All core constructs, except transitions without a target, which are not a concept in state machine cat's language. For these states it will create a self transition, which is as close to the SCXML concept as it can get.

Also, state machine cat's primary goal is to visualize state machines. With that in mind it will focus on the core constructs and not on other parts of the SCXML w3c recommendation (like executable content, data model, external communications).

What more do I need to know?

I've made some choices how to convert state machine cat/ UML constructs to SCXML that seemed logical to me. Which means they might surprise you.

Writing to SCXML - rationale

initial (pseudo) states

In UML initial pseudo state is not the real initial state. The initial state is the one the pseudo state points to.

pics/desugar-05-initial.png

SCXML lives by this realization as well. It does support the initial pseudo state, but it also has an initial attribute in both the top (scxml node) level and at the state level. state machine cat usually transforms initial states into those attributes. E.g. the above state machine would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" initial="off" version="1.0">
    <state id="off">
        <transition event="tap" target="on"/>
    </state>
    <state id="on">
        <transition event="tap" target="off"/>
    </state>
</scxml>

The only reason to still have an <initial> pseudo state in an scxml document is to express that when you go into the real initial state for the very first time, you want an action to happen.

Hence, if state machine cat sees an action on a transition from an initial pseudo state to the real initial state, it'll put an initial pseudo state in the output.

initial => off: /regurgitate the brimstone;
<scxml xmlns="http://www.w3.org/2005/07/scxml" initial="initial" version="1.0">
    <initial id="initial">
        <transition target="off" action="regurgitate the brimstone">
    </initial>
    <state id="off">
    </state>
</scxml>

initial: the Highlander rule

There can be only one initial state per state machine, and each initial pseudo state can only have one transition out. This means there's at most one initial state on scxml level, and at most one initial state for each composite state.

State machine cat (currently) does not enforce this rule, so it has to choose. If there's more than one initial state, or more than one transition out of an initial state it picks the first one it encounters.

Events on states

smcat recognizes the entry/ and exit/ keywords and treats everything after it on the same line to be the 'body' of the trigger.

So this

landing:
  entry/ cabin crew take your seat
  entry/ cabin crew arm the slides
  exit/ cabin crew disarm the slides
;

translates into

<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
    <state id="landing">
        <onentry>cabin crew take your seat</onentry>
        <onentry>cabin crew arm the slides</onentry>
        <onexit>cabin crew disarm the slides</onexit>
    </state>
</scxml>

fork, join, junction and choice pseudo states

At the moment state machine cat makes these into full fledged SCXML states. This is not incorrect, but de-sugaring might be a better approach. I've kept that as a future feature for now.

Events on transitions

If you use the UML way of writing events on transitions, like so:

a => b: event [condition] / action

state machine cat extracts the correct events and emits them into SCXML

<state id="a">
    <transition target="b" event="event" cond="condition">
        action
    </transition>
</state>

Transforming state names to valid XML id's

State id's in SCXML have constraints the smcat language does not have. state-machine-cat transforms state name to conform to these constraints. It uses these rules:

  • If the state name is a valid XML id already it leaves it as is.
  • Replace all invalid state name characters with _'s
  • If the state name starts with a character that (1) is valid in an XML id, but (2) not as a start character it puts a _ in front of it.
  • If the state has no name or an empty name: use empty as a name.
state name valid XML ID
On On
"media player on" media_player_on
"8 ball shaking" _8_ball_shaking
"" empty

One of the consequences of this transformation algorithm is that in edge cases it's possible to get unintended name clashes. E.g. when you have a "yes|no" and a "yes no" state, they'll both map to "yes_no".

Especially with sparsely named pseudo states this might spell trouble; a choice state named ^ and a fork state named ] will both map to _. The obvious way to prevent this is to name the choices/ forks/ joins. It'll make your smcat source more readable in the process:

^cool? -> "tell @sverweij": no;
^cool? -> "star state-machine-cat on github": yes;

Transforming events into valid SCXML event descriptors

In SCXML event descriptors follow some syntactic rules that don't exist in smcat:

Without transformation this would a.o. mean that the event in meow -> eat: human gives food; would in scxml be interpreted as three events; human, gives and food.

  • state-machine-cat treats new lines as event separators.
  • For each of the events that remain after separation:
    • If the event name is a valid id already it leaves it as is.
    • It replaces all 'invalid' event characters with _'s
    • If the event starts with a character that (1) is valid in an XML id, but (2) not as a start character it puts a _ in front of it.
    • If the event has no or an empty name: use empty as a as event descriptor.
meow -> eat:
  human gives food
  human leaves food on the table;
<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
    <state id="meow">
        <transition event="human_gives_food human_leaves_food_on_the_table" target="eat"/>
    </state>
    <state id="eat">
    </state>
</scxml>

Reading from SCXML

Handling target-less transitions

Translating SCXML into state-machine-cat's schema is more straightforward than writing to it as SCXML is generally more restricting. The only exception is that SCXML allows transitions to have no target. We handle these as if they were 'self-transitions'.

XInclude not supported

At this moment state-machine-cat does not support referencing state machines with XInclude.

Handling 'invokes'

In SCXML a state can have invokes. These are not core constructs, but they're used in practice and can be fairly straightforward to represent in a state chart.

State-machine-cat currently shows them in the same section as it shows actions. For example ...

<?xml version="1.0" encoding="UTF-8"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0">
    <state id="doing">
        <invoke id="doSomething()"/>
        <invoke id="doSomethingElse()"/>
        <invoke>does an invoke with a body make sense?</invoke>
        <invoke/> <!-- likewise, does an empty invoke make sense? -->
    </state>
</scxml>

... will show up as ...

pics/scxml-invoke.png

References

Why?

The SCXML specification seemed useful. The only way (for me) to grasp a specification is to use it. Writing an SCXML renderer for state machine cat fit the bill.

This has had two side effects:

  • State machine cat models are now a little more interoperable with the rest of the world.
  • I realized state machine cat could be expanded with some useful concepts:
    • history states (implemented in v2.3.0, deep history in v2.6.0)
    • initial and final states within composite states (implemented in v2.3.0)
    • separate event [condition]/ action in the internal models (implemented in v2.3.0)
    • the scjson format - which is conceptually equal to scxml but in javascript is lot easier to read from and translate into (implemented in v2.3.0)
    • onentry, and onexit events on states (implemented in v2.4.0)
    • parallel states (implemented in v2.5.0)
    • model rules (e.g. one initial per state machine, only actions on initial pseudo to the real initial, ... - future feature)
    • de-sugaring pseudo states into core constructs (!experimental! feature as of v5.2.0)