05-24-2021 10:56 AM - edited 05-24-2021 11:07 AM
Hello everybody, I inherited a huge application with very few SubVIs but huge block diagrams. The main block diagram spans at least 5 screens from what I can garner.
There's no architecture and it's an extreme mess of wires. It reminds me of the internals of an integrated circuit or a computer after being looked at in a microscope.
Some of the first few obvious things I've done:
My main issue/concern:
One of the things I've wanted to do is to place as many sections as I can in SubVIs and start from there. But its almost always the case that the front panel has many, many indicators with property nodes, references, and global variables sprawled everywhere that I literally cannot do it.
Not just because it "breaks" the front panel but because there's a LOT of wires crossed over. (Such that the getting one section to convert to a SubVI just doesn't work)
This isn't the first time I've done this, but the times I have I've performed terribly in my opinion.
What are some of the strategies you guys have employed for refactoring projects like this? Are there any articles/best practices I could read for these sort of topics? I'm mainly curious if VI scripting could somehow help in these situations/looking for ideas, references.
My extra pain is that my poor laptop cannot keep up with the main diagram so obviously that's going to be a first thing to tackle.
05-24-2021 11:21 AM - edited 05-24-2021 11:50 AM
I am very sorry you have to deal with this. It won't be easy. Will it be worth it?
What does "inherited" mean. Does it meant that you have to do significant changes in the future or does it just mean that you are tasked to fix minor bugs discovered by customers?
Does the code currently work well? (not obvious race conditions, bugs, performance, etc.)
Do you understand the basic flow and functionality as currently implemented?
Do you have ways to fully validate functionality after any code change?
05-24-2021 11:23 AM
I have had projects like this and it's certainly no fun. Unfortunately, sometimes it's easiest to just document what you can and start from scratch. Move segments from the spaghetti version to the new version if you can, but otherwise just rewrite.
The last one I did was luckily "organized" chaos over the multiple screens, so the wires weren't crossed but they weren't labeled either!
Without seeing your code it's hard to give a good strategy, but here are some thoughts:
0- Document what the program is SUPPOSED to do and see if the program appears to do that thing! Bad programming might "accidentally" work now, but refactoring could reveal an existing race condition.
1- Pick an area of the BD and simplify it, then pick another one. Don't try to do the whole thing at once; do it in stages.
2- Don't try to "finish" a section in its entirety before moving on. You may replace a section with a subVI, then later that subVI will turn out to not be necessary. You may be able to eventually replace several sections with one modular subVI, but don't try to do that out of the gate unless you can really see an obvious reason to.
3- At the end of this you need to have zero global variables*. Start by picking a global variable and finding all instances of it (Ctrl-F is your friend!). Figure out what the global is doing and try to eliminate one instance of it at a time by replacing it with the right wires. You don't have to kill all instances at one time; you can kill one or two of global A, then one or two of B, then all of C, then back to A, etc. Untangling the spaghetti will take a multi-pass approach.
*The exception to this is "Write once read many" globals that contain non-changing configuration data, like language localization or something. I prefer to not use these, but it's definitely an OK usage.
4- Add a shift register and a "locals" cluster that you can begin passing to subVI's. This will let you replace lots of globals and will let you replace strict property nodes with generic property nodes that take a control reference.
5- Remember, you're going to go through all of the diagram about 100 times. Don't try to clean any one thing entirely at one time.
6- Add LOTS of wire labels to ensure you know what wire does what.
7- Remember, you're going to go through all of the diagram about 100 times. Don't try to clean any one thing entirely at one time. 😉
Good luck. I don't think VI Scripting would generally help me very much here.
05-24-2021 11:42 AM
What it sounds like to me is that someone has very tightly coupled the interface with the program logic. And since the interface is the front panel with all of the controls on it, it's not easily something you can decouple.
I see two possible ways to modularize this:
First way would be somewhat preserving the current structure while allowing it to be made into subVIs more easily. You could create a large type defined cluster containing a reference to each front panel control, then have the first thing the program does is wire those references into one instance of the cluster. Then the way to make subVIs is to pass in one instance of the "megacluster" which then contains all references it needs to the front panel, getting around the problem you have now where there just aren't enough inputs on a subVI to handle them all.
The second way is to implement a messaging system of some kind that will take over the UI functions. For instance, you could create a While loop that has a dequeue function in it and turn it into a queued message handler. Each indicator gets a case in the message handler to get written to, so that when you want to write to an indicator, instead wiring the value to the indicator you wire it into a message sender that sends a message to the UI loop telling it to update. Once you replace all of the value writes in one section with this, you'll just need one queue reference instead of possibly dozens of control references. Reading values from controls is a bit trickier since it would need to be a 2-way system, but I think the idea is sound.
In addition to this, for both options you could look into why there's so many controls, and see if any can be reduced because they aren't needed. For instance, status indicators that no one looks at could be removed. Or if you have a dozen indicators for the same type of value, see if they could be an array instead of a dozen separate indicators.
05-24-2021 11:54 AM
Luckily, I have not had to do this for a while. In at least 2 instances, I replaced a PC with a cRIO (the determinism was a requirement). So those were obvious rewrites. Another that enters my brain I rewrote because I had to upgrade from Traditional DAQ to DAQmx (old XP machines to Windows 10). I know I've had others, but they aren't immediately coming to the front of my brain, some of which were general clean-ups.
Rule 1: Know your requirements. Find out exactly what the program is supposed to do. Talk to those who use it. I guarantee they will have plenty of feedback for you (this doesn't work, that is too slow, this feature isn't needed, this work flow is stupid, I really wish it did this, etc.). From there, you can decide if you need to rewrite or if you think you can patch something in the current code.
05-24-2021 12:27 PM
@crossrulz wrote:
Rule 1: Know your requirements. Find out exactly what the program is supposed to do. Talk to those who use it. I guarantee they will have plenty of feedback for you (this doesn't work, that is too slow, this feature isn't needed, this work flow is stupid, I really wish it did this, etc.). From there, you can decide if you need to rewrite or if you think you can patch something in the current code.
100 times this.
Although, I've had plenty of experiences where the people using the software were perfectly fine with "the old way" of doing things and didn't ask for a better way, simply because they weren't aware there could BE another way.
If you see something that could be improved, bring it up to them and ask if they'd like that instead. Often they'll go "Holy cow, yes, I didn't know you could change that!"
05-24-2021 01:30 PM
@BertMcMahan wrote:
@crossrulz wrote:
Rule 1: Know your requirements. Find out exactly what the program is supposed to do. Talk to those who use it. I guarantee they will have plenty of feedback for you (this doesn't work, that is too slow, this feature isn't needed, this work flow is stupid, I really wish it did this, etc.). From there, you can decide if you need to rewrite or if you think you can patch something in the current code.
100 times this.
Although, I've had plenty of experiences where the people using the software were perfectly fine with "the old way" of doing things and didn't ask for a better way, simply because they weren't aware there could BE another way.
If you see something that could be improved, bring it up to them and ask if they'd like that instead. Often they'll go "Holy cow, yes, I didn't know you could change that!"
I guess the flip side of the coin is "don't fix what's not broke". Try to leave the dataflow alone until you are sure you won't break something by eliminating a race condition, for instance. I've seen badly written code depend on race conditions to work. Creating a subVI is a subtle way of eliminating race conditions, so create subVIs with care. This is often the case with "spaghetti code".
05-24-2021 01:58 PM
@billko wrote:
@BertMcMahan wrote:
@crossrulz wrote:
Rule 1: Know your requirements. Find out exactly what the program is supposed to do. Talk to those who use it. I guarantee they will have plenty of feedback for you (this doesn't work, that is too slow, this feature isn't needed, this work flow is stupid, I really wish it did this, etc.). From there, you can decide if you need to rewrite or if you think you can patch something in the current code.
100 times this.
Although, I've had plenty of experiences where the people using the software were perfectly fine with "the old way" of doing things and didn't ask for a better way, simply because they weren't aware there could BE another way.
If you see something that could be improved, bring it up to them and ask if they'd like that instead. Often they'll go "Holy cow, yes, I didn't know you could change that!"
I guess the flip side of the coin is "don't fix what's not broke". Try to leave the dataflow alone until you are sure you won't break something by eliminating a race condition, for instance. I've seen badly written code depend on race conditions to work. Creating a subVI is a subtle way of eliminating race conditions, so create subVIs with care. This is often the case with "spaghetti code".
This is actually my main concern. In this case I've been asked to fix a few "minor" and somewhat concerning bugs as well as add new features to it. However, I've seen this exact behavior in some programs before so I'm definitely threading carefully.
Guessing by the answers everyone has given it seems like there's definitely no easy way out. I'll struggle through this one, one way or another. (Other than re-writing from scratch but I don't think I can in this situation)
I usually feel bad about complete re-writes but by the looks of it, it is not an invalid solution provided you understand the high level functionality of the application?
As for the context. It is software that runs tests on "motors", and controls test conditions through outputs/actuators. It samples all that data and generates reports and displays a few graphs and data.
The interface allows the user to run these tests, change conditions, parameters, report configurations, etc. (I know this is super vague, I want to share the block diagram, but I don't think I can share it, even though it itself is not useful to anybody outside that context)
05-24-2021 02:10 PM
If you can get funding to rewrite it from scratch (and you feel comfortable doing so!) then it MAY be the way to go. Couldn't say without seeing your code.
That said, if it's bad-but-working, then you have some tactics you can try. We have an old program like that around here. It was written in, I think, LV5 (!) and has been incrementally updated for 15+ years. Unfortunately, since it *does* actually work, there's never a budget for a full rewrite.
My tactic for this has been a very slow piecemeal approach. When someone asks for a new feature, I estimate how long I think it'll take, then double or triple it, depending on the complexity of the new feature. With a program like that, it's *very* tricky to update.
If I can get approval, I'll implement the new feature first, but if I have time left over in the allotted budget I'll fix or refactor something else while I'm in there. If I end up needing all of the estimated time, then that's fine too as I added a reasonable buffer for my estimate.
Over the years, the program has been slowly but steadily morphing into something SLIGHTLY less horrible to debug and use. It still needs a rewrite, but lots of things have been updated under the hood that really help make it work better.
05-24-2021 02:10 PM
@crossrulz wrote:
Luckily, I have not had to do this for a while. In at least 2 instances, I replaced a PC with a cRIO (the determinism was a requirement). So those were obvious rewrites. Another that enters my brain I rewrote because I had to upgrade from Traditional DAQ to DAQmx (old XP machines to Windows 10). I know I've had others, but they aren't immediately coming to the front of my brain, some of which were general clean-ups.
Rule 1: Know your requirements. Find out exactly what the program is supposed to do. Talk to those who use it. I guarantee they will have plenty of feedback for you (this doesn't work, that is too slow, this feature isn't needed, this work flow is stupid, I really wish it did this, etc.). From there, you can decide if you need to rewrite or if you think you can patch something in the current code.
I wish I could do this, though no one here wants to switch to cRIOs though. I've explained some issues that have come up by using user PCs + DAQs only, but they don't want to switch simply due to costs.
That said I'm definitely gathering a list of requirements. It went a bit over my mind, but I have to sort that out too.