As I’ve worked on the past several posts, I had a very specific milestone in mind. You see, I am wanting to use this LabVIEW blog to demonstrate and explain a lot of things that I have learned over the years, but the proper presentation of those topics typically requires a certain amount of pre-existing infrastructure.
That is what I have been doing through the preceding posts — building the conceptual basis for a testbed application that embodies what we have discussed so far. Having this infrastructure is critical for thorough, disciplined learning because without it you are left with example code that may demonstrate one point well, but violates a dozen other principles that are standard “best practices”.
Having said that, however, remember the quotation marks around the word Finished in the title. The marks are there to highlight the fact that, as with all LabVIEW applications, this testbed will never really, and finally finished. In fact, it is planned that it will get better over time, and support more functionality. The best we can say is that the testbed is done for now.
Come next week though, who knows? There’s still a lot to do.
The Testbed Project
Like the UDE template, the current version of the testbed and all its associated VIs is available through the website’s subversion repository at:
http://svn.notatamelion.com/blogProject/testbed application/Tags/Release 1
You can use the web client as you did before, but considering the number of files, you will find it to be less time-consuming (and error prone) to use a dedicated client application like TortoiseSVN on Windows. Regardless of how you get the testbed project onto your computer, the first thing that you see with any application is the project file that is its home. When you open testbed.lvproj note that it embodies two of the conventions that I use on a regular basis.
- There are virtual folders for two types of items — both of which are contained in LabVIEW libraries:
The _ude folder contains the libraries associated with the two UDEs that are currently being used. The _libraries folders contains the two libraries embodying the main functional areas that the application currently uses. The Configuration Management library houses the files that the application uses to fetch configuration information (right now it only needs to read the launch processes). The Startup Processes library contains the VIs that will be launchable when the application starts, and the process-specific VIs that they use. Finally, the Path Utilities library contains the VIs that the code uses to locate the internal project home directory in an executable. - The launcher VI is directly in My Computer, along with its INI file, testbed.ini.
As I have discussed elsewhere, this naming convention simplifies a variety of tasks such as locating the application INI file.
Looking at the VIs
The remainder of this post will look at the VIs used in creating the testbed application. Right now the discussion will be at a rather high-level as we will be digging into them on a more detailed basis in the future. With each VI, I also list a few of the future topics that I will use that VI to demonstrate. If you would like to see others, let me know in the comments.
Testbed.vi
Here we see pretty much what you would expect. As discussed in the previous post, the code starts by calling a subVI that stores the launcher’s path to a FGV that will make it available to the rest of the application. It then calls the subVI that reads the INI file to get the list of processes to launch.
The array of process descriptions thus retrieved is passed to the main loop that processes each element individually. The LabVIEW documentation does a good job of describing asynchronous call-and-forget so I won’t repeat it here. Instead I will point out the 1 second delay that follows the dynamic call logic. Often times, a new process starting up has a lot it needs to do. This delay gives the new process a 1 second window where the launcher is not doing any potentially time-consuming tasks — like loading into memory the next process it will launch. The delay may not be technically necessary, but I have found that it can sometimes make things run a bit smoother.
Finally, you may be wondering, why there is logic to explicitly close the launcher window? Isn’t there a VI property that tells LabVIEW to automatically close the window if it was originally closed? Well, yes there is, and you would think that it would handle this situation. In fact, it does — but only in the development environment. When you build the application into an executable, the auto-close doesn’t work for the top-level VI window, or at least doesn’t with LabVIEW 2014.
- Future Enhancements
- Showing the “busy” cursor
- Enhancing user feedback
- How to launch other standalone executables
- How to launch VIs that need input parameters
Startup Processes.lvlib:Acquire Data.vi
This is the VI that the original producer loop turned into. The most noticeable change has been the addition of an event structure that both allows the process to respond to a application stop event, and ( via the timeout event) provides the acquisition timing.
- Future Enhancements
- Handling errors that occur during initialization
- Turning acquisition on and off
- Changing acquisition parameters
- Correcting for timing “slips” when using the timeout event
Startup Processes.lvlib:Display Data.vi
This VI is the new consumer loop. This VI retains the event loop it had before, but adds an event for stopping the application.
- Future Enhancements
- Using subpanels to generalize your interface code — and avoid tab controls
- Implementing dialog boxes that don’t stop everything while they are open.
- The advantages of the “System”-themed controls
In addition to these VIs, there’s no rule that says there can’t be more — like say for handling errors. You may also need additional processes for handling tasks that have differing timing requirements, or communicate through different interfaces. We’ll look at those too.
But for now, feel free to poke around through the code, and if you have any questions ask in the comments. Also try building the application and perhaps even the installer.
Until next time…
Mike…
Hi! In the last few month I’ve been trying to develop a Labview application on my own. I’ve been following your blog for one month now and i find it pretty neat and clear. It addresses many of the questions I’ve arisen recently. I’ve one question though (I think you pointed that out somewhere in your posts): what would be the best practice to launch Vis that have an interface and that need to be sensitive to this interface’s events? As I see it there are at least two options: 1) Put the controls in the launcher, bundle the references together and launch the Vi. Inside the Vi register for the events. 2) Leave the controls in the Vi and dump it in a subpanel in the launcher. What I don’t like of the first is that the Vi itself is not standalone and you can’t run it without the control references of the launcher. In the second, instead, I don’t like that when the launcher is not running your subpanels don’t show anymore your Vi front panel, making it harder to debug when your code includes several Vis.
What would be your quick suggestion to keep the best of both worlds?
Thanks again for your blog! Dario.
Well, there are several ways to handle things. As I understand what you are asking, you have controls on the front panel of your main user interface and you want the VIs in the subpanels to be able to handle events on those controls. Is this correct?
Mike…
I would think my problem is exactly the other way around. I have all the controls in the subVi, which is launched by the main Vi in 0x80 mode. How do I pass to the main Vi the references of the controls in the subVi? With a Queue? Event? Global Variable? Or is there a simpler/more elegant way? Thanks!
Ah, I see. Well any of them would work, but a couple would not be correct conceptually. Queues and events are used when you would expect repeated usage of the communications channel. However you probably aren’t going to transfer these references more than once. Likewise, queues and events carry a timing of when the transfer was made — information that you really don’t care about. My initial reaction then would be to use some sort of shared memory like a FGV or a DVR. However, if you use a notifier like I showed to gate initialization, there’s no reason you couldn’t piggy back on that notifier. After all the notifier has to have some data, might as well be your reference.
Mike…