How to Make Dynamic SubVIs

The last two posts discussed different ways to use dynamic linking and calling in situations where you want the target VI to run in parallel with the rest of the application. Another major use case for dynamic calling is to create software plugins.

I use the term “plugin architecture” to indicate a technique that strives to simplify code by facilitating runtime changes to limited portions of the code while allowing the basic logic for the function as a whole to remain intact and unchanged. For example, say you are implementing a control algorithm that has, for the sake of argument, two inputs: a position and a load. As long as the readings are accurate, timely and in the proper units, most of the control algorithm doesn’t care how these inputs are measured. It would, therefore, simplify the code base as a whole to incorporate a technique that allows the reuse all the common code by simply “plugging in” different acquisition modules that support the various different types of sensors that can measure position and load.

This isn’t rocket science

This goal might sound lofty, but the hurdle for getting into it is actually pretty low. In fact, when teach the LabVIEW Core 1 and 2 week, I will sometimes introduce what LabVIEW can do by demonstrating a simple plugin architecture that only uses concepts introduced and discussed in those two classes.

I start the demo by opening an application that I have written to implement a simple calculator. It has two numeric inputs labeled “X” and “Y” and an indicator labeled “Output”. The only other control is a popup menu that lists the two math operations it can perform: addition and subtraction. I first show that you can use the simple program to add and subtract numbers.

I then comment that it would be nice if my program could multiply numbers as well. So I drag the program window to one side but leave it running. I then open a new window and add two inputs and an output (labeled like the program). On the block diagram I drop down a multiplier, wire it up, and save the VI with the name Multiply.vi. After closing that VI’s front panel, I tell the class that although they just watched me create the ability to multiply numbers, the program which is already running has already acquired it. At this point, I pull the program front panel back over and show that the menu which only moments before said “Add” and “Subtract”, now has a third option: “Multiply”. Moreover, the new selection does indeed allow me to now multiply numbers. Finally, I make the point that this capability to dynamically expand and change functionality was created using nothing but things that they will be learning in the coming week.

While the students at this point might not be cheering, they are awake and have some motivation to learn. You better believe that when I show them the code Friday afternoon and they can recognize how the program works, they are excited. So let’s see what I can do now to motivate and excite you…

It’s all about the packaging

As we begin to get into the following use cases you should notice that the actual code needed to implement each solution is not really very complicated. If fact, in the following sections we will be dealing with many of the same functions as when we were dynamically launching separate processes. The tricky part here is that we need to fit all the data management and launch logic in a space that would otherwise be occupied by a single VI. For this reason we need to be very careful when packaging this code. To demonstrate what I mean, let’s consider three common use cases that all call a simple test VI (called amazingly enough Test.vi) that simply returns a random number. I have ordered these cases such that each example builds on what went before:

System Initialization

I like to start here when explaining these concepts because it is the simplest structure and so is easy to understand.

System Initialization

Here you see laid out all the basic pieces required to dynamically call a subVI. The first VI (the one marked “Dummy”) is the one we will use through-out this post to represent the data management needed to get the path to the VI that will be called dynamically. Depending upon your application this could come from an INI file, database, or what have you. We won’t go into a lot of detail with that VI because we have discussed the various techniques pretty thoroughly in other posts.

You should recognize the next node (Open VI Reference) as we have already used it a lot. It opens a reference to the VI specified in the input VI name or path. This action also has the effect of loading the VI into memory. Just as statically subVIs can be reentrant, you can specify it here, as well, if needed.

Once the VI is open, we pass its reference to a node that looks a lot like the Start Asynchronous Call we have already used. This node is Call by Reference and like the prior one it expects a strict VI reference and in return provides a connector pane for passing data to the target. However, unlike the asynchronous call, this node always waits until the target finishes executing before continuing, hence its representation of the target’s connector pane also allows us to get data from the target.

Finally, in order to remove the target VI from memory, we need to close the reference when we are done with it — which is what the last node does.

When using this technique it is important to keep in mind that, because it loads, executes and unloads the VI all at once, it is very inefficient when used is a situation where it will be run multiple times. Sort of like trying to use an Express VI inside a loop — but worse.

However, one place where this technique can be used very effectively is doing things like system initialization. Its not uncommon for large and complex applications to require an equally large and complex initialization process, especially the first time they are run. Although you could just write a VI to do this initialization and install it in the program, why should you burden your program with a bunch of code that may only execute one time — ever? This technique allows you to only load and execute a VI if you actually need it.

Function Substitution

This use case is perhaps the most common of the three. It uses the same nodes we just saw, but because it can be used essentially anywhere, the packaging has to be very efficient. A solution I often use is to create a mini state-machine that has states for loading, executing and unloading the target VI, as well as one more for deciding what to do first. The user interface has one (enumerated) control that allows you to specify whether you want to Load the target, Run the target, or Unload the target.

The inputs are pretty simple, and the state-machine logic mirrors that simplicity. If the function requested is Load, the Startup state transitions directly to the Open VI state which opens and buffers the VI reference. Here is the code for these two states:

Function substitution - Startup - Load

Function substitution - Open VI

If the function requested is Run (the input’s default value, by the way), the Startup state first checks to see if the VI reference is valid and if it is, transitions to the Execute state.

Function substitution - Startup - Run

Function substitution - Execute

If the reference is not valid, execution instead continues with the Open VI state which returns to the Execute state after opening the reference.

Finally, if the function requested is Unload, the Startup state transitions directly to the Close VI state:

Function substitution - Startup - Unload

Function substitution - Close VI

The result of all this work is a flexible VI that can be used in a variety of different ways depending upon system requirements. For example, if the target VI loads quickly or the calling process doesn’t have tight timing constraints, you can just install it and let the first call to both load the VI into memory and run it. Alternately, if you don’t want the first call to be delayed by that initial load, you can call it once in a non-time-critical part of the code to just Load the target and then Run it as many times as you like later.

In the same way, the Unload function, which normally isn’t needed, can be used to release the memory resources used by a large dynamic subVI when you know that you aren’t going to be needing it for a while.

This approach could even be extended to create a simple test executive that dynamically loads and unloads whole sets of VIs. In such a situation, though, you probably don’t want to be tied to a single connector pane, so you should consider changing the VI reference to non-strict and modifying the Execute state to use our old friend the Run VI method like so.

Function substitution - Execute - Run VI Method

Interprocess Communication

This use case is in many ways the most expansive of the three because it supports communications between different processes regardless of their physical location. In terms of code, there is very little difference between this case and the previous one. In fact, the only change that is really required is to the Load state. This is what the network-enabled version looks like:

Function substitution - Open VI - IPC

That new icon in front of Open VI Reference is the one that makes the magic. Its name is Open Application Reference and its job is to return a reference to the instance of LabVIEW that is hosting the VI that you want to access. To make this connection, you need to know the host-name or IP address of the computer running the LabVIEW application, and the port number the instance is using for incoming connections. If the application that you are wanting to access in on your own computer, you can either leave the host name string empty or use the name localhost. The port can be any number that isn’t already being used. One common people mistake is to simply accept the default value, which is the port that LabVIEW listens to by default. This causes problems when they go to test their application because now there are two processes (the LabVIEW development environment and their application) trying to use the same port.

An important point to remember is that while the Call by Reference node passes data back and forth, the called VI actually executes on the remote system. Hence, this sort of operation works independent of the target system’s operating system or even the version of LabVIEW that it is running.

The tasks for setting up a system to use this technique involves properly configuring the application to which you are going to be connecting — though it often doesn’t require any code changes. This amazing benefit is possible because the underlying networking is handled by the runtime engine, not the application code you write. I have on a couple occasions come into a place and added remote control functionality to an old application that was not even designed with that capability in mind.

You will, however, have to make changes to the target application’s INI file to enable networking, And you, obviously, will need to have access to the application’s source code so you know what VIs to call. Likewise, if you are accessing a computer outside your local network there can be a variety of network communications details to sort out that are beyond the scope of this post. One other thing to bear in mind is that it is always easier to connect to a remote VI if it is already in memory. The reason for this fact is that if the VI is already in memory all you need to know is the VI’s name. If you are also loading it into memory, you have to know the complete path — the annotation of which can change between platforms.

But assuming you establish the connection, what exactly can you do with it? The answer to that lies in what VIs you choose to execute from the remote process. If you execute a VI that fires an event, that event gets fired on the remote system, so you can use it as a channel for controlling that application.

Alternately, say you chose to execute a VI that is a function global variable (FGV). Depending on whether you are reading from or writing to the FGV, you are now either passing data to the remote system, or collecting data from it. By the way, this is the method I still typically use for passing data over a network between LabVIEW applications — not network-enabled shared variables. Unlike this later “enhancement”, the dynamic calling of a FGV doesn’t need to be “deployed”, is more memory efficient, has a smaller code footprint, is far easier to troubleshoot if there is a problem, and works across all releases of LabVIEW back to Version 6.

The only real limit to what you can do with this technique is your imagination.

So there are the three major use cases for the dynamic linking of subVIs. This discussion is by no means a complete consideration of the topic, but hopefully it will whet your appetite to experiment a bit. The link below is to an archive containing all three examples to get you started.

Dynamic Linking Examples

Next time: Command Line Parameters are not a relic of the past.

If you have spent any time poking into some of the more esoteric corners of the application builder, you may have noticed a checkbox in the Advanced section┬álabeled, “Pass all command line arguments to application”, and wondered what that is all about. Well, wonder no more, that little checkbox is what we are going to discuss next time out — and while we’re at it I’ll cover a really neat use for it.

Until next time…

Mike…

Raising the Bar on Dynamic Linking Even Further

Important: Before we get started this week, if any of you have downloaded the code from last week and have had problems with it, please update you working copy with what is currently in SVN. In working on this post, I found some “issues” — including the significant problem that the database that defines everything didn’t get included in the repository release. The current contents of the repository should fix the problems, and I am sorry for any hair-loss and/or gnashing of teeth these problems might have caused.

Also, be sure that if you plan to run this code in the development environment that you name the directory where it is located “testbed”.

These days, the most common way of implementing the dynamic calling of VIs in LabVIEW is through the Start Asynchronous Call node. In addition to being efficient, this technique is also very convenient in terms of passing data to the VI that is being called. Because it replicates the VI’s connector pane on the node, all you have to do is wire to terminals. However, this convenience comes at a price. For this type of call to work you have to know ahead of time what the connector pane of the VI being called looks like. This constraint can be a problem because it is not uncommon for situations to arise where you are wanting to dynamically call code the connector pane of which varies, is irrelevant (because no data is being passed), or is unknown. As you should by now be coming to expect, LabVIEW has you covered for those situations as well.

Where There’s a Will, There’s a Way Method

Over the years as LabVIEW developed as a language, its inherent object orientation began to become more obvious. For example, when VI Server was introduced it provided a very structured way of interacting with various objects within LabVIEW, as well as LabVIEW itself. With VI Server you can control where things appear on the front panel, how they look and even how LabVIEW itself operates. Although it didn’t reach its full expression until the Scripting API was released, the potential even existed to create LabVIEW code that wrote LabVIEW code.

We won’t be needing anything that complex, however, to accomplish our goal of dynamically launching a VI where we don’t have advance knowledge of its connector pane. In fact the part of VI Server that we will be looking at here is one of the oldest and most stable — the VI object interface. Just as you can get references to front panel controls, indicators and decorations, you can also get references to VIs as a whole.

Like control references, VI references come in two basic forms: strict and non-strict. To recap, a strict control reference contains added information that allows it to represent a particular instance of the given type of control. For example, a strict cluster control reference knows the structure, or datatype, of a particular cluster. By contrast, a non-strict cluster reference knows the control is a cluster, but can’t directly tell you what the various items are that make up the cluster.

In the same way, strict VI references, like we have been using to dynamically launch VIs, know a great deal about a specific class of VI, including the structure of its connector pane. This is why the Start Asynchronous Call node can show the connector pane of the target VI. However, as stated earlier, this nice feature only works with VIs that have connector panes exactly matching the prototype. As you might suspect, the solution to this problem is to use a non-strict VI reference, but that means we need to change our approach to dynamic launching a bit. Instead of using a special node, we’ll use standard VI Server methods to interact with and run VIs.

Mix and Match

To see how this discussion applies to our testbed code base, consider that to this point we have used a single technique to launch all the processes associated with the application. Of course to make that approach work required one teeny tiny hack. Remember when we added to the data source processes an input that tells them “who they are”? Well, that modification necessitated a change to the VIs’ connector panes, and because we were launching all the processes the same way, I had to make the same change to all the processes — even those that didn’t need the added input, like the GUI and the exception handler.

So big deal, right? It was only one control, and it only affected 2 VIs. Well maybe in this case it isn’t a huge issue, but what if it weren’t 2 VIs that needed to be changed, but 5 or 6? Or what if all the various processes needed different things to allow them to initialize themselves? Now we have a problem.

The first step to address this situation was actually taken some time ago when the launcher was designed to support more than one launch methodology. You’ll remember last week when creating the dynamically launched clones, we didn’t have to modify the launcher because it was written from day one to support reentrant VIs. What we have to do now is expand on this existing ability to mix and match VIs with launch methodologies to include two new options in the Process Type.ctl typedef. Here’s what the code for the first addition looks like:

run method - nonreentrant

As before, we start by opening a reference to the target VI, but this time it’s a non-strict reference. Next, we invoke the Run VI method, which has two inputs. The first input specifies whether we want to wait the target VI to finish executing before continuing, and we set it to false. The second parameter is named somewhat obscurely, Auto Dispose Ref. What it does is specify what to do with the VI reference after the VI is launched. In its default state (false) the calling VI retains the VI reference and is responsible for closing it to remove the target VI from memory. In addition, if the calling VI retains the reference to the dynamic VI, when the caller quits, the dynamic VI is also aborted and removed from memory. On the other hand, when this input is set to true, the reference is handed off to the target VI so the reference doesn’t close until the dynamic VI quits — which is what we want.

The other new launch option is like the first, except it connects the option constant that tells the Open VI Reference to open a reference to a reentrant clone. Other than that, it works exactly the same.

run method - reentrant

So with these two new launch methodologies created, all we have to do is change the database configuration for the GUI and Exception Handler processes to use the nonreentrant version of the Run VI method and we are done right? Well not quite…

One of the “quirks” of the Run VI method is that although it does start a VI executing, if that VI is configured to open its front panel when run (like our GUI is), the open operation never gets triggered and the front panel will stay closed. The result is that the VI will be open and running, you just won’t be able to see it.

To compensate for this effect (and the corresponding effect that the front panel won’t automatically close when the VI finishes), we need to add to the GUI a couple VIs from the toolbox that manage the opening and closing of the GUI’s the front panel.

open front panel

That’s the opener there, the last one in line after all the initialization code. This placement is important because it means that nearly all the interface initialization will be completed before the front panel opens. The result is much more professional looking. By the way, this improved appearance is why I rarely use the option to automatically open a VI’s front panel when it is run.

close the front panel

And here is the closer. The input parameter forces the front panel closed in the runtime, but allows it to stay open during development — a helpful feature if there was an error.

Where do we go from here?

So that is the basics of this technique, but there is one more point that needs to be covered. Earlier I talked about flexibility in passing data, so how do you pass data with this API? Well, we ran the VI using a method, so as you would expect, there is other methods that allow you to read or set the value of front panel controls. This is what the interface to the Control Value Set method looks like.

the set control value method

It has two input parameters: a string that is the label of the control you want to manipulate, and a variant that accepts the control’s new data value. Note that because LabVIEW has no way of knowing a priori what the datatype should be, you can get a runtime error here if you pass an incorrect datatype. Obviously, using this method your code can only set one control value at a time so unless you only have 1 or 2 controls that you know you will need to set, this method will often end up inside a loop like so:

set control value in a loop

…but this brings up an interesting, and perhaps exciting, idea. Where can we get that array of control name and value pairs? Would it not be a simple process to create tables in our database to hold this information? And having done that would you have not created a system that is supremely (yet simply) reconfigurable. This technique also works well with processes that don’t need any input parameters to be set. The loop for configuring control values passes the VI reference and error cluster through on shift registers and auto-indexes on the array of control name/value pairs. Consequently, if a given VI has no input parameters, the array will be empty and the loop will execute 0 times — effectively bypassing the loop with no added logic needed. By the way, this is an important principle to bear in mind at all times: Whenever possible avoid “special cases” that have to be managed by case structures or other artificial constructs.

More to Come

Over two consecutive posts, when have now covered two major use cases for using dynamic linking: VIs that will run as separate processes. But there is another large use case that we will look at the next time we get together: How do you dynamically link code that isn’t a separate process, but logically is a subVI?

Testbed Application &mdash Release 12

Toolbox — Release 6

Until next time…

Mike…

Taking Flexibility to the Next Level — Dynamic Linking

When people start using LabVIEW they always find fascinating the way it allows them to assemble complex functionality from simple building blocks, called subVIs. However there are a variety of use cases where — for very legitimate reasons — the usual technique for interconnecting subVIs with wires just won’t do. One thing that many of these use cases have in common is the desire to put off until runtime the decision of what code we want the program to execute next. Noting the power of being able to add functionality to our computers by plugging in a new card, or USB dongle, a common wish is to find a way to do do the same thing in software, a way to create software plugins.

In fact the potential benefits of this sort of structure were so great that in the early days of LabVIEW’s existence, developers would go to absurd lengths to realize even a small bit of it. For example, the only way originally of creating software plugins was to take advantage of the fact that LabVIEW links to subVIs by name and manually change the names of files to select the one you wanted to use before opening the top-level VI. If you’re wondering how that impacted building an application, that wasn’t a problem — there was no application builder!

Thankfully, today we have many different options for creating a true plugin architecture through dynamically calling and linking VIs. To explore those options I’m going consider over the next few posts a few of the basic use cases, and demonstrate the techniques that serves best for each one.

What We Have Already Seen

A good place to start this exploration is a use case with which we are already familiar — the one embodied in our testbed application. When I originally presented the idea of structuring the program as a group of independent processes that were dynamically launched, I spoke about it in terms of promoting a kind of modularization that simplified the code, made the application as a whole more robust, and fostered reusability. While all that is true, there is another side to that coin. By providing you with the ability to select at runtime the capabilities of the software, it opens the door for dramatically improved scalability. To see how that would work, consider the following scenario.

The most recent change to the testbed was to create a state-machine temperature controller that was (as I said in the post) only suitable for controlling the temperature of a “…hen-house, dog house or out house…”. But let’s say you have more than 1 hen-house, dog house or out house that needs temperature control. How should we handle that? One solution that you often see used is to simply duplicate that one VI, but then what if the number of “houses” changes? You could be left in the position of constantly modifying the application to add or subtract resources. Let me show you a better way, a way to reuse the exact same VI as many (or as few) times as might be needed, but without needing to modify the code to change the number if instances created. The trick is to make the code reentrant.

The basic idea behind a reentrant VI is that each time it is called in a program, LabVIEW reuses the same code, but gives each instance its own memory space. The result is the same as if you had multiple copies of the same VI. This basic operating principle is the same regardless of whether the reentrant VI is linked into your program statically, or is being called dynamically. When making a call to a reentrant VI, the resulting instance running in memory is called a clone. It’s important to remember that while all the clones of a given VI will use the same common code, they each have (in addition to their own memory space) their own block diagram for debugging and front panel for user interaction. In some ways it’s as though you are dynamically creating new VIs out of thin air!

Bring in the clones…

So what do you need to do to embed this magic in your program, you ask? Well, perhaps, not as much as you would think…

Reentrancy

The first thing, obviously, is that in order to be cloneable, the VI must be reentrant. But for many people, this feature can be at first confusing and more than a bit intimidating (I know it was for me). The challenge from the perspective of LabVIEW is that, unlike most languages, LabVIEW actually supports two kinds of reentrancy. The easy one is the one that creates “Preallocated” clones, it works the way you see it described if you look up the term “reentrant” online. For many, the confusing part is the other kind of reentrancy, the “Shared” clones. I know the first time I saw the term, it stuck me as an oxymoron of the first order. As I understood reentrancy, the whole point of reentrant execution was that clones used preallocated memory spaces that didn’t get shared. So shared clones, what is that about? In order to answer that, we need to consider how LabVIEW execution works.

Normally, VI execution is blocking. By that I mean that two instances of the same VI cannot run at the same time because they share the same memory space. However, because they have their own memory spaces, multiple instances of reentrant VIs can execute in parallel. Consequently, when writing very low-level VIs that are going to be used in many, many places throughout your code, one would like to make them reentrant so the instances are not all blocking each other. However if you did that willy-nilly, you could develop a problem: memory. You could end up preallocating a lot of memory for dozens or even hundreds of instances that don’t actually run very often, or perhaps ever. To address that problem, LabVIEW created the concept of shared reentrancy that splits the difference between nonreentrant execution on the one hand, and normal reentrant execution (which LabVIEW now calls preallocated), on the other.

Shared cloning creates a pool of a predefined number of sharable memory spaces that the reentrant code can use. Now whenever a particular reentrant function is called, LabVIEW can go to its pool of memory spaces and use a memory spaces that isn’t busy right then. If all the copies in the pool are busy, LabVIEW creates a new memory space for the VI, adds it to the pool and then uses it. All in all, it’s a pretty nice feature that we will see the need to use later…

Parameterized Identity

The next thing clones need is a way tell themselves apart. As a case in point, you can have 3 houses and 3 clones of our control process, but if all 3 clones try to control the same house, you still haven’t accomplished anything useful. Clone 1 has to be able to tell that it is responsible for house 1. Likewise, clone 2 has to know that it is handling house 2, and clone 3 must direct its efforts to controlling house 3. The way to accomplish this assignment is through a process called parameterization, or passing to the clone an input parameter that it can then use to obtain the configuration information it needs to operate.

This area is one of those places where having an adequate data management infrastructure in place (read: database) really pays big dividends. Ideally, you should be able to provide a clone with an identity through a single value, like a name. Other times, the various clones may be controlling specific test systems, so perhaps all you would need is the ID number of the system assigned to the clone. Regardless of the exact nature of that identifying value, with a database in place the clone can use that name or number to query the database for the specific setup information that it needs.

Functional Completeness

A good clone should be functionally complete. By that, I mean that a clone needs to be a complete package unto itself, not needing a lot of detailed external interactions. A few high-level controls are fine, but you don’t want to expose too much detail.

As an example of what I mean, consider the following situation from life. Say, you have a new employee in your department that needs to fill out some sort of form from HR. While it might be reasonable to explain what specific information is needed in one field or another, you shouldn’t have to take the time to explain that the side of the form to fill out is the one with the printing on it. You also shouldn’t have to describe the color of ink to use or the hand in which the person should hold the pen — or for that matter, that they should hold the pen in their hand and not between their toes! The only instruction that should be needed is, “Go fill out this form.”

Likewise with a clone, you should be able to say things like, “This is the code that does the out house temperature control”, or, “This is the process that interfaces with the database.” A good way of judging how well you are doing in meeting this “completeness” goal is to consider whether you can change the details of what the clone does without impacting the rest of the application.

A principle that can be supportive of this sort of completeness is to make sure the clone’s code isn’t burdened with extraneous functionality like extensive GUIs. Remember that adding in nonessential functionality limits the applicability of that code to only those situations where that added functionality is needed. As a practical matter, you particularly don’t want clones to have the ability to open dialog boxes. In case you aren’t clear on the reasoning behind that injunction, consider the pandemonium that could result if you have 15 or 20 clones running and something happens that causes all of them to start opening dialog boxes simultaneously.

Managing Common Resources

Finally, because you can potentially have a large number of clones running at the same time, you need to carefully consider parallel access to common resources. Interfaces that might work well for 2 or 3 clones could run into bandwidth problems when there are a couple dozen. Moreover, as the number of simultaneous accesses increase, so does the potential for access conflicts and race conditions. For example, I can’t tell you how many times I have seen systems crash and burn over something as simple as two processes trying to write to the same file at the same time.

If you have a common resource that could become a bottleneck or produce a race condition, a solution that often works well is to create a separate process with the sole responsibility of managing that resource.

Cloning our Temperature Controller

So what do we need to do in order to close our temperature controller? We know that in order to be cloned, the VI needs to be reentrant, so Step One is to turn that feature on by going to the Execution page of the VI Properties and selecting the option for Preallocated Clones.

reentrant setting

That one setting handles the top-level, but what about the subVIs? They could still block each other and prevent the cloned controllers from running unobstructed. To prevent that possibility, you should go through the subVIs looking for routines that are called regularly, and make them shared clones. In the current code, there is one exception to this advice — and we’ll cover it next.

With the applicable code set to being reentrant, we need to consider the identity issue. That matter is, to an extent, already being handled in the existing code because even nonreentrant VIs sometimes need to be told who they are. All we need to do is expand our usage of the My Name label we are already passing to the dynamically launched VIs by using it to look-up the configuration data for each clone.

The nonreentrant version of our temperature controller state-machine already had a VI for looking up the state-machine setups in the database, all we have to do is modify it so it accepts the My Name value as an input. Here is the modified version of the look-up VI.

load state machine settings

This is the VI that I said earlier that we do not want to make a shared clone, and there are two reasons. First, the VI only gets called once so there would be no benefit. Second, when you consider what the VI is designed to do, making it a shared clone would actually be counterproductive. See the shift registers? They are there to buffer the results of the queries to reduce the number of database accesses that the application has to make. In other words they are there specifically to created a shared memory space, so making it a clone would defeat that purpose.

Next we need to address the dialog box in the data saving routine. It really isn’t a good idea to give users the responsibility to correctly save data files anyway, so let’s change it.

modified save data

This modification saves the file with a dynamically generated name, to a predefined location, in this case the “Documents” directory associated with the current Windows user. Note that part of the file name is a timestamp consisting of the year, month, day, hour, minutes and second the file was created. I like this filename format because (assuming a 4-digit number for year and 2 digits for everything else, and a 24 hour clock for hours), files named in this way will always sort by name in correct chronological order.

Beyond that we are pretty much done. The existing code was already concise and had no user interface to speak of — just a few controls that we would want to have for troubleshooting purposes anyway. The only other thing needed is to define the clones that we want in the database. When you run the modified code, you will see three clones with differing update rates — but you can create as many as you want by simply adding them (and their setup parameters) to the database.

We are almost done for now, so hopefully you see that in some ways dynamic linking isn’t what this post is really about. Oh, I have presented a little bit of technique, and a few code modification, but the real lesson is that expanding the reach of you code in this way is not hard, as long as the code you are working with is well-designed from the beginning. Often you will hear people talking about all the modifications that they had to make in order to make their code cloneable, but if you really look at it you see that most of the modifications were in actuality fixing mistakes that they should never have made in the first place.

What Now

This should be enough for everybody to digest for now, so next week we’ll look at the other way to launch dynamic VIs — and why you would want to use it. (Hint: It addresses a problem that right now you don’t even know you have.)

Testbed Application — Release 11

Toolbox — Release 5

Until next time…

Mike…

Building a Proper LabVIEW State Machine Design Pattern – Pt 2

Last week’s post was rather long because (as is often the case in this work) there was a lot we had to go over before we could start writing code. That article ended with the presentation of a state diagram describing a state machine for a very simple temperature controller. This week we have on our plate the task of turning that state diagram into LabVIEW code that implements the state machine functionality.

The first step in that process is to consider in more detail the temperature control process that the state diagram I presented last week described in broad terms. By the way, as we go through this discussion please remember that the point of this exercise is to learn about state machines — not temperature control techniques. So unless your application is to manage the internal temperature of a hen-house, dog-house or out-house; don’t use this temperature control algorithm anywhere.

How the demo works

The demonstration is simulating temperature control for an exothermic process — which is to say, a process that tends to warm or release heat into the environment over time. To control the temperature, the system has two resources at its disposal: an exhaust fans and a cooler. Because the cooler is actively putting cool air into the area, it has a very dramatic effect on temperature. The fan, on the other hand, has a much smaller effect because it is just reduces the heat through increased ventilation.

When the system starts, the state machine is simply monitoring the area temperature and as long as the temp is below a defined “High Warning Level” it does nothing. When that level is exceeded, the system turns on the fan, as shown by the fan light coming on. In this state, the temperature will continue to rise, but at a slower rate.

Eventually the temperature will, exceed the “High Error Level” and when it does, the system will turn on the cooler (it has a light too). The cooler will cause the temperature to start dropping. When the temperature drops below the “Low Warning Level” the fan will turn off. This action will reduce the cooling rate, but not stop it completely. When the temperature reaches the “Low Error Level”, the cooler will turn off and the temperature will start rising again.

So let’s look at how our state machine will implement that functionality.

“State”-ly Descriptions

As I stated last week, the basic structure is an event structure with most of the state machine functionality built into the timeout case. To get us started with a little perspective, this is what the structure as a whole looks like.

State Machine Overview

Obviously, from the earlier description, this state machine will need some internal data in order to perform its work. Some of the data (the 4 limit values and the sample interval) is stored in the database, while others are generated dynamically as the state machine executes. Regardless of its source, all of this data is stored in a cluster, and you can see two of the values that it contains being unbundled for use. Timeout is the timeout value for the event structure and is initialized to zero. The Mode value is an enumeration with two values. In the Startup case is the logic that implements startup error checking and reads the initial setup values from the database. When it finishes, it sets Mode to its other value: Run. This is where you will find the bulk of the state machine logic. Note that while I don’t implement it here, this logic could be expanded to provide the ability to do such things as pause the state machine.

The following sections describe the function of each state and shows the code that implements it.

Initialization

This state is responsible for getting everything initialized and ready to run.

Initialization State

In a real system, the logic represented here by a single hardware initialization VI would be responsible for initializing the data acquisition, verifying that the system is capable of communicating with the fan and cooler, and reading their operational states. Consequently, this logic might be 1 subVI or there might be 2 or 3 subVIs. The important point is to not show too much detail in various states. Use subVIs. Likewise, do not try to expand on the structure by adding multiple initialization states.

Finally, note that while the selection logic for the next state may appear to be a default transition, it isn’t. The little subVI outside the case structure actually creates a two-way branch in the logic. If the incoming error cluster is good, the incoming state transitions (in this case, Read Input) is passed through unmodified. However, if an error is present, the state machine will instead branch to the Error state where the problem can be addressed as needed.

Read Input

This state is responsible for reading the current temperature (referred to as the Process Variable) and updating the state machine data cluster.

Read Input State

In addition to updating the last value, there are a couple other values that it sets. Both of these values relate to how the acquisition delay is implemented in the state machine. The first of these is the Timeout value, and since we want no delay between states, we set this to zero. The other value is the Last Sample Time. It is a timestamp indicating when the reading was made. You’ll see in a bit how these values are used.

This state also updates two front panel indicators, the graph and a troubleshooting value.

Test Fan Limits

This state incorporates a subVI that analyzes the data contained in the state machine data to determine whether or not the fan needs to change state.

Test Fan Limits State

The selector in this state results in what is the potential for a 3-way branch. If a threshold has been crossed, the next state will be Set Fan, if it has not, the next state will be Test Cooler Limits, and if an error has occurred, the next state will be Error.

Set Fan

Since the fan can only be on or off, all this state needs to do is reverse its current operating condition.

Set Fan State

In addition to the subVI toggling the fan on or off, the resulting Fan State is unbundled from the state machine data and the value is used to control the fan LED.

Test Cooler Limits

This state determines whether the cooler needs to be changed. It can also only be on or off.

Test Cooler Limits State

The logic here is very similar to that used to test the fan limits.

Set Cooler

Again, not unlike the corresponding fan control state, this state changes the cooler state and sets the cooler LED as needed.

Set Cooler State

Acquisition TO Wait

This state handles the part of the state machine that is in often the Achilles Heel of an implementation. How do we delay starting the control sequence again without incurring the inefficiency of polling?

Acquisition TO Wait State

The answer is to take advantage of the timeout that event structures already have. The heart of that capability is the timeout calculation VI, shown here:

Calculate Time Delay

Using inputs from the state machine data, the VI adds the Sample Interval to the Last Sample Time. The result is the time when the next sample will need to be taken. From that value, the code subtracts the current time and converts the difference into milliseconds. This value is stored back into the state machine data as the new timeout. This technique is very efficient because it requires no polling.

But wait, you say. This won’t work if some other event happens before the timeout expires! True. But that is very easy to handle. As an example, here is the modified event handler for the save data button.

Handling Interrupting Events

It looks just as it did before, but with one tiny addition. After reading, formatting and saving the data, the event handler calls the timeout calculation VI again to calculate a new timeout to the intended next sample time.

Error

This state handles errors that occur in the state machine. That being the case, it is the new home for our error VI.

Error State

Deinitialize

Finally, this state provides the way to stop the state machine and the VI running it. To reach this state, the event handler for the UDE that shuts down the application, will branch to this state. Because it is the last thing to execute before the VI terminates, you need to be sure that it includes everything you need to bring the system to a safe condition.

Deinitialize State

With those descriptions done, let’s look at how the code works.

The Code Running

When you look at the new Temperature Controller screen you’ll notice that in addition to the graph and the indicators showing the states of the fan and cooler, there are a couple of numbers below the LEDs. The top one is the amount of elapsed between the last two samples, the bottom one is the delay calculated for the acquisition timeout.

If you watch the program carefully as its running, you’ll notice something a bit odd. The elapsed time indicator shows a constant 10 seconds between updates (plus or minus a couple of milliseconds — which is about all you can hope for on a PC). However, the indicator showing the actual delay being applied is never anywhere near 10,000 milliseconds. Moreover, if you switch to one of the other screens and the back, the indicated delay can be considerably less than 10,000 milliseconds, but the elapsed time never budges from 10 seconds. So what gives?

What you are seeing in action is the delay recalculation, we talked about earlier. In order to better simulate a real-world system, I put a delay in the read function that pauses between 200- and 250-msec. Consequently when execution reaches the timeout calculation, we are already about 1/4 of a second into the 10 second delay. The calculation, however, automatically compensates for this delay because the timeout is always referenced to the time of the last measurement. The same thing happens if another event comes between successive data acquisitions.

On Deck

As always, if you have any questions, concerns, gripes or even (gasp!) complements, post ’em. If not feel free to use any of this logic as you see fit — and above all, play with the code see how you might modify it to do similar sorts of things.

Stay tuned. Next week we will take a deeper look at something we have used before, but not really discussed in detail: Dynamically Calling of VIs. I know there are people out there wondering about this, so it should be fun.

Until next time…

Mike…

Building a Proper LabVIEW State Machine Design Pattern – Pt 1

The other day I was looking at the statistics for this site and I noticed that one of the most popular post with readers was the one I wrote on the producer/consumer design pattern. Given that level of interest, it seemed appropriate to write a bit about another very common and very popular design pattern: the state machine. There’s a good reason for the state-machine’s popularity. It is an excellent, often ideal, way to deal with logic that is repetitive, or branches through many different paths. Although, it certainly isn’t the right design pattern for every application, it is a really good technique for translating a stateful process into LabVIEW code.

However, some of the functionality that state machines offer also means they can present development challenges. For example, they are far more demanding in terms of the design process, and consequently far less forgiving of errors in that process. As we have seen before with other topics, even the most basic discussion of how to properly design and use state machines is too big for a single post. Therefore, I will present one post to discuss the concepts and principles involved, and in a second post present a typical implementation.

State Machine Worst Practices

For some reason it seems like there has been a lot of discussions lately about state machine “best practices”. Unfortunately, some of the recommendations are simply not sound from the engineering standpoint. Meanwhile others fail to take advantage of all that LabVIEW offers because they attempt to mimic the implementation of state machines in primitive (i.e. text-based) languages. Therefore, rather than spinning out yet another “best practices” article, I think it might interesting to spend a bit of time discussing things to never do.

In describing bad habits to avoid, I think it’s often a good idea to start at the most general level and work our way down to the details. So let’s start with the most important mistake you can make.

1. Use the state machine as the underlying structure of your entire application

State machines are best suited for situations where you need to create a complex, cohesive, and tightly-coupled process. While an application might have processes within it that need to be tightly-coupled, you want the application as a whole to exhibit very low levels of coupling. In fact, much of the newest computer science research deprecates the usage of state machines by asserting that they are inherently brittle and non-maintainable.

While I won’t go that far, I do recognize that state machines are typically best suited for lower-level processes that rarely, if ever, appear to the user. For example, communications protocols are often described in terms of state machines and are excellent places to apply them. One big exception to this “no user-interface” rule is the “wizard” dialog box. Wizards will often be built around state machines precisely because they have complex interface functionality requirements.

2. Don’t start with a State Diagram

Ok, so you have a process that you feel will benefit from a state machine implementation. You can’t just jump in and start slinging code right and left. Before you start developing a state machine you need to create a State Diagram (also sometimes called a State Transition Diagram), to serve as a road-map of sorts for you during the development process. If you don’t take the time for this vital step, you are pretty much in the position of a builder that starts work on a large building with no blueprint. To be fair, design patterns exist that are less dependent upon having a completed, through design. However, those patterns tend to be very linear in structure, and so are easy to visualize in good dataflow code. By contrast, state machines are very non-linear in their structure so can be very difficult to develop and maintain. To keep straight what you are trying to accomplish, state machines need to be laid out carefully and very clearly. The unfortunate truth, however, is that state machines are often used for the exact opposite reason. There is a common myth that state machines require a minimum of design because if you get into trouble, you can always just, “add another state”. In fact, I believe that much of the bad advice you will get on state machines finds its basis in this myth.

But even if we buy the idea that state machines require a more through design, why insisted on State Diagrams? One of the things that design patterns do is foster their own particular way of visualizing solutions to programming problems. For example, I have been very candid about how a producer/consumer design pattern lends itself to thinking about applications as a collection of interacting processes. In the same way, state machines foster a viewpoint where the process being developed is seen as a virtual machine constructed from discrete states and the transitions between those states. Given that approach to problem solving, the state diagram is an ideal design tool because it allows you to visually represent the structure that the states and transitions create.

So what does it take to do a good state-machine design? First you need to understand the process — a huge topic on its own. There are many good books available on the topic, as well as several dedicated web sites. Second, having a suitable drawing program to create State Diagrams can be helpful, and one that I have used for some time is a free program called yEd. However fancy graphics aren’t absolutely necessary. You can create perfectly acceptable State Diagrams with nothing more than a paper, a pencil and a reasonably functional brain. I have even drawn them on a white board during a meeting with a client and saved them by taking a picture of them with my cell phone.

Moreover, drawing programs aren’t much help if you don’t know what to draw. The most important knowledge you can have is a firm understanding of what a state machine is. This is how Wikipedia defines a state machine:

A finite-state machine (FSM) or finite-state automaton (plural: automata), or simply a state machine, is a mathematical model of computation used to design both computer programs and sequential logic circuits. It is conceived as an abstract machine that can be in one of a finite number of states. The machine is in only one state at a time; the state it is in at any given time is called the current state. It can change from one state to another when initiated by a triggering event or condition; this is called a transition.

An important point to highlight in this description is that a state machine is at its core is a mathematical model — which itself implies a certain level of needed rigor in their design and implementation. The other point is that it is a model that consists of a “finite number of steps” that the machine moves between on the basis of well-defined events or conditions.

3. Ignore what a “state” is

Other common problems can arise when there is confusion over what constitutes a state. So let’s go back to Wikipedia for one more definition:

A state is a description of the status of a system that is waiting to execute a transition.

A state is, in short, a place where the code does something and then pauses while it waits for something else to happen. Now this “something” can be anything from the expiration of a timer to a response from a piece of equipment that a command had been completed successfully (or not). Too often people get this definition confused with that for a subVI. In fact one very common error is for a developer to treat states as though they were subroutines that they can simply “call” as needed.

4. Use strings for the state variable

The basic structure behind any state machine is that you have a loop that executes one state each time it iterates. To define execution order there is a State Variable that identifies the next state the machine will execute in response to an event or condition change. Although I once saw an interesting object-oriented implementation that used class objects to both identify the next state (via dynamic dispatch) and pass the state machine operational data (in the class data), in most cases there is a much simpler choice of datatype for the State Variable: string or enumeration.

Supposedly there is an ongoing discussion over which of these two datatypes make better state variables. The truth is that while I have heard many reasons for using strings as state variables, I have yet to hear a good one. I see two huge problems with string state variables. First, in the name of “flexibility” they foster the no-design ethic we discussed earlier. Think about it this way, if you know so little about the process you are implementing that you can’t even list the states involved, what in the world are you doing starting development? The second problem with state strings is that using them requires the developer to remember the names of all the states, and how to spell them, and how to capitalize them — or in a code maintenance situation, remember how somebody else spelled and capitalized them. Besides trying to remember that the guy two cubicles down can never seem to remember that “flexible” is spelled with an “i” and not an “a”, don’t forget that there is a large chunk of the planet that thinks words like “behavior” has a “u” in them…

By the way, not only should the state variable be an enumeration, it should be a typedef enumeration.

5. Turn checking the UI for events into a state

In the beginning, there were no events in LabVIEW and so state machines had to be built using what was available — a while loop, a shift register to carry the state variable, and a case structure to implement the various states. When events made their debut in Version 6 of LabVIEW, people began to consider how to integrate the two disparate approaches. Unfortunately, the idea that came to the front was to create a new state (typically called something like, Check UI) that would hold the event structure that handles all the events.

The correct approach is to basically turn that approach inside out and build the state machine inside the event structure — inside the timeout event to be precise. This technique as a number of advantages. To begin with, it allows the state machine to leverage the event structure as a mechanism for controlling the state machine. Secondly, it provides a very efficient mechanism for building state machines that require user interaction to operate.

Say you have a state machine that is basically a wizard that assists the user in setting up some part of your application. To create this interactivity, states in the timeout event would put a prompt on the front panel and sets the timeout for the next iteration to -1. When the user makes the required selection or enters the needed data, they click a “Next” button. The value change event handler for the button knows what state the state machine was last in, and so can send the state machine on to its next state by setting the timeout back to 0. Very neat and, thanks to the event-driven programming, very efficient.

On the other hand, if you are looking for a way to allow your program to lock-up and irritate your users, putting an event structure inside a state is a dandy technique. The problem is that all you need to stop your application in its tracks is one series of state transitions where the “Check UI” state doesn’t get called often enough, or at all. If someone clicks a button or changes something on the UI while those states are executing, LabVIEW will dutifully lock the front panel until the associated event is serviced — which of course can’t happen because the code can’t get to the event structure that would service it. Depending on how bad the overall code design is and the exact circumstances that caused the problem, this sort of lock-up can last several seconds, or be permanent requiring a restart.

6. Allow default state transitions

A default state transition is when State A always immediately transitions to State B. This sort of design practice is the logical equivalent of a sequence structure, and suffers from all the same problems. If you have two or more “states” with default transitions between them, you in reality have a single state that has been arbitrarily split into multiple pieces — pieces that hide the true structure of what the code is doing, complicates code maintenance and increases the potential for error. Plus, what happens if an error occurs, there’s a shutdown request, or anything else to which the code needs to respond? As with an actual sequence structure, you’re stuck going through the entire process.

7. Use a queue to communicate state transitions

Question: If default transitions are bad, why would anyone want to queue up several of them in a row?
Answer: They are too lazy to figure out exactly what they want to do so they create a bunch of pieces that they can assemble at runtime — and then call this kind of mess, “flexibility”. And even if you do come up with some sort of edge case where you might want to enqueue states, there are better ways of accomplishing it than having a queue drive the entire state machine.

Implementation Preview

So this is about all we have room for in this post. Next Monday I’ll demonstrate what I have been writing about by replacing the random number acquisition process in our testbed application with an updated bit of LabVIEW memorabilia. Many years ago the very first LabVIEW demo I saw was a simple “process control” demo. Basically it had a chart with a line on it that trended upwards until it reached a limit. At that point, an onscreen (black and white!) LED would come on indicating a virtual fan had been turned on and the line would start trending back down. When it hit a lower limit, the LED and the virtual fan would go off and the line would start trending back up again. With that early demonstration in mind, I came up with this State Diagram:

Demo State Machine

When we next get together, we’ll look at how I turn this diagram into a state-machine version of the original demo — but with color LEDs!

Until next time…

Mike…

Managing Shared Resources in a Subpanel-Based Interface

We have seen that building a system out of autonomous interacting processes can be a powerful architectural concept. However, there can be opportunities to simplify the operation of the system as a whole (and make it more robust) by designing one of those interacting processes to create common shareable resources that can be reused throughout system.

This time out we will consider a couple simple ways to implement this functional reusability without creating a brittle application.

Creating Reusable Resources

So the first thing we need to do is figure out exactly what we mean by “Reusable Resources”. In a sense we are creating a reusable resource every time we build a reusable library of drivers or functions, but that sort of reuse is not what I am talking about here. The distinction is that things like libraries are for really tools for the developer and users seldom even realize they exist. What I am talking about in this post are aspects of the program that are user facing.

For example, say you have an application that has 4 or 5 screens and they all need a couple buttons and a menu or two, but the buttons and menus on each screen do different things. One solution would be to simply duplicate the button and menu logic on each screen. but there are limitations to what you can do in this way. To begin with, forget the idea of having menus. Windows in subpanels technically aren’t open, so they don’t have menu bars and can’t have menus.

A far more flexible solution is to let the main GUI carry these reusable, or shared resources and let the subpanel VIs interact with them via references. Right now, that approach will be the focus of our attention.

Basic Concepts

One of the beautiful aspects of VI Server is that it allows you to do nearly anything with references that you can do directly. Therefore, our basic approach will be to pass references to the shared resources to the subpanel VI when it becomes active (i.e. visible in the subpanel) so it can register for, and respond to, the events associated with those resources. As a demonstration of how this works, we’ll add a button and a very simple menu example. The menu example will be simple because I am planning a complete discussion of menus for later time.

A New Requirement

The application to this point has provided a means for looking at data, or at least simulated data. It does not, however, have the ability to store any data. What we want to add is the ability to press a button, or make a menu selection, and have the data source we are looking at save the current chart contents to a text file. In addition, to raise the bar a bit higher, let’s also include in this requirement that the button and menu have to reconfigure themselves based on which data source is selected. In other words, if we are looking at the sine data source, the button and menu should say something like, “Save Sine Data”, or if viewing the ramp data, “Save Ramp Data”.

So what do we need to do to implement this functionality? To answer that question, let’s consider what we know. We know that a big reason for using subpanels in the first place is to eliminate data transfers. Therefore the data needs to be saved from the acquisition process itself — what would be the point of moving it somewhere else first?

Moreover, if each process is going to be handling its own data, doesn’t it make sense to let it handle the button clicks and menu selections too? The alternative is for the GUI to detect the button click or the menu selection and, in response, generate another event of some kind to let the acquisition processes know what happened. But there is no benefit to having the GUI acting as a middle man, mediating the communications. Better to go with the simple way.

And this is how you do it:

Dynamically Managing Events

If the easy way to handle this requirement is to let the acquisition processes handle the buttons and menus, we are going to need a way to turn events of all kinds on and off — not just timeouts. After all, there are three acquisition processes and we only want one of them at a time managing the shared resources.

To see how that works, let’s revisit for a moment how you register dynamic events. Here is a typical snippet of code (in fact, a subVI named (re)create reuse events.vi) that is registering a couple events. The basic idea is pretty simply. You wire a reference of some kind to the node and LabVIEW will show you the specific events that you can associate with that reference. In this case, we are creating a value change event from a Boolean control reference, and a menu selection event derived from a VI reference.

zombie event builder

By the way, if you plan on building this subVI from scratch, be aware that this situation is one of the few places where LabVIEW cares about the order in which you wire things. To duplicate my results, first create and name the input controls, then wire them to the Register for Events node, finally create the output event refnum indicator, and then duplicate it to create the corresponding input. Once you have the subVI (or at least me implementation of it), this is what it looks like installed into one of the acquisition processes. It’s the one with the red banner across the top.

initializing zombie events

If you are observant you might be wondering how this is going to work because neither of the reference inputs that the subVI uses to create the registration, are connected to anything — which means that the references they are passing to the registration node aren’t valid. The thing is, when LabVIEW creates an event registration it doesn’t check for the references to be valid. Used in this way, the references only have two functions: Define the type of reference associated with the event, and name the event.

This fact is important because it is central to what we are trying to accomplish. As long as the references associated with the registration are invalid, the events are in essence turned off because you can’t fire an invalid event. To turn them on, all you have to do is modify the registration to use valid references. This snippet shows how I have modified the Change Source event and its event handler.

change source with sequence frame

First, I moved the My Name control outside the event loop because I an going to be needing it several places. Second, I modified the event data to pass three references in addition to the name of the source being selected. Two of those references relate to the values we used to initialize the registration. The third reference is a menu reference that we will discuss in a bit. Third, I modified the event handler such that if My Name matches the selector string from the event data, the registration is recreated using the two real references, thus allowing this VI to receive those two events. If My Name does not match the selector string from the event data it executes an instance of the registration VIs that has no references wired to its inputs. Fourth, what in the world is that single frame sequence structure with the wires passing through it for?

In the finished code you will see a subVI sitting there, but I wanted to pass on a tip. Many times when working I will realize that I will need a subVI that I haven’t created yet. To allow me to continue work, I will drop down a single frame sequence structure and wire up the data inputs and outputs that I know it will need. Once that it done, I turn it into a subVI by selecting it and then choosing the Create SubVI option from the window’s Edit menu. As a reminder that I have to go back and finish it, I will leave the new subVI with the default icon.

We’ll get to what this mystery VI does in a bit. But it should be obvious that these changes are useless if there are no error handlers, so I added them in too. Here is what the handler for the button press looks like.

save button key press

We first call our little library function to pop the button back out (since we obviously can’t have the terminal for the button here) and then calls a new subVI to save the data to a text file. This is what the data saving VI looks like on the inside:

data save subVI

Note the comment I put in the code. I will be wanting to come back and reimplement this data saving better so I put a bookmark in the code to remind me.

We are also going to need an event handler for the menu event as well, but before we can build that code, you need to know a bit more about how menus work in LabVIEW.

The 411 on Menus

As I stated before, I am going to devote an entire post to menus in the near future, so the following discusses a very basic implementation.

The first thing you need to know about menus is that to create and manipulate runtime menus you need a menu reference — no surprise there. Unfortunately, LabVIEW doesn’t treat menu references like it does references to other parts of a VI. For example, if you need a reference to VI’s front panel, you can get it from a reference to the VI. Not so with menu references. The only way to get a menu reference is from a special node that has to reside on the block diagram of the VI that will be showing the menu. This “quirk” is why we need to pass two different references to the subpanel VI that both deal with menus: the VI reference allows us to register an event for the menu, but we need the menu reference in order to make changes to the menu contents.

Next, you need to know that every menu item has a Name and a Tag. In many ways these properties are analogous to a control’s Caption and Label. Like a control’s label, a menu item’s tag cannot be changed dynamically because it is the way that LabVIEW uniquely identifies that particular object. However, the menu item name is the visible string you see when you operate the menu and (like a control’s caption) can and often does change during the execution of an application.

Here is the code in the GUI that will define our very basic menu and its three items. Note that I have packaged this logic in a subVI so when I upgrade the menu generation, it will be easier to do. Remember that a good principle for when to make something a subVI is to ask yourself is this functionality (or the implementation of the functionality) likely to change?

building simple menus

Breaking down the logic, the first node deletes all the existing menus. The next node creates a new top level File menu. Finally, the third node is in a loop and it creates a selection to save the data (tagged: Save Data, a dividing line and a Quit selection to stop the application. As I stated before, the tag for each item must be unique. To make meeting this requirement easy, I create hierarchical tags that are a colon delimited listing of the item’s parent tags. For example, the tag for the Quit selection on the File is File:Quit. This structure also makes the tag easy to parse in event handlers for the menu.

The Menu Event Handlers

Given the menu is itself a shared resource, the response to the menu events is also shared. Here is the menu event handler that is in the GUI. It handles the event that related to the application as a whole — Quit. As you would expect, all it does is run the VI that verifies that the user wants to quit before firing the standard Stop Application event.

quit menu handler

Likewise, there is the menu handler that resides in the acquisition processes. It simply calls the same data saving VI we used in the button press event handler.

save data menu handler

In both handlers, menu events for which they are not responsible, are handled by an (empty) default case.

Changing Resource Appearances

Remember the dummy VI we created earlier? Its job is to implement the requirement that the markings on the button and menu change to reflect the process that is managing them. Here is what it looks like:

modifying shared resource appearance

The My Name value is used to generate the new marking which is then applied to the button’s Boolean Text property and a node that uses it to set the menu item’s name. The node uses the item tag File:Save Data to identify the specific item it is changing. If you check out this node using the online help you’ll notice that it also let’s you enable and disable items, and add or remove check marks in front of items.

Code Testing

After making the required modifications to the other two data sources, we are ready to test the code. As you go from one data source to the next you’ll notice that the menu selection and button text change to reflect the name of the source that is being displayed. While you’re at it, test the data saving and see that the application remembers the last place that you saved a file.

This is all on subpanels for now. I have gotten some really good comments from several of you and will rolling some of those questions into future posts. In addition, when you download the code for a post, be sure to go through all of it. There are a lot of things going on in the code that I comment on in the VIs, but don’t have space for in the post.

Testbed Application — Release 10

Toolbox — Release 5

Oh yes, a preview… In the next post I will start looking at another popular design pattern and what a proper LabVIEW implementation of it looks like.

Until next time…

Mike…

Building a Subpanel-Based User Interface

Having discussed some of the main issues related to incorporating modularity into your application’s GUI, we are now ready to look at how to tweak what we have already built into an application that incorporates those ideas. From the user’s standpoint, the program will operate very much as it did before, but with no data transfer hassles: there’s not any data transfers!

Let’s See Them Work!

To see how subpanels work we will modify our existing testbed application. However, these modifications will only require minor changes to the application. Remember, it is not an accident that this sort of modification can be accomplished without a major rework of the code. This sort of adaptability results from a conscious decision to use techniques and methods that are inherently adaptable. Always be thinking ahead…

And if you do think about what we are doing here you realize the required modifications will be minor in scope because there really isn’t very much in the code that fundamentally cares how the interface is structured. In fact, there are only two places that will need to be changed. The GUI needs to know what to do when a different screen is selected — this logic needs to change. Likewise, the acquisition processes (which previously ran unseen in the background) now need to be prettier, but on the whole they will be doing less than they did before.

Making Background Task Presentable

So let’s start by turning the background tasks into something we would want the world to see on our application’s main interface. It only takes a glance to notice that the existing graphs are too small, so the first step in the transformation is to resize the graphs on our three data sources and make them all the same size as the existing graph in the GUI. In addition, because all three processes will be going into the same subpanel, we also need to make sure the front panels of the three acquisition processes are all the same size: slightly larger than the graph. While we are doing cosmetic things, we also want to make sure that the front panels are the same color as the main GUI.

The functional modification needed to make these VIs live happily in a subpanel is to go into their VI properties and in the Window Appearance section, turn off both scrollbars.

window appearance settings

We now turn to the code changes. In order to put a VI into a subpanel you write a reference to the VI to a subpanel control method called Insert VI. The thing is, retaining references to background processes that you launch can get tricky. This point is especially true when you need to launch multiple instances of a reentrant VI. Although the testbed isn’t (currently) using reentrant processes, the software does support it so we need to think about how to get references to the processes that will be going into the subpanel. One simple way to accomplish this goal is to have the processes “publish” a reference to themselves that can be used elsewhere in the code. To implement this approach, I created a buffer that has two public interface VIs. One appends a reference to the buffer contents and one reads the buffer contents. Note that we won’t take the time to look at the code for these functions here because we have examined this structure before.

To use these VIs, we put an instance of the insert VI at the very beginning of the call chain in each of the acquisition processes. Like so:

inserting vi reference

The only other changes we need to make to the acquisition logic both relate to the timeout value for the loop. We no longer need the acquisition to wait before starting so a single keystroke will change the “-1” timeout in the initialization logic back to “1”.

resetting the timeout

Likewise, we don’t need the Change Source event right now, but we may need it again (soon) so we’ll leave the event handler in place, but modify the code so it doesn’t do anything.

change source doing nothing

…and that is all we have to do to the acquisition processes.

Adding the Subpanel to the GUI

With the acquisition processes modified, all we have to left to do is change the front panel control on the GUI, and alter what happens when you make a Data Source selection.

Changing the front panel control consists of simply removing the graph and replacing it with a subpanel. One thing you need to think about is the size that you are going to make the subpanel. I have found through experience that you get a better looking result if you make the subpanel a skosh bigger than the front panel of the VIs that you are putting in it. For the purposes of this discussion a “skosh” is an empirically determined constant equal to 4 pixels, so make the subpanel 4 pixels taller and wider than the acquisition process front panels.

You’ll note that when you place a subpanel on a front panel, LabVIEW also installs an invoke node for it on the block diagram, we’ll use that in a little bit. The only other thing you need to do that is related to the graph is remove the VIs implementing the update UDE, and delete the associated event handler.

Note: Normally I would put a screenshot here illustrating the change, but in this case I can’t think of a good way of showing something which is no longer present, and isn’t being replaced by something…

Concerning the Data Source value change event, this is where we get to use the invoke node that LabVIEW created when you instantiated the subpanel. Remove the VI for firing the UDE, and wire in the invoke node in its place. As you can see the node has one input that is a reference to the VI that is to appear in the subpanel. Now, our technique for getting that reference is remarkably similar to what we did to get the data value for the UDE. The difference is that instead of selecting one element from an array of strings, the same array index node is now selecting one element from an array of VI references — references that come from the read VI for the reference buffer we discussed earlier.

new data source selection operation

Running the Result

If you run the application that we have created, you will notice that (with the exception of a couple points) the operation is very similar to the way it worked in the last release. As you make selections, you can observe that the data displayed still changes as it did before. But if you watch carefully, you will notice that the plugins continue to acquire data even when you aren’t watching them. Moreover, if you go back to a screen after having not looked at it for few seconds, you will notice that it will initially show the previous data and them update all the “missing” datapoints at once. This might seem strange, but it is the side effect of a very good thing that LabVIEW does for you.

Knowing that updating screens that aren’t currently visible can use a lot of computer resources unnecessarily (especially if they have charts on them), LabVIEW keeps track of front panels that are visible and only takes the time to update the data displays if the screen is visible. The delayed update that you see happening all at once is the result of LabVIEW realizing that the front panel is now visible and sending all the updates to the chart at once. On simple controls and indicators, these updates typically happen so fast that you have to really be looking for them, but I wanted you to see the effect.

Note that in the previous paragraph I did not refer to front panels that are “open”. The thing is, not all screens that are open are visible. For example, VI’s running in the background (like the data acquisition processes used to be) are open but Hidden, i.e. not visible. Conversely, the front panels of VIs in subpanels are visible, but they aren’t open. In any case, here’s the updated code.

Testbed application — Release 9

Project Toolbox — Release 4 (No Change)

Before we move on to the next thing I want to cover, I’m going to hang-out with subpanels for one more post. Specifically, designing VIs in subpanels to be completely autonomous processes is a very powerful technique, but sometimes a good way to foster reusability is to have the main GUI provide some standardized, reusable resources that the various subpanel plugins can use. To cover that situation, next time I’ll look at ways to manage shared resources like buttons and menus from subpanels.

Until next time…

Mike…

Modularization – It’s not just for Block Diagrams (Front Panels can play too!)

At the very end of my last post I suggested that perhaps the best way to display user data on your application’s GUI is to avoid sending data to the GUI in the first place. Of course the big question is how the GUI can display data that you don’t send to it?

The programmatic slight of hand that explains this paradox is a feature of LabVIEW called a subpanel. To understand how subpanels fit into the overall LabVIEW “ecosystem”, let’s review for a moment. As anyone who has used LabVIEW for more than 5 minutes will recall, every VI you create has 3 main parts:

  1. The Front Panel — This part provides the VI with a user interface for interacting with the routine.
  2. The Block Diagram — This window is where you put the graphical source code that describes what you are trying to do.
  3. The Connector Pane/Icon — This part allows you to call your VI from the block diagram of another VI.

Now all programming environments have places to enter source code and methods for calling or reusing other code, but the Front Panel is really unique to LabVIEW. With most languages, you have to write code to create the GUI, but with LabVIEW, the GUI is part of the code. Just as calling one VI from the block diagram of another VI is a way to modularize your program’s logic, so subpanels provide a way to modularize your program’s user interface by incorporating the front panel of one VI into the front panel of another VI.

But Why Subpanels?

To be honest, there are other techniques for “modularizing” user interfaces within LabVIEW, and we’ll take a quick look at a couple — though to be honest we should call the result of one technique “pseudo modularity” because while the interface looks modular, it’s really not.

First we want to consider XControls. A couple years ago at NIWeek, I presented a session on how to use XControls, so it’s a topic with which I am familiar. XControls approach the question of GUI modularization by providing a mechanism for creating custom front panels controls that implement custom functionality not found in standard controls. Although they are very powerful, they have some issues:

    Problems with XControls

  • Complexity: XControls are difficult to create and require both greater skill on the part of the developer and advanced knowledge of how LabVIEW works internally. As you can imagine, an XControl of even modest complexity can take a long time to develop. The result is that the functionality they encapsulate must be sufficiently reusable to justify the effort required to create the XControl.
  • Can Complicate Debugging: One of the “interesting” aspects of using an XControl is that they are almost always running. Of course, when you think about it, this makes sense. A control obviously has to exhibit some useful behavior at run time. However, during development they must also be able respond to changes that the developer is making. For an XControl this requirement means that, on some level, it is running as soon as you drop it down on a front panel. While this fact obviously complicates the process of debugging the XControl itself, it can also adversely effect the debugging of the program you are trying to create — especially if the XControl uses some of the same libraries that you use in creating your program. You can see limited access to subVIs because they are being called in the XControl and so are already running, or are at least reserved for execution.
  • Support is Limited: Actually, “stinks on ice” is probably closer to the truth. Documentation is poor, examples are practically nonexistent, and because NI considers it an advanced topic, tech support is often no real help.

Tab Controls — Just Say “No”

This technique is the one I described as “pseudo modularity”. As a control metaphor, the tab is familiar to anyone who has been around computers for a while. However, operating system designers have been moving away from tab interfaces for a variety of reasons. To be fair, I would like to start this section with some of the positive things about tab controls, but to tell the truth I can’t think of any…

    Problems with Tab Controls

  • Complexity: While a tab control might help visually organize the 30 or 40 controls and indicators on your front panel, the block diagram can rapidly become a mess as you try to deal with the functionality associated that many terminals. Moreover, because the tabs hide complexity, they increase the likely that your front panel will have 30 to 40 controls and indicators.
  • No Reusability: Let’s say you create really nice a user interface on one tab of your application. Now let’s further postulate that the interface is so good that another internal customer wants you to build them an application that uses the same interface. How do you do it? Well, with tabs, your option is to cut and paste the controls and then rebuild the logic on the block diagram.

    Of course that’s just the beginning of the challenges. You still have to verify that the code works the same in the new context, and you have to hope that the new program’s timing doesn’t effect how the interface works.

  • Compatibility Problems: It is unfortunately not uncommon to see posts on the forum where someone is wanting to know why common controls and indicators don’t work as expected when they are on tab controls. Such questions are not uncommon because, to be perfectly frank, LabVIEW has a long history of tab controls causing a variety of screen update and performance problems.
  • Confused VI Server Interface: Normally when working with controls and indicators the terminals of which are on your block diagram, finding there references programmatically is easy. With a reference to the VI’s front panel you can get an array of the controls on the front panel, and the reference to the control will be in that array.

    But a tab control complicates the whole process. First you get a reference to the tab control, from that you get an array of references to the pages in the tab control, and once you have found the right page you can get the array of control references containing the control reference you want.

A Good Solution for GUI Modularity

But there’s another way to answer the question: “But Why Subpanels?”: the advantages that subpanels offer. But please note that you can only expect to realize these advantages if you use subpanels properly — which is to say, VIs appearing in the subpanels are autonomous processes that encapsulate the logic for a single operation and, when necessary, display the results of those operation on their own front panels.

    Subpanel Advantages

  • Subpanels Simplify Code, Reduce Memory Footprint and Improve Efficiency: When using subpanels you are able to realize these benefits because the minimal code required implement them is almost always smaller than the code you take out that implemented the redundant communication and display formatting operations. In addition, remember that because you are reducing the data manipulation the code is performing, you are also reducing the memory (and CPU) needed to manage it.
  • Subpanels Make Code Robust: When designing a process VI that will run in a subpanel, it is easier to create highly cohesive code with very low coupling between it and the remainder of the application.
  • Subpanels Generalize GUI Structure: Working with subpanels inherently puts you into a mode where you start thinking about the GUI at a higher level. One side-effect of thinking in this way is that so much application-specific logic is moved into subpanels that, in the end, the GUI itself doesn’t know or care what application it is running.

    This point is particularly interesting because the interface could get so generic that it could be divided up into separate dedicated areas like this:

    model screen - with notes

    Normally the subpanel frames would not be visible, and there would be no annotations in each section, but then the front panel would be basically blank, and very uninteresting to look at. To get an idea of what could go in each section, consider these descriptions. Remember, not all of these sections would necessarily be on every screen, and their sizes can be adjusted to fit requirements.

    Header: The intent of the header subpanel is to show VIs that present information that is unlikely to change as a function of the screen being shown in the body. This area could include things like the current time, the operator who is logged in, total operating hours or the name of the display.

    Footer: This subpanel extends across the entire bottom of the screen. It could hold VIs that provide things like a status display, or a scrolling alarm status list. Like the header, this section ideally wouldn’t change during the course of program execution.

    Body: The application’s main display VIs would go in this subpanel.

    Sidebar: The VIs providing this section’s contents of could include things like navigation buttons, or supplemental information that is related to the main display.

    If you have ever done any web development, this basic layout might look a little familiar, which is appropriate since I “appropriated” the idea. One of the key concepts of the web is to separate content from where and how the content is displayed to the user. This approach in LabVIEW is a (small) step in that direction with the subpanels providing the display structure.

So combine these advantages these with a host of others, and you can begin to get a feel for why I consider subpanels to be among the top 5 features added to LabVIEW — ever.

Let’s See Them Work!

To get our first taste of subpanels in action, we won’t go to the extent of creating a fully abstracted interface like we just saw. Rather, I will show how to manage one subpanel — and you can expand it from there, however this post is already getting long, so we’ll have to wait until next time to look at that code.

Until next time…

Mike…

Turning a VI On and Off in Software

Something you hear about a lot on the LabVIEW users forum are people who want to stop a VI and then restart it. They will often say things like,

“I have a VI that acquires some data and displays it to the user just the way I want. Now I want to run it several times, but change the test setup between runs – and my customer wants it all done automatically. I have code that can do the test setup, but now I need to run my acquisition and display VI and I can’t figure out how to programmatically click the ‘Abort’ and ‘Run’ buttons.”

Clearly, they can’t actually click the button, but if you consider the situation for a moment you realize that they don’t actually need to start and stop any VIs to obtain the desired behavior. All you really need is a way to put VIs into a mode where they don’t do anything until you are ready for them to continue. It’s, likewise, not unusual to hear from people who are trying to sort out how to add interactivity to a state-machine the normally runs without human intervention. Of course this is why the whole question is interesting: We all run into situations where we want the code to stop and wait until something happens.

Obviously, if you put the problem that way its easy to see that we have just described an event-driven application – which we have just spent several weeks building.

A Little Review

However, to fully realize our goal of implementing “stop-start” behavior, we need to give further consideration to the timeout events we have been using. If a VI is truly going to be dormant when we “turn it off” and then go back to normal when “turned on” we need to first have the code that defines the VI’s “normal” operation (whether it be sequential logic, a state-machine, or something in between) located in the timeout case. Then second, we need to be able to disable and then re-enable the timeout event.

Already in the testbed code we have seen that you can use a negative timeout to turn off the timeout event – as we do in some of the logic for the error checking initialization code. The thing is, this technique has a lot more to offer than as simply a way to trap startup errors. As an example of what else it can do, we are going to expand our testbed application such that instead of simply reading data from one process, it will be capable of drawing data from three different processes. One will return random numbers, one a sine wave and one a ramp signal. To select the input to read, we’ll have a pop-up menu on the display process’s front panel. Although this would seem to be a pretty significant change, you’ll see that it’s really not.

Modifying The DAQ Process

The first thing to note when considering the changes we will need to make, is that the basic infrastructure for this new capability already exists. We already have a structure where we are dynamically changing the timeout event’s timeout.

  1. When it the acquisition process starts, the timeout is set to zero.
  2. If initialization is successful, the timeout is set to 1 so acquisition is started.
  3. Each time a new datapoint is acquired and sent to the GUI, the timeout is set to the value read from a FGV.

All we have to do is expand on this existing functionality, and the first step in that process is to change the behavior in Item 2 above. Currently, the code wants to start acquiring data as soon as it can, so when the error check finishes, the timeout is set to 1. We want to change that behavior because we don’t want the process to start acquiring data until it’s turned on. To implement that change requires a single key-stroke to change the “1” to a “-1”.

Now that the code is set to wait until it is enabled, we need a way to turn it on – and by extension turn off all the processes that are not being enabled. Hopefully, you are thinking ahead and already see where I am going with this: another UDE. Let’s call it something logical like Change Source, and make the data it carries a string named Source Name. Normally, you might think that this would be a place to use something like an enumeration, and normally you would be right. However, there are exceptions to all rules, and as you will see this is an interesting exception.

With the new UDE created, here is its event handler…

the Change Source event handler

…and as you can see it couldn’t be simpler. We compare the Source Name value to the contents of a string control called My Name and if they match, the timeout is set to 1, otherwise it’s set to -1. Because all three data sources will incorporate this same logic, the effect is that only one of the three – the one whose name matches the Source Name value – will be enabled.

But where does My Name come from? I added this control to the front panel and connected it to a terminal on the connector pane. We will need to modify the startup code to populate this just before running the process VI – and that’s our next stop.

Oh, one more thing: Now that we have the acquisition process modifications done,(yes, that is all there is to it…) I made two copies of it changed the data generation code in the copies to produce a ramp and a sine wave, and added the new acquisition VIs to the project.

Passing a Startup Value

To this point, the process VIs haven’t needed any information when they were launched, but now the acquisition processes at least need to know in essence “who they are”. Here is the modified testbed.vi code:

modified launcher for passing identification string to plugin

The VI reference to the Open VI Reference node now shows the added string input terminal, which also appears on the Start Asynchronous Call node. To that new input, I have wired the Label value from the launch process data. Remember that, it will become important in just a moment.

The only other modification required is to add a VI, after the launch loop, that broadcasts a signal to the system telling it that the launch process is finished. Let’s take a quick look at why that signalling is needed and how it works.

Signalling Completion

During startup it can be critical for things to happen in the right order. Most of the time you can ensure proper sequencing by simply launching processes in the right order. Sometimes, however, conflicting requirements can leave you with no “right order”. As you might imagine, there are many possible scenarios requiring this sort of synchronization, and just as many potential solutions from which to choose. Moreover, as with Boolean logic, the “correct” solution will sometimes depend on how you are thinking about the problem.

In our testbed, we see a common situation: I like to have the display process launch very early in the startup process. In fact, the only thing that will typically launch before the GUI is the exception handler – which always comes first because the error handling has to be in place before you do anything. The GUI comes next because it is often the logical place to initialize common system resources that the acquisition processes will need when they start. The conflict lies in the need for the GUI to initialize its own front panel. Now that there are three acquisition processes running, the GUI had to select which one to display first. However, before one can be selected, the process VI has to be running and it won’t launch until after the GUI.

The solution to this conundrum is another type of “stop-start” situation. We need to let the GUI initialize everything except its front panel and then make it pause and wait until it receives a signal telling it that the launcher is done. Once that signal is received, the GUI can finish its initialization and commence normal operation.

To implement this functionality in a generic, reusable way, I have created two simple VIs that encapsulate a named LabVIEW notifier. Here is the routine that waits for the notification:

vi to wait on startup notification

The VI is contained in a library that creates a name space for the logic. The VI first acquires a reference to a notification, the name of which is derived from the name of the library containing it &ndash: in this case Launch Completed. It then waits for that notification to occur, but with a timeout specified. To make this code easier to use, I modified the API slightly to allow the developer to define the timeout in units of time. Finally, if the wait ends in a timeout, I generate an error to that effect.

The VI for sending the notification is equally simple:

vi to send the startup notification

Located in the same library as the wait VI, the sender also derives the notification name from the library name and then sends the notification. An important point to remember is that notifiers are often likened to queues because on the surface they seem very similar. However, in addition to the fact that a new notification will overwrite an old one, there is another big difference. If you have multiple listeners monitoring the same queue, LabVIEW channels the enqueued data to the various VIs in round-robin fashion. For example, say you have three VIs all pulling data from the same queue, any one VI will only see every third item enqueued. By contrast, a notifier works more like an event in that if you have three VIs waiting for notifications the same notifier, they will all see every notification.

Adding Control to the GUI

So to recap, we have modified our existing acquisition process, created two new ones using it as a template, and modified the startup logic to pass a new piece of data that the acquisition processes need. The only thing left now is to update the GUI.

Start on the front panel (always a good place to start), add a test ring control and label it Data Source. But a text ring in the current state isn’t very useful because it doesn’t contain anything. To fix that, we will (using a property node) insert into the control an array of strings that we want to appear as selections. And where do we get the array of strings? Here’s the code:

initializing the ring control

Using data from the same VI that the launcher used to obtain a list of startup processes, we generate an array of all the process labels, delete the first two elements (as discussed earlier, the exception handler and display processes) and write the remainder of the array to the ring control’s Strings[] property. The result is a popup menu containing the names of the acquisition data sources.

Next, we add an event case for a value change event on the ring control.

ring control Value Change event handler

The ring value is a numeric, so the event handler uses it to index an element from the array of process labels, and uses the resulting string to fire the Change Source event. This is why we can use a string as the event data: Both the string we are using the fire the event, and the strings we are using to tell the processes who they are, derive from the same source: the process labels. Consequently, it is impossible for them to not match.

Finally, we need to look at the GUI initialization logic.

wait for the notification, then finish GUI initialization

The first subVI called is the wait routine discussed above. When that notification is received, all the processes are loaded so the GUI can finish initializing itself by firing two value change events: one to set the sample period and a second to select the initial data source.

Summing Up

As usual, the completed code is below and when you run it you will now see that you can select between the three data sources we discussed. However, there is another lesson to be learned. I started this discussion saying that one might feel justified in saying that it would be a major undertaking to make such fundamental changes in the way an application operates. But we have just seen that it really is not. I can honestly say that describing what I did took much longer than making the modifications – and the reason is simple. I didn’t have to write very much code. Instead I was able to leverage features that were already built into the code, and reuse ideas that had already served me well in the past.

The sort of maintainability that allows our code to be nimble and not brittle should be a goal of all that you do, but it takes practice. Practice, and an understanding that learning never stops because there is always more to learn about the craft that we practice.

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

Next time, we are going to take this line of work a step further and consider a more complex case. Sometimes you don’t want the data sources to stop acquiring data when they are “inactive”. Rather you want them to continue monitoring their respective inputs even when they are running in the background. But, how do you then manage the data transfer to the GUI? Well, perhaps the best solution for effectively displaying process data on the GUI is to never pass process data to the GUI. Sound confusing?

Until Next Time…

Mike…

One quick “PS”. Now that I am getting a good number of these posts done, I have been thinking I would like to have them available in languages other than English. I’m not exactly sure how it would work yet, but if you would like to help with the effort, please contact me.

Talking to a Database from LabVIEW

Ok, this is the third part of our discussion of how to effectively utilize databases in a LabVIEW-based application. To recap, In Part 1 we have covered the basic justifications for utilizing databases, and checked-out some drivers that implement basic database connectivity. Part 2 expanded on that information by going into the (very basic) basics of how to design and build a database. I also provided some links for further reading, and a LabVIEW tool for executing an SQL script to turn it into an actual database. What you are looking at now is Part 3 and it will present code that implements the LabVIEW side of the data management capability we discussed earlier. If you haven’t read the other two sections, I would recommend that you do so before continuing — don’t worry, we’ll wait for you.

Those of you who did read the previous portions, feel free to talk among yourselves until the others get back.

Everybody back? Good! Let’s continue.

Example Code

The database we have implemented so far covers three basic areas in the Testbed application. Something that I didn’t mention before is that these areas were not picked at random, or arbitrarily. Rather, if you look at them you see that each one presents an example of one kind of database operation while presenting useful concepts that you will want to know about in the future.

  • Processes to Launch: This section demonstrates data that has an inherent structure as embodied in its one foreign key relationship.
  • Event Recording: Here we considered a table to which applications will eventually write. It also shows a little more structure in that it relates to two different header tables: one that identifies the application generating the event and one that identifies the type of event that is being recorded.
  • Default Sample Period: Although much of the data in a system will be structured, there is still a place for simple setting such as you might store in an INI file. This last example showed such a situation.

Carrying forward with this idea of demonstrating a variety of concepts, as we go through the code that implements the LabVIEW side of the connection, I will point out a few different techniques that you will find useful. The thing to remember is that there are engineering decisions that you have to make and no one technique or approach will serve in every possible situation.

Reading Processes to Launch

The new VI that performs this operation is still named Configuration Management.lvlib:Get Processes to Launch.vi and has the same basic structure as the INI file version (which I renamed), but the configuration file IO logic is replaced with the database IO drivers I presented earlier.

Read Processes to Launch

Although the structure is basically the same as before, there are a few changes due to the improved structure that the database provides. First, there is a new input (Launch Condition) that is tied internally to a database search term. Second, the output data structure is modified to utilize the enumeration for the launch mode, replacing the boolean value used before.

In terms of the query code itself, the large SQL statement in the string constant is for the most part pretty standard code. The statement specifies what columns we want (label, item_path, launch_mode), the table they are in (launch_item) and the WHERE clause provides the search terms that define the output dataset we want. Likewise, note that although I never read the launch_order value, I use it to sort the order of the results. If you have data that needs to be in a specific order this is an important point. Unless you explicitly tell the DBMS how to order the results, the sequence of records is totally undefined. The only real complication is contained in the WHERE clause.

You will recall from our discussion of normalization that the two primary search terms we will be using are stored as ID numbers that reference a pair of header tables. These header tables contain the human-readable labels that we want to use for our searches. This code demonstrates one approach to resolving the ID references through the use of subqueries. A subquery is (as its name suggests) a small query that occurs inside the main query. They are typically employed to lookup data that the calling application doesn’t directly know. In this example, we know that the application name and launch condition, but we don’t know what ID numbers are associated with those inputs. The subqueries look up those values for us so the main query can use them to get the data we want.

The advantage of subqueries is that they allow you to specify what you want in the terms that are meaningful to the calling application. The disadvantage is that they can complicate SQL code and the resulting code can be more DBMS-specific. In addition, with some DBMS (like for example, Jet) there can be a significant performance hit involved in subqueries. You’ll note that the example code mitigates this potential issue by buffering the search results, thus ensuring that the “hit” will only occur once.

Saving Errors to the Database

This operation is performed by a new version of Startup Processes.lvlib:Store Error.vi. As with the original version, the input error cluster provides the information for the error record, and the output error cluster is the result of the record insert.

Store Error to Database

This code shows an alternative to using subqueries to obtain the ID references. The two subVIs look-up the required IDs in the database the first time they are called and buffers the results for later reuse. The two routines are very similar, so here is what the application ID VI looks like:

Get Appl_ID

The advantage of this approach is that the required SQL is much simpler and very standard. The only real “disadvantage” is that you have to create these buffers — which really isn’t very much of a disadvantage. I really like this technique for situations where there are common IDs that are used over and over again. Unfortunately, this sort of modularization isn’t always possible, in most real-world applications you will need to know both techniques.

Read Default Sample Period

You will recall that this is new functionality so there is no “old code” to compare it to. Here is one option for what Configuration Management.lvlib:Get Default Sample Period.vi could look like.

Read Default Sample Period

The code here is very similar to that for reading the processes to launch, the main difference being that two of the search terms are in essence hardcoded. Current, there is only one parameter being stored in this table, but that situation could change at any time. While replicating this VI for each additional parameter would work, the result could be a lot of duplicated code. Consequently, I typically prefer to start with a lower-level VI that is something like this…

Read Misc Setup Values

…and then make the VIs like Configuration Management.lvlib:Get Default Sample Period.vi simple wrappers for the buffer.

Read Default Sample Period - Improved

Hopefully by now you are beginning to see a bit of a pattern here. Every type of access doesn’t require a unique technique. In fact, there are really very few unique use cases — and frankly we just covered most of them. The real key is to get comfortable with these basic methods so you can customize them to address your specific requirements.

Moving On…

So we now have our database, and the VIs for accessing it. How do we roll this into out application? One way is to simply replace the old INI file versions of the VIs with the new database-oriented ones — which is what I have done for now. I say. “…for now…” because there is a better solution, but that will have to wait for another day. Right now, the solution we have is serviceable so I want to get back to a couple more important topics. Here’s the updated application:

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

Until next time…

Mike…