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…

Flashback Thursday

Recently I was moving and in my garage came across something that I thought had been lost years ago. It is a hardcopy of the first thing I ever wrote about LabVIEW for public consumption. The year was 1989 and the event was the first (and I think only) NI User’s Symposium. It was held at the Adam’s Mark hotel in Austin. Reading the paper, I was struck by two things: First, not only have I been using LabVIEW for 29 years, but I have been writing about it for 25 years! Ye Gads!

Second, what that although I alternately cringed and laughed while reading it, I also spent a lot of time nodding. Although the paper is clearly dated, many of the basic concepts have held up very well over the years.

Consequently, I have scanned it into a PDF and have posted it on the site in the White Paper section. Enjoy.

Until next time…

Mike…

PS: Before anybody asks: No, I am not going to post any pictures of me from 1989…

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…