In Part 1 we discussed the Queued Message Handler (QMH) and the Object Oriented (OO) QHM. The OO QHM seemed pretty sweet, but it was a lot of code just to get going. Now we’ll see how the Actor Framework can help us.
This article is part of the Actor Framework Basics series. See the other parts:
- Part 1 – The Background
- Part 2 – The Actor
- Part 3 – Launching and Communicating
- Part 4 – Being Productive With Actors
- Basic Walkthrough – Creating a Logger
The Actor Model is a Math/Computer Science concept from way back in the 70’s. What it boils down to is: a computational entity that contains state information that can send, receive and handle messages. This is almost identical to what we’ve already defined as a QMH. That means any time you see actor, you can pretty much think QHM.
LabVIEW’s Actor Framework (AF) is just an implementation of the Actor Model in LabVIEW. It is an object oriented implementation developed and maintained by NI. This means that all the base classes are defined, implemented, and (most importantly) debugged for us!
Breaking into the AF can be a bit daunting, so let’s look how traditional QMH components map to AF classes and methods.
|QMH Component||AF Component||Notes|
|The Data||Actor Class||State information is stored in the Actor’s private data.|
|The Queue||AF Message Queue Classes||The Message Classes wrap a priority queue.|
|The Message Handler||Actor Core Method||Actor.lvclass:Actor core.vi has the message handling loop inside it. New message are added by overriding classes and methods.|
|The Messages||Message Class||Message all inherit from a common class. Each message has its own unique data associated with it.|
This gets us all of the pros from the QMH (state data encapsulation and separate thread execution) while implementing all of the stuff discussed in the OO QHM section. I want to reiterate (because it’s important): this framework is maintained by NI, it is also “battle hardened”, meaning bugs have been squashed and it’s a pretty robust framework. Issues that you didn’t know you’d run into (like reference lifetime, dynamic calling, etc) are addressed and fixed already.
What are the cons?
- There are a lot of classes. This irks some people and it shouldn’t. Most of the issues people cite when talking about projects with lots of classes boils down to not enough modularization in your project.
- Code is harder to read. It takes a little time to figure out how to read a block diagram for an actor. This is significantly less of a problem once you become familiar with the framework.
Actor Framework Classes
The first thing to get in your head when looking into the AF is that it does a lot in the background. The framework code to make everything happen is very complicated and confusing. Because of this I really don’t recommend looking into HOW it works. Look into WHAT IT DOES. If you’re an AF beginner and you start looking into how it works you will no doubt become frustrated and confused. If you’re looking into the framework and you don’t understand why something is happening, post your code/question on the forums and you’ll get some help. The LAVA OO Forum and the Actor Framework NI Group are good places to start.
Below are some high level descriptions of classes in the framework that you should understand.
This is the main point of extension for the framework. Each QMH that you want to implement will be a child of the Actor Class. Notice that there aren’t really many public methods. All of your extension and use will come from overriding the protected methods and adding methods to your child implementations.
The queue is a bit complicated (but for good reason). First thing to know is that it’s a priority queue. When you message an actor you can send it with one of three priorities: Low, Normal, High. There is another “secret” priority of Critical, but this is only used by the framework.
Next: The priority queue is wrapped in a few classes. There is an enqueuer, and a dequeuer, these are known as the queue pairs.These pairs wrap the same queue reference deep down, but they give us a way to encapsulate limited functionality of the queue. If we wanted someone to be able to enqueue only, we can give them the enqueuer. If we wanted to let someone else dequeue, we could give them the dequeuer. In general, you’ll never see the dequeuer side of the queue (it’s used inside the framework though), and you’ll pass around the enqueuer side to everyone who needs it.
Every message that you send an actor will inherit from the message class. When the receiving actor handles the message (this happens in Actor core.vi) it will call the Do.vi method of the Message class. So messages just need to override this Do.vi to perform whatever action they need.
That being said, the message classes are NOT related to the actor class (it’s a Association or “Uses” OO relationship) . So what can messages do to the actor objects? They can only call public methods. This means that normally all a message class is doing is calling one public method of the actor. The act of creating a message class and writing the “Do” method is pretty well scripted so you almost never actually have to manually create this code. The normal workflow is to create the Actor Method, then run the scripting code that will create a message class for that method.
Questions? Ask them on this discussion topic and I’ll try to answer them!