LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

OOP architecture for device driver

Solved!
Go to solution

Hello everyone,

 

I am writing to get help/support for my OOP architecture. Here's the context:

In my company we produce 4 devices for which we have libraries in ActiveX format, that we call from LabVIEW with "Invoke Node (ActiveX)" VI. The 4 devices are very similar, and the libraries as well (I'd say 95% of the functions are identical), but they are still distinct ActiveX objects.

To use the library, I create a refnum cluster (each library actually contains 5 sub-libraries so I have a cluster with refnums to all 5 sub-libraries)

I often have to run tests on Device 1 (D1), and later switch to another device D2 (hence another library) but with almost the same test. Therefore I change the reference to the library for D2 and all the Invoke nodes update automatically to the new methods (for D2), since methods in library for D1 and D2 (and D3 and D4) have the same names (except for some outliers).

 

As calling methods through Invoke Nodes is pretty ugly in a main VI, I created subVIs for each ActiveX method. Inside, it's pretty simple: cluster with refnums to libraries, Invoke node and inputs/outputs.

Now the issue is that if I want to change the library reference in the main VI, the subVIs don't update automatically to the new reference (since the cluster of refnum to D1 is not the same type as cluster of refnum to D2 for example).

 

My idea is to use OOP and create a parent class that will define methods for each ActiveX function, and children classes for each device, that will inherit methods from the parent class and only override what is different or missing from the parent class.

 

Problem:

I need to use a "generic" reference in the parent class in order to use Invoke Node in the parent methods. For now I use the reference to D1 but would like to have a generic reference. Is it possible?
In a traditional inheritance, when I create a child instance, I would overwrite the parent's reference. However, here it is not possible, since the parent's reference and the child reference are clusters of different type.
My solution for now is to override each parent's methods with child method (Create VI for override and then replace subVI with contents), but that is not good reusability and upgrades (if I want to update the parent method, the child method won't update).
I don't know if I am clear enough or if I miss the point of OOP. Let me know if you need more details or examples.

 

(Using LabVIEW 2017)

 

0 Kudos
Message 1 of 15
(1,878 Views)

What I would do is create an "abstract class".  They don't technically exist in LabVIEW, but you can fake it.  Make a parent class that has nothing in it, but defines the interface the child classes will have to use.  If you have any common data items, take care of it in this parent class.  Then the rest of your libraries just turn into child classes of this "abstract class".  Dynamic Dispatch is about all you are really going to achieve here from what I am gathering.  But that will greatly simplify your main code (which only needs to call the parent class).


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 2 of 15
(1,834 Views)

Hi, 

Thanks for your answer.

I am not really sure about what you mean by "interface". My goal is to define most methods in the parent class, so I can then inherit them in the child classes. But to do so, I need a reference in the parent class' definition in order to "configure" the Invoke nodes in the parent's methods.

0 Kudos
Message 3 of 15
(1,829 Views)

Why do you need a reference to the object?  You just use the wire of the parent and load whichever child class you need at the initialization.  Dynamic Dispatch takes care of the rest.

 

By "interface", I mostly mean the VIs that will be overridden.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
0 Kudos
Message 4 of 15
(1,809 Views)

I think I understand what you meant but I bump into an issue when doing that.

First I declare a reference to a generic device and its libraries (it's not truly generic as we don't have such library, so I just used reference to device 1 for the parent reference, but hope to override it in child classes).

Then, I create a method in the parent class, where I unbundle the class wire and use a reference to one of the sub-libraries to call the Invoke Node.

Finally, in the last picture, I created a VI where I used a Write accessor to the device reference of the parent and try to overwrite the parent reference with the Device 2 reference. But here's the issue: Since Device 1's libraries refer to ActiveX objects called "Device1...." and Device 2's libraries refer to objects called "Device2...", the two clusters are not the same type and it's impossible to replace Device 1 reference with Device 2 reference (broken wire due to type mismatch). No idea how to avoid that.

 

parent_attributes.PNGparent_method.PNGVI.PNG

Download All
0 Kudos
Message 5 of 15
(1,799 Views)
Solution
Accepted by seb_bd

@seb_bd wrote:

Then, I create a method in the parent class, where I unbundle the class wire and use a reference to one of the sub-libraries to call the Invoke Node.


That is your problem.  You are trying to put data specific to a certain class into a more generic class.  That data does NOT belong in the parent class.  You should not be doing any of this unbundling in the parent class.  Force the children to override your public VIs and let them go all of the work.  The parent class is just a way to maintain an interface, not to do any of the actual work.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 6 of 15
(1,785 Views)

I just read Bloomy's post about Interfaces (http://www.bloomy.com/support/blog/so-you-want-labview-interface) and I now understand what you meant.

In the Interface (parent class), I should simply define the methods (no code inside, just create the VIs) and make it compulsory for the children to overwrite the method. It is actually what I did at first, but then I have to code the methods for each children.

And if one day, I need to modify one of the methods (let's say I want to implement a CRC check or a limit check), I will have to do it in all the children methods, right? It's okay now since I have only 4 children, but if I had 100?

I thought there would be a more efficient way, but I guess I will just have to do this way.

Anyway, thanks a lot for your answers!

0 Kudos
Message 7 of 15
(1,783 Views)

@seb_bd

And if one day, I need to modify one of the methods (let's say I want to implement a CRC check or a limit check), I will have to do it in all the children methods, right? It's okay now since I have only 4 children, but if I had 100?

I thought there would be a more efficient way, but I guess I will just have to do this way.


I'm not an OO expert but I think I know the answer to this one.  You can implement a VI in the parent for calculating the CRC, then the children can all call that to do the work.  But if you need a specific CRC that only that child would want, you can turn that parent VI into a dynamic dispatch, and only override it in the child that wants to.  If there isn't an override available it just calls the parents implementation.  Don't need CRC?  Don't call it.  Need the common CRC?  Don't override it.  Need a custom CRC?  Implement the override.

Message 8 of 15
(1,770 Views)

Hooovahh,

Thanks for your answer. I think I gave a bad example. I understand the notion of inheritance and override if needed, but my main issue here is that if I needed to implement this "CheckCRC" in all the "Read" methods of my devices, I would need to wire it in all the children VIs (since all children have to override the parent/interface methods).

I understand that my case is not ideal because of these "similar but different" libraries, so my guess would be that it would be a better idea to rework the ActiveX libraries from scratch, if the work required to make the modification was too important (as I said, if I had 100 devices instead of 4).

Thank you for your help!

0 Kudos
Message 9 of 15
(1,745 Views)

@seb_bd wrote:

my main issue here is that if I needed to implement this "CheckCRC" in all the "Read" methods of my devices, I would need to wire it in all the children VIs (since all children have to override the parent/interface methods).


Well ... yes. Sooner or later you have to code. 🙂

/Y

G# - Award winning reference based OOP for LV, for free! ADDQ VIPM Now on GitHub
"Only dead fish swim downstream" - "My life for Kudos!" - "Dumb people repeat old mistakes - smart ones create new ones."
Certified-LabVIEW-Developer
0 Kudos
Message 10 of 15
(1,734 Views)