LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Stopping processes in helper loop of actor core

Solved!
Go to solution

I'm trying to use a data acquisition device within the actor core helper loop but am having trouble getting the loop to stop.  I'm sure this has been covered, but I wasn't able to find a discussion specific to the actor framework.  In my project, when I click a trigger button, I generate a digital waveform with 0s and 1s that is used to trigger a read command on my device.  However, I cannot figure out how to pause the acquisition before the loop terminates.  In the consumer/producer architecture, this is easily done by adding a stop event to the enqueuer.  However, I don't see a good way to do this in AF.  Once my acquisition starts, how do pass a command into the loop to make it stop?  Everything I have tried (e.g. writing a stop command into the class private data of the actor class) fails since it boils down to not being able to get data into the loop.  Obviously, I could incorporate a consumer/producer strategy, but then I'm defeating the purpose of using AF in the first place.  Any suggestions?

 

 

0 Kudos
Message 1 of 9
(2,776 Views)

Your helper loops will need some form of communication between your Actor Core and the helper loop. I usually use Queues or User Events. That's not defeating the point of the AF as the queue or user event is strictly internal to that actor.

 

Say you're using a queue- in Actor Core, create a communications queue and store the reference in the Actor's private data, also sending it to the helper loop. When you need to tell the loop to "do something", then the method in which "something is done" should send the right message to the helper loop.

 

One thing to keep in mind with acquisition-based helper loops: it is tempting to try to spin up an actor at the beginning of the program that handles "acquisition", then have an internal state machine where you can tell it to start, stop, pause, reset, etc. If you truly need this, it can be done, BUT, it's complicated (what if there's an error? How will it notify the caller? How does the caller know how to send a reset message? How does the caller know it started acquisition properly?). A better way is to just spawn an acquisition actor as-needed. Acquire your resources in Pre-launch init. If that fails, then your caller knows because Launch Actor will return an error. The acquisition actor can just start up and begin acquiring. When you're done acquiring, just Stop the actor. When you need it again, launch it again. You generally don't gain anything by keeping the Actor alive while it has nothing to do.

Message 2 of 9
(2,728 Views)

Hi,

 

I haven't used queue too much, so I'd like to stick to user events for now.  I also like your suggestion about using pre-launch init and only launching the actor for acquisition.  But for now, I am still struggling with the idea of using user events to stop my loop.  Say I have a simple actor which has a 'start' and 'stop' button and some method like generating a random number.  When I click 'start' I want my random number generator to start and then when I click stop, I want it to stop.  I can create a user event for 'start', which send a message to my actor to run a Read Data.vi method, which has a loop inside of it.  At each instance of the loop, I generate a user event to display my random number to an indicator.  Now, I need to stop this method somehow.  When I select 'Stop' on my front panel, I can generate another user event. I thought to write a boolean value to the actor class private data but this doesn't work as my Read.vi method will just run indefinitely.  That is, that value is not passed to the method loop.  I need something like a dynamic user event which gets broadcast everywhere (or a queue, I suppose).  Does that mean I need to create another event structure inside my Read Data.vi method?  This doesn't seem right.  If you have an example with user events (or queues) to do this, I would appreciate seeing it.

0 Kudos
Message 3 of 9
(2,713 Views)

I don't have an example handy, but basically you want to put the helper loop inside your actor's Actor Core.vi, not inside the method. It should be in parallel with Actor Core's Call Parent function, not within the message's method. User Events and Queues can both do this, it's just a slightly different implementation.

 

For your example, when you send a Start message, don't put the loop in the Start method. Send a message to the helper loop to Start in the Start method, and have the helper loop send its data via a normal Actor method back to the Actor's enqueuer.

0 Kudos
Message 4 of 9
(2,711 Views)

I always have a helper loop inside of my actor core.vi, which is where I place my event structure.  However, if I want to run a continuous acquisition, then I need to use the timeout event case because otherwise the event only executes once when it's triggered.  I tried to avoid the timeout event case for this purpose, but I suppose if that's the only way, then it's fine (I would have to toggle between a finite and infinite timeout case).  I could also add a second loop, but then I'm back to consumer/producer type architecture (again, this is fine if that's the only way to do this).  I agree that putting a loop in the method is a bad idea.  In other words, I can easily build a program to acquire every time I push the start button, but I can't get it to acquire over and over again (or at least not in any way that I can easily stop the loop).  

0 Kudos
Message 5 of 9
(2,708 Views)

If you just want one task to repeat frequently just use Time Delayed Send Message. Make a method and message that does the "thing" one time, then feed that message to Time Delayed Send Message (TDSM). Configure it to have -1 copies (so just keep sending) and a time value of something reasonable (say, 500 ms? Depends on the application.)

 

It will spawn an automatic "message sender" to send the message every 500 ms (or whatever you configure it as). It also returns a notifier- store this notifier in the Actor's private data. When you want to stop sending the message, send a notification to that notifier. You can choose things like "send next message now", "send next message then stop", "stop immediately", etc.

 

It's a great way to do repetitive tasks. I wouldn't use it for acquisition though as your acquisition loop has its own timing source, which will fight TDSM's timing source. For acquisition, I'd use DAQmx Events to trigger when it needs to acquire- then you don't have to use the Timeout case. Another way to do it is to have an "Acquire from buffer" message that will re-send itself after executing. Of course you need a way to *stop* this process, so you can either use TDSM with 1 copy, storing the notifier, and when you "stop" the process you simply send a notification that says "Don't send this message" and it'll stop.

 

You can also have a simple "Continuous mode" flag along with a Start, Sample, and Stop message. Start sets the flag to True and sends a Sample message. Sample checks the flag and, if it's True, samples one time and sends the Sample message again. Stop just sets the flag to False, so when Sample runs again it'll see that the flag is False and stop sending copies of itself. Note that this one will run VERY fast if there are no intrinsic timing limiations (like a DAQ sampling or something). It's also uninterruptible while Sample is running, so I would recommend either the TDSM or DAQmx Events method (if you're doing actual hardware sampling).

 

Hope that makes sense. TDSM is an absolute godsend for simplifying repetitive tasks that aren't super time-dependent. Note that, since it IS sending a message, you likely don't want the message it sends to be blocking or you will clog up the message processor.

0 Kudos
Message 6 of 9
(2,705 Views)

Thanks for telling me about TDSM; I had not heard of it before.  My issue is that many third party devices are not compatible with DAQmx, or may not have hardware triggering capabilities.  I suppose one possibility, perhaps what you were alluding to, is to send a message after each acquisition is complete asking the helper loop, whether to continue.  There is the overhead of sending messages, but as long as the loop isn't executing too quickly, perhaps this is ok.  I'll continue to think about it.  I already made a program to create a software digital waveform trigger and this works nicely, but, again, I can't stop it until the entire waveform has been sampled.  

0 Kudos
Message 7 of 9
(2,695 Views)
Solution
Accepted by topic author speclabAI

If the function doesn't return until all samples have been completed, you *can* just block and wait for it to finish. There will be some delay when you need to stop, but unless it takes more than a second or so to return it shouldn't be noticeable to the user. I think that would be better than just spamming it with "Are you done sampling yet?" (which itself could be done at a reasonable rate- just add a Wait(200) or something).

 

If it has a defined sampling time (say, once a second) you can get around most of the blocking by adding a timeout to the event structure or a TDSM that's about 80% of the sample period. For example: say your device samples every 1000 ms, you can send a "Read" function after 800ms of waiting. If another message comes in (like Stop) during the Wait period, it's processed immediately. If it comes in during the other 200ms, well, you only have to wait 200 ms.

 

If your function works such that it both starts a sample period AND gets data in a single function (thus blocking for a full 1000 ms) well, then you're stuck waiting for it to complete every once in a while.

 

Either way I think adding all of the blocking/waiting functions to their own helper loop, that way your message handlers can process incoming messages quickly. And there's nothing at all wrong with having multiple helper loops in your Actor Core.

0 Kudos
Message 8 of 9
(2,691 Views)

Thank you for the message.  I think that using these approaches is not accurate enough for my purposes.  I was able to implement the prelaunch init. suggestion that you made and it really does make my life easier.  You are right, no need to run the actor if it does initialize properly.  And I can use the destroy user events override to clean up references and tasks.  It's a much easier work flow.  I'm still stuck with some triggering issues but I'll post that separately.  

0 Kudos
Message 9 of 9
(2,608 Views)