Actor Framework is not as hard as you think and here is why…

From LabVIEW Wiki
Jump to: navigation, search
by Quentin"Q" Alldredge, Q Software Innovations, LLC
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.

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.

Queued Message Handler 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:

  1. something to define the Message Type (usually a string or enum)
  2. 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.

Comparison of QMH to AF Components

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:

Actor Framework QMH Design Pattern

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:

  1. The Do Method contains the code executed when the Message is sent
  2. 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:

  1. Read the queue from the Actor Class using the built-in method “Read Self Enqueuer”
  2. 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.

New Actor Shortcut Menu Item

Next, give your actor a name.

Give 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.)

Select where to inherit from.

Click OK. 

A library, class and two virtual folders will be created for you.

Project after Actor is created.

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.

Add original code to Actor

3. Override the Actor Core, Pre-Launch Init, Handle Error, and Stop Core

Right-click on your Actor and select New-->VI from Override…

Select New VI from Override Shortcut Menu Item

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.

Select VIs to Override

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:

Add User Event References to Class Private Data
Create the User Event Reference and Bundle into Class

5. Edit Stop Core

Edit the Stop Core to send the Stop Event and close the User Event references.

Stop User Events in Stop Core

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.

State data in QMH

I’ll add this to my Class Private Data.

Added "Count" to 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:

  1. Default – for unhandled string commands (won’t be needed in the AF version)
  2. Increment – for adding amount to the number
  3. Decrement – for subtracting amount from the number
  4. 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.

Front Panel of Increment.vi (Decrement.vi is exactly the same)
Block Diagram of Increment.vi
Block Diagram of Decrement.vi

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).

Message Creation Tool

In the Actor Framework Message Marker (MGI) Dialog, select the methods and click Build/Update Selected Message(s).

Select Methods to make into Messages

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:

Project after Messages were created.

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.

Copying the EHL from the QMH

Now add in the Message Queue by using the AF method “Read Self Enqueuer” and add the User Event Registration.

Adding Message Queue and 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”.

Change Increment Message Cluster to "Send Increment.vi"
Change Stop Message Cluster to "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.

Launch Root Actor

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.