LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Modularizing very large VI with interdependent modules

Solved!
Go to solution
Highlighted

Hi everyone,

 

I'm currently working with a large, unwieldy VI that was been developed over ~10 years by multiple people. It consists of many parallel loops taking up ~150 screens worth of real estate. I would like to modularize the program. To illustrate, I've attached a screenshot of the VI. Note the number of tabs. Each one contains roughly the same level of complexity as the one shown, and many of them talk to one another through local variables and property nodes (usually to trigger event structs).

 

My current approach to modularizing the VI has been to make subVIs out of each module, passing front panel items as references to the subVIs, and accessing them through the main VI via subpanels. However, I realized that this will not work for modules that need to talk to each other, unless I keep the front panel items in the main VI, which I feel partly defeats the purpose. Is there a better way to modularize interconnected modules of a very large VI?

 

Thank you for your help!

0 Kudos
Message 1 of 7
(186 Views)

It sounds like a decent start. For communication between modules (and also for indicator updates, but that can be the next step) you should try User Events. A common solution that's very scalable, is to have a Cluster as datatype, with a String and a Variant as data. The string would be the command sent, and since you send a variant it can be anything (although you need to unpack it with a Variant to Data each time)

I've attached a snipped as an idea. As you realize you can have multiple user events if needed, e.g. splitting to/from or different modules, but start with one.

UserEvent.png

/Y

G# - Award winning reference based OOP for LV, for free! ADDQ VIPM Now on GitHub
"Only dead fish swim downstream" - "My life for Kudos!" - "Dumb people repeat old mistakes - smart ones create new ones."
Certified-LabVIEW-Developer
0 Kudos
Message 2 of 7
(173 Views)

Many years ago, one Mantra of Good Software Development was "Write the Documentation First".  I didn't realize the wisdom of this until I was handed my first LabVIEW task, which involved a LabVIEW 7.0 Real-Time Data Acquisition (and Experiment Control) routine that had a similar multi-tabbed Front Panel, and hundreds of sub-VIs, some of which covered tens of pages.  After struggling with "Where do I start?" for several months, I stopped looking at the LabVIEW code and started Writing the Documentation for "What did we want to do?".  By focusing on the "What" rather than the "How (do we do this)", I was able to completely restructure this task, completely discarding many of the less-than-optimum design decisions made by the original Developers, and delivered a product that (a) worked, (b) collected data at double the data rate of the previous routine, (c) never "dropped" data (the original routine would miss the occasional Data Sample and flag the run as "containing missed data"), (d) streamed the data from the RT side to the PC to disk continuously, so that a "crash" (rarer now!) resulted in no lost data, and (also a design criterion!) had no Block Diagram that took more than a single Monitor to view.

 

Well-written Documentation (which tends to be Top-Down) can inspire better Top-Down Code Design.

 

Bob Schor

 

P.S. -- I've since encountered other situations similar to yours, "How do I improve my out-of-control code?", to which my usual answer is "Start Over" (and Write the Documentation First).  It is advice rarely followed, but in at least one case (where I played a major role), we actually did this and the results were similarly spectacular, resulting in a presentation at a recent NIWeek).

0 Kudos
Message 3 of 7
(132 Views)

Maybe you should consider designing and remaking it from scratch. Do you feel that when you change something in one end, some new bug pops up which in a completely unrelated function (VI/feature)? I think that in a case like this, you will be spending a lot of time fixing bugs, time you could invest in making a well designed software that can live a long time and be much better and more fun to wok with.

 

If you have to keep fixing bugs to satisfy your customer, then remaking it from scratch might not be an option at the moment. You could then try to modularize part by part to try and slowly making it more modular, and at the same time thinking about how to make it better when you can make it from scratch.

 

I suggest you have a look at the example "measure and log". It goes through step by step the evolution of creating an application that is similar to yours (and similar to what many of us do) and how it can be implemented using channel wires. I created my latest application with channel wires and really like it. The communication between modules is very visual.



Certified LabVIEW Architect

G# - Award winning reference based OOP for LV, for free! VIPM Github
Message 4 of 7
(124 Views)

I am also in the camp of a complete rewrite.  Study the program carefully and figure out what it is trying to do.  Also talk to the engineers and technicians who will be using the software and figure out what is is supposed to do.  Sometimes you will gleam caveats from the current code.  But you are almost guaranteed to find out that the users really hate parts of the software and you should work away from that.  From there, you have requirements and then we can start talking about architectures.


There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
0 Kudos
Message 5 of 7
(113 Views)

Thank you everyone for the very helpful replies. Special thanks to thols for pointing out channel wires to me. I missed this when it was released and expect it to dramatically change my VIs going forward.

 

It sounds like a rewrite is the right way to go here, so I'll give that a shot. As a preemptive measure, however, I'd like to ask one more specific question about subVI communication, as I expect it to come up later:

 

I often use Event Structures to streamline user triggered events in the VI. Occasionally, I use Property Node Value (signalling) to trigger one or more events from another module. For example, I may use it to automate a batch of stereotyped actions that are typically done one at a time by user. How should I go about implementing this if the event structure (and its associated front panel items) and the automation components are in different subVIs?

 

Thanks again for all the help!

0 Kudos
Message 6 of 7
(99 Views)
Solution
Accepted by topic author charleslu
06-18-2019 06:25 AM

@charleslu wrote:

I often use Event Structures to streamline user triggered events in the VI. Occasionally, I use Property Node Value (signalling) to trigger one or more events from another module. For example, I may use it to automate a batch of stereotyped actions that are typically done one at a time by user. How should I go about implementing this if the event structure (and its associated front panel items) and the automation components are in different subVIs?


Break the "tasks" out into its own separate subVI that runs asynchronously to the rest of your code, then use messages sent to that module to ask it to "do the thing". Your UI will then send a message to this subroutine telling it to "do the thing" instead of the UI "doing the thing" itself. Now any module can send a message to your worker to have it "do a thing" whenever it needs to.

 

For example, say you have a text box on your GUI that allows users to write in notes, where they're logged with a timestamp (e.g., "Joe spilled coffee on the table by the machine, beware of odd data at this timestamp"). Right now, perhaps you have a Value Change Boolean event set up to write the text in User Input to a file, but suddenly your requirements change so that you want the program to automatically write something to the file as well (e.g., "Overheat detected by thermocouple 16").

 

You can make a new routine that simply has a string-based queue as an input. When you send a string to this queue, the "worker" VI will write it to a file. Your GUI will now send a value on the queue wire when the Boolean Value Change triggers, and your temperature monitoring system can send a value down its wire when it detects an overtemp. You could make the subprogram be a little more sophisticated if you made the datatype a cluster with an Enum describing its action and a Variant describing the payload. For example, one action could be "Log message" where the payload is converted to a String. Another action could be "Create new log file" where the payload is converted to a Filepath. Yet another could be "Log error message" where the payload is an Error cluster.

 

To sum up, the above is basically a manual implementation of what the Actor Framework does, but using objects and some special sauce to make more advanced stuff work. The process I mentioned above that runs independently and has its own queue for commands is called an Actor (in that framework) as it Acts independently, and anything can send it a message.

Message 7 of 7
(89 Views)