Jump to content

Configuring Many Instruments


Recommended Posts

I'm sure most people working on large applications have developed several methods for controlling many subVI's, and probably someone has had a similar application to mine. It seems there are several ways to do this, but before starting to implement it I'd like to know which option(s) other users think would be best.

I am acquiring data from a subset of the various instruments in our lab. For example, I'll often measure the I-V curve of a device, which involves using a current source and a nanovoltmeter. What makes this a challenge is that in our lab we have several different models of current sources, nanovoltemeters, and even some units that do both functions. Which instrument(s) I use depends on the type of sample I'll measure, as well as which instruments are currently free.

Originally I had a whole slew of VI's written for the various combinations of equipment, but this is from before I learned the ways to dynamically control VI's (of which I'm still a novice, BTW). Similarly, these VI's have been used as subVI's in larger data acquisition programs, which I would re-wire each time to use the right equipment. Now I want to be able to select which current source and voltmeter I'll use from the master program, and have it dynamically choose the proper subroutines.

To make things even more confusing, there are a variety of parameters to be adjusted on the equipment, such as analog filtering, digital filtering, range, triggering, buffering, etc. This has proven to be too many controls to put on the front panel, even for the subVI's to take the I-V data. And it varies too, for example some units don't have filters, some only have analog filters, etc.

What I've considered was to dynamically add menu items on the main control panel, depending upon which instrumentation I would be using for that run. Each menu item would correspond to the exact piece of equipment I was using. I was then hoping to either add sub-menus for the various options that can be adjusted on that particular piece of equipment, or to have the menu item create a popup which would control

the settings on that particular piece of equipment. I envision having some cluster on the main VI denote which instruments are being used, and correspondingly each instrument would have it's own specific cluster describing all of its settings.

So - has anybody done anything like this before and can either suggest this way or a better way to control the instruments? I'm also not too sure how to capture the menu item events. I was thinking of using an event structure on the main VI to call a dedicated menu item processor VI, and I would register with that processor VI which subVI's to call depending on which piece of equipment is being used, etc. As far as I know this should all be possible, but I don't have much experience with this.

Would using the VI server be a way to go? And/or would it be advisable to use globals to access things, or should I avoid them? Namely, when I change which equipment I'm using and the settings of that equipment, what is the best way to allow the other subVI's to access that information (presumably through the clusters)?

Link to comment
I'm sure most people working on large applications have developed several methods for controlling many subVI's, and probably someone has had a similar application to mine.  It seems there are several ways to do this, but before starting to implement it I'd like to know which option(s) other users think would be best.

Originally I had a whole slew of VI's written for the various combinations of equipment, but this is from before I learned the ways to dynamically control VI's (of which I'm still a novice, BTW).  Similarly, these VI's have been used as subVI's in larger data acquisition programs, which I would re-wire each time to use the right equipment.  Now I want to be able to select which current source and voltmeter I'll use from the master program, and have it dynamically choose the proper subroutines.

3210[/snapback]

Sounds like a familiar scenario.

I use an approach with plugins (instrument driver VIs loaded dynamically at run time). Each instrument is represented by ONE (driver) VI. Everything that the main.vi needs to access from this instrument has to go through this driver VI. The connector pane of the driver VI is always the same, for all instruments. One of the parameters to driver VI is a string, labeled 'ACTION'. The values of ACTION at run-time ar: INIT, CONFIG, TRIGGER, MEAS, SET, RESET. Within the VI there is a case structure with the corresponding action cases.

For each instrument in our institute there is (or hopefully will be at some day) ONE such driver VI. In the main.vi a user can select from the available drivers the subset he needs. (Of course such a selection be stored in a config file for later retrieval). The selected drivers are loaded at runtime as plugins (as is e.g demostrated in \examples\viserver\plugins.llb).

To make things even more confusing, there are a variety of parameters to be adjusted on the equipment, such as analog filtering, digital filtering, range, triggering, buffering, etc.  This has proven to be too many controls to put on the front panel, even for the subVI's to take the I-V data.  And it varies too, for example some units don't have filters, some only have analog filters, etc.

3210[/snapback]

The available parameters of a given instrument are stored in what I call 'informative controls' on the driver VI front panel (using the 'Make current values default' feature). The informative controls are not used on the connector pane or at any place on the block diagram. They are just read by the VI from the main hierarchy which loads the driver VI. That way the main knows everything about the intrument you want it to know. The momentary settings of an instrument can be changed with a pop-up dialog in the main hierarchy. Again, they are stored in a file to keep them between runs of main.vi

Would using the VI server be a way to go?  And/or would it be advisable to use globals to access things, or should I avoid them?  Namely, when I change which equipment I'm using and the settings of that equipment, what is the best way to allow the other subVI's to access that information (presumably through the clusters)?

3210[/snapback]

I avoid globals as much as possible, only use them to make some general config information available throughout the main hierarchy.

The plugin mechanism I use is wrapped into an object oriented framework that I developed with the old (freely available) version of the GOOP toolkit. This GOOP approach makes it possible to encapsulate the config information of a loaded driver plugin with the driver code itself. The GOOP appraoch allows for another nice feature: The destructor method of a driver VI object contains code which stores all the info in a file when the main.vi destroys all dynamically created objects before it terminates. The next time when main.vi is run it recreates all the previously used objects with the same status they had before, since the constructor method of the driver VI object recreates the object using the stored information. (One of the private data of the driver object is of course a reference to the actual instrument driver VI).

In an earlier approach I also used to store the information collected during a measurement (the "measurement data") within the instrument object data space. That turned out to become slower and slower when the data size was large (> 10000 data per scan). I decided to use what is called a LV 2 style global (basically this is an 'intelligent' global which uses a VI with a shift register just for data storage). I actually implemented a storage heap mechanism and the instrument objects now only need to store a pointer to their data on the heap, not the data itself. That was fairly efficient.

I know this may all sound rather complicated, but it works really well. We use this program now at many different places and there is no problem with quickly plugging in a new instrument if the driver VI for it is available. If it is not and needs to be written, it takes a bit longer, and of course there needs to be somebody who can write it. But that is not awfully difficult either (depends of course on the instrument's complexity).

I attach a hardcopy of the main.vi, so you get an impression how it looks:

post-833-1104168165.png?width=400

post-181-1151970322.gif?width=400

Link to comment
Sounds like a familiar scenario.

I use an approach with plugins (instrument driver VIs loaded dynamically at run time). Each instrument is represented by ONE (driver) VI. Everything that the main.vi needs to access from this instrument has to go through this driver VI. The connector pane of the driver VI is always the same, for all instruments.

That sounds pretty cool, I didn't really think to make a common driver plugin like that. That makes sense, especially in light of the SCPI syntax for many new GPIB devices. So for the CONFIG action, the main vi calls the driver plugin's specific popup routine?

I was also thinking of keeping the state of the instrument, or perhaps different files for saving various commonly-used states. I also want a 'log' state where the relevent device operating parameters would be sent to a datafile associated with that particular data set from the main, such that 5 months down the line I can see exactly how the instruments were configured that day. Maybe even set it up such that the instrument states can all be fully recalled to duplicate specific datasets.

The available parameters of a given instrument are stored in what I call 'informative controls' on the driver VI front panel (using the 'Make current values default' feature). The informative controls are not used on the connector pane or at any place on the block diagram.
So does the driver popup come over the main vi? And how do you capture the events to send the config command to the plugin, from an event loop or similar in main? I assume you build the menus dynamically. I'm pretty new to these topics, so I'm not sure how to best implement them in a large setting. Namely having subVI's stop or pause in the middle of their operation, from a control on the main VI. For example, due to changing a parameter or quitting the main vi.
The GOOP appraoch allows for another nice feature: The destructor method of a driver VI object contains code which stores all the info in a file when the main.vi destroys all dynamically created objects before it terminates.
Yeah, that does sound pretty useful. Would it be that much harder to implement without GOOP, just using initializing and shut-down routines? I don't know GOOP yet (I do know OOP through Java), but between dynamic VI calls, VI server, event loops, using HDF files, and maybe Lua scripting through LabVIEW, I feel like I'm drowning in LabVIEW overload before getting involved with GOOP. So I'll try to go OOP-less at first.
I decided to use what is called a LV 2 style global (basically this is an 'intelligent' global which uses a VI with a shift register just for data storage). I actually implemented a storage heap mechanism and the instrument objects now only need to store a pointer to their data on the heap, not the data itself. That was fairly efficient.

That sounds useful, I've read about those LV 2 globals and at first they just seemed bizarre to me, almost counter-intuitive. But I guess it works pretty well. So the way you implement that, then, is to have all your data stored through the uninitialized shift register, indexed in some manner, and when you want to extract it, you send a pointer to the proper index of the LV2 global to whichever subVI wants that data? That seems pretty sensible to me.

Is there anything special you do for scripting the batch commands in that window? Ie, if you want to sweep magnetic field and record data, or another time you want to sweep pressure (if you can) and at each pressure sweep the magnetic field, can your program dynamically accomodate these two different data-taking methods? I want to implement things dynamically like that, and it gets really annoying to re-wire all the time. So I've been looking at LuaVIEW lately, which lets one call LabVIEW VI's from within a Lua script (Lua is a very elegant and small scripting language). What makes it great is that you can call the Lua script from LabVIEW, so you can have a text window on the main VI with a user-controlled script describing the batch operations, for example.

Thanks for the response, it was pretty useful.

Link to comment
That makes sense, especially in light of the SCPI syntax for many new GPIB devices.  So for the CONFIG action, the main vi calls the driver plugin's specific popup routine?

Actually, the driver VIs are meant to cover ANY type of instrument, not just GPIB. I have driver VIs for serial, USB instruments and some which just simulate an instrument (so I can do some testing of the main.vi without having any instrument connected. Look at the HTML Doc of a typical driver VI. It is a driver for a Agilent HF source. There is only code in the action case 'SET'. Everything that I want the driver to be capable of I have to program into the various cases. Here I just put code in the SET case. The good thing with my approach is that one can start with such a minimum of coding (and later add more if needed).

The configuraton you mentioned is done from the main panel. The attached image shows the main panel when the user clicks on the button labeled 'Current I/nA': a pop-up dialog occurs and one can configure the instrument.

post-833-1104241413.png?width=400

(Actually the objects are what I call a 'Signal': a signal consists of an instrument, transformation rule (maybe you read e.g. a resistance, but its physical meaning is a temperature, so one needs a transformation), and some other onfi like name, unit etc.)

In the displayed example there is not really a current meter shown, but a simulated instrument called 'Random'. It has 2 parameters, 'offset' and 'scale', but this could be anything for a real instrument, like e.g. sensitivity, filter, cut-off freq, time-constant, ....

If YES is activated in the last column of the dialog table, the corresponding entry is saved in the metadata of the measurement file.

I was also thinking of keeping the state of the instrument, or perhaps different files for saving various commonly-used states.  I also want a 'log' state where the relevent device operating parameters would be sent to a datafile associated with that particular data set from the main, such that 5 months down the line I can see exactly how the instruments were configured that day.  Maybe even set it up such that the instrument states can all be fully recalled to duplicate specific datasets.

That would be cool, my program does not yet allow this. Just as I said above, I can have those instrument settings which I consider as relevant stored in the measurement file's metadata.
So does the driver popup come over the main vi?  And how do you capture the events to send the config command to the plugin, from an event loop or similar in main?  I assume you build the menus dynamically.  I'm pretty new to these topics, so I'm not sure how to best implement them in a large setting.  Namely having subVI's stop or pause in the middle of their operation, from a control on the main VI.    For example, due to changing a parameter or quitting the main vi.

OK, this place is too short to describe in detail what my program does (It's size is ~ 280 VIs). See the link at the end of this post.

I use menus only for configuration things (saving the status, assembling a new set of signals etc.). Things which are done seldom.

Most of the user interaction is with the list of signals in the lower part of the main panel.

The main.vi is always in one of three states:

1) running idle: user selects which measurement he wants to do next

2) user has opened a configuration pop-up dialog

3) a measurement is running: controls (except STOP button) are disabled, ongoing measurement is displayed in the graph panel. Updating of the graph is done by a separately running VI which peeks on the heap and displays (at low rep rate) what is new.

Would it be that much harder to implement without GOOP, just using initializing and shut-down routines? 

I should definitely be possible without GOOP, but for me it was of great help to use such a prestructured framework (..I am a pretty chaotic programmer. No fishing for compliments here, just plain truth)
Is there anything special you do for scripting the batch commands in that window?

The batch capability is based on a minimal scripting language invented ad-hoc. It basically allows to make most user selections on the front panel also programatically (via the batch kob). Technically it was not too difficult to implement since my main.vi consists of an queue driven state machine. It does not matter if the queued actions come directly from the user or from a batch processor (which is just another state in the state machine). The batch language has only 8 different commands, but even they are difficult to remember, so I added a 'batchmaker' which programmatically creates a batch job for the most useful cases: e.g. measure I-V curves at 300 different gate voltages, repeat this at 20 different vales of B-field and so on..

Here you find the complete doc of Modulab

One of the things that I might change in the future is the format of the stored data. I use a custom format which was defined a few years ago with some ideas (about connectivity to a selfmade database) which are now obsolete. I'm therefore very much interested in the thread on HDF format which you started in one of the forums.

Link to comment

Hi, i'm working in a framework for instruments control.

You use the loader with vi server for calling the driver of instruments.

I use this in advance but when i build the executable, the complex vi called by the loader, don't load correct (more errors appeare)...

The secon way now i build a database from setting-storage results.

The driver vi read a propery value from database, for example:

Rs232 take from database value of baud rade,the communication port ecc.

I think it's a hard to create but if the work is good is easy to plug a new instruments on the future (or edit an existent driver).....

Link to comment
You use the loader with vi server for calling the driver of instruments.

I use this in advance but when i build the executable, the complex vi called by the loader, don't load correct (more errors appeare)...

I do not experience any problems when I load the dynamic VIs into a built application (.exe file). The instrument drivers are not included in the .exe but are located, as regular LV VIs, in the filesystem. Important is that their location is made known to the .exe at runtime (and of course that they are in a runnable state). I use config files (.ini files) for this. Driver VIs which the user wants to make available to the bulit app are simply added to this config VI (which you can consider as a sort of primitive database) with a text editor.

There is one slight drawback to this approach: when a new driver is written it usually needs some debugging to optimize it. In the LV Runtime environment (RTE) this is not possible.

BUT you can also run the compiled main app in the RTE. Just rename main.exe to main.llb and open the llb's top level main.vi in the LV development environment. Then you can set breakpoints, probes etc. in the driver.vi (and its subVIs) which you developped. (The hierarchy of main.vi is of course still inaccessible to debugging since its BD was remved during the application build.)

Link to comment

:thumbup:

Well, in this days i've try to perform a database system, but the appplication don't work like plug in.

I return in the loader system and slave vi's for the driver....

In effect i can use a vi panel to edit the ini file.

But i have another question, when i build the executable i attach the dinamics vi in my application, the vi's wich have a dll or other parts joined don't work in the target PC's.

I think to lock all subvi's and dll joined, to group in .llb, but how i call the top level vi from a llb?

Can you give me a suggestion for this?

Very thanks for your feedback :worship:

Have a happy new year day

Nicola

Link to comment

All of the functions above are part of one version of what is called a Test Executive (TE). Creating configurations, calling drivers, saving data, saving configs and scripts to be reused later.

There are two main models of TE:

1. The TE calls "Modules" or discrete Tests, each of which has a PASS/FAIL and may internally call one or dozens of drivers/instruments. This is the model favored by TestStand and the majority of TEs that have been built for the commercial market.

2. The TE calls individual drivers/instruments, returns the data which maybe logged, or evaluated by analysis plugins. This is the model favored by the Open Source Test Executive (OSTE) project some of us are working on.

Of course, most TEs can be made to work with either model above, and only favor one type or the other. On the production floor, version 1 seems to predominate, but in the laboratory, during design, development and later debug, version 2 predominates due to the greater need for interactivity with the test and more flexible control of the instruments available.

Link to comment
Actually, the driver VIs are meant to cover ANY type of instrument, not just GPIB..<SNIP>.. The good thing with my approach is that one can start with such a minimum of coding (and later add more if needed).

The configuraton you mentioned is done from the main panel.

I've started working on my Test Executive as Michael called it, and I'm following a model similar to yours. Instead of using GOOP (I probably will want to use OpenGOOP in the future, but its documentation is too confusing for me now), I add two extra routines for the drivers - Construct and Destruct, to handle constructor and destructor functions. The instrument drivers open the driver VI by reference, and pass a function enum, as well as a main cluster, which contain refs to all the relevent information of the system. All items are strictly-typed (LabVIEW docs say I cannot pass strictly-typedef'd controls to a VI by reference, but it worked in 7.1).

Regarding typedefs, my overall cluster is getting pretty complicated, and each time I put it on the front panel it takes up a huge space. For now I've gotten around this by hiding it on the front panel, but I'd like to make a "normal-sized" reference-like picture for it on the front panel, but I can't find any decent documentation on editing control appearances. Does anybody have a decent link to this?

Another question regarding sending information. You mention three states, such as running idle, opening popus, etc. Do you run the risk of race conditions as you update information on the whole system state, or do you have specific read/write routines to access the overall system parameters?

As I said previously, I have a strict typedef cluster, which contains a link to the main VI reference, the main VI's menubar (I searched through all the invoke and property methods I could find, and couldn't figure out how to get a ref to another VI's menubar), and so far a cluster with all the menu/driver/instrument information. I'm in the early stages, so I don't have actual data or files yet, but they will be added soon (data will be through indexing to a LV2 global). What I've been doing now is passing this overall cluster into and out of each function I call, kind of like error in/out. DOes this seem like a stupid thing to do? The vi's can get kind of annoying dealing with this, but I think it can help prevent race conditions, or maybe I'll need to either add non-reentrant subVI's to read/write from the cluster and/or use semaphores.

My main isn't queue-driven, I basically have an event structure that captures all relevent front-panel events and process them. As I'm only in the starting stages, I don't know if this approach will be too limiting later on. Do you suggest using queued state machines? Or does the event structure help make it easier to work without those? IIRC, the event structure has queueing built into it? Of course the benefit of your approach is batching looks the same as user interactions. And I'm also not sure how the event structure will handle routines that will take a long time to run, Ie, if the main VI can respond to other events or not.

I'm therefore very much interested in the thread on HDF format which you started in one of the forums.

I've made further progress on using HDF. The basic synopsis so far - it's a real pain in the arse to use so far, NI provides a few of the most-common lowest-level calls, and made a few slightly higher-level calls. NI also provided high-level methods for saving LabVIEW waveforms, which might be quite user friendly, but I don't have the need for them now. There are so many low-level calls of the HDF5 library, though, it can be pretty daunting. Two annoying things to deal with are the continuous usage of 64-bit integers (which they provide some VI's to convert either 32-or-less ints, floats, doubles, or 2 32-bit ints into), as well as the large number of typedef's in the HDF5 header file (which they provide another DLL to get a few of these values back). So while creating and writing data into simple HDF5 file can be done with only a handful of lines of C code, the corresponding LabVIEW Vi gets pretty complicated pretty quickly, with all the repeated calls to the 64-bit integer and typedef libraries.

I've thought the NI approach isn't the easiest, and pretty soon I'll start making my own set of libraries built on top of the NI-provided libraries. I'd consider re-doing the whole thing, but dealing with re-creating the DLL's is too time-consuming for me now.

Link to comment
Regarding typedefs, my overall cluster is getting pretty complicated, and each time I put it on the front panel it takes up a huge space. 

(data will be through indexing to a LV2 global).  What I've been doing now is passing this overall cluster into and out of each function I call, kind of like error in/out. 

You might want to look at passing variants or arrays of variants. These are some nice tools in the OpenG Toolkit for working with them and converting between clusters, etc.

My main isn't queue-driven, I basically have an event structure that captures all relevent front-panel events and process them.  As I'm only in the starting stages, I don't know if this approach will be too limiting later on.  Do you suggest using queued state machines ([QSM])?  Or does the event structure help make it easier to work without those?

3286[/snapback]

You might want to look at using the event structure to drive a QSM in another loop. This is becoming pretty common nowadays. To make it slightly more flexible don't pass just strings in the queue, but a cluster of the command string and an array of variants that serves as sort of a polymorphic parameter list to the command string. This all takes a little more work, but pays off in a very flexible and extensible architecture.

Link to comment
In your example, Michael, how come the RegEx used to strip contiguoes EOL chars is "[\r\n\r]+" ?  It seems like the last "\r" is redundantly redundant.

3302[/snapback]

Hmm, I just did a test and it works without it. I can't recall the reason for doing that. It was copied from a very old early version of LV code. Also, I had less experience at the time. I just never went back to reflect on it. In any case you're right, [\r\n]+ works fine.

Link to comment
I just replied, but it didn't seem to register so hopefully this isn't a duplicate.

Is there a reason most of the queued state machine examples presented here use strings to refer to the state machine case, instead of typedef'd enums?  It would seem a typedef'd enum would be easier to manage, and couldn't result in an invalid case.

3305[/snapback]

i prefer typdefed enums, simply to eliminate the possibility of mis-spelling ;-). My experience is that the "most used way" is not allways the best way (what ever best way does mean ...)

but it does not really make a difference, you can use numbers if you want. i think it's just a "oppinion" and a kind of approach what is "perfect code"

best regards, cb

P.S: where's the limit for a "large application" from your point of view ? 200 (self-written) VIs ? 500 VIs ?

Link to comment
...The instrument drivers open the driver VI by reference, and pass a function enum, as well as a main cluster, which contain refs to all the relevent information of the system....

... my overall cluster is getting pretty complicated, and each time I put it on the front panel it takes up a huge space...

One advantage of the encapsulation provided by an OOP approach is that you don't need to pass ALL RELEVANT INFORMATION to the objects. Each objects 'knows' what it needs to know. Of course you need to pass parameters when calling a 'method' of an object (the 'method' is one of the VIs which comprise the functionality of a class). But these parameters should contain just the information the 'method' needs to know in order to perform its task.

Do you run the risk of race conditions as you update information on the whole system state, or do you have specific read/write routines to access the overall system parameters?

I use specific read/write routines to access the system parameters. When a measurement is running, the main panel is not responsive to user input and system parameters cannot change. (Exception: the user can stop a running measurement or batch job. The status of the main (Idle/Measuring) is indicated in the main's titlebar
My main isn't queue-driven, I basically have an event structure that captures all relevent front-panel events and process them.  As I'm only in the starting stages, I don't know if this approach will be too limiting later on.  Do you suggest using queued state machines?  Or does the event structure help make it easier to work without those?  IIRC, the event structure has queueing built into it?  Of course the benefit of your approach is batching looks the same as user interactions. 

I use a simple QSM since the program dates back to LV 4 or 5. I didn't find the time (nor the motivation) to change it to the event machine driven consumer-producer design pattern mentioned by Michael Ashe and Michael Aivaliotis. For new projects I prefer (and recommend) their approach.

I've made further progress on using HDF.  The basic synopsis so far - it's a real pain in the arse to use so far...

3286[/snapback]

Thanks for that info, I guess I'll wait with the HDF stuff until someone makes it work less painful...

Link to comment
i prefer typdefed enums, simply to eliminate the possibility of mis-spelling ;-). My experience is that the "most used way" is not allways the best way (what ever best way does mean ...)

but it does not really make a difference, you can use numbers if you want. i think it's just a "oppinion" and a kind of approach what is "perfect code"

3307[/snapback]

There are indeed many ways to label the states of a state-machine. But the string or enum approach make the code much more readable than just numbering the different states.

I prefer the strings over the enums for two reasons:

- it is (a bit) easier to add a new state when you use strings (the danger of mis-spelling is reduced if there is a default case in the state machine which generates a run-time error like e.g. 'invalid state')

- when a menu event occurs, I just pass the menu tag string into the queue which drives the state machine loop. No need to map the menu tag to an enum. This can be done in the simple QSM (queue driven state machine) or in the ESM (event driven state-machine, producer-consumer pattern).

Link to comment
Michael, I'm not exactly sure what you are talking about here.  Do you mean having the event structure push a message onto the QSM?

3288[/snapback]

Hi Jeff, I was a little rushed before, sorry, and didn't have time to do a better example jpg, etc. Here is a simplified version of the 2 loop QSM using a Command cluster as the queue data type;

post-45-1104609137.jpg?width=400

The cluster that serves as the queue data looks like

post-45-1104609225.jpg?width=400

I hope this shows up in the post okay, if not I will edit.

This method of using two loops, one for UI and the other for doing the work is one I have been using in various forms since the early 1990s. Fortunately new feature in LabVIEW make this easier. Initially I used a LV2 USR type global as the "queue" (since we didn't have queues then) and we used the flatten to string functions to make an array of clusters of data names and flattened data. This is so much easier and flexible now using variants. No one was happier than me when Jim Kring, et al, did all the variant tools in the OpenG Toolkit. We don't have polymorphism yet, but variants take us pretty close from a practical standpoint.

If you download and install the OpenG Commander off of SourceForge OpenG Commander you will find several of these items or very similar ones inside. The Open Source Test Executive Editor (and other components) will also be based on this type of architecture.

Link to comment

Jeff,

Since, since you're looking into a flexible instrumentation control architecture, and are considering Lua for scripting, let me point out LuaVIEW modules. They work well for wrapping instrumentation drivers such that the corresponding instruments can be configured, managed, and operated from Lua. The implementation is simply a LabVIEW VI with a bunch of cases that correspond to the various instrument functions to be exposed.

The functions of the instrument that must be callable from test or control scripts can be exported. Management of the instrument (configuration, initialization, periodic actions, cleanup) can be done from the script that opens the module: all other functions of the module are available to it.

Modules are dynamically loaded and, with a little care, can even be reinitialized at runtime without breaking ongoing calls to export functions. When you have multiple instruments of the same type, use a template VI so that you can open multiple module instances. On opening, a configuration table can be passed that maps to some arbitrary LabVIEW cluster. To track the configuration, have the script that opens and configures the module log itself. A task script can retrieve itself using the task.script() function.

Albert-Jan

Link to comment

Everybody, thanks for all your help so far, I've really learned alot and have made some good progress in my test executive suite (although it's only still only the framework so far).

Here are some questions as to the several responses :

To ahlers01 : The OOP style seems like it would be pretty good, and maybe I'm limiting myself by not using it this early in the game, but I've already re-designed the basic framework several times, and actually need to get onwards with making it actually able to take data. I am using a driver-like model similar to yours, and I will also have separate types of drivers. Actually, as I'm writing this now I realize that a GOOP framework w/ inheritance would really work well here. CurrentSource and VoltMeter would both inherent from Device, for example. Maybe I should re-design again? I will take a good look at OpenGOOP later tonite.

To Michael_Aivaliotis : In your producer/consumer model, is there a reason you send the queue through the producer loop in a shift register? I'm under the impression that the queue parameter just references the queue, and all queue operations seem to return it unchanged. (The only reason I can think of is to provide a nice-looking way to send the queue reference to the queue release function).

To i2dx : I like using enums when wiring the states, that way I pick one exact state from the list. Questions on enums : are they guaranteed to run from zero to a max number with no missed states, and is there a way to find out the state items of a strictly typedef'd enum? I've been able to do this by making an enum control, then using the property node for get_strings, but I'd prefer to do it without needing that control on the vi.

To Michael Ashe : I implemented the producer/consumer style setup, and used the array of variants as per your suggestion. At first I thought it was overkill, but I quickly found good uses for the variant data. Question about OpenGOOP - does it have something similar to interfaces as Java has? I mention above about having the various specific devices 'extend' functionality of a device class. But some devices do many functions (some source meters act as current sources, voltage sources, current meters, voltage meters, etc). So implementing as an interface would be the best way to deal with this.

To Albert-Jan Brouwer : I haven't played too much w/ Lua since talking to you about the two-button dialog example. I definitely intend to implement LuaVIEW scripting for dynamic control of system execution and acquisition, to allow for maximum flexibility. I will take a closer look at the lua modules, at this point I was planning to implement the drivers and handlers in LabVIEW, do you really think it would be easier to manage the instruments in Lua instead of LabVIEW? Ie, one of the main strengths of LabVIEW is the availability of drivers and ease of controlling instruments. In your suggestion, would you do the bookkeeping in lua, and then call the labview driver from lua?

Link to comment
To Michael_Aivaliotis : In your producer/consumer model, is there a reason you send the queue through the producer loop in a shift register?  I'm under the impression that the queue parameter just references the queue, and all queue operations seem to return it unchanged.  (The only reason I can think of is to provide a nice-looking way to send the queue reference to the queue release function).

3328[/snapback]

Yes, you are correct. No need to pass it thru a shift register.

Link to comment
I will take a closer look at the modules, at this point I was planning to implement the drivers and handlers in LabVIEW, do you really think it would be easier to manage the instruments in Lua instead of LabVIEW?  Ie, one of the main strengths of LabVIEW is the availability of drivers and ease of controlling instruments. In your suggestion, would you do the bookkeeping in lua, and then call the labview driver from lua?

3328[/snapback]

Yes, no. Though LabVIEW is (as yet) unbeaten at talking to instrumentation, there are a number of issues surrounding the management of instruments that can benefit from scripting. In brief, the suggestion is to do low-level operations from a LabVIEW-implemented module VI, and high-level actions from scripts. Standard scripting strategy, really. So what issues are these?

1. Test sequencing

Typically, during a test or experiment, you want to step the physical parameters through a series of states. The dataflow nature of LabVIEW necessitates extra work to sequence steps and control their execution. Instead, test sequencing is a good fit for scripts since script execution is step-by-step and is provided with control over execution (pause, resume, stop). Modules allow you to expose LabVIEW-implemented instrument actions to test-sequence scripts.

2. Synchronous operations

When performing a step in a test sequence, the corresponding instrumentation operation often has to be known to have completed before the next step can commence. A synchronous interface as provided by callable module functions makes this easy and explicit. It is difficult to create a modular instrumentation architecture with LabVIEW that exposes a synchronous interface for test sequencing. For this reason, LabVIEW-based instrumentation architectures typically rely on message queues, which are asynchronous. An asynchronous architecture has a potential performance advantage but is much harder to reason about, is difficult to debug, and is not conductive to error handling.

3. Error handling

Talking to instrumentation is error prone. The exception mechanism of Lua allows you to disentangle the error handling. Errors messages include line numbers, so it's easy to figure out at what step of a test sequence specified via a script things went wrong.

4. Modularity

With instrumentation there is particular need for software modularity. When you change or add instruments, you don't want to rearchitect your application. When connecting multiple instruments of the same type, you want to instantiate from a single implementation. When using different instruments with similar functionality, you want to isolate them behind an abstract interface so that you do not need to change your test scripts. It is useful to be able to dynamically load and reload the instrumentation software. Modules make all that easy.

5. Configuration

Doing the configuration from a script saves the bother of creating a graphical user interface: a text editor can be used instead. Most instrument configuration settings change infrequently if at all. For those, the time invested in creating a graphical configuration interface cannot be recouped. Of course, anything that needs very frequent adjustment should be given a LabVIEW GUI.

Since configuration scripts are little programs, they allow for conditional configuration. E.g. configure the system differently depending on whether it is running in test or operational mode, or as a built application instead of under the development system. Batch operations are easy too, e.g. iterate over a table to a configure multiple channels. Also, with configuration scripts there is no ambiguity as to when the changes are effected: this happens when the script is next run. And scripts are pretty compact so you can log them for future reference.

In summary, the point of using a module is to wrap the LabVIEW instrumentation in a manageable format that exposes high-level functionality to the Lua side for scripting. Note that this means that the finicky details of handling the instrument should remain hidden and be dealt with solely on the LabVIEW side. Also, it may be sensible to have one module wrap multiple instruments when the operations of those instruments are, from a high-level point of view, inextricably linked. For example, if you have a digital multimeter fronted by a switch, a DMM reading will always be preceded by switching to the proper channel.

Albert-Jan

Link to comment
To Michael Ashe : I implemented the producer/consumer style setup, and used the array of variants as per your suggestion.  At first I thought it was overkill, but I quickly found good uses for the variant data.  Question about OpenGOOP - does it have something similar to interfaces as Java has?  I mention above about having the various specific devices 'extend' functionality of a device class.  But some devices do many functions (some source meters act as current sources, voltage sources, current meters, voltage meters, etc).  So implementing as an interface would be the best way to deal with this.

3328[/snapback]

I am rather ignorant of JAVA at this time, but the OpenGOOP implementation does make instrument driver implementation easier, and the methods can be used to separate out the different functions of the same instrument/driver as you need.

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.