Jump to content

Creating a hierarchy of instrument classes - anyone done this?


Recommended Posts

I work for a medium-sized company with 4 full-time plus a few occasional developers who use LabVIEW, me being one of the full-time developers.  The company isn't that old, and apart from me none of the developers have formal Computer Science training (i.e. they are engineers who transitioned to it gradually).

 

Right now we're getting to the point where we need to start being a lot more efficient in our re-use of code, as currently we pretty much assign one developer to one station, and then most of what they write isn't applicable to any other station.

 

So what I have proposed to them (but nothing officially done yet) is that we switch to a hierarchy of classes that would allow for re-use of effort, so instead of writing 10 different VIs for "Read voltage" for 10 different test stations using 3 different instrument vendors and then re-writing a bunch of code when we discontinue using one of the vendors, we'll use inherited classes to abstract it all out.

 

The hierarchy I am thinking of is basically:

 

Base Instrument (everything) -> Instrument type (i.e., multimeter, oscilloscope) -> Specific instrument (i.e. Tektronix DPO3054 oscilloscope)

 

OR

 

Base Instrument (everything) -> Instrument type (i.e., multimeter, oscilloscope) -> Manufacturer (i.e. Tektronix) -> Specific instrument (i.e. DPO3054 oscilloscope)

 

The idea is that we will have one "set up" VI that creates a reference to one specific instrument and model, creating a reference to the child class farthest down the line at the very start, cast it to the "Instrument Type" class, and then write a set of possible commands to the instrument type that are then overridden by the specific instrument class lower in the hierarchy.

 

The "manufacturer" class would be there for vendors like Tektronix that have many shared commands that work across many of their scopes in the exact same way, but some of the more expensive ones have additional features.

 

I've written a few sort of "proof of concept" VIs to show that it seems to work with simulated instruments but nothing beyond that.  We'd like to get started as soon as possible but I'd like to make sure I don't make any initial mistakes that are going to kind of hobble us from the beginning if I do them wrong... so I was wondering if anyone else had done things like this before and could offer advice, or if anyone in general has ideas based on what I'm trying to do as I haven't used OOP in LabVIEW much before.

 

Thanks!

Link to comment

The factory pattern you describe works very well for instruments. I would certainly recommend going down that path if you want to be flexible and avoid rewriting the same code over and over again. It allows you to easily swap instruments between test stations - then all you have to do is replace the configuration and the code does the rest (assuming all methods are supported).

 

The "manufacturer" parent class is probably only worth considering if you have many instrument types with many instruments of the same manufacturer. The problem you might run into here is that you can't encapsulate communication in the child class. You might have to write a separate communication class structure that offers different types (USB, GPIB, TCP/IP, RS232, ...) and then hand an object of that class around so you can do half the communication in the parent and the other half in the specific class without them actually knowing what type of communication they're using. That way, it doesn't matter if your Tektronix scope A is connected via USB and Tektronix scope B is connected via GPIB and they're both trying to send the same command.

 

We are using a very similar architecture with over 40 instrument types - without the "manufacturer" layer. Configuration is done via a single "file" containing a section for each instrument and some global stuff like paths that can be used inside the sections. Some of the sections' properties are handled by the base class (instrument type and model, address, calibration dates, etc) others by the instrument type layer and others by specific classes. The same goes for methods. Init, Reset, Destroy, etc are defined in the base and more specific methods like MeasureFrequency() could appear in the scope subclass. Unfortunately, I don't think our code is allowed to go public.

Link to comment

We do in fact use many different models of Tektronix scopes.  I know of at least 5 models in use at the moment.  The programming manuals for each are very similar and in many cases they share the same manual with the occasional caveat on some of the functions such as "(Model XXX only)".

 

Our code is also probably not going to go public either, I understand.  However, are there any generic things you can share that you discovered in your case?  Pitfalls, things you didn't think of at first but found that you needed later?

Link to comment

What you've shown is a pretty common structure and I've seen it before. I would comment that instead of manufacturer you might instead specify "family" -- if your devices use a common set of commands like SCPI* it might make sense to use that as a parent, rather than just the specific manufacturer.

(*which I don't really know anything about, but it sounds like it might fit in a "family" category as it was described here)

 

The other type of abstraction I've seen is a measurement hierarchy (ie voltage/current ->strain/accel/etc -> vibration/sound/etc). However I think this is usually built on top of a HAL so it may be an entirely different boat that isn't too useful to you.

 

Something else you might want to decide is if you want the abstraction to be called directly by other code or if you want the functionality to be in the form of actors (either actor framework or regular queued message handlers). A neat, but probably more complicated than what you need right now implementation was developed by Eli Kerry and is available here:

https://decibel.ni.com/content/docs/DOC-21441

I wouldn't necessarily recommend this for 1.0, but you did ask who else has done this and its a pretty neat design in my opinion. 

 

Finally, if you need some help convincing your colleagues (or teaching them), this looks like a decent webcast (also from Eli Kerry):

http://ekerry.wordpress.com/2014/06/10/introduction-to-object-oriented-programming-for-hal-design-in-labview/

My guess would be that it wouldn't benefit you personally (seems more basic than what you already seem to know) but again it might benefit some of the guys around you.

Link to comment

Also consider using Classes by reference for instruments.
Yes, I know that LabVIEW is a by value language, but also LabVIEW is a perfect langue to run this in parallel, and that can’t be done with instruments as by value objects, that’s why VISA, TCP/IP, DaqMx, Vision, are all by reference
;-)

Link to comment

I don't think we want to overlay this on top of the Actor framework.  I've looked into that in the past and there are a number of reasons why it probably isn't something we would benefit from, both because of the initial overhead in setting everything up plus the fact that my co-developers are just wrapping their heads around OOP to begin with.  

 

Queued message handlers may be used on a case-by-case basis.  I've used some hardware before that the only way to interact with it was with a .NET object dropped onto a front panel somewhere with a long series of setup commands before it is ready to run instructions, and that sort of device would definitely use one.  Something simple like a 1-channel power supply would never need to do that, which is a lot of what we use.  

 

For those instruments that wouldn't use a QMH but might be called from 2 different threads at the same time (I'm thinking of multi-channel devices of some kind) the classes by reference concept is an interesting one.  I'm not sure I like the 1-length-queue method the example uses, but some method of being able to not get query/response pairs in parallel threads mixed up seems like it could be handy to have as an option.

 

Something else I was wondering... we use Subversion with TortoiseSVN as a repository.  Has anyone found a "best" method to share home-grown instrument libraries across dev machines and deployment environments?  Perhaps one where each separate basic instrument type has it's own menu on the tool palette? 

Link to comment

I went the second route of heirarchy (base -> typeof instrument -> specific) but now rather wish I'd split it into one heirarchy that did the data transport and command formatting - so handling different terminators, SCPI vs non SCPI, instruments that reposnd by repeating the command back to you etc, and the measurement function heirarchy. Currently my base class is too full of things that are not fully generic (like commands to help prepare SCPI command strings).

That said, the core idea of writing instrument drivers as classes does work really well and makes it significantly faster to implement drivers for new instruments where the manufacturer has made an incremental change that adds extra functionality.

Link to comment

Something else I was wondering... we use Subversion with TortoiseSVN as a repository.  Has anyone found a "best" method to share home-grown instrument libraries across dev machines and deployment environments?  Perhaps one where each separate basic instrument type has it's own menu on the tool palette? 

 

Use two repositories. One that contains your project and one that contains the instrument drivers and other "shared" resources nicely arranged in a tree structure that makes sense to you.

 

I imagine that tool palette would become very crowded very soon.

Instead of having all your instruments and methods in the palette, why not use quick drop with this handy class select plugin.

That way you only have methods available of the instruments that you have added to your project.

Link to comment

Use two repositories. One that contains your project and one that contains the instrument drivers and other "shared" resources nicely arranged in a tree structure that makes sense to you.

 

I imagine that tool palette would become very crowded very soon.

Instead of having all your instruments and methods in the palette, why not use quick drop with this handy class select plugin.

That way you only have methods available of the instruments that you have added to your project.

Right now I'm thinking that we have a big directory tree that we put all of the instrument classes in, and put that in the instr.lib directory.  Devs would update it regularly; working stations would only upgrade it if something is changed and the version they had no longer works.  The root class plus each of the child classes would each have a .mnu file in their directories, so all of the VIs would be accessible using either deep-browsing the default palette, adding only the relevant .mnu files to a customized palette, or using whichever form of quick drop they prefer.

 

We already kind of do this for the VIs we use for interacting with the database that we use, but in the user.lib directory instead, and no major issues with that so far.

 

Individual projects would be in their own project directory and repository, somewhere outside of the National Instruments directory structure.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.