Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Problem understanding change from QSM to AF

Solved!
Go to solution

Hello there,

I have what is probably a basic question as I try and get my head round AF - if anyone could help it'd be great...

I have a data-acquisition module that previously existed as a traditional QSM.  Each time the 'Get Data' state ran it would finish by (conditionally) adding 'Get Data' to the QSM queue to cause it to be over and over again.  Similarly, my 'Initialise' state would conditionally enqueue 'Get Data' so that the acquisition auto-started.

This is the bit I don't get about AF - I have the messaging and associated 'Do' methods in place which call the appropriate public method of my data aquisition class.  How can I let a method enqueue a call to itself again? It doesn't have access to the message queue.

I see the 'time-delayed send message' but that seems different - it will keep enqueuing regardless of whether the 'Get Data' method completed successfully or not.

Can anyone help my understanding? I know I must be missing something and trying to do something the 'wrong' way.

Thank you,

Martin

0 Kudos
Message 1 of 10
(7,780 Views)
Solution
Accepted by MartinMcD

Hi Martin,

MartinMcD wrote:

This is the bit I don't get about AF - I have the messaging and associated 'Do' methods in place which call the appropriate public method of my data aquisition class.  How can I let a method enqueue a call to itself again? It doesn't have access to the message queue.

Actually, it *does* have access to the message queue. The "Actor" class has a method "Get queue for sending to self" (the leftmost in the image):

Screenshot - 16-Jul-12 , 15_15_05.png

Hope this solves your problem!

Best,

Onno

Science & Wires — a blog on LabVIEW and scientific programming
http://scienceandwires.com
Message 2 of 10
(4,367 Views)

Ahh, fantastic thank you for that.  The icon for that function isn't on my pallet so I was thrown but I see it in the project folder now.

Am I right to assume it is okay to use it, and I'm not trying to shoe-horn bad practice into AF? I can't see how I could manage without it but maybe I'm being blinded by my old ways.

Thank you,

Martin

0 Kudos
Message 3 of 10
(4,367 Views)

Yes, you need to know it's there Also note its sister "Get queue for sending to caller", which you can use to send messages to any Actor that spawned the current one.

Using it is perfectly OK, by the way. It's actually quite essential to get anything done 😃

Good luck!

Best,

Onno

Science & Wires — a blog on LabVIEW and scientific programming
http://scienceandwires.com
0 Kudos
Message 4 of 10
(4,367 Views)

...and I'm not trying to shoe-horn bad practice into AF?

It's no secret I'm not a fan of the QSM, but whether or not you are doing something "bad" depends a lot on your particular implementation.

Converting a standard QSM to a well-implemented Actor likely requires more than just wrapping your QSM in an actor object.  You may have to change your ideas about messages and how the actor responds to them.  Most QSM implementations use a command-based approach.  Each message is essentially commanding the receiver to do something, which it happily does regardless of whether or not it should do it.  Actors use a request-based approach.  Each message is asking the actor to do something.  The actor decides whether or not it makes sense to do what was requested. 

In the video link recently posted Hewitt used a chicken metaphore to describe the difference.  In typical programming models if someone needs a dead chicken they grab an axe and cut the chicken's head off.  If the chicken were an actor, you'd ask the chicken to cut off it's own head.  It's entirely within the chicken's perogotive to decide not to cut off it's own head if the conditions for doing that action have not been met.

In very simple cases there isn't much difference between a QSM and an actor.  An actor can be written to accept and execute every request.  I'll call these "behavioral stateless" actors, meaning the reaction to each message (which defines the actor's behavior) doesn't change over time.  As the actor's behavioral complexity increases, it makes all the difference in the world because the actor keeps track of it's own state and how it will respond to requests.  The actor is filtering (as appropriate) the messages it receives instead of forcing the sender to know which messages are appropriate at any given point in time.  (Requiring the sender to know the state of the receiver before sending a message leads to race conditions that are difficult to resolve.)

One important thing to keep in mind when designing actors is they can receive any message at any time.  It's up to the actor developer to build the actor in a way that it is robust against unusual message sequences.  For example, what happens if--while your actor is happily sending itself Get Data messages--it receives a Get Data message from another sender?  Now you have two Get Data messages on the queue while the actor expects there to be only one.  Depending on your data collection exit-conditions and implementation, it might do something innocuous like collecting an extra data point before going back to Idle.  It might also automatically start another data collection loop.  Or, if your Get Data message also includes data like a file refnum to write the data to, the actor may slip into an unexpected bi-stable quasi-state where it alternates between sending data to each of the two files.

Take a look at your actor code, grab a pencil and paper, and instead of just working through the happy path (the expected sequence of messages received,) figure out what happens to your actor at any given time when it receives an unexpected message.  If you do that and don't discover any unusual behavior, you'll probably be fine.  However, many QSM implementations do not handle out of sequence messages well and exhibit unexpected behavior when they receive them.  IMO that is contrary to one of the fundamental principles of creating a well-behaved actor.

This doesn't mean you cannot require a sender to issue requests in a certain order to achieve a goal.  It's perfectly reasonable for a Data Persistence actor to require an Open File message before it can sucessfully process an Append Data message.  What it means is the actor has a defined, predictable, and intential reaction to every possible message at any point in time.  It is impossible to "confuse" the actor simply by sending it arbitrary messages at arbitrary times.  I usually write my actors to send a "Debug.UnhandledMessage" when a specific message is inappropriate for a given behavioral state.  It helps me discover and squish bugs during development.

Message 5 of 10
(4,367 Views)

Most QSM implementations use a command-based approach.  Each message is essentially commanding the receiver to do something, which it happily does regardless of whether or not it should do it.  Actors use a request-based approach.  Each message is asking the actor to do something.  The actor decides whether or not it makes sense to do what was requested. 

Thank you for taking the time to write such a detailed response Daklu.  I like the chicken metaphor and that explains exactly the issue I have been having with understanding the AF, but the other way around...

My app at the moment is a big data aquisition/analyis program with lots of independent modules/actors.  Each module is a separate vi with or without a front panel and is an event driven producer/consumer where the events can be user interface events or messages coming from other modules.  So as an example my 'Log & Save' actor would send out messages (user events) to all the open measurement child windows (say an FFT, time series, level vs time etc) and say 'Please Save Your Data'.  It isn't worded exactly like that but not far off - each message is written very much as a request rather than a command. Each child will then then decide whether it is in a position to save or not (no data, currently logging, disabled etc) and either perform the save or send a message back saying 'Sorry, nothing to save' for example. 

The issue (of understanding, and clearly I'm wrong) I've had with the AF is exactly that it has seemed to me to be a command message because the Do method calls a public method on the appropriate object eg 'Save Data', and so I couldn't see how to implement the request-messaging that I've done.  I realise that I should just build the same choose-whether-to-comply logic into that public method, and perhaps name those methods carefully so they seem/feel more like the handling of requests than execution of commands.

I like the sound of the Debug.UnhandledMessage that you return - do you use self-addressed messages or pass in a reference to a debug actor at initialisation?

Thank you,

Martin

0 Kudos
Message 6 of 10
(4,367 Views)

Daklu wrote:

Actors use a request-based approach.  Each message is asking the actor to do something.  The actor decides whether or not it makes sense to do what was requested

Just to clarify, isn't the Actor Framework QDSM (Actor Core.vi) based upon the command pattern?  So it is not an "Actor" in the Actor Framework until the developer implements methods that react to the current Actor state?

Daklu wrote:

I usually write my actors to send a "Debug.UnhandledMessage" when a specific message is inappropriate for a given behavioral state.  It helps me discover and squish bugs during development.

What method do you use for tracking behavioral state?  Do you use boolean flags or enums in your Actor's private class data?

CLA, CTA
0 Kudos
Message 7 of 10
(4,367 Views)

LVB wrote:

Daklu wrote:

Actors use a request-based approach.  Each message is asking the actor to do something.  The actor decides whether or not it makes sense to do what was requested

Just to clarify, isn't the Actor Framework QDSM (Actor Core.vi) based upon the command pattern?  So it is not an "Actor" in the Actor Framework until the developer implements methods that react to the current Actor state?

It's an actor as long as it inherits from the class Actor. It just doesn't do much until it has methods. 🙂

LVB wrote:

Daklu wrote:

I usually write my actors to send a "Debug.UnhandledMessage" when a specific message is inappropriate for a given behavioral state.  It helps me discover and squish bugs during development.

What method do you use for tracking behavioral state?  Do you use boolean flags or enums in your Actor's private class data?

Since it is private state, it's whatever seems appropriate to that particular actor... there isn't one true way here. Sometimes it is a flag that says, "I shouldn't be receiving this message until after I've gotten this other message". Sometimes its an internal enum like a state machine. In other cases, I just try to do the thing and if I get an error trying to do it, that's the signal that I'm in a bad state and I report the error accordingly (sometimes by shutting down, sometimes by sending a message up to my caller).

0 Kudos
Message 8 of 10
(4,367 Views)

MartinMcD wrote:

and perhaps name those methods carefully so they seem/feel more like the handling of requests than execution of commands.

The naming convention adopted in .NET languages is "TryXYZ", which returns a boolean about whether or not the task was done, where as "XYZ" directly returns an error (exception) if it cannot be done.

I've often dodged the issue in my own code because I tend to think of the actors as announcing, "I'm now doing this" and letting everyone who hears the message decide if they care or not. For example, the Last Ack Msg... you can think of it as a command "Your child has stopped... do something about it!" or you can think of it as an announcement, "I'm stopping now. You might want to react."

0 Kudos
Message 9 of 10
(4,367 Views)

I realise that I should just build the same choose-whether-to-comply logic into that public method, and perhaps name those methods carefully so they seem/feel more like the handling of requests than execution of commands.

Assuming the public methods you're referring to are the actor's message.Do methods, yep, that's one option.  Where exactly you elect to put the choose-to-comply logic depends partly (again) on your implementation.  I think all of the AF example code I've seen embeds all of the actor's behavior in the actor and/or message classes.  I often create actors that wrap and delegate to pre-existing classes rather than creating new functionality in the actor from scratch.  This lets me code and test the functionality without the overhead and complexities of dynamic launching.  If your actors delegate, you can put the choose-to-comply logic in either the message class, actor core, or the class being delegated to.  I can't offer any advice on which solution is the best... only that you try to keep it consistent.

I like the sound of the Debug.UnhandledMessage that you return - do you use self-addressed messages or pass in a reference to a debug actor at initialisation?

Actually, neither.  I do actor-oriented programming but I don't use the Actor Framework.  Currently my actors are based on a slightly different messaging model.  In the AF the message and message handler are combined into a single class, which makes all messages "input" (or request) messages.  That is, each message is defined by the actor that accepts the message as a request to do something.  If actor A wants to receive status updates from actor B, it uses callbacks.  It might send an A.ProcessBStatusUpdate message object to B as an argument of a B.RegisterForStatusUpdate message.  When B needs to send out a status update message, it is sending a message to A that has been defined by class A... A.ProcessBStatusUpdate.

My messaging model separates the message and message data from the message handler.  That allows me to have both "input" (or request) messages and "output" (or event) messages for each actor.  Instead of B sending an A.ProcessBStatusUpdate message everytime it updates, it sends out a B.StatusUpdated message.  B doesn't know or care if anyone ever does anything with the StatusUpdated message; it just sends them out because some other actor might need to know about it. 

There are lots of specific differences between my actor implementations and the AF actor implementations, but functionally they are very similar.  Semantically I prefer input and output messages because it makes more sense to me and is easier for me to reason about.  I also haven't figured out how to fit the AF into my natural code evolution paths without being very disruptive.  On the other hand, I believe the AF's restriction to input-based messages only is a more "correct" interpretation of the actor model explained by Hewitt.

To get back to your question, the Debug.UnhandledMessage message is an output message.  B places the message on its output queue (something that does not exist in the AF afaik) and is received by B's "governing" actor, so there is no need to use self-addressed messages or pass in a reference to a debug actor in my model.  However, I believe either one of those solutions would serve the same purpose in the AF.

Just to clarify, isn't the Actor Framework QDSM (Actor Core.vi) based upon the command pattern? 

Yes, the AF is based on the command pattern.  The command pattern is a different concept than what I was referring to when I said many QSMs are "command-based."  Command-based and request-based refers to who is making the decision on whether or not to do what the message indicates, the message sender or message receiver.  I can go into more detail if you'd like but I'm not quite sure where you're going with the question.

So it is not an "Actor" in the Actor Framework until the developer implements methods that react to the current Actor state?

There's always lots of room for interpretation, and I'm not the person to define exactly when an object in the AF "becomes" an actor.  One could reasonably argue any class that inherits from the Actor base class is an actor.  It may not be a particularly useful actor, but it could be considered an actor in the AF.

As for "reacting to the current Actor state," it kind of depends on what you mean by "the current Actor state."  Like so many things in computer science, the word "state" is overloaded.  Obviously the actor has to be active/executing for it to react, so I'll assume you didn't mean the actor's running/not running state.  Often "state" means the values of all the system's variables at a given time.  That is a reasonable way to use the word, but to help avoid confusion I'll refer to that as the actor's "data state."

An actor's "behavioral state" defines the subset of valid messages an actor responds to (as opposed to ignoring, throwing an error, sending an UnhandledMessage message, etc.) at a given time.  Suppose I have a non-actor instrument driver class with a Connect method.  If Connect is called when a connection is currently active, the instrument automatically disconnects, resets, and establishes a new connection. 

Implementing the driver as an actor (D) presents a bit of a problem.  What happens if actor A has established a connection and is acquiring data from the instrument, and actor B sends D a Connect message?  A's data collection gets interrupted!  So while designing D we decide we only want it to accept the Connect message if it is currently unconnected.  If it is connected, additional Connect messages are ignored.  "Connected" and "Disconnected" are different behavioral states.  The actor's behavior (how it responds to any given message) has changed.

Having explained my interpretation of the difference between data state and behavioral state, I read the latter part of your question above as,

"...implements methods that react differently based on the Actor's current behavioral state."

Does an actor need to have methods that react differently based on an actor's data state?  Nope.  Data states change quite frequently.  Behavioral states usually change relatively infrequently.  Does an actor need to have multiple behavioral states to be an actor?  Nope.  I can make a valid actor that does nothing but add two numbers together, return the result, and increment its internal NumCalcs counter.  Its data state changes every time it recevies an Add message, but it never changes behavioral states--it is a behavioral stateless actor.

(Having gone through all that, I'm not even sure I answered your questions...)

What method do you use for tracking behavioral state? Do you use boolean flags or enums in your Actor's private class data?

It depends.  If the behavior changes are small and limited to a few messages I might use flags or enums.  When the number of behavioral states becomes large or the behavior changes are significant, I'll switch over to one of my state machine implementations and swap in a new set of message handlers in a single go.  (I've described/shown them in various posts on Lava but don't have a link handy at the moment.) 

In my state machine actors each behavioral state contains its own set of message handlers.  I think the AF's messaging system pushes you more towards a design where a single message handler is invoked for a specific message regardless of the behavioral state, but the message handler invokes different code based on the behavioral state.  Both accomplish the same thing.  Personally I find the idea of message handlers inside a state more natural than states inside a message handler.  YMMV. 

Unfortunately, I don't have enough experience with the AF to provide advice on how to code a system with the message handlers inside the states.  I've played around with creating MessageHandler classes that include all the message handling code for a given behavioral state, but I don't know how well that would apply to an AF application.

0 Kudos
Message 10 of 10
(4,367 Views)