DQMH Consortium Toolkits Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Updating module state from helper loop

I've come across a design issue on 2 different DQMH projects now involving updating module state from a repetitive helper loop. In other words I have a helper loop running repeatedly receiving some broadcast data from a lower level module, and I want this data to be available to the rest of the module, both in the MHL and other helper loops.

 

Private requests seem the idiomatic way of doing this but in my head this creates a whole bunch of new problems. If my helper loop is receiving broadcasts at say 10Hz (and this is all the loop does), if it wants to pass that data into the MHL it can send a private request to the EHL containing it, but this means that the EHL is now constantly receiving these broadcasts which is exactly the reason this functionality was split out into a helper loop in the first place. Ditto if I want the data to be available to other helper loops.

Another issue with private requests is that it makes timings awkward in helper loops. Say I've got a second helper loop running at a set frequency that needs to use the values obtained from the first loop, if it's being bombarded with private requests with the new measurements it won't timeout and do its regular task. This can be avoided by dynamically setting the timeout on each iteration which is something I've done before, but it feels a bit hacky to me as I don't like the way it forces the timeout.

 

I could use local variables, since typically the received data will be published on the front panel, but I've never used local variables before as I've been generally warned to steer clear. Since I'd be only writing to them in one place (this helper loop) but reading in multiple places (MHL, other helper loops) I think it should be fine, and the additional overhead of additional copies of the data is a small price to pay for convenience.

 

The other issue with local variables is that I frequently encapsulate functionality in my MHL into subVIs, which also need to access the live data. I guess here I could pass a reference to the front panel element so that subVIs can access the most recent data point?

 

If I were doing it synchronously I would have a single loop running at my desired frequency that would request the current measurement from the lower level module using a request & reply, but this seems to go against the DQMH paradigm.

 

For a concrete example if it helps, I've got a process that needs to interpolate setpoints at a set frequency, then using the calculated setpoint and the most recent measurement (broadcast from a lower level module), calculate PID parameters. I've designed this as one helper loop that simply receives the broadcast measurements as they become available, and another helper loop that runs at the desired frequency to interpolate the setpoints and calculate the PID values, so I need to make the measurements from the first loop available in the second.

 

0 Kudos
Message 1 of 8
(437 Views)

The first thing that comes to mind is to have both helper loops use the message queue to communicate with the MHL.

 

1. the first HL receiving the broadcasts shares the data with MHL, which puts it into the module data shift register

2. the second HL is a simple message pump, sending a message at the required frequency to the MHL telling it to do the calculation based on the data on the shift register

 

Would this work for you?




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (Developer Experience that makes you smile )


Message 2 of 8
(431 Views)

Hi Joerg, thank you for the speedy response.

 

I could see how that would work, but then my MHL is going to be busy receiving both the updated data values and the command to run the setpoint calculation, as well as responding to any external requests. This makes it function as a god loop and it might actually not be running the setpoint calculations at the desired frequency, which is the problem I thought helper loops were solving?

 

Are you suggesting directly using the message queue rather than going through private requests because there's less overhead?

 

This solution would work for the concrete example I posted, but in another project I need access to the continually updating values inside a subVI that's being run within my MHL, in which case I guess my 2 choices are refactor this out into the Main.VI so I can use this messages pattern, or pass a reference in?

0 Kudos
Message 3 of 8
(418 Views)

Yes, I strongly suggest not creating an event when all you want is to communicate with your MHL from within your module. Using the queue is the preferred default solution for this scenario.

 

The MHL is supposed to be the workhorse of the module. It is the more readable architecture as long as timing and performance don't dictate otherwise.

 

If you prefer to keep the calculation out of MHL, then I'd suggest to keep the received broadcast data on one of the HL's shift registers and creating a private request to talk from one HL directly to the other:

 

- HL1 receives broadcast data and puts it on a shift register

- HL2 has the timing function and acts as a message pump, sending a private request to HL1 periodically

- HL1 will do the calculation whenever triggered by HL2 with the data on its shift register




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (Developer Experience that makes you smile )


Message 4 of 8
(405 Views)

Thanks Joerg, that's really helpful.

 

One final question, you haven't mentioned local variables as a possible solution. Is that for performance reasons?

0 Kudos
Message 5 of 8
(386 Views)

 

@stulacy wrote:

 

I could use local variables, since typically the received data will be published on the front panel, but I've never used local variables before as I've been generally warned to steer clear. Since I'd be only writing to them in one place (this helper loop) but reading in multiple places (MHL, other helper loops) I think it should be fine, and the additional overhead of additional copies of the data is a small price to pay for convenience.

 


Joerg has already given you good suggestions but I wanted to return to this section on local variables.

Yes, we are told to stay clear but the order of the decision on when to use a local variable goes as follows:

 

1) Can I get a wire to it? Then do it, this is the preferred method

2) If I cannot get a wire to it, I can then use a local variable

3a) If I cannot use a local variable, then I could use a reference (This could be a reference to the control on the front panel or a DVR (DVRs now have a read parallel option that doesn't block access to your data) 
3b) There is always the option of using a global variable, they also get a bad reputation but if you are writing to them in one place only, then there is nothing wrong with using them. You could make the Global Variable private, its writer private, and then the reader would be public. If you want to make it even clearer that you are writing only in a single place

3c) If you need to write in multiple places, Functional Global Variables could be used but most of the time the DVR or the Global is enough. The only advantage to the FGV would be that it keeps its last value in the shift register and that can come in handy but they are really only useful if you make them an "Action Engine", meaning that they are not just READ or WRITE but they actually encapsulate an action inside them. So the VI acts as a blocking mechanism, you read from the shift register, do the calculation, put it back in the shift register, and output a solution. This blocks anyone else doing the same operation in parallel and causes a race condition to who gets there earlier.

 

A more thorough explanation can be found in this post:

https://stravaro.com/lvjournal/2011/08/race-conditions-and-functional-global-variables-in-labview/

 

Please let us know what you end up doing. 

 

And remember KISS, keep it simple and .... 

Sometimes the easier solution beats in readability and maintainability an elegant solution

 

Happy wiring, 

Fab

For an opportunity to learn from experienced developers / entrepeneurs (Steve, Joerg, and Brian amongst them):
Check out DSH Pragmatic Software Development Workshop!

DQMH Lead Architect * DQMH Trusted Advisor * Certified LabVIEW Architect * Certified LabVIEW Embedded Developer * Certified Professional Instructor * LabVIEW Champion * Code Janitor

Have you been nice to future you?
Message 6 of 8
(374 Views)

Honestly, often a local variable is a very simple solution.  It's not messy (less wires running everywhere), it's simple, and quick.  You just need to be aware of how you're using local variables before you do (and it sounds like you've thought it through).  We regularly share data between loops with local variables.  You don't want to use it if there's a chance of a race condition for instance.

Another option I have experimented with, with some success is channel wires.  The good thing about these is that there are different types of channel wires.  They can be lossy or lossless, single or multiple data points, single or multiple writers, etc.  It takes a while to work out what type you want to use, but once you do they can be quite powerful.  The downside is that you've got events/queues/channel wires all going on in your VI, so it feels inelegant, but it works.

Christopher Farmer

Certified LabVIEW Architect and LabVIEW Champion
DQMH Trusted Advisor
https://wiredinsoftware.com.au

0 Kudos
Message 7 of 8
(355 Views)

Thanks Fab and Chris, I'll experiment with both the message passing and local variable solutions and see if there's any performance difference, if not I'll go with whatever I find looks most readable.

Message 8 of 8
(333 Views)