LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Dynamically Dispatching for User Input; best practices?

Solved!
Go to solution

Hi all,

 

Using LabVIEW 2013 (32 bit). Taking my first foray into LVOOP and I'm currently designing our user input. This User Input VI is dynamically dispatched. The goal is to allow to user to "mix and match" different pieces of the application as much as possible. The application is for a controller of a biological system. In particular, we have the notion of a "Controller", a "Model", and a "Patient". Patients all carry the same characteristics, but controllers and models only share an external interface. Some controllers may control to a set point, some to a range, some to a set of ranges depending on the time and current state of the Patient. As such, I'd like to have some sort of dynamic dispatching going on during the user input phase. The way I thought to do this was via subpanels, with a tab control to allow the user to jump between different parts of the setup process.

 

Here's a mockup of the User Input front panel:

 

http://i.imgur.com/sMZ7xFD.png

 

So if you have a certain controller, that controllers UI panel would show up in a subpanel, via some sort of dynamic dispatch. This would allow you to seamlessly switch between controllers and still have a consistent User Experience, eg controller 1 may have you choose a range to control to (two digital numerics), whereas controller 2 may have a set point to control to (just one numeric), like so:

 

http://i.imgur.com/6QpKXco.png

 

http://i.imgur.com/u2mla8N.png

 

You can assume that we have access to the instances of the Controller class, the Patient class, and the Model class at this stage for dynamic dispatching.

 

When I tried to implement this, I ran into problems:

 

http://i.imgur.com/2ZZqdka.png

 

The error that occured is: "Call by Reference: refers to a dynamic member VI; The connector pane of the strictly typed VI reference input includes a dynamically dispatched terminal, which means it is part of a method of a LabVIEW class. Dynamic member VIs are not supported by the Call by Reference node in this version of LabVIEW."

 

I understand the problem, maybe this is too much overhead for LabVIEW to handle cleanly, but is there a way for me to maintain the "niceness" of dynamic dispatch in this particular instance?

 

0 Kudos
Message 1 of 9
(4,609 Views)
Solution
Accepted by topic author ijustlovemath

What I did was make a "protected" VI that was Dynamic Dispatch for getting the reference to the GUI VI.  This way I could make the object show the parent's GUI if I wanted to instead of making yet another screen.  This VI was then called by a public VI that only the top parent class was to handle (static dispatch).  I didn't use the Asynchronous Call, but I think this would 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 2 of 9
(4,579 Views)
This is what seems to be the way to handle it. Will that ":GUI VI" be the most general/extensible way to handle creating the VI reference? Also, do you then use that VI reference to read control values off the sub panel? That seems a little messy to me, but I've never done this sort of thing before.
0 Kudos
Message 3 of 9
(4,572 Views)

Another option is to pass the subpanel reference into the VI (or keep it as part of the object) and then have the VI use the This VI constant to insert itself into the SP, then simply use a regular subVI call. This isn't ideal, because it ties the VI to the SP and distributes the control over the SP, but it is an option.

 

A variant on this is to have the VI pass its reference to the caller using some other means (event, queue, etc.) and having the caller insert the VI when it gets the message.

 

Side note - there's no need to use imgur. You can attach images here and embed them in your posts using the relevant button in the editor.


___________________
Try to take over the world!
0 Kudos
Message 4 of 9
(4,555 Views)

edit: Reread crossrulz's reply and it seems like what he said will work. I replaced the class constant with the class constant of the parent class, and renamed the indicator from which we read to "Model", which preserves the class hierarchy.

 

This works great for running the VIs, but processing the data is a slightly different story. Here's my current design:

 

 

This works great for individual classes, but it seems like I have to copy and paste this code, modify the case structure, and replace the class constant. Is there a better way to extract the information from the class as it comes in, essentially like dynamically dispatching?

0 Kudos
Message 5 of 9
(4,392 Views)

Personally, I quite dislike setting (and particularly getting) specific control values by name. My preferred solution here would probably be to do an ACBR call, although I don't think I ever actually did a call and collect.

 

Since that apparently doesn't work, my next preference would be to give the VI some communication channel (event, queue, notifier, etc.) and have it pass the information back through that channel.

 

Even if you don't do that, you can probably simplify your code by a number of ways, such as using the Ctrl Value.Get method, rather than the Get All method, but the most significant thing is that you can use the behavior of LVOOP which propagates LVOOP types correctly as you wire them. Specifically, you could even ditch the case structure entirely and just use To More Specific (or Preserve Run-Time Class, which you will probably need) and stop the loop when there's no error. You can do the entire conversion as a VI of the parent class, so there's no need to write it more than once.


___________________
Try to take over the world!
0 Kudos
Message 6 of 9
(4,372 Views)

Thanks tst, I ended up changing the conversion to that of the parent class, as you mentioned. Good call on the ControlVal.Get, and I like the suggestions of a queue or FG even more. I think I may end up using a functional global, as the VI the reference points to is structured as an infinite while loop with events:

 

 

You may see it as poor taste to have the VI be infinitely looping and then being aborted, but doing it this way prevents the user from having to click some sort of "Continue" button in each subpanel of the main UI VI. A queue might work, but you'd have to flush the queue each time the object was modified, replacing it with the new object. Or you could do a lossy enqueue with a queue size of 1, but I think that's about the same as using a functional global.The best way, I think, is to put a functional global where that inside indicator is now, and just read from it when the VI is aborted, instead of doing the CtrlValue.Get method.

 

Thoughts?

0 Kudos
Message 7 of 9
(4,355 Views)

@ijustlovemath wrote:

 

Thoughts?


Mainly that I don't understand what you want to do well enough to provide useful feedback which is not "use a good architecture". The advatage of something which is reference based is that you can generate N copies of it dynamically, which often tends to be needed when working with OOP, but if a FG global works for you, then you can use that.

 

And the infinite while loop certainly doesn't seem to make sense as coded, because there's nothing stopping the loop, so there's no point in passing the value out, since the code will never reach there. If your loop has an event structure, then an external stop event seems like a good way to stop the loop. I also think the loop inside the case structure is a good idea, but I'm assuming you did that mainly because that's how LVOOP VIs are generated.


___________________
Try to take over the world!
Message 8 of 9
(4,344 Views)

The objects being modified are single instance objects in my application. Namely, there's the notion of a Controller, and a Model, each of which has their own private data that must be set by the user prior to executing the application. This is where they are set. The extra indicators and error case structure are, as you noted, the remainder of what was generated from a static dispatch template. The value was originally passed out by getting the indicator value of the indicator inside the loop. The while loop is halted using the "Abort VI" method because I had already created a reference to the VI in order to insert it into a subpanel, and figured it did the same thing as sending a stop event, without the extra wires involved in registering a callback.

 

Though the reason I posted is for feedback, so if you think there's a better way, I'm all ears! Also, sorry for marking your comment as a solution; misclick.

0 Kudos
Message 9 of 9
(4,335 Views)