In the previous installment, we built a very simple program which consisted of a single actor and a single message. In this part we will create an application that is a bit more complex. This example consists of two actors: a DAQ actor which can read an analog voltage and a User Interface actor which provides a graphical user interface to display the data. Let’s get started!
Note: In this tutorial I am assuming the reader is familiar with the process of creating an actor and I will be glossing over some of the step-by-step details of actor and message creation. If you’re unsure of how to create an actor class, be sure to check out Part 1 of this series here.
In the previous tutorial, we made the simplest possible actor-based application. It consisted of a single actor and a single message. Most applications, however, will require multiple actors communicating with each other using many messages. In this tutorial, we will take a step in that direction by creating an application with two actors: a DAQ actor that reads data using DAQmx and a UI actor that displays the data to the user.
To start, create a new blank project and save it. Add the “Actor Framework.lvlib” library to the project by right clicking on “My Computer”, select “Add >> File”, and browse to “C:\Program Files (x86)\National Instruments\LabVIEW 2014\vi.lib\ActorFramework\Actor Framework.lvlib”.
It is my preference to have each actor reside in its own library, so we’ll begin by creating two new libraries “DAQ Actor.lvlib” and “UI Actor.lvlib” by right clicking on “My Computer” and selecting “New >> Library” for each one (you must save the libraries in order to set their names). Within each of these libraries, create a virtual folder called “Messages” and create a new class with the same name as the library (DAQ Actor.lvclass and UI Actor.lvclass). Set each new class to inherit from Actor.lvclass.
Now that our actors are created, it is time to start configuring them. In the UI Actor class, create an override of “Actor Core.vi” by right clicking the class and selecting “New >> VI for Override” and then selecting “Actor Core.vi” and clicking OK. This is the VI we will use to create our user interface shortly. We’ll also need to create a new method which we will call “Update Data Ind.vi” in the UI Actor class. This VI will be used to receive data from the DAQ Actor and display it on the UI. Create this VI by right clicking the UI Actor class and selecting “New >> VI from Static Dispatch Template” and save it as “Update Data Ind.vi”. We will soon be filling in the contents of these VI’s, but let’s create the VI’s for the DAQ Actor first.
Create two override VI’s in the DAQ Actor class: “Pre Launch Init.vi” and “Stop Core.vi”, by following the same process used to create “Actor Core.vi” previously. As the names suggest, these override VIs are typically used for actor initialization and cleanup code. We’ll be creating our DAQ task in “Pre Launch Init.vi” and clearing our DAQ task in “Stop Core.vi”. We’ll also need a Static Dispatch method in the DAQ Actor class which we will call “Read.vi”, which will perform a single shot DAQ read.
We’ll also need a VI to launch our application, so just create a new VI at the top level of your project (outside of any libraries) called “Launch.vi”.
We are now ready to create the two messages needed for this project. The DAQ actor will have a message that allows the UI actor to tell it to read a data point. The UI actor will have a message that the DAQ actor can call to pass the data point to the UI. As such, use the message maker tool to create messages for “Read.vi” and “Update Data Ind.vi”. If you use the MGI message maker, it should automatically move the messages to the appropriate Messages virtual folders. If not, you’ll have to move them in manually.
At this point, your project should look something like this:
Before moving forward in LabVIEW, let’s take a minute to set up our DAQ hardware. If you have a DAQmx compatible device available that has analog inputs feel free to use it, but for the purposes of this tutorial I will assume we’ll be using simulated hardware.
To create a simulated device, open NI Measurement and Automation Explorer (MAX), right click “Devices and Interfaces” and select “Create New”. Select “Simulated NI-DAQmx Device of Modular Instrument” and finally choose the X Series PCIe-6320 device as shown in the images below.
Now that our hardware is configured, it is time to start filling in the details of our actors. We’ll start with the DAQ actor. In your project, open “DAQ Actor.ctl” (under “DAQ Actor.lvclass”). This is the control that specifies the data that your actor will contain. If this is a new concept to you, you can think of this as the “shift register” of a typical Queued Message Handler loop. This data will persist from one state to the next as the state machine progresses. The data for this actor will consist of a DAQmx task. Let’s drop down a DAQmx task control into the “DAQ Actor.ctl” cluster as shown in the following images. Be sure to save and close “DAQ Actor.ctl” before continuing to the next steps.
Next we will fill in the details of the DAQ actor methods. Screenshots below show the details required for these VIs. As you can see, the Pre Launch Init VI creates a new DAQmx task, the Read VI reads an analog value, and the Stop Core VI closes the task. There are a few points worth noting here. First, the “Send Update Data Ind.vi” VI is in the “Update Data Ind.lvclass” message class. Also, the simulated device on my system is called “Dev1”, which is the default name for a new device. If your device has a different name in MAX, be sure to select that one in the Pre Launch Init VI. Finally, it is worth noting that this is performing a single shot read (it stops and starts the task after each data point is read). Most applications need continuous read functionality, but for the sake of simplicity I’ve chosen to do a simple single shot read for this example.
Pre Launch Init
Now we will finish up our application by filling in the details of the UI Actor. As in the DAQ actor, the first step is to specify the data that the actor will contain, in this case in the “UI Actor.ctl” control. The UI actor needs a reference to the numeric indicator on its front panel to display the data it receives from the DAQ actor. Although there are several ways for UI actors to update their front panels, my preference is to use references to the UI controls and indicators. It is also fairly common, and perfectly valid, to use User Events for this purpose, but I’ve found that references tend to be more flexible and less cumbersome. To continue, drop in a Control Refnum and set its type to numeric as shown in the following images. Be sure to save and close “UI Actor.ctl” before continuing to the next steps.
To complete the UI actor, fill in the Actor Core and Update Data Ind Vis as shown in the following images.
Update Data Ind
Let’s take a moment to discuss what’s happening here. The UI Actor Core VI will serve as the main user interface for our application. When the UI Actor Core VI is called, it launches the DAQ Actor as a “Nested” actor. This means that when the UI Actor receives a stop message, it will also tell the DAQ Actor to stop. This is why we only send the stop message to the UI Actor in the Panel Close event. Note: This is a new feature in the LabVIEW 2014 version of the Actor Framework. Earlier versions of the Actor Framework did not have the concept of nested actors. In the event that handles the Read button click, we send a message to the DAQ Actor telling it to call its Read VI. The Read VI reads a data point and sends it back to the UI Actor by sending it a message that calls its Update Data Ind VI. The Update Data Ind VI uses the reference to the Numeric indicator to update the front panel with the data point value.
All that’s left to do is to launch our application. To do this, all we have to do is launch the UI actor as a Root Actor. Fill in the Launch VI as shown below to do this. It’s worth noting that the LabVIEW 2014 version of the Actor Framework greatly simplified the code required to launch an actor.
Run the Launch VI and verify that clicking the Read button properly reads a value and displays it in the Data numeric indicator. Closing the VI should stop the application and unlock all of the libraries in your project.
Congratulations on completing this tutorial. Hopefully you now have a stronger understanding of how to use the Actor Framework in your applications. In future articles, I will be discussing some of the pros and cons of the architectural decisions made in this tutorial. For example, the UI and DAQ actors in this example are dependent on each other, since they call each other’s messages directly. It is possible to loosen or completely eliminate this coupling by using techniques like generic messages and the observer pattern. These techniques come at the cost of increased complexity, however. Stay tuned for future articles where we discuss these topics further!