This post was originally published at Walking the Wires
If you are a regular visitor to this blog, you may have heard us mention the DQMH (Delacor Queued Message Handler) from time to time. The DQMH is a framework that we use with our customers, and it is available on the LabVIEW Tools Network.
In this post I’m going to be looking at a particular implementation pattern using the DQMH, although it is just as applicable to any other flavour of QMH, such as the templates that ship with LabVIEW.
We are going to implement a Hardware Abstraction Layer (HAL) in my DQMH module. We are also going to take a look at calling this DQMH module from TestStand, as we think this makes using TestStand a breeze, especially with LVOOP (LabVIEW Object Oriented Programming) abstraction layers.
If you are impatient and want to see what we will be looking at, or if you are time bound and need to get the facts quickly then take a look at the video below. It gives an overview and will hopefully encourage you to keep reading.

The hardware abstraction layer we will be using abstracts the behavior of the ubiquitous bench top DC power supply.
The steps involved are:

  1. Defining the DC Power Supply API methods
  2. Create Power Supply parent class
  3. Implement a descendant class for a specific Power Supply model
  4. Use Factory Pattern to load a specific class at runtime
  5. Create Power Supply DQMH module
  6. Implement DQMH events
  7. Call Power Supply DQMH Public API from TestStand


1. Define the DC Power Supply API methods
The first part of this task is to define the API methods for our DC Power Supply.  In other words, the things we are likely to want to do with it. Fortunately this is a fairly simple instrument so it has limited functionality, which will hopefully make a good demo for this blog post!
The things we are likely to want to do with our DC Power Supply are:

  • Set Current Limit
  • Set Voltage Level
  • Set Output State
  • Measure Current Draw
  • Reset Instrument

We are likely to want to do these things regardless of the model of power supply in use, although the commands to make them do it may vary considerably.

2. Create Power Supply parent class
We start by creating a parent class to define these methods. This looks something likes this:

Power Supply Parent Class

Power Supply Parent Class

Each of the method VIs in the image above is effectively empty, because we do not implement any instrument commands in this class (although we will put something there, but more on that later).
Even though these methods are empty, we use them in the calling code.  At run time, the magic of dynamic dispatch calls an override of each particular method, depending on the actual device selected.  The child class being called is determined dynamically either through a configuration file or a user selection control.
You can provide a nicer experience (and reduce the risk of bugs) for the developers in your team by setting the option (in the class properties) to require descendant classes to override the parent implementation. Setting this option results in a broken arrow if they don’t provide an override, which will quickly make them realise they missed creating the VI in the child class.

Require Descendant Class To Override Method

Require descendant class to override method.  To open this window, right-click on the parent class on the project and select Properties.

This is all that we need to do at edit time, but what happens if we just call the parent’s method at run time? Is this not just the same as calling a series of empty method VIs?
To make sure we capture this behaviour as an error, we can place an Error Ring inside a wrapper VI and call this VI in each method VI that should be overridden (and therefore never called). We can also define a custom error code and give it a custom error description.
With this method, we see an error if the parent is inadvertently called, as opposed to not having anything happen.

Error Ring

Error Ring with custom error to inform developer that a parent method was called by mistake.

The class private data will typically contain data relating to the instrument (such as VISA refnum) or configuration of the measurements to be performed using the instrument.
In this example, the class private data contains the VISA refnum and the class has accessors for reading and writing this data.

Class Private Data Accessors

Class Private Data Accessors


3. Implement a descendant class for a specific Power Supply model
The next step is to implement a descendant class for a specific model of Power Supply.  Let’s call this class “Agilent 3634A”, and change its inheritance properties to inherit from the abstract Power Supply class.

Inheritance

Inheritance.   To open this window, right-click on the parent class on the project and select Properties.

The next step is to create the overrides for this new class.

Create New Override

Create New Override.  To open this window, right click the new class and select New VI for Override.

Once we have created the overrides, the Agilent 3634A class should look something like this:

Overrides in Agilent 3634A Class

Overrides in Agilent 3634A Class

We can also add some virtual folders to organise our Agilent 3634A class in a similar fashion to its parent.

Organise as parent

Organise as parent


4. Use Factory Pattern to load a specific class at runtime
We can now write higher level code, making calls to the abstract Power Supply class and then dispatching the desired specific implementation at runtime.
The Factory Pattern is a means of loading a specific class at runtime by knowing the path to the class definition on disk.

Factory Pattern loads class from path on disk

Factory Pattern loads class from path on disk

The great thing about this pattern is that we can add additional child classes in the future without having to make changes to the calling code. In the test world, where new models of equipment become available every few years, this is a powerful feature.

5. Create Power Supply DQMH module
The next step is to create the Power Supply DQMH module. This will be an asynchronously-running module that can receive messages from other modules or from TestStand (which is our use case).
To add a new DQMH module, use the built-in scripting tools that ship with the DQMH Toolkit.  Simply click Tools > Delacor > DQMH > Add New DQMH Module.
https://youtu.be/-SXW6lYa6Hs
A test system may have more than one power supply, so we want this to be a ‘cloneable’ DQMH module.
Our project should look like this:

Added Power Supply to project

Project including DQMH module and Power Supply classes


6. Implement DQMH events
The final piece is to implement methods in the DQMH module that can be invoked via events.
As we covered earlier, the methods supported are:

  • Set Current Limit
  • Set Voltage Level
  • Set Output State
  • Measure Current Draw
  • Reset Instrument

In addition to these, one additional method defines the specific power supply being used.
To implement a message that sets the power supply type, we add the Power Supply object to the DQMH module’s private data.

Module private data persisted in Shift Register

Module private data persisted in Shift Register

Module Private Data

Module Private Data

We can now create a message to define the target power supply class.  We already have our Factory Pattern code that loads a class from a path on disk, so our “Set Power Supply Type” message should have a “path” type argument.
The following video shows just how easy it is to create a new message for a DQMH module;
https://youtu.be/xsF0QmGog-g
We can now call our code for loading a class from disk using the path sent as part of the above message. Next we can write the class to the private data of the module. From now on, we know the class of power supply being used and can use this to make calls to the other class methods.

Populate Private Data

Populate Private Data

To implement the “Set Current Limit Method” we first need to create a new event to send to our Power Supply DQMH module.  Since we need the module to set a parameter, we need to create a request type event. The current limit parameter is a numeric value, so we will pass a numeric argument along with our event.
Click Tools >  Delacor > DQMH > Add DQMH Event

Create the Set Current Limit Event

Create the Set Current Limit Event

We can now add a call to Power Supply.lvclass:Configure Current Limit.vi to the Set Current Limit case of the message handling loop in the power supply DQMH module.  At runtime, a child implementation of this method may be called, depending on the configured power supply class type.

Set Current Limit

Set Current Limit

We can implement the remaining power supply API methods in a similar fashion. This gives us a complete public API for our Power Supply module.
We will have the following request events (request events are things that the outside world can request a module to do):

  • Set Current Limit
  • Set Voltage Level
  • Set Output State
  • Reset Instrument

In addition, we will have at least one request and wait on reply event (this is a request event with a reply payload) for the following method:

  • Measure Current

We can now launch our module and fire request events on it from LabVIEW or TestStand:
From LabVIEW we can simply drop the Public API methods of the module onto a new block diagram in order to interact with it.

Non DQMH calling code

LabVIEW code calling Power Supply DQMH API


7. Call Power Supply DQMH Public API from TestStand
From TestStand, we make calls to the same Public API methods directly form our sequence file. No additional references or operations are required:

TestStand example

TestStand calling Power Supply DQMH Public API

Of course, now that we are using the module from TestStand, we have access to the great features it offers, such as User Management, Report Generation, Database Logging and much more.
The nice thing about this development pattern is that you can develop and debug your module entirely from LabVIEW before calling it from TestStand. You can also use the API testers that are included with each DQMH module as sniffers to check or set values and behaviours in the module event since it is also being controlled by TestStand in parallel.  We will go into more details in a future post (In the mean time, we can tell you that for the tester to work as a sniffer, you need to make sure to set the tester to run in the main LabVIEW application instance).
We think that this pattern is a great way for LabVIEW development teams to transition to TestStand.  It is also great way for TestStand developers to gain the benefits of dynamic dispatch, which is great for hardware abstraction without needing to deal with LabVIEW objects in TestStand.
If you are going to be running the TestStand example that is included in the example code with this blog post, please make sure to edit the Configuration.txt file to reflect the location of the power supply driver on your PC! You can get the example code as a ZIP file.
As always, we look forward to hearing your thoughts, and if you have a better way, we would love to hear about it, too.
Happy wiring folks!
Chris