Implementing Dynamic User-Defined Events

In the previous post, we started looking at how to deal with the situation where you have reentrant VIs that need to have UDEs that are specific to particular instances of the VI. That post covers a lot of the basic theory and design issues, so if you haven’t read that post, please take a few moments to check it out as much of the following won’t make sense without that background.

Filling in the Blanks

The basic approach we took in designing our example consisted of first defining the program’s overall intended operation. With that theory sorted-out, we then began creating the program’s high-level structure, being sure to leave blanks or prototypes as placeholders for details that we hadn’t yet defined. At one time this sort of approach would have been considered heresy. The feeling was you hade to have everything worked out in detail before you wrote even one line of code or created even a single VI.

Clearly there are a lot of practical problems with this approach, but for one of the biggest (in terms of long-term impact on the project) is that it leads to a break-down in the walls that information hiding is trying so hard to erect. Think about it for a second, if an integral part of designing a process launcher includes figuring out the internal operation of the processes it is going to launch, it’s going to be very difficult to keep that knowledge from contaminating your launcher design. Or to put it another way, there will be a strong tendency to create code that depends on the process VIs behaving in a particular way.

You can see a better approach in the way we designed the launcher for our reentrant VI. Last time we defined a VI that (beyond the mechanics of how to launch a reentrant VI) only concerned itself with the data that the process VIs need to do their work. Based on a black-box description of what the routines were supposed to do, we able to create the logic for providing that data without worrying about what the routines were going to do with it. This is a Very Good Approach.

Getting Registered

One of the “blanks” that we left to be filled until now was a VI called DAQ Operations.lvlib:Interval Registry.vi. When we first considered this VI’s external persona we noted that, when passed an enumerated value Check and an interval, it would return a boolean flag indicating whether or not that interval already existed. However, we made no assumptions about how it might go about completing that task. Having taken, so to speak, a step inside the veil we can look at how it works.

Interval Registry - Check

This is the logic that completes the Check function, and as it turns out, there isn’t really very much to it. In fact, we see logic that implements a simple FGV. All the function in question really does is search an array of intervals to see if the one we asked about exists. Given this level of simplicity, a logical question would be, “Why bother with the subVI? The process manager has a loop, why not just use it to maintain the list of active intervals?”

This approach would handle half of the problem quit well – the half dealing with whether or not a particular interval was launched. But if you think about it, that isn’t what we really need to know. Operationally, we don’t care if a particular interval was ever launched. We want to know whether the interval is still running, and the interval’s current state is something of which the manager has no knowledge. Remember that when we defined the rules governing the high-level behavior of the acquisition subsystem, we said that when an acquisition process sees that it has no addresses left to poll it will shut itself down. How is the process manager supposed to know that happened?

Beyond this practical consideration, there is also a conceptual problem associated with storing the interval list in the process manager. You might not realize it, but all data has an associated “scope” that defines the area, or context, which owns the data. If we store the interval list in the process manager we are, conceptually, giving ownership of a piece of information that should belong to the subsystem as a whole, to one specific VI. Moreover, that one specific VI would then be required to manage all accesses to that data. Now while that functionality could certainly be implemented, it would be at the cost of additional complications in the form of additional signalling, and event handlers to respond to those messages.

By contrast, moving the array into a FGV that any VI in the subsystem can access, makes the data immediately available to the subsystem as a whole: No event handlers, no added signalling. As we get into the VI that performs the (simulated) Modbus IO Collect Data.vi, we see how the FGV’s remaining two functions work.

Keeping Time

Next, let’s look at the reentant VI (Metronome.vi) that is responsible for triggering acquisitions at a fixed interval. You will recall that the VI is passed three parameters when it is launched: The desired interval between acquisitions, the event to fire when the interval times out, and the event that will fire when the interval is shutting down. Here is the logic for initializing the VI.

Metronome - Initialization

We see that the interval value drives the Timeout node of an event loop, the shutdown event is registered, and the event that this VI will fire, is simply passed into the event loop. As you might expect, the event loop itself only has two events. Here is the Timeout event:

Metronome - Timeout

No surprises here: all the event needs to do is fire the acquisition event – which this code does with alacrity. The shutdown event (which handles both types of shutdown) is likewise uncomplicated:

Metronome - Stop Interval

It destroys the two dynamic UDEs and stops the event loop. The acquisition event is destroyed because once a shutdown is initiated, it is no longer needed. The interval stop event is destroyed because it is only fired in the acquisition VI, so by the time we get to this point in the code it has already been fired by the only other VI that needs it. The small delay between the two operations is to ensure that the acquisition VI has time to start shutting itself down.

Getting into the Publishing Business

The last code we need to look at is the reentrant VI that reads the simulated data and publishes it for use (Collect Data.vi). Again starting with the initialization logic, we see something very similar to the metronome VI:

Collect Data - Initialization

The main difference is that this VI needs to register to receive both dynamic UDEs. Although the VI will be generating the Delete Interval event it also needs to be able to respond to it because that context is the best place put the logic for deinitializing any acquisition logic that might be used. Another conceptual difference from the metronome is that this VI relates to the interval input in a way that is fundamentally different. For the metronome the interval is a number that has a specific meaning: it is the number of milliseconds between triggers. For this data collection routine however, it is simply a number that provides it way for it to identify itself. The broader point is that you need to remember to manage the expectations when different processes look at the same value and see different things.

Because this VI is going to be acquiring data, and performing other operations, its event loop is also more complex. Let’s look at the 5 events it will handle:

Timeout – This application is only generating simulated data, but in most other situations, there will need to be some sort of initialization performed, like opening DAQ references or establishing connections to one or more Modbus devices, and this event is a good place to handle such initialization. The way this logic is written, it is easy to make the initialization run just once, but still have it readily available if it ever needs to be run again.

Collect Data - Timeout

However, there is other initialization that needs to be performed even for simulated data. Specifically, if the initialization logic completes with no errors, we need to tell the rest of the application that this process is open and ready for business. To record that state change, we use our “repository” VI again, but this time running the Insert logic…

Interval Registry - Insert

…which is the very soul of simplicity.

Start Addresses – This event’s purpose is to maintain the process’ internal list of addresses in response to the user starting additional addresses. In completing this work, there are two cases that it will need to address.

Collect Data - Start Addresses - Adding

First there is the case where the interval number matches the value that the process is using to identify itself. In that situation the code needs to add the new addresses to the existing list, or more correctly, it needs to add addresses to the array that don’t already exist in the array. This logic protects the logic from what would be a very common operator mistake: adding addresses tht already exist. The second situation is one where the interval number does not match the value that the process is using to identify itself.

Collect Data - Start Addresses - Delete Case

In this case, we need to enforce the rule that only one process can be polling a given address. Hence, the logic needs to remove from its array any addresses any addresses contained in the new event. Of course this operation raises the spectre of the entire polling array being emptied out, with the contingent requirement that the interval shut itself down. The logic handles that scenario with a two-step process. First it tells the rest of the application that its stopping by calling the registry VI using the delete logic.

Interval Registry - Delete

After removing itself from the registry, the VI fires the Stop Interval UDE to close both itself and its associated metronome process.

Stop Addresses – This event has logic that is similar to the previous event’s delete logic, but is simpler because the only thing that matters is whether the indicated address is in the process’ address list.

Collect Data - Stop Addresses

Get Data – This event generates an array of simulated data and passes it to the static Publish Data UDE, along with an array of addresses associated with the data values. In addition for troubleshooting purposes, the code also writes the data to a front panel table.

Collect Data - Get Data

Stop Interval; Stop Application – Finally, this event stops this VI if either the interval or the application as a whole is shutting down.

Collect Data - Stop Interval

As the comment says, if the acquisition logic needs to be deinitialized, this in the place to put that logic. But why is the code interested in the front panel’s state? Although this VI usually runs in the background unseen, there are times that you want to be able to view its front panel so you can verify its operation. In this example, I implemented that functionality using the VI properties to force the front panel open when it starts running. This logic checks to see if the front panel is open and, if so, closes it.

Testing the Code

So with all the code implemented, here are the Subversion links to the application an the toolbox of reusable code:

Dynamic Registration – Release 1
Toolbox – Release 18

The first thing I would recommend after downloading the code, it to go through it while re-reading both this post and the previous one. Often times it is easier to understand things when looking at the code, that are otherwise a bit obscure when all you have are pictures of the code.

Next run the top-level VI (Dynamic Registration.vi). When the front panel opens, click on the Add Addresses button to define some addresses. For the purpose of this example, I created a simple dialog box that lets you specify a starting address, the number of consecutive addresses to collect, and the sample interval. The starting address needs to be between 40000 and 49999, the number of consecutive address must be less than 1000 and the sample interval needs to be between 300 and 5000 (milliseconds). These parameter limits are set in the dialog box code – feel free to change them as you desire. Likewise, the output of this dialog box is a list of addresses, so you can also change the selection interface if you so desire.

To get started, define 5 addresses starting at 40000 with a sample interval of 1000-msec. You should see the front panel of acquisition VI pop open showing the 5 addresses being updated once per second. In addition, the main GUI should show data from the same 5 addresses.

Click the Add Addresses button again, but this time define 5 addresses starting at 40008, and updating every 2000-msec. A second acquisition VI windows will open showing the 5 new addresses, and the main GUI will show a total of 10 addresses with the results changing at different rates.

Let’s next see what happens if you specify addresses that are already being polled. Click the Add Addresses button one more time and define 5 addresses starting at 40004, and updating every 3000-msec. This action defines a range where 40004 is already being polled once a second and 40008 is being polled every 2 seconds. In response you will see a third acquisition window open, but you will observe that one address is removed from each of the two existing polling lists, and that the main GUI shows a total of 13 addresses being polled.

Finally, to test the auto-shutdown operation click the Delete Addresses button and in the resulting dialog box, tell the system to delete 5 addresses starting at 40008. Because the 2000-msec interval is emptied out, the acquisition VI window associated with that interval will close. Finally, the polling list for the 3000-msec interval will be reduced by one.

The Big Tease

So that’s about all for now on this topic, but what’s in store for next time? One of the things that I like to talk about in this venue are things that can cause unexpected complications, so next time I’m going to discuss what happens when a DLL misbehaves as you are trying to close it.

Until Next Time…
Mike…

It’s a big interconnected world – and LabVIEW apps can play too

If I were asked to identify one characteristic that set modern test systems apart from their predecessors, my answer would be clear: distributed processing. In the past, test applications were often monolithic programs – but how things have changed! In the past we have talked several times about how the concept of distributed processing has radically altered our approach to system architecture. In fact, the internal design of our Testbed Application is a very concrete expression of that architectural shift. However, the move away from monolithic programs has had a profound impact in another area as well.

In the days of yore, code you wrote rarely had to interact with outside software. The basic rule was that if you wanted your program to do something you had to implement the functionality yourself. You had to assume this burden because there was no alternative. There were no reusable libraries of software components that you could leverage, and almost no way to pass data from one program to another. About all you could hope for were some OS functions that would allow you to do things like read and write disk files, or draw on the screen. In this blog we have looked at ways that our LabVIEW applications can use outside resources through standardized interfaces like .NET or ActiveX. But what if someone else wants to write a program that uses our LabVIEW code as a drop in component? How does that work?

As it turns out, the developers saw this possibility coming and have provides mechanisms that allow LabVIEW application to provide the same sort of standardized interface that we see other applications present. Over the next few posts, we are going to look at how to use incorporate basic remote interfaces ranging from traditional TCP/IP-based networking to building modern .NET assemblies.

What Can We Access?

However, before we can dig into all of that, we need to think about what these interfaces are going to access. In our case, because we have an existing application that we will be retrofitting to incorporate this functionality, we will be looking at the testbed to identify some basic “touchpoints” that a remote application might want to access. By contrast, if you are creating a new application, the process of identifying and defining these touchpoints should be an integral part of you design methodology from day one.

The following sections present the areas where we will be implementing remote access in our testbed application. Each section will describe the remote interface we desire to add and discuss some of the possible implementations for the interface’s “server” side.

Export Data from Plugins

The obvious place to start is by looking at ways of exporting the data. This is, after all, why most remote applications are going to want to access our application: They want the data that we are gathering. So the first thing to consider is, where does the data reside right now? If you go back and look at the original code, you will see that, in all cases, the primary data display for a plugin is a chart that is plotting one new point at a time. Here is what the logic looked like in the Acquire Sine Data.vi plugin.

Simple Acquisition and Charting

As you can see, the only place the simulated data goes after it is “acquired” is the chart. Likewise, if you looked at the code for saving the data, you would see that it was getting the data by reading the chart’s History property.

Save Chart Data to File

Now, we could expand on that technique to implement the new export functionality, but there is one big consequence to that decision. Approaching the problem in this way would have the side-effect of tying together the number of data points that are saved to the chart’s configuration. Hence, because the amount of data that a chart buffers can’t be changed at runtime, you would have to modify the LabVIEW code to change the amount of data that is returned to the calling application.

A better solution is to leverage what we have learned recently about DVRs and in-place structures to create a storage location the size of which we can control without modifying the application code. A side-effect of this approach is that we will be able to leverage it to improve the efficiency of the local storage of plugin data – yes, sometimes side-effects are good.

To implement this logic we will need three storage buffers: One for each of the two “acquisition” plugins and one for the reentrant “temperature controller” plugin. The interface to each storage buffer will consist of three VIs, the first one of which is responsible for initializing the buffer:

Initialize Buffer

This screenshot represents the version of the initialization routine that serves the Ramp Signal acquisition process. The basic structure of this code is to create a circular buffer that will save the last N samples – where “N” is a reconfigurable number stored in the database. To support this functionality, the DVR incorporates two values: The array of datapoints and a counter that operates as a pointer to track where the current insertion point is in the buffer. These VIs will be installed in the initialization state of the associated plugin screen’s state machine. With the buffer initialized, we next need to be able to insert data. This is typical code for performing that operation:

Insert Data Point

Because the DVR data array is initialized with the proper number of elements at startup, all this logic has to do is replace an existing value in the array with a newly acquired datapoint value, using the counter of course to tell it which element to replace. Although we have a value in the DVR called Counter we can’t use it without a little tweaking. Specifically, the DVR’s counter value increments without limit each time a value is inserted, however, there is only a finite number of elements in the data array. What we need for our circular buffer is a counter that starts at 0, counts to N-1 and then returns to 0 and starts over. The code in the image shows the easiest way to generate this counter. Simply take the limitless count and modulo divide it by the number of points in the buffer. The output of the modulo division operation is a quotient and a remainder. The remainder is the counter we need.

Modulo division is also important to the routine for reading the data from the buffer, but in this case we need both values. The quotient output is used to identify when the buffer is still in the process of being filled with the initial N datapoints:

Read All Data.1

During this initial period, when the quotient is 0, the code uses the remainder to trim off the portion of the buffer contents that are yet to be filled with live data. However, once the buffer is filled, the counter ceases being the a marker identifying the end of the data, and it becomes a demarcation point between the new data and the old data. Therefore, once the quotient increments past 0, a little different processing is required.

Read All Data.2

Once the circular buffer is full, the element that the remainder is pointing at is the oldest data in the array (chronologically speaking), while the datapoint one element up from it is newest. Hence, while the remainder is still used to split the data array, the point now is to swap the two subarrays to put the data in correct chronological order.

Retrieve Graph Images from Plugins

The next opportunity for remote access is to fetch not the data itself, but a graph of the data as it is shown on the application’s GUI. This functionality could form the basic for a remote user interface, or perhaps as an input to a minimalistic web presentation. Simplifying this operation is a control method that allows you to generate an image of the graph and the various objects surrounding it like the plot legend or cursor display. Consequently, the VI that will be servicing the remote connections only needs to be able to access the chart control reference for each plugin. To make those references available, the code now incorporates a buffer that is structurally very similar to the one that we use to store the VI references that allow the GUI to insert the plugins into its subpanel. Due to its similarity to existing code, I won’t cover it in detail, but here are a few key points:

  • Encapsulated in a library to establish a namespace and provided access control
  • The FGV that actually stores the references is scoped as Private
  • Access to the functionality is mediated though publicly-scoped VIs

This FGV is the only new code we will need to add to the existing code.

Adding Remote Control

One thing that both of the remote hooks we just discussed have in common is that they are both pretty passive – or to make this point another way, they both are monitoring what the application is doing without changing what it is doing. Now we want to look at some remote hooks that will allow remote applications control the application’s operation, at least in a limited way.

Since the way the application works is largely dependent upon the contents of the database, it should surprise no one that these control functions will need to provide provisions for the remote application to alter the database contents in a safe and controlled way.

Some Things to Consider

The really important words in that last sentence are “safe” and “controlled”. You see, the thing is that as long as you are simply letting people read the data you are generating, your potential risk is often limited to the value of the data that you are exposing. However, when you give remote users or other applications the ability to control your system, the potential exists that you could lose everything. Please understand that I take no joy in this conversation – I still remember working professionally on a computer that didn’t even have a password. However, in a world where “cyber-crime”, “cyber-terrorism” and “cyber-warfare” have become household terms, this conversation is unavoidable.

To begin with, as a disclaimer you should note that I will not be presenting anything close to a complete security solution, just the part of it that involves test applications directly. The advice I will be providing assumes that you, or someone within your organization, has already done the basic work of securing your network and the computers on that network.

So when it comes to securing applications that you write, the basic principle in play here is that you never give direct access to anything. You always qualify, error-check and validate all inputs coming from remote users or applications. True, you should be doing this input validation anyway, but the fact of the matter is that most developers don’t put a lot of time into validating inputs coming from local users. So here are a couple of recommendations:

Parametrize by Selecting Values – This idea is an expansion on a basic concept I use when creating any sort of interface. I have often said that anything you can do to take the keyboard out of your users’ hands is a good thing. By replacing data that has to be typed with data menus from which they can select you make software more robust and reduce errors. When working with remote interfaces, you do have to support typed strings because unless the remote application was written in LabVIEW, typing is the only option. But what you can do is limit the inputs to a list of specific values. On the LabVIEW-side the code can convert those string values into either a valid enumeration, or a predefined error that cancels the operation and leaves your system unaltered. When dealing with numbers, be sure to validate them also by applying common-sense limits to the inputs.

Create Well-Defined APIs – You want to define a set of interfaces that specify exactly what each operation does, and with as few side-effects as possible. In fancy computer-science terms, this means that operations should be atomic functions that either succeed or fail as a unit. No half-way states allowed! Needless to say, a huge part of being “well-defined” is that the APIs are well-documented. When someone calls a remote function, they should know exactly what is expected of them and exactly what they will get in response.

Keep it Simple – Let’s be honest, the “Swiss Army Knife” approach to interface design can be enticing. You only have to design one interface where everything is parametrized and you’re done, or at least you seem to be for a while. The problem is that as requirements change and expand you have to be constantly adding to that one routine and sooner or later (typically sooner) you will run into something that doesn’t quite fit well into the structure that you created. When that happens, people often try to take the “easy” path and modify their one interface to allow it to handle this new special case – after all, “…it’s just one special case…”. However once you start down that road, special cases tend to multiply like rabbits and the next thing you know, your interface is a complicated, insecure mess. The approach that is truly simple is to create an interface that implements separate calls or functions for each logical piece of information.

With those guidelines in mind, let’s look at the three parameters that we are going to be allowing remote users or applications to access. I picked these parameters because each shows a slightly different use case.

Set the Acquisition Sample Interval

One of the basic ways that you can store a set of parameters is using a DVR, and I demonstrated this technique by using it to store the sample rates that the “acquisition” loops use to pace their operation. In the original code, the parameter was already designed to be changed during operation. You will recall that the basic idea for the parameter’s storage was that of a drop box. It wasn’t important that the logic using the data know exactly when the parameter was changed, as long as it got the correct value the next time it tried to use the data. Consequently, we already have a VI that writes to the DVR (called Sample Rate.lvlib:Write.vi) and, as it turns out, it is all we will need moving forward.

Set Number of Samples to Save

This parameter is interesting because it’s a parameter that didn’t even exist until we started adding the logic for exporting the plugin data. This fact makes it a good illustration of the principle that one change can easily lead to requirements that spawn yet other changes. In this case, creating resizable data buffers leads to the need to be able change the size of those buffers.

To this point, the libraries that we have defined to encapsulate these buffers each incorporate three VIs: one to initialize the buffer, one to insert a new datapoint into it, and one to read all the data stored in the buffer. A logical extension of this pattern would be the addition of a fourth VI, this time one to resize the existing buffer. Called Reset Buffer Size.vi these routines are responsible for both resizing the buffer, and correctly positioning the existing data in the new buffer space. So the first thing the code does is borrow the logic from the buffer reading code to put the dataset in the proper order with the oldest samples at the top and the newest samples at the bottom.

Put the Data in Order

Next the code compares the new and old buffer sizes in order to determine whether the buffer is growing larger, shrinking smaller or staying the same size. Note that the mechanism for performing this “comparison” is to subtract the two value. While a math function might seem to be a curious comparison operator, this technique makes it easy to identify the three conditions that we need to detect. For example, if the values are the same the difference will be 0, and the code can use that value to bypass further operations. Likewise, if the two numbers are not equal, the sign of the result will indicate which input is larger, and the absolute magnitude of the result tells us how much difference there is between the two.

This is the code that is selected when the result of the subtraction is a positive number representing the number of element that are to be added to the buffer.

Add points to Buffer

The code uses the difference value to create an array of appropriate size and then appends it to the bottom of the existing array. In addition, the logic has to set the Counter value point to the first element of the newly appended values so the next insert will go in the correct place. By contrast, if the buffer is shrinking in size, we need to operate on the top of the array.

Remove points from buffer

Because the buffer is getting smaller, the difference is a negative number representing the number of elements to be removed from the buffer data. Hence, the first thing we need to do is extract the number’s absolute value and use it to split the array, effectively removing the elements above the split point. As before, we also need to set the Counter value, but the logic is a little more involved.

You will remember that the most recent data is on the bottom of the array, so where does the next data point need to go? That’s right, the buffer has to wrap around and insert the next datapoint at element 0 again, but here is where the extra complexity comes in. If we simply set Counter to 0 the data insert logic won’t work correctly. Reviewing the insert logic you will notice that the first pass through the buffer (modulo quotient = 0) is handled differently. What we need is to reinitialize Counter with a number that when subjected to the modulo division will result in a remainder of 0, and a quotient that is not 0. An easily derived value that meets that criteria is the size of the array itself.

Finally we have to decide what to do when the buffer size isn’t changing, and here is that code. Based on our discussions just now, you should be able to understand it.

buffer size not changing

Set Temperature Controller Operating Limits

Finally, there are two reasons I wanted to look at this operation: First, it is an example of where you can have several related parameters that logically form a single value. In this case, we have 5 separate numbers that, together, define the operation of one of the “temperature controller” processes. You need to be on the look-out for this sort of situation because, while treating this information as 5 distinct value would not be essentially wrong, that treatment would result in you needing to generate a lot of redundant code.

However, this parameter illustrates a larger issue, namely that changes in requirements can make design decisions you made earlier – let’s say – problematic. As it was originally designed, the temperature controller values were loaded when the plugins initialized, and they were never intended to be changed during while the plugin was running. However, our new requirement to provide remote control functionality means that this parameter now needs to be dynamic. When confronted with such a situation, you need to look for a solution that will require the least rework of existing code and the fewest side-effects. So you could:

  1. Redesign the plugin so it can restart itself: This might sound inviting at first because the reloading of the operating limits would occur “automatically” when the plugin restarted. Unfortunately, it also means that you would have to add a whole new piece of functionality: the ability for the application to stop and then restart a plugin. Moreover, you would be creating a situation where, from the standpoint of a local operator, some part of the system would be restarting itself at odd intervals for no apparent reason. Not a good idea.
  2. Redesign the plugin to update the limits on the fly: This idea is a bit better, but because the limits are currently being carried through the state machine in a cluster that resides in a shift-register, to implement this idea we will need to interrupt the state machine to make the change. Imposing such an interruption risks disrupting the state machine’s timing.

The best solution (as in all such cases) is to address the fundamental cause: the setups only load when the plugin starts and so are carried in the typedef cluster. The first step is to remove the 5 numbers associated with the temperature controller operating parameters from the cluster. Because the cluster is a typedef, this change conveniently doesn’t modify the plugin itself, though it does alter a couple of subVIs – which even more conveniently show up as being broken. All that is needed to repairs these VIs is to go through them one by one and modify the code to read the now-missing cluster data values with the corresponding values that the buffered configuration VI reads from the database. Said configuration VI (Load Machine Configuration.vi) also requires one very small tweak:

Reload Enable

Previously, the only time logic would force a reload of the data was when the VI had not been called before. This modification adds an input to allow the calling code to request a reload by setting the new Reload? input to true. To prevent this change from impacting the places where the VI is already being called, the default value for this input is false, the input is tied to a here-to-fore unused terminal on the connector pane, and the terminal is marked as an Optional input.

Building Out the Infrastructure

At this point in the process, all the modifications that need to be done to the plugins themselves have been accomplished, so now we need is a place for the external interface functionality itself to live. One of the basic concepts of good software design is to look at functionality from the standpoint of what you don’t know or what is likely to change in the future, and then put those things into abstracted modules by themselves. In this way, you can isolate the application as a whole, and thus protect it from changes that occur in the modularized functionality.

The way this concepts applies to the current question should be obvious: There is no way that we can in the here and now develop a compete list of the remote access functionality that users will require in the future. The whole question is at its essence, open-ended. Regardless if how much time you spend studying the issue, users have an inherently different view of your application than you do and so they will come up with needs that you can’t imagine. Hence, while today we might be able to shoe-horn the various data access and control functions into different places in the current structure, to do so would be to start down a dead-end road because it is unlikely that those modifications would meet the requirements of tomorrow. What we need here is a separate process that will allow us to expand or alter the suite of data access and control functionality we will offer.

Introducing the Remote Access Engine

The name of our new process is Remote Access.vi and (like most of the code in the project) it is designed utilizing an event-drive structure that ensures it is quiescent unless it is being actively accessed. The process’ basic theory of operation is that when one of its events fire, it performs the requested operation and then sends a reply in the form of a notification. The name of the notification used for the reply is sent as part of the event data. This basic process is not very different from the concept of “callbacks” used in languages such as JavaScript.

Although this process is primarily intended to run unseen in the background, I have added three indicators to its front panel as aides in troubleshooting. These indicators show the name of the last event that it received, the name of the plugin that the event was targeting, and the name of the response notifier.

The Read Graph Data Event

The description of this event handler will be longer than the others because it pretty much sets the pattern that we will see repeated for each of the other events. It starts by calling a subVI (Validate Plugin Name.vi) that tests to see if the Graph Name coming from the event data is a valid plugin name, and if so, returns the appropriate enumeration.

Validate plugin name

The heart of this routine is the built-in Scan from String function. However, due to the way the scan operation operates, there are edge conditions where it might not always perform as expected when used by itself. Let’s say I have a typedef enumeration named Things I Spend Too Much Time Obsessing Over.ctl with the values My House, My Car, My Cell Phone, and My House Boat, in that order. Now as I attempt to scan these values from strings, I notice a couple of “issues”. First there is the problems of false positives. As you would expect, it correctly converts the string “My House Boat” into the enumerated value My House Boat. However, it would also convert the string “My House Boat on the Grand Canal” to the same enumeration and pass the last part of the string (” on the Grand Canal”) out its remaining string output. Please note that this behavior is not a bug. In fact, in many situations it can be a very useful behavior – it’s just not the behavior that we want right now because we are only interested in exact matches. To trap this situation, the code marks the name as invalid if the remaining string output is not empty.

The other issue you can have is what I call the default output problem. The scan function is designed such that if the input string is not scanned successfully, it outputs the value present at the default value input. Again, this operation can be a good thing, but it is not the behavior that we want. To deal with this difference, the code tests the error cluster output (which generates and error code 85 for a failed scan) and marks the name as invalid if there is an error.

When Validate Plugin Name.vi finishes executing, we have a converted plugin name and a flag that tells us whether or not we can trust it. So the first thing we do is test that flag to see whether to continue processing the event or return an error message to the caller. Here is the code that executes when the result of the name validation process is Name Not Valid.

Name Not Valid

If the Response Notifier value from the event data is not null, the code uses it to send the error message, “Update Failed”. Note that this same message is sent whenever any problem arises in the remote interface. While this convention certainly results in a non-specific error message, it also ensures that the error message doesn’t give away any hints to “bad guys” trying to break in. If the Response Notifier value is null (as it will be for local requests) the code does nothing – remember we are also wanting to leverage this logic locally.

If the result of the name validation process is Name Valid, the code now considers the Plugin Name enumeration and predicates its further actions based on what it finds there. This example for Sine Source shows the basic logic.

Name Valid - Remote

The code reads the data buffer associated with the signal and passes the result into a case structure that does one of two different things depending on whether the event was fired locally, or resulted from a remote request. For a remote request (Response Notifier is not null), the code turns the data into a variant and uses it as the data for the response notifier. However, if the request is local…

Name Valid - Local

…it sends the same data to the VI that saves a local copy of the data.

The Read Graph Image Event

As I promised above, this event shares much of the basic structure as the one we just considered. In fact, the processing for a Name Not Valid validation result is identical. The Name Valid case, however, is a bit simpler:

Read Graph Image

The reason for this simplification is that regardless of the plugin being accessed, the datatypes involved in the operation are always the same. The code always starts with a graph control reference (which I get from the lookup buffer) and always generates an Image Data cluster. If the event was fired locally, the image data is passed to a VI (Write PNG File.vi) that prompts the user for a file name and then saves it locally. However, if instead of saving a file, you are wanting to pass the image in a form that is usable in a non-LabVIEW environment, a bit more work is required. To encapsulate that added logic, I created the subVI Send Image Data.vi.

Send Image Data

The idea is to convert the proprietary image data coming from the invoke node into a generic form by rendering it as a standard format image. Once in that form, it is a simple matter to send it as a binary stream. To implement this approach, the code first saves the image to a temporary png file. It then reads back the binary contents of the file and uses it as the data for the response notifier. Finally, it deletes the (now redundant) temporary file.

The Set Acquisition Rate Event

This event is the first one to control the operation of the application. It also has no need to be leveraged locally, so no dual operation depending on the contents of the Response Notifier value.

Set Acquisition Rate

Moreover, because the event action is a command and not a request, the response can only have one of two values: “Update Failed” or “Update Good”. The success message is only be sent if the plugin name is either Sine Source or Ramp Source, and no errors occurs during the update. While on the topic of errors, there are two operations that need to be performed for a complete update: the code must modify both the database and the buffer holding the live copy of the setting that the rest of the application uses. In setting the order of these two operations, I considered which of the two is most likely to generate an error and put it first. When you consider that most of the places storing live data are incapable of generating an error, the database update clearly should go first.

So after verifying the plugin name, the subVI responsible for updating the database (Set Default Sample Period.vi) looks to see if the value is changing. If the “new” value and the “old” value are equal, the code returns a Boolean false to its Changed? output and sets the Result output to Update Good. It might seem strange to return a value to the remote application that the update succeeded when there was no update performed, but think about it from the standpoint of the remote user. If I want a sample period of 1000ms, an output of Update Good tells me I got what I wanted – I don’t care that it didn’t have to change to get there. If the value is changing…

Set Default Sample Period

…the code validates the input by sending it to a subVI that compares it to some set limits (500 < period < 2500). Right now these limits are hardcoded, and in some cases that might be perfectly fine. You will encounter many situations where the limits are fixed by the physics of a process or a required input to some piece of equipment. Still, you might want these limits to be programmable too, but I’ll leave that modification as, “…as exercise for the reader.” In any case, if the data is valid, the code uses it to update the database and sets the subVI’s two outputs to reflect whether the database update generated an error. If the data is not valid, it returns the standard error message stating so.

The Set Data Buffer Depth Event

The basic dataflow for this event is very much like the previous one.

Set Data Buffer Depth

The primary logical difference between the two is that all plugins support this parameter. The logic simply has to select the correct buffer to resize.

The Set TC Parameters Event

With our third and (for now at least) final control event, we return to one that is only valid for some of the plugins – this time the temperature controllers.

Set TC Parameters

The interesting part of this event processing is that, because its data was not originally intended to be reloaded at runtime, the live copy of the data is read and buffered in the object-oriented configuration VIs.

Save Machine Configuration

Consequently, the routine to update the database (Save Machine Configuration.vi) first creates a Config Data object and then use that object to read the current configuration data. If the data has changed, and is valid, the same object is passed on to the method that writes the data to the database. Note also, that the validation criteria is more complex.

Validate TC Parameters

In addition to simple limits on the sample interval, the Error High Level cannot exceed 100, the Error Low Level cannot go below 30, and all the levels have to be correct relative to each other.

Testing

With the last of the basic interface code written and in place, we need to look at how to test it. To aide in that effort, I created five test VIs – one for each event. The point of these VIs is to simply exercise the associated event so we can observe and validate the code’s response. For instance, here’s the one for reading the graph data:

Test Read Graph Data

It incorporates two separate execution paths because it has two things that it has to be doing in parallel: Sending the event (the top path) and waiting for a response (the bottom path). Driving both paths, is the output from a support VI from the notifier library (not_Generic Named Notifier.lvlib:Generate Notifier Name.vi). It’s job is to generate a unique notifier name based on the current time and a 4-digit random number. Once the upper path has fired the event, it’s work is done. The bottom path displays the raw notifier response and graphs of the data that is transferred. Next, the test VI for reading the graph image sports similar logic, but the processing of the response data is more complex.

Test Read Graph Image

Here, the response notifier name is also used to form the name for a temporary png file that the code uses to store the image data from the event response. As soon as the file is written, the code reads it back in as a png image and passes it to a subVI that writes it to a 2D picture control on the VI’s front panel. Finally, the three test VIs for the control operations are so similar, I’ll only show one of them.

Test Resizing Data Buffers

This exemplar is for resizing the data buffers. The only response processing is that it displays the raw variant response data.

To use these VIs, launch the testbed application and run these test VIs manually one at a time. For the VIs that set operating parameters, observe that entering invalid data generates the standard error message. Likewise, when you enter a valid setting look for the correct response in both the program’s behavior and the data stored in the database. For the VI’s testing the read functions, run them and observe that the data they display matches what the selected plugin shows on the application’s GUI.

Testbed Application – Release 18
Toolbox – Release 15

The Big Tease

In this post, we have successfully implemented a remote access/control capability. However, we don’t as of yet have any way of accessing that capability from outside LabVIEW. Next time, we start correcting that matter by creating a TCP/IP interface into the logic we just created. After that introduction, there will be posts covering .NET, ActiveX and maybe even WebSockets – we’ll see how it goes.

Until Next Time…
Mike…

Dropping-In on the Testbed

Last time out we started exploring one common application of so-called “drop-in” VI. The technique is based on the idea of creating VIs that are capable of performing something useful for the VI that is hosting it, but without interacting directly with that VI’s basic logic. The example we considered was manipulating the font and type size used to present textual data.

At the close of that post we has created a basic object-oriented structure that could manipulate the label or caption of any front panel control or indicator. I want to finish this discussion by looking at how to expand that basic implementation to allow it to set the text properties of text contained inside a control or indicator. For that we will return to our testbed application.

A Brief Recap

It has been a while since we have worked with this code, so a brief refresher on what it does is probably in order. The testbed application we will be modifying consists of several processes that run independently of one another. To begin with, there is a background process that oversees the reporting of errors that occur. Handling the user interface duties, a GUI process incorporates a subpanel that can display the front panels of several simulated acquisition and process-control VIs. The whole thing is kicked off by a launcher VI that loads the various processes into memory and starts them executing.

Our goal here will be to add the drop-in VI we created last time to all the user-facing VIs and add classes as necessary to allow it to handle the controls and indicators on those VIs. However, if you don’t already have a tool for editing database contents directly, you should first download a tool called Database .NET (the link is to a zip file, and is at the bottom of the page). The program is a simple utility that lets you examine and edit database data from a number of different DBMS. I don’t know the folks that wrote this, and have no vested interest in the program other than I have used it for years and found it very useful. Note that this program has no installer so it has a very small footprint – it will even run from a USB stick. To “install” the program, simply create a directory for it on your computer and then drag into it the program that is inside the zip archive you downloaded, and installation is complete. The easiest way to invoke it is to set it as the default application for *.mdb files.

  • Note that if you decide to install this utility in a subdirectory of the Program Files (x86) directory, you may have to play around with the folder permissions a bit before it will run. Because the program generates several temporary files when it’s starting up, the user has to have Full Access to the folder in which it is installed.

One other caveat to bear in mind before we dive into the modifications is that, these operations cannot override limits on these properties that might exist for other reasons. For example, these techniques will not work on controls that you have defined as strict typedefs. The reason: The strict typedef defines everything about the control’s appearance and the property node will throw an error if you try to change them. Likewise, a System-themed control will let you change the font characteristics, but will complain if you try to change colors.

Making With the Modifications

So where do we start? Well the first hing we need to do is to make a couple minor tweaks to the Display Font Manager.vi. First, we need to define what happens to the drop-ins errors. Because it’s important to preserve them, we will save the errors that arise in the drop-in to the same location that errors from the testbed application proper are stored – but without bothering the program’s operator. To accomplish that task, let’s reuse a the subVI that the error handling logic uses to store error data.

Drop-in Error Handling

Note that I had to add a case structure because the location where this subVI was originally used only executed if there was an error. So unless we want to have spurious records being posted, we have to add that logic here.

Next, as the code is currently written, the error chain in the drop-in’s logic starts with the Error In control and terminates in the Error Out indicator. Although this arrangement works fine during development and testing, when the time comes to deploy the code, this is not what we want. As I said last time, drop-in VIs should not interact with the host VI and should not inject their own errors into the host’s error stream. Still, it can be useful to be able to use the drop-in’s error IO to establish data dependencies that control when it runs. The solution is for the drop-in to have error clusters, but not have them be connected internally.

Errors - Straight Through

Changing the Testbed

Now that we are to install the drop-in, we need to look for where to install it. Completing that examination of the code, we see that there are 5 VIs that are user-facing:

  1. The Launcher (testbed.vi)
  2. The Main GUI (Display Data.vi)
  3. The Temperature Controller (Temperature Controller.vi)
  4. Two “Acquisition” VIs (Acquire Ramp Data.vi and Acquire Sine Data.vi)

So the first thing I do is modify each of these VIs by dropping a copy of the drop-in VI on to their block diagram outside the outer-most loop. For example, this is what the modified launcher block diagram looks like:

textbed.vi with drop-in installed

As promised earlier, this is all the modification that the application will need – which means we are ready to start testing.

The First Test

“But wait a minute…” you protest. “…we haven’t configured anything yet. There’s nothing to test!”

Well you’re half right. We have not gone into the database and configured any controls to be modified, but we still have something to test. We still have to verify the drop-in’s default behavior, which by the way, is to do nothing. Yes, you read that right, we have to test that nothing happens. You see, a major aspect of the drop-in concepts is that drop-ins don’t do anything unless they are explicitly told to through their configuration. Right now we have installed the drop-in code, but there are no controls configured in the database so we need to make sure that the main application continues to run as it did before: no side-effects and no errors. In short, the drop-in right now should do nothing, and we need to make sure that it fulfills that requirement.

So launch the top-level VI (testbed.vi) or run the standalone executable. As before, the launcher will show the names of the processes it’s launching and when it finishes the main GUI will open. Again as before, you will be able to switch between screens using the popup menu and the plugins will operate just as they did before. Finally, if you look at the contents of the event table in the database, you will see that no errors have been generated.

It’s All About the Children

Now that we have “nothing” working, we need to finish implementing all the “somethings”. You will recall that when we ended last time I had created a basic implementation of the font manager functionality that could change the label or caption of any type of control. The tricky part, I said was going to be implementing the subclass, or children, methods that would modify the font of a configured control’s contents. So let’s look at those children.

The String and Digital Subclasses

I choose to start with these two because they are the easiest to understand, and are very much alike. Here’s the child method for handing strings…

String Subclass Method

…and the one for digital numerics…

Digital Subclass Method

In either subclass, the logic starts by calling the parent methods (which handles labels and captions) and then extracting from the parent’s class data the reference to the control that will be manipulated. At the same time that is going on, the Font Parameters data is unbundled and the Component to Set value controls what, if anything, happens next. If the selected component is Label or Caption a case is selected which does nothing but pass through the error cluster. If, however, the selected component is Contents the associated case casts the basic control reference from the parent class data into the control’s specific control class, and then sets the appropriate properties.

The Boolean and RingSubclasses

The next two I want to consider are, again, similar each other, but differ from the preceding pair in that they represent control classes that don’t have any readily discernible textual value. Booleans represent logical true and false conditions, while rings are technically numerics, but the number that is their value doesn’t appear anywhere. In this sort of situation, the idea is to look for text that is not the control’s value but is associated with that value. For example, Boolean controls in LabVIEW can have textual displays that state the control’s condition. These strings are called Boolean Text and are often used to label push buttons or lights…

Boolean Subclass Method

Likewise, the Ring control appears to the user as a pop-up menu, so we can use this code to set the text properties of the text that appears in the menu…

Ring Subclass Method

The WaveformChart Subclass

Finally, we need to take the idea of strings that are only associated with data one more step. What about complex controls that can have multiple strings associated with their values? Objects like charts are good examples of what I am talking about. Just to start, there is text associated with the axis tick marks, there is text that forms the axis labels, and there is text in the plot legends.

The most flexible approach would be to figure out how to uniquely identify each of these components, however we must be careful to not create an API that is so flexible that it is unusable. One solution would be to simply make all the text the same font and size – which is what they are anyway. A look that I prefer however is to have the tick mark labels slightly smaller than the axis labels. Here is one way to do that:

WaveformChart Subclass Method

As you can see, the code treats the two axes the same by combining references to them into an array and then passing that array into a loop that manipulates the display parameters. This logic makes the axis labels the size specified in the configuration, but does a bit of math to make the tick mark labels about 10% smaller. This difference might not seem like much, but it works. If this isn’t exactly what you want, that’s OK. The point here is not to present a canonical solution, but to present concepts and ideas that help you find your own way.

Adding Configurations

Now we are ready to add the font definitions to the database. I have created a total of 12 definitions covering 9 different controls and indicators and you can see them all by examining the SQL file in the _repos subdirectory in the project (starting at line 27). However, to give you a taste of what the SQL code for this functionality looks like, here is the SQL for the table holding the font configurations, and the font definition for the string indicator on the front panel of the launcher.

CREATE TABLE ctrl_font_definition (
    id          AUTOINCREMENT PRIMARY KEY,
    owner_name  TEXT(50) WITH COMPRESSION,
    ctrl_name   TEXT(50) WITH COMPRESSION,
    font_name   TEXT(20) WITH COMPRESSION,
    font_size   INTEGER,
    ctrl_comp   TEXT(20) WITH COMPRESSION
  )
;

INSERT INTO ctrl_font_definition
  (owner_name, ctrl_name, font_name, font_size, ctrl_comp)
VALUES
  ('testbed.vi', 'progress', 'Segoe UI', 24, 'Contents')
;

The goal of these initial definitions is to “turn-on” the functionality without changing too much. For example, the ‘Segoe UI’ font is the default font that LabVIEW uses on recent versions of the Windows platform. If you are running this code on the Macintosh or Linux (or an older version of Windows), the default font will be different. So on other platforms you may need to modify these definitions before you install them.

Once we have the definitions in the database, let’s try the testbed application again. You might not notice a lot of difference, that is sort of the point. This initial test is to reproduce the default values. One place where you will notice a difference is if you are running Windows and you have the display font scaling on your display set to the non-default value. The text size will now always be the same relative to the size of the window regardless of how the display setting changes.

From here I would recommend that you play around a bit and manually change the font and size of the various controls to see the effect.

Testbed Application – Release 16
Toolbox – Release 12
Testbed Installer – Release 16

Please note that I have included in this release a built version of the application so you can practice working with the database. The LocalDB.mdb file included with this installer has the table defined for holding the font definitions, but the table is empty. This release has two purposes: One, by adding to and manipulating the data in its database, you can see that you really can modify the visual presentation without changing code. Two, I have started using LabVIEW 2015 and realize that some of you may not have upgraded yet. If this version change is a problem, post a comment and I will send you a version of the code back-saved to LabVIEW 2014.

The Big Tease

One of the things that I like about NI Week is the opportunity to meet friends both new and old. Before a keynote address one morning I was talking to another one of the LabVIEW Champions, Jack Dunaway by name, and the topic of this blog came up. To make a long story short, he suggested a topic that sounded so good, I’m going to get started on it next time.

One good of way showing a lot of data in a small space is what is known as a tree control. It’s valuable because its structure is inherently hierarchical and so can display a lot of data while not taking up a lot of screen real estate. In addition, it can reduce the overwhelm that you sometimes feel when looking at large datasets because, when done well, they allow you to start with a high-level view of the data and gradually drill down to the specific results you want.

If you are working in Windows, there are two such controls available: one that is part of Windows, and one that is native to LabVIEW. So next time: the Native LabVIEW Tree Control. Be there or be square.

Until Next Time…

Mike…

More Than One Kind of Modularity

When learning something that you haven’t done before – like .NET – it’s not uncommon to go through a phase where you look at some of the code you wrote early on and cringe (or at least sigh deeply). The problem is that you are often not only learning a new interface or API, but you are learning how to best use that interface or API. The cause of all the cringing and sighing is that as you learn more, you begin to realize that some of the assumptions and design decisions that you made were misguided, if not flat-out wrong. If you look at the code we put together last time to help us learn about .NET in general, and the NotifyIcon assemble in particular, we see a gold-plated example of just such code. Although it is clearly accomplished it’s original goal of demonstrating how to access .NET functionality and illustrating how the various objects can relate to one another, it is certainly not reusable – or maintainable, or extensible, or any of the other “-ables” that good software needs to be.

In fact, I created the code in that way so this time we can take the lesson one step further to fix those shortcomings, and thus demonstrate how you can go about cleaning up code (of your own or inherited) that is making you cringe or sigh. Remember, it is always worth your time to fix bad design. I can’t tell you how many times I have seen people struggling with bad decisions made years before. Rather than taking a bit of time to fix the root cause of their trouble, they continue to waste hours on project after project in order to workaround the problem.

Ok, so where do we start?

Clearly this code would benefit from cleaning-up and refactoring, but where and how should we start? Well, if you are working on an older code base, the question of where to start will not be a problem. You start with where the most pain is. To put it another way, start with the things that cause you the biggest problems on a day-to-day basis.

This point, however, doesn’t mean that you should just sit around and wait for problems to arise. As you are working always be asking yourself if what you are doing has limitations, or embodies assumptions that might cause problems in the future.

The next thing to remember is that this work can, and should, be iterative. In other words you don’t have to fix everything at once. Start with the most egregious errors, and address the others as you have the opportunity. For example, if you see the code doing something stupid like using a string as a state variable, you can fix that quickly by replacing the strings with a typedef enumeration. I have even fixed some long-standing bugs in doing this replacement because it resolved places where states were subtly misspelled or contained extraneous spaces.

Finally, remember that the biggest payoffs, in terms of long-term benefit, come from improved modularity that corrects basic architectural problems. As we shall see in the following discussion, I include under this broad heading modularity in all its forms: modular functionality, modular logic and modular data.

Revisiting Modular Functionality

Modular functionality is the result of taking small reusable bits of code and encapsulating it in routines that simplify access, standardize the interface or ensure proper usage. There are good examples of all these usages in the application modified code, starting with Create NotifyIcon.vi:

Create NotifyIcon VI

Your first thought might be why I bothered turning this functionality into a subVI. After all, it’s just one constructor node. Well, yes that is true but it’s also true that in order to create that one node you have to remember multiple steps and object names. Even though this subVI appears rather simple, if you consider what it would take to recreate it multiple times in the future, you realize that it actually encapsulates quite a bit of knowledge. Moreover, I want to point out that this knowledge is largely stuff for which there is no overwhelming benefit to be gained from you committing it to memory.

Next, let’s consider the question of standardizing interfaces. Our example in this case is a new subVI I created to handle the task of assigning an icon to the interface we are creating. I have named it Set NotifyIcon Icon.vi:

Set NotifyIcon Icon VI

You will remember from out previous discussion that this task involves passing a .NET object encapsulating the icon we wish to use to a property node for the NotifyIcon object. Originally, this property was combined with several others on a single node. A more flexible approach is to breakup that functionality and standardize the interfaces for all the subVIs that will be setting NotifyIcon to simply consist of an object reference and the data to be used to set the property in a standard LabVIEW datatype – in this case a path input. This approach also clearly simplifies access to the desired functionality.

Finally, there is the matter of ensuring proper usage. A good place to highlight that feature is in the last subVI that the application calls before quitting: Drop NotifyIcon.vi.

Drop NotifyIcon VI

You have probably been warned many times about the necessity of closing references that you open. However, when working with .NET objects, that action by itself is sometimes not sufficient to completely release all the system resources that the assembly had been using. Most of the time if you don’t completely close out the assembly you many notice memory leaks or errors from attempting to access resources that still think they are busy. However with the NotifyIcon assembly you will see a problem that is far more noticeable, and embarrassing. If you don’t call the Dispose method your program will close and release all the memory it was using, but if you go to the System Tray you’ll still see your icon. In fact, you will be able to open its menu and even make selections – it just doesn’t do anything. Moreover, the only way to make it go away it to restart your computer.

Given the consequences of forgetting to include this method in your shutdown sequence, it is a good idea to make it an integral part of the code that you can’t forget to include.

Getting Down with Modular Logic

But as powerful as this technique is, there can still be situations where the basic concept of modularity needs to be expressed in a slightly different way. To see such a situation, let’s look at the structure that results from simply applying the previous form of modularity to the problem of building the menus that go with the icon.

Create ContextMenu VI

Comparing this diagram to the original one from last time, you can see that I have encapsulated the repetitive code that generated the MenuItem objects into dedicated subVIs. By any measure this change is a significant improvement: the code is cleaner, better organized, and far more readable. For example, it is pretty easy to visualize what menu items are on submenus. However, in cases such as this one, this improved readability can be a bit of a double-edged sword. To see what I mean, consider that for the structure of your code to allow you to visualize your menu organization, said organization must be hard-coded into the structure of the code. Consequently, changes to the menus will, as a matter of course, require modification to the fundamental structure of the code. If the justifications for modularity is to include concepts like flexibility and reusability, you just missed the boat.

The solution to this situation is to realize that there is more than one flavor of modularity. In addition to modularizing specific functionality, you can also modularize the logic required to perform complex and changeable tasks (like building menus) that you don’t want to hard code. If this seems like a strange idea to you, consider that computers spend most of their time using their generalized hardware to performed specialized tasks defined by lists of instructions called “programs”. The thing that makes this process work is a generalized bit of software called a “compiler” that turns the programs into data structures that the generalized hardware can use to perform specialized actions.

Carrying forward with this line of reasoning, what we need is a simple way of defining a menu structure that is external to our program, and a “menu compiler” that turns that definition into the MenuItem references that our program needs. So let’s build one…

Creating the Data for Our Menu Compiler

So what should this menu definition look like? Well, to answer that question we need to start with the data required to define a single MenuItem. We see that as a minimum, every item in a menu has to have a name for display to the user, a tag to identify it, and a parent tag that says if the item has a parent item (and if so which item is its parent). In addition, we haven’t really talked about it, but the order of references in an array of menu items defines the order in which the items appear in the menu or submenu – so we need a way to specify its menu position as well. Finally, because in the end the menu will consist of a list (array) of menu item references, it makes sense to express the overall menu definition that we will eventually compile into that array of references as a list (and eventually also an array).

But where should we store this list of menu item definitions? At least part of the to this question depends on who you want to be able to modify the menu, and the level of technical expertise that person has. For example, you could store this data in text files as INI keys, or as XML or JSON strings. These files have the advantage of being easy to generate and are readily accessible to anyone who has access to a text editor – of course that is their major disadvantage, as well. Databases on the other hand are more secure, but not as easy to access. For the purposes of this discussion, I’ll store the menu definitions in a JSON file because, when done properly, the whole issue of how to parse the data simply goes away.

To see what I mean, here is a nicely indented JSON file that describes the menu that we have been working using for our example NotifyIcon application:

[
	{
		"Menu Order":0,
		"Item Name":"Larry",
		"Item Tag":"Larry",
		"Parent Tag":"",
		"Enabled":true
	},{
		"Menu Order":1,
		"Item Name":"Moe",
		"Item Tag":"Moe",
		"Parent Tag":"",
		"Enabled":true
	},{
		"Menu Order":2,
		"Item Name":"The Other Stooge",
		"Item Tag":"The Other Stooge",
		"Parent Tag":"",
		"Enabled":true
	},{
		"Menu Order":3,
		"Item Name":"-",
		"Item Tag":"",
		"Parent Tag":"",
		"Enabled":true
	},{
		"Menu Order":4,
		"Item Name":"Quit",
		"Item Tag":"Quit",
		"Parent Tag":"",
		"Enabled":true
	},{
		"Menu Order":0,
		"Item Name":"Curley",
		"Item Tag":"Curley",
		"Parent Tag":"The Other Stooge",
		"Enabled":true
	},{
		"Menu Order":1,
		"Item Name":"Shep",
		"Item Tag":"Shep",
		"Parent Tag":"The Other Stooge",
		"Enabled":true
	},{
		"Menu Order":2,
		"Item Name":"Joe",
		"Item Tag":"Joe",
		"Parent Tag":"The Other Stooge",
		"Enabled":true
	}
]

And here is the LabVIEW code will convert this string into a LabVIEW array (even if it isn’t nicely indented):

Read JSON String

JSON has a lot of advantages over techniques like XML: For starters, it’s easier to read, and a lot more efficient, but this is why I really like using JSON: It is so very convenient.

Starting the Compilation

Now that we have our raw menu definition string read into LabVIEW and converted into a datatype that will simplify the next step in the processing, we need to ensure that the data is in the right order. To see why, we need to remember that the final data structure we are building is hierarchical, so the order in which we build it matters. For instance, “The Other Stooge” is a top-level menu item, but it is also a submenu so we can’t build it until we have references to all the menu items that are under it. Likewise, if one of the items under it is a submenu, we can’t build it until all its children are created.

So given the importance of order, we need to be careful how we handle the data because none of the available storage techniques can on their own guarantee proper ordering. The string formats can all be edited manually, and it’s not reasonable to expect people to always type in data in the right order. Even though databases can sort the result of queries, there isn’t enough information in the menu definition to allow it to do so.

The menu definition we created does have a numeric value that specifies the order of items in their respective menus and submenus. We don’t, however, yet have a way of telling the level the items reside at relative to the overall menu structure. Logically we can see that “Larry” is a top-level menu item, and “Shep” is one level down, but we can’t yet determine that information programmatically. Still the information we need is present in the data, it just needs to be massaged a bit. Here is the code for that task:

Ordering the Menu Items

As you can see, the process is basically pretty simple. I first rewrite the Item Tag value by adding the original Item Tag value to the colon-delimited list that starts with the Parent Tag. I then count the number of colons in the resulting string, and that is my Menu Level value. The exception to this processing are the top-level menu items which are easy to identify due to their null parent tags. I simply force their Menu Level values to zero and replace the null string with a known value that will make the subsequent processing easier. The real magic however, occurs after the loop stops. The code first sorts the array in ascending order and then reverses the array. Due to the way the 1D array sort works when operating on arrays of clusters, the array will be sorted first by Menu Level and then Menu Order – the first two items in the cluster. This sorting, in concert with the array reversal, guarantees that the children of a submenu will always be processed before the submenu item itself.

Some of you may be wondering why we go to all this trouble. After all, couldn’t we just add a value to the menu definition data to hold the Menu Level? Yes, we could, but it’s not a good idea, and here’s why. In some areas of software development (like database development, for instance) the experts put a lot of store in reducing “redundancy” – which they define basically as storing the same piece of information in more than one place. The problem is that if you have redundant information, you have to decide how to respond when the two pieces of information that are supposed to be the same, aren’t. So let’s say we add a field to the menu definition for the menu level. Now we have the same piece of information stored in two different places: It is stored explicitly in the Menu Level value while at the same time it is also stored implicitly in Parent Tag.

Generating the Menu Item “Code”

In order to turn this listing into the MenuItem references we need, we will pass this sorted and ordered array into a loop that will process one element at a time. And here it is:

Compiling the Menu-1

You can see that the loop carries two shift registers. The top SR holds a 1D array of strings that consists of the submenu tags that the loop has encountered so far. The other SR also carries a 1D array but each element in it is a cluster containing an array of MenuItem references associated with the submenu named in the corresponding element of the top SR.

As the screenshot shows, the first thing that happens in the loop is that the code checks to see if the indexed Item Tag is contained in the top SR. If the tag is missing from the array it means that the item is not a submenu, so the code uses its data to create a non-submenu MenuItem. In parallel with that operation, the code is also determining what to do with the reference that is being created by looking to see if the item’s Parent Tag exists in the top SR. If the item’s parent is also missing from the array, the code creates entries for it in both arrays. If the parent’s tag is found in the top SR, it means that one or more of the item’s sibling items has already been processed so code is executed to add the new MenuItem to the array of existing ones:

Compiling the Menu-2

Note that the new reference is added to the top of the array. The reason for this departure from the norm is that due to the way the sorting works, the menu order is also reversed and this logic puts the items on each submenu back in their correct order. Note also that during this processing the references associated the menu items are also accumulated in a separate array that will be used to initialize the callbacks. Because the array indexing operation is conditional, only a MenuItem that is not a submenu, will be included in this array.

Generating the Submenu “Code”

If the indexed Item Tag is found in the top SR, the item is a submenu and the MenuItem references needed to create its MenuItem should be in the array of references stored in the bottom SR.

Compiling the Menu-3

So the first thing the code does is delete the tag and its data from the two array (since they are no longer needed) and uses the data thus obtained to create the submenu’s MenuItem. At the same time, the code is also checking to see if the submenu’s parent exists in the top SR. As before, if the Parent Tag doesn’t exist in the array, the code creates an entry for it, and if it does…

Compiling the Menu-4

…adds the new MenuItem to the existing array – again at the top of the array. By the time this loop finishes, there should be only one element in each array. The only item left in the top SR should be “[top-menu]” and the bottom SR should be holding the references to the top-level menu items. The array of references is in turn used to create the ContextMenu object which written to the NotifyIcon object’s ContextMenu property.

What Could Possibly Go Wrong?

At this point, you can run the example code and see an iconic system tray interface that behaves pretty much as it did before, but with a few extra selections. However, we need to have a brief conversation about error checking, and frankly in this situation there are two schools of though on this topic. There is ample opportunity for errors to creep into the menu structure. Something as simple as misspelling a parent tag name could result in an “orphan” menu that would never get displayed – or could end up being the only one that is displayed. So the question is how much error checking do we really need to do? There are those that think you should spend a lot of time going through the logic looking for and trapping every possible error.

Given that most menus should be rather minimal, and errors are really obvious, I tend to concentrate on the low-hanging fruit. For example, one simple check that will catch a large number of possible errors, is looking to see if at the end of the processing, there is more than one menu name left in the top SR – and finding an extra one, asserting an error that gives the name of the extra menu. You should probably also use this error as an opportunity to abort the application launch since you could be left in a situation when you can’t shutdown the program because the “Quit” option is missing.

Something else that you might want to consider is what to do if the external file containing the menu definitions comes up missing. The most obvious solution is to, again, abort the application launch with some sort of appropriate error message. However, depending on the application it might be valuable to provide a hard-coded default menu that doesn’t depend on external files and provides a certain minimum level of functionality. In fact, I once worked on an application where this was an explicit requirement because one of the things that the program allowed the user to do was create custom menus, the structure of which was stored in external files.

Stooge Identifier – Release 2
Toolbox – Release 11

The Big Tease

So what are we going to talk about next time? Well something that I have seen coming up a lot lately on the user forum is the need to be able to work with very large datasets. Often, this issue arises when someone tries to display the results of a test that ran for several hours (or days!) only to discover that the complete dataset consists of hundreds of thousands of separate datapoints. While LabVIEW can easily deal with datasets of this magnitude, it should be obvious that you need to really bring you memory management “A” game. Next time will look into how to plot and manage VLDs (Very Large Datasets).

Until Next Time…

Mike…

Helping a Window to Remember

One of the most common, most basic, and most mindless, things we do with computers every day is open windows. Launching a program or opening a document is often synonymous (on a practical level at least) with opening a window. As common as this action is, we rarely give any thought to what is going on behind the scenes when we open a window – hence the wisecrack about it being a mindless operation.

However, if we want to make the most of our design efforts, be need to replace this “mindlessness” with “mindfulness” by really thinking about the things that make windows easy and comfortable to use.

Defining a Well Behaved Window

As we begin looking at the behavior of windows, I want to emphasise that I am not talking about user interface design. User interface design deals with the details of what a window does functionally. Rather what I’m talking about is an examination to the behaviors a window should exhibit, regardless of what happens to be on the screen.

If we think about the window as a kind of frame that supports the interface’s core functionality, we see that one of the big things a window can do is remember things. Over the years, people have developed, and posted on the LabVIEW forums, a variety of toolboxes for storing generic window information like screen customizations, positions and settings. One of these toolsets combined with an event-driven structure can make it easy to significantly pump up the convenience factor of just about any application.

To see how these sorts of tools work, we’re going to enhance our undockable windows application with a simple addition that automatically saves a windows last position and restores that position the next time the window undocks. Although the basic logic is simple, it provides us with the opportunity to discuss many of the major issues that impact this sort of functionality.

The Data, and Where to Keep it

When considering the data that this sort of functionality requires and uses, the operative word is: “convenience”. By that I mean that this data may make using the screen more convenient, but nobody is going to be crying if it gets lost. In fact, a valuable behavior is the ability basically “reset” all the stored data back to its default value by simply deleting the data from the file that is storing it and letting the application rebuild it as needed.

Likewise the data should be of low “intelligence” value. In other words, we don’t want to include things in this data that could constitute a security risk. However, having said that, we also want to make sure that a well-meaning user doesn’t mess up the program’s operation by manually editing the data. My approach to blocking such edits has three major points:

  • Be careful about what you name things: You want to give identifiers that are, of course, meaningful. However, you don’t want to use names that will call attention to themselves in a way that says, “Hi, I’m a setting you might want to play with…”
  • Use a non-obvious data structure: For example, in our example we don’t save a window’s position as a simple list of four values. The problem is that a user looking at these values might decide to try to edit them manually – a simple act that could have some significant side effects. To see why consider that the way you move a window around the screen is by changing the VI’s WinBounds property. However, this property defines a windows position by essentially specifying the location of the window’s upper-left and lower-right corners. Consequently, while it does set the window’s location, it is also specifying the window’s size.
  • Provide a simple way to validate the data: Given that there is no way to know ahead of time what sort of data you might be wanting to store, validation might seem like a huge task, but it’s really not. Remember, when validating the data you don’t have to prove the data values are valid, just that they haven’t changed since the program wrote them.

As we get into how the position saving is implemented, you’ll see how I put these ideas into action, but first we need to look at how we are going to implement the capability from a high-level view.

Our Basic Approach

When adding in new or enhanced functionality, you want to do so in a way that requires as few modifications to (and has as little impact on) the existing structure, as possible. This ability to easily incorporate new functionality is a large part of the meaning of the term, “maintainable”. It is also why it is always good to think about your overall application in terms of functional blocks – or specific VIs that do specific things, and handle specific situations.

With that point in mind we know we have two basic operations we need to add: one sets the position when we open a VI and one writes a new position when we close it. Of these two operations, the simplest is the one that reads the last saved location and moves the window to that location. It’s simple because there is only one place in the code where that opening takes place, and that is right here:

Read Position Installed

This is the VI Float the VI.vi and if you compare it to the version that I presented last week you will note that it has one extra subVI that uses a reference to the VI being opened to look up and set the window position. we’ll look at exactly how it does that in just a moment. The operation that saves a VI’s last open position can also be boiled down to a single subVI, but due to the nature of our application, it will have to be installed in two locations.

Save Position Installed 1

Here’s the first of those locations. It occurs in the subVI Unfloat the VI.vi and it handles the case where the user closes any of the floating windows. Again you’ll notice one added subVI. Using the VI reference supplied to it, the subVI determines the target VI’s new window position and saves it. The other place where this VI occurs is in the event that stops the application.

Save Position Installed 2

Here the event logic checks to see if a window is docked, and if not calls the same subVI to save the window position of the VI associated with the reference.

Digging for Details

Now that we see where the modification fits into the application, let’s look at how the subVIs work – starting with the routine that saves the window position.

Save Window Location

As you can see, I am using the configuration file VIs to store the data in a text file using the INI file structure. However, it’s not the application’s INI file but rather one that I am creating in the user’s “My Documents” directory. This selection has at least a couple of implications. First it means that every user that logs into the computer will have their own set of customizations. Second, if the user wants to reset all their customizations back to default, all they have to do is delete or rename that one file.

Next, notice that this VI was designed to be usable in two different ways. If the VI reference input carries a valid reference the code uses that reference to get the data it wants to save. Alternatively, if the VI reference provided is not valid, the true case of the structure (not shown) open a reference to the VI calling this subVI and save the data for it.

Finally, let’s look at the subVI that the code uses to convert the window bounds data into the string that will be saved to the custom INI file.

Pack WinBounds

In keeping with the concepts I cited earlier, I obfuscate the datatype by flattening the structure to a string, convert the string into an array of U8s, and finally format the array as a string of 2-character hexadecimal values. However, before making that last conversion I provide a mechanism for ensuring data validity: I append a 16 bit CRC. The result is a string that will allow you to detect if it has been manipulated outside the program.

Turning now to the VI that retrieves the data, we see logic that reverses what was done in the position save routine. However, there is one added twist: if this VI is being called for the first time, the position of the target VI might not be in the INI file. Consequently the code needs to be able to recognize that situation and just let the windows open in its default position.

Read Location and Position Window

The subVI (below) that converts the string from the INI file back into the LabVIEW data structure for defining a windows bounds, generates a structure containing all 0s when it is passed an empty string – which is what you get when you try to read a string value from an INI file that doesn’t exist.

Unpack WinBounds

Also notice how this VI verifies the CRC. It’s commonly believed that in order to verify a CRC you have to split the CRC from the message, calculate a new CRC on the message and compare the result to the CRC received as part of the message. However, such is not the case. Due to the way the CRC calculation works, if you simply perform a CRC on the entire message including the original CRC the results will always be 0 for a valid message and CRC. Hence, the logic only goes to the trouble of splitting the message from the CRC after it has determined that the message is valid. In the case where the second CRC calculation is not zero (indicating a corrupted message) the logic outputs the same invalid data structure that you get from a null input string.

Given these facts, the VI for reading the target VI’s last position only has to look for that known-invalid data structure and if it finds it, bypasses the logic that set the window’s bounds.

Undockable Windows – Release 2
Toolbox – Release 10

The Big Tease

So there we have it, a basic framework that you can use to implement a variety of “window memory” functions. But what about next time? I have talked a lot about processes that run in the background. What happens though, if you want to be able to provide a minimal interface that isn’t always there but can be easily called up when needed. Next time I’m going how you can utilize the Windows system tray to house just such an interface. At the same time we’ll look at one of the more interesting backwaters of LabVIEW development – .net callbacks.

Until Next Time…

Mike…

Managing Standalone Executables

A while back when we were discussing opportunities for code modularization, I made the comment that it is even possible to incorporate processes into a new application that have already been compiled into standalone executables. At the time we didn’t have the time to go into detail on the point, but now we will take the time to consider the various tasks required to manage an existing standalone executable that you wish to leverage for a new application.

So What’s to Manage?

When I talk about managing an executable, I’m including all those tasks needed to control the executable from an operating system level. Therefore, to complete these tasks successfully we need a certain basic understanding of Windows and how it interacts with the programs that it is running. As you might imagine, most of these tasks will involve simply starting and stopping programs, but there are nuances that are important too. For example, when I double-click on a program the first time, Windows will launch it, but what if I double-click on it again? In this situation Windows has two possible options: launch another instance of the program or activate (bring to the front) the instance that is already running. One of the things that we will look at is how to specify that action for the executables we create,

Likewise, sometimes it isn’t enough to simply launch a program, sometimes we need to take high level look at its operation to determine such things as: How many instances of it are currently running or how much memory it is using. This information can help us decide how to best husband the computer resources that our application as a whole uses. Consequently, we will spend a little time considering the alternatives.

Finally, there can even be subtleties when it comes to stopping an executable. Can we simply signal the executable that it needs to stop, or do we need to force it to stop Now.

Getting Things Started

The first thing we want to cover is how to programmatically launch standalone applications. So to state the obvious, this topic is first and foremost about how to tell the operating system to launch the application for us. Luckily, this task is easily handled by the built-in System Exec.vi. This function allows you to do from within LabVIEW anything you would do from a command prompt — though at times the operations aren’t done in the exact the same way. In fact, for some older versions of Windows the differences can be significant. But even in Windows 8 there can be small inconsistencies. For example, say you have a program called Small Test Executable.exe that can accept a couple of command-line arguments like p3365 fpd2. You could launch this program from the command line like so:

C:\"Program Files\Test Executable\Small Test Executable.exe" -- p3365 fpd2

Unfortunately, this doesn’t work with System Exec.vi, which simply returns an (undocumented) error. To get the command to work you have to either write it to a temporary batch file and then execute the batch file, or modify the command like this:

"C:\Program Files\Test Executable\Small Test Executable.exe" -- p3365 fpd2

Don’t see a difference? Check out the location of the first double quote… To simplify dealing with these variations, I have created a subVI to encapsulate the functionality called Launch Executable w-Command Line Parameters.vi and included it in the new release of the toolbox.

Launch EXE with Parameters

The other thing about getting an executable running is that there are a few important launch parameters that we can only set in the application INI file, so we’ll deal with those next.

A Windows Convention

One of the conventions that Windows imposes is that the INI for an executable will have the name as the executable (but with the file extension ini) and the standard configuration parameters will reside is a section with the same name as the file (but minus the file extension). It is in this section that the runtime engine expects to find the parameters that it needs to successfully launch the executable.

The first such parameter we need to discuss controls the behavior when the executable is called more than once. By default, when you create an executable with LabVIEW only one copy of it will launch. If you call it again, Windows will simple bring to the front the one instance of the program that is already running. However, you can change this behavior by including this line in the INI file:

AllowMultipleInstances = TRUE

With this parameter set as shown, Windows will launch another instance of the program each time it is called. An important point to remember, however, is that no matter how many instances you launch, they all share the same INI file. Consequently, if you want to pass unique parameters to each instance, you will have to do so through the command line when the instances are launched.

The second INI file parameter I want to mention controls the program’s visibility to the computer user. Regardless of whether its GUI is visible, when a program normally launches it has a tab that appears on the taskbar. But what if you are creating a program that is intended to run unseen in the background? This setting handles that case.

HideRootWindow = True

When this setting is true, the program can still have a user interface but there is no tab associated with its window on the taskbar. As a side effect, the program also doesn’t appear in the task manager as an “App”, but rather lists it as a “Background Process”. Please note that this terminology is what Windows 8.x uses, older versions make the same distinction but use the word “Programs” and “Processes” to describe it.

The third INI file setting to discuss, is really not so much of a setting, as it is a family of settings that work together to control the program’s ability to respond to external connections via TCP/IP and VI Server.

server.tcp.enabled=True
server.tcp.port=3363
server.tcp.serviceName=""
server.tcp.acl="290000000A000000010000001D00000003000000010000002A10000000030000000000010000000000"
server.vi.access="+*"
server.vi.callsEnabled=True
server.app.propertiesEnabled=True
server.vi.propertiesEnabled=True
server.control.propertiesEnabled=True

Some of these settings are easy to understand, while others are rather obtuse. For right now, don’t worry about what these settings all mean. Next week we will.look at these parameters in detail.

Passing Parameters

To reiterate a point I made earlier, creating a standalone executable that will support multiple instances pretty much necessitates the use of command line arguments. In some ways, this situation is analogous to the situation concerning reentrant clones. The instance needs to be able to identify itself in the midst of a cloud of identical instances.

Given that point, one of the things you should consider when creating such an application is how the program should respond if the required command line parameters are missing. The answer to that question depends, to a large extent, on the nature of the parameters. If the input is essentially a customization for which there is a valid default value, it might be acceptable to simply accept that default and go on. However, in some situations there is no default value possible, so you should consider shutting down the program if the command line parameter is invalid or missing.

Getting Status

After we have gotten the instance (or instances) of a program up and running, one of the things that we might want to do is check on how, and perhaps what, it is doing. For now we will consider this matter in terms of the performance aspects that are visible to Windows. It is certainly possible to access the executable programmatically to obtain internal status information, and even control its operation, but that discussion will have to wait until next week.

General Information

One of the most basic indicators of the health of a program is its memory usage. You should expect the memory that a program has allocated will rise and fall over time. It should not treat trend steadily upwards without regard for what the program is actually doing. If a program’s memory consumption does consistently increase over time, that condition is referred to as a “memory leak” and it is a condition that needs to be addressed. Typically this remediation takes the form of figuring out why the program wants ever-increasing amounts of memory (like programming errors resulting in file or I/O being left open) and fixing the problem. Occasionally however, you will find that the leak is occurring in a place where you have no visibility — like inside LabVIEW itself. In those situations all you can do is try forcing Windows to deallocate the memory or change the way you are doing something so the problem doesn’t arise.

A simple way to read memory usage is to call the simple command line function tasklist and parse the result. Here is the code I wrote to do the job. Its name is Read Task List.vi.

Read Task List

If you leave the Image Name control empty, the call returns information on all the tasks currently running. However, if you populate that control with the name of a program, it returns a list of all the programs with that name. For example, on my computer right now LabVIEW.exe returns 1 item, while chrome.exe returns 13. (Why does Chrome need 13 instances of itself running in the background?)

The two most valuable pieces of information that you can get from this command is the application’s memory usage (expressed in kilobytes) and its PID. The PID is a numeric tag that uniquely identifies each program that is currently running. It is important because other commands or system calls will often require the PID of the program that you are wanting to check.

Digging for the Details

Beyond this basic information there is a variety of other details that you can find, For example, here you can find a community-developed VI that makes direct calls to a .NET assembly in order to return a plethora of information about your computer in general, and the programs that are running in particular. From the standpoint of accessing this information programmatically, this VI has two big problems:

  1. The code is at least 7 years old: This is a problem because there are places where the same operations could be done much more efficiently using techniques that LabVIEW has introduced in the intervening time.
  2. It’s essentially undocumented: To figure out how to really use it you will have to spend time researching the calls and the specific parameters — which can vary from one version of Windows to the next.
  3. It was designed for manual operation: In other words, it was designed to be used interactively, and not programmatically.

Because we need something that is usable from within a program, I created two subVIs that essentially repackage the functionality in a more program-friendly form. The first subVI is called Get Available Instances.vi. To see why this VI is needed, think back to the output of the tasklist function. In that output multiple instances of the same executable (like the 13 copies of chrome) all had the same name. They were distinguished from one another by a numeric id number called a PID. The designers of the .NET interface must have thought this approach confusing because they took a different approach that did away with the PID in favor of a name that was modified to make it unique — as in chrome through chrome#12. Likewise, any file extension was also dropped from the name.

Get Available Instances

The other subVI (Get Task Performance.vi) accepts as inputs an instance name and an enumeration that lists the available performance parameters that the VI can fetch under Windows 8.

Get Task Performance

But what is the deal with the loop and all the “extra” logic? This loop is needed because, while some of the parameters are instantaneous values that can be read at any time, others are not. As a case in point, consider the processor usage measurements. Everyone knows (or at least everyone in this business should know) that a CPU can really only do one thing at a time. In fact, the illusion that is central to much of modern technology is the programmatic slight of hand that makes it look like computers are doing multiple things at once.

Now since a CPU can only do one thing at a time, logically there are only two possible immediate answers to the question of how much processor are you’re using. Either you are not executing (in which case you have 0%) or you are executing (resulting in 100% usage). In short, for this type of measurement to have any sort of meaning it has to be approached as a statistic, not an absolute measurement. In the VI, this task is implemented by making two calls to the same measurement counter. The first call — which always returns a 0 — starts the measurement process, with the second and subsequent reads returning the statistical results for the previous measurement period.

Oh, and the logic for stopping the loop after just one iteration? That’s to optimize operation for parameters that are absolute.

Stopping: Fast and Half-Fast

It has been said that, “All good things must come to an end” and this is true for programs as well. However Windows actually provides two different types of shutdown events. The first causes Windows to simply abort the targeted program. This approach, while very fast, is logically equivalent to clicking the abort button on a LabVIEW program — and carries with it all the same dangers. The other type of shutdown event takes longer, but is much safer. It basically sends a message to the targeted program that requests it to stop. However, to reiterate something we learned before, for this messaging to work properly the executable must be written such that the Application Instance Close event will always initiate an orderly shut down.

In addition to using these messages for its own purposes, Windows also provides a command line function that allows users and individual programs to generate these messages as well. I implement both types of shut down requests in the subVI Task Killer.vi.

As a polymorphic VI, the routine incorporates instances that can shut down processes by name and by PID. You simply pick the version that gives you the level of control that you need. For instance, if I wanted to shut down all 13 instances of chrome, using the name version of the code would get the job done all at once. However, if I wanted to stop just a few select instances, I would use the version that uses the PID input. Here is the name version of the code.

task killer

But why start and stop at all?

Before ending this time, there’s one more idea that we should consider. To this point we have been thinking about the management of executables in a rather conventional sort of way: Start something just before we use it and close it when we are done. But might not there be situations where the “conventional” doesn’t make sense? I would assert that there are more than you might at first imagine.

The whole point of interacting with a process that is deployed as a separate standalone executable is that it obviously implements some bit of functionality that more than one application would need. Given that fact, starting and stopping it — especially stopping it — can get complicated. Just because one application is stopping and no longer needs it, that doesn’t mean there might not still be another application that does. One obvious solution to this problem is to simply let Windows handle it all. Install the background task such that it starts when either the computer boots, or the user logs in; and then stops when Windows shuts down. This solution might seem wasteful, but what is the real impact of such a solution? Oh sure, the program will be in memory, but if it isn’t being used it will probably be shuffled off to the page file pretty quickly. Likewise, if the executable is written correctly, it will be event driven which means that unless it is accessed by a program that is using it, the process won’t be burning up any CPU cycles either — so what is there to lose?

Just something to be thinking about.

Toolbox Release 8

The Big Tease

Ok, this is all for now. Next time I’ll introduce a little executable that will let us practice some of the theory we have examined today — and expand the conversation to include how to use VI Server to interact with executables created in LabVIEW.

Until Next Time…

Mike…

Finishing the Configuration Management

For the last couple posts we have been looking at how to best utilize object-oriented programming methodologies. In that quest, we have taken as our example the goal of converting the parts of our testbed application that use stored configuration data to a configuration manager based on object classes. In this implementation, the classes represent the various types of potential data repositories.

Maximizing Flexibility by Leveraging Existing Code

When we stopped last time we had created all the basic code infrastructure and all we had to do was construct the dynamic dispatch methods that retrieve the data the application needs. In creating these methods, we have as our guiding principle reducing the amount of code we have to create by reusing as much code as we can. In other words we need to really spend some time thinking about how to structure our code such that it combines maximum reuse with maximum flexibility.

One good way of attaining that ideal is to allow for multiple levels of dynamic dispatch within the same method. Let’s say for the sake of argument that you have a method that will need to be accessible from 10 different subclasses. Furthermore, let’s say that 4 of those subclasses all need to do the same thing, an additional 4 are mostly the same but with a few differences, and the last 2 subclasses require fundamentally different logic to perform the same task. You could implement the logic that is common to the largest group of subclasses (the first 4) in the parent and let the remaining 6 override the parent to define their own solutions. This solution would work, but could potentially result in a lot of duplicated code. It depends on how similar the second group of 4 subclasses are to the first group of 4.

In creating the Initialize New method last week we saw a far better way to optimize the code: For the 4 subclasses that are similar, we could call the parent method in the child (thus taking advantage of that existing code) and then add a little logic to customize the functionality. This solution will work well in many situations, but one area where it will not is in scenarios where the common parts of the code need to pass data to the unique parts.

To address those situations, a very useful solution can be to write the parent method such that it has a subVI encapsulating the similar, but unique bits, that is itself a dynamic dispatch VI. By providing multiple levels of dynamic dispatch, you create a situation that is easy to understand and minimizes duplication of code. In our example, the 4 subclasses would use the parent implementations of the dynamic dispatch VI and subVI. The 4 subclasses that are similar, but a bit different would use the parent method, but override the dynamic dispatch subVI, and the two that are fundamentally different would override the parent method VI itself.

Getting Down to Business

So let’s start looking at what we need to do to finish our conversion — while remembering that most of this discussion will be about the reorganization and repurposing of existing code. For the most part, I won’t be going into how the basic functionality works since we discussed that code when it was originally introduced.

The basic internal structure of our four “blueprint” VIs will be largely similar. We will call the VI that creates the object we want, followed by a dynamic dispatch method that does what we need done. However, note that there isn’t (and shouldn’t be) a direct one-to-one correlation between the blueprint VI and the underlying method. We still need to be looking for ways to minimize redundant code. Back when we were setting up the testbed application originally we considered the need for unstructured configuration values — like you would usually store in an INI file. In fact, we implemented that capability and used it to store the default sample period. Consequently, one of the three methods we end up implementing will be used in a couple of different places now, and will be reusable in the future for other unstructured configuration data. So, let’s start with that one.

Get Misc Setups

Creating this method follows the same procedure as we have used before, except that this method has two input parameters and an output string. So the finished parent method has a front panel that looks like this:

misc read front panel

Another difference with this method is that if it is called for a subclass that does not override it, the parent VI does not provide any default functionality. To see what I mean, check out the VI’s block diagram. You see that it does nothing. This structure might seem rather pointless, but in reality it can at times be pretty handy. Say you have a method that is really only needed by one subclass. Without dynamic dispatch you would have to put a case structure around the code so the VI is only called in that one particular circumstance. However, with dynamic dispatch, this selection takes place automatically and without cluttering up the code with case structures.

With the parent VI (Read Misc Settings.vi) created and saved, we can now build the two subclass overrides. Starting with the Text version, to read the data from the INI file, we create an override VI in Config Data_Text.lvclass containing code that uses the built-in configuration VIs to fetch the data:

read misc - ini

But wait! This won’t work. You notice that the path to the INI file comes from the class data — which is fine except that we forgot to initialize it. I wanted to highlight this point because it is a very easy, and common mistake to make. Initializing the DVR is not the same as initializing the data in the DVR. All we have to do to fix this memory lapse is modify the class’ version of the Initialize New method, like so:

finish initializing the DVR - text

The subVI with the green banner builds the path to the application’s INI file programmatically. By the way, in case you’re wondering about whether the same issue applies to the other override to this method (the one for databases), the answer is “Yes”. However the solution there is a bit more complex, so we’ll deal with it in a moment.

The other thing to notice about the setup reading method’s code is that if the read doesn’t find the desired value in the INI file, it creates it and gives it a default value. Adding this extra bit is often helpful for recovering from situations where a uses has gotten in and mucked around with the INI file and deleted something they should not have. In any case, to see this code in action, we need to finish up the code in the Get Default Sample Period.vi “blueprint” VI:

get default sample period

You can now run this VI and get a valid result from the INI file. Of course, if you set the configuration data selector value in the INI file to Jet you will get a zero back. This results is because we haven’t provided an override for that subclass yet. Note that you do not get an error because not overriding a parent method is a perfectly valid thing to do.

Updating the Database Initialization

But we need the database data too, so let’s backtrack to the Initialize New VI in Config Data_DB.lvclass. The class data for this subclass is a string that the code will use in one way or another to connect to the database. However, there are three possible sources for this string:

  • Most ADO interfaces use a rather complex and specialized connection string that can identify logical names, network paths, security parameters and a lot more.
  • Although Jet does use ADO drivers, its connection string is much simpler. In fact, with the exception of the file path, it can be largely treated as a big string constant.
  • SQLite (which we aren’t going to support right now, but which exists in the class hierarchy) doesn’t use any ADO parameters. Keeping with its minimalist approach, all it needs is a file path.

So what are we going to do? Well, if you said, this sounds like a job for dynamic dispatch, you’re right! We need to create a method that will derive the correct string depending on which specific subclass is present. So let’s create a virtual folder and a subdirectory named _protected in Config Data_DB.lvclass with Protected access scope. Inside the virtual folder we will create a new VI using the dynamic dispatch template called Get Connection String.vi, and save it in the subdirectory. In addition to the signal IO the template provides, we need to add a string output:

get connection string

After saving this new the parent class method, we can fill in the overrides. However that code is pretty trivial, mostly scavenged from the original code, and has very little to do with OOP. Consequently, I won’t take time here to highlight it, but feel free to check it for yourself. Then with this work completed, we can finish the initialization code.

finish initializing the DVR - jet

Accessing the Database

Although we will be using a Jet database, note that the override for the Read Misc Settings method will reside in the Config Data_DB_ADO.lvclass subclass, not Config Data_DB_ADO_Jet.lvclass. The reason for this placement is that with the exception of the connection string (which we handle during initialization) the query logic is the same for Jet or most other ADO databases. By the way, what would we do if we ever did find a DBMS that needed something different? That’s right, we would just create a subclass for it and override the method in that new subclass.

In any case, most of the code we need now was lifted wholesale from the old configuration library:

read misc - jet

And with that we are done with the first of our blueprint VIs. However, this is also the procedure we will follow the VIs to read the error handling parameters, the startup processes and load the machine configurations for each state machine clone. Again, I won’t step through the creation of this code because from the standpoint of this post’s main topic (object oriented code development) there is nothing in these that is any different from the one we did go through. As a friend of mine used to say, “From here on, it’s just a matter of turnin’ the crank.”

Testing

To make the results of these changes easier to verify, I have made slight differences between the configuration in the INI file, and the configuration in the database. Notice that if you start the application with the INI file set to Text, the application launches with only two TC state machines running and the acquisition sample rate defaults to 500 msec. However, a setting of Jet produces the functionality we have seen before (3 state machines and a 1000 msec default sample rate).

Notice also that you have to make the INI file setting changes before starting the application. The logic could certainly have been written to make the settings reconfigurable on the fly, but it would have added another level of complication, so I will leave that as, “…as an exercise for the reader…”.

Before tagging this release of the code in SVN, I also went through the “recycled” code and made sure that the VIs were all saved in their proper locations and all the redundant code had been removed. A project window feature that made the latter task much easier was the ability to right-click on a folder and have LabVIEW list all the files with no callers. One thing to be careful about, however, is deleting “unused” files that are in classes. The logic behind this feature can’t identify VIs that are being dynamically linked — which pretty much describes dynamic dispatch VIs.

Finally, before closings out this post, I want to remind you of something important. I make no claim that this code is the best implementation for all possible situations. Although a lot of it is based on software that I developed for deliverable systems, the point on this blog is to provide you with examples, demonstrations, and models that you can then customize and mold to fit your customers’ specific needs. In music, there is the idea of “variations on a theme”. In fact, in a few cases the variations have become more famous than the original. So take the themes I offer here and feel free to deconstruct, rearrange and reassemble them into something that is new and exciting.

Testbed application Release 15
Toolbox Release 7

The Big Tease

What if I told you that LabVIEW incorporates a feature that can improve the quality of your code and reduce errors by helping to validate your math? Hey, this is a no-brainer! Everybody can use help validating their code. But, what would you say if I told you that most people never use this feature? Crazy…

Until Next Time…

Mike…

Objectifying the Testbed

Object-oriented programming as a technique promises a host of benefits, but suffers from the impression that it is in some way an “advanced” topic. In contrast, I feel that OOP is just a logical extension of the concepts that LabVIEW developers use every day. The basic problem has been with the way it has been taught. However, the various object-oriented frameworks that are overly complex and difficult to learn, haven’t helped matters. These bad “actors” often only serve to hide the inherent elegance of the OOP paradigm and scare off users that could benefit from it.

To help clear away some of the extraneous mystique, I have presented a brief introduction to the topic that provides a foundation sufficient to let us get into OOP by implementing a module for managing program configuration data that provides the calling application with a common interface regardless of how (or where) the data is stored.

Filling a Niche

Most of the work we will be doing will eventually replace the Configuration Management library. Now while this might sound like a major shift, it really is not because (like I repeatedly tell you) the whole point of good design is to make changes and upgrades like this possible. So let’s look at what this upgrade will need to do.

Normally a large part of any upgrade projects is defining the requirements, but due to the design work that was put in originally, we already have a pretty good handle on what the new class structure has to do. In terms of surface functionality, we know we have to be able to handle all the same information as before — with, of course, the ability to add more when we want or need that ability.

Designing the Structure

The trick is going to be sorting out what new functionality will be needed under the covers. At the most basic level we need to be able use either text files or databases to actually store the configuration data, so there we have two subclasses. But we need to consider whether each of those options needs to be broken down further.

On the “text file” side, the data might be coming from a standard INI file, or the code might be in using a custom text file format. Custom text configuration files are very common when some of the configuration data is tabular, since it is a pain to store tabular data in an INI file. However, regardless of the format of the contents, the basic mechanism for reading and writing text files remains the same so it probably won’t be valuable to have any subclasses under “text files”.

On the “database” side of things, however, the situation is very different. First of all, in terms of connectivity, you can access most databases through the standard ADO (ActiveX Data Objects, also sometimes called ole-db) interface. However, “most” is not the same as “all” and one common exception is SQLite. A popular, lightweight data management engine, SQLite can run on a variety of platforms — including some real-time systems. To keep its footprint small, SQLite utilizes a small custom DLL, rather than a large, but standardized, interface. So we need to make provisions for other types of connectivity by creating (for now) two subclasses below “database”: “ado” and “sqlite” — though we won’t be implementing the SQLite functionality right now.

Finally, what about “ado”? Can it be broken down further? Maybe. One of the advantages of ADO is that it, for the most part it does a pretty good job of hiding the differences between one database management system (or DBMS) and the next, but there are some variations it can’t paper over. These differences often relate to the version, or dialect, of SQL the DBMS speaks. However sometimes differences arise because some DBMS fundamentally don’t operate the same. For example, while most DBMS go to extraordinary lengths to hide exactly where and how the data is actually stored, Jet (the DBMS built into Windows) stores the data in a file you explicitly identify. Hence, while the connection to other DBMS might be defined in terms of network paths and logical names, with Jet you are connecting to a particular file.

To provide for these sorts of functional nuances, let’s create a subclass below “ado” for “jet” — understanding there could be others in the future.

Configuration Data Classes

This is what the hierarchy looks like so far, all drawn out.

Doing the Rough Framing

When you are building a house the first tradesmen to show up onsite are the carpenters to do the so-called “rough framing”. This process creates the skeletal form of the final house that is covered with rough exterior plywood. The idea is that later workers will fill in the details and fine tune the construction. And metaphorically speaking, that’s what we have to do now for our configuration data class.

For a class hierarchy, that framing consists of the directory structure and the class files themselves. Using the techniques I gave last time, I first create mirroring directory structures inside the project directory and the project itself…

Configuration Data Classes in Project

Note that I have also created some virtual folders inside the Config Data class which represents actual sub directories.

  • Interface
    The VIs in this folder will have public access scope. In fact they will be the only VIs in the library that are so scoped. Because these VIs are the only ones that outside callers will be able to call, they alone form the interface between the class hierarchy and the rest of the code.
  • _private
    As the folder title implies, the files that go in here will have private access scope. This assignment means that they will only be accessible from other VIs in the top-level class.
  • _protected
    This is another folder that specifies access scope for its contents. In the case of protected scope, the VIs in this folder will only be accessible from the top-level class, or any of its child classes.

Creating the Interface

With the functional scaffolding in place, we can start filling in the blanks. And the first thing we need to do is create what I referred to as the “blueprint” in the previous post — the VIs that the rest of the application will call. After going through the public VIs in the old library we see that there are really only 4 VIs that the rest of the application uses directly

  1. Get Default Sample Period.vi
  2. Get Error Handling Parameters.vi
  3. Get Processes to Launch.vi
  4. Load Machine Configuration.vi

Many of the others will still be used, but their presence will be hidden in subclasses. As I create these (for now) empty VIs, I make sure they have the same front panels and connector panes as the ones they will be replacing.

Adding Infrastructure

Getting back to our housebuilding analogy: After the framework is completed and the outside skin is on, the next job is to start installing some of the needed infrastructure, because without electric, water, sewer and perhaps gas connections, our new home is not much better than the cave dwellings that our prehistoric ancestors inhabited — and in some ways is far worse.

What we need to add to our nascent configuration management subsystem is some data handling, but not for our data. The data I’m talking is the private internal data that the subsystem needs to maintain in order to do its job.

Thinking about what our various bits of code need to do, we see first that there is certain data that will commonly be needed regardless of how you actually end up getting the data. What I’m thinking about here is the name of the operator and a password. Now, some subclasses might need both, while others might need only one or the other, and that’s fine. The important point is that if all subclasses could potentially need at least some of this data, it has to be data that is associated with the top-level class. However, this requirement for global availability causes a problem.

Remember how when we were defining terms in the previous post we said that in the LabVIEW world an object is a wire? LabVIEW wires, and data contained in them are by definition not global. If you want data from a wire you need to be connected to it. So how can we create wires that are separate, but which still share at least some of the same data? Well, one very good way of doing it would be to create one central data store that all the wires can access, and as it turns out LabVIEW incorporates a feature that is very efficient and so is perfect for such an implementation. I’m talking about the Data Value Reference, or DVR.

Our approach will be simple. We first create a DVR that is defined to hold the data we need and make a reference to that DVR the class data for our Config Data class. Then to access that data, we create a family of data access VIs to insert data into, or read data from, the DVR.

The first step on this process is to create another virtual folder in the top-level class named _dvr with privateaccess scope. Next, I create in that folder a typedef control named Config Mgr Data.ctl that consists of a cluster containing two strings, one for user name and one for password.

Config Mgr Data

When saving this control I create a subdirectory (called _dvr) to hold it. Likewise, I also create a VI in the same directory (and virtual folder) called Config Mgr DVR.vi that contains this code:

Config Mgr DVR

Two comments about this code. First, it has the basic form of a FGV where the variable value is the DVR reference. Because the case that generates the reference is only run once, this VI will always return a reference to the same DVR no matter how many times it is run. Second, the data for the DVR is the typedef we created. This point is critical. As with UDEs, if you define a DVR reference using a typedef, you can later change the typedef and it won’t break the reference.

To make this DVR available and inheritable through the class, I copy and paste the reference indicator into the class data cluster, like so.

Config Mgr DVR in Class Data

Then to provide access to the DVR contents, I create protected scope VIs that I store in a virtual folder and subdirectory both named _data access. Here is what the read VI for the user ID parameter looks like…

User ID Read

…and the write VI…

User ID Write

I next realize that the two main subclasses also have some data that will need to be held in common for their subclasses. So I repeat the process I just used to store a file path for the file subclass and the connection string that the db subclass needs. Here’s what the project looks like now.

project with basic infrastructure

Finally, before moving on we are going to need a way to initialize all the logic we have created, and to do that we will take our first foray into the exciting — though sometimes confusing — world of creating dynamic dispatch VIs. The goal is to create a method called Initialize New that causes the DVR in a new instance of a class to automatically initialize itself.

I start by right clicking on the _protected virtual folder in Config Data.lvclass and from the New sub menu selecting the option to create a new VI using the dynamic dispatch template. I leave the front panel of the resulting VI the way it is, but I change the connector pane, edit the icon and add this code to the block diagram.

Initialize top-level dvr

If the DVR in the class data is not valid, I call the DVR VI to get a valid reference and then use that reference to populate the class data. If the DVR reference is valid, the false case (not shown) does nothing. When I save this VI, I put it in a subdirectory named _protected.

To create the subclass versions of this method, I right-click on the subclass name and from the New sub menu select the item to create a new VI for override — which is the technical term for what we are doing. We are overriding the parent functionality with different functionality in the child.

However before we get a new VI, LabVIEW opens a dialog to ask us which parent method we want to override. After double clicking on Initialize New we get our new VI, also called Initialize New. After saving this new VI, (the default location LabVIEW picks is perfect) I modify the code to look like this:

initialize new - child

Most of this logic should be familiar because it is the same as we did for the parent. If the child DVR reference is not valid, we initialize it. Otherwise, we do nothing. But what is that funny looking VI in front of the initialization logic?

Object-oriented methodology recognizes that there are going to be times when a method in a child class will need to do what the parent method does, but perhaps a bit more or maybe do it a bit differently. One solution to this situation would be to simply duplicate all the parent code in the child. However that approach would be wasteful. What object-oriented logic does instead is it allows a child to directly call its parent’s version of the method. So here, the code first calls the parent’s version of the initialization VI and then executes the logic to initialize itself. In the end, both the parent and the child will get initialized.

Creating Objects

The time is now upon us to start tying this infrastructure together into an organized system. The first thing to sort out is how to create an object of a given type. One way is very similar to what you would do with a conventional datatype. If you wanted to create, say an I32 value, you would drop down a constant and start using it. In the same way, you can also drop down a class constant and wire to it, thus creating an object. The problem with this approach is that when LabVIEW instantiates a class it also loads into memory all the VIs associated with the class. What you can end up with is a situation where all the VIs for all the classes are loaded, even if you will never use some of the classes. The way to get around that problem is to load classes dynamically as you need them. This is the code I use to perform that operation.

create object dynamically

You will notice that the VI has no inputs, save the requisite error cluster. This is because the basic piece of information that specifies the specific class to be created will be loaded from the application INI file. But why the INI file? Isn’t the point of this exercise to get rid or configuration data in that file? Well yes, but there is a bit of a paradox at work here. Simply put, an application can’t go to a database it doesn’t know it has to find if it should look in the database to get its setup data. That basic piece of information has to be stored somewhere that will always be there, and on the Windows platform you have exactly two choices: the INI file and the Windows registry. Of the two, the INI file is much safer — you at least don’t have to worry about a user going wild and trashing their whole computer.

We are initially only going to support two options for managing configuration data, so we only need two settings (Text and Jet). The VI that communicates with the INI file reads one of these values from the Configuration Data key in the Data Management section and returns two values based on what it finds there: A relative path to the location of a class file, and the name of the file. Thanks to the naming convention we use, these two values are very closely related.

The remaining code loads the target class into memory and initializes it. In addition, the resulting object is buffered as in a FGV. For the details of how this process works, see the comments in the code. The final thing to notice is that the output from this VI is an indicator of the Config Data class datatype.

Building Out the Remaining Methods

All that’s left now is to implement the code that does what the testbed needs done, however this post is getting long and I have already passed on a lot of information for you to absorb — so we’ll leave that discussion (and the testing!) for next week.

Until Next Time…

Mike…

Objectifying LabVIEW

I suppose a good place to start this post is with an admission that, in a sense, it is flying a false flag. One way that you could reasonably interpret the title is that in this post I am going to be showing you how to start using objects in LabVIEW. That interpretation is not correct, and the troublesome word is “start”. The fact of the matter is that you can’t use LabVIEW without interacting with objects and many parts of it (think: VI Server) are overtly object-oriented — even without an obvious class structure. The language is built on an objects oriented foundation and so, in a very real way, has been object-oriented since Version 1.

What I am going to be showing you is how to simplify your work by building your own classes. As I stated in the teaser last time, the starting point for this discussion is the recommendation given in NI’s object-oriented training class that you should make your first attempts at using explicit object-oriented technique small, easy to manage subsystems — or put more simply, we need to start with baby steps.

Object-oriented baby steps

OK, so this is the point in the presentation where most presenters hauls out some standard theory, and moth-eaten descriptions of objects and classes — often lifted wholesale from a book on C++ programming. The problem with this approach is of course that we aren’t C++ programmers and the amount of useful information we can draw from an implementation of objects oriented programming that is so fundamentally flawed is minimal at best. The approach I intend to take instead focuses on key aspects of the technique that are of immediate, practical importance to someone who is working in LabVIEW and wants to take advantage of explicitly implementing object-oriented class structures.

A Quick Glossary

The first thing we need is a vocabulary that will let us talk about the topic at hand.

OOP Clouds

Now be forewarned that some of these definitions may not exactly match what you may read elsewhere, but they are correct for the LabVIEW development environment.

  • Class — An abstract datatype.
    If you think that sounds a lot like the definition of a cluster, you’re right! Due to the way LabVIEW implements object orientation, a class is essentially a very fancy cluster. In fact, when you create a class the first item that LabVIEW inserts into it is a typedef consisting of an empty cluster. Although you don’t have to put anything into the cluster, it provides a place to put data that is private to that class.
  • Object — An instance of a class.
    As with a normal cluster, every instance of a class has its own memory space. Consequently, a class wire is in most ways the same as any other wire in LabVIEW. We are still working in a dataflow environment.
  • Property — A piece of data that tells you something about the object.
    This is why there is a cluster at the heart of the class. You want to put in that cluster information that will describe the object is a way that is meaningful to you application. Because each instance of the class is a separate wire that has its own memory space, the data contained in the cluster describes that particular object.
  • Method — A VI that is associated with a particular class and which does something useful.
    So what do I mean by, “…something useful…”? Well that all depends on the class’ purpose. A the class that is responsible for creating a visual interface might have a method that causes an object to draw itself. While a class that manages the interface to data storage would likely have a method to store or retrieve application data.

From this simple list of words we can begin to see the general shape of the arena in which we will be playing. To recap: A class is a kind of wire. An object is a particular wire. A property is data carried in the wire that describes it in a useful way, and methods use the object data to do something you need done.

Dynamic Dispatch

Now that we have a basic vocabulary in place that lets us talk about this stuff, there are a couple of concepts that we need to discuss. I want to start with this exploration is with the mechanism that LabVIEW uses to call methods. Referred to as dynamic dispatch this feature it is often a source of confusion to developers getting started with object-oriented programming. A good way to come to grips with dynamic dispatch is to compare and contrast it to a feature of LabVIEW with which you may already be familiar: polymorphism.

Polymorphism (from the perspective of the developer using a polymorphic subVI) is the ability of a single functions to adapt to whatever datatype is wired to its inputs. For example, the low-level Add node in LabVIEW is polymorphic. Consequently, it can add scalar numeric if all types, as well as arrays of numerics of varied dimensions, clusters of numerics and even arrays of clusters of numerics.

Of course, from the perspective of the developer creating a polymorphic VI the view is much different. This flexibility doesn’t happen on its own. Rather, you have to create all the individual instance VIs that handle the various datatypes. For example, I often want to know if a value at a specific point in the code has changed from the last time this bit of code executed. So I created a polymorphic VI that performs this function. To create this subVI, I had to write variations of the same basic logic for about a half-dozen or so basic datatypes, as well as a version that used the variant datatype to catch everything else.

Dynamic dispatch (which is actually a form of polymorphism) works much the same way, but with a couple significant differences.

  • When the decision is made as to which instance VI is to be executed
    With conventional polymorphism, the decision of which instance VI to call happens as you wire in the subVI. In the case of my polymorphic subVI, as soon as I wire a U32 to the input, LabVIEW automatically selects the U32 version of the code. However, with dynamic dispatch, that decision gets put off until runtime with LabVIEW making the decision based on the datatype present on the wire as the subVI is called. Of course for that to work, you need a different kind of wire. Which brings us to the other point…

  • The criteria for choosing between VIs
    The wires that conventional polymorphism uses to select a VI all have one thing in common — they are all static datatypes. By that I mean that a wire is a U32, or a string or whatever and it can’t change on the fly. By contrast, with dynamic dispatch, the basis for selection is a wire that is an instance of a class, and the datatype of an object can be dynamic. However this variability is not infinite. A given class wire can’t hold just any object because class structure is also hierarchical.

Say you have a class named Geometric Shapes to Draw. You can define other classes (called subclasses) like Circle or Square that are interpreted by LabVIEW as being more specific instances of Geometric Shapes to Draw objects. Due to this hierarchical relationship, a given wire can be typed as a Geometric Shapes to Draw but at runtime really be carrying a Circle or Square. As a result, a dynamic dispatch VI can call different instance VIs based on the datatype at runtime.

However, one big thing that conventional polymorphism does have in common with dynamic dispatch, is that the power doesn’t come for free. You still have to write the method VIs for dynamic dispatch to call.

Inheritance

Remember a moment ago I referred to class datatypes as being hierarchical? The fancy computer science concept governing the use of hierarchical class structures is called inheritance. The point of this label is to drive home the idea that not only are subclasses logically related to the classes above them in the hierarchy, but these so-called child classes also have access to the properties and methods contained in their parent classes. In other words they can “inherit” or use data and capabilities that belong to their parents.

Handled properly, inheritance can significantly reduced the amount of code that you have to write. Handled poorly, inheritance can turn an otherwise promising project into a veritable train wreck. Which brings up our last point…

Proper Organization

Although organization isn’t really a feature of object-oriented programming, it is never the less critical. The simple fact of the matter is that while a disorganized, undisciplined developer might be able to get by when working in conventional LabVIEW, introducing the explicit use of classes can result in utter chaos. Of the real object-oriented failures that I have seen over the years, they all shared a lack of, or inconsistent, organization.

So what sort of organizational things am I talking about? Well it’s a lot of the same stuff that we have talked about before. For a more general discussion of the topic you can check out a post that I wrote very early on titled, Conventional Wisdom. What I want to do right now is highlight some of the points that are particularly important for object-oriented work.

The two main conventions (directory structure and file naming) go together because the point of one is to mirror the other. But rather than simply list some rules, I’ll demonstrate how this works. To start, I will create a directory that is named for the class hierarchy that I will build inside it. So if the point of this class hierarchy is, for example, to update my program’s user interface, I would call the directory something obvious like GUI Update. Inside this directory I would then create the top-level class with the file name GUI Update.lvclass. At this time I will also create a couple subdirectories (_subVIs and _typedefs) that I know I will undoubtedly be needing. Finally, I have learned over the years that being able to tightly control access to VIs is very important, so I will also create at this time a project library named GUI Update.lvlib and put into it the top-level class and a virtual folder called _subclasses with its access scope set to Private.

So the parent class is set up, but what about the subclasses? I simply repeat the pattern. Let’s say the GUI Update class has subclasses for three types of controls that it will need to update: Boolean, Digital and Cluster. I create subdirectories in the parent directory that are named for the subclass that will go into each, and hierarchically name the three subclasses GUI Update_Boolean.lvclass, GUI Update_Digital.lvclass, and GUI Update_Cluster.lvclass. I am also careful to remember to add the subclass files to the _subclasses virtual folder in the library, edit their icon overlays, and set their inheritance correctly — which is to say, identify their parents. Note that while the hierarchical naming structure doesn’t automatically establish correct inheritance, this convention does make it easier to visualize class relationships in the project file.

And so I go building each layer in my class hierarchy. With each new subclass I continue the same pattern so if I eventually want to find, say a subVI associated with the class GUI Update_Digital_Unsigned Word.lvclass, I know I will find it in the directory ../GUI Update/Digital/Unsigned Word/_subVIs.

Having a pattern to which you stick relentlessly — even one as simple as this one — will save you immeasurable amounts of time.

Creating the Blueprint

The next thing I do when creating a class hierarchy (but the last thing I want to talk about right now) is how the rest of the application will interface with my new GUI Update class. This is where the access scope we have been so careful to create comes into play. In the top-level class I always create a group of VIs that have their access scope set to public. These interface VIs form the totality of the external interface to the class hierarchy and so include the functions that define what the application as a whole needs GUI Update to do for it. The logical implications of this interface layer is why I sometimes call this step in the process, “Creating the Blueprint”.

In addition to providing a very clean interface, another advantage of having this “blueprint” is that if you ever need to expand your stable of subclasses, these interface VIs will serve as a list of functions you need to support in the new subclass — or at least a list of functions that you should consider implementing in the new subclass. To see what I mean, consider that the scenario we have been discussing is actually drawn from an application I created once. The list of public interface VIs was really very short: There was a method that read a value from a remote device and wrote it to the GUI object, one that looked for control value changes to write them to the remote device, and one that allowed the calling application to set control specific properties.

Of these, all GUI objects had to implement the first one because even the controls needed to be updated once a second. The reason for this constraint was that the remote device could also be reconfigured from a local interface and the LabVIEW application needed to keep itself up to date. However, the second interface method was only applicable to controls. Finally, the third interface method was implemented very rarely for the few subclasses that needed it.

What’s up next?

We have just about run out of space for this installment, but you may have noticed that something is missing from this post: Any actual LabVIEW code. Next time we will correct that sad situation by considering how to apply these principles to the creation of a class hierarchy that provides a common mechanism for storing and retrieving program data and setup parameters that works the same (from the application’s perspective at least) regardless of whether the program is interfacing with a database or text files.

Until Next Time…

Mike…

Command Line Arguments aren’t a relic of the past

Back when phones were something with wires attached to them, computer programs had nearly non-existent user interfaces. However, the need still existed to be able to pass data to programs. The standard way that developers used for accomplishing this vital task was through cryptic codes called command line arguments. These codes which were cryptic by necessity (a data entry line couldn’t be more than 80 characters), were simply appended to the name of the program the user was wanting to run.

Despite the advances that have come along over the years, operating systems still support command line arguments — and LabVIEW applications still sometimes have a need for them.

How they work

Even today, commands still get sent to operating systems in the form of strings, and curiously, the primary delimiter still used to separate the name of the program from other parameters is the space. This is why if you look at the shortcut for a program, the path to said program is almost always in double quotes: path and program names contain spaces.

The way the process works is that anything before the first space not inside double quotes is considered to be the name of a program and is sent to the part of the OS that is responsible for launching stuff. Everything after that first space is sent to the program that is being launched, which is then responsible for determining whether the remaining string contains valid information or trash.

When LabVIEW (or the LabVIEW runtime engine) parses the remaining text, the first thing it looks for is an initial delimiter in the form of two hyphens in a row, like this:

command line arguments

Anything after the hyphens are returned to the program through an application property as an array of strings. Note that the double hyphens have a space on either side.

But what’s it good for?

Unfortunately, while it may be easy to describe what this feature does, it can be tough to identify a good use case. To help us identify where this feature might be useful, let’s consider some of the technique’s major attributes.

  • It’s not very convenient
    Because the technique depends on creating what is essentially a custom command for launching your application, it can only be used within the context of a Windows shortcut — and did I mention that you have to create the shortcut manually?

  • It can still be pretty cryptic
    Although you have complete freedom in defining how the arguments are formatted and what they do, you still have a maximum line length constraining how verbose your complete command line can be.

  • It’s not secure
    The command line arguments are unprotected and can be changed by anyone with even a smidgen of technical savvy. Moreover, they can be bypassed completely if the user simply chooses to launch the program by double-clicking the application itself.

  • It’s error-prone
    There is no way to error check arguments before they get passed to the application. Consequently, the application needs to be very careful and validate all inputs.

Doesn’t sound too promising does it? Clearly command line arguments not suitable for anything critical, so the application needs to run just as well if the arguments aren’t there. Likewise, you usually don’t want to have users needing to muck around with them. Well, believe it or not there is at least one bit of functionality that falls well within this technique’s capability. Check it out.

Tracking execution

A common problem can arise when you deploy your application as an executable and it doesn’t work on the customer’s computer. Although, LabVIEW provides facilities for remote debugging that are really nice, it sometimes isn’t usable. First of all, to use it requires you to make a special build of your application that includes the remote debug capability. Second, this debugging technique assumes a level of network access that might not be available. Third, because you don’t want to sit for hours connected to a customer’s computer waiting for something bad to happen, it is only useful for problems that you can quickly and easily duplicate. However many problems are difficult to recreate on command.

One of the earliest software troubleshooting techniques can be particularly useful in isolating this type of intermittent error. The technique involved inserting code in your application that simply prints program values to a log file. These log values can range from critical internal values like the result of an intermediate calculation, to an electronic version of bread crumbs that simply says, “I made it to the error checking state”. But if you are going to include this sort of logic, you are also going to need some way to control it. After all, we are trying to avoid creating special “debug” versions of our code, but every installation doesn’t need to be generating debug files all the time — this where command line arguments comes into play.

The basic idea is to go through you code and identify places where information is available that would be of potential value in debugging, and then create VIs that will write that information to a file. To save memory, you can even make these trace VIs dynamic so they are only loaded if they are enabled. But how do you enable them? A structure I often use is to assign each potential trace operation one bit of a U32 number. I pass the bit-mapped number into the application using a command line parameter, and store it in a FGV. Then each trace operation looks at its bit in the number and only generates its trace if the bit is set.

Putting theory into practice

To see how this approach would actually work, let’s add some trace capability to our test bed application. The following figure shows the mappings of trace operations to bits:

bit mappings

Note that we will actually be using 10 bits because three of the trace operations are in our reentrant temperature controller state machine, and we want to be able to control the trace for each clone individually. Next, we want to define the syntax for the command line parameter. To keep it simple, we will use the structure I showed above, where the value following the “d” is the bit-mapped number that will provide our enables. To generate this number, simply total the numeric values for each on the trace options you wish enabled. For example, the argument “d1” would turn on just the Sample Period Change trace, while the argument “d146” (2+16+128) would turn on the Fan State Change trace for all three temperature controllers.

In case you’re wondering how users are supposed to generate these magic numbers, that’s easy — they don’t. The intended use is that a support person will tell the customer what to enter with the instruction, “When you see the problem we are troubleshooting, go to the directory where the application is installed. You will find there a file named ‘trace.log’. Email it to us.”

Reading the arguments

In the LabVIEW environment, you gain access to the command-line arguments by reading the Command Line Arguments application property. The data is returned as a 1D array of string that LabVIEW creates using the space as a delimiter between elements. This property is always available in the development environment, but in a runtime system you have to enable it in the Advanced section of the application builder parameters. To retrieve our debugging parameter I have built a VI that searches this array for the first element that starts with the letter “d” and then converts the remainder of the string into a number.

initialize debugging

Once it has isolated the number, the code converts it into a Boolean array and stores the result in a FGV for later use.

Creating the traces

With the array of Boolean values safely tucked away in a FGV, we can begin building the code that implements the trace functionality. However, not being stupid, the first thing I do is create a subVI that will format the trace entries in a standard way. Assuming there were no errors generated before the trace operation is called, each entry will consist of a timestamp, the name of the trace point generating the data, and the trace data itself.

save trace data

If an error did occur earlier in the error chain, the trace subVI instead saves the error. Ideally, all errors should be saved to the error log as part of your program’s normal operation — but mistakes in propagating errors can easily happen.

save trace data - error

With that foundation laid, we move up a layer in the code hierarchy, and the first application-specific trace option we create is one that will record changes in the sample period that the two acquisition processes use to pace their operation.

sample period trace option

Thanks to the subVI created a moment ago, you can see that all the trace VI really has to do is check to make sure that its bit is set in the array of Boolean enables from the FGV and (if it is) write pass the trace data to the subVI. To simplify things, I also built a subVI to encapsulate the indexing process and return an enumeration giving the trace state.

The remaining options we want to implement are for the temperature controller. First, we want to save a trace message whenever the fan or cooler state changes. This is the trace VI for the Fans State Change trace. The Cooler State Change trace is almost identical:

fan state change trace option

A new challenge that this code needs to address is that for it to work properly, the VI needs to know which of three clones is calling it. To enable this new requirement, I added a new parameter to the data that is passed to the clones when they are launched. Called Launch Order it tells each clone its ordinal position in the launch sequence. With that number and a little basic math, the trace VI can calculate the correct index for its enable.

The last trace operation (also in the temperature controller) records the state machine’s state transitions. Due to its similarity to the others, it’s not worth showing its code. However, this trace option is a good example of a type of operation that can be very helpful, but which you want to be sure isn’t left enabled for an extended period of time. Every state change will be recorded — an operation that can generate a lot of entries really fast. Consequently, if you put in this sort of trace you should think about augmenting it, for example, with the ability to only report certain state transitions, or perhaps only save the last 100 trace messages.

Testing our work

To see this code in action you can build an executable, or run it from within the development environment. In either case you need to start by creating a shortcut on your desktop that points to either LabVIEW.exe or Testbed.exe. Next, edit the shortcut to add in a debug argument with a numeric value of you choosing.

Now use the shortcut to launch LabVIEW or the testbed application and monitor the project directory. A trace.log file will appear and be updated as the application executes. A handy tip is if you are going to be testing a number of set configurations, it can save time to create a number of shortcuts with different debug configurations preconfigured.

Testbed Application Release 14
Toolbox Release 7

So this is one use I came up with for this technique. Where does you intuition and imagination say to try it? In the mean time, the requisite teaser for the next post…

Objectifying LabVIEW

The National Instruments training course on object-oriented programming recommends trying it in small doses as you are learning — an attitude I heartily endorse. Next time we are going to take a small step in that direction by developing a more general solution for at least part of our data management. Along the way we’ll discover that while it may not be the ultimate programming paradigm that will revolutionize civilization as we know it (as some Object-Orient Fanboys proclaim) it can still be real useful — even in small doses.

Until Next Time…

Mike…