Actor Framework is not as hard as you think and here is why…
This started due to a conversation I had at a Users' Group. I stated during my presentation that the Actor Framework is not as hard as everyone makes it and that it really is just an Object-Oriented implementation of the Queued Message Handler Design Pattern (this is an oversimplification but the concepts are the same, see Actor Oriented Design Patterns). One colleague then asked how to make the transition. Hopefully this article will answer that question.
If you are familiar with the Queued Message Handler (QMH) Design Pattern and Object-Oriented Programming (OOP) then you already know everything you need to start with Actor Framework. The purpose of this article is to show the parts of the Actor Framework that are comparable to the QMH Design Pattern and provide a step-by-step procedure for converting a QMH project to an Actor Framework project.
Contents
- 1 Queued Message Handler (QMH)
- 2 Actor Framework (AF)
- 3 Converting QHM to AF
- 3.1 1. Create your Actor Class
- 3.2 2. Add your QMH VI and all SubVIs to the Class
- 3.3 3. Override the Actor Core, Pre-Launch Init, Handle Error, and Stop Core
- 3.4 4. Edit Pre-Launch Init
- 3.5 5. Edit Stop Core
- 3.6 6. Add State Data to Actor Private Data
- 3.7 7. Create the Message Methods
- 3.8 8. Create the Messages
- 3.9 9. Edit the Actor Core
- 3.10 10. Create the Launcher
- 4 Why use AF over QMH?
Queued Message Handler (QMH)
First let’s define the basic parts of the QMH Design Pattern.
The basic components of a QMH is:
- The Message Cluster
- The Event Handler Loop (EHL)
- The Message Handler Loop (MHL)
- A Queue to communicate Messages from the EHL to the MHL
- User Events to communicate back from the MHL to the EHL
Below is a basic program created using the QMH Design Pattern.
The Message Cluster
The Message Cluster is the structure of a Message to be sent from the EHL to the MHL. The Message Cluster contains two parts:
- something to define the Message Type (usually a string or enum)
- something to define the Message Data (usually a variant)
It is this cluster that defines the data type of the Message Queue.
The Event Handler Loop (EHL)
The Event Handler Loop (EHL) enqueues the Messages based on the execution UI Events or User Events. The EHL consist of a standard Event Handler Design Pattern with an Event Structure and While Loop. The Event Structure could register for User Events that serve as communication back from the MHL.
The Message Handler Loop (MHL)
The Message Handler Loop (MHL) dequeues the Messages and executes the Message based on Message Type. Inside of the case for the Message Type, the Message Data is unflattened accordingly and used to execute the case.
EHL to MHL Communication
Communication from the EHL to the MHL is through a queue created with the Message Cluster as the data type. This queue created in the initialization step before the EHL and MHL are started.
MHL to EHL Communication
Communication from the MHL to the EHL is through one or more User Events of whatever data type is needed. These User Events are created in the initialization step before the EHL and MHL are started.
Actor Framework (AF)
The Actor Framework is just the QMH without having to create your own MHL and Message Queue.
Your main code will exist in a Class you create and inherit from the Actor Class. The main code exists in the Actor Core Method which is an override of the method in the parent Actor Class.
Translating the QMH code to AF code now looks like this:
The Message Class
The Message Cluster becomes the Message Class. Each Message Type becomes its own class that inherits from the parent Message Class. The Message Data becomes the Private Data of these classes.
Message Classes includes two main methods:
- The Do Method contains the code executed when the Message is sent
- The Send Method is the code that enqueues the Message
The Event Helper Loop
The EHL becomes the Event Helper Loop in the Actor Core Method. It still enqueues Messages based on the execution UI Events or User Events and still consists of a standard Event Handler Design Pattern with an Event Structure and While Loop. The only difference is the queue that is used.
The Call to Parent Actor Core and Do Methods
Under the Hood in the Actor Framework the parent Actor Core Method consists of a loop that dequeues incoming Messages and dynamically dispatches the Do Method.
The code in the Case Structure that would have been in the MHL becomes overridden methods in the Message Classes. These methods are called the Do Method. The Do Method wraps a method of the Actor Class that executes the code based on the Message.
To enqueue a message do the following:
- Read the queue from the Actor Class using the built-in method “Read Self Enqueuer”
- Use the Send Method created for each Message to enqueue the Message
If updated data is sent with the Message, there will be inputs for it in the Send Methods
Event Helper Loop to Parent Actor Core Communication
Communication from the Event Helper Loop to Parent Actor Core is through a queue that is created by the Actor Framework (AF). Its data type is the parent Message Class where any child of the Message Class can also be enqueued. The Message queue is obtained by the AF method “Read Self Enqueuer”.
Parent Actor Core to Event Helper Loop Communication
Communication from the Parent Actor Core to Event Helper Loop Communication is through one or more User Events of whatever data type is needed, just like the QMH Design Pattern. These User Events can be created in the overridden Actor Core Method before the EHL and MHL are started, or in another method called the “Pre-Launch Init” Method which is guaranteed to run before the Actor Core Method.
Converting QHM to AF
So now, I have a QMH Program that I want to convert to AF. What should I do?
Well, it is probably easier to start with AF that to convert but here are the steps to take if you were to do this:
1. Create your Actor Class
This has become significantly easier in LabVIEW 2018 (before LabVIEW 2018 view documentation). To create an Actor simply right-click on My Computer in the Project Explorer and select New-->Actor.
Next, give your actor a name.
Select where to inherit from. Most likely you will only have one choice unless you have other tools installed. (I like using MGI’s Monitored Actor for debugging purposes.)
Click OK.
A library, class and two virtual folders will be created for you.
2. Add your QMH VI and all SubVIs to the Class
Find your code for your QMH and add it to the Actor. You can organize SubVIs later.
3. Override the Actor Core, Pre-Launch Init, Handle Error, and Stop Core
Right-click on your Actor and select New-->VI for Override…
Select Actor Core, Pre-Launch Init, Handle Error, and Stop Core and click OK. A VI will be created for each of these which we’ll edit in the next steps.
4. Edit Pre-Launch Init
Edit the Pre-Launch Init Method to add the Create User Event code. Add the User Event references to the Actor Class Private Data so they can be bundled into the class. The Private Data and code should look like this:
5. Edit Stop Core
Edit the Stop Core to send the Stop Event and close the User Event references.
6. Add State Data to Actor Private Data
Your state data, if you have any, should be stored in the Actor and only manipulated in the Actor’s methods.
For this example, my state data was the “Count” and was stored in a shift register in the MHL.
I’ll add this to my Class Private Data.
7. Create the Message Methods
This might be the only real tricky part because this is the main business logic of your code. For this example, my MHL had four cases in it:
- Default – for unhandled string commands (won’t be needed in the AF version)
- Increment – for adding amount to the number
- Decrement – for subtracting amount from the number
- Stop – stopping the MHL and sending user event to the EHL (won’t be needed in the AF version because the “Stop Core.vi” handles this)
So, I need to create two methods for Increment and Decrement. These methods are almost identical. Make sure to include “Amount” as an input in the Connector Pane.
These VIs perform the operation on the state data and the send a User Event to update the count in the UI. This is exactly how the MHL cases performed.
8. Create the Messages
Creating Messages from the Increment and Decrement methods is accomplished through the Message Maker in the Tools Menu (Tools-->Actor Framework Message Maker, I use MGI’s version of this tool because of the added features or better icons and the ability to update previously created messages).
In the Actor Framework Message Marker (MGI) Dialog, select the methods and click Build/Update Selected Message(s).
The Message Classes, the Do Methods, and the Send Methods will be created.
Click “X” to close the dialog.
9. Edit the Actor Core
My Project now looks like this:
Now it is time to create the Event Helper Loop in the Actor Core.vi. You can copy over the EHL into the “Actor Core.vi”. Shown below with some organization.
Now add in the Message Queue by using the AF method “Read Self Enqueuer” and add the User Event Registration.
Next change the Message enqueuing to the Send methods of the Message Classes for the Increment and Decrement event. For the Stop and Panel Close Events use the built-in AF method, “Send Normal Stop”.
10. Create the Launcher
An Actor cannot launch itself, so the last part of an AF project is the “Launcher.vi”. This can be named something else and can be disguised as a splash screen or whatever. Use the built-in AF method, “Launch Root Actor” with the “Show Actor Core Front Panel” flag set to TRUE. Run it and everything should run as you would expect.
Why use AF over QMH?
Bottomline Up Front: Extensibility, Readability, and Maintainability.
Sure Actor Framework had it hang-ups in the beginning like:
- How do I find it to inherit from it?
- How do I create/update Messages?
- How do I debug AF code?
A lot of these problems have been solved in the newer versions of LabVIEW and in addons like the:
- Created Actors from the “New” Shortcut Menu Option
- Message Maker (AF Original or MGI’s)
- MGI’s Monitored Actor
The Main Consideration is This…
When you start to have more that one module in an application or more operations that must happen in parallel or asynchronously then you need a more expandable framework. The Actor Framework is made just for this purpose. Other addon tools expand this further like MGI’s Panel Manager.