05-12-2009 08:07 AM - edited 05-12-2009 08:08 AM
It sounds like you are walking the path that I would anticipate.
If your widgets have to run by themselves without being "tickled" by something else then you may be moving toward what I understand as an "Active Object". Active Objects are not built into LV but they can be implemented by using VIT's (State machines) where the "state" that could be a candidate for an action engine are stored in a Shift register in the Active Object. Methods for the Active Objects can be invoked using a Queue to pass the request for the method invokation and if there is return data for the method packing the ref for the response queue into the queue with the the method request. (I bet that comes across as "neBulus" )
I have found SSD (System Sequence Diagrams) to be very helpful in getting my ideas straight an getting those ideas on paper so the code developers can work without me (or to remind myself what I decided I ws going to do two months ago). here is one page out of a 97 page document for an app I did.
They let me illustrate how all of the widgets will interact and when completed are just one step away from the contracts associated with each method. In the above SSD I am showing what is involved in deleting a front panel object from the screen. The user starts it out by hitting the delete key when the app is in edit mode and an object has been previously selected.
RUI invokes the method "DeleteObject" on the active Picture which in turn invokes a "Remove ObjectFro.." on the Object_Manager (that is just a LVOOP wrapper around an AE that keeps track of all of the dynamic objects and the queues that link them in.) After tell ing the Information and DB object that the widget is going away (they both get updates from the Object) It uses a "KillObject" method to tell the widget to go away. They object respondes by telling the Picture what its background looks like with its image showing (deletes its image from the picture) and then exists.
So I wouls trongly urge you do some non-LV drawing just to help yourself and if you get stuck you can post them with your Q's here so we can get into your app without a pile of words.
But throughout this all, don't forget to HAVE FUN!
Ben
05-12-2009 01:52 PM
Ben, I think my head just exploded.
While I understand the concepts behind OOP, I'm finding even basic LVOOP difficult to implement. I've been looking at the shipping examples, reading and re-reading this thread, and I'm just confused by the whole thing. I can see what the examples are doing...I just can't figure out the steps to take to do it myself (like creating child implementations of a parent VI). Just accessing the data in a LVOOP control seems like a huge production.
I don't think I'm ready for Active Objects yet. I need to get the LVOOP basics down first and I'm struggling.
I have drawn some pictures as you suggested (I generally like to do that since it helps clarify things in my head), so I think I have it pretty clearly mapped out on paper what the steps are to perform each of the methods. My data structures are pretty solid now. The functionality that I can't seem to wrap my head around is how to use multiple instances of the same VI (dynamically spawned?), in parallel loops on the block diagram (the event structure and the main loop), and handle all of the communication between them such that I don't try to execute two commands at once on the same instrument (single nonreentrant VI?), avoid collisions on my serial bus between instrument chains (single element queue to lock / unlock the communication line?), AND maintain communication between the different module components which reside in different chains (another queue?).
Any wisdom you can offer?
In the meantime, I'll continue poking around the forum and looking at shipping examples, and maybe I'll experience a "Eureka!" moment.
Thanks again for your help, too. 🙂
05-12-2009 02:23 PM
DianeS wrote:Ben, I think my head just exploded.
Yeah it feels funny at first but after I get used to it the fresh air feels good.
LAVA's OOP forum helped me a lot to learn LVOOP (I post on that forum under the name "neBulus" because of my dyslexia and the thread "What if LAVA contributors all had Roller Derby Girl names?").
Aritos Queue (Chief architect beihnd LVOOP at NI) and Tomi Maila (LVOOP LabVIEW Champion) respond to posting in that forum.
DianeS wrote:... The functionality that I can't seem to wrap my head around is how to use multiple instances of the same VI (dynamically spawned?), in parallel loops on the block diagram (the event structure and the main loop), and handle all of the communication between them such that I don't try to execute two commands at once on the same instrument (single nonreentrant VI?), avoid collisions on my serial bus between instrument chains (single element queue to lock / unlock the communication line?), AND maintain communication between the different module components which reside in different chains (another queue?).
Any wisdom you can offer?
In the meantime, I'll continue poking around the forum and looking at shipping examples, and maybe I'll experience a "Eureka!" moment.
Thanks again for your help, too. 🙂
One thing at a time.
To prevent simultaneous access to the serial port write a non-reentrant VI that send the query and waits fro the reply. Use that VI in everything that talks to that port. The "non-reentrant" property will enusre that a second object can't access the port at the same time since that VI is already running under the previous call. An Action Engine can be used for this.
You can use VI Server to open and run re-entrant VIs or templates for each instance you need.
Queues are good for passing commands and waiting for responses.
If you posted those images as jpg's I we could take a look and offer more specific suggestions.
Ben
05-12-2009 04:52 PM
05-12-2009 07:22 PM
Ben...LOVED the animation. That's almost exactly what it felt like. You're also right about the fresh air.
Yair...thanks for the little example. I think that the entropy is gradually dying down in that bowl of spaghetti I call my brain.
Is this what you have been trying to tell me?:
- Spawn multiple copies of a reentrant VI to handle the individual instrument channels within a serial chain.
- Inside that reentrant VI, put a NON-reentrant VI which handles the actual communication (Read and Write) with the instrument over the serial bus. That will prevent collisions.
- Use a queue to pass information between VIs which relate to specific modules. Use another queue to pass other types of information around (such as the refnum and the queue reference) between VIs as needed.
Am I getting closer? LVOOP aside, I understand how to implement this using plain old LabVIEW.
I wrote myself a little example earlier this afternoon as I was trying to comprehend the spawning part, and accessing the resulting VI instances from multiple loops. I was going to post it so you guys could take a look...probably don't need to now that I've seen Yair's example, but here it is anyway. It's sort of chicken scratch-y, but all I was trying to do was play with the concept and try to figure out how it works. (I do that a lot -- write weird little VIs in an attempt to teach myself something.)
05-13-2009 01:39 AM
DianeS wrote:
Is this what you have been trying to tell me?:
- Spawn multiple copies of a reentrant VI to handle the individual instrument channels within a serial chain.
Probably. The truth is that you only need to do this if you actually need parallel processes. Otherwise, you might find it easier to simply use an array of clusters (which probably means an array of LVOOP objects) and iterate over the array. Since you have a single hardware resource, this might be best for you.
- Inside that reentrant VI, put a NON-reentrant VI which handles the actual communication (Read and Write) with the instrument over the serial bus. That will prevent collisions.
That's the simple answer. If you need various interlocks and checks, you might need a more complex architecture. This also depends on the response time that you need.
You should also try looking forward. For example, if there's a reasonable chance that you'll get more devices which will be connected to another port or use another means of communication, you might wish to move the communication part into classes as well so that you can set it separately for each object. For something like this, you can have the non-reentrant VI called by reference (using the Call By Reference Node), so that you can have as many of those as you need. In any case, you're the one who knows the system, so don't drive yourself crazy over the things we're saying.
- Use a queue to pass information between VIs which relate to specific modules. Use another queue to pass other types of information around (such as the refnum and the queue reference) between VIs as needed.
Use as many queues (or notifiers) as you need. The main reason I like them for something like this (other than their actual functionality) is that they can be generated dynamically.
I do that a lot -- write weird little VIs in an attempt to teach myself something.
Any programmer (and probably engineer) worth their salt does this. It's perfectly reasonable and logical.
P.S. You don't want to use a hardcoded path. The best way to get the path is using the method shown in my example, because it protects you from everything.
P.P.S. In general, passing data to dynamic VIs using the Set Value method isn't a great idea. People usually do it before running the VI (I did in my example), and I never particularly liked it either. There are other ways (e.g. I could have typecast the VI's reference to a string, used that as the queue's name and then do an Obtain Queue in both VIs), but it's not a critical issue.
05-13-2009 05:44 AM
tst wrote:
DianeS wrote:...
"I think she's got it!"
I have nothing to add.
Ben
05-13-2009 12:09 PM
Hi guys!
Thanks for your comments, suggestions, and patience! It takes me awhile to catch on, but eventually I do get there. I just wanted to respond to a couple of Yair's comments.
Yair, you suggested that it might be easier to use an array of clusters and iterate through the array to handle the serial communications for each chain. In my original post, I wrote...
Does it make better sense to have a separate AE for each instrument, or does it make better sense to use a single AE and arrays to hold the data items for each module (i.e. an array which holds the instrument session reference, one that holds the setpoints, and one that holds the query data returned by the instrument)?
So I think the conclusion I've come to is, it'll work either way. Using an array and iterating through it is more straightforward, as well as being easily expandable...since I have to communicate along the chain in series anyway, I think I'll do it that way. I agree it would make more sense to use an array of clusters containing the data items for each object, as opposed to using separate arrays for grouping similar components (instrument sessions, etc.).
Nevertheless, it's always good to consider more than one approach to a problem, and I have learned a lot from this!
I'd already decided to move the communication part into classes. It just made more sense since the command syntax is a little different for each instrument.
I no longer use hardcoded paths in VIs I deliver, having learned the hard way not to. 🙂 As I said, that VI was just sort of a chicken scratch. Same thing with the "Set Value" method...to be honest, I think that's the first VI I've ever written which actually used that method. I do use "Get Value" from time to time, though.
Thank you again. This has been very valuable to me and I am very appreciative of your time and help (and your funny animations). I am sure that I will not be the only one to profit from this discussion!
05-13-2009 01:01 PM
Just to add some clarification to close out the thread.
If you are going to process all of the widgets one after the other with the sate of each widget in a cluster then that is a good situation where using LVOOP would work well since you can build an array of different LVOOP classes into a single array (first bonus point) and as you index through the array the dynamic dispatching of LVOOP will choose the method that is appropriate for that class (second bonus point). This was illustrated well in Tomi's demo for dropping shape in a picture control.
If the app can no endure one widget delaying the processing of the next widget, then keeping the widget info in an array will have this issue. In that case I would use seprate templates for each.
Have fun!
Ben
05-13-2009 01:16 PM
Yup. 🙂
I'm planning on doing a combination of the two...arrays of clusters for processing within an instrument chain, and separate templates for each of the three chains. So it'll look sort of like this:
Read TEC 1 Read Seed 1 Read Amp 1
Read TEC 2 Read Seed 2 Read Amp 2
Read TEC 3 Read Seed 3 Read Amp 3
The commands in the columns are done serially. The commands in the rows can (and usually will) be done in parallel.
Yay!
(I almost always have fun when I'm programming in LabVIEW.)