Actor Framework Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

What is a good way to repeat start and stop under Actor Framework

There are many ways to do this...I've done a few different ways myself. 

 

Here are 2 possibilities:

 

1) Open the connection to your task / instrument / data source in Pre-launch init and stick the reference in private data cluster. Then in actor core, grab the reference to that connection from the actor wire BEFORE call parent node and feed it to helper loop. The helper loop calls whatever read function is necessary to get the data, then feed data to appropriate destination. You can control the timing of the loop or wait for buffers to fill (eg. DAQmx task waiting for 100 samples) and the life of the helper loop in your preferred means of stopping helper loops. The data destination could be a message to itself (send your actor a message from your helper loop for further processing/logging) or a basic queue (I use queues for streaming high rate data to avoid the overhead of the messages) or any other data transfer mechanism. Just be sure to control the life of that data transfer mechanism at a higher level (whereever it makes sense). Close the references in stop core.vi. 

 

2) Put the traditional LabVIEW state machine (while loop with case structure with "init", "connect", "read", "close", "error" and other cases) in your actor as a helper loop. While this isn't really using the actor messaging the way it was intended, it has it's place. The actor provides a convenient asynchronous container and you can still use messages to configure the actor and the helper loop. I use this setup in places where i connect to a device but that connection drops often because the device is turned on and off randomly and thus I need to sit loop trying to re-connect until told to stop. 

 

If you need to transfer data from an incoming actor message to a helper loop, you can use standard any standard LabVIEW data transfer mechanism like user events, queues, notifiers, control ref's, etc. Setup the mechanism in Pre-launch init so that you can access it in methods and in actor core.vi you can grab it and hand the ref to the help loop. To send data from the helper loop to actor, send yourself a message (use "read self enqueuer" vi to get the actors own enqueuer for the helper loop to send messages to itself).

Message 11 of 23
(4,155 Views)

wrkcrw00, thanks for your detailed explanation : D

 

Today, I tried to add QMH architecture into Helper Loop but I left off the operation.

We could eliminate all of that the variant data from QMH, and the data appeared again... I feel itchy.

  

I learned that AF isn't a perfect architecture, should be combined to other one.

Im going to consider what is the best way to communicate between actor core.vi and helper loop.

 

 

Thanks,

Embor

Certified LabVIEW Developer
There are only two ways to tell somebody thanks: Kudos and Marked Solutions

GCentral
0 Kudos
Message 12 of 23
(4,138 Views)

This may be a little late, but one way to do this would be to use a helper loop that's timed by your DAQ system. A DAQmx Read function will block until it gets its data, then return that data. You can then have it send that as a message back to the main loop where it can be processed or filtered or whatever (or forwarded to another Actor).

 

Your helper loop can be a Dequeue Elements with the Timeout input set in a shift register. Have it default to -1, so you'll never time out, and will always wait on a Queue element (which can be a simple string or Enum, or even a Boolean). When a Start command comes in on the queue, set your Timeout shift register to 0 and run whatever DAQ config you need to. The next time the loop executes, there will be 0 data on the Dequeue, so it will return instantly, then you can execute a "Timed Out" case in the loop, which is your actual Data Acquisition function. Your helper loop will block here until it's acquired all of its data, then you can send that as a message to the main Actor Core. When it's done, it will then check the Dequeue function, and if there are no messages, it'll wait to acquire data again. If there is a "Stop" command on the Dequeue, you can stop polling your data and set the Shift Register back to -1. (This example used Queues, you could easily do this with User Events as well).

 

This will give you "implicit" timing based on the DAQmx Read functions and you don't need to worry about making sure your measurements are sent at the correct time.

 

If you're doing software defined measurements and just want a single point every X amount of time, you can do the same thing but add a Wait Until Next ms Multiple to your queue timeout case. That will sync up your timing. (Note: you could just wire the Queue timeout to your sample period, but that counter resets every time you get a new message. Wait until Multiple will let you handle small other tasks if you need to implement them in the future.

 

All that said, I went down this road a couple of months ago, and it dawned on me... why do I need to stop and start the acquisition WITHIN the actor? I realized I didn't, and it was causing me LOTS more headache. Why not add your DAQ initialization stuff to Pre-Launch Init, and just run the acquisition constantly? When you need to stop logging, stop the actor. When you need to start again, relaunch the actor. This simplified my code tremendously.

 

(By the way, this is a good place to introduce Zero coupling to your code- implement a "Send New Data" as an abstract message that is sent when your code acquires its chunk of data, and you can have any number of different Actors receive that data- your Acquire Data function doesn't need to know how your Caller actor works, just what message to send.)

Message 13 of 23
(4,106 Views)

Dear All, I finally decided to use QMH in parallel.

 

<<Parent.vi>>

Parent.vi just calls "Send" VIs. It doesn't care state of each module.

Parent.PNG

 

<<Acquire Actor>>

This actor has QMH loop. This actor cares own state and does each operation.

Acquiring Actor.PNG

<<Start Measurement.vi>>

"Send Start Measurement.vi" calls this VI. This VI just sends message to QMH located at Acquire Actor.

Start Acquire.PNG

 

One disatisfaction is "duplicated class data". Both "Parent.vi" and "Acquire Actor" have Acquisition class.

Anyway, thanks to your kind advice, I could develop my startup AF Application. Thank you very much : D

Certified LabVIEW Developer
There are only two ways to tell somebody thanks: Kudos and Marked Solutions

GCentral
0 Kudos
Message 14 of 23
(4,034 Views)

This seems like an insanely over complicated and convoluted solution to a pretty simple problem. 

 

What was wrong with the time delayed send message and holding state information in the actor's class data? 

 

 

Message 15 of 23
(3,991 Views)

Time delayed send message can only be used for slow, non-deterministic acquisitions. If you need high data rates, you need to let the DAQ take care of its own timing.

 

What the OP is doing is holding state information inside the actor- the complexity you see is a direct result of handing state transmissions, idle states, error states, initialization states, and so on inside the actor. I can't speak for him, but removing states from my DAQ actors saved a massive, massive amount of headache and overhead- if I need to stop acquiring, I stop the actor. Initialization happens inside Pre-launch Init, so if initialization of the DAQ device fails, the caller knows instantly and can make a decision from there. If the actor is already running and the initialization fails, you need to make a new message for the caller notifying it of what happened. Then you have your actor in an Error state, which needs more messages to handle "Acknowledge error and restart" or "Ignore error, shut down" or whatever else you need to handle.

 

Removing states means you can handle initialization errors at initialization time directly within your caller (since they happen in Pre-Launch Init). Errors during measurements can either be handled internally to the measurement actor and fixed, or if they're critical, they can just stop the actor and notify the caller that it failed and is now shutting down. Errors during Stop Acquiring are usually ignorable- I don't think I've ever cared about an error when Stopping a DAQ task. I'm not saying they're always ignorable, just that you can throw away 99.9% of them since you're shutting down the acquisition anyway. Non-ignorable errors can, of course, send a message.

 

In short: holding state information in the actor's private data is basically what the OP did, but it's in an internal QMH instead of the main actor's private data. The complexity comes from handling all of the state transitions. Generally, I don't want my caller to be the one deciding how to handle errors that happen *inside* my acquisition loop anyway- the caller should just know "Is acquisition doing its thing OK or not?"

0 Kudos
Message 16 of 23
(3,985 Views)

@BertMcMahan wrote:

Time delayed send message can only be used for slow, non-deterministic acquisitions. If you need high data rates, you need to let the DAQ take care of its own timing.

 

What the OP is doing is holding state information inside the actor- the complexity you see is a direct result of handing state transmissions, idle states, error states, initialization states, and so on inside the actor. I can't speak for him, but removing states from my DAQ actors saved a massive, massive amount of headache and overhead- if I need to stop acquiring, I stop the actor. Initialization happens inside Pre-launch Init, so if initialization of the DAQ device fails, the caller knows instantly and can make a decision from there. If the actor is already running and the initialization fails, you need to make a new message for the caller notifying it of what happened. Then you have your actor in an Error state, which needs more messages to handle "Acknowledge error and restart" or "Ignore error, shut down" or whatever else you need to handle.

 

Removing states means you can handle initialization errors at initialization time directly within your caller (since they happen in Pre-Launch Init). Errors during measurements happen during measurement, but they can either be handled internally to the measurement actor and fixed, or if they're critical, they can just stop the actor and notify the caller that it failed and is now shutting down. Errors during Stop Acquiring are usually ignorable- I don't think I've ever cared about an error when Stopping a DAQ task. I'm not saying they're always ignorable, just that you can throw away 99.9% of them since you're shutting down the acquisition anyway.

 

In short: holding state information in the actor's private data is basically what the OP did, but it's in an internal QMH instead of the main actor's private data. The complexity comes from handling all of the state transitions.


I don't see anything indicating that high data rates are required - given that in many of the images the waits are above 100ms, there is no problem using the time delayed send message. Your method of just starting and stopping the acquisition actor is another perfectly good solution to the problem. 

 

The implementation pictured above is not a good solution to the problem. Its an overly complicated and convoluted way of doing things. There is no reason not to just put all that functionality into methods that are called by messages. 

 

 

0 Kudos
Message 17 of 23
(3,975 Views)

@BertMcMahan

 

In short: holding state information in the actor's private data is basically what the OP did, but it's in an internal QMH instead of the main actor's private data. The complexity comes from handling all of the state transitions. Generally, I don't want my caller to be the one deciding how to handle errors that happen *inside* my acquisition loop anyway- the caller should just know "Is acquisition doing its thing OK or not?"


The complexity comes from adding one form of message handling loop to another form of message handling loop, and having the first message handling loop reformat and forward messages to the second message handling loop, when the first message handling loop is perfectly capable of handling them. 

 

Hey, why not add another loop? Maybe use an event structure, and have the QMH loop generate events and that is where the acquisition occurs? Its added complexity that is totally unnecessary.

 

There are times when a parallel message handling loop is needed by an actor. This isnt one of them. 

0 Kudos
Message 18 of 23
(3,972 Views)

@paul.r

 

There are times when a parallel message handling loop is needed by an actor. This isnt one of them. 


Agreed 100%. I'll retract my previous statement about where the complexity comes from; if you have an acquisition helper loop with its own timing source (i.e., the DAQ's onboard timing), then you can wind up having lots of required complexity from handling messages between that loop and the actor core. I should say, PART of his complexity is coming from maintaining state across multiple loops.

0 Kudos
Message 19 of 23
(3,967 Views)

Dear Emboar, I am currently trying to develop something similar to your application, but struggling with Actor Framework. Would you do me a favor by posting your code for download here?

 

Thanks

0 Kudos
Message 20 of 23
(3,391 Views)