LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Silly & basic question - where did my child data go?


wiebe@CARYA wrote:

You can give the analog child a Get Data.vi that returns double(s), and give the digital class a Get Data.vi that returns Boolean(s).

 

If the parent doesn't have a Get Data.vi method, the connector panes don't have to match. The returned data can be a double(s) and Boolean(s) indicator. But you won't have run time polymorphism. You will get compile time polymorphism, for instance if you use Get Data.vi in a .vim. So you can get doubles from a analog class, but you can't call Get Data.vi on an array of analog and Boolean objects. Well, you can, but you have to cast each individual object to it's more specific class. This is a code smell, not something you want...


This solution requires you to typecast the class to the specific child implementation in order to be able to call it. If your user knows which child class it uses then that is no problem but that is not the typical use case of the Factory Pattern. The idea here is usually to instantiate a specifc object implementation without later bothering exactly which one you use. Of course in order to interpret the returned data you anyhow need to know what it really represents but that can be a runtime decision by using variants for instance, rather than a compile time decision by typecasting the class wire to a specific implementation type.

 

This might not be a problem you want to use to learn (or like) OO. It will give the impression that OO is really hard. But in fact, the problem is hard. For instance, think about solving the exact same problem without OO... My guess is it will be equally hard or even harder.

Definitely even harder!

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
Message 11 of 27
(975 Views)
@rolfk wrote:

But a parent can be (exclusively or partially) an interface definition and in doing so, absolutely can define an interface that a child should/must implement, to access something that the parent basically implies a child should have. Otherwise that child should not need to inherit from that (interface) class.

 

The complication in the OPs problem is that he wants to have analog and digital children. So if the parent (interface) defines a Get Data.vi accessor method it needs to be implemented in a way that it can return both types of data. Or you implement a Get Analog Data.vi and Get Digital Data.vi method in the parent and overwrite the according method in the child, but that is not very scalable if you ever intend to extend the hierarchy to support other data types and should be avoided whenever possible. It also burdens the user of Analog child to worry about the Get Digital Data.vi method which has no functionality. That is really muddying the object interface definition and generally an ugly solution, but often chosen as quick and dirty implementation when extending existing class hierarchies, as a proper redesign would require changing the parent and all its already existing offspring. 😂

Wouldn't that Interface be like inheriting your good looks. 😉

Yeah, the Get data would need to use a common data output like Variant, or you would have to skip the root function and have unique child functions or the 'ugly' one (i don't find it that bad) with a Get Analog and Get Digital. In the root method you simply generate an Error "Function not implemented" and the appropriate child implements its version.

 

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
Message 12 of 27
(973 Views)

wiebe@CARYA wrote:

This problem is typical when you're using inheritance to extend a parent. Polymorphism is easier to comprehend (and like) when children implement their parent.


That's an excellent way of explaining it! I'll try to remember that one.

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 13 of 27
(970 Views)

@rolfk wrote:

wiebe@CARYA wrote:

You can give the analog child a Get Data.vi that returns double(s), and give the digital class a Get Data.vi that returns Boolean(s).

 

If the parent doesn't have a Get Data.vi method, the connector panes don't have to match. The returned data can be a double(s) and Boolean(s) indicator. But you won't have run time polymorphism. You will get compile time polymorphism, for instance if you use Get Data.vi in a .vim. So you can get doubles from a analog class, but you can't call Get Data.vi on an array of analog and Boolean objects. Well, you can, but you have to cast each individual object to it's more specific class. This is a code smell, not something you want...


This solution requires you to typecast the class to the specific child implementation in order to be able to call it. If your user knows which child class it uses then that is no problem but that is not the typical use case of the Factory Pattern. The idea here is usually to instantiate a specifc object implementation without later bothering exactly which one you use. Of course in order to interpret the returned data you anyhow need to know what it really represents but that can be a runtime decision by using variants for instance, rather than a compile time decision by typecasting the class wire to a specific implementation type.


The factory can create the classes, and a loop or selector can split them up. So the user does need to know the type, or anticipate each type, and make specific code for each type. This is something you need to know at compile time.

 

I agree, the idea behind a factory (and polymorphism) is you don't want to be bothered by the differences.

 

I do have factories that output LabVIEW objects, and then split them up into specific arrays. Not sure it it's typical, but it is useful. I still don't want to be bothered with the differences, once the factory output has been split up.

 

You can't have it both ways though. If the caller doesn't want to be bothered by the differences, there shouldn't be differences from the caller's PoV. If you do want the differences, you need casting at some point. Either casting the output to something general, or casting to specific and handling the specifics.

 

I think we're in agreement here 😊.

0 Kudos
Message 14 of 27
(962 Views)

BTW. We had this exact same thread before... Not sure how to find it, but the problem, solution and discussion seem very familiar.

0 Kudos
Message 15 of 27
(959 Views)

Yes, Elans ... Elons, something, thread was very similar. 🙂

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 16 of 27
(944 Views)

Thank you guys Rolf, Carya, Yamaeda and Kyle SO MUCH! I only hope I can repay the kindness & patience y'all have shown me back to the community (OK, once I actually know something).  Your words will take about a week to digest and assimilate, maybe.  I introduced myself to OOP 2 weeks ago tomorrow, and strangely enough I haven't totally mastered it yet 🤔.

 

In my Factory scheme - our situation is this: I'm designing an upgrade to a system which they have used for 25 years.  Obviously all the existing HW & SW go in the garbage can (well except for the operator GUI, which they like).  But for this upgrade/renovation they want a HAL-ish SW structure for future-proofing against obsolescence.  They'd gotten burned previously because the HW vendor discontinued the product line just as they'd gotten 2/3 thru a new design using that HW 😫.  So here I come.  I hear "HAL" and think LVOOP naturally!  {only problem was I couldn't spell OOP!}.  Anyway, when it's time for the next upgrade (5-10-15 years down the road), someone (probably not me) will need to write new child classes for the new hardware, but if I do it right, they should be able to just "plug them in" to my spiffy parent classes.  So it's not a dynamic  Factory situation - some ini files will need some new stuff, etc. but hopefully I'm just minimizing the future pain for whoever needs to do the next upgrade.  Does this make sense?  Thank you all again for your fantastic tutelage! Stay healthy! paul

Message 17 of 27
(936 Views)

@PaulOfElora wrote:

Thank you guys Rolf, Carya, Yamaeda and Kyle SO MUCH! I only hope I can repay the kindness & patience y'all have shown me back to the community (OK, once I actually know something). 


That is the spirit! Pay it forward!

 

Keep in mind that HAL's are one of the most difficult things to make. And for some reason, the thing beginners start with 🤔.

 

Pick your battles...

 

If you have analog devices, and digital devices, perhaps they shouldn't be in the same hierarchy? Or perhaps they don't have to come from the same factory? That makes it easier.

 

Interfaces probably make your solution easier as well. In a way, you're looking for multiple inheritance here. An analog device inherit from device, but Analog can be a class on it's own (not a child of Device). You could have something analog that isn't a device. For just two "mixes of hierarchies" (device, analog\digital), it shows as the polymorphism problem you're facing now, but what if you have 10 mixed hierarchies? It's not easy to make examples for that; perhaps an analog class (not a device) that is a database?

 

This would make the problem more dramatic, and (the need for) a good solution more obvious. Without interfaces and multiple inheritance this would require compositions (classes containing there available "featured flavors"). Tedious, but what we have for now. With interfaces, you have a steep learning curve (haven't used them myself), but more power and a more elegant solution.

0 Kudos
Message 18 of 27
(930 Views)

What Wiebe said. There have been many attempts at creating HALs. And almost all of them sooner or later land at a point where you have to decide: Abandon the idea to have a flexible HAL interface or throw away everything and start new?

 

The chances are pretty high that you need several of these "iterational" steps before you end up with something that actually sort of works. To get it all right, flexible, pluggable and enjoyable is an art that very few master. I haven't really tried it myself aside from some fairly simple Factory Pattern implementations around a specific group of device protocols!

Rolf Kalbermatter  My Blog
DEMO, Electronic and Mechanical Support department, room 36.LB00.390
0 Kudos
Message 19 of 27
(921 Views)

Thanks again wiebe.  Definitely bit off too big of a chunk for a beginner - maybe next week😜.  Since I know that there will only ever be DIO and analog input data coming from/to these IO controllers I should slow down, park, take the key out of the ignition, get out of the car, and start again SIMPLE.  Funny thing was I got started down this particular rabbit hole looking at Elijah K's Ginormous Measurement monster at

 

https://forums.ni.com/t5/LabVIEW-Development-Best/Measurement-Abstraction-Plugin-Framework-with-Opti...

 

What intrigued me was his use of variant attributes as lookup tables/dictionaries, which he uses to select which measurement classes to use for particular tests/steps/HW.  So methinks "hey instead of my usual array of clusters that define an IO point "mapping", with a search for the desired IO point name to get a bitmask or analog channel (works, but klugey), I'll do the "mapping" with a dictionary!". Zoom, down the rabbit hole.  I still like the dictionary idea as an "IO point mapping" technique tho.

0 Kudos
Message 20 of 27
(912 Views)