LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Controlling when a Global Variable is Read

Solved!
Go to solution

Forgive me in advance if this is confusing, but the TL;DR is this: I want to be able to control the flow of when I read a global variable without needing to resort to sequence (or other) structures.  Is this possible?

 

The project I am working on is an automated tester for an external device which we communicate with via serial port.  In our original implementation of the project, all data for the device is stored in a single cluster, which itself is stored as a hidden indicator on the main VI and whose reference is passed around to every single subVI so that the comms subVIs can update the data and everyone else can see the latest data in real time.  I recently discovered global variables and think that they might be a better fit.

 

My goal is to have each item that is stored for the external device as a separate control in a single global VI.  I am aware that accessing globals copies the data, so I would break the cluster into all of its individual components to make sure everything is as speedy as possible.  Here is my dilemma: because there is one thread working on comms all the time, we have some queues which send commands to the comm, which sends commands to the device, which responds soon afterwards.  At present, I can control when the software looks at the telemetry data by using property nodes for the control reference with the error in coming from the error out of the command node.  I have attached a simple example of a subVI I made to get the device into its Maintenance state; you can see that I pass a command to the command queue, perform a 2-second synchronous delay, and then read the telemetry from the telemetry reference.

 

Enter Exit Maintenance Mode.PNG

 

Let me take an extra second to comment on the "Delay" block, because it's a perfect example of what I am trying to achieve.  Before, if I wanted to delay for 2 seconds after something occurred, I had to use a sequence structure, like so:

 

Enter Exit Maintenance Mode - Sequence.PNG

 

Stuff like this used to be ALL OVER the code base.  I know sequence structures aren't generally best practice, but I wouldn't have needed it if only the Wait (ms) block had the error in/out nodes most other VIs do.  So, I made my own:

 

Synchronous Delay.PNG

 

Now I can control exactly when in the subdiagram the delay occurs and ensure nothing downstream runs for the amount of time needed.  Best of all, the other threads, such as communication, keep running perfectly in the background during the delay.  So, I am looking for something similar with globals.  For controls/indicators on the current front panel, I can just grab a property node and wire the error in/out lines to control when the read takes place.  Without it, I am looking back at using sequence diagrams:

Enter Exit Maintenance Mode - Global.PNG

 

The only solutions I can think of are less functional than the current model of passing the telemetry reference around.

1. I am not looking to use functional globals.  That adds unnecessary overhead for what I feel like should have a simple solution.  If it is the ONLY solution, I would appreciate a diagram showing how it would look; I feel like I would need an enum with all of the telemetry values pre-defined as a programming safeguard against using strings or something.

2. I know there are ways to get references to globals so that I could use its property nodes, but that has even more overhead.

 

 

0 Kudos
Message 1 of 13
(3,035 Views)
Solution
Accepted by topic author maluigario

Hi maluigario,

 


@maluigario wrote:

1. I am not looking to use functional globals.  That adds unnecessary overhead for what I feel like should have a simple solution.  If it is the ONLY solution, I would appreciate a diagram showing how it would look; I feel like I would need an enum with all of the telemetry values pre-defined as a programming safeguard against using strings or something.


How much overhead does a FGV add? Certainly less then having to wire references to frontpanel elements! You get an error in/out for free to allow for easy dataflow programming…

 

I like FGVs for simple jobs like bundling (and storing) data for a certain task (like parameters of a single device). I don't say they will be the only solution, but a "organized" solution.

 


@maluigario wrote:

My goal is to have each item that is stored for the external device as a separate control in a single global VI. 


The goal should be to place all related parameters into a single cluster, so you only need one shift register/feedback node in your FGV.

You may see FGVs as a step towards OOP: LabVIEW keeps the class/object data also in a cluster…

Best regards,
GerdW


using LV2016/2019/2021 on Win10/11+cRIO, TestStand2016/2019
0 Kudos
Message 2 of 13
(3,012 Views)

@maluigario wrote:

 

I know sequence structures aren't generally best practice, but I wouldn't have needed it if only the Wait (ms) block had the error in/out nodes most other VIs do.  So, I made my own:

 

Synchronous Delay.PNG

 


Hm.
You traded the sequence structure (not the worst option) for FOUR property nodes (terrible option).
If this is subVI, this code enough

 

timeout.png

 

I think a good idea would be to skip the pause if there is a mistake.

0 Kudos
Message 3 of 13
(3,002 Views)

In LabVIEW 2017 the (then new) malleable VI, "Stall Data Flow.vim", does exactly that - it contains a Flat Sequence Structure and a Wait (ms) node, and then some input/output of the same type (malleable so can be any wire type) so that you can put it on any wire you want for a delay.

 

It didn't get the no-wait-on-error though (that's a somewhat popular idea, but not implemented in LabVIEW by default). See here for the VIM specific case: https://forums.ni.com/t5/LabVIEW-Idea-Exchange/Stall-Data-Flow-should-be-a-no-op-in-the-event-of-an-...

 

For global variables, I'd be tempted to use the Flat Sequence Structure directly. Although I'd also be tempted to see if I really needed the global variable.


GCentral
0 Kudos
Message 4 of 13
(2,976 Views)

In terms of time or space of execution for the step, I would agree that the extra property nodes add a lot, and I can also agree that the delay should be skipped if an error occurs.  However, as far as I am aware, the only way to ensure that an unwired terminal is always called with its default value is to reset it to its default value at the end of the subVI's execution.  If there is some other, simpler way, I would love to know about it because I have added those "Reinit to Default" Invoke nodes in a lot of subVIs where I want an unwired to be set to the default.  Like, if I call the same Delay subVI twice in a row, first wiring in "2000" and then wiring in nothing, the control will retain the "2000" from the previous execution and make it run 2 seconds both times instead of 2 seconds the first time and 1 second the second time.

0 Kudos
Message 5 of 13
(2,968 Views)

@maluigario wrote:

In terms of time or space of execution for the step, I would agree that the extra property nodes add a lot, and I can also agree that the delay should be skipped if an error occurs.  However, as far as I am aware, the only way to ensure that an unwired terminal is always called with its default value is to reset it to its default value at the end of the subVI's execution.  If there is some other, simpler way, I would love to know about it because I have added those "Reinit to Default" Invoke nodes in a lot of subVIs where I want an unwired to be set to the default.  Like, if I call the same Delay subVI twice in a row, first wiring in "2000" and then wiring in nothing, the control will retain the "2000" from the previous execution and make it run 2 seconds both times instead of 2 seconds the first time and 1 second the second time.


I didn't see that (edit: I tried with "wait.vi" as non-reentrant and preallocated, and didn't see it in either case, just in case you wondered if it was due to reentrancy).

Example_VI.pngNumeric set to 1000 as defaultNumeric set to 1000 as default


GCentral
0 Kudos
Message 6 of 13
(2,964 Views)

@cbutcher wrote:

In LabVIEW 2017 the (then new) malleable VI, "Stall Data Flow.vim", does exactly that - it contains a Flat Sequence Structure and a Wait (ms) node, and then some input/output of the same type (malleable so can be any wire type) so that you can put it on any wire you want for a delay.


I did not know about that VI; that would have been helpful.  And I'm not opposed to using the sequence structure in an isolated incident like that; it's the idea of having a sequence structure put down every time I need to access a global variable that I'm trying to avoid.  I've done some further looking into functional globals, and they're not nearly as far from the case I was going for as I at first thought.  It's also relatively easy to replace all of the property nodes that once took references to the AllTelem cluster with the FGV subVI.  I just had really been hoping there was a way to control regular globals the same way.  It seems so strange to me that a language that places such heavy emphasis on proper order of operations wouldn't have an easy way, like a Global Property Node or something, to use the error in/out flow to get the global variable (or even a local variable) at a specific point in the execution without resorting to sequence diagrams.

0 Kudos
Message 7 of 13
(2,955 Views)

How many global variables do you have?

 

One option (if you have a relatively small number of variables, but a sizeable number of usages) is to create a VI for each of the global variables and add whatever other wire you might like (e.g. error in/out, or use a malleable VI in the style of Stall Data Flow if you have 2017+).

 

This also allows you to later make changes to every read in one place (or every write), like ensuring a written value is within a range, or changing scaling of the output, etc.


GCentral
0 Kudos
Message 8 of 13
(2,950 Views)

@cbutcher wrote:


I didn't see that (edit: I tried with "wait.vi" as non-reentrant and preallocated, and didn't see it in either case, just in case you wondered if it was due to reentrancy).


Well, I'm a bit flabbergasted.  I have no idea what I did on the VI that was retaining the previous values, but I just copied what you did to test out my Delay subVI without all of the "Reinit to Default" stuff and it worked.  Good to know I can remove all of that stuff from my other subVIs.  I'll double-check to make sure they're all set to non-reentrant or preallocated as well.  Thank you for alerting me to this.

0 Kudos
Message 9 of 13
(2,945 Views)

Sorry - to be clear I expect it will work with shared clone too. I just only tested it with the other settings.

 

I checked it only because you said you'd experienced otherwise at some point, and although I felt that was probably a huge problem if true (in the way you described) it didn't seem inconceivable that you might have observed that different behaviour in some specific similar but different case that I couldn't think of. So I tested reentrancy because that seemed a good place to start (although I'd have been surprised if it made a difference - like I mentioned above if an unwired input doesn't take the default value it's a big problem).

 

Maybe it happens differently if you have a reference to a VI and call it in different ways? 😕 Not sure... but I'd generally expect the behaviour I wrote and you're now seeing.


GCentral
0 Kudos
Message 10 of 13
(2,940 Views)