09-16-2016 06:00 PM
This one's easy.
In each state, you send yourself a message to go to the next state, then you wait 3 seconds. In state 4, you send yourself a message to go to state 4.
1. You're in state 4. You have sent yourself a message to go to state 4.
2. A stop shows up. But you don't act on it, because you are waiting.
3. You go to state 4, because that message is in the queue. You send yourself a message to go to state 4.
4. You go to stop, because that message is in the queue before "go to state 4". You send yourself a message to go to idle.
5. You go to state 4, because you told yourself to do so in step 3. You tell yourself to go to state 4.
6. You go idle, because you put that on the queue in step 4.
6. You go to state 4, because you put that on the queue in step 5.
These kinds of problems are inevitable and very hard to manage when you set up cycles in the message handler. If the "do next action" message is in your queue, you are going to have to dequeue and handle it, and it's virtually impossible to avoid getting one or two extra messages in your queue when you get the stop. You compound the problem in this case, because the message handler is working at 100% - every message takes 3 seconds, and ends by putting a new message on the queue. So there is *always* something to dequeue and process. But even if you didn't do that (if, say, your steps took zero time, but you only got a message every 3 seconds), you'd still have an extra message once in a while.
Here is one way to do this:
I would put this process in a helper loop in Actor Core, and drive it with a queue (or notifier, but let's assume queue). Store the queue refnum in the actor's attributes, before you invoke Call Parent Node. When you send a start or stop to the actor, it puts a start or stop on the queue.
The helper loop stores the action enum in a shift reigster, and waits for a message. If it doesn't get one, it times out, and does whatever is on the shift register (which will be "idle" to start). When it gets as start message, it changes the enum to begin. When you time out again, youe execute "begin", which changes the enum to "state 1", and so on. When you send a stop, you change the enum to idle. When you want to stop the entire actor, you kill the queue, and exit the helper loop.
You could do this without a helper loop by adding a state enum to the actor's attributes. The actor will need a "do next thing" message that it sends to itself, but here's the key: use something like the Time Delay Message to send yourself "do next thing". Do NOT send yourself "do next thing" at the end of each state.
09-19-2016 11:42 AM
Thank you for the 2 suggestions. Your first suggestion with the queue is exactly how i would handle this problem if i were to use my all time favourite QMH. I was afraid of doing something similar with actors because... i think i may have read somewhere on this forum that, it would be like some sort of an abominable anti-pattern. If i remeber correctly somebody suggested using the messaging tools built into the actor framework, rather than creating my own queues within the actors. I guess then your 2nd suggestion with the time delayed message is more along those lines. And then there are the channels, but for that I'd have to upgrade from 2015 to 2016, which I'll do at some point since i'm on support. Please correct me if anything i've said is wrong, as I'll be honest my brain still sort of "knee-jerks" in the direction of QMH and away from actors, although i'm trying.
Now more importanty however, and I haven't taken a closer look at it yet, but wouldn't your earlier post titled Implementing the state pattern in actor framework, https://decibel.ni.com/content/message/42468#42468 be the best way to handle my problem, now that you've sort of seen the sample code I've posted above.
09-20-2016 10:50 AM
Radical_Raf wrote:
Your first suggestion with the queue is exactly how i would handle this problem if i were to use my all time favourite QMH. I was afraid of doing something similar with actors because... i think i may have read somewhere on this forum that, it would be like some sort of an abominable anti-pattern.
What you do *inside* an actor is your own business. There are recommended practices, of course. But most of the rules of AF are about how actors talk *to each other*. A state machine, or even a small QMH, is a perfectly reasonable addition to an actor that must manage a sequence. The state machine runs the sequence, and the actor's message loop handles communication with the outside world.
It is occasionally even reasonable to set up an alternate communications channel *between* two actors, though the number of use cases is small. The one that leaps to mind is data streaming. AF queues are slow compared to a regular queue, which can matter in some high-speed applications.
Now more importanty however, and I haven't taken a closer look at it yet, but wouldn't your earlier post titled Implementing the state pattern in actor framework, https://decibel.ni.com/content/message/42468#42468 be the best way to handle my problem, now that you've sort of seen the sample code I've posted above.
I'd have to see what else is involved in your application. If you have only one method that changes behavior based on state, it's hard to argue against just using an enum, as you are doing. If you have, or anticipate, more than one such method, or if you expect the number of states to expannd, then state pattern starts to make sense.
09-20-2016 11:15 AM
Again thank you for all the advice. It is really appreciated. As for the intended application for this particular actor, it is simply meant to continuously read data from 2 different piece of hardware. Both pieces of hardware are DAQmx driven, both made by NI. They won't be used at the same time. This actor is supposed to run on legacy hardware as well as new hardware and acquire data. Both pieces of hardware acquire the same kind of data. the newer hardware is simply a new version of what NI has been selling for years, so even the DAQmx calls are for the most part identical, except that the newer hardware has a few extra features not there in the old, but that's pretty much it.
09-22-2016 12:59 PM
So i've taken your advice. I have a small QMH inside the nested actor. The queue in there does rely on a time out, 10msec to be exact. This may not the prettiest code i apologize, but i'm very very much a beginner at it. Would there be any way to accomplish what the attached is doing without having to rely on the time out of the queue inside the nested actor. Something in the back of my mind tells me that "time outs" are bad and bring bad luck.
P.S. I forgot to destroy the daq status user event in the top actor. My mistake, and apologies. Also I've just realised that my attempt at zero-coupling the status message is half baked, as well, i'm working on correcting it but won't post it here.
09-23-2016 02:14 PM
Radical_Raf wrote:
Would there be any way to accomplish what the attached is doing without having to rely on the time out of the queue inside the nested actor. Something in the back of my mind tells me that "time outs" are bad and bring bad luck.
There might be, but why bother? Using a timeout in this way is legitimate. I do it all the time. We use the technique in the Time Delay Message, for example.
09-23-2016 02:47 PM
awesome, thank you for the re-assurance, if it's good enough for the framework, it's good enough for me.