Jump to content
News about the LabVIEW Wiki! Read more... ×
dterry

Calling LVOOP Parent Implementations as User Interfaces for Configuration

Recommended Posts

Hello LAVA,

 

I have been working on a configuration interface lately for my framework, and have found a situation in which it seems desirable to be able to decide between a child and parent implementation of a class method at run time.  The idea is that each level of a class hierarchy is responsible for some data which is to be configured at the start of operation.  To avoid having to rebuild then extend the Config UI for each child, I have been trying to find a way to display each level (Parent, Child, Grandchild) UI in an "Options Dialog" style window.  The user should be able to select the hierarchy level to view and configure it's data.  

 

I have attached some sample code to illustrate, but it basically entails a listbox for the different hierarchy implementations, and a subpanel to display the selected implementation.  The code starts by traversing the class hierarchy to populate the listbox with any ancestors of the selected (Child) class that implement "UI.vi".  Then it waits for the user to select one of these implementations before setting the class object vi Control reference, running the VI and inserting it into the subpanel.

 

zc3vi38.png  7PemxIa.png
 

Note: It seems like it would be much easier to use the CBR/ACBR nodes to do this, but I was unable to figure out a way to call the different implementations, due to the dynamic dispatch terminals.

 

Because this was not particularly straightforward to implement, I have a strong suspicion that it breaks OOP, is generally terrible, which is exactly why I am posting it here.  Can someone give me a reason why this is a good or bad idea?

LVOOP UI.zip

Edited by dterry

Share this post


Link to post
Share on other sites

I don't think lv is really going to provide an easy solution here. Mercer wrote this (http://forums.ni.com/t5/LabVIEW/An-experiment-in-creating-compositable-user-interfaces-for/td-p/1262378) a while back but...

 

As to your specific code, I don't know that all those methods would work in an exe and its pretty fragile. It would be nicer to just give the parent a dynamic method for "get user interface(s)" and another DD VI to be the connector pane for those UIs, and you'd call them with start async call (rather than setting the FP elements by name).

You might also take a closer look at the CEF (http://www.ni.com/example/51881/en/). I've used multiple view nodes which share the same data to do something like what you're showing.

Edited by smithd

Share this post


Link to post
Share on other sites

I think how you do it is a bad idea.  There are almost certainly more robust und easier to understand methods to do this.

 

You can call the "Call parent method" in any child DD method and in this way pass on UI data to the base class (which will be called last) which then creates and shows the UI.  In this way you automatically have a hierarchy of UIs available.  If, for some reason, some UIs are not always available, then you simply don't add the data to the chain for that instance of the inheritance hierarchy (abstract classes for example).

Share this post


Link to post
Share on other sites

Thanks for the replies guys!

 

I don't think lv is really going to provide an easy solution here. Mercer wrote this (http://forums.ni.com/t5/LabVIEW/An-experiment-in-creating-compositable-user-interfaces-for/td-p/1262378) a while back but...

 

As to your specific code, I don't know that all those methods would work in an exe and its pretty fragile. It would be nicer to just give the parent a dynamic method for "get user interface(s)" and another DD VI to be the connector pane for those UIs, and you'd call them with start async call (rather than setting the FP elements by name).

You might also take a closer look at the CEF (http://www.ni.com/example/51881/en/). I've used multiple view nodes which share the same data to do something like what you're showing.

 

What do you have in mind as an alternative to LV?  What prevents it from being a good fit?

 

I agree that the code is fragile, hence looking for suggestions here.  I did try to use the ACBR nodes, but these don't allow dynamic terminals.  I'll think on the additional parent method for "Get UIs", but any ideas you have on the specifics would be helpful!

 

As for the CEF, I drew a lot of inspiration for my current configuration editor from it, but I'm not sure how you would implement this idea with CEF.  If you mean creating different UIs for each set of data, this is what I was trying to avoid, since it duplicates code/effort.

 

I think how you do it is a bad idea.  There are almost certainly more robust und easier to understand methods to do this.

 

You can call the "Call parent method" in any child DD method and in this way pass on UI data to the base class (which will be called last) which then creates and shows the UI.  In this way you automatically have a hierarchy of UIs available.  If, for some reason, some UIs are not always available, then you simply don't add the data to the chain for that instance of the inheritance hierarchy (abstract classes for example).

 

I certainly hope so! I just haven't found any yet.

 

I am aware of the Call Parent Method VI in LVOOP, but my problem with this has always been how to run it asynchronously.  However, it occurs to me that instead of calling the UI through the Call Parent Method, I could put a static reference to the correct UI in each DD implementation, in order to build the array of VI refs this way.  I will test this out and post an update when I have more information.

 

Thanks,

Drew

Share this post


Link to post
Share on other sites

(New code attached, run "Launch UI 2.vi")

 

Alright, let's try this again.  First off, I modified the original example to obtain it's references via the Call Parent Method, thanks to the suggestion from shoneill.  Each implementation contains a static reference to its own implementation of the UI, and adds it to the array.  This definitely seems way less brittle/fragile, and will scale easily.  Thanks shoneill!

 

juM5M8S.png9ALkP4B.png

UEbvsKc.png

 

smithd, I went ahead and set it up with ACBR to show you what I am talking about.  It definitely looks nicer, but since the terminals of the DD UI VI are dynamic, the VI is broken.  Any thoughts on getting around this, aside from setting the control values by name?

 

qMjhWPW.png

VOPsXiU.png

LVOOP UI (2).zip

Share this post


Link to post
Share on other sites

wrap the dd vi in a static vi. done. dusted.

 

Normally, this would be my go to as well, but this particular design restricts that option, see below

  • Wrap each class' implementation in a static vi.
    • You cannot have VIs in child/parent classes with the same name unless they are DD.  Therefore, I would have to create and name each UI differently, which is not very maintainable.
  • Wrap the DD vi in a static VI at the parent level only
    • This would mean that it would no longer be possible to call the specific implementation that I want.  This option is great for the general case of calling a DD vi with an ACBR node, and I use it in several places already.

This line of thinking is why I was wondering if this breaks OO design rules, although I am not well versed enough to know why.  Essentially, I am looking to call a DD vi statically in special cases.  Any other thoughts?  I have implemented the set control value code into my framework, and it seems to be working alright (if not a little slow) so far.

 

Thanks,

Edited by dterry

Share this post


Link to post
Share on other sites

(New code attached, run "Launch UI 2.vi")

 

Alright, let's try this again.  First off, I modified the original example to obtain it's references via the Call Parent Method, thanks to the suggestion from shoneill.  Each implementation contains a static reference to its own implementation of the UI, and adds it to the array.  This definitely seems way less brittle/fragile, and will scale easily.  Thanks shoneill!

 

juM5M8S.png9ALkP4B.png

 

 

smithd, I went ahead and set it up with ACBR to show you what I am talking about.  It definitely looks nicer, but since the terminals of the DD UI VI are dynamic, the VI is broken.  Any thoughts on getting around this, aside from setting the control values by name?

 

 

 

Minor style point: I think its nicer if each class just returns an array, and you build up that array in each successive child. That way if someone says "oh I don't want this so I'm not going to wire it up" it won't hurt as much.

 

Normally, this would be my go to as well, but this particular design restricts that option, see below

  • Wrap each class' implementation in a static vi.
    • You cannot have VIs in child/parent classes with the same name unless they are DD.  Therefore, I would have to create and name each UI differently, which is not very maintainable.
  • Wrap the DD vi in a static VI at the parent level only
    • This would mean that it would no longer be possible to call the specific implementation that I want.  This option is great for the general case of calling a DD vi with an ACBR node, and I use it in several places already.

This line of thinking is why I was wondering if this breaks OO design rules, although I am not well versed enough to know why.  Essentially, I am looking to call a DD vi statically in special cases.  Any other thoughts?  I have implemented the set control value code into my framework, and it seems to be working alright (if not a little slow) so far.

 

Thanks,

I was thinking the same as shoneill: The ACBR should call a static dispatch method and then the static dispatch method just calls the DD method. This works great for normal ACBR code but its more of a challenge because you also want to insert the VI into the subpanel...so all you'd get is the static, not the called DD. I suppose you *could* do something like what actor framework does. When you launch the VI you pass a queue (size 1, type vi refnum) into the static dispatch VI, which passes it to the DD vi, which immediately gets a reference to itself ("This VI" ref) and sends it back through the queue to the caller. On the one hand its kind of horrible, but on the other hand I think its safer than setting controls by name. Even if the child class screws up (and fails to return its ref through the queue for example) you can simply abort from the caller and go on with your life.

Share this post


Link to post
Share on other sites

Minor style point: I think its nicer if each class just returns an array, and you build up that array in each successive child. That way if someone says "oh I don't want this so I'm not going to wire it up" it won't hurt as much.

 

Totally agree here, something I missed doing it so fast.  Thanks!

 

 

 

I was thinking the same as shoneill: The ACBR should call a static dispatch method and then the static dispatch method just calls the DD method. This works great for normal ACBR code but its more of a challenge because you also want to insert the VI into the subpanel...so all you'd get is the static, not the called DD. I suppose you *could* do something like what actor framework does. When you launch the VI you pass a queue (size 1, type vi refnum) into the static dispatch VI, which passes it to the DD vi, which immediately gets a reference to itself ("This VI" ref) and sends it back through the queue to the caller. On the one hand its kind of horrible, but on the other hand I think its safer than setting controls by name. Even if the child class screws up (and fails to return its ref through the queue for example) you can simply abort from the caller and go on with your life.

 

I think I am either misunderstanding yours and shoneill's recommendations, or misstating the problem.  I have actually done exactly what you suggested with the single element queue passing back the refnum, but I don't think that helps here.

 

To restate my issue: If you have a static dispatch wrapper around the DD method, there is no way to allow the user to select which implementation of the class to use; the LabVIEW will select the implementation associated with the object input.  If you then use the Call Parent Method node in the child implementation to access the parent's implementation, I can't see a way to insert the parent UI implementation into the subpanel.  

 

To be fair, I have found one way to do this, by passing the subpanel reference as an input and allowing the method to insert itself.  However, I am using these classes on an RT system, and including the subpanel reference in the class will cause it to fail during deployment.  Therefore, I am looking for a way to do this without having the method insert itself.

 

Am I missing something?

 

Thanks,

Share this post


Link to post
Share on other sites

What do you have in mind as an alternative to LV?  What prevents it from being a good fit?

 

Lack of dynamic UI generation or easy ways to compose UIs (xcontrols being excluded from the easy category in general). If we're being really simple, for example, you could theoretically concatenate two sections of HTML and the website will render the 'child' and 'parent' data more or less as expected. .Net has something similar with xaml and windows forms also allow pretty simple creation of controls if I'm not mistaken. So for example one of the methods could be "add your controls to this container in this pane" and poof you get a single interface with both sets of info.

 

To restate my issue: If you have a static dispatch wrapper around the DD method, there is no way to allow the user to select which implementation of the class to use; the LabVIEW will select the implementation associated with the object input.  If you then use the Call Parent Method node in the child implementation to access the parent's implementation, I can't see a way to insert the parent UI implementation into the subpanel.  

 

To be fair, I have found one way to do this, by passing the subpanel reference as an input and allowing the method to insert itself.  However, I am using these classes on an RT system, and including the subpanel reference in the class will cause it to fail during deployment.  Therefore, I am looking for a way to do this without having the method insert itself.

No that makes sense, I think I was mixing up two ways of doing the task. Way 1 is to have a DD method which returns another VI which is the user interface. Another would be to call the user interface directly (static method wrapping dynamic method) and it can insert itself into the subpanel or return a reference to its front panel, but as you said this wouldn't let you show the parent class UI, so you're stuck with option 1 I think. Since you're selecting which implementation you want yourself you can't use DD, you have to use static dispatch. That having been said, I don't think its too "not very unmaintainable" to have a different static method for each class called "MyClass A UI.vi" and "MyClass B UI.vi". Its less than ideal but its not that terrible.

 

One other point that caught my eye. You're going to hit that same issue a number of times where you have code you want to run on windows that can't go on the cRIO. For example maybe you want to create a report or use report gen to read an excel table you're using to import configurations in bulk. I'd strongly recommend splitting each piece of functionality into at least two classes, probably three classes or two classes and a shared libary. Class 1 is windows only, UI only. Class 2 is cRIO only, RT only. Class 3 or the library is any shared type defs which are generated by class 1 and used to configure class 2. Its a pain to make so many but it also keeps your code separated by function -- theres no risk you break something on windows cause you included an RT-only method, and theres no risk you break your RT code because you included reportgen or some .net code. If you looked at the 3.0 release of CEF, this is why the "hierarchical" example has a UI class and a Config class that are different. Not shown is the fact that this was built hand in hand with the distributed control and automation framework (aka tag bus framework aka platypus) where every runtime plugin module is paired with 1 configuration class and N user interface classes. I can't stress enough how well this worked for us both by reducing the headaches and by forcing us to explicitly separate stuff that really only belongs in the UI from stuff that really only belongs in the runtime system.

Share this post


Link to post
Share on other sites

Normally, this would be my go to as well, but this particular design restricts that option, see below

  • Wrap each class' implementation in a static vi.
    • You cannot have VIs in child/parent classes with the same name unless they are DD.  Therefore, I would have to create and name each UI differently, which is not very maintainable.
  • Wrap the DD vi in a static VI at the parent level only
    • This would mean that it would no longer be possible to call the specific implementation that I want.  This option is great for the general case of calling a DD vi with an ACBR node, and I use it in several places already.

 

I don't see the problem with this approach to be honest.  Even if the VI you are calling is not DD (Implemented as static in the Parent) the VI being actually invoked will be the correct child version.  Your problem is knowing which version on the hierarchy chain to call explicitly.  If you already have a function to return unique identifiers for the different UIs, make that an input of your static VI and pass it to the DD VI which subseqeuntly makes an ID check at each level of the hierarchy and then launches the UI if the ID matches and passes the control to the "Call parent method" node if it doesn't match.  Think of it as a "Chain of responsibility" which is a valid OO approach.

 

This way you can filter down from the actual child until you have the correct UI.

Edited by shoneill
  • Like 1

Share this post


Link to post
Share on other sites

Lack of dynamic UI generation or easy ways to compose UIs (xcontrols being excluded from the easy category in general). If we're being really simple, for example, you could theoretically concatenate two sections of HTML and the website will render the 'child' and 'parent' data more or less as expected. .Net has something similar with xaml and windows forms also allow pretty simple creation of controls if I'm not mistaken. So for example one of the methods could be "add your controls to this container in this pane" and poof you get a single interface with both sets of info.

 

Very cool, maybe I'll have a chance to work with this at some point.

 

 

No that makes sense, I think I was mixing up two ways of doing the task. Way 1 is to have a DD method which returns another VI which is the user interface. Another would be to call the user interface directly (static method wrapping dynamic method) and it can insert itself into the subpanel or return a reference to its front panel, but as you said this wouldn't let you show the parent class UI, so you're stuck with option 1 I think. Since you're selecting which implementation you want yourself you can't use DD, you have to use static dispatch. That having been said, I don't think its too "not very unmaintainable" to have a different static method for each class called "MyClass A UI.vi" and "MyClass B UI.vi". Its less than ideal but its not that terrible.

 

That's fair.  How would you compare the differently named static methods option to the control value set option?  In my mind, the latter is less to keep track of, and "just works".  While it is not the ideal solution, the only downside I see is if you change the names of the controls, which can be mitigated by using an override template.  Since there is a lack of performance and stability concerns with either method, I think this may come down to personal preference.

 

One other point that caught my eye. You're going to hit that same issue a number of times where you have code you want to run on windows that can't go on the cRIO. For example maybe you want to create a report or use report gen to read an excel table you're using to import configurations in bulk. I'd strongly recommend splitting each piece of functionality into at least two classes, probably three classes or two classes and a shared libary. Class 1 is windows only, UI only. Class 2 is cRIO only, RT only. Class 3 or the library is any shared type defs which are generated by class 1 and used to configure class 2. Its a pain to make so many but it also keeps your code separated by function -- theres no risk you break something on windows cause you included an RT-only method, and theres no risk you break your RT code because you included reportgen or some .net code. If you looked at the 3.0 release of CEF, this is why the "hierarchical" example has a UI class and a Config class that are different. Not shown is the fact that this was built hand in hand with the distributed control and automation framework (aka tag bus framework aka platypus) where every runtime plugin module is paired with 1 configuration class and N user interface classes. I can't stress enough how well this worked for us both by reducing the headaches and by forcing us to explicitly separate stuff that really only belongs in the UI from stuff that really only belongs in the runtime system.

 

You bring up good points here, and I originally looked at just using the CEF/TBF, but I couldn't really wrap my mind around it, and I lean towards a "simpler is better" style in my code, which made 1 class more attractive than 3.  That being said, I definitely understand where you are coming from, and have considered moving UI elements into their own classes before.  

 

As far as windows code "contaminating" the classes for RT, the subpanel issue is the only one that I have seen that couldn't be solved by either a conditional disable structure or an abstraction API.  

 

I don't see the problem with this approach to be honest.  Even if the VI you are calling is not DD (Implemented as static in the Parent) the VI being actually invoked will be the correct child version.  Your problem is knowing which version on the hierarchy chain to call explicitly.  If you already have a function to return unique identifiers for the different UIs, make that an input of your static VI and pass it to the DD VI which subseqeuntly makes an ID check at each level of the hierarchy and then launches the UI if the ID matches and passes the control to the "Call parent method" node if it doesn't match.  Think of it as a "Chain of responsibility" which is a valid OO approach.

 

This way you can filter down from the actual child until you have the correct UI.

 

Ooooooo I like this idea shoneill!  Let me see if I understand it: So the basic flow would be to call the "Get UI Refs" DD vi to gather the necessary identifiers, then use ACBR to call the DD UI method (wrapped in a static VI in the parent method) and pass it the chosen UI ref.  The DD method then checks its own VI ref against the input ref, and calls the parent if it doesn't match.  The only task left is to pass the active vi ref back to the caller for insertion into the subpanel, which can be accomplished with the single element queue we discussed previously.  Am I understanding your suggestion correctly?

 

This is why I love LAVAG, great architectural discussions!

Share this post


Link to post
Share on other sites

 

Ooooooo I like this idea shoneill!  Let me see if I understand it: So the basic flow would be to call the "Get UI Refs" DD vi to gather the necessary identifiers, then use ACBR to call the DD UI method (wrapped in a static VI in the parent method) and pass it the chosen UI ref.  The DD method then checks its own VI ref against the input ref, and calls the parent if it doesn't match.  The only task left is to pass the active vi ref back to the caller for insertion into the subpanel, which can be accomplished with the single element queue we discussed previously.  Am I understanding your suggestion correctly?

 

Where do you get the refnum for the comparison?  Can't you use the string identifier and then request the reference from the DD vi?  Alternative would be to pass in the reference for a subpanel and tell the object to insert itself.  That way verything stays encapsulated within the objects and changes in methodology can be better managed.

 

Otherwise yup, that what I was going for.

Share this post


Link to post
Share on other sites

Where do you get the refnum for the comparison?  Can't you use the string identifier and then request the reference from the DD vi?  Alternative would be to pass in the reference for a subpanel and tell the object to insert itself.  That way verything stays encapsulated within the objects and changes in methodology can be better managed.

 

Otherwise yup, that what I was going for.

 

The refnum was coming from the code I posted previously (see below), but a string would work fine as well (probably better, I have no idea how refnum comparison works, but I would guess its not as simple as you might think).

 

As mentioned above, passing the subpanel prevents me from using the class on RT, so I am avoiding that option, although it does simplify things a bit.

 

Thanks!  I'll mark your post as the solution as soon as I can test it out.

 

juM5M8S.png9ALkP4B.png

Share this post


Link to post
Share on other sites

Ah RT, gotcha.

 

You COULD pass on a display OBJECT instead of an actual subpanel reference (Abstract base with no action / data) with a DD "display" method and only provide a child of this with actual subpanel functionality for non-RT code.  That way you retain encapsulation and provide extensibility but it IS more work.  this transfers to a NOP on RT and can be modified as required if future code changes are needed.

  • Like 1

Share this post


Link to post
Share on other sites

I like that thought as well, more flexibility.  Probably looking at iceboxing that one for future investigation tho, gotta keep a schedule!

Share this post


Link to post
Share on other sites

Well, not sure how to mark this as answered with the new layout, but I tried shoneill's suggestion, and it works great.  Thanks for everyones contributions to the thread!!

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×

Important Information

By using this site, you agree to our Terms of Use.