Software Overview  |  Sitemap  |  Downloads  |  Developers  |  Forums
Small_clear_logo_llc

Table Rows and When, State, and Then Statements

We've seen that State Machine tables are built out of rows, and tables can be written in a compact or expanded YAML syntax. Here we'll discus the differences between the two YAML syntaxes and some details about When, State and Then statements.

2 YAML Syntaxes

YAML supports multiple syntaxes. We'll be exploring two of them. We'll call them "compact" and "expanded". A compact table row looks like this:

{When: {in1: "value1"},  State: {in2: "value2"}, Then: {out: "value3"}}

The same row written in expanded syntax looks like this:

When: {in1: "value1"}
State: {in2: "value2"}
Then: {out: "value3"}

Though it has more lines, the expanded syntax is generally more readable. Expanded syntax uses new lines in place of some of the brackets in the compact format. When looking at a complete table, there are other differences between the two syntaxes (see the compact YAML and expanded YAML table examples in the previous section).

Generally, the differences can be summarized by saying the expanded syntax uses white space (new lines and indentation) in place of some of the brackets of the compact format*. For larger tables, the expanded syntax tends to be easier to read.

  • Note: When using indentation, be sure you are indenting with spaces and not tabs. YAML does not allow tabs.

When, State and Then Statements

Every table row is built from When, State, and Then statements. "When" statements look for actions. State statements check state. Then statements create actions.

In a row, the State and Then statements are optional. Without a State statement, a row becomes active any time a When condition is met. A row without a Then statement is active anytime both its When and State statements are satisfied, but it performs no action. Then-less rows are useful when you want to keep a particular row, lower in a table, from causing an action.

Events in When and Then Statements

What exactly is an action? Usually, an action is a state change, say a signal going from "on" to "off". There are more subtle types of actions as well. If you were using the Device Explorer and clicked on a State Machine terminal and changed its value from "on" to "off", the State Machine would see that as an action (and check its When clauses for "off" actions). But what if you clicked the same terminal again and reapplied the same value, "off"? You aren't changing the value, it was "off" already, but you are performing an action. For any action, whether the action changes a value or just reasserts a value, the State Machine will see it and check its table.

Generally when talking about State Machines, we use the word "event" instead of "state change" or "action", because an event doesn't imply a change of state the way the other two phrases do. Events trigger When statements, and Then statements create events.

Here is a row which creates an "off" event on the "out" signal any time there is an "off" event on the "in" signal.

{When: {in: "off"}, Then: {out: "off"}}

This row will generate an "off" event even if the "in" signal was already "off". If you wanted the row to only respond to changes, you could precede it with a Then-less row. Here is the original row with a Then-less row before it. The original row will only be active if the "off" event causes a state change.

{When: {in: "off"}, State: {old_in_val: "off"}}, # consume event and do nothing
{When: {in: "off"}, Then: {out: "off"}}

Signals and Values

When we write When, State, and Then statements, we write signal value pairs. In the following When statement:

When: {in: "on"}

the signal value pair is in: "on". The signal part of the pair is in, and the value part is "on". Signals must begin with a small letter and be followed by letters A-Z, a-z, and underscores. Values must be quoted strings, true/false, and numbers.

Which Signals are the Inputs and Which are Outputs?

In a State Machine, it's generally clear what signals are input signals and output signals. Input signals show up in When statements, and output signals show up in Then statements. What shows up in a State statement could be either or neither.

What if we used the same signal in both a When and Then statement?

{When: {signal: "on"}, Then: {signal: "off"}}

There is nothing which prevents us from doing this. This row would change "signal" to "off" every time it turned on. Generally, it makes sense to build State Machines that respond to inputs and change a different set of outputs. However, sometimes State Machines have more than one output, and we can think of them as being a group of separate little machines. Here is a State Machine with 3 parts. 2 monitor inputs and set "active" outputs. The third sets an alarm when either monitor is active:

{When: {in1: "on"}, Then: {in1_active: "on"}},  
{When: {in2: "on"}, Then: {in2_active: "on"}},  
{When: {in1_active: "on", in2_active: "on"}, Then: {alarm: "on"}}

In this example, one could just as easily have written:

{When: {in1: "on", in2: "on"}, Then: {alarm: "on"}},

However, if the two outputs were harder to generate, generating "alarm" from the outputs makes a lot of sense.

When a State Value Changes

We are going to discuss a subtlety that happens when a row's When statement receives an input event. When an event satisfies a row's When statement, is the state carried in the event seen by the row's State statement? In the row below:

    {When: {in: "value"}, State: {in: "value"}, ...}

When the "When" statement is true, is the State statement true, false, or unknown? We can rule out false, but what about true or unknown? If State is the state of "in" from a time before the When occurs, then the state of "in" in the State statement is unknown. If the state of "in" is the state after the "When" has occurred, then the State statement is true whenever the When is true.

The answer to the question is the State statement is true. "State" sees the state carried in the "When" statement. Using similar reasoning, the statement:

{When:{in:"value1"},  State:{in:"value2"}} # "in"'s value is "value1"

is never true.

Event Ordering in State Space

If you've seen the State Space Device section, you know you can have more than one State Machine in a State Space. When you have more than one State Machine and your State Machines are sharing signals, how are events processed? In the order they occur.

To better understand event ordering, think of all State Space events as going into a single queue - as soon as a State Machine creates an event, it's queued. While queued, an event does nothing. One at a time, events are dequeued and processed. During processing, events may create other events which get queued.

Coming back to multiple State Machines in State Space, when a State Machine generates an output event, it gets queued. Any events which came before it will get processed first. When all previous events are processed, the output event gets processed.

Imagine there's an event which goes to multiple Machines in a State Space. It causes them to generate output events which affect each other. Sounds messy. Who gets the event first? After getting the event, which State Machine gets it next? How is state affected by the Machine which ran before it?

With queuing, the answers are pretty simple. It doesn't matter which State Machine gets the event first, because it won't change anything until all the State Machines have seen the original event. Once all the machines have seen it, any events they might have generated get dequeued and processed. These later events may stimulate other machines, change state, etc.

Multi-Signal Statements

In their simplest form, statements are a When/State/Then followed by a single signal-value pair. The following row has each of the 3 statement types followed by a single signal-value pair.

{When: {w1: "wv1"},  State: {s1: "sv1"}, Then: {t1: "tv1"}}

Any statement can have more than one signal-value pair. Our "When" statement could have 2 inputs:

{When: {w1: "wv1", w2: "wv2"} ....}

And so could our State and Then statements:

-
  When: {w1: "wv1", w2: "wv2"}
  State: {s1: "sv1", s2: "sv2"}
  Then: {t1: "tv1", t2: "tv2"}

These multi-signal statements mean different things for each of the Statement types. "When"s for multiple signals mean when any one of the events occur. So in the above example, the When will be true when either the "w1" input sees a "wv1" event, or when the "w2" input sees a "wv2" event.

When a "State" statement has multiple terminals, it means all the terminal state must be satisfied. So in the above example, the row's State statement will be true when "s1" is "sv1" and "s2" is "sv2".

When a "Then" statement has multiple terminals, it will create events for each of the signals. In the example above, if the When and State statements are satisfied, the State Machine will create a "tv1" event on terminal "t1" and a "tv2" event on terminal "t2".

The Wildcard Value

We've seen that When, State, and Then statements are built from one or more signal-value pairs. Generally values are strings or numbers which are matched (in When/State statements) or emitted (in Then statements). The value "*" is different. It's called the wildcard value. The wildcard value means "any value".

In When Statements

In the case of a When statement, the row:

{When: {w1: "*"}, ...}

means the When will match any event from the "w1" signal.

Wildcards are most commonly used in When statements. Often one wants to know if an event has occurred, but its value is not important.

Here's a table which generates "on" events when 3 inputs are "on", and "off"s otherwise.

"my_function": [
  {When: {in1: "on", in2: "on", in3: "on"}, Then: {out: "on"}},
  {When: {in1: "*", in2: "*", in3: "*"}, Then: {out: "off"}}
]

Without the wildcard, coding for all the possible values when the 3 inputs were not "on" would have required a longer table.

In State Statements

Wildcards are less commonly used in State statements. The row:

{When: {w1: "wv1"}, State: {s1: "*"}, ...}

means the State will match any state on the "s1" signal.

Generally, wildcards are not useful in State statements, because you can achieve the same result by leaving the wildcarded signal out altogether. Put another way, when you leave a signal out of a State statement, it means the signal can have any value.

Though functionally there is no reason to use wildcards in State statements, sometimes they are useful. If you are developing a State Machine, but have not fleshed out all the rows, you can make placeholder rows with State signals included with wildcards, and finish them up later. In meantime, the wildcarded State signals serve as reminders of the missing signals.

In Then Statements

Wildcards in Then statements are not supported.

The Uninitialized Value

Like the wildcard value, the uninitialized value is a signal value with a special meaning. The uninitialized value is typed as "" (empty quotes), and it means a signal has no value. When a State Machine starts up, all its signals start with no value. They get a value when they receive their first event.

The uninitialized value is only used in State statements, and they test if a signal has been initialized. Here is part of a State Machine which catches and holds the first time an "in" signal is "on":

{When: {in: "on"}, Then: {out: "on"}},
{When: {in: "*"}, State: {out: ""}, Then {out: "off"}}

What's unique is the State Machine knows when its output is uninitialized, and uses the first event it sees to give it a value.

The uninitialized value allows State Machines to know when signals are uninitialized and perform any one time initializations.

Multivalued Signal Values

All the signal values we've seen are single values. "When" and "State" statements also understand multivalued signal values. Multivalues are typed like this: ["val1", "val2", ....], and they mean a signal can have any one of the values.

The row:

{When: {in: ["on", 2]}, ....}

means the When statement will be true when an "in" event has value "on" or 2.

The row:

{When: {in: "on"}, State: {state: ["off", "on"]}, ....}

means the State statement will be true when the "state" signal has state "off" or "on".

Multivalued State statements can include the uninitialized value, "". Wildcards are not allowed (a multivalue with a wildcard is no different from "*").

Multivalued Then statements are not supported.

Output String Interpolation

Sometimes, you want to generate a State Machine output value which isn't fixed - it's a function of other values within the State Machine. So for example, suppose you have an "Then" statement which looks like this:

{..., Then: {out: "\"The current time is:\""}}

Here, you want the current time added to the end of the Then output value. Suppose the State Machine has a signal, "time", whose value is a string containing the current time. Then we could write:

{..., Then: {out: "\"The current time is: #{time}\""}}

If the time signal had the value "10:24:15", the "out" signal of the State Machine would be:

"The current time is: \"10:24:15\""

So in any Then statement, you can reference the value of any signal within the State Machine by using the #{} construct. Put whatever signal you want inside the curly brackets, and its value will show up in the Then value (when it fires). You can put as many signals as you want into a Then output by adding more #{} constructs.

Note:

  • String interpolation is only supported in Then statements. It is not supported in When or State statements.

  • String interpolation is only supported in Then outputs which are quoted strings (strings which look like "\" .... \"'). This last restriction may seem a little odd, but when you think about it, inserting a value into any other kind of string would make no sense to Virtual Wiring. Without the extra set of quotes, Virtual Wiring would try to interpret the entire string's contents (which would almost never make sense).

Catalina Computing, LLC.

Copyright © Catalina Computing, LLC. (2013-2018)




Page last updated: Thu Nov 8 19:14:18 2018 (UTC)