State Machines are great for monitoring and controlling other Devices. Their behavior and their terminals are both user programmable, so they can be custom fit into many different applications. Aside from a user written Virtual Device, a State Machine is the most programmable and flexible Device in the system. Common uses for State Machines are intelligent sensor monitors, light switching systems, and control systems.
The State Machine Table
State Machines are built around a user defined element called a table. Tables specify a State Machine's behavior, input and output events (actions), and the state they monitor. Here is a simple State Machine Table.
Without going into the details, you can probably see that this table turns its output off when its input is off, and turns its output on when its input is on.
Tables have a name and a set of rows. In the example above, the table is named "simple_table", and there are 2 rows. Rows are bracketed by s, and they are separated by commas. In the example above, the rows are:
Rows contain from 1-3 statements. Statements are one of 3 types: When, State, and Then. Every row has one When statement, and can optionally have one State and one Then statement. In the example above each of our 2 rows has a When statement, a Then statement, but no State statement.
"When" statements define State Machine input events, State statements describe the state being monitored, and Then statements specify actions or output events. The three statements work together in a row, and they interact as follows. When an event defined in a row's When statement occurs, it causes the State Machine to check its State statement. If the state described in the State statement matches the actual state, the action defined by the row's Then statement is taken.
Here's an example of a row from a state machine table:
In the row above, there is a When statement, a State statement, and a Then statement. As described, when the "When" statement is satisfied, the State statement is checked. If both statements are satisfied, the Then action is taken. Here, the When is looking for either when "in1" is asserted
As mentioned, State and Then statements are both optional, while a When statement is required. In the row above, had there been no State statement, the Then action would have been taken right after the When statement was satisfied. Had there been no Then statement, nothing would have happened, but the row would still have been considered valid and properly executed.
Most tables have more than one row. When there are multiple rows, it's possible that an event and state could cause more than one row's Then and State statements to get satisfied. What happens? Here is how multi-row tables operate.
Whenever an event occurs, a State Machine will find every row in its table that is sensitive to that event (it knows how to do this from the "When" clauses). Next, starting at the topmost found row and progressing to the bottommost, the State Machine will look for the first row whose "State" value matches the actual, current state (a row with no State always matches). If it finds such a row, it will perform that row's action. If it doesn't find any matching rows, it will take no action. If there is more than one row that matches, it will apply the Then clause from the first row (the topmost row) it finds and ignore the other (lower) rows. If the topmost row it finds has no Then clause, it will still consider that row found (and perform no actions at all).
Here is a State Machine table which describes a counting behavior. It counts higher whenever its "in" input is asserted
Notice in the 1st and 3rd rows, the output state (the "Then" part) is only partially specified - "out2" is not mentioned. It would be OK to specify the state of both "out1" and "out2" in those rows, but since "out2" is already in the state we want it in, there is no need put it there again.
Hopefully, even without understanding the details, what's going on in a table is pretty clear. If not, learning the YAML language is time well spent, as it shares a common syntax with many other languages.
Because YAML's sole purpose is to express objects, it's quite good at it. Tables can be expressed in the common format we've been using, or in other formats. Among its many tricks, it also supports a syntax which has more white space and less brackets and commas.
Here is our original "simple_table" expressed using white space (indentation) instead of brackets and commas:
Here is our same counting table with the white spaced syntax:
Both syntaxes are equivalent and both are valid YAML. Sometimes, you may prefer writing YAML using white spaces, because it looks cleaner. Other times, perhaps for small tables, you may prefer writing in the more compact style. Notice that the less compact YAML starts each table row with a "-", and the When/State/Then statements are indented. For more information on YAML see: http://en.wikipedia.org/wiki/YAML.
When writing your table files, make your file names end with ".yaml" or ".yml". Either of these two file name endings tell the system you have written your state table(s) in YAML.
More Details about Table Rows
We've given an overview of the syntax for tables, table rows, and the When, State, and Then statements. For more information about Rows and When, State, and Then statements, see the Table Rows and When, State, Then Statements section.
There's a special kind of row we haven't covered yet. It has a single statement called an Initialize statement. Initialize statements cause State Machines to assume certain values at initialization time. To learn more about Initialize statements, see the Initialize Statement section.
Creating State Machines
Like all Devices, State Machines are created by running Scripts. To learn how to build State Machines, see the Building State Machines section.