Hands On: Actor Framework On Instructions

User Manual: Pdf

Open the PDF directly: View PDF PDF.
Page Count: 41

National Instruments
Hands-On:
Actor Framework
The Object is the Message
2
3
Introduction
The Actor Framework (AF) helps you build applications in LabVIEW where multiple parallel tasks must
communicate with each other. Using the framework, you will avoid many of the common deadlock and
race conditions of parallel systems, and enjoy more code reuse. Starting from a list of requirements and a
module diagram, you will build a small working example consisting of several actors and messages.
The framework first appeared in 2010 in the online NI.com forums. As of LabVIEW 2012, it ships as part
of LabVIEW.
You will get the most benefit from this session if you have some knowledge of object-oriented
programming in LabVIEW. The instructions for this exercise assume that you are familiar with the
mechanics of creating LabVIEW classes, setting their inheritance, creating static dispatch, dynamic
dispatch, override, and accessor VIs, and setting access scope. If you are not, you may wish to read
Help LabVIEW Help… Contents Fundamentals LabVIEW Object-Oriented Programming before
continuing.
Prior to this hands-on session, you may wish to visit http://ni.com/actorframework to read more about
how the AF can help you as a developer. If you are familiar with the framework, you can skip to page 6.
What is an Actor?
Let’s start with a brief introduction to the Actor Framework with a single actor object. In this simple
example, we will launch multiple actors of the same class, and interact with those actors with a few
messages. This will give you a feel for how actors operate within an application.
1. Open the project <Desktop>\Seminars\Actor Framework\Simple Actor Example\Simple Actor
Example.lvproj.
2. Open and run Simple Actor Example.vi. This is the top level VI that will launch our actors.
3. Click on New. This will launch a single actor.
4. Observe the gauge as it sweeps through its range.
4
5. On Simple Actor Example.vi,click on Zero. Observe that the gauge on the actor’s front panel
goes to zero, and then resumes sweeping. This is an example of the top level application
sending a message to an actor.
6. On Simple Actor Example.vi, click on New. This will launch a second instance of the actor.
Note that the new instance has a different title bar, and a different gauge value from the first
actor, but it shows the same sweep behavior.
7. On Simple Actor Example.vi, click the Target Actor menu ring, and select 1.
8. Click Zero. Note that the gauge on actor 1 reset to zero, but the gauge on actor 0 did not. This
demonstrates that we can send messages to a single actor, without affecting other actors, even
when the other actors are of the same actor class.
9. Click Zero All. Note that the gauges on both actors reset to zero. We can send the same
message to more than one actor, and both will respond.
10. Click New to launch a third instance of the actor.
5
11. Click Stop. Note that only the panel for actor 1 disappears because that is the one currently set
in our Target selector. We can stop actors independently.
12. Note that a dialog box appears that displays a Final Time Index. When an actor stops, it sends a
message to its owner - a last acknowledgement that contains the final state of the actor (in this
case, the time index used to generate the value displayed in the actor’s gauge). Note that the
gauges of the remaining actors keep running while this dialog box is displayed. Every actor is
independent, making it easy to write applications where user interfaces remain responsive even
when other tasks are executing. Click OK to dismiss the dialog box.
13. Click STOP DEMO. Note that Simple Actor Example.vi stops, and the panels for both
remaining actors close. Note also that no Final Time Index dialogs appear this example
chooses to ignore messages from its actors during shutdown.
Take a moment to consider how you would write this application without the Actor Framework.
How do you currently write applications with multiple, parallel processes?
6
How do you manage message traffic in your parallel systems?
How do you launch multiple copies of the same process?
How do you keep your UI responsive to the user while other operations are executing?
These are common challenges for large systems developed in LabVIEW. Actor Framework gives us a
set of standard tools for addressing these challenges that minimizes software errors and creates
opportunities for code reuse.
The Challenge
In this guided example, we will build an application to control an evaporative cooler. The finished
application will be a simplified version of the Feedback Evaporative Cooler sample project that ships with
LabVIEW 2012. An evaporative cooler is a simple device that uses water evaporation to cool air. Such
coolers are used as low-cost solutions for home cooling in hot, dry climates like the American Southwest,
and similar devices appear throughout industry. A schematic of such a cooler is shown in Figure 1.
Figure 1: An Evaporative Cooler
By Nevit Dilman (Own work) [CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons
An evaporative cooler contains three distinct subsystems:
1. a water reservoir, where water level is maintained by a valve,
2. a pump, which distributes water over the sponge-like evaporative pads, and
3. a fan or blower, to force the cool moist air into the home.
Water distributed over the pads drips back into the water reservoir, so the pump does not actually empty
the reservoir. However, evaporation will deplete the reservoir over time, even if the cooler is not running.
Although the system could be controlled manually by an operator, with separate switches to turn on the
pump and fan, we want to write a controller that will maintain a more-or-less constant temperature without
human interaction.
Our controller must do the following:
7
1) Control the water level in the reservoir, independent of any other system operation
a) Open a valve to add water to the reservoir when the water level drops below a defined low level
b) Close the water valve when the water level reaches a defined high level
2) Control the temperature
a) Turn on the cooler when the inside temperature reaches a high limit
i) Turn on the pump to wet the pads for some interval and then
ii) Turn on the fan (pre-wetted pads provide more effective cooling)
b) Turn the cooler off when the temperature reaches a low limit of (high limit minus five degrees)
3) Continuously display inside temperature, pump status, and fan status to the operator
4) Allow the operator to change the high limit used to turn on the cooler through a user interface.
To make things more interesting, we will assume that the controller is for an industrial cooler, with the
following additional requirements:
5) The fan will be a dual fan system, with fault detection. If a fault is detected while the primary fan is
running, the cooler will stop the primary fan and start a secondary fan.
6) If a fault in the secondary fan is detected while it is running, the cooler will stop the secondary fan,
and restart the primary fan, provided the primary fan is no longer in a fault state.
7) The cooler will operate with or without a user interface, to facilitate using the cooler system as part of
a larger application.
The Solution Design
To solve this problem, we are going to create a series of software modules, each of which will have its
own independent task on which to work. We call these modules “actors”, and each type of actor will be
defined by a LabVIEW class. The actors will have a limited ability to communicate with each other. The
communication is deliberately limited in order to create a system where it is possible to state with certainty
where certain messages originate. That leads to an application that is easier to debug and to modify
without worrying about breaking a little-known connection between modules.
The Actor Framework defines the ancestor Actor.lvclass from which all actor classes inherit. It defines the
communication mechanism that the actors use. It gives us a structure we can use to understand the
program architecture, which is often bewildering in unstructured-but-highly-parallel applications.
Here is a module diagram for a cooler system based on the Actor Framework. It shows the relationships
between the modules we will write.
8
Figure 2: Cooler Controller Implemented in Actor Framework
This is the communications graph. See Figure 3 for the inheritance tree of the classes.
The main object in this system is an instance of Cooler.lvclass. Cooler controls a Dual Fan system
(Dual Fan.lvclass) and a Water Level controller (Water Level.lvclass), and communicates with a user
interface (Cooler Panel). Cooler is responsible for the control logic for starting/stopping the cooler
hardware, and creating/destroying its software components. Since our pump is a simple on/off device with
no feedback, Cooler will manage it directly.
Dual Fan.lvclass manages a pair of fans, including detecting and managing fan faults.
Water Level.lvclass monitors a water level sensor and controls the valve to add water to the cooler’s
reservoir.
Cooler Panel.lvclass is the user interface. It shows the current status of the fan system and water
pump, and the outside temperature. It has a user control for desired temperature.
We will build the system on a set of common ancestor classes. Each adds control logic that is shared by
other classes in the system. Timed Loop Controller.lvclass implements the timed polling scheme that
Dual Fan, Water Level, and Cooler will use to read inputs from system hardware. Level
Controller.lvclass implements logic for a simple limit/deadband control scheme used by Water Level and
Cooler. Cooler UI with Events.lvclass implements an interface for communicating with user interface
actors.
9
Figure 3: Class Hierarchy of the Cooler Controller
Actors communicate by exchanging messages over queues. Each message is defined by a LabVIEW
class. Actor Framework includes Message.lvclass; all message classes inherit from this common
ancestor. A message class consists of the data to be transmitted (if any) and some code that invokes a
method of the receiving actor. In the course of the exercise, we will build several message classes to
manage the interactions between the modules of our cooler system.
The Exercise
You will find the project file for this exercise at <Desktop>\Seminars\Actor Framework\Exercise\AF
Hands-On Exercise.lvproj.
In this exercise, we will build each of these actors and the message classes that tie them together. Since
our focus is on Actor Framework and not object-oriented LabVIEW, the project, library structure, and actor
classes (but not most of their methods) have already been created. The exercise also provides VIs that
contain the control logic the various actors will need. You will implement the inheritance structure,
messaging, user interface, and actor startup/shutdown functionality.
For this exercise, we chose to simulate hardware I/O with global variables, as they are both hardware and
platform independent. The global variable VI is Simulation.lvlib:Simulation Data_Global.vi. Another
VI, Simulation.lvlib:Simulation VI.vi, contains the code for running the I/O simulation. You will run this
VI concurrently with your finished example to simulate the response of the system.
Figure 4: Simulation Control
Cooler
Application
Global variables
10
Part 1 Create the Cooler Controller
1. Create the Timed Loop Controller
(10 minutes)
Cooler, Dual Fan, and Water Level each read a process value (PV) and write a control value (CV). We
have decided to make each of these actors poll for their data. Furthermore, we want each instance of
these actors to be able to poll at different rates.
We will define a polling mechanism for all three actors, and encapsulate that behavior in Timed Loop
Controller.lvclass. At this stage, we are only concerned with the mechanism for triggering an update;
the details of how an input value is read and transformed into an output value will be left to the child
classes. Timed Loop Controller is an abstract class, one that exists primarily to define a common
interface for child classes (called concrete classes).
a. Complete the basic class structure
The basic class has been created for you, and can be found in Timed Loop Controller.lvlib:Timed
Loop Controller.lvclass. It has one attribute, Poll Rate. We have provided a write accessor for this
attribute.
1. Modify Timed Loop Controller.lvclass to inherit from Actor.lvclass.
Next, we must create the method VI that will perform the actual update. Since Timed Loop Controller
cannot know the read/write mechanism that will be used, this method is an abstract method one that will
be overwritten by a child class.
2. Create a new VI from Dynamic Dispatch Template
3. Save the VI as Update.vi. You do not need to modify either the front panel or block diagram.
4. Give the VI the following icon.
5. Save and close the VI when you are done.
b. Create messages for this actor
Now that the basic class structure is complete, we will create the message classes we will need to access
the actor’s methods. In general, you will provide a message class for every public method of your actor.
LabVIEW provides a tool to automate creating message classes for your actors. Most of the message
classes generated by this tool can be used without further modification.
1. From the LabVIEW menu bar, select Tools Actor Framework Message Maker…
2. On the Message Maker dialog, select the project to display. (AF Hands-On Exercise should
be the only one available.)
3. Under Timed Loop Controller.lvclass, highlight the method “Update.” Click on Build
Selected.
11
4. When Message Maker has finished creating VIs, click Close.
The Message Maker will create a new class, Update Msg.lvclass, and open three new VIs in that class:
Update Msg.ctl defines the data the message will carry (in this case, none)
Send Update.vi sends this message to an actor using the actor’s queue
Do.vi is executed by the receiving actor; this Do.vi invokes the Update method of that actor
The VIs are automatically saved by the Message Maker.
5. Close these VIs when you are done inspecting them.
6. Move Update Msg.lvclass to the “Messages For This Actor” folder of Timed Loop
Controller.lvlib.
We recommend grouping an actor and most of the messages it can receive into a common .lvlib file.
Exceptions to this general advice are discussed in the AF documentation. Packaging the actor and
messages together helps programmers keep track of which messages go to which actors. Doing so also
allows us to limit the access scope of a message, and thus limit the list of possible senders of that
message. In this case, we want to guarantee the update timing of a Timed Loop Controller, which we
cannot do if any actor can send the Update message to the controller. Protecting against this possibility
is simple.
12
7. Change the access scope of Update Msg.lvclass to Private.
You will note that we will not provide a message class for Write Poll Rate.vi. As you will see when you
create the actor core, Timed Loop Controller only uses the poll rate when it starts the timing loop, so
changing the rate after startup will not affect execution. Because the poll rate is only set prior to
launching the Timed Loop Controller actor, it does not make sense to provide a message class for this
method.
c. Create Actor Core
Our last step is to add polling functionality. We will do this by appending a timing loop to the Actor Core
which will generate Update messages at a fixed interval.
1. Right click on Timed Loop Controller.lvclass and select New VI for Override...
2. Select Actor Core.vi. LabVIEW will create Timed Loop Controller.lvclass:Actor Core.vi.
3. Modify the new VI as shown.
You can find Read Self Enqueuer.vi on the Functions Data Communications Actor Framework
palette.
Note that the ‘stop?’ control must not use a latching mechanical action. If you create ‘stop?’ by right-
clicking on the stop terminal of the while loop, you must remember to manually change the mechanical
action to one of the switch options. Alternately, you can simply drop push button, rocker, or switch
Boolean (but not an OK, Cancel, or Stop button) on the front panel.
4. On the front panel, hide the ‘stop?’ control.
5. Save and close this VI.
6. Right click on Timed Loop Controller.lvlib, and select Save All (this Library).
13
2. Create Level Controller
(10 min)
For Cooler and Water Level, we will need to implement logic to control a binary control state (on or off for
a valve or cooling system) in response to a numeric value (water level or inside temperature). The logic is
the same for both classes:
The controller state will go high if the process value rises above the high limit
The controller state will go low if the process value drops below the low limit
When the controller is initialized, if the process value is between the high and low limits, the
controller state will be set to low
Note that Cooler actors and Water Level actors perform different actions when the controller changes
state, but the fundamental logic is the same. It makes sense to create an abstract parent class that
encapsulates this behavior so that we programmers only have to write the logic of the state changes
once.
We want this actor to use the update mechanism we defined for Timed Loop Controllers, so we will make
our new Level Controller actor inherit from Timed Loop Controller.
a. Complete the basic class structure
Level Controller.lvclass has been created for you. Level Controller’s attributes include a cluster called
Deadband, which contains a high and a low value. If the variable being controlled falls between these
two values, the controller does not change state. The class private data has already been written for you:
1. Change Level Controller to inherit from Timed Loop Controller.
In order to keep Level Controller an abstract class, we will need to create abstract methods to acquire the
process value and drive the control value. Later, concrete child classes (Cooler and Water Level) will
override these methods.
2. Right click on Level Controller.lvclass, and select New VI from Dynamic Dispatch
Template.
3. Save the new VI as Get New Level.vi.
4. Add the Level numeric indicator to its front panel and connector pane as shown below.
5. Modify the icon as shown. You do not need to modify its block diagram.
14
6. Save and close the VI.
7. Change its access scope to Protected.
8. Create another VI from Dynamic Dispatch Template.
9. Name the new VI Set Output State.vi.
10. Add the Output State Boolean control to its front panel and connector pane as shown below.
11. Modify the icon as shown. You do not need to modify its block diagram.
15
12. Save and close this VI.
13. Change its access scope to Protected.
Now that we can interact with our process and control values, we need to provide the control logic. We
do this by overriding the Update method. Remember that the Update method of Timed Loop defines the
action to take every time the time interval elapses.
14. Create a new VI for Override, and select Update.vi.
15. Replace the block diagram with the following.
16
State Logic.vi is a private method of Level Controller, and has been provided for you. It implements the
control logic we defined for this actor.
Since Level Controller is a type of Timed Loop Controller, it will, at a fixed interval, acquire a new process
value from Get New Level.vi and apply control logic to it in State Logic.vi. If State Logic.vi indicates that
the controller’s state must change, Level Controller will use the results to update the control value with
Set Output State.vi.
b. Create messages for this actor
Write Deadband.vi is the only method of this actor that we wish to allow other actors to invoke. A
message class for this method has been provided for you.
1. Right click on Level Controller.lvlib\Messages For This Actor, and select Add File…
2. Select the file <Desktop>\Seminars\Actor Framework\Exercise\Level Controller\Level
Controller Messages\Write Deadband Msg\Write Deadband Msg.lvclass.
3. Save All (this Library) when you are done.
Since Level Controller does not require an additional processing loop, and has no nested actors, we do
not need to override Actor Core.vi.
3. Create Water Level
(5 min)
Water Level is a concrete implementation of a Level Controller. We will use it to guarantee that the water
in our evaporative cooler’s reservoir never falls below a minimum safe level. We need to define how we
acquire our process value and how we set our control value, as neither behavior is specified by the parent
class.
a. Complete the basic class structure
Water Level.lvclass has been created for you.
1. Change WaterLevel.lvclass to inherit from Level Controller.
2. Create a new VI for Override, and select Get New Level.vi.
3. Modify the block diagram of Get New Level.vi to look like this. Recall that the global variable
VI is located in Simulation.lvlib, and is called Simulation Data_Global.vi.
4. Create a new VI for Override, and select Set Output State.vi.
5. Modify its block diagram to look like this.
17
Note how Water Level uses the results of Level Controller’s state logic. A logical value of “high”,
associated with a process value above the high limit, is converted into a control value of “low,” which
closes the water valve.
b. Override Stop Core.vi to guarantee safe shutdown
We should make sure the water valve is closed when Water Level shuts down.
1. Create a new VI for Override, and select Stop Core.vi.
2. Add the Water Valve State global from Simulation.lvlib:Simulation Data_Global.vi to the
block diagram, as shown.
3. Wire a Boolean False value to the global variable.
4. Save and close this VI.
c. Test the actor
We have provided you with a VI to test Water Level.
1. Open Water Level Test.vi in the Test VIs folder of the Exercise project.
2. Open Simulation Data_Global.vi in Simulation.lvlib.
3. Start Water Level Test.vi. Note that Water Valve State becomes True.
4. We have set the water level high limit to 7 and the low limit to 5. On Simulation
Data_Global.vi, adjust the value of Water Level, and observe the change in valve state.
5. Set the water level to 4 and stop Water Level Test.vi. Observe that the valve state changes
to False.
6. Close both of these VIs when you are done.
Tip: Make a hardware abstraction layer (HAL)
18
Water Level illustrates one approach to building a hardware abstraction layer. In the preceding exercise,
we used global variables to simulate I/O, but you could very easily write another concrete implementation
of Level Controller that uses DAQ, CompactRIO, or any other hardware. These thin, hardware-specific
inheritance layers can be swapped out at run-time. The Feedback Evaporative Cooler sample project
shows this technique in more detail. For more information on building Hardware Abstraction Layers in
LabVIEW, please visit: http://zone.ni.com/devzone/cda/epd/p/id/6307
4. Create Dual Fan
(20 min)
Dual Fan is a concrete implementation of a Timed Loop Controller. It manages the I/O and logic
operations to control a pair of fans (primary and backup) and respond to hardware faults. When Dual Fan
is off, both fans are idle. When Dual Fan is on, the following rules apply:
If no fan faults are detected, turning on Dual Fan starts Fan A.
If a fan fault is detected for a running fan, Dual Fan will stop the faulted fan and attempt to start
the other fan.
If both fan faults are detected, Dual Fan will stop both fans.
If the fan is on and both fans are faulty, as soon as one is fixed, that one will start running.
a. Complete the basic class structure
Dual Fan.lvlib:Dual Fan.lvclass has been created for you, along with some of its methods.
1. Open Dual Fan.ctl and examine the class’s attributes.
2. Open Power On .vi and inspect its block diagram. This method contains the logic to start
the dual fan system.
19
3. Close this VI when you are done.
4. Open Power Off.vi and inspect its block diagram. This method contains the logic to stop the
dual fan system.
5. Close this VI when you are done.
We want to use the update mechanism we defined for Timed Loop Controllers to drive our new Dual
Fan class. We will change Dual Fan to inherit from Timed Loop Controller, and provide a concrete
implementation of Update.vi that implements the desired control logic.
6. Change Dual Fan to inherit from Timed Loop Controller.
7. Create a Create a new VI for Override, and select Update.vi.
8. Modify the VI’s block diagram as shown. Recall that the global variables can be found on
Simulation.lvlib:Simulation Data_Global.vi.
State Logic.vi is a private method that implements the control logic we have defined for this actor. It has
been provided for you.
b. Create a stop core to guarantee safe shutdown
We should make sure to stop the fan hardware when Dual Fan shuts down.
20
1. Create a new VI for Override, and select Stop Core.vi.
2. Modify the block diagram as shown.
3. Save and close this VI.
c. Create messages for this actor
1. Use the Actor Framework Message Maker (Tools Actor Framework Message Maker…) to
create message classes for Power On and Power Off. You can create more than one
message class at a time. Ctrl-click on a method to add it to your selections, or shift-click on
two methods to select all methods in a range. You may only select methods for a single actor
at a time. If you select an actor, you can create messages for all of its public methods.
2. Once you have made your selections, click on Build Selected.
21
3. Close the Message Maker when it is done.
4. Move Power On Msg.lvclass and Power Off Msg.lvclass into Dual Fan.lvlib\Messages For
This Actor.
5. Save All (this Library) when you are done.
Dual Fan does not require an override of Actor Core.vi.
d. Test the actor
We have provided you with a VI to test Dual Fan. You will need to complete this VI.
1. Open Dual Fan Test.vi in the Test VIs folder of the Exercise project, and go to its block
diagram.
2. Go to the Fan State case of the event structure. Add Send Power On.vi to the True case of
the case structure.
3. Wire the Caller-To-Actor Enqueuer to its enqueuer input.
4. Add Send Power Off.vi to the False case of the case structure.
5. Wire the Caller-To-Actor Enqueuer to its enqueuer input.
22
6. Save your changes to this VI.
7. Open Simulation.lvlib:Simulation Data_Global.vi.
8. Start Dual Fan Test.vi, and click Fan State to turn on the Dual Fan. Note that Fan A State
becomes True.
9. Toggle Fan A Fault and Fan B Fault, and observe the change in fan states.
10. Stop Dual Fan Test.vi, and close both of these VIs when you are done.
5. Create Cooler
(20 min)
We are now ready to create Cooler.lvclass. Like Water Level, Cooler is a concrete child class of the
Level Controller. Cooler is responsible for managing the lifetimes of Dual Fan and Water Level. Cooler
must initialize and launch these two actors, and must tell them to stop when Cooler is stopped.
Dual Fan and Water Level are examples of nested actors. An actor that launches nested actors is the
caller of those actors.
a. Complete the basic class structure
Cooler.lvlib:Cooler.lvclass has been created for you.
1. Change Cooler to inherit from Level Controller.
2. Open Cooler.ctl, and add three instances of Message Enqueuer.lvclass to Cooler’s private
data. Message Enqueuer.lvclass can be found in Actor Framework.lvlib.
23
3. Name the new objects Dual Fan, Water Level, and UI. (We will use UI later, when we write
the user interface).
4. Save and close Cooler.ctl.
Since Cooler is a concrete implementation of Level Controller, it must override the Get New Level.vi and
Set Output State.vi methods.
An override VI has been provided for Get New Level.vi. This VI obtains a new indoor temperature value
from the simulator, and is substantially similar to the override we made for Water Level.
An override VI has also been provided for Set Output State.vi. We need to modify it to implement the
delay between when the cooler’s pump turns on, and when the fans start. We will implement this feature
using the Time-Delayed Message. The Time-Delayed Message is not actually a message class, but is
rather a set of VIs that allow you to specify a message to be sent to an actor after some specified delay
period. We will use these VIs to complete Set Output State.vi.
5. Open Set Output State.vi, and modify the block diagram as shown.
When the output state transitions to True, we want to send the Power On message to Dual Fan.
24
6. Add an instance of Dual Fan.lvlib:Power On Msg.lvclass to the True case.
7. Add Time-Delayed Send Message.vi to the True case, and wire it as shown. Time-Delayed
Send Message.vi can be found on the Functions Data Communication Actor Framework
Advanced palette.
We want to configure Time-Delayed Send Message.vi to send one copy of the message after a 5
second delay.
8. Wire a value of 5000 to the Milliseconds to Wait input of Time-Delayed Send Message.vi.
9. Wire a value of 1 to the # Copies input.
The Time-Delayed Message uses a notifier to manage the time delay. We can use the notifier to manage
the delivery of any remaining, unsent messages.
When the output state transitions to False, we want to send the Power Off message.
10. Add Dual Fan.lvlib:Power Off Msg.lvclass:Send Power Off.vi to the False case, and wire
it as shown.
We also need to abort the time-delayed message, should the output state transition to False during our
delay period. Sending the “Stop all further copies” notification will cancel any pending message and
destroy the notifier. We should wait for the notifier to be destroyed before sending the Power Off
message. By waiting, we guarantee that we cannot send a Power On message after we send Power Off.
11. Add a Send Notification function to the False case, and wire it as shown.
12. Add a Wait on Notification, and wire it as shown. Wire a Boolean “True” to the ignore
previous (F) input.
25
13. Save and close the VI.
14. Create a new VI for Data Member Access. Select a static accessor for the UI attribute, select
write access, and uncheck “Include error handling terminals.” The VI will look like this when
you are done.
15. Save and close this VI.
b. Create messages for this actor
1. Use the Actor Framework Message Maker to create a message class for Write UI.
2. Move the new message class to Cooler.lvlib\Messages For This Actor.
3. Save All (this Library) when you are done.
We will need this accessor method and message when we implement the user interface.
c. Create Actor Core
Cooler must manage the lifetimes of its parts, Dual Fan and Water Level. This means that we must add
code to create and launch these actors. Launching nested actors is a function of Actor Core.vi.
1. Create a new VI for Override, and select Actor Core.vi.
26
2. Modify the block diagram to look like this.
Recall that Write Deadband.vi is a Level Controller method. Launch Actor.vi is a method of
Actor.lvclass, which can be found in Actor Framework.lvlib or in the Actor Framework palette.
Note that Dual Fan and Water Level must be launched before Call Parent Method. This ensures that
Dual Fan and Water Level are both ready to receive messages from Cooler before Cooler can send any
messages.
Note also that we are setting default deadband values for our two Level Controllers before launch. An
actor is a normal LabVIEW object; you can invoke any of its methods or properties as needed prior to
calling Launch Actor.vi.
d. Create behavior to correctly stop nested actors
An actor is responsible for destroying anything it creates. This includes objects such as its queue or any
of its nested actors. For nested actors, this is done in an override of Stop Core.vi.
1. Create a new VI for Override, and select Stop Core.vi.
2. Modify its block diagram as shown.
3. Close the VI and Save All (This Library)
Send Normal Or Emergency Stop.vi is a method of Stop Msg.lvclass, which can be found in Actor
Framework.lvlib or in the Actor Framework palette.
27
e. Test the actor
The example includes a VI to test Cooler. You must first add it to the project.
1. Add the file <Desktop>\Actor Framework\Exercise\Support\Cooler Test.vi to the Test VIs
folder of the Exercise project.
2. Open Cooler Test.vi.
3. Inspect the VI’s block diagram.
4. Open Simulation.lvlib:Simulation Data_Global.vi.
5. Start Cooler Test.vi.
6. Adjust the indoor temperature and observe the change in pump and fan states. Recall that
there is a five second delay after the pump turns on before the fan starts.
7. Toggle the fan fault Booleans and watch the change in fan states.
8. Adjust the water level, and watch the change in the state of the water valve.
9. Stop Cooler Test.vi, and close both of these VIs when you are done.
This concludes Part 1. At this point, we have implemented all of the functionality of an evaporative cooler
controller except for the user interface. Our solution is both modular and parallelized. Actor Framework
made this task easier in many key ways.
We wrote a single update mechanism (Timed Loop Controller) that we were able to reuse for
three separate processes (Water Level, Dual Fan, and Cooler).
We wrote a single deadband controller (Level Controller) that we were able to reuse for two
processes (Water Level and Cooler).
We did not have to write code to manage message traffic or launch parallel processes.
We used the Actor Framework Message Maker to easily generate messages for our actors.
If time permits, you may proceed to Part 2, where you will build the user interface for the cooler controller.
28
Part 2 Create the User Interface
Now that we have completed our software model of the system, we can turn our attention to the user
interface. Recall our requirements:
Display inside temperature, pump status, and fan status to the operator
Allow the operator to set the temperature (the high limit) at which to turn on the cooler
Allow the cooler to operate with or without a user interface
The most robust solution that meets all of our requirements is to create a separate UI actor and
messaging infrastructure. This decouples the lifetimes of Cooler and Cooler Panel, so we can launch and
stop them independently, write new panels without risk to the cooler’s execution logic, and reuse our user
interface with a different software model (i.e. a different type of cooler or any device that sends the same
messages).
To facilitate these benefits, it is sometimes helpful to start with an abstract user interface layer, from which
our actual UIs will inherit. This abstract layer will typically include all of the messages supported by the UI
class family, and may include some common support code. Cooler will be able to send status messages
to any child of this abstract class.
1. Create Cooler UI with Events
(20 min)
In this example, the abstract class is Cooler UI.lvlib:Cooler UI with Events.lvclass. Cooler UI with
Events receives messages from Cooler, and translates those messages into user events that can be
received by an event structure. Children of this class will register for those events, and update their front
panels when they receive messages. Cooler UI with Events has been created for you. The class already
inherits from Actor.lvclass. It includes a read accessor method that returns a cluster of user events.
a. Complete the basic class structure
We will start by creating the set of user events. We wish to guarantee that the event refnums are
available as soon as we create an instance of this or any child class, so the best place to create the
events is in an override of Actor.lvclass:Pre Launch Init.vi. Launch Actor.vi invokes this protected
method just prior to launching Actor Core.vi. Note that you cannot launch a nested actor inside Pre
Launch Init.vi; however, any other initialization operations should occur here.
1. Create a new VI for Override, and select Pre Launch Init.vi.
2. Modify the VI’s block diagram as shown.
29
The Fan Status cluster has been built for you. It has been saved as Status.ctl, and is a member of
Cooler UI with Events. A cluster of user events matching the cluster you will create is already part of the
class attributes.
Cooler UI with Events will need to send messages to Cooler, so it will need to hold a copy of Cooler’s
enqueuer as an attribute.
3. Open Cooler UI with Events.ctl
4. Add a Message Enqueuer object to the cluster of class private data.
5. Rename the Message Enqueuer “Cooler.”
6. Save and close Cooler UI with Events.ctl.
7. Create a VI for Data Member Access for Cooler, and select write access.
8. The VI should look like this when you are done.
30
9. Save this VI with its default name, Write Cooler.vi.
Having created a set of references, Cooler UI with Events must destroy them. The proper place to do this
is in Stop Core.vi.
Create a new VI for Override, and select Stop Core.vi. Add the following to its block diagram.
10. Save and close this VI.
Next, we will create a method to change the Cooler’s target temperature.
31
11. Create a new VI from Static Dispatch Template.
12. Modify the VI’s front panel, connector pane, and icon as shown.
13. Add the following code to its block diagram. Note that Send Write Deadband.vi is part of
Write Deadband Msg.lvclass, which you created for Level Controller.
32
Do not wire the error cluster through Send Write Deadband.vi. We want to allow for the possibility that
the Cooler might not be present at run time. This would be the case, for example, if we choose to test our
UI independently of the software model. This may cause issues if you have automatic error handling
enabled, or use VI Analyzer to inspect your code, so consider explicitly wiring the error output of Send
Write Deadband.vi to the case structure and documenting why you are not propagating the error any
further, as shown.
For the purpose of this example, we have decided to always set the low limit to five degrees below the
high limit
14. Save this VI as Change Desired Temperature.vi
Next, we will create a VI to update the fan display.
15. Create a new VI from Dynamic Dispatch Template.
16. Modify the VI’s front panel, connector pane, and icon as shown. For the Fan A and Fan B
inputs, use Unit Data.ctl, in the Support VIs folder.
17. Add the following code to the VI’s block diagram.
33
18. Save this VI as Update Fan.vi.
Methods to update the pump and temperature displays have already been provided. They are similar to
Update Fan.vi.
b. Create messages for this actor
Use the Actor Framework Message Maker to create message classes for
1. Change Desired Temperature
2. Update Fan
3. Update Pump
4. Update Temperature
5. Write Cooler
Recall that you can create more than one message at a time.
6. Move these messages to Cooler UI.lvlib\Messages For This Actor. Save All (this Library)
when done.
2. Modify Cooler Class to Send Messages to Cooler UI
(5 min)
Now that we have defined a message set for our user interfaces, we can revisit Cooler, and modify it to
send status updates. In the previous section, we created several messages for Cooler UI with Events.
We will modify three methods of Cooler.lvclass to send one of those messages to Cooler UI with
Events.lvclass.
1. Open Cooler.lvclass:Get New Level.vi.
2. Modify the VI’s block diagram as shown. Recall that Send Update Temperature.vi is a
member of Cooler UI with Events.lvlib:Update Temperature Msg.lvclass.
34
3. Do not propagate the error cluster through Send Update Temperature.vi, but consider
wiring the error output to the border of the case structure, and including a comment.
4. Save and close this VI.
5. Open Set Output State.vi.
6. Modify the VI’s block diagram as shown. Consider propagating the error output from Send
Update Pump.vi and including a comment.
7. Save and close this VI.
8. Open Update Fan Status.vi.
9. Modify the VI’s block diagram as shown. Consider propagating the error output from Send
Update Fan.vi and including a comment
35
10. Save and close this VI.
3. Modify Dual Fan to send a message to Cooler
(5 min)
In the previous section, we created a Cooler method that sends the status of the Dual Fan system to our
user interface. Cooler, however, does not currently have access to that status information. We need to
modify Dual Fan to update Cooler whenever the status of its fans changes.
a. Create the message for the cooler
1. Use the Actor Framework Message Maker to create a message for Cooler.lvclass:Update
Fan Status.vi
2. Move this class to Cooler.lvlib\Messages For This Actor.
3. Save All (this Library) when you are done.
b. Modify Dual Fan
We want Dual Fan to notify Cooler whenever its fan status changes. This can happen when the Power
On, Power Off, or Update methods are invoked. It makes sense to create a method VI to encapsulate the
messaging behavior. We have provided the method, Post Update.vi, for you.
1. Open Dual Fan.lvclass:Post Update.vi.
2. Modify the VI’s block diagram as shown.
36
Read Caller Enqueuer.vi is a member of Actor.lvclass, which can be found in Actor Framework.lvlib.
3. Save your changes.
You will now add this method to Power On.vi, Power Off.vi, and Update.vi.
Open Power On.vi. Modify its block diagram as shown.
4. Save and close the VI.
5. Open Power Off.vi.
6. Modify the VI’s block diagram as shown.
7. Save and close the VI.
8. Open Update.vi.
9. Modify the VI’s block diagram as shown.
37
10. Save and close the VI.
4. Create the Cooler Panel
(15 min)
Cooler Panel is a concrete child class of Cooler UI with Events. It builds on the functionality of its parent
to provide the actual user interface.
Cooler Panel.lvclass has been created for you.
1. Change Cooler Panel to inherit from Cooler UI with Events.lvclass.
2. Create a new VI for Override, and select Actor Core.vi.
3. Open the new VI’s block diagram.
We have provided the user interface elements for you in an example VI.
4. Open Cooler Panel UI Components.vi, located in the Support VIs folder.
5. Open this VI’s block diagram.
6. Copy the entire contents of the block diagram of Cooler Panel UI Components.vi to the
block diagram of Actor Core.vi. (This will also copy the front panel elements for you).
7. Modify Actor Core.vi to look like this (a detailed sequence of steps is included below):
38
Read Self Enqueuer.vi is a method of Actor.lvclass. Recall that Read Events.vi is a method of Cooler
UI with Events.lvclass.
7a. Wire the Events output of Read Events.vi to a new input of Register For Events .
7b. Modify the Desired Temperature value change event as shown.
Recall that Change Desired Temperature is a message for Cooler.
7c. Link the following event to the Fan Status event.
7d. Link the following event to the Temp event.
39
7e. Link the following event to the Pump event.
7f. Arrange the front panel of Actor Core.vi to look like this.
7g. Save Actor Core.vi.
40
5. Create Application Launcher
(5 min)
Finally, we will create a small top-level VI to launch our actor system.
1. Create the following VI. Save it as Exercise\Feedback Evaporative Cooler Demo.vi
You will find most of the VIs you will need in Actor Framework.lvlib.
Obtain Message Queue.vi, Read Enqueuer.vi, and Release Message Queue.vi are methods of
Message Queue.lvclass.
Send Normal Stop.vi is a method of Stop Msg.lvclass. You can send a Stop Msg to any actor; that actor
will shut down and return a Last Ack message to its caller.
Launch Actor.vi is the only public method of Actor.lvclass.
Note that this top-level application handles the exchange of queues between Cooler and Cooler Panel.
This step is required to allow the two actors to exchange messages.
41
2. Save this VI.
3. Open and run Simulation.lvlib:Simulation.vi, and then
4. Run Feedback Evaporative Cooler Demo.vi.
You should see the pump turn on, followed by the primary fan. The indoor temperature will fall to 70
degrees, at which point the fan will stop and the temperature will start to rise again. If you wish, you can
open Simulation.lvlib:Simulation Data_Global.vi, and observe the operation of Water Level.
This concludes Part 2. We have implemented a user interface for our cooler controller that displays the
state of the system and allows operators to set desired temperature. We can run the cooler with or
without the user interface. We can also create new user interfaces by inheriting from Cooler UI with
Events, and use whatever interface suits our current needs.
Thank you for participating in the Actor Framework Hands-On session. Please join the online community
for the framework at
http://ni.com/actorframework

Navigation menu