LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

OOP principles in the application

I went away from the Perform() approach. It returned VAR and so it was easy to create edit time bugs with incompatible types from the application point of view. I switched to interfaces like IReadVoltage, defining methods like IReadVoltage Scalar (returning double and timestamp) and IReadVoltage WDT. This makes developing applications much easier for people using your design not being CLA.

 

Actor Framework
0 Kudos
Message 11 of 20
(427 Views)

We have several "types" of instrument in the LVOOP hierarchy that we implemented at my company.

 

They tend to fall in these categories:

 

  • Abstract
    • These are the instruments that don't really exist, like "Multimeter", that define all the common VI connection panes with Dynamic Dispatch, and have child classes that implement the functionality
  • Interfaces
    • These are things that are a "has a" relationship, instead of an "is a" relationship.  This is for things like the different connection types (Serial, Ethernet) and for common but not universal features (things like some sort of safety interlock or E-Stop functionality).  Uses the interface type from LV2020 to implement on top of other real devices.
  • Standard real
    • This is things like power supplies, multimeters, etc., the most basic and universal instruments
  • Specialty
    • This is for the true one-off instruments.  Things developed in-house, or instruments that no other manufacturer makes because they're so specific.
    • These skip the abstraction layer of OOP except for the topmost "Generic instrument" level, but can still use interfaces
  • Virtual
    • This is for "combo" instruments.  Things like where you buy an oscilloscope but it comes with a built-in function generator.
    • One instrument is primary, but when it runs common functions like "load config from disk" and "initialize", it does it on both instruments
    • The secondary instruments have a special flag that is set on them that points back to their primary owner for important commands, like shutting down, but otherwise act normal
    • Can also be used in some cases to have one instrument type mimic another.  For instance, in a pinch you can use a single channel on an oscilloscope to measure voltage like a low-resolution multimeter.
  • Hybrid
    • This is for multiple instruments pretending to be one instrument.  For instance, 4 power supplies pretending to be one large 4-channel power supply.  Not common, but can happen.
  • Simulation
    • Like any other instrument, but runs entirely on software but implements all Dynamic Dispatch VIs from a parent class to allow offline testing of software without real devices and without changing your code.
  • Semi-specialty
    • This is a rare category but it has happened to us.  We use it for instruments that have measurements in common but need to be set up completely differently from each other. 
    • Example: We do a lot of measurements on lasers, and sometimes we use cameras to get a full image of the beam, but other times we just use a "scanning slit" where the beam goes through a narrow plus-shaped hole and we just get measurements on two lines instead of a full grid.  In both cases, we still want to measure the width of the laser beam, and both devices can do it, but the setup commands for each are wildly different.
      • And even with that, different camera manufacturers have wildly different ways to configure their camera to measure lasers.
    • Our method for this is that we develop either an abstract class or interface to define all the measurements but then use the equivalent of a "specialty" class to do all the setup before the measurements.

 

If you intend to use mostly NI DAQs but occasional other devices, then that last "Semi-specialty" category is what I would implement the equivalent of.  Leave all the setup stuff in "raw" DAQmx, but then when it comes time to extract the measurement, you can abstract it to things like "Get waveform from channel" or something along those lines, where you input a generic channel ID nonspecific to hardware and it outputs a waveform, including the units and timing data so you can create generic handlers for the output as needed.

Message 12 of 20
(407 Views)

@mcduff wrote:


That is what I do. I have one non-reentrant part that scans for all instruments and makes a map of device information, model, sampling rate, etc. I use the instrument alias as the key. The user enters the parameters for an Analog Input acquisition, and this non-entrant VI configures all the task parameters in the map. Once the task is configured and committed, it is sent to a reentrant VI that starts the task. This reentrant VI allows one to change the display, analysis, etc of the running task. Thus multiple devices can run at once.

 

I do not know if this is the correct way, but it works.


Thank you very much for your feedback! It seems that I was on the right track.

0 Kudos
Message 13 of 20
(382 Views)

@Quiztus2 wrote:

I went away from the Perform() approach. It returned VAR and so it was easy to create edit time bugs with incompatible types from the application point of view. I switched to interfaces like IReadVoltage, defining methods like IReadVoltage Scalar (returning double and timestamp) and IReadVoltage WDT. This makes developing applications much easier for people using your design not being CLA.

 


Yeah, I have had a negative experience with using VARs, which is why I thought it would be better to store acquisition data within the classes. Good to know that my decision wasn't a bad idea after all! 🙂

0 Kudos
Message 14 of 20
(381 Views)

@Shatoshi wrote:

@Quiztus2 wrote:

I went away from the Perform() approach. It returned VAR and so it was easy to create edit time bugs with incompatible types from the application point of view. I switched to interfaces like IReadVoltage, defining methods like IReadVoltage Scalar (returning double and timestamp) and IReadVoltage WDT. This makes developing applications much easier for people using your design not being CLA.

 


Yeah, I have had a negative experience with using VARs, which is why I thought it would be better to store acquisition data within the classes. Good to know that my decision wasn't a bad idea after all! 🙂


Can you share a class diagram regarding acquisition data. How did you make the data accessible?

Actor Framework
0 Kudos
Message 15 of 20
(335 Views)

@Quiztus2,

I'm still working on it and as soon as I have a working solution, I will share it with you 🙂


0 Kudos
Message 16 of 20
(323 Views)

Very often there's no need, or not even a real benefit, to putting all HW in a class hierarchy (HAL).

 

HALs can be really powerful, but only if the application's HW setup needs to be highly configurable 

 

If you're not ever going to use polymorphism on the devices, there's no gain in a class hierarchy. If it's hard to 'make up' a class hierarchy, maybe you shouldn't.

 

Even if you're planning on doing "polymorphic magic", an interface could do that. With an interface, you can make just a few methods polymorphic and you won't have to loose your sanity thinking about a full HAL.

 

When making a HAL, chances are you un into multiple inheritance issues pretty fast. And other messy things like same methods returning different data types, etc..

 

I'd advice you to at least consider how simple things could be if you simply make a class for each device and use it in your application. Each class should of course use a library (a class) to do all the reusable things.

Message 17 of 20
(320 Views)

Hello,


wiebe@CARYA wrote:

Very often there's no need, or not even a real benefit, to putting all HW in a class hierarchy (HAL).

 

HALs can be really powerful, but only if the application's HW setup needs to be highly configurable 

 

If you're not ever going to use polymorphism on the devices, there's no gain in a class hierarchy. If it's hard to 'make up' a class hierarchy, maybe you shouldn't.

 

Even if you're planning on doing "polymorphic magic", an interface could do that. With an interface, you can make just a few methods polymorphic and you won't have to loose your sanity thinking about a full HAL.

 

When making a HAL, chances are you un into multiple inheritance issues pretty fast. And other messy things like same methods returning different data types, etc..

 

I'd advice you to at least consider how simple things could be if you simply make a class for each device and use it in your application. Each class should of course use a library (a class) to do all the reusable things.


I am simply trying to implement a light abstraction rather than a complex HAL for the DAQ part. I completely agree with what  mentioned according common devices part.

This went fine and works great for common devices like power supplies and multimeters. Things like setting max volts/amps, toggling output channels on and off, doing single measurements, all very easy to do generically and in a way that obscures the exact connection method and the commands sent.


Although after some research on the forum, I feel that it would be better to try the NI QMH pattern but with AMC (https://forums.ni.com/t5/Reference-Design-Content/Asynchronous-Message-Communication-AMC-Library/ta-...) or even the DQMH framework to achieve my goals. What do you think about that?

0 Kudos
Message 18 of 20
(260 Views)

I would favor decoupling my hardware abstraction from any framework. Measurement abstraction layer could be coupled to a framework like DQMH,AF or whatever.

Actor Framework
Message 19 of 20
(249 Views)

@Quiztus2 wrote:

I would favor decoupling my hardware abstraction from any framework. Measurement abstraction layer could be coupled to a framework like DQMH,AF or whatever.


I feel the same.

 

A HAL\HAL light\Set of drivers should have little to do with your architecture.

 

Of course you can facilitate your HAL\HAL light\Set of drivers to work conveniently in your architecture, but that should be mostly boilerplates around the driver hierarchy, nothing too invasive.  

0 Kudos
Message 20 of 20
(212 Views)