From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

What architecture and good practice should I use for a massive application.

Hi!

 

I am just taking over someone else's program controlling a test bench and simulating some stuff on the side as well.

I had quite some training with labview, but I am definitely still on a learning curve, especially because it's been a few years I haven't practiced at all and I would need some guidance concerning the following.

 

This program is by far the biggest I have been dealing with but I am quite surprised with it as it features:

  • a lot of VIs that are quite messy
  • A massive use of Global&Local variables (I've always been told to restrict their use to the strict minimum)
  • (Almost no comments nor documentation 🙄)
  • The use of one while loop per "thing to do": For example if the user click on "Set parameters", this will open a new window (a new VI) runned by a parrallel loop. a screen shot below. Is this some kind of "Master/Slave" Architecture but for bigger apps? In the example attached, the while loop on top is the main user interface controller. At the bottom is one of the many loop controlling/showing additional parameters. Basically, if the user click on "orbit" button on the main interface, it sends true to the "view orbit" indicator linked by local variable to the other loop that turns continuously until the all program is shut down. That sounds extremely processor consuming to me, especially given that there are around 20-30 other stuff like this (11 other loops controlled exactly that way, and then a bunch of other stuff simulated into on bigger loop.)

"orbit" is a button used to open the "View orbit" window (VI) by activating the "Orbit vi" indicator through local variables."orbit" is a button used to open the "View orbit" window (VI) by activating the "Orbit vi" indicator through local variables.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

To give a bit more details on the program itself:

It is supposed to communicate with the testbed by wifi, controlling motors (with the least lag possible), acquiring data and monitoring instruments health (battery level, positions etc.) as well as simulating and predicting events. Thus a quite a lot of parameters buttons, indicators and controllers that need to interact with the user.

 

The program was originally designed on LV2011 and updated on LV2019.

 

My first work with this program will be to clean it up and document it a bit more, but later on I was thinking of restructuring it as well, and for that I wanted to have you're expericenced opinion on what I should go for.

Again, I was thinking of using a Slave/master architecture, but I'm not sure that it would be appropriate for big programs like this.

I'm gonna do some more reading on this, I also heard about Action Engines but this seems to be quite old, is it still worth spending time on it?

 

I hope my message was clear, please tell me if you need more info.

Thanks a lot in advance for your help.

Vincent.

0 Kudos
Message 1 of 32
(3,341 Views)

@VinnyAstro wrote:
  • The use of one while loop per "thing to do": For example if the user click on "Set parameters", this will open a new window (a new VI) runned by a parrallel loop. a screen shot below. Is this some kind of "Master/Slave" Architecture but for bigger apps? In the example attached, the while loop on top is the main user interface controller. At the bottom is one of the many loop controlling/showing additional parameters. Basically, if the user click on "orbit" button on the main interface, it sends true to the "view orbit" indicator linked by local variable to the other loop that turns continuously until the all program is shut down. That sounds extremely processor consuming to me, especially given that there are around 20-30 other stuff like this (11 other loops controlled exactly that way, and then a bunch of other stuff simulated into on bigger loop.)

"orbit" is a button used to open the "View orbit" window (VI) by activating the "Orbit vi" indicator through local variables."orbit" is a button used to open the "View orbit" window (VI) by activating the "Orbit vi" indicator through local variables.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Before there where event structures (<LV7, IIRC) this was a accepted way to do it. Not a good way, but accepted. If I had to do that right now, I'd probably design a queue based 'fake' user event-ish construct, but back then we didn't really know better. And a huge application wasn't huge by todays standards, so you'd usually get away with all malpractices anyway.

 


@VinnyAstro wrote:

Again, I was thinking of using a Slave/master architecture, but I'm not sure that it would be appropriate for big programs like this.


If you google, use master\slave. It's the used term, not slave\master. Not sure if effects the results, google being awful smart nowadays.

 

@VinnyAstro wrote:

I'm gonna do some more reading on this, I also heard about Action Engines but this seems to be quite old, is it still worth spending time on it?

Action engines and any architecture are more or less complementary.

 

AE are a way to share data and functionality across the application, and can be used in any architecture.

 

For me, a master\slave, or state machine is a really un-intuitive to base an application on. They are not really architectures to me, more patterns for a VI. IMHO, it's a bad idea to make every VI a state machine or MS (and I have seen that). In general, it's a bad idea to take a pattern and use it as an architecture. A MS for example, splits UI and handling of the UI input. But just as often you want your UI synchronized with the handling (for instance in a settings dialog), and it's just silly to use a MS if you don't need one.

 

Actor framework and DQMH are more architectures than they are patterns. You'd still might use a state machine and\or master\slave to complement them.

 

I'd make an OO analysis and an OO design, then an OO program. That's not that easy to explain in a post though. And if I did, it wouldn't be concise and might do more wrong than right. What I like about it is that I can usually divide the functionality in terms that make sense for the application, and glue them together with known patterns, like events and\or queues and\or channel wires.

 

The problem is, everyone who is used to a way of working, will prefer and recommend that way to do things.

 

It's really more about what you are comfortable with.

0 Kudos
Message 2 of 32
(3,320 Views)

I feel sorry for you, because that does not sound like a lot of fun.I assume that the program is currently working correctly. Especially if you are still learning LabVIEW there will be a lot of landmines that could alter functionality in a subtle way, only to be discovered at a high cost much later, so tread lightly.

 

I assume you have detailed specifications what the program is supposed to do so you can keep testing after each change.

 

The code you are showing is quite horrible, but seems to be a twisted approach to a "start asynchronous call", basically launching a subVI without stalling the main caller loop.

0 Kudos
Message 3 of 32
(3,307 Views)

Yeah, that's the 'no design'-design. 🙂

As for having a single loop for something isn't a big thing in this case as long as there's a decent wait so you're not doing a greedy loop. The use of Indicators as variables (i guess that Orbit is a hidden indicator?) is just bad design, although it works often enough.

Only controls and indicators that's a part of the UI should exist, all other data should be kept in shift register. Usually 1 (big-ish) cluster.

The Orbit being a good example of what should be a Value change-event of that button, and as you mention, having a producer/consumer design where this Orbit.vi is launched.

FGVs are basically fancy Globals (or more primitive in a way since it was the LV2 global) and the more advanced version Action Engines (where you add some more functions than get/set) is in the range of Polished FGV to Poor mans OO (I'm slightly hyperbolic). 🙂

 

So, asking asking if you should change Globals to FGVs is much like asking if you should polish a turd. It might look nicer, but it still smells bad. Action Engines can be awesome, but they're not the solution to your problem.

 

Producer/Consumer is a very solid design that will take you far. Start by adding an event structure that takes case of simple button presses, add a consumer that does everything that takes >10ms.

 

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 4 of 32
(3,306 Views)

@Yamaeda wrote:

Only controls and indicators that's a part of the UI should exist, all other data should be kept in shift register. Usually 1 (big-ish) cluster.

Hmm. I sometimes use a hidden control to receive a value signaling event from a dynamically started VI.

 

But I admit, only because it's convenient, not because it's pretty 🙂.

Message 5 of 32
(3,297 Views)

@VinnyAstro wrote:
Basically, if the user click on "orbit" button on the main interface, it sends true to the "view orbit" indicator linked by local variable to the other loop that turns continuously until the all program is shut down. That sounds extremely processor consuming to me, especially given that there are around 20-30 other stuff like this (11 other loops controlled exactly that way, and then a bunch of other stuff simulated into on bigger loop.)

No, that loop only spins every 100ms, which is a very very long wait for a computer and uses negligible CPU. Once "Orbit" is pressed, the subVI launches, and the small loop stalls completely until the subVI completes (And if the panel of the subVI is closed while it is still running, it will keep running with the panel closed and the button never resets and the loop never spins again. It also means that pressing "exit" cannot stop that loop when this happens). Only if the subVI completes, the button resets and the subVI can be called again. If the subVI is well behaved, this solution (while convoluted and problematic by many other metrics) is not "expensive" for the CPU.

0 Kudos
Message 6 of 32
(3,288 Views)

@altenbach wrote:

I feel sorry for you, because that does not sound like a lot of fun.I assume that the program is currently working correctly. Especially if you are still learning LabVIEW there will be a lot of landmines that could alter functionality in a subtle way, only to be discovered at a high cost much later, so tread lightly.


Before you do ANYTHING be sure that you have proper source code control in place so that you can revert changes.

 

I generally like to use a queued message handler as a basic but generally effective approach, though I have been moving more toward object oriented. In your situation I would consider keeping the asynchronous loops and just control them via messages from a single messaging loop. If data needs to be shared between loops I would pass it through the messaging loop (two-way communication between the loops).

Message 7 of 32
(3,279 Views)

Somewhat tangential to the actual restructuring itself, I would want to know how much time is spent adding new features, fixing bugs, and in other various maintenance tasks. It might feel really good to clean up a mess but how much time you spend cleaning everything up should be somewhat proportional to how much time you expect to be working in the codebase.

 

Don't make things worse but if you're going to spend 40 hours this year fixing a few bugs here and there, don't spend 120 hours upfront making everything look nice.

Matt J | National Instruments | CLA
Message 8 of 32
(3,277 Views)

@Jacobson-ni wrote:

Somewhat tangential to the actual restructuring itself, I would want to know how much time is spent adding new features, fixing bugs, and in other various maintenance tasks. It might feel really good to clean up a mess but how much time you spend cleaning everything up should be somewhat proportional to how much time you expect to be working in the codebase.

 

Don't make things worse but if you're going to spend 40 hours this year fixing a few bugs here and there, don't spend 120 hours upfront making everything look nice.


To add to this point. You also need to consider what is the life of this code and will it need to be extended in the future. While it might be true that this year you may only need to touch it a few times, if you know that next year major changes will be needed, it may still be wise to restructure the code.

 

Most of the systems I work on are messaged based systems. Similar to actor framework but we developed our framework prior to AF being released. Regardless of the architecture or framework you may settle on one thing to keep in mind though is to do your best to separate the UI from the business logic. UIs can and do change. In addition, it is much easier to re-use code (think libraries) when they are not coupled to the UI. Here is where OOP can help a lot. You may also want to consider the SOLID when designing your system. Even if you don't go OOP, these principles are still quite valuable.



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 9 of 32
(3,258 Views)

Hi all!

 

Thanks a lot for your prompt answers, I was not expecting such reactivity 🙂

It is actually quite conforting to see that I was not wrong haha

 

I'm still getting used to this forum and it seems to me I can't do multi-quoting so I'll answer you old school style:

 


@altenbach wrote:

I feel sorry for you, because that does not sound like a lot of fun.I assume that the program is currently working correctly. Especially if you are still learning LabVIEW there will be a lot of landmines that could alter functionality in a subtle way, only to be discovered at a high cost much later, so tread lightly.

 

I assume you have detailed specifications what the program is supposed to do so you can keep testing after each change.

 

The code you are showing is quite horrible, but seems to be a twisted approach to a "start asynchronous call", basically launching a subVI without stalling the main caller loop.


Don't be sorry, it's gonna be tricky but I hope learning a lot from it and pass some certifications in the meantime 😄

Yes I do have a apparently working program right now with some minors bug apparently. But this program was designed for a specific testbed, and we are building an updated version at the moment, so some stuff will probably have to be updated as well (for the easiest part simulation constants, for the worse I'd say complete change in the communication with the testbed.) 

So all of that will be my future work: understanding the program, fixing current issues and then adapting when the new bed is ready to test.

And I do have a sorta detailed doc of what it should do (I'm still in the whole documentation, I started on wednesday and doing part-time on that for now)

 

 

@Wiebe@Caria wrote:

If you google, use master\slave. It's the used term, not slave\master.[...] Action engines and any architecture are more or less complementary. AE are a way to share data and functionality across the application, and can be used in any architecture.

 

Actually, yes I know it's not slave/master, but for some reasons, my brain always switch it over 🙄

Cool, I'll then have a deeper look at AEs 🙂

 

 

@Yamaeda wrote:

As for having a single loop for something isn't a big thing in this case as long as there's a decent wait so you're not doing a greedy loop. The use of Indicators as variables (i guess that Orbit is a hidden indicator?) is just bad design, although it works often enough.

 

Yeah, I've run the code in differents situation and it is really not so greedy indeed, the app max proc use at 7% ... BUT I am not controlling the testbed yet, so no comms, no control of a ot of stuff, so this figure might be irrelevant for now.

And no, it is a design used indicator, placed in the corner of the actual button, it's looking nice, but not really useful.

 

Producer/Consumer is a very solid design that will take you far. Start by adding an event structure that takes case of simple button presses, add a consumer that does everything that takes >10ms.

 

Cool, I'll work on that, thanks!

 

 

@Altenbach wrote:

No, that loop only spins every 100ms, which is a very very long wait for a computer and uses negligible CPU.

 

Indeed, as I said just above 🙂

 

@johntrich1971 wrote:

Before you do ANYTHING be sure that you have proper source code control in place so that you can revert changes.

 

I generally like to use a queued message handler as a basic but generally effective approach, though I have been moving more toward object oriented. In your situation I would consider keeping the asynchronous loops and just control them via messages from a single messaging loop. If data needs to be shared between loops I would pass it through the messaging loop (two-way communication between the loops).

 

Very good point, but no worries, we are using SVN but for now I am doing basic test on a copy anyway 🙂

The queued message handler is actually what I've learned but kinda was the "summum" of our classes, we never had the time to go further, and I assimilated that it was more the proper way to do (this mixed with a producer/consumer architecture)

I have never used OO with LV, I'm curious to see how it works but it is quite far in my priorities for now.

 

 

@jacobson and @Mark_Yedinak you are pointing out a very good question.

As I said, I have just started and still discovering the program. I tend to be perfectionnist without really counting my time ... But I will have results to show.

That said,, as I answered above, there will be an update very soon of the hardware, and it is expected to have some more but mainly also a production at some point. So I need to know very well the program to answer any questions, be able to fix future bugs and make it just enough modulable to do easy changes.

It is anyway not my priority to restructure itnow, but I thought it would be important to already think about it 🙂

 

 

Thanks again for you help, I'll continue to dip in and see the current structure (that already look more complex than when I posted my first message ...)

 

0 Kudos
Message 10 of 32
(3,218 Views)