Advantages of Using Alternative Logic Symbols

There is a feature hidden on the LabVIEW function palettes that implements a very useful bit of functionality, but unfortunately is not being used nearly enough. I am speaking of the Compound Arithmetic node which can be found on either the Numeric or Boolean palette. Despite its name, the node does far more than simple “Arithmetic”. In this post we’ll consider how it can and should be used to help us implement logic operations.

Most folks use it when they need to save space while implementing a logic gate with more than 2 inputs, but there is a lot more functionality hidden inside it. For example, have you ever gotten tied up in the logic of what you feel should be a pretty simple operation, but end up spend a lot of time figuring it out? Well this little node can help you define logic using a simple, straightforward process.

Ever look at a logical operation in a piece of inherited code (or worse code you wrote yourself six months ago) and sat scratching your head trying to figure out what the original developer was trying to do? Well this node can help you learn not only what the developer wanted, but what they were thinking.

Augustus De Morgan: The Other Big Name in Binary Logic

I first learned about binary and logic gates while going through electronics school in the United States Air Force. However, when I got out and found my first civilian job, I saw a gate on a schematic that looked like something like this:

Alternative OR Gate

I of course knew what an AND gate was, but what was the deal with all the extra bubbles? When I asked an engineer I worked with about the symbol, he explained that it’s just another way of drawing an OR gate. When I asked why the designer didn’t just draw an OR gate, he explained (somewhat irritated) that the designer couldn’t because an OR gate means something different. So I asked one more question, “Well, if it’s the same as an OR gate how can it mean something different?”. The engineer responded (this time, decidedly irritated), “Because one is an OR gate and one is an AND gate!” At this point he stomped off praying under his breath for some unnamed deity to give him strength when dealing with, “stupid technicians”.

OK, so this mystery gate with all the extra bubbles is just another way of drawing an OR gate, but somehow it also managed to mean something different from an OR gate. In other words, it is simultaneously the same-as and different-from an OR gate. At this point, my head began to hurt as I wondered how functions that allowed this sort of confusion could be called “logic”.

Somewhat later I discovered Augustus De Morgan, and specifically De Morgan’s Law which stated (in the linguistic gobbledygook that mathematicians seem to love):

  • The negation of a conjunction is the disjunction of the negations.
  • The negation of a disjunction is the conjunction of the negations.

Uh, right… or in normal in English: If you draw the truth-tables for an AND gate with all its inputs and the output inverted and an OR gate you will see that they are in fact identical. Likewise, the opposite is also true. The truth table for an OR gate with all its inputs and output inverted is the same as that for an AND gate. Well, that explained the bit about the modified gate symbol being, “…another way of drawing an OR gate.”

But what about the part where they mean different things? Again De Morgan provided the answer, not in the form of another pithily worded law, but in the basic meaning of the word “meaning”. Coming from the background I did, the output of a logic gate was either high or low, on or off, +4.5V or 0V. It never occurred to me that these signals could have a broader meaning — like a low on this input means that something important has happened, or even basic concepts like true and false. This realization made the engineer’s last statement make sense. An OR gate and an AND gate express fundamentally different logical ideas, regardless of how their inputs and output might be inverted.

At its most basic, an OR gate says, I want a given output when any input is in the indicated state. By contrast, the message an AND gate delivers is that I want a given output when all inputs are in the indicated states. So they really do mean different things. Still while this discussion may have been interesting, and perhaps even intellectually stimulating, if there is no practical applications of the knowledge, we have wasted our time. However the preceding discussions has several very practical implications. To begin with, it points the way to the possibility of creating code that not only defines a given functionality, but can actually provide insight into what you were thinking as you were designing and implementing the code. Moreover, due to the way LabVIEW implements the Compound Arithmetic node, you can easily build-up even complex logic operations through a simple 3-step process. To see how this works, let’s walk through a simple example that shows all the steps.

Setting-up the Example

The example we’ll consider is developing the logic that either allows a while loop to keep running or causes it to stop — depending upon your point of view. We know that, by default, passing a Boolean true to the conditional node causes the loop to stop at the end of the current iteration. Now we can change that behavior by right-clicking on the conditional node — but why bother? Here is skeleton of the loop that we’ll be controlling.

Basic Boolean Problem

The goal will be to control the operation of the loop where we have two values upon which to make our control decisions: a scaled random number that is measured against a range of 0 to 975, and the number of loop iterations which tested to determine whether or not it is equal to 5o.

Defining the Output

Our first challenge is to decide what we are wanting to do: Let the loop run until something is true, or let the loop run as long as something is not true? Either one would work equally well, but given the specifics of the code you’re working on, one may represent how you are thinking about the task at hand better than the other. You want to use the one that better fits your thinking.

To implement this first decision, place a Compound Arithmetic node on the diagram and if you are thinking that the loop should run until something happens leave the output as is, otherwise right click on the node’s output terminal and select Invert from the pop-up menu. I’ll invert the output.

Added the logic node

Defining the Core Logic

To this point, we have been talking in generalities about “something” happening or not happening. Now we need to drill down a bit and start defining the “something”. Specifically, do we want the event to be flagged when all the inputs are in particular states or when any of the inputs are in particular states? The answer to this question tells us whether we fundamentally want an AND operation or an OR operation. Looking at the logic, I see the two parameters and I want the loop to continue if they are both valid — an AND operation. Now if you get your Compound Arithmetic node from the Boolean palette, the default operation is OR. On the other hand, if you get the node from the Numeric palette you’ll note that the default operation is ADD. In either case you will need to right-click on the node and select AND from the Change Mode… submenu. While we’re at it, let’s wire up our two conditions.

Correct logic gate selected and its all wired-up

Note that although the run arrow is no longer broken, we aren’t done yet. We still have one more step.

Defining the Inputs

I said earlier that I wanted the gate to test whether all the inputs are valid. So now we need to look at each of the two inputs and identify what the valid state for each. The top terminal is wired to the In Range? output from the In Range and Coerce node. Because this output is true when the input number is in range (and therefore, valid) we will leave its input as is. However the bottom input is coming from logic that is comparing the loop counter to a limit (50). Since that value is a limit, a valid input is one that has not yet reached the limit. Consequently, in this case, a valid number is indicated by the output of the comparison being false. To indicate that fact, right-click on the lower input terminal and select Invert from the popup menu. Here is the finished code.

Logic Example All Done

If you run it you’ll notice that the loop counter indicator will show a variety of values, depending on which of the two conditions stopped the loop, but the count will never be greater than 50.

Admittedly, at first glance the logic gate looks a little strange, but if you deconstruct it by stepping through the foregoing process backwards you can easily what it’s doing. More importantly, though, the structure gives you an insight into what I was thinking when I created the logic. Also note that depending on how you think about what it is that you are doing, there are several other ways the gate could be defined that would give the same functionality.

Until next time…

Mike…

When are UDEs not the Right Answer?

As we have seen, UDEs can be a powerful way of passing data between processes, but they are no Silver Bullet, no Panacea, no Balm of Gilead, no … well, you get the point. Given that we aren’t going to do something silly like only use one method for passing all our data, we need to ask an important question: When should you consider the other techniques?

Knowing What is Important

The reason people choose one thing over another (or at least why people should choose one thing over another) is that they are looking for better performance — however you might define that word. But before we can say one technique is better than another, we need to understand the key features of each technique. With that knowledge in hand we can then apply those techniques in ways where the features work to our advantage, but we avoid problematic side-effects. So, since our present concern is data passing, let’s consider the issue I call, immediacy.

There are times when something needs to happen as quickly as possible after an input value changes. In the hardware world this sort of condition is called an edge trigger and as a system architect working with them you are often primarily interested in when the change occurred. Of course an event can carry data, but the key distinguishing factor for this sort of communications is timing. These are the sorts of signals for which UDEs are an excellent choice because UDEs are very good at telling remote processes that something has changed.

On the other hand there are signals where functionally it doesn’t matter really when they changed. All the code really cares about is what their value is the next time they are used. These are the signals that you could pass with a UDE, but to do so would be wasteful of the receiving process’ time. Remember that when an event fires any process registered to receive that event has to stop what was doing to handle the event and then return to what it was doing before it was interrupted — even though it fundamentally doesn’t care when the value changes. Although these diversions might be small, they do take some time and so can effect loop timing. Moreover, if you have something running in a Timeout event a UDE (or any other type of event) can drastically effect the timing of when the next timeout occurs. Remember that a timeout, of say 1000 msec, does not mean that the code in it will execute every 1000 msec. Rather, the event triggers when the time since the last occurrence of any event exceeds 1000 msec.

In fact, in this situation, if you have a UDE that fires every 500 msec, the timeout will never occur. To summarize this point, a good way to think about this is that an event actually carries two pieces of information: data and timing. What we need sometimes, though, is just the data.

Just the Data

The good news is that LabVIEW offers a variety of options for passing just the data. The oldest (and still very useful) way is with a Functional Global Variable (FGV), also sometimes called a LabVIEW 2 style global. A FGV uses an uninitialized shift register to hold data in memory. More recent additions to our arsenal include feedback nodes, data value references (DVRs) and shared variables.

Your choice between these options should be driven by memory and performance concerns. A FGV is easy to create and very flexible, but if the data being buffered is large, they can suffer performance issues due to copying of memory buffers. A DVR on the other hand, is a little trickier to use because there is a reference you have to pass around, but is very efficient. With large datasets i will often split the difference and combine the two: I will use the DVR to store the data, but use a FGV to buffer and store the DVR reference. Although it is sort of overkill for the data that I will transferring, I’ll show you the technique in the following example.

What We’ll Build

Right now in the testbed application the delay between sampled is hardcoded to 1000 msec. So to demonstrate the passing of non-time-critical data, let’s modify our code to allow the UI process to vary the delay between data samples.

The first thing we want to do is create the buffer we will use to transfer the data. So in the project directory create a subdirectory named _buffers and inside it create a subdirectory named Sample Rate. Finally inside that subdirectory create a LabVIEW library named Sample Rate.lvlib and two more subdirectories called _subVIs and _typedefs. If you have been following this blog from the beginning, this arrangement should look familiar as it is very similar to what we did for organizing UDEs — and we are repeating it here for all the same reasons (unique name space, access control for subVIs, etc.).

The Buffer

The first real code will be the buffer and this is what it looks like:

DVT-Based Data Cache

As promised, it has the basic structure of a FGV, but the global data is the DVR reference. Note also that the DVR’s datatype is a cluster (typedef’d of course) that contains a single integer. The typedef is saved in the typedef directory with the name Data.ctl and the buffer is saved in the subVI directory with the name Buffer.vi. The two subdirectories have been added to the library and the access scope for the subVI directory is set to Private.

Reading and Writing the Buffer

With the buffer itself created we need to add to the library a pair of publicly accessible VIs for accessing it — and here they are. First the read:

Read Write DVR Data Cache

…and now the write:

Write DVR Data Cache

Note that if anything about the data or how it is stored would ever need to change in the future, this one library would be the only place in the code that would be impacted.

Modifying the Testbed

All we have to do now is modify the testbed to add in the logic that we just created, but thanks to the infrastructure that we have created, that will be an easy task. In fact it will take exactly three changes — total modification time, less than 5 minutes.

  1. Add an I32 control to the front panel of the display process and create a value change event for it. In the event handler for the value change, wire the NewVal event data node to the input of the buffer write VI.

    Sample Period Value Change Event

  2. Setup the initialization by adding a property node to fire the value change event using the default control value.

    Initialize Sample Period Event

  3. Add the buffer read to the acquisition VI as shown.

    Modified Acquisition Loop Timing

That’s all there is to it. Another good point for this approach is that because the DVR’s datatype is defined as a typedef, you can make changes to the data in the buffer without recreating everything. In addition, because the data is a cluster, the existing code won’t break if you add another value to the cluster. Go ahead and give it a try. Open the typedef and add another control to the cluster. After you have saved the change note that the the existing code did not break.

Check here for the updated project:
http://svn.notatamelion.com/blogProject/testbed application/Tags/Release 6

Oh yes, one more thing. Remember how we created a zip archive holding the basic template files for a UDE? It might be a good idea to do the same thing for this structure. Until next time…

Mike…

Customers and Other Difficult People

This time I want to take a break from the usual technical “how-to” and talk a bit about another type of “how-to” — dealing with difficult people in a technical environment. Never fear, however, these points are very practical. So here we go:

Customers Lie

Ok, I can hear you already: “But, I have always heard that the customer is always right!” Well in some circumstances that maxim may be immediately applicable, but in the field in which we work it is almost always wrong, unless you substitute one little word. Let’s replace “always” with a more realistic “eventually”. So what do I mean by, “The customer is eventually right”? Let me explain:

When I was very new to this business, I got a phone call one afternoon from a friend who had arranged to so some work for someone, but was not going to have the time to fulfill the contract. He assured me that the job was very simple. All I had to do was go in, develop a simple data-logging program for the customer and leave.

The customer was a doctor doing research at Massachusetts General Hospital. She had gotten her hardware all set-up by the local NI sales representative but needed a simple program built to record the data she got. When I arrived for our appointment she was feeling very rushed and needed the software done very quickly. She said all she needed was for the software to read a new data point once a second and display it in a table on the front panel. This task that took 5 or 10 minutes.

When I showed her the result she was delighted. She affirmed that the program would significantly speed up their work because all her grad students have to do now was copy the numbers off the screen and plot them.

“Oh”, says I, “You need to plot the data?”

“Yes”, she replied, “but we don’t have the time…”

Asking for a moment more, I replaced the table with a graph and re-ran the program.

“My students are going to love you!”, she enthused, “With the graphing done, all they will need to do now is trace the image from the screen onto typing paper.”

“Wait a minute”, says I, “You need to print the graph?”

“Well, yes — but we don’t have the time…”

I think you can see where the conversation was going. In total, we did that dance for the better part of an hour and a half, and in the end, she had a program that did everything she needed. The problem was that her attempt to save time actually resulted in the program taking twice as long to develop because she never actually told me what she needed. The reason for this omission was simple: She had already decided that implementing all of what she needed would take too long and cost too much. Unfortunately that decision was made without information on me, LabVIEW or what can be done with LabVIEW in a short period of time (…even in Ver 1!). Consequently, she didn’t really tell me what she needed, rather she told me what she thought I could accomplish.

Over the years I have come to see that this is a common problem. You see, by the time you walk in the door, many customers are like this doctor. They have been through some sort of internal approval process — which always takes longer than expected — so they start off having less time than they thought they had. Moreover, now they have a dollar figure that they can’t go over.

To prevent this sort of situation, I now always begin all new customer contacts with what I call “The Talk”. During this conversation I point out politely, but firmly, that their job during the initial meetings is to tell me everything they need from the proposed application, both now and in the foreseeable future. The most important part of the process is that they are to hold nothing back. It is only after that information is on the table that we begin talking about schedules and budgets. When you know all the facts, the customer will get the best results and you can do your best work.

Never Ask Permission to do What is Needed

This section is actually something I have been wanting to write about for a while, but wasn’t sure about how to get into it. The problem, of course, is that when misunderstood or applied incorrectly, this point can become an open door for ego to come charging in and muddy things up. Like many things in life the answer lies not in maintaining some sort of “balance” but rather to learn to be comfortable with having two ideas running around in your head at the same time that on might seem contradictory:

  1. I have been doing this work a long time know what needs to be done.
  2. I might be wrong.

For a healthy career (and/or life) these ideas need to be in constant tension because clinging to either one will lead to problems: either arrogance on the one side or fear-induced immobility on the other. So how do you learn to be comfortable with this tension? Well, that is the Big Question that folks have been trying to figure out for millennia. If you have any thoughts on the topic I would be glad to hear them, but in the mean time, here are a couple things that work for me.

  1. Know your standards:
    There are things on which we feel free to compromise. There are also also things for which compromise is not possible for to compromise on those things would be to compromise on who we are. And then there is, of course, that vast gray area between the two extremes. I won’t try to tell you where the lines should be drawn — I’m just going to remind you that there are lines for you to draw.
  2. Always be learning:
    Learning is a great way of “keeping it real”. I read new things that come out, but I also reread things that I first read decades ago. An interest phenomenon that I have noticed is that each time I read certain papers I see things that I didn’t see before. It’s sort of like building: every time you learn something you are laying down a stone, and then standing on that stone you can see further than you did before.

Well, that’s all for now. Until next time…

Mike…

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…