We have seen that building a system out of autonomous interacting processes can be a powerful architectural concept. However, there can be opportunities to simplify the operation of the system as a whole (and make it more robust) by designing one of those interacting processes to create common shareable resources that can be reused throughout system.
This time out we will consider a couple simple ways to implement this functional reusability without creating a brittle application.
Creating Reusable Resources
So the first thing we need to do is figure out exactly what we mean by “Reusable Resources”. In a sense we are creating a reusable resource every time we build a reusable library of drivers or functions, but that sort of reuse is not what I am talking about here. The distinction is that things like libraries are for really tools for the developer and users seldom even realize they exist. What I am talking about in this post are aspects of the program that are user facing.
For example, say you have an application that has 4 or 5 screens and they all need a couple buttons and a menu or two, but the buttons and menus on each screen do different things. One solution would be to simply duplicate the button and menu logic on each screen. but there are limitations to what you can do in this way. To begin with, forget the idea of having menus. Windows in subpanels technically aren’t open, so they don’t have menu bars and can’t have menus.
A far more flexible solution is to let the main GUI carry these reusable, or shared resources and let the subpanel VIs interact with them via references. Right now, that approach will be the focus of our attention.
One of the beautiful aspects of VI Server is that it allows you to do nearly anything with references that you can do directly. Therefore, our basic approach will be to pass references to the shared resources to the subpanel VI when it becomes active (i.e. visible in the subpanel) so it can register for, and respond to, the events associated with those resources. As a demonstration of how this works, we’ll add a button and a very simple menu example. The menu example will be simple because I am planning a complete discussion of menus for later time.
A New Requirement
The application to this point has provided a means for looking at data, or at least simulated data. It does not, however, have the ability to store any data. What we want to add is the ability to press a button, or make a menu selection, and have the data source we are looking at save the current chart contents to a text file. In addition, to raise the bar a bit higher, let’s also include in this requirement that the button and menu have to reconfigure themselves based on which data source is selected. In other words, if we are looking at the sine data source, the button and menu should say something like, “Save Sine Data”, or if viewing the ramp data, “Save Ramp Data”.
So what do we need to do to implement this functionality? To answer that question, let’s consider what we know. We know that a big reason for using subpanels in the first place is to eliminate data transfers. Therefore the data needs to be saved from the acquisition process itself — what would be the point of moving it somewhere else first?
Moreover, if each process is going to be handling its own data, doesn’t it make sense to let it handle the button clicks and menu selections too? The alternative is for the GUI to detect the button click or the menu selection and, in response, generate another event of some kind to let the acquisition processes know what happened. But there is no benefit to having the GUI acting as a middle man, mediating the communications. Better to go with the simple way.
And this is how you do it:
Dynamically Managing Events
If the easy way to handle this requirement is to let the acquisition processes handle the buttons and menus, we are going to need a way to turn events of all kinds on and off — not just timeouts. After all, there are three acquisition processes and we only want one of them at a time managing the shared resources.
To see how that works, let’s revisit for a moment how you register dynamic events. Here is a typical snippet of code (in fact, a subVI named
(re)create reuse events.vi) that is registering a couple events. The basic idea is pretty simply. You wire a reference of some kind to the node and LabVIEW will show you the specific events that you can associate with that reference. In this case, we are creating a value change event from a Boolean control reference, and a menu selection event derived from a VI reference.
By the way, if you plan on building this subVI from scratch, be aware that this situation is one of the few places where LabVIEW cares about the order in which you wire things. To duplicate my results, first create and name the input controls, then wire them to the
Register for Events node, finally create the output event refnum indicator, and then duplicate it to create the corresponding input. Once you have the subVI (or at least me implementation of it), this is what it looks like installed into one of the acquisition processes. It’s the one with the red banner across the top.
If you are observant you might be wondering how this is going to work because neither of the reference inputs that the subVI uses to create the registration, are connected to anything — which means that the references they are passing to the registration node aren’t valid. The thing is, when LabVIEW creates an event registration it doesn’t check for the references to be valid. Used in this way, the references only have two functions: Define the type of reference associated with the event, and name the event.
This fact is important because it is central to what we are trying to accomplish. As long as the references associated with the registration are invalid, the events are in essence turned off because you can’t fire an invalid event. To turn them on, all you have to do is modify the registration to use valid references. This snippet shows how I have modified the
Change Source event and its event handler.
First, I moved the
My Name control outside the event loop because I an going to be needing it several places. Second, I modified the event data to pass three references in addition to the name of the source being selected. Two of those references relate to the values we used to initialize the registration. The third reference is a menu reference that we will discuss in a bit. Third, I modified the event handler such that if
My Name matches the selector string from the event data, the registration is recreated using the two real references, thus allowing this VI to receive those two events. If
My Name does not match the selector string from the event data it executes an instance of the registration VIs that has no references wired to its inputs. Fourth, what in the world is that single frame sequence structure with the wires passing through it for?
In the finished code you will see a subVI sitting there, but I wanted to pass on a tip. Many times when working I will realize that I will need a subVI that I haven’t created yet. To allow me to continue work, I will drop down a single frame sequence structure and wire up the data inputs and outputs that I know it will need. Once that it done, I turn it into a subVI by selecting it and then choosing the
Create SubVI option from the window’s
Edit menu. As a reminder that I have to go back and finish it, I will leave the new subVI with the default icon.
We’ll get to what this mystery VI does in a bit. But it should be obvious that these changes are useless if there are no error handlers, so I added them in too. Here is what the handler for the button press looks like.
We first call our little library function to pop the button back out (since we obviously can’t have the terminal for the button here) and then calls a new subVI to save the data to a text file. This is what the data saving VI looks like on the inside:
Note the comment I put in the code. I will be wanting to come back and reimplement this data saving better so I put a bookmark in the code to remind me.
We are also going to need an event handler for the menu event as well, but before we can build that code, you need to know a bit more about how menus work in LabVIEW.
The 411 on Menus
As I stated before, I am going to devote an entire post to menus in the near future, so the following discusses a very basic implementation.
The first thing you need to know about menus is that to create and manipulate runtime menus you need a menu reference — no surprise there. Unfortunately, LabVIEW doesn’t treat menu references like it does references to other parts of a VI. For example, if you need a reference to VI’s front panel, you can get it from a reference to the VI. Not so with menu references. The only way to get a menu reference is from a special node that has to reside on the block diagram of the VI that will be showing the menu. This “quirk” is why we need to pass two different references to the subpanel VI that both deal with menus: the VI reference allows us to register an event for the menu, but we need the menu reference in order to make changes to the menu contents.
Next, you need to know that every menu item has a
Name and a
Tag. In many ways these properties are analogous to a control’s
Label. Like a control’s label, a menu item’s tag cannot be changed dynamically because it is the way that LabVIEW uniquely identifies that particular object. However, the menu item name is the visible string you see when you operate the menu and (like a control’s caption) can and often does change during the execution of an application.
Here is the code in the GUI that will define our very basic menu and its three items. Note that I have packaged this logic in a subVI so when I upgrade the menu generation, it will be easier to do. Remember that a good principle for when to make something a subVI is to ask yourself is this functionality (or the implementation of the functionality) likely to change?
Breaking down the logic, the first node deletes all the existing menus. The next node creates a new top level
File menu. Finally, the third node is in a loop and it creates a selection to save the data (tagged:
Save Data, a dividing line and a
Quit selection to stop the application. As I stated before, the tag for each item must be unique. To make meeting this requirement easy, I create hierarchical tags that are a colon delimited listing of the item’s parent tags. For example, the tag for the
Quit selection on the
File:Quit. This structure also makes the tag easy to parse in event handlers for the menu.
The Menu Event Handlers
Given the menu is itself a shared resource, the response to the menu events is also shared. Here is the menu event handler that is in the GUI. It handles the event that related to the application as a whole —
Quit. As you would expect, all it does is run the VI that verifies that the user wants to quit before firing the standard
Stop Application event.
Likewise, there is the menu handler that resides in the acquisition processes. It simply calls the same data saving VI we used in the button press event handler.
In both handlers, menu events for which they are not responsible, are handled by an (empty) default case.
Changing Resource Appearances
Remember the dummy VI we created earlier? Its job is to implement the requirement that the markings on the button and menu change to reflect the process that is managing them. Here is what it looks like:
My Name value is used to generate the new marking which is then applied to the button’s
Boolean Text property and a node that uses it to set the menu item’s name. The node uses the item tag
File:Save Data to identify the specific item it is changing. If you check out this node using the online help you’ll notice that it also let’s you enable and disable items, and add or remove check marks in front of items.
After making the required modifications to the other two data sources, we are ready to test the code. As you go from one data source to the next you’ll notice that the menu selection and button text change to reflect the name of the source that is being displayed. While you’re at it, test the data saving and see that the application remembers the last place that you saved a file.
This is all on subpanels for now. I have gotten some really good comments from several of you and will rolling some of those questions into future posts. In addition, when you download the code for a post, be sure to go through all of it. There are a lot of things going on in the code that I comment on in the VIs, but don’t have space for in the post.
Oh yes, a preview… In the next post I will start looking at another popular design pattern and what a proper LabVIEW implementation of it looks like.
Until next time…