Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

AF Sync Messaging

Hi All,

I've been dabbling in LV actor framework for a while now. I do like it, but so far I've only tackled relatively small projects. For a while now, I've been trying to wrap my head around converting a large scale application I wrote and maintain over to AF. The application has grown over the years and it's really now too disorganized to work on, so I think it's a good time to rip it up and rebuild it.

However, I keep running into the same issue with AF. When I try to sort out what "things" will become actors, everything begins to fall apart. The system has 8 modules that operate fairly independantly, so I figured an actor would be appropriate to run each one. However, they each use shared resources (central cRIO for control and data collection, central robot to deliver liquids). As I try to imagine the messaging, I can't find a way do do the messaging asynchronously. In every case, the module will need to know the action is requested of the shared resource is complete before carrying on. It seems AF is written for the case where all actors can operate sperately.

What am I missing here? Am I thinking wrong about messaging or about what should be actors?

Thanks,

Tim

0 Kudos
Message 1 of 13
(5,240 Views)

Hi Tim,

IMHO a general answer to this question is difficult, especially if it comes to shared ressources. Different setups are avaliable...

with the information available, my first approach would be to have a central Actor for the cRIO. This Actor could have several subactors to delegate to...

Synchronous messaging shouldn't be used... but there is the possbility of implementing publishers and subscripctions using seperate channels which could do the job AND keep you from deadlocks.

I know.... only very general answers, you'd like to have mor flesh to the bone, so could you elaborate a little more on your application?

0 Kudos
Message 2 of 13
(4,892 Views)

Honestly, that was my worry with writing that. It's really tough to explain the whole application and not flood too much information. I'll give some more and see if it helps in understanding.

We have a cRIO that is tasked with collecting data from signals coming from each module. It also handles hardware changes on each module (i.e. fires a valve, sets a voltage, etc.). The robot is a liquid handling robot. Each module may request a liquid delivery. The request will sit in a queue until the robot can get to it, so the module may wait a bit to be serviced.

Now, here's where it gets tricky to me. Each module can process a script. That script is a list of functions to perform. Each of those functions may request hardware changes, or liquid deliveries or other time consuming actions. Those functions need to know when those actions are completed before they can carry on.

In my current application, that script is a file filled with class objects that all share a common ancestor. The executor just performs the "do" method of the ancestor and dynamic dispatch takes it from there. As you can see, this is very much like AF, though it's very rudamentary and written before AF was really popular in LV. But, in the current implemenation, hardware controls are handled through FGVs. This provided direct control and knowledge that the action was complete (or pass an error if it wasn't).

Does that help?

0 Kudos
Message 3 of 13
(4,892 Views)

Here's one idea:  Create a central Actor for the cRIO, as Oli suggests.  For each message that you send to that actor, as one of these lengthy commands to carry out, include a child message of an abstract "callback" message.  (Each module actor would use a different child of the abstract parent message for this purpose.)

The requesting module actor sends the command message (containing the callback message and any needed data parameters) and then "sleeps", waiting for results (or does other parallel tasks, as needed).  The follow-on method for the module's task is the target of the callback message it includes in its command message.

When the cRIO actor dequeues and handles the command message, it carries out the lengthy task, and at the conclusion, it retrieves the callback message, inserts any data into it (which would need to be defined in the abstract parent class's atribute definition) and sends it back to the original requesting module actor.

When the requesting module actor eventually receives the callback message, the message triggers its next task method, using any data in the callback message's abstract parent attributes.

This is asynchronous, and may or may not dovetail cleanly with your "script" architecture.  But it's a powerful design pattern, and it may be worthwhile to explore adjustments to your design to accommmodate it.

0 Kudos
Message 4 of 13
(4,892 Views)

I see where you're going there and I like that scheme.

However, still a few holes in my mind. Let me see if I can lay it out better.

The module has a method that needs to do the following:

1. apply a voltage

2. change a valve state

3. monitor a signal for a certain threshold

4. when threshold complete, change the valve back and shut off voltage

I've always thought of this as a method that the module actor could do. It literally has 50 plus methods like this, so breaking these down into stepwise back and forth communication seems like it would suck. Plus some are very complicated so I just don't think that's practical. I've assumed signal data would be delivered to the module actor by the cRIO actor. But then you can't monitor signals as they change within a method, right? Because that module actor is bound up completing the method itself. Again, I'm probably thinking of this all wrong, but I just can't get past these things.

I've thought of creating a script processing actor which is called by the module actor. It could manage calling each of these methods in the order prescribed by the script. That seems to fit nicely with what you've described. I.e. Here's a step, go do it, let me know how it worked out and I'll decide what the next step is. That's kind of how I have it now, except it actually does the methods and the methods use FGVs to control hardware and DVRs to get signal data as needed.

0 Kudos
Message 5 of 13
(4,892 Views)

There's (at least) two variations you could employ:

1.) Let the cRIO actor do the heavy lifting: Each method commands the cRIO with

  a.) A command, consisting of a "script" of task activities, which specifies sequence, target states/values, etc.,

  b.) A callback for when the task has completed successfully,

  c.) An alternate callback if the task failed for some reason.

In this design, the cRIO actor could employ your "script processing actor" to sequence the steps it's been given.  The methods called out by the script are cRIO methods to make hardware changes and specify values, thresholds, timeouts, etc., along with logic to control timing, sequence, and exception responses.  In this way, each message the method actor sends "looks like" an atomic operation on the part of the cRIO actor to make a complicated operation happen (such as voltage change, valve change, signal monitor, valve change, voltage change).

2.) Have the module actor manage the task, using the cRIO actor as an agent to affect the hardware: Each method commands the cRIO with

  a.) A command, such as "Set voltage X",

  b.) A callback for when the target has been achieved,

  c.) An alternate callback if the target could not be achieved.

In this design, the cRIO implements a simple, specific hardware change, then reports back success or failure.  In the case of success, the callback message continues the task:  It sends the next scripted command to change the valve state.  On success, the success callback calls a method that continues by sending the cRIO actor a command that amounts to "When signal Y surpasses threshold Z, send Callback A; otherwise, if timeout T is exceeded, send Callback B".  Once the successful callback is received, the method actor continues with the follow-up valve change and voltage change.  In this way, each message the method actor sends looks like an atomic change to some aspect of the hardware to achieve a desired hardware state, step-by-step.

It all depends on which actor it makes sense to be in charge of carrying out the script/sequence.

0 Kudos
Message 6 of 13
(4,892 Views)

I don't think either is a good fit. If I use the cRIO actor as the long method processor, it would prevent every other module from operating in parallel. The second scenario wants to break down the method into smaller commands. I guess I didn't fully convey how complicated those methods can be. I gave a simple example to show basically what it does, but in reality some of these are very complicated with many hardware changes conducted in loops while monitoring signals. Number 2 seems to take an already complicated method and make it much more complicated by creating a ton of back and forth messaging. Plus, it would become incredibly complicated to track what's happening in that method. Right now, that method is contained in a single VI. It may have many subs to complete the complicated task, but it's fairly easy to see the general flow in the single VI. My head already hurts trying to track all the back and forth messaging to see the overall method in what you are suggesting.

I should say though....thank you for your help! I've been trying to wrap my head around this for a while now, and this discussion really helps.

0 Kudos
Message 7 of 13
(4,892 Views)

thutch79 wrote:

If I use the cRIO actor as the long method processor, it would prevent every other module from operating in parallel.

Is it true that each "complicated method", once started, must run to completion before any other module can run its method?

Or can the cRIO "interleave" operations, doing a step for one method, then a step for a second method, etc.?

From your latest, it sounds as though (one architecture or the other) that you might be better off with synchronous calls to a hardware controller.  With synchronous calls (i.e., to a subVI that encapsulates hardware operations), you can easily pass in parameters and get results back out -- and these calls can be wrapped in loops, etc.

That would mean that using (asynchronous) actors may not be a good fit, but I would still consider using LVOOP for encapsulation and inheritance advantages.  Not every application is best served by AF -- it depends on relative independence of modules and if asynchronous communications between them makes sense.  As you point out, there is overhead involved with passing messages and signalling back-and-forth between actors in AF.  If the interaction is frequent and complicated, it may not be a good fit.  If you can partition things into a reasonable number of "responsibilities" that allow you to find "pinch points" that reduce the number of interactions and the amount of data passed (between actors managing the collection of responsibilities), then it might be a good fit.

0 Kudos
Message 8 of 13
(4,892 Views)

Maybe that's the best advice. This may just not be the right fit. I do like alot about AF, but I just can't get beyond the fact that much of what I need to do is syncronous. Unfortunately, that leaves me somewhat lost on where to turn to next. AF would be well suited if there were an syncronous call function. I know there is one, but every time I come across it there seems to be the caveat that it shouldn't be used unless your really need to. I understand why asyncronous communication is prefered, but I could see a nice compromise if you had an syncronous call with a timeout.

0 Kudos
Message 9 of 13
(4,892 Views)

I understand the admonitions to try to find ways to avoid using the synchronous "Send Message and Wait for Response" AF method.  However, if pressed, they'll admit that there are reasonable cases for using it.  (That said, I wouldn't base an architecture on this feature.)

But let us ask ourselves: What is AF with synchronous call-response?  Which begs considering what AF is, fundamentally, in this regard.  It's naturally asynchronous because each actor object is run within its own independent LV process -- you cannot wire data to it or from it any more than you can do that with a separate while loop running in parallel with other code.

So how do we distinguish synchronous code?  It's not running in a loop, which also means it can't be running in a seprate process.  How does that reconcile with AF?  Not really compatible (unless you take pains to short-cut it, like the SM&WfR method). 

Take the object out of the independent process...

Then what are you left with?  An object class, as before... with methods, as before.  Only the methods are not asynchronously called and executed by queuing "messages" to the object inside a loop.  With "just an object", you have methods on that object that you can call -- synchronously.

So if AF really isn't a good fit for your architecture, then you're free to consider using objects and synchronous method calls on those objects instead.  AF is really only that, with the object migrated into a message-handling loop that runs in a separate process.  Which makes it into an asynchronous architecture.

Perhaps your cRIO "actor" should be a cRIO "object" (of cRIO class).  And your method "actors" become method classes and objects.  Then you can design it to use synchronous or asynchronous calls (depending on loop implementations).  If the design ends up working well with it pretty much all asynchronous calls, then you can say that AF is a good choice -- because it standardizes that pattern and has the bugs worked out.

Lastly, you're quite able to design a "mixed use" case, if that ends up being the best fit.  You can have actors that represent major components of your application, such as the user interface, a test manager, etc.  Other elements of your system can be classes & objects that aren't AF Actors because they work better with synchronous methods.  This is how I design with AF, in fact.  My actors tend to be composed of non-actor objects, because not everything "works" as an actor.

Bottom line, consider different design patterns an solutions and "pick the best tool(s) for the job".  No one pattern fits all.

0 Kudos
Message 10 of 13
(4,892 Views)