Jump to content

Strict TypeDef Referencing


Recommended Posts

I use strict typedef referencing to manage large data clusters. To make this data available globally I create a global reference which is initialized on startup with the reference of the FP cluster control (where the data is stored). My current problem is that when I change the specific typedef (what's in the cluster--say I add a new item or something), all of the SubVI's that use that specific typed input will update automatically (as they should). The reference to the typedef control containing the data however is now typed differently than the strict type of the old definition used for the global variable, which is used to access the data by reference anywhere in the program. That means of course, all kinds of wires break until I fix the global.

Here's a summary of what happens:

  • I add an item to the specific typedef
  • All subVI's update with the new typedef
  • The reference -> Global Variable wire breaks
  • Lots of other wires break wherever data is accessed by reference using the global.

To fix this currently I must:

  • Click the reference
  • Create a control so I now have a specific typed reference control
  • Rename it to be the same as that of my current global variable containing the old specific typed reference control
  • Double click to open it on the front panel
  • Ctrl+X to cut it
  • Back to the diagram... click the global to open the global definition
  • Ctrl+V to paste new typedef reference

This is, at a minimum, annoying--especially when I'm making numerous changes to the control :headbang:

Does anyone know of a way to create a global variable that is linked or automatically updated as specific typedefs are? I have tried to create a reference control that is typed to the cluster control, but I can't make it linked to that cluster control--so that the reference type automatically updates when the cluster type is updated.

Link to post

Well, I don't want to act like a smart-#ss... well ok, I'll be a smart-#ss.

Why are you using globals? Globals in general are a no no. :nono: I don't know if you heard this before, but if not, here it is. Never use globals. I know you should never use the word never, but I'm using it here: never use globals.

Ok, now something else. If you still want to use globals then here is a solution that will avoid having to update your control refnum control. Make a generic (non-strict) control refnum control. Use this in your global. Notice in the attached image that there is a coercion dot where the control reference meets the indicator? This is ok. It is ok to have a coercion dot here. This means that the strict reference is being upcast to a more generic cluster reference. This will avoid you having to re-create the control since it's immune to change. Thus no broken wires. On the receiving end you need to convert the variant to the proper data type. This is where you would use the actual strict-type control constant as the data type.

post-2-1095745449.png?width=400

Link to post

In regards to globals... consider why they are "a no no". It is because they are horribly innefficient correct? But this is an innefficiency from the computer's point of view. From the programmer's point of view (mine anyway), they are not horribly innefficient at all. Horrible innefficiency from a programming perspective is wiring the same thing through ten different VI's, having a mess of wires, not being able to understand the code, not being able to code new things easily, not being able to change old things easily, etc etc. Globals can serve a very good purpose in this regard--sure it's nice to know good programming practices and to follow them (which I do), but I am also of the "don't care" philosophy when it comes to balancing the efficiencies listed above. So it costs me an extra ten megs of memory... or maybe fifty... or maybe a certain routine even goes a little slow. New computers aren't getting slower though, or coming with less memory, so really I don't mind making my coding several of orders magnitude more efficient at the expense of a little bloat here and there, just so long as it's limited to areas where there are large efficiency benefits. Also if I am not mistaken, there are 'efficient' ways to use globals too, by using a 32-bit reference for example, instead of an array of several hundred cluster elements with who-knows-how-much stored in them.

About the example you posted--that will work, but essentially what you have to wind up changing then is the strict-type constant for every instance where you access the data of the cluster--this would be worse than what I have now since you would basically be re-coding everything any time there is a change, much as you would have to recode a non-strict typedef control if you were passing through several VI's by value. When passing by value, strict typedef controls let you not recode everything on the receiving end by opening the master typedef control and changing it once. There is apparently no way to link those changes to a reference control though that is strictly typed to the strict-typedef control.

Link to post
About the example you posted--that will work, but essentially what you have to wind up changing then is the strict-type constant for every instance where you access the data of the cluster

1845[/snapback]

Why do you have to change every instance? The constant is a strict-type. It will get updated when you change the master strict-type control. I'm not following your logic.

Link to post
Why do you have to change every instance? The constant is a strict-type. It will get updated when you change the master strict-type control. I'm not following your logic.

1851[/snapback]

Alright... you're right on... :yes:

I got your example to work after some fiddling. I had no idea that a constant created from a strict-typed control would update with that control--that's the missing link I was looking for. Which is what you were saying previously, just that it looked like the "strict-type" constant in your example was just strictly-typing the variant--I didn't realize that it itself was strictly typed (linked) to the control it was created from. Very helpful.

My previous understanding of the behavior of that constant would be if you were to create a constant from the control without the control being a strict typedef... hence my errant conclusion that it would just be a constant then, not linked to anything, and require re-creating a new constant every time the control is updated.

:oops:

Thanks for the explanation... :thumbup:

Link to post
My previous understanding of the behavior of that constant would be if you were to create a constant from the control without the control being a strict typedef... hence my errant conclusion that it would just be a constant then, not linked to anything, and require re-creating a new constant every time the control is updated. 

1852[/snapback]

On linked constants, I think there are certain instances where the constants created from a strict typdef are not linked (might be cut and paste, might be older versions of LV too). I always check by RMB on the constant to see that "Update" is selected.

Link to post
It is because they are horribly innefficient correct?

1845[/snapback]

Yes ... and no. They are less efficent, but for the efficiency reasons you list, they're efficient. :D

The real trap (IMHO) is that they disrupt dataflow and as your program expands globals can (and more often so with inexperienced LV programmers), lead to race conditions. Once you have a complex program, tracking race conditions becomes difficult, tiresome, wearying, frustrating, etc, etc because when the pressure is on, locating a race condition that is there in the built exe but never occurs under the development environment, could send you mental. This is exacerbated when more than one developer is involved in a project.

This really arises from the parallel nature of LV. It is impossible to predict which of two nodes will execute and in what order simply by looking at a LV diagram UNLESS there is data flow linking the nodes. A nasty (destructive) race condition can occur when a global is written to (by one part of the program) BETWEEN the read and write of a global pair. A less benign race condition might be that a READ occurs before the global contains the correct value as a result of a previous WRITE. IOW global READ WRITES are not atomic (you can make them appear so, but then the coding efficiency decreases dramatically).

Unlike some other 4GL tools, LV execution is NOT left to right OR up to down, but many people mistakingly believe this because they observe (in isolation) this ordering process. It is my understanding that even turning on the GLOBE does not show you the order of operations that true running VI will follow (dataflow aside).

Link to post
In regards to globals... consider why they are "a no no".

Just as an example:

In our production there is a piece of software that is used since quite a while. It was wired by my boss in a time I wasn't already working here, and he is a typical text-based programmer. This means, that every bit of data that might be relevant to review ends up in a global. I absolutely agree with him that you all time see what is happing inside the code, but from the moment that something doesn't work as it should, you're a poor guy (actually it's me, he gave me the software to support it since he hasn't any time anymore :( )! Because, when e.g. a boolean changes to true and shouldn't... in which of these 300 or so sub-vis might it happen???

At the moment, if time let it, I'm recoding the whole stuff, based on use-cases, state- & flow-diagrams I drew, based on many hours in production using this program.

I'm absolutely glad to see, that you use (at least) the global by reference and not just popping the needed global to the code. For myself I use GOOP (Graphical Object Oriented Programming) since several years with success. GOOP is downloadable from ni.com.

Didier

Link to post

Well... after implementing the strict-type constant approach I've found (unfortunately) the major downfall: Unbundle by name functions do not retain the same name if the strict typedef control is updated. They are based instead on an index that remains constant. Ie, after inserting an element into the front of the cluster, the advantage is no broken wires on storing the reference, but all of the numerous unbundle by name calls in various VI's are now accessing the wrong data! doh! If the names of the items they were unbundling linked back to items #2, #3 and #4 in the cluster, they will now be unbundling items #2, #3 and #4 with the new item inserted, which means they're all off by one.

Old Cluster

  • Boolean
  • String
  • String

Unbundle by Name:

  • Boolean
  • String

New Cluster

  • Double
  • Boolean
  • String
  • String

Unbundle by Name now is automatically updated and errantly says:

  • Double
  • Boolean

Link to post

I'm sure you know that you can right-click on a cluster and 'Reorder Controls in Cluster...'. Your efficiency now has the higher cost of making sure each new cluster element has the largest cluster index. This may beg for a LV2 global with a strict type def enum that chooses which global element gets accessed.

Link to post
I'm sure you know that you can right-click on a cluster and 'Reorder Controls in Cluster...'. Your efficiency now has the higher cost of making sure each new cluster element has the largest cluster index. This may beg for a LV2 global with a strict type def enum that chooses which global element gets accessed.

1886[/snapback]

Actually, clusters automatically have items added to the end numerically--you have to reorder the control to be an earlier index (which is what I did since I wanted the items grouped together).

Second... I have been reading info on LV2 globals and do not understand how there are less copies of memory made for an LV2 global in comparison to using a global or a global reference that is used to pull the value out through a property node. When an LV2 global (subVI) is called... isn't a copy of the global created at the output terminal of the subVI? Is this any different than a copy being created when you read a global variable, or a global-variable-referenced property node to some other control? Obviously there are other benefits, such as avoiding race conditions, but I'm still trying to figure out what the big advantage is from a memory management perspective.

If I had a large array on the main.vi and accessed the Value property through a global reference, it would create a copy of that array, correct? And if I simply had the array itself as a global and I read the value directly, LabVIEW would make a copy of it right? And if I had the array stored in an unitialized shift register that gets passed to the output array terminal of a subVI, there is then a copy that is generated for that too right? So is there any difference besides the overhead of loading a global in comparison to loading a subVI?

Finally, a note about my use of globals is that their use is typically limited by the execution flow control of my program. Since program execution is linear, there cannot be a problem with race conditions.

Link to post
Actually, clusters automatically have items added to the end numerically--you have to reorder the control to be an earlier index (which is what I did since I wanted the items grouped together).

Second... I have been reading info on LV2 globals and do not understand how there are less copies of memory made for an LV2 global in comparison to using a global or a global reference that is used to pull the value out through a property node.  When an LV2 global (subVI) is called... isn't a copy of the global created at the output terminal of the subVI?  Is this any different than a copy being created when you read a global variable, or a global-variable-referenced property node to some other control?  Obviously there are other benefits, such as avoiding race conditions, but I'm still trying to figure out what the big advantage is from a memory management perspective.

If I had a large array on the main.vi and accessed the Value property through a global reference, it would create a copy of that array, correct?  And if I simply had the array itself as a global and I read the value directly, LabVIEW would make a copy of it right?  And if I had the array stored in an unitialized shift register that gets passed to the output array terminal of a subVI, there is then a copy that is generated for that too right?  So is there any difference besides the overhead of loading a global in comparison to loading a subVI?

Finally, a note about my use of globals is that their use is typically limited by the execution flow control of my program.  Since program execution is linear, there cannot be a problem with race conditions.

1889[/snapback]

With a huge array held in a LV2 global, there is no advantage of ouputting the whole array to modify it because a copy is being made. Avoid to put the array on any indicator. Look at the attached picture. No copy of the whole array is made, only the elements needed to be modified are replaced.

The idea is to include in the LV2 Global a lot of functions to manipulate the array in place

without having to access/copy the whole array on calling diagrams.

post-447-1095896661.png?width=400

Link to post
Well... after implementing the strict-type constant approach I've found (unfortunately) the major downfall:  Unbundle by name functions do not retain the same name if the strict typedef control is updated.  They are based instead on an index that remains constant.

1880[/snapback]

Actually, the problem occurs with using the Variant form of the data (non-strict). If you are using the strict control refernece then this problem does not occur. This is because the refnum contains the type information on the wire and does not rely on the variant to data function. So in other words, your original implementation would not have this problem.

Link to post
Second... I have been reading info on LV2 globals and do not understand how there are less copies of memory made for an LV2 global in comparison to using a global or a global reference that is used to pull the value out through a property node.  When an LV2 global (subVI) is called... isn't a copy of the global created at the output terminal of the subVI?  Is this any different than a copy being created when you read a global variable, or a global-variable-referenced property node to some other control?  Obviously there are other benefits, such as avoiding race conditions, but I'm still trying to figure out what the big advantage is from a memory management perspective.

I recommend you read this post (if you haven't already):

http://forums.lavausergroup.org/index.php?...=findpost&p=675

So the issue really is that with globals is this. LabVIEW really has no way of protecting access to the global data. To work around this, LabVIEW allocates a seperate parallel memory buffer for each and every instance of the global. In other words, it does NOT reuse memory buffers allocated to globals like it does with other nodes. So if you plop 5MB of graph data into a global that is located in 10 different diagram locations, you will have allocated 50MB of memory space.

If I had a large array on the main.vi and accessed the Value property through a global reference, it would create a copy of that array, correct?  And if I simply had the array itself as a global and I read the value directly, LabVIEW would make a copy of it right?  And if I had the array stored in an unitialized shift register that gets passed to the output array terminal of a subVI, there is then a copy that is generated for that too right?  So is there any difference besides the overhead of loading a global in comparison to loading a subVI?

Well, in general the fastest data access time is always a plain ol' wire. The value property ranks as the slowest (over 10x slower) method of data manipulation. Even slower than using a local. In my first post I said never use globals. My second comment would be: almost never use the value property. I choose locals over value every time. Finally, you may ask, well then what the hell do you use? LV2-style globals rank #1 for me with GOOP as a close second. GOOP is the ultimate in accessing data by reference.
Finally, a note about my use of globals is that their use is typically limited by the execution flow control of my program.  Since program execution is linear, there cannot be a problem with race conditions.

I would re-think that. Program execution is never linear in LabVIEW unless you're forcing execution with sequence structures or wires.

With a huge array held in a LV2 global, there is no advantage of ouputting the whole array to modify it because a copy is being made. Avoid to put the array on any indicator.  Look at the attached picture. No copy of the whole array is made, only the elements needed to be modified are replaced. 

The idea is to include in the LV2 Global a lot of functions to manipulate the array in place

without having to access/copy the whole array on calling diagrams.

1895[/snapback]

Yes jpdrolet I agree. A modification that I would recommend to your image is a s follows based on your last statement:

post-2-1095906042.gif?width=400

This entire code with the while loop would make up the the LV2 global VI.

Link to post
Yes jpdrolet I agree. A modification that I would recommend to your image is a s follows based on your last statement:

post-2-1095906042.gif?width=400

This entire code with the while loop would make up the the LV2 global VI.

1901[/snapback]

What I wanted to emphasize with the previous diagram is that the whole array is not accessed by the calling diagram. It is encapsulated in the LV2 Global. A better illustration is like:

post-447-1095907091.png?width=400

where only a little part of the array is read/modified/written.

Of course you are right Michael, if the operation "increment subset" is used frequently, then it makes sense and it is more efficient to include it as a function of the LV2 Global (attached). This implementation can still cause race conditions. To avoid that, either put all possible operations on the array in cases of the LV2 Global or provide a lock mechanism to prevent simultaneous read/modify/write (like in GOOP).

Download File:post-447-1095907186.vi

Link to post
  • 2 weeks later...

M3nt,

There are ways around 'creating large' clusters in your code. I started using what I call "monocluster state machines" a few years back and discovered a few problems. One of which is that the memory footprint is huge - even though it is a strict type def - and should really be only a few in memory, the llb size was quickly getting over 10megs for a meduim program.

The other problem is the code becomes unwieldy. You forget what functions should read and write vars in the cluster. I've had other programmers in the team - change values in the cluster (thinking they were unused??)- wreaking havoc in the program. Reading about Object Oriented programming - especially the concept of "Public" and "Private" data helped me develop a new kind of architecture that is much cleaner, more resuable and is pretty efficient.

In general the core data that is relevent to a function - should be contained in that function - and no where else. For instance the GPIB address of the device does not need to be a global, its only important to the GPIB Read and Write core functions know the address.

If you use queues or other messaging methods - you can easily modify and change the settings of a function. You can add features to a function - without (seriously) impacting the rest of the code.

That said I do use alot of strict type defs in my code. There is no right answer but too use of one type of method should cause one to question if a better method is out there.

J.Hamilton (yes, I am still alive...)

Link to post
  • 3 months later...
In general the core data that is relevent to a function - should be contained in that function - and no where else. For instance the GPIB address of the device does not need to be a global, its only important to the GPIB Read and Write core functions know the address.

2095[/snapback]

Yes. This is one of the "achievements" of an Object Oriented Programming (OOP) approach (regardless of language). It's all about the "data hiding". By specifying the object's interface you can control how the object is utilised.

Interestingly enough a functional global (LV2 style global) by implication becomes an "object", albeit a singleton (single instance). At its simplest it will be just a container, but at it most complex can be a functional component. There was a LabVIEW book written using this componentisation. Personally I prefer OOP (ie GOOP) because when I start a project I never know when the client goes from wanting "one is all we'll ever need" to "can we have four of those". Usually its at commission time :D

cheers, Alex.

Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.