Jump to content
Sign in to follow this  
John Lokanis

A LabVIEW WORM

Recommended Posts

No, not that kind of WORM!

A Write Once, Read Many variable.

Background:

For large applications with many FP controls that you want to access and modify from sub-vis, a common means of doing this is to build a large cluster of references to all the FP controls and then pass this cluster to all the sub-vis that need access to any of these controls. While this uses the 'value' property to read and write to the FP controls, which is usually frowned upon due to speed issues, in a GUI, this usually not an issue due to the slowness of the human user. The problem with this method is needing to have this large cluster of FP controls on every sub-vi. This can take up a lot of space on the sub-vi panels and can cause problems if you update the cluster with more control refs in the future, causing it to resize and overlap with other FP objects. (obviously, the cluster of refs would be a strict type def)

A Solution:

So, how to solve this?

Well, the need is to set all these references at the beginning of execution and then be able to read them from anywhere in the application. Sounds like a good use for a Global variable. But I don't like global variables. I won't go into all the reasons, but one of the key ones is that they can be written to by anyone anywhere in the application, which would be bad in this case.

So, what I needed was a write once, read many variable that could be accessed from any VI in the application. It would be initialized only once when the EXE started. Sounds like a job for a Functional global! Well, there is a new form of functional global that is extremely simple using the feedback node.

See the attached simple example. This uses only one reference. You must ensure that the first call that sets the value completes before and subsequent calls execute.

Download File:post-2411-1204569204.zip

What do you think? Does this seem like a good solution or is there a better one out there? Are there any pitfalls of this solution?

thanks,

-John

Share this post


Link to post
Share on other sites

Looks nice, although the Demo is a little bit counter intuitive.

Could a similar approach be made with notifiers?

Ton

Share this post


Link to post
Share on other sites

QUOTE(jlokanis @ Mar 3 2008, 08:34 PM)

Are there any pitfalls of this solution?

Well, as you said: "You must ensure that the first call that sets the value completes before and subsequent calls execute."

Share this post


Link to post
Share on other sites

QUOTE(jlokanis @ Mar 3 2008, 10:34 AM)

No, not that kind of WORM!

A Write Once, Read Many variable.

CRUD is more flexible than a WORM :)

A Create, Read, Update, and Delete interface.

Share this post


Link to post
Share on other sites

QUOTE(tcplomp @ Mar 3 2008, 11:17 AM)

Looks nice, although the Demo is a little bit counter intuitive.

Could a similar approach be made with notifiers?

Ton

A notifier or a single element queue could work, but these would allow multiple writes. My goal was to make a 'by reference static variable' This would always be initialized by the app in the beginning and then could be used anywhere in the app's VI tree. If you are working with several devs on a project, you could provide this VI as a tool for writting sub-vis that need access to the main app's FP references. The nice thing is that the number of VIs that must have an instance of the type-def'ed cluster of references is kept to a minimum. This makes it easier to maintain as you add more FP controls that others need access to. Since it is write once, you don't need to worry about it being overwritten by accident in a sub-vi.

Also, I suspect that the feedback node is a very efficient way of storing and accessing this data. The implementation, therefore, is much simpler that using Queues, Notifiers or traditional functional globals, IMHO.

To Jim's comment, I also use CRUDs, but mine are ACRUDs, Auto Create, Read, Update, Destroy. These use a single element queue and feedback node for storage of the queue ref. They test the queue ref to see if it is valid on each call and if not, create it. They then perform the READ, WRITE or DESTROY operation and output the queue reference back to the feedback node. I like this better than using the first call fuction because I can destroy and recreate several times within an app as needed to control memory usage.

But again, the point of this tool was a STATIC variable, since FP references do not change while the app is running.

-John

Share this post


Link to post
Share on other sites

Hey,

Funny that you came with your WORM just now as I also wrote a Write-Once-Read-Many variable called One-Time Store and released it last week as a library that ships with the Active VI Toolkit. Unlike your example One-Time Store must be referred with a valid refnum so it cannot be used as a global directly, although the refnum itself can be stored to any of the globals available. The image below illustrates how the One-Time Store library functions.

post-4014-1204614102.png?width=400

Anybody who is interested in playing around with the One-Time Store library is more than welcome to download the Active VI Toolkit.

Cheers,

Tomi

Share this post


Link to post
Share on other sites

That is an interesting solution. What you seem to have built is a pass by reference variable that can store any data. I am curious how this was implemented. I assume that the data is stored as a variant, but how do you cast the variant back to the original data type without needing a type input on the read function?

Will this allow you to store ANY data type (including complex custom types) or just LV primative data types.

I guess I need to download it and peek inside...

-John

Share this post


Link to post
Share on other sites

QUOTE(Tomi Maila @ Mar 3 2008, 11:07 PM)

Funny that you came with your WORM just now as I also wrote a Write-Once-Read-Many variable called One-Time Store and released it last week as a library that ships with the Active VI Toolkit. Unlike your example One-Time Store must be referred with a valid refnum so it cannot be used as a global directly, although the refnum itself can be stored to any of the globals available. The image below illustrates how the One-Time Store library functions.

I gave this a try. Cool tool. It does not seem to like complex data types, however. I get a broken wire from the create to the read node. Not sure if my cluster is too complex or is because it is a typedef'd cluster. It seems to work for simple clusters. Is there a known limit to the datatypes it can support?

I would be interested to know how this was built. I am assuming some sort of xnode voodoo scripting stuff that required 7.1.1 to access...

-John

Share this post


Link to post
Share on other sites

I have absolutely no experience with LVOOP, but does this implementation actually provide TRUE parallel read functionality? Any functional global implementation obviously does not, and in most instances, that's not a big deal.

However, there are still some cases where I prefer to use true globals as WORMs because you can read in parallel (reading a global does not block other reads of the same global like a functional global would).

Share this post


Link to post
Share on other sites

QUOTE(Yuri33 @ Mar 5 2008, 01:46 AM)

(reading a global does not block other reads of the same global like a functional global would).

Actually, I don't think that is true. I seem to remember reading somewhere that reading a global requires a task switch to the user interface thread, thereby causing parallel accesses to be done serially. Perhaps an NI dev can chime in on this?

But, you are right, my idea using the feedback node based functional global is not a parallel design. It was not intended to be. If you need parallelism, I recommend single element queues or perhaps Tomi's new toolkit, if it supports parallel access.

But in the case of an actuall LV global, it still allows write access anywhere, which was what I was trying to prevent.

Share this post


Link to post
Share on other sites

QUOTE(Tomi Maila @ Mar 4 2008, 02:22 PM)

It doesn't support LabVIEW classes, all other types should be supported but ones that contain LabVIEW classes. This is a limitation of the used technique. If you get a broken wire with something that doesn't contain LabVIEW classes, then I've a bug that needs fixing. Can you provide any sample code and steps to reproduce the broken wire?

It was developed solely within LabVIEW 8.5, no voodoo. And yes it uses XNodes, though.

See attached zip for an example. You will need to wire the three objects together. When you do, the connection between the second and third objects will be broken. If you save the VI, the wire will change to not broken, but if you try to create an indicator from the value output, it will be a string and not the cluster used to define the datatype.

This is an extreme example, as you can see by the complexity of the datatype. But, I don't see why this should not work.

I was under them impression that Xnodes were not available to devs outside of NI after 8.0. I guess you are one of the lucky ones...

Share this post


Link to post
Share on other sites

QUOTE(neB @ Mar 5 2008, 12:13 PM)

The answer is NO, the UI thread is not required to read or write Globals.

True, although one of the reasons it took so long is because the behaviour changed (yes, it used to reside in the UI thread) - I don't know when this changed, but I'd guess somewhere between 6 and 8.

Share this post


Link to post
Share on other sites

Thanks for reporting the bug! I was able to reproduce it and I can try to fix it.

QUOTE(jlokanis @ Mar 5 2008, 07:15 PM)

I was under them impression that Xnodes were not available to devs outside of NI after 8.0. I guess you are one of the lucky ones...

No I'm not lucky one in this respect.

Share this post


Link to post
Share on other sites

Hi John,

I have an observation and a question about your WORM.

I'm assuming this has always been possible utilising the "First Call?" primitive inside a functional global variable to ensure that it could only get written to once. If that's the case, then yes you have proposed an elegant solution to reducing the programming overhead of a functional gobal to achieve the same thing.

Does writing to a top level FP control/indicator using a WORM cause a switch to the UI thread in exactly the same manner as it would have if using the traditional approach ? (i.e. by wiring to the reference passed via a terminal on a sub-vi's FP).

regards

Peter

Share this post


Link to post
Share on other sites

QUOTE (PeterB @ Jul 24 2008, 12:28 AM)

Does writing to a top level FP control/indicator using a WORM cause a switch to the UI thread in exactly the same manner as it would have if using the traditional approach ? (i.e. by wiring to the reference passed via a terminal on a sub-vi's FP).

If you are writing to the FP control, I suppose this always causes a UI thread hit since it is 'updating' the UI. The thing that is particularly bad is to write to the value property of a FP control using VI Server.

I have a general rule of thumb: Only write to FP controls when you *really* need to. For example, I have some UIs that use a tree control to display data. The temptation is to write to the tree all the time and let it 'store' the data. Unfortunately this is very slow. So, instead I have created a set of VIs that implement a 'shadow' data structure that stores all writes to the tree (including state changes, like cell colors). IF the FP is currently displayed, then the writes are also passed to the tree itself. If not, then they are only written to the shadow structure and when the FP is opened, I grey the tree, defer updates, update all the data from the shadow and then un-grey, un-defer and let the display refresh.

When you have over 50 instances of a VI each with two trees on their FPs, this helps a lot.

Sorry for the long winded description. I'm glad you like the WORM. It is particularly useful in multi-dev situations to ensure 'static' variables are not messed with by someone in some sub-vi.

-John

Share this post


Link to post
Share on other sites

QUOTE (jlokanis @ Jul 25 2008, 06:29 AM)

If you are writing to the FP control, I suppose this always causes a UI thread hit since it is 'updating' the UI. The thing that is particularly bad is to write to the value property of a FP control using VI Server.

I have a general rule of thumb: Only write to FP controls when you *really* need to. For example, I have some UIs that use a tree control to display data. The temptation is to write to the tree all the time and let it 'store' the data. Unfortunately this is very slow. So, instead I have created a set of VIs that implement a 'shadow' data structure that stores all writes to the tree (including state changes, like cell colors).

........John

Thanks John, I figured as much too.

I think the concept of a shadow data structure that only propagates data to the FP on a less regular basis is a valuable idea which BTW has already been implemented in LabVIEW by disabling the Advanced>Synchronous Update option. Presumably that option is still too taxing for the speeds we/you are after otherwise you would be using it rather than the shadow structure. So perhaps if NI allowed us to adjust the priority of the asynchronous updates to a FP control/indicator so we could tune them for our particular application's speed needs. What do you think ? Then all the penalties associated with GUI refnums would be greatly diminished.

regards

Peter

Share this post


Link to post
Share on other sites

QUOTE (PeterB @ Jul 24 2008, 04:39 PM)

Thanks John, I figured as much too.

I think the concept of a shadow data structure that only propagates data to the FP on a less regular basis is a valuable idea which BTW has already been implemented in LabVIEW by disabling the Advanced>Synchronous Update option. Presumably that option is still too taxing for the speeds we/you are after otherwise you would be using it rather than the shadow structure. So perhaps if NI allowed us to adjust the priority of the asynchronous updates to a FP control/indicator so we could tune them for our particular application's speed needs. What do you think ? Then all the penalties associated with GUI refnums would be greatly diminished.

regards

Peter

I would agree with you in most cases, but the tree control is an exception. It is notoriously slow to update but a very powerful GUI display for hierarchical data.

Share this post


Link to post
Share on other sites

QUOTE (jlokanis @ Jul 25 2008, 12:36 AM)

I would agree with you in most cases, but the tree control is an exception. It is notoriously slow to update but a very powerful GUI display for hierarchical data.

It is slow since you typically have to execute several methods/properties to update it. After each method/property node it is redrawn ... unless you use the defer panel update method. Switching that on before a tree control update sequence and then off afterwards makes updating a tree control a lot faster.

Rolf Kalbermatter

Share this post


Link to post
Share on other sites

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.

Sign in to follow this  

×
×
  • Create New...

Important Information

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