Refueling in Flight
Imagine you’re testing a system that has a long startup time, and you find a small change that needs to be made. Now you have to make the software change, rebuild the executable, shut down the application, deploy the new executable, and go through the long startup period again. What a waste of time!!! Wouldn’t it be great if you could avoid having to stop and restart the entire application? It would be like refueling in flight. Well, you can…!
I attended the US Certified LabVIEWTM Architect (US CLA) Summit in Austin this past spring. After Steve Ball’s presentation, Packed Project Library Pointers Framework Edition, I had an epiphany. With encouragement from Omar Mussa and Jeff DeBuhr, I decided to pursue my idea.
My idea was that by using Packed Project Libraries (PPL) I could build software components that could be changed while a LabVIEW built application was still running. I built a sequencer application that would allow a user to select a particular piece of code or step to run. While the application is running, a developer changes that particular step’s code and rebuilds the PPL. If the user runs the same step again the new code will be used. Now let’s see this in action.
The first thing we do is launch the application and run the startup step.
Then we run Test1, but it fails.
So we look at the code for Test1 and realize all we have to do to make the test pass is change the message text to say Test1 passed. Don’t you wish it was always that easy?
To deploy this change we could run the shutdown step, deploy the change, run the long startup step, and then run the Test1 step. Instead we can just make the software change and build the PPL while the application is still running.
We run Test1 again and we have a passing test.
Now let’s look at the code that is called when we press the Run Step button. As you can see the PPL file path is an input. I decided to not show it on the front panel so I could keep the user interface clean for demonstration purposes. This path could be locally stored or on the network. We strip off the name of the PPL and use it to find the path of the Run Step.vi and LabVIEW (LV) Class that will be run. The Run Step.vi is dynamically run using VI Server. Using VI Server is an important part of the solution as I explain later in this post. The LV Class Path is an input into the Run Step.vi. The message displayed on the front of the application is updated when the Run Step.vi has finished.
The Run Step.vi calls one of the children of the step class using the factory pattern.
This approach definitely has some advantages, but there are some design constraints to consider. The code for the PPL must reside in a different LV project from the calling VI. LabVIEW will return an error if you try to build a PPL while that same PPL is in memory in another open project. This means that my approach of making changes and rebuilding the PPL while the application is running only works if the application is an executable.
Another consideration is related to making changes to LV classes inside of the PPL. If you change code inside of a LV class in a PPL, then that change will not be reflected in run-time if that LV class is in memory. So that begs the question, how do you know if the LV class is in memory or not when you make the change to the PPL?
As Stephen Loftus-Mercer, senior software engineer in LabVIEW R&D explained, "The only way that a class is able to leave memory is when we detect that all of the VIs using that class are all idle and are themselves leaving memory. Any VI using a parent class counts as a use of all of that parent's child classes. So you simply have to make the caller VI go idle and then unload that."
He also clarified that the reason he has said,“'there's no way to unload plug-ins' is that people who want to do this generally mean 'I want to leave the framework running but unload a specific plug-in.’ The unloading described here only works if you halt and unload the whole framework."
This is why I chose to use VI Server to call the Run Step.vi and did not call the LV class code directly in the block diagram of my application.
In my example, I am unloading the entire framework, i.e the Run Step.vi. The key here is that you have to be aware of memory management with LV classes. This becomes more difficult with more complex class hierarchies. One approach suggested by Omar Mussa is that you could make a LV class manager to keep track of which classes are in memory. You could then remove a particular class from memory by sending a message to that class to shut itself down or having the class monitor for changes to the PPL on disk and then shut itself down.
In summary, by using a PPL and VI Server, I was able to make changes to my built application without having to stop the application. When deciding whether to implement this approach you should weigh the time it saves making changes during run-time against the increase in development time. The additional development time efforts include creating multiple projects, building the main application into an executable, and designing around memory management of LV classes.