Validating Data Input

Last week we covered the basics of creating a dialog box that doesn’t block the execution of the application that called it. This time I want to expand on that discussion to look at dialog boxes that allow the user to enter data – which in turn brings up the topic of data validation.

What We’ll Build

To demonstrate the various ideas that I want to cover, I have created a dialog box that shows some typical situations. As you can see from this screen shot of the front panel we will look at two ways of validating numeric data, as well as a couple more techniques for dealing with strings.

Number and String Validation FP

Remember, the one thing that is foundational to this discussion is the concept that the Ok button doesn’t get enabled until all the data is valid.

Building the Infrastructure

As you can see from the screen shot below, the basic infrastructure for determining when to enable the Ok consists of a cluster containing one Boolean value (I call them validation flags) for each item that needs to be validated, and for which the user could make an invalid entry. The second part of that definition is important because it’s perfectly feasible for there to be parameters that have no invalid values. Moreover, a couple of the techniques I will show you can actually prevent the user from entering incorrect data so they don’t require explicit validation.

Validation Flag Cluster

Note also that I always use a cluster to store these validation flags because, in a cluster, you can give each value a meaningful name. Consequently, the validation cluster in our example contains two flags named, “Numeric 1” and “String 1”. The other two values will be the ones where the user won’t be able to enter incorrect data.

The key idea behind this cluster of validation tags is that each value needing validation has its own flag. Consequently, the various tests can’t interfere with each other. All you have to do is wait until all the flags show that their associated values are good and you can enable the Ok button. To be consistent with the error logic that LabVIEW uses, a true value indicates a parameter that is in error, while a false indicates no error. To determine the enabled state of the button I created a polymorphic VI that accepts either a single Boolean or an array of Booleans, and outputs the enumeration that the Disabled control property expects. Any true input results in a Disabled and Grayed Out output value.

Getting Down to Specifics

To see how all this fit together, we’ll start with the two values where users can enter incorrect values.

Validating Numeric 1

We’ll start with this input because it is the easiest case. The technique is basically to take the NewVal value from the event data and compare it to the constraints to which it has to conform. In this case, upper and lower limits.

Numeric 1 Validation

If the new data, is not in the desired range, the code writes a true to the input’s validation flag. If there is in range, it writes a false. Once the flag is updated, the cluster is converted to an array and the result drives the polymorphic VI I discussed earlier to determine the Ok button’s new state.

Validating Numeric 2

This input is the first of the “no error possible” solutions but is, frankly not the way I prefer to deal with simple limits — and you’ll see why in a few moments. Note that in this example, the code implementing this method actually exits outside the event loop in the form of two property nodes.

Numeric 2 - no error

The first of these nodes define the control’s behavior if an out-of-range value is entered. To ban invalid data, we want the control to coerce the value to the closest valid number. The second node defines what the high and low limits are. The API for this functionality also allows you to control the minimum amount a value can change. For example, you could define an input that is settable between 1 and 10 in increments of 0.1.

But why isn’t this one of my preferred techniques? This method can ensure that the input is technically valid, but it can’t guarantee that it is what the user wanted. If this field is one of many on the screen, it is too easy for the user to miss it and fail to enter the number that they want. The result is a value that is valid, but nevertheless incorrect. Allowing the presence of invalid data on the screen forces the user to correct the field’s contents.

The best place to use this approach is with data fields where the default value is correct 90% or more of the time. Also, the ranges can also be set at any time — not just during initialization.

Validating String 1

This field is the other one that needs to be programmatically validated, and as you can see it’s not very different from the Numeric 1 example. The test for this string is that it can’t be empty and it can’t be more than 15 characters long.

String 1 Validation

But even this simple pair of constraints can provide us with some food for thought. For example, you can and should tell your users what the expected criteria are for each input. However, telling a user that the string can’t be more than 15 characters long isn’t going to as helpful as you might think it would be. The problem is that most people can’t look at a string that long and tell how many characters are in it. They have to count the letters — which is a pain.

A better approach is to simply manipulate the input such that it will only accept 15 characters. Now I’m not going to show how to do that. Instead I will leave that (as they say in fancy college textbooks) as an exercise for the reader. But to get “the reader” off on the right foot, let’s look at what I’m doing with the other string input.

Validating String 2

For this example I am drawing on an experience where a customer wanted a numeric input that was blank until the operator typed a number into it. The problem, of course, is that numeric inputs always have a number in them. The solution is to create a string control that operates like a numeric input. And to accomplish that, we need to look at each keystroke as the operator is making it and only allow those keys that are valid. The alchemy that allows such magic is the Key Down? filter event.

But you also need to know a bit about how a PC keyboard works. Some of the keys have ASCII values associated with them, like 0x35 for the character “5” or 0x2E for the period. LabVIEW returns the ASCII value of keys in the Char ;event data node. However, some keystrokes return an ASCII code of 0. These are control keys that do things and don’t represent printable characters. Here I’m talking about things like the delete or arrow keys. When you see an ASCII value of 0, the VKey event data value, which is an enumeration, will tell you what key was pressed.

Our event handler will need to manage both. So let’s first consider the ASCII codes we need to handle. Clearly, the 10 digits will need to be accepted (0x30 through 0x39) so when any of those values are returned we will set the Discard? node to false.

ASCI Digit Enables

Likewise if we are to accept floating point numbers we need to be able to type a period as well — but we can only have one so we need a tiny bit more logic.

ASCI Period Enable

So we’re done right? Not really, no. To begin with, you’ll notice that there are two other ASCII codes in the same case as the numeric digits: tab and backspace. They are there because while your users are entering information, they still need to be able to navigate the screen, so these two codes are also needed — and several more besides that fall in the area of control keys. Check out the code in the repository…

Summing up…

So that’s all I have for now. I do however want to point out that the last technique I showed you is very powerful and I have used it many time to “synthesize” custom inputs that don’t natively exist in LabVIEW, like an IP address entry field. The modified code is in SVN at:

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

and

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

Check it out and if you have any questions, post ’em.

Until next time…

Mike…

Adding a Non-Blocking Dialog Box

Key to using event structures effectively is to keep things moving. This maxim applies with any sort of event-driven structure — even (especially!) XControls. So if you don’t want to ever stop an event loop, you can see how you are going to have problems with dialog boxes. However, the last time we were together I alluded to approaches that provide the functionality of dialog boxes, but without the execution blocking that normally occurs. This post will start the discussion of how to implement this functionality by creating a simple “Are you sure?” -type dialog; in a later post we will expand on this idea to include dialog boxes that support the entering of setup or configuration data.

The Basics

The first idea you need to get your head wrapped around is that a dialog box is just another LabVIEW program, not unlike any other program you write. The only significant difference is that when this program runs, it looks like a dialog box. So what makes a good “dialog box” program? In addition to the usual traits of a good program, there are a few that are unique — or at least have a special significance for dialog boxes:

Uncomplicated

No program should be inherently complicated, but with a dialog box this point is particularly important. Your users should be able to look at the dialog and easily determine what information the system expects and/or wants from them. To aide your users in making that determination, don’t be afraid to state on the front panel exactly what you expect from them, but no bloviating: keep it pithy. If you feel there are aspects of the operation that need long-form explanations, considering having a pop-up help screen that provides all the gory details.

You might, however, also consider whether the task at hand is too large for a dialog box. You don’t want to try to do too much in a single dialog box — or at least on a single dialog box screen. A very effective technique for dealing with complexity can be to create a “wizard” interface, where the user is logically stepped through a large task. Just be careful how many screens you give the wizard, more than 3 or 4 and the user can lose track of where they are in process.

Finally, I have seen applications where the program’s basic user interface was one (very large) dialog box. While I understand that the goal of such a misguided effort is often to prevent the user from gaining access computer desktop, this sort of structure is very bad form. To begin with, the goal itself is problematic in that it complicates working with the code, but more importantly it never really works very well anyway.

Instills Confidence

One of the things that many people don’t think about is that sometimes the very presence of a dialog box can cause uncertainty — like what happens if I inadvertently select the wrong thing and a dialog box I wasn’t expecting opens. I might press the wrong button, and then what?

There are a couple of ways of preventing this sort of stress. First, given that the dialog is really needed (sometimes a static configuration setting is just as good — or even better!) write the code such that nothing changes in the application’s state until the “OK” button is pressed. I know, this sounds like a small, common-sense thing, but users can get worried if they see things starting to change in anticipation of a button selection that they haven’t yet made. However, it can complicate life for the developer as well. What happens if the user doesn’t click “OK” but “Cancel” instead? Now you have to change everything back to the way it was — assuming of course that it is possible to get the system back to where it was before. I have seen troubles caused when an external input change caused the device under test (DUT) to change it’s internal state in a way that couldn’t be easily reversed.

The second way of instilling confidence is to have a robust “Cancel” button. By that I mean that pressing “Cancel” should undo any modifications that are pending so no change is made in the application’s state. “But,” you might ask, “what about applications where changes have to be made along the way?” That is a valid point. I have written dialog boxes that served as an interface for editing system setups that, for various reasons, had to be saved as the user went along. The correct solution in those cases is not to have a “Cancel” button. Instead have one labelled, “Done”. The logical implication now goes from, “I am abandoning all my changes” to, “I am finished making changes”.

Before we move on, consider that this point brings up a larger issue: Always make sure the buttons are labelled for what they do. In many cases, buttons relate to verbs, things you want to do. However, in others, they may answer a question, like, “Are you sure you want to quit?” In that situation, Buttons labelled “Yes” and “No” are appropriate, “OK” and “Cancel” are not.

Provides Validated Output

A common use of dialog boxes is to enter configuration data. However, this sort of usage means that it is possible for the user to enter something that is incorrect. There are a number of ways to address this issue, many of which are just good user interface design; but some do involve the logic of the dialog box itself. For example, you should always design data-entry dialog boxes such that they don’t let the user click the “OK” button until all the data they have input has been validated.

Sometimes this validation procedure is simple, other times it will be complex with many interactions between various input values. Regardless of how simple or complex, this validation is worth the effort because working proactively to prevent an error is always easier than dealing with the error after the fact.

Our First Dialog

So with these points in mind, let’s get back to the issue that we ended with last time: How can we verify the user’s desire to shut down the application without blocking the execution of the code?

Well, the first thing we need to do is make a decision. To wit: Should the dialog box be a reusable VI that could be used anywhere a two-button dialog box is needed? Or should it be a custom design that is only used in this specific place in the code? To tell the truth, there are good arguments to be made on both sides. One line of reasoning says that using a common dialog simplifies the process of creating a professional, standardized interface. Plus, the code is reusable. The other school of thought asserts that quitting the application is a major operation and that, while you want the verification dialog to be of a similar style, you do want it to be different enough to reduce the likelihood that the user will inadvertently hit the wrong button and quit the application.

I typically prefer the second approach, so that is the one that we will take – to start with, at least. Here is the block diagram of the dialog box VI itself.

Simple 'are your sure' Block Diagram

You’ll first note that the diagram is missing one thing that you typically see wrapped around an event structure: a While loop. If you think about it, however, this omission makes sense. When you get right down to it, the whole purpose of this code is to determine whether to fire the Stop Application UDE to shut down the application, or bypass the event and let execution continue. For this reason the VI’s front panel only has two buttons (one for each potential outcome) and clicking either one will stop the execution of the VI and close its front panel. The event handler shown deals with the case where the user affirms their choice to quit, it contains the event generator for the Stop Application UDE. A second event handler (which is empty) is for bypassing the shutdown.

However, before the event structure is reached, the code first registers to receive the Stop Application event. The justification for this added code is that there is actually a third situation that the VI needs to handle. What if while this dialog box is open, Windows starts to shut down? Many devices like uninterruptible power supplies have the ability to shut down Windows automatically when needed. You don’t want the shutdown process to hang-up while the user contemplates the dialog box. By registering to receive the Stop Application event, the application will shutdown even if the dialog is open. The event handler for the UDE is empty like the one for the “No” button.

The front panel for the dialog box is as uncomplicated as the block diagram:

Simple 'are your sure' Front panel

The only non-visible settings are in the VI Properties where I used the predefined Dialog setting for the VI appearance, and used the Run-time Position properties to center the VI’s front panel in the primary monitor.

Launching the Dialog

Now that we have a dialog box that will happily run as a separate process, we need a way to get the VI loaded into memory and launched. There are two basic techniques, each with their own pros and cons. In a future post, we’ll compare and contrast the two options, but for now we’ll just use asynchronous call-and-forget, since it is very easy.

Simple Dialog Box Launcher

The code starts with a static reference to the dialog box VI. This reference node loads the VI into memory, but does not reserve it for execution. The subsequent property node uses the reference to get the VI’s name, and the name drives the Open VI Reference node. The input to which the name is attached normally expects a complete path to the VI but if the VI is already in memory, the name is all that it needs — and the static reference holds the VI in memory. The 0x80 option input value tells LabVIEW that the asynchronous run will be using the call-and-forget method so it doesn’t have to wait for execution to finish before going on. Finally, the Start Asynchronous Call starts the dialog box running. One last screen shot shows the display process modified to use the non-blocking dialog box. You will note that I also modified the logic for handling the Stop button event in the same manner.

Fixed Panel Close Event

When you run this version of the code, you’ll notice that when the dialog box opens, the display continues updating. The modified project is available at:

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

That’s enough for now. When we again get together, I’ll expand on this basic dialog box structure to show how to qualify input in a more complex dialog.

Until next time…

Mike…

Handling Shutdown Situations

The very essence of event-driven programming in LabVIEW is the ability to handle occurrences in the application that are not directly related to, or synchronized with, other things happening in the system. As we have seen in previous posts, the proper handling of these asynchronous events is critical to an application’s success. So far we have covered events arising from such things as the user changing a control value, one part of an application using an event to signal another process to do something, and time-based events that fire periodically.

However there are other sources for events that you need to be aware of, not the least of which are other parts of LabVIEW and even Windows itself.

Incoming Windows Events

Starting with Windows, this event source shouldn’t be too surprising. After all every modern operating system incorporates some type of messaging scheme for communicating with the applications that they are running. Now to tell the truth, most of the messaging that Windows does is not very useful for what we do, but there is one very large exception. When a user tells Windows to shut down or restart, one of the first things that Windows does is broadcast a message that tells all applications currently running that they need to quit. This message (which, by the way, also fires when the task manager tells a program to stop) is important because it allows programs to come to a controlled, graceful stop. When all you’re doing is calculating a spreadsheet, a “controlled, graceful stop” is a convenience. However if your application is controlling the operation of potentially dangerous equipment, an orderly shutdown is an absolute necessity.

Because LabVIEW applications tend to be doing this sort of thing on a regular basis, the language designers have chosen to expose this message to us in the form of the Application Close? filter event. Basically what happens is that when Windows generates the shutdown message, LabVIEW, or the LabVIEW runtime engine, catches the message and passes it on to any user-program that has registered to receive the associated event. From this point on, the event is handled however that code sees fit. If no program has registered for the event, LabVIEW performs an abort to stop all running code. The thing to remember is that using abort to stop a program is like stopping your car by running it into a tree: It works, but there can be negative consequences.

To prevent these “consequences”, every application should have a process that is registered to catch the Application Close? event. The good news is that this logic is very easy to implement — assuming of course that you have written your program such that it is easy to stop. The only other thing you really have to decide is where to put the code. There are arguments that can be made for putting this event handler in a variety of places, but the spot I prefer is in the Exception Handler process. It is after all, the centralized spot for handling things that would interfere with what the program is normally doing. You know, things like errors or shutdown commands from the OS.

This is what the event handler looks like added to Exception Handler.vi in our testbed application.

Application Close Event

As you should expect, there isn’t a whole lot to look at: When Windows sends a shutdown message, the error handler fires the internal Stop Application UDE to shut down our application. In addition, it discards the original Application Close? event to prevent it from having any further effect in the LabVIEW world.

Closing the GUI

The other shutdown situation that you should always handle is the case where the user tries to stop the application by clicking on the close box in the upper right-hand corner of the screen. Conceptually, the case is very similar to the previous one, but with one big exception: When the operator tries top close the window, actually stopping is optional. When Windows starts closing, your application will be shutting down. The only question is the circumstances: will it be a systematic shutdown or an abort.

Due to this difference, it is not uncommon for the logic that handles this event include a prompt to the user asking them if they are sure that they want to quit. If you do decide to include a dialog box, make the prompt short and easy to understand. I once saw a dialog that gave stated:

“Are you sure you don’t want to not abort?”

In any case, here is the code for implementing a handler for the Panel Close? event. It is, of course located in the GUI process — the only place it would make sense, right?

Panel Close Event

You will notice, however, that there is a slight problem here. Namely, as soon as the dialog box opens the screen updates stop. You might not think that this is much of a problem because you are, after all, stopping the application. But what if the user realizes their mistake and decides to continue? Now you have lost (or at the very least, distorted the timing of) perhaps several seconds of data. Moreover, what happens if during that time something important happens to which the software needs to respond? Well, that doesn’t happen either. The problem is that the dialog box is “blocking”. In other words, until the user responds to the dialog, further execution of the loop is blocked.

Never fear though, there are ways around this problem that retains both your ability to prompt users for input, and prevents the blocking of your program’s execution. And we’ll start looking at those solutions in the next post. Once said fix is implemented I’ll update SVN.

Until next time…

Mike…

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.

Give Them Something to Watch

In one of our recent conversations together, I mentioned a “magic number”: 200-msec. You may recall that 200-msec is important because that is the amount of time that most people will wait before getting concerned that there is something wrong with the program they are using. To help address that (very-short) deadline, we created an architecture that implements a splash screen the point of which is to keep the users’ eyeballs busy while the application loads. The code works well “as is” for small fast-loading processes, but what about processes that might take several seconds to load? Or applications with a large number of processes that need to be launched?

This time out I want to look at a couple of techniques that will help in those more-demanding situations as well.

Spinning Cursors

In a the computer world, a universally-recognized way for a program to tell the user, “Wait a minute, I’m thinking” is the busy cursor. Whether a rotating hour-glass or a spinning wheel, people recognize the symbolism. To support this technique, LabVIEW will occasionally show the busy cursor on its own. However, these “automatic” cursor changes don’t always fall where we would like them, so LabVIEW also provides a set of VIs for managing cursors. The two basic VIs you will want to use are Set Busy.vi and Unset Busy.vi which allow you to turn the busy cursor on and off. Normally, you can just drop them down on a block diagram, wire-up the error clusters, and you’re good to go.

However, Set Busy.vi has a couple optional inputs that can be useful in specific situations. For example, by default, the busy cursor displayed is the system default defined in your Windows display theme. However, you can also specify one of your own. The top-most terminal on the left side accepts a cursor reference that allows you display an alternative cursor. To derive this reference, the cursor palette also includes a function called Create Cursor From File.VI that allows you to open .ani or .cur files.

The second terminal down the left side of Set Busy.vi is for a VI reference. If left unwired, LabVIEW defaults to the current VI. This input is useful in situations where this subVI isn’t on the block diagram of the open window. However, it can also be handy if you should happen to have more than one window open, but you only want one of them to show as being busy.

Finally, the third terminal down is a Boolean input that allows you to force LabVIEW to continue to register mouse clicks while the cursor is showing busy. Again, you may not want to use this input all the time, but I have seen applications where you can abort a long process by clicking anywhere in the window. This input provides a mechanism for implementing that functionality. All you have to do is create a Mouse Up? event on the front panel as a whole.

Changing Labels

As you would expect, people who begin to worry about system responsiveness after only 200-msec, can only stare at a spinning cursor for so long before they start to worry again. There are a variety of ways addressing this issue. Progress bars are nice, but can be a pain to create such that they update nicely. An alternative that I have used, that is easily expandable, is to simply take the name of the process being launched, strip off the file extension and put the rest in a string indicator on the front panel.

This approach has the added benefit of providing potentially useful troubleshooting information. For example, say your application really does lock-up or crash or something. The name on the screen tells you what it was trying to do when its world came to an end.

I have also modified this basic approach to make the indicator larger and give it a scrollbar. Then when updating it I prepend the new data to the beginning of what was already there. Now instead of simply showing the last process launched it will display a complete log of all the processes and the order in which they started — again a boon in troubleshooting. You could even pass a reference to the indicator to the processes so they can post information to it while they are initializing themselves.

In any case, here is what the launcher looks like after both of the modifications we’ve discussed (no SVN update this time):

Launcher process, with more user feedback

One additional and interesting point that I want to point out is that if you compare this code to the previous release, you will notice that the delay following the launch is longer — in fact I doubled it. Why? Well, therein lies a bit of a paradox and its all about user perceptions. You see these same people that go through life with a 200-msec fuse on their patience also sometimes get unhappy if things happen too fast…

People don’t like it when things blip past too fast so I slow the loop down. As a customer once told me, this simple change gave him confidence that, “…the computer is really doing something…”. However, as illogical as that argument might seem, I didn’t complain. After all it set-up a situation where a couple months later one of the other users expressed a concern over how long it took to launch the application. Of course I was able to impress him greatly by being able to deliver an “optimized” version the very next day.

Yes, all I did was change the timeout; and no, I didn’t charge them.

Until next time…

Mike…

Adding Basic Error Handling

Now that we have a basic application running, there is something important we need to add. The first release of the testbed application makes a mistake that is common too much commercial LabVIEW code: It assumes that nothing will ever go wrong. To address that failing, we are going to look at a couple refinements that will report and record errors, as well as provide the hooks for later refinement of the error handling process.

Startup Errors

The acquisition process right now is simply passing to the display process a random number. Consequently, the acquisition needs no initialization — and so presents no real opportunities for an error to occur. However, if you decide to use this testbed as a basis for a real-world application, you will discover that most kinds of acquisition will require some sort of initialization that needs to occur when the process starts.

Now the most obvious place to put this initialization is before the event registrations. Unfortunately this placement can result in a situation where an error in the early parts of the initialization can keep your events from registering so you have no way of stopping the process — or event reporting that an error has occurred.

You could, of course, put the initialization that can generate errors after the event registrations, and so preserve the functionality of your events. As it turns out though, that move really isn’t very helpful because you still don’t have any way of reporting the error, stopping the process or trying to reinitialize the code. Remember it is perfectly possible to have transient problems like an instrument was turned off, or there was a momentary network outage.

What I want to show you is an easy way to leverage something you already have in the code to handle initialization errors. I’ll first show you an easy technique that simply stops the process and closes it. This technique is useful for many situations. Then I’ll discuss how to build on that technique to implement a more complex approach that supports retrying the initialization process. Both techniques are based on the fact that all event structures can have a timeout event.

Stop Process on Errors

Implementing the first technique, is easy because the event structure already has a timeout case in it. All we have to do is add a little functionality to it. Since the timeout right now is fixed at 1000 msec, the first thing we need to do is provide a way of changing the value being passed to the timeout terminal. The idea is that we will carry the timeout value in a shift register that is initialized with a zero. This setting means that the first time through the loop, the timeout will fire immediately. Inside the timeout event, if the timeout value for the current iteration is zero, the code knows it just finished initializing and so does nothing but check the state of the error cluster. If it is showing an error, the process quits. This case also sets the timeout value shift register to the normal 1000 msec value so if there are no errors, the loop will continue with its normal operation. This is what the check for initialization errors looks like:

Acquire Data w-error handling

The default case (not shown) for the inner case structure contains the code that is currently in the timeout event case.

Retry on Error

I haven’t implemented the complete retry technique since it tends to be very specific to what a given process is doing, but based on the previous simple example it isn’t hard to visualize some of the tweaks the code would need.

To begin with, in the simple example there were only two possible states for the timeout case to address:

  • The VI has been initialized
  • The VI has not been initialized

Because there are only two possibilities, it is logical — and valid — to essentially “encode” the knowledge about whether the VI is initialized in the timeout value, where a zero means it is not initialized and any other value means it is initialized.

Now however, the situation is more complex because, in this case, there can be two reasons why the VI in not initialized. It could be because the VI is just starting, or it could be because the last attempt at initialization failed. Additionally, if a previous initialization attempt has failed, you might not want to have the code sit there forever retrying, so the code needs to know how many time has it has failed. Finally, if you are going to retry the initialization at some interval, you need to be able to define that interval without restraint.

Clearly, we are going to need more than one value to represent all that information, so I would recommend two numbers (in separate shift registers). One would be the timeout value and it would have no meaning other than how long to wait for the timeout. The second numeric value would encode the retry state like so:

  • ..-1 = The VI has been initialized
  • 0 = All retry attempts have been exhausted
  • 1.. = The number of retry attempts remaining

It is this new second value that would now control the inner case structure such that the acquisition code would go in the ..-1 case and pass both shift register values through unmodified. The 0 case would stop the loop, and the 1.. case would attempt to initialize the VI. If the attempt fails, the logic would set the timeout to the retry timeout value and decrement the value controlling the case structure. If the attempt is successful, the logic would set the timeout to the acquisition timeout value, and set the control value to -1.

And remember, this example is but one of many possible variations on the theme.

Displaying and Recording Errors

Having collected errors, your application also needs a mechanism for displaying them to the operator and/or recording their occurrence. I have seen approaches that try to decentralize this functionality but for many reasons the results are often less than ideal. Chief among these reasons can be additional errors resulting from trying to access a common resource such as a file or database from multiple locations within the code. The solution to this conundrum is to create a separate process, the sole purchase of which is to report and display errors. That is what I am presenting here:

Exception Handler

As you can see, the code is very simple. When something fires the error event, the parameters needed to make the error handler VI work are read from the system configuration information. This information consists of what the VI should do with the error, and the labels for the dialog box buttons. The error handler VI itself (Show Error.vi) is a simplified version of the error handler that ships with LabVIEW. For safety reasons, the VI no longer has the ability to abort the running application.

The process’ event structure only services three events:

  1. The error event (shown) — <sys:System Error>:User Event
    This event is fired from other processes when they detect an error in their operation. The first VI it calls can either display the error to the operator in a dialog box, save it to file or both. The output from the VI is an enumeration that specifies whether to continue execution of the application, or command a shutdown. If the reporting VI presents the error in a dialog box, this output is set in accordance with the user’s selection. If the reporting VI is only recording the error, the output is always Continue.
  2. The application stop event — <sys:Stop Application>:User Event
    This event fires whenever the application is stopping. The only thing unique about this process’s implementation of the event handler is that before stopping itself, it pauses and waits 5 seconds for all the other process to at least get started on their shutdown operations.
  3. The Stop button value change event
    Operationally, there is no real reason for this button, the front panel will after all be closed. Its main use is to assist in troubleshooting or preliminary testing where the formal shutdown logic may not be completed. It simply fired the shutdown event.

But how do you pass errors to this error handler process? Like all events, the Handle Errors event has a Generate Event.vi. In this case, however, the operation is tweaked slightly.

Generate Error Event

Because we only want the event generated when an error occurs, the code is structured such that the event is only fired if there is an incoming event; in which case, the data for the event is the incoming error cluster. The output error cluster, however, is the result of the event generation. If there is no incoming error, the error cluster is passed through unmodified.

The other variation for typical for this VI is that because it is likely to be used in multiple locations, I have set its execution to be shared clone reentrant. This causes LabVIEW to preallocate a pool of clones that can be used as needed. This setting is useful in situations where there is code that you don’t want instances blocking each other, but don’t need a unique memory space between instances.

The event generator is placed in the acquisition and display VIs and the last thing to do before the loop repeats. The testbed code with the two modifications discussed in this post is available from:

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

You should know the drill for grabbing a copy of it by now.

Until next time…

Mike…

A “Finished” Testbed Application

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.

  1. 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.
  2. 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.

Launcher

    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.

Acquire Data

    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.

Display Data

    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…

Getting Everything Running

I left you last time stuck on the horns of a dilemma. To wit: It’s not very hard to create LabVIEW applications structured as multiple parallel processes that are for the most part operating independently of one another. However, now you are going to need some way to launch all those independent processes and manage their operation.

These are some of the issues that we’ll deal with this time, but first you may be wondering if this methodology is really worthwhile. Specifically, what benefits are to be garnered? In a word: modularity — on steroids (Ok, that’s three words. So sue me).

The Case for Autonomous Processes

A few posts ago, I dealt in broad terms with the issue of modularity and mentioned that effects both the design and implementation of an application. Consequently, modularity has implications that can reach far beyond simply the structure of an application. Modularity can also effect such diverse, and apparently unrelated, areas as resource allocation and even staffing.

For example, let’s say you have an application that needs to acquire data, display data, and manage system events that occur. Now let’s say you structure this application such that it has three loops on the same block diagram — or worse, one big loop that tries to do everything. How many developers can effectively work on the application at once? One.

Oh you might be able to split off a few random tasks like developing lower-level drivers, or prototyping the user interface, but when crunch time comes the work will fall to one person. Heaven help you if that one person gets sick. Or that one person gets hit by a bus. Or that one person gets tired of dealing with the stress caused by being the project critical path incarnate and quits.

And don’t forget that one developer is a best-case scenario. Such projects can easily turn into such a mess that there is no way anyone can “effectively” work on them. If you get thrust into such a situation, and you don’t have a manager that really and truly has your back, the best you can hope for is to survive the encounter. (And, yes. I am speaking from personal experience.)

But let’s say that you decide use a different modularization. Let’s say that you break the application into three independent, but interacting, processes. Now how many people can effectively work on the project at once? Three. Likewise, at crunch time how many people can assist with integration testing and validation? Three — and each one with a specific area of expertise in the project domain.

Oh, in case you’re wondering, “three” is a worst-case scenario. Depending how each of those three pieces are structured, the number of possible concurrent operations could go much higher. For instance, the lead developer of the DAQ module might decide to create separate processes for DAQ boards and GPIB instruments; or the GUI developer might identify 4 separate interface screens that will run independently, and so can be developed independently. Now you are up to 7 parallel development tasks, and 7 pairs of hands that can pitch in at crunch time.

So in terms of justification, ‘Nuff said… All we have to do now is figure out where to put the logic that we need.

Making a Splash [Screen]

Did you ever wonder why so many large applications have big splash screens telling you about the program you are launching? Is narcissism really running rampant in the software industry? Or is there a little slight-of-hand going on here?

Part of the answer to those questions lies in a magic number: 200. That number is important because that is the approximate number of milliseconds that average users will wait for a program to respond before they begin to wonder if something has gone horribly wrong. Any way you slice it 200-msec isn’t remotely long enough to get a large program loaded into memory and visible to the user.

So what is a developer to do? As quickly as possible put up an appealing bit of eye-candy the implicit (and occasionally explicit) purpose of which is to congratulate the user for having the wisdom and foresight to decide to launch your program. Then while the user is thus distracted, get everything else loaded and running as quickly as you can.

That is why programs have splash screens. It also points to one of the unique aspects of a splash screen: Unlike most VIs, what you see on the front panel of a splash screen often has little or nothing to do with what the VI is actually doing. Or put another way, splash screens are a practical implementation of the concept, “Ignore the man behind the curtain!”

Designing a Launch Pad

As you should expect from me by now, the first thing to do is consider the basic requirements for this launcher VI and list the essential components:

  1. A list of processes to be launched. As a minimum, this list will provide all the information needed to identify the process VIs. However it must also be expandable to allow the inclusion of additional runtime parameters.
  2. A loop to process the launch list. This loop will step through the launch list one element at a time and perform whatever operations are needed to get each process running. As a first-pass effort, the launcher must be able to handle reentrant and non-reentrant processes.

Because the loop that does the actual launching is pretty trivial, we’ll concentrate our design effort on a discussion of the data structure needed to represent the launch list. Clearly, the list will need to vary in size, so the basic structure will need to be an array. Next, to properly represent or describe each process to be launched (as well as any runtime data it may require) will probably require a variety of different datatypes, so the structure inside the array will need to be a cluster — and a typedef’d one at that. It is inevitable that eventually this data will need to change.

To determine what information the cluster will need to contain, let’s look at what we know about the call process that we will be using. Because these processes will be operating independently from one another, the launcher will use the Asynchronous Call-and-Forget method. To make this technique work, we will need the path to the VI so the launcher can load it into memory, and something to identify how each VI should be run. Remember, we are wanting to support both reentrant and non-reentrant processes. While the launch processes that the two types of VIs require are very similar they are not identical, so at the very least we need to be able to distinguish between them. But what should the datatype of this selector be? Most folks would figure that the selector only has 2 possible states, and so they would make it a Boolean…

…and most folks would be wrong.

There are 3 reasons why a Boolean value is incorrect:

  1. It is inherently confusing. There is no natural or intuitive mapping between possible logical values of this parameter and the true or false states available with a Boolean.
  2. It runs counter to the logical purpose of a Boolean. Boolean controls aren’t for just any value that has two states. They are for values that can only have two states. This selector coincidentally happens to have two states.
  3. It will complicate future expansion. There are other possible situations where the technique for using a given process might vary from the two currently being envisioned. When that happens you’ll have to go back and change the parameter’s fundamental datatype — or do something equally ugly.

The correct solution is, of course, to make the value a typedef enumeration with two values, “Normal” and “Reentrant”.

Handling Paths

While we are considering the data details, we also need to talk a bit about the VI path parameter. At first blush, this might seem a straight-forward matter — and in the development environment it is. The challenge arises when you decide to build your code into a standalone executable. To understand why this is so, we need to consider a little history.

With LabVIEW versions prior to LabVIEW 2009, executables that you created stored files internally in what was essentially a flat directory structure. Hence if you wanted to build a path to a VI inside an executable it was just the path to the executable with the VI name added to the end, like so:

C:\myProgram\myProgram.exe\myProgram.vi

This simple structure made it very easy to build a path to a VI and worked well for many years. However, as the LabVIEW development environment became more sophisticated, this simple structure began to exhibit several negative side-effects. One of most pernicious of those effects was one that nearly everyone tripped over at least once. The path of a VI in an executable, while simple to build, was different from the path to the same VI in the development environment.

To fix this problem (and many, many, many others) LabVIEW 2009 defined a new internal structure that more closely reflects the directory structure present in the development environment. Thanks to this change, once you know the path to the “project directory” inside the executable, the logic to building a path to a VI is exactly the same, regardless of the environment in which it was running. The only trick now is getting to that internal starting point, and that can be a bit tricky depending on how you have VIs stored on your computer. Being basically a simple man, when approached with a requirement such as this I prefer an uncomplicated approach — and what could be less complicated than a functional global variable?

Because the launcher VI is always in the project root directory, writing its path (stripped of the VI name, of course) to a FGV provides a fast shortcut for any process or function that needs to build an internal path. Note that this expedient is only needed for LabVIEW files that are located in the executable. For non-LabVIEW files, the Application Directory node provides the same logical starting point for path building.

Managing Windows

Finally, if you implemented an application with all the features that we have discussed to this point you would have an application that would acquire data in the producer loop, and seamlessly pass it to the consumer loop. Unfortunately, you wouldn’t be able to what it was doing because there wouldn’t be any windows open.

By default, when you dynamically call a VI the target of the call doesn’t automatically open its front panel. This behavior is fine for the acquisition VI, because we want it to run unseen in the background anyway. However, it also means that you can’t see the data display. To make the display visible we need to tweak a few VI properties for the display VI. To make this sort of setting easy, the Window Appearance portion in the VI properties dialog box provides a couple, predefined window definitions. The one that will get you what we want is the top-most radio button labeled Top-level application window. This selection sets a number of properties such as turning off the scrollbars and setting the VI to automatically open its front panel when it runs and closing it when it finishes.

Note that the Window Appearance screen also allows you to specify a runtime title for the window — an optional but professional touch to add to you application. This setting only defines a static title, you can also set it dynamically from the VI using a VI property node.

So that’s about it for now. The next time we get together I’ll introduce how I have applied all the infrastructure we have been discussing to the “testbed” application introduced in the post on UDEs.

Until next time…

Mike…

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…

Don’t re-code when you can re-configure

A common problem that you see being discussed on the LabVIEW user forums comes from people who hard-code configuration data in their programs. Often they will be complaining about things like how long it takes to maintain all the different versions of their application, when in truth the only difference between “versions” is the contents of a few constants on a block diagram. Ultimately, these folks may realize that putting configuration data is constants is not good, but they may feel like they are skilled enough, or don’t have enough time, to do anything better.

i suppose that the fundamental trouble is that it is way too easy to drop down a constant and type in some configuration data. Unfortunately, you can end up in a situation where you are constantly having to go back and manipulate or manage this “easy” solution. The good news however, is that it is not hard to come up with a really good way of handling configuration data.

Data Abstraction

The first step to really fixing the problem is to draw a distinction between two things that we typically hold as being the same. Namely, what a particular parameter represents or means, and the parameter’s value. What we need to do is develop a mechanism for obtaining parameter’s value based on what that value represents. Or to say it differently, we need to replace the constant containing a set parameter value with a VI that can return a value based on the parameter’s logical identity. I want to be able to say, for example, “What is the name of the directory where I should store the data files?” and have this VI return the parameter’s current value. Assuming that the configured value is stored outside the code itself, you can change that value to anything you want, anytime you want, without modifying a bit of code.

In terms of where to store this configuration that is outside the code, there are several options:

  • A configuration file on disk
  • Local or networked database
  • The Windows registry

Each of these options has a place but at this point the important point is that you have it somewhere outside the code. Once you have accomplished that, changing the data’s location from say an INI file to a database is comparatively trivial. With that point in mind, here is the code that I use to read data from the application’s ini file. I will put this code is a VI named for the parameter it is reading and use it in place of the constants that I would otherwise use. To make it easy to reuse, you might want to put it in a VI template file. The remainder of this post will concentrate on how the code works.

ini-based parameter reader

The first thing you’ll notice is that it takes the basic structure of a self-initializing functional global variable, or FGV. Remember that the point of this VI is to replace a constant, but you don’t want to have the code re-reading the configuration file every time that it is called. Using a FGV structure allows the code to automatically load the required data the first time it’s needed, and thereafter use the buffered value. Note that if the file ini file operations fail for whatever reason, the logic will also attempt to reread the file each time it is called. Feel free to remove this logic if you desire, but it can be useful.

Next, consider how I derive the path to the ini file. This logic is based on (and simplified by) the conventions that I follow for naming things. All the files associated with a project like my “Ultimate Data Logger” will reside in the directory:

c:/workspace/projects/Ultimate Data Logger

Likewise, the name of the project file itself will be Ultimate Data Logger.lvproj, the name of the top-level VI will be Ultimate Data Logger.vi, the name of the executable that is eventually built will be Ultimate Data Logger.exe and it will be installed in the directory Ultimate Data Logger on the target machine. Moreover, due to the way that Windows works, the name of the ini file associated with that executable must be Ultimate Data Logger.ini. Consequently, simply knowing the name of the application directory will tell me the name of the ini file. All I need to do is add the “.ini” file extension.

When using this logic, you will need to define the desired data value’s logical identity. In the context of this VI, that identity is defined by the Section and Key values on the block diagram. In this definition, the Section value should be seen as a broad category that includes several Keys or individual values. For example, you might have a section “file paths” that includes all the paths that your application needs so in that section you would find keys like “data file destination” or “import directory”.

Obviously, you will also need to change the datatype of the data being obtained from the ini file. This job is easy because the low-level configuration file VIs are all polymorphic and so will adjust to the datatype you wire to them. A couple of recommendations I would offer is to not store paths as paths, but rather convert them to strings and then save the string. Either way will work, but saving paths as strings produces a result that is more human-readable in the ini file. Likewise, when you are working with strings, there is an optional input on the VI that tells the function to save and read the data as “raw” strings. Always wire a true Boolean constant to these inputs.

Finally, note that the code includes the logic to create the section and key in the ini file, if they do not already exist. This is something that I have found useful, but if you don’t feel free to remove it as you see fit.

This basic structure works well for parameters that have single values, but what about if you have a parameter that is associated with a several values, like a cluster? That’s a situation we will deal with — real soon.

Until next time…

Mike…