LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

LVOOP design question on factory style

Hi,

 

I'm not sure how to do what I want to do, but essentially I'm trying to support a (slowly) evolving custom TDMS data structure.  I was thinking that surely classes and inhertiance (overrides) would be perfect for this. For example, I have a data visualization tool that I've developed. Every so often, (3 times in 4 years) the internal structure of the TDMS data files have been modified (added new stuff, changed some formatting etc.) and I'm trying to make such changes be less of a PITA when maintaining my visualization code, which would need to be able to load/read files from any 'author generation'.  If my base parent class has all the methods my visulaizer currently needs, I was picturing coding the tdms reads as methods using the parent class made for dynamic overrides, then if/when things change, create a child of the parent who extends and overrides the base methods as required?

 

I picture having a 'launcher' helper.vi (see screen below) that takes a (TDMS) file path as the input, then starting with the parent class (basic and generic tdms operations as methods in the class, tdms reference as class data) gets the 'author' version from the TDMS file, passes that to a case structure (factory-style) which then spawns the correct child.  Now, because I feel I shouldn't have to close the tdms reference in the parent class and create a brand new child class, I attempt to cast 'To more Specific Class' to case/cast the already started parent class object to become the appropriate 'child', but I get an error 1448 and the description is sadly not meaningful to me.

 

Now, what I want to acheive may best be done in a different way and/or the way I tried to acheive it may not make sense in LVOOP, this is my FIRST attempt at leveraging inhertiance, so feel free to propose alternate ways to go about this..

 

Below is a screenshot showing a mock-up gist of what I intended, and a (bastardized) attempt at UML'ey describing the above ramble as a class dependency figure (created with www.draw.io, attached both as xml and picture).

class inheritance and factory.png

tdms class diagram.png

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 1 of 8
(3,353 Views)

(Is there an LVOOP sub-category that I missed, or did I just scare everyone away with my figures? 😛

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 2 of 8
(3,295 Views)

@QFang wrote:

(Is there an LVOOP sub-category that I missed, or did I just scare everyone away with my figures? 😛


Your post was long, and you didn't get to the issue for a couple of  paragraphs.   Summarize in the first paragraph. if not the first sentence, so people can decide if they wish to read the detailed post. 

 

Your issue, which is a very common one, is that you thought that "To more specific class" actually changes the class of an object.  Actually, it is just a test of the class, throwing an error if it is not of the more specific class.  Think of it as a "Is the object of this specific class?" method.  It changes the wire type that the (unchanged) object travels down.

 

 

Message 3 of 8
(3,279 Views)

Thanks for reading, the length of my post is probably directly proportional with my LVOOP skill-level..  If I had known a shorter way to post what I want to accomplish, I might also know how to acomplish it.

 

-What is a common way to accomplish what I desire (to cast a parent class to a more specific child class)?  And if not possible, what is a common way to do this instead?  I presume that if it is such a common misstake, there might be a common way to deal with it too?

 

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 4 of 8
(3,272 Views)

-What is a common way to accomplish what I desire (to cast a parent class to a more specific child class)?  And if not possible, what is a common way to do this instead?  I presume that if it is such a common misstake, there might be a common way to deal with it too?


You'd have to write a method that explicitly creates the child from the parent.  However, you don't actually need to create the parent object.   You just need a factory VI that opens the TDMS file, checks the version, selects the corresponding child and passes it the open TDMS reference.  

Message 5 of 8
(3,250 Views)

The only question in this reply/post is: where should the factory helper.vi 'live'? (scope?)  -It can't be 'inside' the parent class since it uses children, and it shouldn't be in a child either, since then it would need to move with every new child? Thinking re-use library / VIPM module, should I put my classes and this helper VI in a LabVIEW library??

 

The rest of the post is a 'journal of discovery' since the process of writing this  reply/post helped me realize several basic (and in hindsight obvious) things. I'm posting the 'journey' in case someone else finds it helpful! The below text is 'chronological' as I evolved and experimented, except *bold comments* that I went back and entered my take on the issue/question/problem after (hopefully) figuring things out!  (Thanks drjdpowell for providing the answer to something I didn't know was my question in your first reply!) 

 

Journal of self-discovery of LVOOP basics:

Forgive me for I must surely be missing something that is likely in numerous documents and posts elsewhere, but shouldn't a child object wire be able to use a parent method (didn't need to over-ride) and go on from that parent method to a child method (now I need to do something child specific)?  *-Yes, but only if the class is guaranteed at compile time to be of type 'child' or inherited from 'child'!*

 

In the simple setup below, I have a factory helper.vi that opens a tdms, reads the author property then creates an object of the appropriate type/generation in a case structure.  This all worked well until I tried adding a child method inbetween two parent methods.

unable to compile.png

 

Writing this post, it occurs to me that this (above) can't work, because the case structure class output could be either a parent or a child (dynamic) and this is indeterminate at compile time!  As such, half (in my simple example facotry of two classes) of the factory cases would not be able to 'do some child stuff'.  If 'some child stuff' was an over-ride of a parent method, I would still be fine, since the code could run 'that' method on any generation, so now my question becomes:

 

How do I case/add child specific code 'in-line' with generic code?  -> enter lesson learned from what I was incorrectly trying to do in my opening post: use a 'to more specific class' to test the object wire!

child compile.png

 

Obviously in a real-world use-case, you probably wouldn't do 'child stuff' (or parent 'close' for that matter) inside the factory.  Since the factory helper is part of the parent class library, we can add future children and update this helper/factory without breaking code that depends on the 'parent out' object from the factory because those 'legacy' users will only use parent methods and children methods that it needed when it was first created.

 

Now I'm a giant step closer to something I could use in my data visualization program, albeit it's a bit different than what I had envisioned/hoped for. Basically if a new child generation requires brand new methods as opposed to overriding existing (parent) methods, I would need to test the object wire and add those new method's similar to how I did 'some child stuff' in image above, but if the only thing that changes is behind the scenes stuff and the methods from past generations are still 'logical'.  For example, a 'return processed data' method may know that in version 1.0 of tdms, processed data was always in a group called 'A', but for some reason, later on it is in a tdms group 'B', I can override that method for that generation, and my visualization code would only need to be recompiled with the new class libraries, no block diagram changes requried as the original 'return processed data' method would be dynamically overriden!

 

 

I feel just a bit smarter now!  -I hope my 'journal' might help others down the path of (obvious?) discoveries!  I think I will keep coming back to this thread as I try to further architect and develop this class hiearchy and share thoughts and progress and obstacles along the way.  Kudo's if you read and are interested in more like this!

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 6 of 8
(3,170 Views)

@drjdpowell wrote:
Your issue, which is a very common one, is that you thought that "To more specific class" actually changes the class of an object.  Actually, it is just a test of the class, throwing an error if it is not of the more specific class.  Think of it as a "Is the object of this specific class?" method.  It changes the wire type that the (unchanged) object travels down. 

The above certainly seemed correct based on my posted example, but, going to the NI Factory Pattern which is where I was getting my idea to begin with, they use 'to more specific' in exactly the way I was trying to.. well, or not exactly as my way didn't work and theirs did. 😛

 

In the downloadable factory pattern example, the 'Load Plugin From Absolute Path.vi' uses 'to more specific' to go from a LV base class to the generic plug-in class, and in a video presentation it mentions what sounds to me to be the same expected behavior (sorry, I didn't write down the time where this was mentioned).

 

So what makes NI's use of the function 'to more specific class' actually change the class wire and live up to its name?  Is the magic sauce that it only changes the output object wire if the input wire is the most basic LV native class?  

ni to more specific class.png

QFang
-------------
CLD LabVIEW 7.1 to 2016
0 Kudos
Message 7 of 8
(3,156 Views)

The concept you need to understand is the difference between the wire type and the object type (not official terms). The wire type is what you see. The type of the left wire is LV Object. The type of the right wire is Generic Plugin. This is determined at edit time.

 

The object type is the type of the object on the wire at run time. This object is guaranteed to be of the type of the wire, but it can actually be a child class. In the case of the NI example, the type of the object on the left wire is actually SOME CLASS AT THE PATH INPUT (or LV object if there was an error).

 

This might be more clear to you if you think of the VI server hierarchy. Create a boolean and a numeric control and create references to them. Wire the references to a Build Array. The wire coming out of that is a 1D array of Control, but the actual value at run time points to a numeric and a boolean.

 

Also, you should consider that you have a bug in your code - the TMSC primitive always outputs a class of type X, but if the input class is not of type X (or possibly also if there's an error in), that forces the primitive to generate a new valid object of type X, which has the default value. In your code you pass this new object through the error case structure. Instead, in the case of an error, you should take the original object going into the TMSC node.


___________________
Try to take over the world!
Message 8 of 8
(3,147 Views)