LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Actor framework - message communication problem


@BertMcMahan wrote:

Of course, ask away... I'm certainly no AF pro.

 

1- Yes, any IO that takes longer than a few milliseconds will go in a helper loop in Actor Core.

 

2- UI Event Loops also go in Actor Core. Look up MGI's Panel Actor; it's an absolutely wonderful tool. It uses helper loops in Actor Core to catch button presses, then you can either do some stuff in the helper loop (if it's very trivial and doesn't need to interact with anything) or you can just use Self Enqueuer and send yourself a message.

 

"Helper Loops" always go in Actor Core. I suppose you could put them in a subVI, but they never run in message payloads. Those would just be called a "loop"- AFAIK, most of the common usage of the term "helper loop" in this context refers to a loop running in Actor Core in *parallel* to the Call Parent Method Actor Core.

 

You do have to have communications between the two, however. Helper Loop to Actor is simple; you just use Messages you send from the helper loop to the actor itself. The reverse means you have to have a secondary channel. I normally use User Events. I'll create a Start user event in Actor Core prior to the main Actor Core loop starting, then store the event ref in the Actor's private data. In Stop Core, you fire the user event that will then shut down the loop.

 

(MGI's Panel Actors does that for you, which saves a bunch of boilerplate code and "just works". It also provides some "Initialized", "Panel Shown", "Panel Hidden", and other Events that your helper loop can respond to. One of my most favorite tools.)

 

Regarding IO- I do put most IO in helper loops, but it depends somewhat on exactly what you're doing. Most of my IO goes in helper loops because it's a semi-continuous process that needs to execute for many seconds, minutes, hours, or (in one case) weeks. If it's a one-off thing then I'll put it in a message. Otherwise you have a Message send a user event back to the helper loop and have the helper loop send something back. Usually I'm happy enough to just block during the IO if it's short and non-recurring.

 

One thing I'd caution against is handling your UI stuff *in* your Actor Core's helper loop. Yes, it's easiest initially to work in there, but I recommend storing all of the control refnums in the Actor's private data, and handle all actual displays or updates in Messages. It's a touch more work to get started but it'll pay off for any non-trivial UI's. For example, your Helper Loop won't have access to the Actor's private data once it starts, so it can't make context-sensitive decisions. I guarantee you'll also find that you need your Messages to manipulate the GUI in some way at some point, so doing it via refnum is a LOT easier than trying to send messages back to the helper loop via User Event.

 

Sorry for rambling a bit.. hope that helps, lol. Please also let me know if any of this either doesn't make sense or sounds like the wrong way to do it. I've gathered most of this from tutorials and toolkits I've seen online, so I could've misunderstood something, but I've been doing AF for about 5 years intermittently and this method seems to work very well.


I am in a similar situation using the Actor Framework, been using it in production code ~4 years, playing with it for around 5. 

 

This is my opinion based on some experience, I am open to any criticism. There was not a lot of training material out there for the AF so most of this knowledge was found the hard way by direct trial and error. 

 

You bring up a point that has always bugged me with AF: 


 

... For example, your Helper Loop won't have access to the Actor's private data once it starts, so it can't make context-sensitive decisions. I guarantee you'll also find that you need your Messages to manipulate the GUI in some way at some point, so doing it via refnum is a LOT easier than trying to send messages back to the helper loop via User Event.

 

 

I never could get past why you would need to create refs or notifiers to get into the helper loop. I used the MGI model for helper loops when I first started using AF but In my mind I thought it should not be that hard/complex to interact with the actor core class instance data. The quarter finally dropped and I realized, I was fighting the AF instead of using its power. I came to understand that running loops in an AF should be message based, not soemthing sitting in the Core VI that has no access to the actors state. After having that realization I stopped fighting the AF and started using it for just about everything. I never came across an instance where the message loop/handling was slower than the I/O task it was wrapped around. The timing of the loop was always dictated by the I/O, not the loop  implementation. 

 

This is an example of what I am talking about. A server/client connection actor in which the TCP read call sits in an actor message VI, that calls itself , it runs fast, works great, and you never have to worry about all the issues of a loop running in the actor core. 

Jay14159265_1-1683579652851.png

 

 

The argument against doing it this way is using the AF for UIs where the User Event Structure needs to exist in a loop in an actor and does not release execution back to the underlying AF. In this case I don't think that Actors are the correct tool for the job. Probably a controversial view, but if you have to hack it to make it work ( by passing refs / notifiers OUT of the actor ), it was probably not the intended use case. If NI wants to step in here and correct me, that would be awesome : ) 

 

Making extensible UI pages is probably best handled by making a page/window/form base class object (not actor) that can launch a handler Actor that can accept and act on messages from the page/window. This way the event loop does not run in the actor. This works great because it decouples the IP ( which runs in the actor ) from the user interface which is just a class object vi with no 'smarts', it just sends messages and displays state. 

 

 

 

 

______________________________________________________________
Have a pleasant day and be sure to learn Python for success and prosperity.
Message 11 of 14
(320 Views)

I didn't quite follow your concept for making UI's work- it sounds like you're making a VI that executes, runs loops, etc that has your actual GUI on it, and it launches another Actor that it communicates with. Is that right? Any reason the GUI can't be actually IN the actor? I do love having my GUI's decoupled from my business logic. Right now my displays are all Actors and 90% of them are decoupled from business logic, but they stay Actors.

 


@Jay14159265 wrote:

I never could get past why you would need to create refs or notifiers to get into the helper loop.


Perhaps there was some misunderstanding, I don't send anything to my GUI helper loop. All of the GUI handling is done within the Actor's messages. The only thing the helper loop is doing is monitoring for button presses, screen resizes, etc. and sends messages notifying the Actor of such. When I need to do something like update a plot or gray out a button, I do that in a message. I read the refnum of the appropriate control or indicator inside the refnum and operate using property nodes.

 

With your use case I can see the self-calling message working well. I've used it in the past as well. If your helper loop relies heavily on the Actor's state, then it probably shouldn't be a Helper loop since you have to pass messages back and forth so much.

 

Generally, my Helper loops only communicate TO the main Actor. Yes, I'll have user events to stop them and sometimes trigger something, but other than that it's too much of a pain to sync them up (as you've mentioned). Something like a UI event handler only ever sends messages TO the Actor other than Stop. Similarly, a DAQ Read Actor will just sit there reading data and sending it to the Actor when it gets new data. If I want to stop or change the data acquisition (meaning I'd need access to some state info) then I'll just kill the Actor and restart it with the new settings. In that case it's designed that the Actor is never "Idle"- if I want to stop reading data, I just Stop the Actor itself. Starting and stopping an Actor has such little overhead that it's not worth it to give that one any State information.

 

Of course if I had a data acquisition app that DID need to continually monitor State or something, then a Helper loop is a lot less... helpful 🙂

 

I think it just comes down to a compromise of speed and usability and, of course, just using the tool that works best in a specific instance. In your case, your Actor always sends itself a message to do the same thing again right when it finishes. Your timing is then regulated by either that message having a Wait function in it or the operation itself controlling timing (like a DAQ read function). As long as that happens quickly then it's all well and good, but messages that take a long time to return have the potential to block snappy execution of the Actor. (if your timeout was something like 30 seconds, you'd be waiting an average of 15 seconds before any message could be read).

 

Another downside to that method is that *all* new messages arrive while another message is being handled. Since the handled message will always re-send itself, then when a new message comes in, you're guaranteed to have another "Read myself" message queued up behind the new message. This means that you have to be aware of that fact in all incoming messages. Stop will just stop the Actor and throw that other message away, which is fine, but it's something to keep in mind. If some configuration changes, and the next "Read" step needs to do something different, that could get complicated. Again you can work with this by simply having all configuration stuff stored in the Actor, and never send information on what or how to Read in the message itself (i.e., the "Read" message simply triggers a read, and contains no payload info beyond that).

Message 12 of 14
(309 Views)

@BertMcMahan wrote:

I didn't quite follow your concept for making UI's work- it sounds like you're making a VI that executes, runs loops, etc that has your actual GUI on it, and it launches another Actor that it communicates with. Is that right? Any reason the GUI can't be actually IN the actor? I do love having my GUI's decoupled from my business logic. Right now my displays are all Actors and 90% of them are decoupled from business logic, but they stay Actors.

 


@Jay14159265 wrote:

I never could get past why you would need to create refs or notifiers to get into the helper loop.


Perhaps there was some misunderstanding, I don't send anything to my GUI helper loop. All of the GUI handling is done within the Actor's messages. The only thing the helper loop is doing is monitoring for button presses, screen resizes, etc. and sends messages notifying the Actor of such. When I need to do something like update a plot or gray out a button, I do that in a message. I read the refnum of the appropriate control or indicator inside the refnum and operate using property nodes.

 

With your use case I can see the self-calling message working well. I've used it in the past as well. If your helper loop relies heavily on the Actor's state, then it probably shouldn't be a Helper loop since you have to pass messages back and forth so much.

 

Generally, my Helper loops only communicate TO the main Actor. Yes, I'll have user events to stop them and sometimes trigger something, but other than that it's too much of a pain to sync them up (as you've mentioned). Something like a UI event handler only ever sends messages TO the Actor other than Stop. Similarly, a DAQ Read Actor will just sit there reading data and sending it to the Actor when it gets new data. If I want to stop or change the data acquisition (meaning I'd need access to some state info) then I'll just kill the Actor and restart it with the new settings. In that case it's designed that the Actor is never "Idle"- if I want to stop reading data, I just Stop the Actor itself. Starting and stopping an Actor has such little overhead that it's not worth it to give that one any State information.

 

Of course if I had a data acquisition app that DID need to continually monitor State or something, then a Helper loop is a lot less... helpful 🙂

 

I think it just comes down to a compromise of speed and usability and, of course, just using the tool that works best in a specific instance. In your case, your Actor always sends itself a message to do the same thing again right when it finishes. Your timing is then regulated by either that message having a Wait function in it or the operation itself controlling timing (like a DAQ read function). As long as that happens quickly then it's all well and good, but messages that take a long time to return have the potential to block snappy execution of the Actor. (if your timeout was something like 30 seconds, you'd be waiting an average of 15 seconds before any message could be read).

 

Another downside to that method is that *all* new messages arrive while another message is being handled. Since the handled message will always re-send itself, then when a new message comes in, you're guaranteed to have another "Read myself" message queued up behind the new message. This means that you have to be aware of that fact in all incoming messages. Stop will just stop the Actor and throw that other message away, which is fine, but it's something to keep in mind. If some configuration changes, and the next "Read" step needs to do something different, that could get complicated. Again you can work with this by simply having all configuration stuff stored in the Actor, and never send information on what or how to Read in the message itself (i.e., the "Read" message simply triggers a read, and contains no payload info beyond that).


Well at this point I feel bad for the OP because we are off on a tangent that might or might not be useful at this point. 

 

I agree with everything you are saying, especially about just using the tool that works best in a specific instance. 

 

For the UI, the UI VI with the User Event Handler launches an Actor that has a ref to the calling VIs front panel which is a typedef, then you can make messages for the UI handler Actor that updates the UI VI front panel / state. In this way you are instantiating the Actor class object (the UI handler) and passing it  everything it needs to run, you never have to pass references back up the call chain to the parent, it is all handled in the UI handlers context per 'the rules' https://en.wikipedia.org/wiki/Actor_model

 

In most of my designs the data I/O is a separate class than the data processing so there is never a case where an I/O actor is doing anything besides reading or writing to a file/bus/socket whatever. In this way there is typically only 5 methods that are defined: open, read, write, query and close. Then that data is passed off to some other class to process it.

 

Sometimes I connect the I/O directly to an instrument class in which case you need to worry about other messages on the AF queue besides read, write and close but those are handled slightly differently than the server approach where you are always listening for communication on a bus forever in a 'while loop'. 

 

I guess the other side of this is timing, I live in a world where I never need to worry about software timed DAQ, so I never worry about it. All I'm ever doing is reading a buffer. If I had to worry about it, I would try to figure out some way to make it hardware timed, software timed DAQ even on RTOS is not awesome. 

 

For the last message in the queue issue. This is something that happens, I have a  method to flush the queue on the exit message. Its a work around I guess, or a feature, who knows. I wish NI would generate a learning module for the Actor Framework. 

 

Well lots of thoughts. I need to try the the latest MGI Actor library. I have a feeling it has changed since the last time I was messing with it. 

 

Thanks for the conversation, hopefully the OP might get some ideas from reading. 

 

 

______________________________________________________________
Have a pleasant day and be sure to learn Python for success and prosperity.
0 Kudos
Message 13 of 14
(296 Views)

Yeah, this conversation has been very helpful.

 

Regarding the MGI Panel library- it actually contains implementations of Panels outside of the Actor Framework as well as Panel Actors. It's super helpful so I'd recommend looking at it, even if you don't use the Actor half. It sounds like you'd be able to use the library anyway.

 

All in all it sounds kind of like an "inheritance vs composition" thing. In my case, Actor Core's front panel is the GUI, and the Actor itself inherits from the Panel Actor class. In your case it sounds like the actual GUI is passed as a parameter to the GUI handling Actor. I can see how that would make things more generalized.

 

I also have a set of reusable "display only" Actors but they just contain the front panel on Actor Core instead of launching a separate display VI. It definitely helps usability when you can decouple displays from processing or data IO.

Message 14 of 14
(265 Views)