LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Structuring a large application with classes, FGV's etc.

Hi all,

 

I'm looking for some advice/input on how to structure large application with multiple hardware interactions and data acquisition/processing in a nice way, so that parts are easily maintainable and reusable. I've managed to create a working version. Now I need to create an almost identical copy with different hardware, and it is clear that my application is built like Frankenstein's monster.

 

The application uses multiple hardware devices (visa serial, daqmx and imaq). The basic structure is added in the snippet: it has an initialization step, followed by parallel data acquisition (producer), data processing "state machine" (producer & consumer) and data logging (consumer). Furthermore there are two parallel loops for front panel actions and receiving external triggers (interrupts for the state machine).

I decided for the double producer/consumer so that these steps would not jeopardize the timing of the system.

 

Since multiple parallel loops require information about connections, output data etc., I created one massive Master-cluster (cluster of clusters) to pass on all the data without having too many wires. Currently this contains somewhat 7 sub-clusters and is becoming a monster itself. (**Note: data transfer between loops is via queues.)

The subVI's take this Master cluster as input and extract the required data from it. To me it feels like a massive entanglement of subVI's and clusters, and changing one thing leads to a string of conflicts that need to be updated.

 

Basically I want to tame this dragon before someone else has to work with it. Of course I'm looking into classes like I'm used to in Python and C++ programming, but I have to practice a bit with these in LabVIEW still.

Additionally I found that Functional Global Variables (fgv's) are a great way to pass data between subVI's (looking at this example at least). I think that would severely improve readability and modularity of my application. I think I would need to stick with the Producer/Consumer-layout, but it would be a lot nicer with more modular subVI's.

 

How would I implement this? Classes for all the hardware devices, subVI's with FGV's for all the data acquisition, processing, and logging? One subVI for example that contains the entire state machine, so that my main VI is super clean?

Any other methods that I'm overlooking?

 

Happy to hear your thoughts on this.

0 Kudos
Message 1 of 9
(255 Views)

There are so many options. But I have some suggestions to start with.

 

Check https://forums.ni.com/t5/LabVIEW-Development-Best/Measurement-Abstraction-Plugin-Framework-with-Opti...

 

And take a look at the example "measure and log" in the example finder



Certified LabVIEW Architect

G# - Award winning reference based OOP for LV, for free! VIPM Github
Message 2 of 9
(238 Views)

Thanks for the tips!

The Measure and Log example looks a lot like what I am trying to achieve. And shows that I really created Frankenstein's monster.

I will also look into the other link later.

0 Kudos
Message 3 of 9
(209 Views)

Whenever defining a system you should seriously consider what data is being passed to what components. If you are using the "Uber clsuer" to pass everything to everyone, that is a bad design. You should only be passing actual data that is required/necessary for the component to do its job. Good OOP design can help with this definiton but simply using classes does not guarantee a good design. I would recommend looking at the SOLID principles of system design.



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
0 Kudos
Message 4 of 9
(184 Views)

When you are wondering if you should use a class or an FGV for something, you will want to think about the question: "Will I need more than one of these?" If you end up needing more than one, then the class will be a lot better as you can easily make multiple instances.

 

An even better question to ask when considering whether to use a FGV is "Would it be bad if I had more than one of this thing?" If the answer to that is yes, it really is something that your application should fundamentally only have one of, then a FGV is a really good solution because it is fundamentally global. But if it wouldn't be bad and would be foreseeably useful to have more than one, then you should probably make a class.

Message 5 of 9
(174 Views)
Highlighted

I may not be reading your OP correctly but if so it's worth mentioning the following:

Do FGV's or OOP.  Don't do both.FGVs have their downfalls and OOP has it's learning curve but using FGVs with OOP in LabVIEW is a great way to create a impossible-to-debug mess.  I've done both.  FGVs have a much lower learning curve, the challenges have already been mentioned above.  OOP is tougher but has a lot of benefits, especially when combined with a QMH of some kind.  This is my recommendation if you are comfortable with it.

 

http://www.ni.com/tutorial/53391/en/

 

0 Kudos
Message 6 of 9
(151 Views)

This all looks very interesting, thanks for the advice! I need some time to look into this a bit more.

 

The Measurement & Log example with the VI's seems like a very elegant solution. In principle it is similar to what I have now, but way neater organised.

 

"Whenever defining a system you should seriously consider what data is being passed to what components. If you are using the "Uber clsuer" to pass everything to everyone, that is a bad design. You should only be passing actual data that is required/necessary for the component to do its job."

 

This is in principle what I do. Data only gets written/modified in one place. But since I do not want to clutter the main VI with unpacking the cluster, I generally  make my subVI's take the "Uber Cluster" as input and do the unpacking there. I do avoid modification of the data in two spots.

0 Kudos
Message 7 of 9
(95 Views)

Disclaimer: My coffee has not kicked in yet.

 

I suggest you first step back and develop a design document that shows the components of the system you are trying to tame. The first doc should look a lot like the hardware/software elements that compose the system.

 

Then revise that diagram and assign meaningful names to the software components that you need to implement. That diagram should show what interactions take place between those components.

 

I offer the following image from a blog post I wrote (found here) below as a simple example.

 

Design.png

For each of the interactions that you define/discover you can THEN choose from the various synchronization or design patterns that LabVIEW offers.

 

In other words let the application requirements define the application and do not limit yourself to the simple out of the box solutions.

 

Now since the previous posts have compared OOP and FGV, let me please invite you to review that blog entry I linked above. I compares LVOOP with Action Engines in libraries and may help you benefit from the simple to understand Action Engine with the re-use available with Libraries.

 

Done for now.

 

Ben

 

0 Kudos
Message 8 of 9
(61 Views)

@irPaul wrote:

 

"Whenever defining a system you should seriously consider what data is being passed to what components. If you are using the "Uber clsuer" to pass everything to everyone, that is a bad design. You should only be passing actual data that is required/necessary for the component to do its job."

 

This is in principle what I do. Data only gets written/modified in one place. But since I do not want to clutter the main VI with unpacking the cluster, I generally  make my subVI's take the "Uber Cluster" as input and do the unpacking there. I do avoid modification of the data in two spots.


IF you are passing the Uber cluster than you are not following my advice. Just because you only modify a portion of it doesn't mean you are only passing the relevant data. You are still passing ALL of the data to everyone. I very rarely use "uber" clusters. Generally the only place they will exist is in a shift register in a state machine to minimize the wires running through the state machine. This "uber" cluster is usually comprises of many other well defined clusters with very specific purposes. Those are the pieces of data that get passed to subVI or other components of the system. Passing all data to everyone opens the doors for bugs that are very difficult to trace since the data can get changed anywhere in the system.



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
Message 9 of 9
(40 Views)