LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

LV_Abstract Open Source OOP Library

I completely agree.

0 Kudos
Message 11 of 26
(606 Views)

You have got me thinking though.
All value classes have a ‘to String’ method, I can extend the unsigned integer classes and use a variant property to read the enum type, the ‘to string’ method would return the enum string value if valid.

0 Kudos
Message 12 of 26
(604 Views)

Why though? And I ask this as someone who has ventured down similar paths before.

 

What is the advantage of having these classes? To inherit from? Well, here's my 2c on that.

 

Let's say you need these packed as classes so that we can define "new" versions with some added functionality. Well, my critique of this would be that the base class is so lightweight that it can actually simply be replaced with the datatype itself, and all "extra" features can be defined in a class which exposes the functionality but does NOT encapsulate the actual value. Keep the actual datatype on the connector pane. This will actually decrease the number of object function calls you will have to make with practically no decrease to usability or comfort. In fact, I would argue it will increase readability and the ability to debug. Decreasing the number of object calls is important because each call has overhead. Yes,each call is not very large but if you implement the object model on such a granular level, any real-world action you end up trying to perform ends up containing hundreds of such calls and the overhead actually really starts to add up.

I'm a firm believer that in order to take the step towards LVOOP for something like this there was to be one of these two cases:

 

  • The class itself implements significant additional functionality which is of benefit in such a wide range of circumstances that it is generally beneficial to pay the overhead price globally for the times where the additional functionality is useful. These kinds of classes can actually be set to be inlined and can mitigate the overhead issue. But then inheritance is impossible.
  • It is required to be exchangeable with different "versions" i.e. strategy pattern. This requires inheritance which makes inlining impossible.

Maybe I'm missing the use case, but I don't really see either of these being met here.

 

So this is why I would ask "Why?". It's not a troll post, it's not a critique per-se, but maybe you can outline what the ACTUAL benefit of this would be versus having a "helper class" which acts on the datatype in question.

0 Kudos
Message 13 of 26
(583 Views)

II never pushed it this far, but I have data parents with string, Boolean,float, [Boolean],[float] childs.

 

It's not to inherit from, but as a replacement for a variant.

 

You can have any VI return a value (the parent), and the object can be a string, integer or whatever. Pretty much what a variant does, but you get polymorphism and type safety.

 

For instance, I have a formula parser that takes floats and Booleans, and you can do math on them. Later we added strings, so we can execute operations on strings too. This library accepts arrays of floats, Booleans and strings, to an operate ('+' for instance) can act on the array without a for loop.

 

This turns out cleaner (and probably faster) than going from variants to types and back. 

0 Kudos
Message 14 of 26
(562 Views)

What OO does better than anything is dynamic dispatch, where the current parent class can be replaced with a different class, that inherits from the parent class, but changes original functionality.

 

A use case for me, was when I worked at a rocket company writing launch pad control software for multiple launch pads. The launch pads would basically be the same but the reality was each launch pad was created better than the next and so different, with different IO and  different controllers from different manufacturers etc. so instead of rewriting all of the code each time the IO was abstracted, with the parent class being similar to these classes I present here, and all we would just have to do is write a new child class for the specific IO that was used for that specific pad. An off shoot of abstracting all IO is we could test the launch pad software by replacing the physical IO in the software with simulated IO to perform Software in the Loop Testing (SITL) for regression testing etc and if the simulation gets good enough launch operator training as well.

Basically I am describing using these library as the basis of a HAL Hardware Abstraction Layer.

 

Once you start abstracting the IO, you can then start reusing code. Say you write a really good temperature control loop from a cRIO using scan engine IO but later on you need to do the same with IO from the  FPGA or IO from DAQmx etc instead of copy and change the core temperature control loop each time you can abstract the IO and use the same temp control loop each time but you just have to pass in the specific class that represents the io you want to use. With this approach you can reuse code, even share with others and they can develop their own IO for it.

 

You don’t just have to abstract IO but you can abstract system variables. Are you values coming from shared variables, will they be global variables, is the data being shared via tcp, udp, notification etc, you don’t know because some one else is working on the variable layer. So by agreeing to abstracting the variables you can go ahead and write and test your functions with out waiting from the variables layer to be written/decided on.


I hope this helps.

 

i have my own use cases too, and i plan to share this code too but first this library.

0 Kudos
Message 15 of 26
(554 Views)

Also part of the reason for sharing this code is, I hope people will write and share IO and Variable drivers that uses these classes and other people the share the functions they made that can use them.

 

0 Kudos
Message 16 of 26
(548 Views)

@LV_Tim wrote:

A use case for me, was when I worked at a rocket company writing launch pad control software for multiple launch pads. The launch pads would basically be the same but the reality was each launch pad was created better than the next and so different, with different IO and  different controllers from different manufacturers etc. so instead of rewriting all of the code each time the IO was abstracted, with the parent class being similar to these classes I present here, and all we would just have to do is write a new child class for the specific IO that was used for that specific pad. An off shoot of abstracting all IO is we could test the launch pad software by replacing the physical IO in the software with simulated IO to perform Software in the Loop Testing (SITL) for regression testing etc and if the simulation gets good enough launch operator training as well.

Basically I am describing using these library as the basis of a HAL Hardware Abstraction Layer.


Of course with this flexibility of polymorphism, you're moving edit\compile time errors to run time errors.

 

Where a double output won't fit a double input, a 'data' class (or variant), will not break the wire. You get flexibility, but at a price.

 

There is a balance, and it should be carefully considered.

 

The nice thing with OO is that even when you get a traditional driver, you have many options. Modify it, add interface methods, add interface methods in a child (if the class is locked), make a proxy wrapper class, etc.

 

I definitely don't advocate a universal HAL (or any HAL), because I've seen this fail more than once. Even a HAL should be as simple as possible, and a "one size fits all" HAL is the opposite.

 

If you just want a driver, this will all just be redundant complexity. Pretty much like any abstraction, including OO. Of course there's a huge bias toward what you know as a developer. If you're into OO (or HAL, or AF, or whatever), the price of the abstraction get's lower, the benefits higher. OO works best with OO libraries, a HAL works well if everything fits the HAL, AF works (probably?) great if you have a lot of actors.

0 Kudos
Message 17 of 26
(531 Views)

wiebe@CARYA wrote:

 

Where a double output won't fit a double input, a 'data' class (or variant), will not break the wire. You get flexibility, but at a price.

 

There is a balance, and it should be carefully considered.

 

 


In my experience this approach works when limited to a single context of code. Once you get into more complex code bases, the flexibility increases linearly, the cost increases exponentially. Small additions beyond the optimal use of OOP can quickly erode all benefits gained up to that point.

 

If this hierarchy were possible to be instantiated, to create multiple versions of it to suite each individual context individually, then sure, it could provide some real value. But as a global resource (seeing how LV deals with classes) it can quickly lead to dependency problems and maintenance explosion. Just my 2c.

0 Kudos
Message 18 of 26
(518 Views)

But they will end up being mutually exclusive pieces of code all sharing the same inheritance hierarchy. This is bound to run into some severe violations of the Liskov substitution principle.

 

That is code smell to me.

 

If the hierarchy could be instantiated, we would be able to avoid the majority of the downsides of it. But as it is, being essentially a shared hierarchy, it can lead to a lot of very unwanted side-effects.

0 Kudos
Message 19 of 26
(516 Views)

@Intaris wrote:

wiebe@CARYA wrote:

Where a double output won't fit a double input, a 'data' class (or variant), will not break the wire. You get flexibility, but at a price.

 

There is a balance, and it should be carefully considered.


In my experience this approach works when limited to a single context of code. Once you get into more complex code bases, the flexibility increases linearly, the cost increases exponentially. Small additions beyond the optimal use of OOP can quickly erode all benefits gained up to that point.


Yes, it's a balancing act.

 

That might not be an OO problem. You could make the same argument for variants. If you take those too far, you end up with "Python" code. Nothing is self-describing anymore, and you end up with mud.

 

It can get ugly fast. Once you make your data flexible, everything needs to adhere to the flexibility, nothing is strong typed anymore, and understanding how things work might only be possible by examining run time behavior.

 

The flexibility can be required, if the application is completely defined at run time.

 


@Intaris wrote:

If this hierarchy were possible to be instantiated, to create multiple versions of it to suite each individual context individually, then sure, it could provide some real value. 


With an interface parent (maybe even in stead of the current parent) you would be able to create multiple versions.

 


@Intaris wrote:
But as a global resource (seeing how LV deals with classes) it can quickly lead to dependency problems and maintenance explosion. Just my 2c.

I don't get the global resource part. Isn't it a by wire library?

 

Or do you mean like a global dependency (used everywhere)?

 

You could still use this class to be useful with a global resource, for instance a CVT. The UI could update it's values (e.g. setpoints) and controllers could use them, Boolean (e.g. valves) or double (e.g. flow controllers). Esp. when the UI is dynamic and the devices are too. The CVT will be the glue.

 

This would couple the UI with the CVT and the CVT with the controller. Again, it will also result in run time errors vs compiler errors. But it could be convenient when done well, and required for some type of applications.

0 Kudos
Message 20 of 26
(498 Views)