10-29-2024 10:18 AM
I've mostly been working with DQMH over the past few years and this is definitely the first time I've messed with AF since interfaces were introduced. I'm currently messing with what should be a general re-use library for a larger AF-based project and many completely unrelated/unknown future projects. It has a lot of similar use-cases & characteristics as would a "Timer" so I'm going to use a Timer as an example for my question. So, let's pretend we want to create an Actor-based Timer that can be used in any future AF project. I am particularly fond of the DVR based Timer that NI has published on VIPM as well as the Extensible Session Framework Timer example. Two features that are handy, and have good parallels with the real module that I am working on (that we are calling a Timer here) with the ESF Timer is the ability to get a user event reference from the Timer so that you can register for an event that fires when the Timer expires and the ability to have it asynchronously launch a display of the actual Timer (I don't recall if I added this second feature to my copy of the ESF Timer or if it came that way).
So my initial thoughts are, we of course make our Timer an Actor so that it can easily be integrated into AF projects, easy spawning of clones, all the nice shutdown features of AF, etc... Actor Core could be the UI (hidden by default) and could be activated if that's desired or even placed into a subpanel. We want the actors that use this Timer to be able to be notified/messaged when the Timer expires. We also may want to set something up where the Timer sends the remaining time to the using actors at certain intervals.
All seemed totally possible while I was just thinking about. Then when I sat down to code I am not sure the best way to resolve the following issue. The key to keep in mind is that many many projects might want to use this same Timer actor.
Issue: The widely used method of sending messages up the actor chain is an interface which the calling actor inherits from. So that we mean in order to receive AF messages from our Timer, the user would need to also add the interface. This sort of makes it seem logical, in my mind, that our Timer own the interface since the interface will have to be present in any project which uses our Timer actor. Yet, this seems to be strongly discouraged, if anything it seems most people think the upstream actor should own the interface, if anyone owns it. Is there an exception to the rule for re-use library actors? Is using AF messaging the "wrong" way to go about "talking" to the callers of our Timer actor? Is this a case for some sort of use of self-addressed messaging (it seems there could be times you might want a data payload with the message though)? If the expiration notification is via some other medium, say user events, would the caller module provide the user event to fire for our Timer, or would it be better for the Timer to create the event and share it?
Very interested in thoughts and discussion about different ways to approach this scenario.
Solved! Go to Solution.
10-29-2024 11:14 AM - edited 10-29-2024 11:14 AM
@DoctorAutomatic wrote:
Issue: The widely used method of sending messages up the actor chain is an interface which the calling actor inherits from. So that we mean in order to receive AF messages from our Timer, the user would need to also add the interface. This sort of makes it seem logical, in my mind, that our Timer own the interface since the interface will have to be present in any project which uses our Timer actor.
I would agree with this. The caller interface would either be packaged with your Timer actor or packaged on its own.
@DoctorAutomatic wrote:
Is using AF messaging the "wrong" way to go about "talking" to the callers of our Timer actor?
Not necessarily, but using the interface paradigm might not be the best choice. This kind of scenario is an example where abstract messages for a caller have an advantage over the use of interfaces. It seems plausible that a caller actor might create multiple timers for different purposes. The caller would probably want to do something different upon the expiration of each timer. With interface messaging, there is only one VI in the caller available to handle all timer expiration messages. So you'd have to keep track of which timer was sending which expiration and then select the corresponding action. However, if the timers define an abstract expiration message for the caller, then the caller can pass a different response message to each timer it launches. Logic for handling each expiration message can be kept in separate caller VIs.
10-29-2024 02:11 PM
@zsmorison wrote:
@DoctorAutomatic wrote:
Issue: The widely used method of sending messages up the actor chain is an interface which the calling actor inherits from. So that we mean in order to receive AF messages from our Timer, the user would need to also add the interface. This sort of makes it seem logical, in my mind, that our Timer own the interface since the interface will have to be present in any project which uses our Timer actor.
I would agree with this. The caller interface would either be packaged with your Timer actor or packaged on its own.
This is correct. The "Timer Caller" interface is part of the Timer package, because it is statically linked. The caller (which is a client of the Timer) is written to receive messages from it.
@zsmorison wrote:
@DoctorAutomatic wrote:
Is using AF messaging the "wrong" way to go about "talking" to the callers of our Timer actor?
Not necessarily, but using the interface paradigm might not be the best choice. This kind of scenario is an example where abstract messages for a caller have an advantage over the use of interfaces. It seems plausible that a caller actor might create multiple timers for different purposes. The caller would probably want to do something different upon the expiration of each timer. With interface messaging, there is only one VI in the caller available to handle all timer expiration messages. So you'd have to keep track of which timer was sending which expiration and then select the corresponding action. However, if the timers define an abstract expiration message for the caller, then the caller can pass a different response message to each timer it launches. Logic for handling each expiration message can be kept in separate caller VIs.
I strongly disagree, mostly because abstracts are cumbersome, imply a message direction, and can lead to weird coupling effects. And really not needed here.
Have the timer self-terminate when it expires. It will send a Last Ack to its caller, which is the caller's clue that time has elapsed. Last Ack also contains the timer's (now invalid) enqueuer, which uniquely identifies the actor that just stopped. (Incidentally, doing this eliminates the need for an "expired" message for the caller, and thus possibly the need for an interface.)
If you need to send messages while the timer is running, include the timer's enqueuer as message data. The caller has access to that enqueuer anyway, so the tree is protected.
10-30-2024 05:28 AM
@DoctorAutomatic wrote:
Issue: The widely used method of sending messages up the actor chain is an interface which the calling actor inherits from. So that we mean in order to receive AF messages from our Timer, the user would need to also add the interface. This sort of makes it seem logical, in my mind, that our Timer own the interface since the interface will have to be present in any project which uses our Timer actor. Yet, this seems to be strongly discouraged, if anything it seems most people think the upstream actor should own the interface, if anyone owns it. Is there an exception to the rule for re-use library actors? Is using AF messaging the "wrong" way to go about "talking" to the callers of our Timer actor? Is this a case for some sort of use of self-addressed messaging (it seems there could be times you might want a data payload with the message though)? If the expiration notification is via some other medium, say user events, would the caller module provide the user event to fire for our Timer, or would it be better for the Timer to create the event and share it?
I had built such a Timer actor with the interface route. But, quickly discovered the constraints that @zsmorison pointed out about interface inheritance collision when multiple timers are used by the same actor. Also, I've learnt the hard way that interfaces are really unique to the context of the messaging, and must have the same intent even when transmitted up the actor tree.
I also tried the self-addressed message approach, where the calling actor could provide self-addressed messages for different events: tick interval, completion of timer, and abort of timer. In this case, the calling actor needed to provide those messages anyway, and they had to be public.
Now, I'm using Time-Delayed Send Message (TDSM), as the messages for this can at least be private in the actor's library. '# Copies' and 'Milliseconds To Wait' parameters enable me to decide the timed events that I want. Enabling and Disabling TDSMs are also easy as their respective notifier refnums can be in the actor class' private data for conditional manipulation.
Only recently, I needed a 'Timed Message', which I implemented as an Actor. so that it displayed a message along with a count-down numeric for the user to wait for a physical event to occur. I couldn't achieve this UI Actor with a TDSM alone.
11-16-2024 02:52 PM
Wow, thanks to all of you for the very thoughtful responses. The way seems clearer now.
Out of curiosity, when and why would you NOT have the callee (the role of the Timer in my post) own the interface? Maybe if you didn't expect to use the actor in another project and instead anticipated the possibility of replacing it in the current project with a new actor? It seems obvious to me my Timer example should own the interface, but that in most other circumstances the interface is actually better kept within/alongside the higher level actor, the one that's inheriting it, after all).