System Definition (No States)
What? No states! The package is called "...Hierarchical State Machine..."!
But you did read the description, didn't you? It talks about "active objects" even before it mentions state machines. Let's investigate this aspect by building two interacting objects. (Oh, and we will still really include HSMs, but we won't be adding any new states beyond those included in the template.)
We'll define a very simple system: two otherwise independent VIs sending text messages back and forth.
What do we need in our objects?
First, we do need two objects: a master and a slave. They are nearly identical, except that the master will initially launch and finally close the slave. Since the two are nearly identical, let's build the master and then edit a duplicate copy to make the slave. (Copy and paste is great!) This is really not that different from the way we built Project 2 from Project 1.
Instead of considering the states, events, and actions first, this time we'll take a different approach to defining the system: we'll start with considering what the panels should look like.
The Panel View
What do our objects need to look like in order to accomplish this text messaging? Well, obviously, there needs to be a text control for the text to be sent to the other object, a button to send that text, and a text indicator to display the text received from the other object, as in Figure 1.
What events are needed to support such a panel? Obviously, pushing the SEND button will cause an event. Let's call it Send Requested. But there must also be an event resulting from pushing the SEND button on the other panel. We'll call that Message from Slave. (Remember that we're building the master first and then editing that to form the slave.)
To be useful, pushing the SEND button must cause a Send Message to Slave action. By analogy, pushing EXIT on the master must cause a Send Exit to Slave, as well. To do this sending, we'll need a communication channel with the slave, so we'll need a Connect to Slave action to set up this channel and a Disconnect from Slave action to clean things up when we're through. To set up a communication channel with the slave, the slave must be running, so we also need a Launch Slave action. Finally, when a message comes in, we need a Decode and Display Msg action to make the message visible in the Received message indicator.
(It is common in LabVIEW to hear operations like the above referred to as "states": Sending Message..., etc. But they really aren't states. States should be terminated by events that cause transitions to various other states, determined by the value or type of event that occurred. The type of operation listed above terminates when it is done, typically in microseconds, and the sequence of subsequent operations is determined only by whether the current operation failed. It is much clearer and less convoluted to consider them atomic and essentially instantaneous actions rather than persisting states.)
In the traffic light example, we toyed with combining some actions but decided to keep them all atomic for that project. Here, we'll combine the launch and the connect actions into one: Launch and Connect to Slave, while we'll leave the Send Exit and Disconnect actions separate, even though they occur together. (Combining the Send Exit and Disconnect actions would have gotten us in trouble when we tried, below, to match the behavior of our project to the example that ships with LabHSM.)
If you have not done Project 1 using LabHSM version 1.1, please at least read the
Getting Started section now.
Create a new HSM VI either from the LabHSM - HSM Template.vit or by dropping LabHSM - HSM Template.vi on a blank VI.
Save this VI as Demo_Master (run me).vi in a convenient place.
The "run me" is just a little hint to remind us how this is supposed to work when we come back to it a few months from now.
Open the LabHSM Editor... from the Tools menu.
Click the NEW button.
SAVE the HSM file as Demo_Master (run me) in the same folder as the VI.
As previously mentioned, the Send Requested event is caused by pressing the SEND button. This button functions just like the EXIT button, so you know from Project 1 that we will just duplicate the Exit Button:Value Change event case in the event loop to get the new button and the template of its support (Figure 3).
Switch to the block diagram window.
Select the "Exit Button": Value Change case in the event structure.
Duplicate the case.
Choose Exit Button 2:Value Change as the event handled by this new case.
Show the label of the button terminal.
Edit it to "Send Message Button".
Change the string constant to "Send Requested" (pasting it from the Events List Editor to avoid spelling errors).
Figure 3: Updated Event Case
Glance at Figure 1 to refresh your memory about our goal for the front panel: a push button, a text control, and a text indicator.
Double click the Send Message Button terminal to find it on the front panel.
Change the text value and color.
Move it to the left of the panel.
Align it with the EXIT button (which I also moved a little to the left).
Add a string control labeled Message to Send.
Change the duplicate to an indicator labeled Received Message.
The panel should now look like Figure 1. The second new event, Message from Slave, doesn't originate in this VI, so we're done with the events for now. Let's turn our attention to the five actions we have to implement.
Choose the Actions List editor by button or from the menu.
Align it near the case label of the Actions Case Structure, as we have done before.
Select the last action, Run State Machine.
Use Add New After to create the Launch and Connect to Slave action.
A couple of times now, as I thrash around among different versions of the same LabHSM project, the Actions Editor has become disconnected from the block diagram. In the unlikely event that this happens to you, just SAVE then OPEN the .hsm file again to reestablish the connection.
This is the most complicated action (Figure 4, where I have removed two vertical strips from the diagram to conserve space in the illustration).
Figure 4: Launch and Connect Action
To this new action case, add two VIs from the LabHSM palette: Start VI.vi from the main palette and Connect to.vi from the Messaging subpalette.
In the following steps, we flesh out this action. Start VI requires the path of the target VI. Since the master and the slave are in the same directory, we substitute the slave's name in the current VI's (the master's) path using primitives from the File I/O palette. Since we want to see the slave, we ask for its panel to be open. For convenience, we let LabVIEW handle closing the references involved.
Drop the Current VI's Path, Strip Path, and Build Path primitives from the File I/O palette into this case and wire them as shown in Figure 4.
Pop-up on the name or relative path terminal and create the Demo_Slave.vi string constant.
Pop-up on the Front Panel Window: Open and Auto Dispose Ref terminals and create constants for them.
Set both constants TRUE.
To pass a message from one VI to another, there must be a common communication channel. Connect to.vi establishes this channel and provides access to it through a cluster describing the channel. This cluster is required input to the VIs that use and control this channel, so we must preserve it for future use. The communicating VIs example that ships with LabHSM uses local variables to store and recover the data in a front panel indicator.
To illustrate another approach, we'll add a new shift register to the processing loop border for this purpose. In this trade-off, we save memory but incur wiring hassles. All other action cases must have this shift register through-wired to preserve this data. Forgetting any case, results in a broken VI. (The FSM Frequently Used Data cluster already included in the template must also be wired through every case to avoid breaking the VI. David Boyd's
Tunnel_Wiring_Wizard.vi is especially helpful in fixing these problems.)
(If you'd like a third approach, offers an LCOD add-on that encapsulates and hides completely the FSM Frequently Used Data cluster and the local messaging VIs we use in this project.)
Pop-up on the Processing Loop border and create a new shift register. It does not require initialization.
Wire the Target Message Queue Info output of Connect to.vi to the right hand terminal of this shift register.
Label this wire Slave Message Queue Info.
Wire the Demo_Slave.vi constant to the Object Name input of Connect to.vi.
Wire the error clusters.
At this point, we've broken the VI. The new exit tunnel we've introduced in the Actions Case Structure is unwired in most cases. Since the LabHSM Editor creates new action cases from the old ones, it makes the most sense to correct this problem now, while there are the fewest cases that need fixing.
Wire the lefthand terminal of the new shift register to the border of the Actions Case Structure.
Wire this new tunnel through to the new righthand tunnel in all the other action cases (using the
Disconnecting this communication channel (Figure 5) is a very simple operation in the LabHSM package.
Use Add New After to create the Disconnect from Slave action.
Add the Disconnect from.vi (in the Messaging subpalette).
Wire the Slave Message Queue Info and the error cluster through the rest of the diagram.
Label the Slave Message Queue Info wire.
Figure 5: Disconnect Action
Use Add Copy After to create the Send Exit to Slave action (Figure 6), to preserve the through wiring.
Select this new case.
Replace the Disconnect from.vi with the Send Message.vi, also on the Messaging subpalette.
Pop up on the Message (Public Event Name in Another HSM), create a constant, and make it "Exit Requested" (copied from the Events List editor?).
For the moment, ignore the implication of the text in parentheses in the terminal label; we'll learn more about that later.
Figure 6: Send Exit Action
Use Add Copy After to create the Send Message to Slave case (Figure 7).
Select this new case.
Change the string constant to "Message from Master" (copied in part from the Events List editor?).
Find the string control terminal and wire it to the Message (Event) Data input of the VI.
A coercion dot [Mine are red.] will appear, since this terminal is a variant. Don't worry about it. Anything can be safely coerced to a variant. [My icons have a red half dot at all variant inputs.] If you really don't like the dot, you can insert the To Variant primitive from the Communications>>Data Sockets palette.
Figure 7: Send Message Action
The Decode and Display Message case (Figure 8, with both a vertical and a horizontal stripe removed to conserve space) is sufficiently different that it probably makes sense to start from scratch.
Figure 8: Display Message Action
Use Add New After to create the Decode and Display Message action.
Select this new case.
Use an Unbundle by Name to extract the Event Data element of the Event cluster of the FSM Frequently Used Data cluster prewired along the bottom of the Actions cases.
This datum is the message from the slave. To handle all forms of data, this variable is typed as a variant and must be converted back to a string for display here.
Add the Variant to Data primitive from the Communications>>Data Sockets palette.
Wire an Empty String Constant to the type terminal.
Drag the terminal of the string indicator you created into this case and wire the output of the Variant to Data primitive to it.
Wire the Slave Message Queue Info (if necessary) and error cluster.
At this point, the master VI is finished. (You did remember to wire the Slave Message Queue Info through in the older cases, didn't you?)
Pop-up on the actions case label and Rearrange...>>Sort to avoid future confusion.
Save the VI.
Link Actions to Events
Switch to the LabHSM Editor.
Select the default Ready/Idle state. (Remember, we said we weren't adding any states.)
Set its entry action to Launch and Connect to Slave so that the master starts up the slave before it tries to do anything else to it (Figure 9).
Similarly, set the exit actions to Send Exit to Slave and Disconnect from Slave so that these are the final actions executed (except for the Close executed higher in the hierarchy).
Figure 9: Entry and Exit Actions
Use transition 0 to cause a Send Message to Slave action on the Send Requested event (Figure 9).
Use transition 1 to cause a Decode and Display Message action on the Message from Slave event (Figure 10).
The order is actually unimportant and neither of these really causes a transition at all.
Figure 10: Message from Slave Event
At this point, all the data is defined for the .hsm file.
SAVE the .hsm file.
Make the Slave
We said (well, OK, I said) we would make the slave by duplicating and editing the master.
Use SAVE AS to duplicate the .hsm file as Demo_Slave.hsm in the same directory.
Similarly, save the VI as Demo_Slave.vi there.
Open the Actions List editor.
Use the Rename button to change each instance of the word "Slave" to "Master" and to delete the "Launch and" from the Connect action (Figure 11).
It wouldn't do to have these two VIs launching each other in a perpetual round-robin.
Change "Slave" to "Master" in the Events list as well.
I suggest keeping the Send Exit to Master action for now for some experiments we'll do later.
If you try to delete the Send Exit to Slave action instead of renaming it, you will get a warning that it is in use. Now that the Actions editor is no longer modal, you can just switch to the main LabHSM editor window and remove the unneeded exit action (perhaps by replacing it with Do Nothing).
These are the only differences between master and slave in the .hsm file, so
Figure 11: Slave Actions
|Now edit the block diagram.
Remove Start VI.vi and all its support from the (automatically renamed) Connect to Master case.
Change "Slave" to "Master" and vice versa in all the action cases (e.g., Figure 12).
Limitations in the currently public knowledge of LabVIEW scripting require renaming actions by duplicating the original, renaming the duplicate, then deleting the original. That means that the Message to Send control is now really a duplicate of the original. Its name has been changed to Message to Send 2 and its position on the front panel has shifted slightly. You may want to correct these changes.
Save the updated VI.
Figure 12: Send Message to Master Action
Test the Pudding
Close Demo_Slave.vi, reload Demo_Master (run me).vi, and start it running. Demo_Slave.vi will launch and, apparently, take the place of Demo_Master (run me).vi. Since the slave is a copy of the master, they exactly superimpose and you'll find the master still running underneath if you move the slave panel aside.
Type in the master or slave message control then hit the SEND button. Nothing happens!
Private and Public Events
Nothing is exactly what should happen.
Recall that odd terminal name Message (Public Event Name in Another HSM)? We gave the terminal the name of an event in another HSM actor, but it wasn't a Public event. Supporting data encapsulation, it should not be (and was not) accessible to the other VI (the other actor).
|The fix is easy.
Power up the LabHSM Editor again, if necessary.
Open Demo_Master (run me).hsm.
Select the Events List Editor.
Select the Message from ... event.
Click Mark/Unmark as Public.
Make the Exit Requested event public as well, at least for now (Figure 13).
SAVE the updated .hsm file.
Repeat these steps for Demo_Slave.hsm.
The main editor will show these events as Public as well (Figure 14). When you run Demo_Master (run me).vi, the communicating objects will communicate!
Figure 13: Make Events Public
Figure 14: Events Now Show as Public
This version of the two communicating objects example works slightly differently from the version in the examples folder. Clicking the EXIT button on our slave shuts down the master as well; in the example version, this button just stops the slave. There are two quite different ways to get the same behavior as the example: make the Exit Requested event of the master Private again, or remove the Send Exit to Master action from the slave. If the project is not being extended beyond these two current objects (and you don't want the slave shutting down the master), both changes should be made, so that future readers of your code will not be confused. (Remember that, a few months from now, you may be that confused future reader. While writing up this tutorial, I was puzzled why the slave didn't shut down the master when Exit Requested was Public. I had already forgotten that I had removed the designation of Send Exit to Master as an exit action of the slave but had not deleted the action. If you make a change, clean up all the pieces to avoid confusion!)
Paul F. Sullivan