This is the second of a two part post about the use of Malleable VIs in a practical project – rewriting a test system to use TestStand. In Part One, I introduced the project and the decision to use LabVIEW OO and Hardware Abstraction Layers to allow an easy switch between Simulated and Physical hardware in the test sequence.
In this part, I will discuss the implementation in more detail and how I am using the Class Adaptation feature of Malleable VIs. Class Adaptation allows me to create a malleable VI that can act on unrelated classes (i.e. no inheritance between them). I believe in Computer Science terms, this qualifies as some sort of Mixin.
The project uses Malleable VIs for better code re-use in Object-Oriented (OO) Hardware Abstraction Layers (HALs) at different implementation layers – the simulated instrument classes use Malleable VIs from a ‘Simulation Utility’ library and the GPIB instruments use Malleable VIs from a ‘GPIB Utility’ Library.
Malleable VI Class Adaptation
The basic principle of a Malleable VI (.vim), as per my introductory post on the subject, is that the data type of the terminals on the connector pane can adapt to the data type passed in to them. This also applies to LabVIEW Objects.
Class Adaptation Example
To demonstrate Class Adaption with Malleable VIs, I have created a simple example that demonstrates the principle of class adaptation.
Let’s say I have a bunch of unrelated classes (i.e. no inheritance between them) but I want to be able to display some data from them and log that data to a file – this could be state data, status information or configuration data. Could be useful, right?
Before Malleable VIs, you would need to implement a ‘Get String Data’ method in each class and then pass that to a separate VI to do something with the data (e.g. log it to file, display it). With Malleable VIs, you can do that within a single VI.
Get String Data.vim
Here is the Malleable VI for ‘Get String Data’:
All this VI does is take a Class wire in, call the ‘Get String Data.vi’ method of that class and return the string. Because this is a Malleable VI, the Class wire can be any LabVIEW Class and the code will compile/execute as long as the class has a ‘Get String Data.vi’ method with the same connector pane.
Putting the malleable VI into an example gives the following VI:
As you can see, my ‘Get String Data.vim’ and ‘Log String to File.vim’ Malleable VIs have adapted to the two classes that have been wired into them. Note that this is not dynamic dispatch, as the classes are unrelated and my Malleable VIs are not class method VIs.
The LabVIEW Project
If we take a look at the LabVIEW Project for this example, you can see how it fits together:
My Malleable VIs are contained within my String Data Utility Library – a collection of functions that can act on a class to display/log string data. The only requirement for my project classes to implement that functionality is that they contain the ‘Get String Data.vi’ method with the same connector pane.
From my (fairly limited) experience, I think it would be practice to define an interface class for your Class Adaptation Malleable VI – this defines the interfaces and methods used by the Malleable VIs and helps to avoid any external dependencies – your Malleable VI has a dependency on your interface class and doesn’t depend on an implementation/project-specific class.
Hopefully you can see how this is a very powerful feature and allows for creation of helpful utility VIs which can act on classes. In case you can’t, read on to see how I used them in the TestStand project introduced in Part One.
Piecing it Together – Practical Application
At the end of Part One, I outlined how I have a group of simulation classes and a group of GPIB instrument classes that share common functionality. Now, with the power of Malleable VIs, I can modify my Class UML diagram to include the Simulation Utility and GPIB Utility helper libraries:
As you can see, each layer of my hardware abstraction now calls upon an extra library for the common functionality. I have highlighted the functions in the libraries that are Malleable VIs. I only have one utility library per layer, but it is possible to add more later (e.g. TCP/IP, RS232 or logging/configuration utlities).
For the Simulation layer, the Enqueue Command VIM calls the ‘Get Message Q Ref’ method (remember – you can’t use property nodes in Malleables) to enqueue a message to the UI panel (a QMH) and wait for the response.
For the GPIB layer, the GPIB Write and GPIB Write + Read functions call the ‘Get GPIB Settings’ method of the GPIB classes to retrieve the GPIB configuration (e.g. address, read/write mode, default timeout). This makes implementing the GPIB protocol/functions for the instruments very simple – as shown in the LDC function example below.
I hadn’t expected this to be a two-part article but I felt that the first part was important to talk a little about Hardware Abstraction Layers, LabVIEW Object-Oriented programming explain a little background to the project I’m discussing.
I think the key takeaways from this are:
- If you’re not using Object-Oriented LabVIEW, I suggest starting with hardware abstraction as it’s one of the easiest ways to understand the basic concepts (‘cluster on a wire with functions’, inheritance, dynamic dispatch etc.) and offers lots of benefits (e.g. ability to simulate/switch hardware much more easily).
- Building Simulation interfaces into software and test systems is a good idea.
- The Class Adaptation of Malleable VIs allows the creation of reusable libraries which can act on unrelated classes. I have presented a couple of practical examples here from one of my current projects but each time I delve into them, I discover more cases where they might be useful.
As ever, let me know if you’ve found this useful and if you’re using Malleable VIs in your projects, it would be great to hear from you to see how you’re using them.