Queued message handler with multiple process loops
The queued message handler contains multiple process loops operating independently and in parallel that communicate with each other by sending messages through queues. Each process is a well-defined task implemented by the "Queued State Machine" design pattern. Breaking up the system into self-contained tasks greatly simplifies the design of complex systems.
Use cases
RT system controller with network interface to user interface running on PC host
Design technique for complex systems that are reliable, scalable, and maintainable
Features
Multiple parallel process loops, each based on the Queued State Machine (QSM) design pattern, and each with its own named queue
Process loops operate independently and communicate with other process loops by sending messages to the queue of that process
The message is a string of the form “state:params” in which the optional “:params” provides state-specific parameters
Three standard process loops for the queued message handler (QMH) design pattern include:
System Manager – maintains the high-level state of the overall system, issues commands to the system-specific task loops, and receives alerts (events) from all of the other process loops in the system
Command Parser – waits for a network stream connection request from the PC host human-machine interface (HMI, implemented as an event-driven producer-consumer state machine), and then translates messages from the PC into commands to insert into the System Manager queue
Error Handler – all process loops trap errors and forward them via a special queue to the error handler which takes appropriate action such as shutting down all of process loops immediately (useful during software development) or logging the error and notifying the user (appropriate for a deployed application that should continue running if at all possible)
The remaining process loops implement application-specific tasks such hardware I/O, timers, FPGA control, etc.
See the complete Home Security System project based on the “Queued Message Handler” pattern.
Keep in mind
A queue is a memory structure maintained by LabVIEW. The data wire created by the “Obtain Queue” function does not contain the queue elements themselves, but rather a reference to the queue. This can initially be confusing when looking at block diagrams based on queues, since we are accustomed to thinking of data traveling along the wire. When you trace the queue wire throughout the block diagram, remember that it simply conveys the “address” of the queue to any subVI that needs it.
Queues can be named when first created and then referenced by name later on, which can greatly reduce wiring complexity at the expense of some CPU overhead. Provide the “Obtain Queue” function with the name of an existing queue to receive a new reference to the existing queue, and then use the “Release Queue” function when you are done with this queue reference – this does not remove the queue itself, but only the newly-obtained reference.
The support VIs “enqueue single message” and “enqueue multiple messages” accept the name of the queue as well as the state as a single string and take care of the previously-mentioned details
Referring to queues by name instead of by wire obscures the communication paths between the loops, and potentially can make debugging more difficult. Use the LabVIEW “Find” facility (Ctrl+F) to search for all instances of the queue name, e.g., to track down an enqueue operation in a process that you had forgotten about.
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
Observe the initial state of the “Command Parser” QSM:
State is “wait for connection”
Loop iteration indicator increments once per second (QSM regenerates “wait for connection” state until PC host establishes connection)
Click “Connect” to establish the network stream connection to the system controller, and observe the following changes to the command parser indicators:
State moves to “connected”
“PC host connected” flag is true
“reader endpoint” is “/RT-reader” indicating network stream endpoint has been established
Click “Disconnect” to close the network stream connection, and observe the following changes to the command parser indicators:
State moves to “wait for connection”
“PC host connected” flag is false
“reader endpoint” is empty
“command” received from PC host is “Disconnect”
Click “Connect” again
Observe “command” and then click “Reset”; observe that the received command is “SysMgr/Initialize”
Continue to observe “command” and then click “Valid Code”; observe that the received command is “SysMgr/alert:valid”
Continue to observe “command” and then click “PANIC”; observe that the received command is “SysMgr/alarm”
Click “Reset”. Observe the “system manager state” and click “PANIC” again; observe how the system manager moves to the “alarm” state as a result of the command parser enqueuing this message
Click “Manual Stop” to stop the controller – the Error Handler enqueues the “Shutdown” message to every QSM in the system, including itself
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:
Each loop is an independent process running in parallel
Process loops implement behavior with queued state machines (QSMs); see the “Queued State Machine” module for complete details
Process loops transfer messages between each other through named queues
Queues are referenced by name much more often than by wire, therefore most of the connections between the process loops are not visually evident
System Manager
Maintains the high-level operating mode of the overall system and issues commands to the application-specific task loops
Task loops issue alerts to the System Manager
Task loops can directly message other task loops to reduce complexity of the System Manager
Command Parser
Accepts connection requests from PC host human-machine interface (HMI), also called the user interface (UI)
Receives commands from PC host, parses them, and inserts messages into the System Manager queue
Uses network stream communication with PC host
PC host HMI
Producer loop translates button clicks into messages to send as well as “Connect” and “Disconnect” states
Consumer loop handles PC host side of network stream
Error Handler
All process loops forward their error messages to the error handler queue
On receiving an error message the error handler issues a “shutdown” message to all process loops, displays the error message, and stops the VI (useful for development and debugging)
A deployed production system would log the error message to a file and attempt to keep the VI running; normally the RT application should continue to run while gracefully handling error conditions
Manual stop button (used only for development/debugging purposes)
Polls the “Manual Stop” button and enqueues the “Manual stop” error into the error handler queue
“enqueue single message” VI
Accepts a string of the form “QueueName/Message”
Separates the string into the queue name and the message to enqueue
Obtains a new temporary queue reference to an existing named queue; does NOT create a new queue if the queue reference is not found, but instead generates an error message (this catches mistyped queue names)
Enqueues the message string
Releases the temporary queue reference; the queue itself is not released
Only runs when enabled
Implements standard error behavior
“enqueue multiple messages” VI
Accepts an array of strings of the form “QueueName/Message”
Loops through the array and invokes “enqueue single message” VI
Reduces block diagram clutter when multiple messages must be sent
Only runs when enabled
Implements standard error behavior
Command Parser QSM states
“wait for connection”
Create network stream reader endpoint
string datatype
user-adjustable timeout (default of 1 second)
buffer size indicates maximum number of messages expected in one transmission from PC host
Check for timeout error
No error: Network stream established with PC host, enqueue the “connected” state, store the reader endpoint, and set the “PC host connected” flag
Error: Clear the error and enqueue “wait for connection” state
“connected”
Wait for a message from PC host using the user-adjustable timeout value
Timeout: Re-enqueue the “connected” state
No timeout: Enqueue the “disconnect” state if the received string is “Disconnect”, otherwise re-enqueue the “connected” state and enqueue the received “QueueName/Message” string
“disconnect”
Destroy the network stream endpoint
Clear the “PC host connected” flag
Enqueue the “wait for connection” state
Error Handler QSM
Single “run” state
Waits for an error message to be available in the error handler queue (blocks indefinitely)
When an error message is received, retrieves an array of all queue references in the system (including the error handler), enqueues the “Shutdown” state to all message queues, and stores the error message in its data highway