LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Clusters as data structures

I am looking for the best and simplest way to create and manage data structures in Labview.

Most often I use clusters as data structures, however the thing I don't like about this approach is that when I pass the cluster to a subroutine, the subroutine needs a local copy of the cluster.

If I change the cluster later (say I add a new data member), then I need to go through all the subroutines with local copies of the cluster and make the edit of the new member (delete/save/relink to sub-vi, etc).

On a few occasions in the past, I've tried NI GOOP, but I find the extra overhead associated with this approach cumbersome, I don't want to have to write 'get' and 'set' methods for every integer and string, I like being able to access the cluster/object data via the "unbundle by name" feature.

Is there a simple or clever way of having a single global reference to a data object (say a cluster) that is shared by a group of subroutines and which can then be used as a template as an input or output parameter? I might guess the answer is no because Labview is interpreted and so the data object has to be passed as a handle, which I guess is how GOOP works, and I have the choice of putting in the extra energy up front (using GOOP) or later (using clusters if I have to edit the data structure). Would it be advisable to just use a data cluster as a global variable?

I'm curious how other programmers handle this. Is GOOP pretty widely used? Is it the best approach for creating maintainable LV software ?

Alex
Message 1 of 20
(13,889 Views)
Right click on the cluster and choose

advanced >>> customize.

In the drop down that says "control" select "type def". Save the control.

Use that custom control ever place you use the cluster.

When you need to change the cluster definition, edit the type def, save it and under file choose,

"Apply change"

All of the instances will automatically update!

Ben
Retired Senior Automation Systems Architect with Data Science Automation LabVIEW Champion Knight of NI and Prepper LinkedIn Profile YouTube Channel
Message 2 of 20
(13,867 Views)
Ben,
Thank you! That's perfect.
Alex
0 Kudos
Message 3 of 20
(13,863 Views)
Storing the cluster as a type def is a good suggestion. The additional benefit of using GOOP would be that you would get control over which VIs was using this type def. If you use GOOP, there will be a cluster type def defining the data which can be stored in the object. Since you have tried GOOP I am sure you already seen that. GOOP provides encapsulation of data meaning that the only VIs in your application which actually uses this type def are the VIs defined as methods of your class.

To minimize the cumbersome get and set methods reading pieces of object data you must start to think of the class not only as a global variable but as a place to group data and functionality together. Many of the VIs using the data of your class could probably be converted to methods of the class. If you design like this there will be fewer get and set methods (there will always be some, its nothing wrong with that) and more high level functions (VIs). You will start to think of your class not as data but as a code module which provides useful functionality. If you learn to apply GOOP it can simplify maintenance.

As an example I provide two versions of a GPIBSignalGenerator class. It is just a dummy class but hopefully it gives you an idea of what I mean by avoiding Get/Set methods.
TheGetSetGPIBSignalGeneratorClass
- SetGPIBAddress(String id)
- SetGPIBstring(String s)
- SendString()
- ReadString() String

TheAvoidGetSetGPIBSignalGeneratorClass
- Create(String gpibadress)
- SetFrequency(double val)
- Setpattern(enum pattern)
- Start()
- Stop()

Thanks,
Jan Klasson
Endevo
www.endevo.se
0 Kudos
Message 4 of 20
(13,813 Views)
I will also comment that LabVIEW is compiled, not interpreted. This is why it takes longer to start executing a program the first time the run arrow is pressed after editing.


Putnam Monroe
Putnam
Certified LabVIEW Developer

Senior Test Engineer North Shore Technology, Inc.
Currently using LV 2012-LabVIEW 2018, RT8.5


LabVIEW Champion



Message 5 of 20
(13,809 Views)

Alex,

Encapsulation of data is critical to maintaining a large program. You need global, but restricted, access to your data structures. You need a method that guarantees serial, atomic access so that your exposure to race conditions is minimimized. Since LabVIEW is inherently multi-threaded, it is very easy to shoot yourself in the foot. I can feel your pain when you mention writing all those get and set VIs. However, I can tell you that it is far less painful than trying to debug a race condition. Making a LabVIEW object also forces you to think through your program structure ahead of time - not something we LabVIEW programmers are accustomed to doing, but very necessary for large program success. I have use three methods of data encapsulation.

    1. NI GOOP - You can get NI GOOP from the tutorial Graphical Object Oriented Programming (GOOP). It uses a code interface node to store the strict typedef data cluster. The wizard eases maintenance. Unfortunately, the code interface node forces you through the UI thread any time you access data, which dramatically slows performance (about an order of magnitude worse than the next couple of methods).

 

    1. Functional Globals - These are also called LV2 style globals or shift register globals. The zip file attached includes an NI-Week presentation on the basics of how to use this approach with an amusing example. The commercial Endevo GOOP toolkit now uses this method instead of the code interface node method.

 

  1. Single-Element Queues - The data is stored in a single element queue. You create the database by creating the queue and stuffing it with your data. A get function is implemented by popping the data from the queue, doing an unbundle by name, then pushing the data back into the queue. A set is done by popping the data from the queue, doing a bundle by name, then pushing the data back into the queue. You destroy the data by destroying the queue with a force destroy. By always pulling the element from the queue before doing any operation, you force any other caller to wait for the queue to have an element before executing. This serializes access to your database. I have just started using this approach and do not have a good example or lots of experience with it, but can post more info if you need it. Let me know.
Message 6 of 20
(13,785 Views)
Too early in the morning. Here is the attachment I promised with the earlier post.
Message 7 of 20
(13,784 Views)
Thanks for the feedback guys (no I didn't know VI's were compiled). I have a longer history working in C++/Java and greatly respect the importance of encapsulation. For some reason, I find it quicker to implement OO in those langauges than in the LV environment - maybe because I haven't learned the LV-way yet. I've given GOOP a couple chances over the past few years and every time I try it, it just doesn't sit well with me. Perhaps as a consequence I usually limit my use of LV to simpler applications; I can't say I've had a problem with race conditions yet - thank goodness!

Accomplishing some things in LV is so easy - it is fun, I have to say it - compared to how I might do it using another language or development environment - it would be nice for NI to find a way to extend this simplicity to OO, maybe they already have.

From this enthusiasm about GOOP (NI marketing should rename this!) I will promise to give it another chance and try again.

Alex
Message 8 of 20
(13,763 Views)
Alex,

I just want to comment on DFGrays reply on GOOP performance.

The original GOOP solution developed by NI and Endevo does use a Code Interface Node. There is some performance penalty due to all data being streamed on read/writes to the GOOP repository. (The info about the UI thread where new to me. You learn every day:-). Although the performance issue I have used it for several years and found it a very useful tool. I like the structure it gives my programmes.

We have (WARNING this is promotion, but also info for you) developed a new GOOP kernel which is 100% LabVIEW. It stores object data in unititialized shift registers and there is no streaming of data. Performance on read/write object data is in the magnitued of 10 times faster. The newer GOOP also provides inheritance and virtual methods in a C++ style. The new Wizard is called Endevo GOOP Wizard 3 and is included in the GOOP Inheritance Toolkit. Apart from the new kernel the Wizard itself is also improved. It supports class renaming, automated icon editors etc. We also have a UML Modeller supporting reverse engineering. Visit www.endevo.se for more info or free trials.
End of promotion.

The Single-Element-Queues is an interesting approach. But I would guess the functional globals perfoms better. Haven't benchmarked it though.

Regarding protection for race conditions the Functional Globals and Single-Element-Queues protects the write operation. The GOOP solution provides a higher level protection, it puts protection in a method and thus protects a full transaction of: ReadObjecData-Modify-WriteObjectData.

Thanks,
Jan
Endevo
0 Kudos
Message 9 of 20
(13,741 Views)
Jan, you got me interested, so I dusted off a set of run-time database benchmarks I wrote back in the LV6.0 days and added the queue database to them. I was more than a bit surprised. I got the following numbers using the tests on my machine (2.4GHz HT Pentium 4, 512MBytes RAM), 10,000 iterations. If you run the datasocket test, make sure you have the server running first. See the attached benchmarks for details of the tests (LV7.1).

Datasocket - 13,000ms
NI-GOOP - 2440ms
Control References - 2050ms
Configuration VIs - 810ms
Shift Registers - 525ms
Queue - 86ms

There is some jitter in the test and they are probably dependent upon LV version. Take home message - use them queues.
Message 10 of 20
(13,710 Views)