John Lokanis Posted March 4, 2008 Report Share Posted March 4, 2008 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 Quote Link to comment
Ton Plomp Posted March 4, 2008 Report Share Posted March 4, 2008 Looks nice, although the Demo is a little bit counter intuitive. Could a similar approach be made with notifiers? Ton Quote Link to comment
Yair Posted March 4, 2008 Report Share Posted March 4, 2008 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." Quote Link to comment
Jim Kring Posted March 4, 2008 Report Share Posted March 4, 2008 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. Quote Link to comment
John Lokanis Posted March 4, 2008 Author Report Share Posted March 4, 2008 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 Quote Link to comment
Tomi Maila Posted March 5, 2008 Report Share Posted March 5, 2008 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. 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 Quote Link to comment
John Lokanis Posted March 5, 2008 Author Report Share Posted March 5, 2008 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 Quote Link to comment
John Lokanis Posted March 5, 2008 Author Report Share Posted March 5, 2008 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 Quote Link to comment
Tomi Maila Posted March 5, 2008 Report Share Posted March 5, 2008 QUOTE(jlokanis @ Mar 5 2008, 12:17 AM) 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? 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? QUOTE(jlokanis @ Mar 5 2008, 12:17 AM) 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... It was developed solely within LabVIEW 8.5, no voodoo. And yes it uses XNodes, though. Quote Link to comment
Yuri33 Posted March 6, 2008 Report Share Posted March 6, 2008 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). Quote Link to comment
John Lokanis Posted March 6, 2008 Author Report Share Posted March 6, 2008 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. Quote Link to comment
LAVA 1.0 Content Posted March 6, 2008 Report Share Posted March 6, 2008 QUOTE(jlokanis @ Mar 5 2008, 12:01 PM) 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?... It took us years to get a difinative answer to that question! The answer is NO, the UI thread is not required to read or write Globals. You can see this in http://forums.ni.com/ni/board/message?board.id=170&view=by_date_ascending&message.id=176661#M176661' target="_blank">reply #29 of this thread on the Dark-side. I think that whole thread is worth reading if you have not done so previously. Ben Quote Link to comment
John Lokanis Posted March 6, 2008 Author Report Share Posted March 6, 2008 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... Quote Link to comment
crelf Posted March 6, 2008 Report Share Posted March 6, 2008 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. Quote Link to comment
Tomi Maila Posted March 6, 2008 Report Share Posted March 6, 2008 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. Quote Link to comment
PeterB Posted July 25, 2008 Report Share Posted July 25, 2008 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 Quote Link to comment
John Lokanis Posted July 25, 2008 Author Report Share Posted July 25, 2008 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 Quote Link to comment
PeterB Posted July 26, 2008 Report Share Posted July 26, 2008 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 Quote Link to comment
John Lokanis Posted July 26, 2008 Author Report Share Posted July 26, 2008 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. Quote Link to comment
Rolf Kalbermatter Posted July 29, 2008 Report Share Posted July 29, 2008 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 Quote Link to comment
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.