Building a LabVIEW Toolbox

In some ways, reusable LabVIEW code is sort of like motherhood and apple pie. Everybody agrees with the ideal, but sometimes the reality doesn’t quite measure up to the hype. While nobody will openly talk down code reusability, it often times ends up as one of those things we’ll get to, “one day”. No one questions that reusable code simplifies work and improves the quality of finished applications, but how do you create this toolbox in the first place? Well, dear readers, is what we are going to deal with this time: Where to look to get the VIs for the toolbox, and where to put them so they are available.

Every Toolbox Needs a Home

So let’s start with where we are going to store our reusable VIs, and the first thing to observe is that there are a couple basic requirements that any potential location has to be able to meet. To begin with, it should be a set location that doesn’t need to change from one project to the next. This is why I recommend the conventions that I presented a couple of weeks ago: Setting up your workspace as I suggested creates a directory structure that doesn’t need to change from one project to the next. Likewise, each functional area of your toolbox should, have its own subdirectory that contains a LabVIEW library with the same name as the directory where it is located. All VIs associated with a given functional area should be in that area’s directory and linked to its library. This organization creates a unique namespace that can help clarify what a given VI does and reduce the likelihood of naming conflicts.

Directory-Structure

The second requirement for a reuse code storage location is that it has to be easy to find during development. While having the toolbox located just a couple clicks off the root of a hard drive is a step in the right direction, there’s a lot more we can do. For example, on the function menu that you get when you right-click in free space on the block diagram, there is a palette selection called User Library and in theory it is the place where your toolbox would go. The only problem is that by default it points to the user.lib subdirectory in the LabVIEW installation directory. This directory association is a problem because with each release, Windows is getting more restrictive about being able to modify the contents of program installation directories. Luckily, that default association can be changed that the new association will automatically update to show new files that you add to the toolbox.

To proceed, with just LabVIEW open go to the Advanced submenu on the Tools menu, and select the Edit Palette Set… option. In the Functions palette window, you should see User Libraries icon. Right click on it and you will see an item with a check mark next to it called Synchronize With Directory — click on it once to deselect it and then select it a second time to reselect it. When you reselect it, LabVIEW will open a dialog box asking you to select the directory that you want to associate with the icon. Navigate to the directory where you are going to be creating your toolbox (by convention, C:/Workspace/Toolbox) and click the Select Folder Button. At this point, the icon will be linked to your new toolbox directory, plus as you add items to your toolbox, they will automatically show-up in the menu the next time you restart LabVIEW.

While we are here, there is one other palette modification you might want to make, and that is to add a copy of the User Libraries menu to the Programming palette. Because that palette is often open by default, it makes it handy to have the link to your toolbox there as well. To add it, click on the Programming icon to open the palette, and then right-click in any open space. From the resulting menu, go to the Insert submenu and select Subpalette… . The first thing you will see is the Insert Subpalette dialog box. From the available options, select Link to a directory and click the OK button. In the resulting dialog box, navigate to the directory where LabVIEW is installed, and then open the user.lib subdirectory. Finally, click the Select Folder Button.

To save your work and return to the LabVIEW development environment, click the Save Changes button in the Edit Controls and Functions Palette Set window.

Where to get the VIs

Now that we have a place for the VIs to go, let’s get some code to put there. The first place to look is with your existing code — in our case, the test bed application. However, don’t start first thing looking for large involved chunks of code to reuse. That level of reuse is important, and it will come, but let’s get our feet wet looking for small simple pieces. Remember that small things you use a lot provide greater time savings than large complex VIs that you reuse once or twice. So what do you look for to make reusable routines?

Here’s a few things to consider:

  1. Things you do a lot
    A good case in point here is the delay that follows the launching logic in testbed.vi. I have a routine in my toolbox that wraps a conditional structure around a wait, and it is probably one of the most commonly used routines in my toolbox. It’s very simple, but it seems that I am constantly running into situations where I need either the error clusters to create a data dependency that defines exactly when the delay runs; or the error case to allow me to bypass long errors.However, you will likely want to modify this snippet before reusing it — otherwise all you will have is a reusable 2 second delay. Before turning the code into subVI, bring the constant outside the case structure so the delay time becomes a parameter that is passed into the routine. Carrying this idea a step further, turning this code into a subVI can also be an opportunity to recreate the API to better-fit your needs. For instance you may want to be able to wait until a particular clock time, or you may want to be able to specify the delay is something other than milliseconds. In a future post we’ll revisit this subVI talking about using LabVIEW units to simplify code.
    For another example of how I used the repackaging for reuse to tweak the API to make it more to my liking, check-out the changes I made to the logic for closing the launcher front panel. As it was, it would always close the windows regardless of what you were doing, or wanting to do. However, there are times during development when it would be better to leave it open. To address this need, I modified the code so it has an additional input that allows you to specify when you want it to close the front panel. The enumeration has two values: Always and Not in Development.
  2. Low-level functions that should have error clusters, but don’t
    Repackaging the Wait (ms) function is a good example of this point too, but I also have a whole family of routines that put wrappers around things like the built-in one- and two-button dialog box functions. Remember, just because a given function might not be able to generate an error, it doesn’t mean that the routine might not reasonably need to be able to respond to an error.If an earlier stage of a process has failed, do you really want to keep prompting the user to do things? To do so confuses the problem by hiding (from the user’s perspective) where the error occurred. If you don’t think the user’s perceptions matter, remember that a confused user can’t give you good feedback, and efficient troubleshooting and debugging is dependent upon good feedback.
  3. Functions that, while not difficult, took you a while to figure out
    Often times you will run into a situation where it takes you a while to figure out how to do something rather simple, like using .NET calls to perform a network ping more efficiently than using the system executive function. Such code is perfect to turn into reusable code. Just remember to give a little thought to what should be input parameters to make the code flexible.
  4. Functions that implement an agreed-upon convention
    I spend a lot of time talking about conventions and the efficiency they can bring to a development process. An easy way of implementing some conventions is to embody them in code. For example, you can create a VI that builds the standardized directory structure you use, or one that builds paths to the application INI file based on the conventions that your organization uses.
    Note that this point can also serve as your avenue for reuse of larger sections of code. For example, you may to want collect and report errors is a certain way, or store test results in a particular format or in a particular location. Developing a standalone process that implements these techniques can significantly simplify the work needed to standardize on the techniques across all your projects.

Obviously, there are a lot of other places to look for reusable code, but this will get you started. Just don’t be surprised when we come back to this in the future.

A testbed and a toolbox

So where does all this work leave us? Going through what we have built so far, I have implemented a toolbox that currently has two VIs in it, and then used those VIs in the testbed. By the way, while I was there, it occurred to me that many of the projects I will be creating will use the same basic structures for stopping the application and reporting errors, so I also moved the UDEs associated with those operations to a section in the toolbox called Standard UDEs. You can find the modified testbed code in our subversion repository at:

http://svn.notatamelion.com/blogProject/testbed application/Tags/Release 3

while the toolbox is at:

http://svn.notatamelion.com/blogProject/toolbox/Tags/Release 1

Be assured that going forward we will be adding more code to our toolbox, but for now this will do. In terms of using the toolbox, you’ll note also that I did not include the reuse libraries in the project. To do so can create its own kind of cross linking problems, and is unnecessary. All you need to do is simply use the VI’s you need and let LabVIEW maintain the libraries as included in the Dependencies section of the project.

Oh yes, one more thing for you to think about: If I am going to be using the same error collection technique in the future, why didn’t I make the Exception Handler.vi a part of the reuse library? And why is it named “Exception Handler” anyway, it’s just for errors, right?

Until next time…

Mike…

PS: Happy Birthday to me… Yes, it is official: I am older than dirt.

Making UDEs Easy

For the very first post on this blog, I presented a modest proposal for an alternative implementation of the LabVIEW version of the producer/consumer design pattern. I also said that we would be back to talk about it a bit more — and here we are!

The point of the original post was to present the modified design pattern in a form similar to that used for the version of the pattern that ships with LabVIEW. The problem is that while it demonstrates the interaction between the two loops, the structure cannot be expanded very far before you start running into obstacles. For example, it’s not uncommon to have the producer and consumer loops in separate VIs. Likewise, as with a person I recently dealt with on the forums, you might want to have more than one producer loop passing data to the same consumer. In either case, explicitly passing around a reference complicates matters because you have to come up with ways for all the separate processes to get the references they need.

The way around this conundrum lies in the concept of information hiding and the related process of encapsulation.

Moving On

The idea behind information hiding is that you want to hide from a function any information that it doesn’t need to do its job. Hiding information in this sense makes code more robust because what a routine doesn’t know about it can’t break. Encapsulation is an excellent way if implementing information hiding.

In the case of our design pattern, the information that is complicating things is the detailed logic of how the user event is implemented, and the resulting event reference. What we need is a way to hide the event logic, while retaining the functionality. We can accomplish this goal by encapsulating the data passing logic in a set of VIs that hide the messy details about how they do their job.

Standardizing User-Defined Events

The remainder of this post will present a technique that I have used for several years to implement UDEs. The code is not particularly difficult to build, but if you are a registered subscriber the code can be downloaded from the site’s Subversion SCC server.

The first thing we need to do is think a bit and come up with a list of things that a calling program would legitimately need to do with an event — and it’s actually a pretty short list.

  1. Register to Receive the Event
  2. Generate the Event
  3. Destroy the Event When the Process Using it Stops

This list tells us what VIs the calling VI will need. However, there are a couple more objects that those VIs will be needed internally. One is a VI that will generate and store the event reference, the other is a type definition defining the event data.

Finally, if we are going to be creating 4 VIs and a typedef for each event in a project, we are going to need some way of keeping things organized. So let’s define a few conventions for ourselves.

Convention Number 1

To make it easy to identify what event VI performs a given function, let’s standardize the names. Thus, any VI that creates an event registration will be called Register for Event.vi. The other two event interface VI will, likewise, have simple, descriptive names: Generate Event.vi and Destroy Event.vi. Finally, the VI that gets the event reference for the interface VIs, shall be called Get Event Reference.vi and the typedef that defines the event data will be Event Data.ctl.

But doesn’t LabVIEW require unique VI names? Yes, you are quite right. LabVIEW does indeed require unique VI names. So you can’t have a dozen VIs all named Generate Event.vi. Thus we define:

Convention Number 2

All 5 files associated with an event shall be associated with a LabVIEW library that is named the same as the event. This action solves the VI Name problem because LabVIEW creates a fully-qualified VI name by concatenating the library name and the VI file name. For example, the name of the VI that generates the Pass Data event would have the name:
Pass Data.lvlib:Generate Event.vi
While the VI Name of the VI that generates the Stop Application event would be:
Stop Application.lvlib:Generate Event.vi

The result also reads pretty nice. Though, it still doesn’t help the OS which will not allow two files with the same name to coexist in the same directory. So we need:

Convention Number 3

The event library file, as well as the 5 files associated with the event, will reside in a directory with the same name as the event — but without the lvlib file extension. Hence Pass Data.lvlib, and the 5 files associated with it would reside in the Pass Data directory, while Stop Application.lvlib and its 5 files would be found in the directory Stop Application.

So do you have to follow these conventions? No of course not, but as conventions go, they make a lot of sense logically. So why not just use them and save your creative energies for other things…

The UDE Files

So now that we have places for our event VIs to be saved, and we don’t have to worry about what to name them, what do the VIs themselves look like? As I mentioned before, you can grab a working copy from our Subversion SCC server. The repository resides at:

http://svn.NotaTameLion.com/blogProject/ude_templates

To get started, you can simply click on the link and the Subversion web client will let you grab copies of the necessary files. You’ll notice that when you get to the SCC directory, it only has two files in it: UDE.zip and readme.pdf. The reason for the zip file is that I am going to be using the files inside the archive as templates and don’t want to get them accidentally linked to a project. The readme file explains how to use the templates, and you should go through that material on your own. What I want to cover here is how the templates work.

Get Event Reference.vi

This VI’s purpose is to create, and store for reuse, a reference to the event we are creating. Given that description, you shouldn’t be too surprised to see that it is built around the structure of a functional global variable, or FGV. However, instead of using an input from the caller to determine whether it needs to create a reference, it tests the reference in the shift-register and if it is invalid, creates a reference. If the reference is valid, it passes out the one already in the shift-register.

If you consider the constant that is defining the event datatype, you observe two things. First, you’ll see that it is a scalar variant. For events that essentially operate like triggers and so don’t have any real data to pass, this configuration works fine. Second, there is a little black rectangle in the corner of the constant indicating that it is a typedef (Event Data.ctl). This designation is important because it significantly simplifies code modification.

If the constant were not a typedef, the datatype of the event reference would be a scalar variant and any change to it would mean that the output indicator would have to be recreated. However, with the constant as a typedef, the datatype of the event is the type definition. Consequently you can modify the typedef any way you want and every place the event reference is used will automatically track the change.

Register for Event.vi

This VI is used wherever a VI needs to be able to respond to the associated event. Due to the way events operate, multiple VIs can, and often will, register to receive the same event. As you look at the template block diagram, however, you’ll something is missing: the registration output. The reason for this omission lies in how LabVIEW names events.

When LabVIEW creates an event reference it naturally needs to generate a name for the event. This name is used in event structures to identify the specific particular event handler that will be responding to an event. To obtain the name that it will use, LabVIEW looks for a label associated with the event reference wire. In this case, the event reference is coming from a subVI, so LabVIEW uses the label of the subVI indicator as the event name. Unfortunately, if the name of this indicator changes after the registration reference indictor is created, the name change does not get propagated. Consequently, this indicator can only be created after you have renamed the output of the Get Event Reference.vi subVI to the name that you wish the event to have.

The event naming process doesn’t stop with the event reference name. The label given to the registration reference can also become important. If you bundle multiple references together before connecting them to an event structure’s input dynamic event terminal, the registration indicator is added to the front of the event name. This fact has two implications:

  1. You should keep the labels short
  2. You can use these labels to further identify the event

You could, for example, identify events that are just used as triggers with the label trig. Alternately, you could use this prefix to identify the subsystem that is generating the event like daq or gui.

Generate Event.vi

The logic of this template is pretty straight-forward. The only noteworthy thing about it is that the event data (right now a simple variant) is a control on the front panel. I coded it this way to save a couple steps if I need to modify it for an event that is passing data. Changing the typedef will modify the front panel control, so all I have to do is attach it to a terminal on the connector pane.

Destroy Event

Again, this is very simple code. Note that this VI only destroys the event reference. If it has been registered somewhere, that registration will need to be destroyed separately.

Putting it all Together

So how would all this fit into our design pattern? The instructions in the readme file give the step-by-step procedure, but here is the result.

image

As explained in the instructions, I intend to use this example as a testbed of sorts to demonstrate some things, so I also modified the event data to be a numeric, and changed the display into a chart. You’ll also notice that the wire carrying the event reference is no longer needed. With the two loops thus disconnected from each other logically, it would be child’s play to restructure this logic to have multiple producer loops, or to have the producer loop(s) and the consumer loop(s) in separate VIs.

By the way, there’s no reason you can’t have multiple consumer loops too. You might find a situation where, for example, the data is acquired once a second, but the consumer loops takes a second and a half to do the necessary processing. The solution? Multiple consumer loops.

However, there is still one teeny-weensy problem. If you now have an application that consists of several parallel processes running in memory, how do you get them all launched in the proper order?

Until next time…

Mike…