LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Asynchronously retrieving data from parallel loop

Solved!
Go to solution

I have 2 loops, an event driven loop that responds to UI elements ("loop 1"), and a data acquisition loop that is continuously receiving data from a serial source and plotting it ("loop 2"). When a user presses a button I'd like to have the most recent data point from loop 2 available in loop 1 so that it can be written to disk, presented to the user, or something else.

I can see several ways of achieving this in LabView but I'm unsure of what is considered best practice.

 

  • Loop 2 stores the data in a global variable so that it can be accessed from loop 1. Seems like a code smell
  • Loop 2 is also continuously appending the data to a queue that loop 1 reads from when the button is clicked. This is the attached 1_loop.vi. My concern with this is that if a long time passes before the user clicks the button, the queue is just ever incrementing in size and could cause a memory leak
  • There are 2 queues connecting the loops. When a user clicks the button, loop 1 sends a signal to loop 2 to indicate it needs a measurement, and only when this signal is received in loop 2 does it add the current measurement to the second queue, which loop 1 is then waiting on. This is my 2_loops.vi example. This resolves my memory issue and works well in this trivial example, but I worry in my full program it adds additional complexity and would be hard to keep track of. Also I'm aware the first queue could be a notifier.
  • Both loops have access to the same data source and loop 1 can just read from it at will. I haven't tested this yet but it seems possible to have 2 simultaneous serial (VISA) connections to the same source as long as the reading is done in a sub-VI that has "non-reentrant execution". However, it also seems like a code smell, as ideally there should be a single point of responsibility for data acquisition.

Any advice would be very much appreciated!

Download All
0 Kudos
Message 1 of 10
(2,407 Views)
Solution
Accepted by stulacy

Hi stulacy,

 


@stulacy wrote:

I have 2 loops, an event driven loop that responds to UI elements ("loop 1"), and a data acquisition loop that is continuously receiving data from a serial source and plotting it ("loop 2"). When a user presses a button I'd like to have the most recent data point from loop 2 available in loop 1 so that it can be written to disk, presented to the user, or something else.


Sounds like you want to use a notifier: they can store the most recent data/samples!

So your DAQ loop writes its data to a notifier and the UI loop can read from notifier when needed…

 

As this simple solution requires you to constantly write data to the notifier (and so may create some CPU overhead for large data blocks) I can also image message handlers: the UI loop sends a command to the DAQ loop to request for new data and the DAQ loop answers with its data message… (Basically your 3rd option.)

Best regards,
GerdW


using LV2016/2019/2021 on Win10/11+cRIO, TestStand2016/2019
Message 2 of 10
(2,391 Views)

Hi Stulacy,

 

If you are only interested in the last available value, a notifier would be perfect for this job. Write the value continuously in loop 2 and read when needed in loop 1.

 

If you want the N last values, create a queue with a max queue size set to N, then use the "Lossy Enqueue Element" function to write elements so that your queue always keeps the N last values.

 

Regards,

Raphaël.

Message 3 of 10
(2,386 Views)

Fantastic, two very fast responses both mentioning notifiers. I hadn't realised you could keep overwriting notifiers, that sounds ideal. I'll keep an eye on CPU usage and try the message handling approach if needs be.

 

Thank you both very much!

0 Kudos
Message 4 of 10
(2,380 Views)

Regarding your general question about "best practices", here are some brief comments:

 

Messaging among parallel loops seems to have become the preferred approach, IMO largely due to the signaling mechanism built into queues, notifiers, and channels.  This in turn leads to a tendency to have data sources that push or "publish" new data when there's something new to report and client code that "subscribes" and waits to receive updates.   Overall, a move toward event-driven programming and programming structures designed around waiting and reacting.

 

This takes the place of a non-signaling approach using shared access to a common variable (sometimes packaged in a "functional global").  In that approach, the client would poll the variable to "pull" a value out and would be responsible for noticing if it changed.  

 

The limited-size lossy queue mentioned by raphschru can sometimes be a great option when a limited-length history of values is more useful than just 1 single most recent value.   A Notifier is pretty much just such a lossy queue with a size of 1.   The difference is that if 2 clients wait on the same notification, they can both receive the same new value.  Whereas if 2 clients wait on the same lossy queue, only one of them gets each new value (and not necessarily the same one every time).  

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy coming to an end (finally!). Permanent license pricing remains WIP. Tread carefully.
Message 5 of 10
(2,320 Views)

@raphschru  a écrit :

If you want the N last values, create a queue with a max queue size set to N, then use the "Lossy Enqueue Element" function to write elements so that your queue always keeps the N last values.


Raph, I've tried doing this in the past, but the performances were not great (for a big queue). If I remember well, it's equivalent to have an array in which you constantly resize and change the place of elements in it (a sort of circular buffer, but by changing all elements all the time instead of just one)

Maybe an Action Engine / Functional Global (Never know which one is which, if they're not more or less the same) with an actual circular buffer would be preferable if there is a lot of data 🙂 

 

[EDIT] For some reason I haven't seen the response from Kevin_Price before writing mine.

0 Kudos
Message 6 of 10
(808 Views)

@Kevin_Price wrote:

A Notifier is pretty much just such a lossy queue with a size of 1. The difference is that if 2 clients wait on the same notification, they can both receive the same new value.  Whereas if 2 clients wait on the same lossy queue, only one of them gets each new value (and not necessarily the same one every time). 


You can also use "Get Queue Status" to get elements without dequeuing them, which allows to read from several places just as for notifiers.

 

 

@VinnyAstro wrote:

I've tried doing this in the past, but the performances were not great (for a big queue). If I remember well, it's equivalent to have an array in which you constantly resize and change the place of elements in it (a sort of circular buffer, but by changing all elements all the time instead of just one)

Maybe an Action Engine / Functional Global (Never know which one is which, if they're not more or less the same) with an actual circular buffer would be preferable if there is a lot of data


I've already had an application with hundreds of sensors, each having a lossy queue (size 100-200) to which I continuously published their values and did not notice any particular performance problem. I couldn't find an official documentation that explains how queues are implemented internally though (if someone has it, I'm interested), but I guess it is not just a plain array, but rather an array with a circular index or even a linked list, which avoids to shift all elements at each enqueue/dequeue.

Also, the problem with AE/FGV is that they are not instanciable, which makes code that is not easily cloneable.

Message 7 of 10
(782 Views)

@raphschru wrote:

@Kevin_Price wrote:

A Notifier is pretty much just such a lossy queue with a size of 1. The difference is that if 2 clients wait on the same notification, they can both receive the same new value.  Whereas if 2 clients wait on the same lossy queue, only one of them gets each new value (and not necessarily the same one every time). 


You can also use "Get Queue Status" to get elements without dequeuing them, which allows to read from several places just as for notifiers.

True enough, BUT...

 

"Get Queue Status" brings you back into using a client-side polling mechanism rather than taking advantage of the wait-to-be-signalled mechanism available in queues & notifiers.  

 

Not trying to argue that using it is *wrong*, just trying to help clarify the tradeoffs for the OP.  So I'll note that Notifiers can also support polling by setting the 'ignore previous' boolean input to False.

 

In short, both Queues and Notifiers support both signaling-based and polling-based client-side code, where client-side means the code that reads/consumes the data.   Variables support polling only, no signaling mechanism available.  User-defined events support signaling only, no polling mechanism available.

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy coming to an end (finally!). Permanent license pricing remains WIP. Tread carefully.
0 Kudos
Message 8 of 10
(759 Views)

@Kevin_Price wrote:

 

In short, both Queues and Notifiers support both signaling-based and polling-based client-side code, where client-side means the code that reads/consumes the data.   Variables support polling only, no signaling mechanism available.  User-defined events support signaling only, no polling mechanism available.

 

 

-Kevin P


I'd add, for completeness sake that the actual call-site of the "Wait on Notifier" primitive is what controls this difference in behaviour between Queues and Notifiers. If you want to have reliable client behaviour with Notifiers, you MUST make sure you're re-using the SAME actual call-site for the read operation. Confusion occurs when you pack the notifier handling code into re-entrant VIs.

 

The "Wait on Notifier" primitive maintains a Timestamp of the last valid message it returned. If you start mixing up different VIs with the same Notifier reference, weird things happen as the different Timestamps from the different Call-sites (think of each specific clone of a VI having its own distinct Call-site and Timestamp) get mixed up.

 

Just a clarification for some 0.1% corner-cases....

 

Shane

Message 9 of 10
(687 Views)

Good tip from Intaris

 

Here's a brief illustrative example showing the recommended solution to the problem he brings up -- using the function from "Wait on Notification with Notifier History" from the advanced palette for notifier waiting.

 

And here's the initial discussion which (as far as I know) planted the seed for the "history" fix.  The second post from "Aristos Queue" is a particular gem for understanding some Notifier nuances.

 

 

-Kevin P

ALERT! LabVIEW's subscription-only policy coming to an end (finally!). Permanent license pricing remains WIP. Tread carefully.
0 Kudos
Message 10 of 10
(676 Views)