06-04-2016 01:30 PM
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?
Solved! Go to Solution.
06-04-2016 09:12 PM - edited 06-04-2016 09:13 PM
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.
06-04-2016 09:17 PM
06-05-2016 04:37 AM
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.
06-22-2016 07:41 PM - edited 06-22-2016 07:58 PM
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?
06-23-2016 05:08 AM
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.
06-23-2016 08:54 AM
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?
06-23-2016 11:05 AM
@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.
06-23-2016 11:59 AM - edited 06-23-2016 12:00 PM
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.