State machines perform system control, data processing, and any task that involves executing a sequence of activities in response to inputs from the surrounding physical system, the user interface, and other processes within the system. The queued state machine is a particular implementation style that is flexible and versatile, easy to maintain, and computationally efficient.
Use cases
System controller that maintains high-level operating mode of the complete system
Process loops such as hardware I/O interfacing, timers, command parsers, and error handlers
State machines that are reliable, scalable, and maintainable
Features
Single-loop design in which the system state is stored in a queue
Queue contains message strings of the form “state:params” in which the optional “:params” provides state-specific parameters
Guard clause (a Boolean case structure) inspects the error cluster on each loop iteration and acts as follows:
No error – dequeue message and return the state and optional parameters; blocks (waits) when the queue is empty
Error – enqueue the error message to the error handler (another QSM) and return “Error” as the state
Kernel (a subVI containing a case structure) contains the particular tasks to be performed for each type of state message, possibly enqueuing one or more states for subsequent activity
Standard QSM states include:
Default case – traps unrecognized state strings and inserts an error into the error cluster; useful for catching mistyped state messages
Initialize – initializes the data highway cluster to user-specified values and performs any other necessary start-up activity
Shutdown – performs any needed shut-down activity and then stops the loop by feeding a “true” constant to the loop conditional terminal
Error – performs any additional actions that may be required after the guard clause handles an error
Data highway cluster collects inputs, outputs, and any persistent variables into a single cluster wire based on a type def
Feedback node maintains the state of the data highway from one loop iteration to the next
See the complete Home Security System project to see a variety of QSMs that work independently in parallel and communicate with each other by passing messages to each other through queues.
Keep in mind
The LabVIEW “Dequeue Element” function in the guard clause waits indefinitely for a state message to become available to promote event-driven behavior, that is, the state machine only expends CPU effort when there is a task to execute instead of repetitively checking to see what task to perform next
Tasks that must continue to run from one loop iteration to the next (for example, to poll an input or to run a timer) must re-enqueue the same state; include a “Wait” function in the process loop to set the iteration time to an appropriate value
The kernel subVI must not contain any functions that block data flow, otherwise the guard clause will not be able to respond to new messages in the queue (such as “Initialize” and “Shutdown”) and the QSM will appear to have locked up
LabVIEW block diagram elements
Locate these elements with "Quick Drop" (press Ctrl+Space and start typing the name); click on an icon to see more sample code that uses that element:
Example code
Connect your Academic RIO Device to your PC using USBLAN, Ethernet, or Wi-Fi. NOTE: Not all Academic RIO Devices have Ethernet and Wi-Fi connectivity options.
If using the NI myRIO 1950 or NI RIO Control Module start with the NI myRIO 1900 Archive.
Different IP address: Right-click on the "NI myRIO 1900" Device, choose "Properties", and then enter the new IP address
Different device:
Right-click on the top of the project hierarchy, select "New Targets and Devices", keep the "Existing target or device" option, and then find and select your particular device
Select all of the components under the "NI myRIO 1900" device: click the first one and then shift+click the last one
Drag the selected components to the new device
Right-click the "NI myRIO 1900" device and select "Remove from project"
Open and run “RT Main”; see Home Security System for complete operating instructions, including “PC Main” which is a remote user interface that runs on the PC host
Study the right side of the front panel that indicates the state of each process loop (QSM), loop iteration count, and data highway variables as you control the user interface panel. Loops that handle sensor inputs run continually because they poll the sensors for activity. The other loops only run as necessary, thereby making efficient use of limited CPU resources.
Compare the “code validator” QSM to the following pseudo-code description of its behavior:
“Initialize” state: Initialize the data highway values
Set the data highway values:
“timer initial value” = 30
“time remaining” = 30
“stored code” = 123
Enqueue the “get code” state
“get code” state: Assemble a three-digit code from the keypad and compare it to the stored code; restart the process if too much time elapses between any keypress
Set these data highway values when a new keypad digit is available
“time remaining” = “timer initial value”
increment “digit count”
append “keypad digit” to “entered code”
Send these messages to other QSMs when a new keypad digit is available
“keypress beep” to “Buzzer”
“valid code” alert to “System Manager” if “entered code” matches “stored code”
Set these data highway values when a new keypad digit is not available
decrement “time remaining” if “digit count” is greater than zero
Enqueue the “Initialize” state if “digit count” is greater than two or “time remaining” is less than zero, otherwise enqueue the “get code” state
Click “Manual Stop” to stop the controller – this inserts an error into the error cluster which is trapped by the error handler to cause a shutdown of the entire system
“Obtain Queue” function creates a named queue and returns a reference to the queue
IMPORTANT: The wire is NOT the queue itself, but rather serves as a way to refer to the queue memory structure that LabVIEW creates; do not think of the wire as somehow carrying the enqueued values themselves
“Enqueue Element” function enqueues the string “Initialize” as the first state message
Process loop
“Guard Clause” VI dequeues the state message and returns a string; an error generated on the previous loop iteration is enqueued to the error handler queue and “Error” is returned as the state message
“Kernel” VI acts on the state message; includes standard actions such as “Initialize,” “Shutdown,” and “Error” as well as the application-specific tasks
Feedback node maintains persistent storage of data values contained in a user-defined cluster or “data highway” for the kernel from one loop iteration to the next; the data highway often includes values from I/O devices
Loop stops when the kernel generates a Boolean “true” as part of the “Shutdown” action
“Release Queue” function destroys the queue after the process loop stops
“Guard Clause” VI:
Boolean case structure selected by the error cluster status
Error status = false
Block data flow (wait indefinitely) until the queue contains a state message; the QSM consumes essentially zero CPU resources until then
Dequeue the state message when available and return it as a string
Error status = true
Ignore the message queue
Enqueue the error message to the error handler (another QSM)
Return “Error” as the state message
Returns a duplicate of the queue reference for wiring convenience
Must set the execution mode to “preallocated clone re-entrant” (type Ctrl+I and select the “Execution” tab) to allow multiple instances of this VI to operate independently of one another
“Kernel” VI:
Implements specific behavior in response to each state message
Implements LabVIEW standard error behavior: performs no action when inbound error cluster status is false other than pass-through
Custom control (strict type def) defines a cluster of values for persistant storage and I/O; initial values must be set here because user-defined defaults in the type definition are not available
“State & Args” VI splits the state message into the state string and the optional arguments (or parameters) string; a colon “:” delimits the state from the argument(s), e.g., “initCounter:10” could mean initialize a counter with value 10
Case structure selected by state string with one subdiagram for each possible state; each subdiagram implements the behavior required for the state
Standard states (indicated by upper-case first letter):
Default – unrecognized state string (most likely due to a typo elsewhere in the block diagram); call “error - unknown state” VI to return a custom error message that includes the queue name and the unrecognized state string
Initialize – set the data highway cluster to the start-up values, perform any other necessary start-up activity, and (usually) enqueue the first application-specific state
Shutdown – performs any needed shut-down activity such as turning off peripheral devices; set the “stop” output to “true”
Error – performs any additional actions that might be required after the guard clause handles an error
Application-specific states (indicated by lower-case first letter) often include these behaviors:
Read selected data highway values with an “Unbundle by Name” function to access locally-stored data values (see “run” subdiagram of “kernel - timer” VI) or to access an input device value (see “monitor” subdiagram of “kernel - sensors” VI)
Modify the data highway values, e.g., increment or decrement a counter, or change a Boolean status flag
Write selected data highway values with a “Bundle by Name” function to update locally-stored values or output device values
Enqueue the next state in the sequence (unconditional or conditional); see “kernel - command parser” VI
Regenerate the current state (enqueue the same state string) to continue an activity from one loop iteration to the next, e.g., to run a timer or to poll an input peripheral device; requires a “Wait” function in the process loop to define the loop iteration time (see “get code” subdiagram of “kernel - code validator” VI and “run” subdiagram of “kernel - timer” VI)
Send a message to another QSM with “enqueue single message” VI or multiple messages with “enqueue multiple messages” VI
Handle mistyped parameters in the state message with “error - unknown parameter” VI; see “alert” then “valid” subdiagram of “kernel - system manager” VI (note use of case-insensitive match option)
Application-specific states must NOT block data flow, otherwise the guard clause will not be able respond to new state messages in the queue
TIP: Use LabVIEW “Find” feature (Ctrl+F) to locate state strings by name