11-25-2014 03:34 PM
I can't find your other post, but I don't really understand the purpose of this. To me you get one benefit, assuming you use a single FGV per queue, and that is easy searchability. But decent wiring can get you close. In exchange for that one benefit, you get performance issues, maintentance issues (any bug must be fixed everywhere, any new features must be added everywhere), code duplication (one FGV per queue data type, while queues adapt to the type you wire in), less local tracibility (you can't just follow a wire), and usability issues (like the dequeue problem you mentioned, as well at the universal problem with FGVs which is that you don't know which inputs are required).
What problem does this solve that makes it worth all those headaches?
11-25-2014 04:26 PM
@smithd wrote:
I can't find your other post, but I don't really understand the purpose of this. To me you get one benefit, assuming you use a single FGV per queue, and that is easy searchability. But decent wiring can get you close. In exchange for that one benefit, you get performance issues, maintentance issues (any bug must be fixed everywhere, any new features must be added everywhere), code duplication (one FGV per queue data type, while queues adapt to the type you wire in), less local tracibility (you can't just follow a wire), and usability issues (like the dequeue problem you mentioned, as well at the universal problem with FGVs which is that you don't know which inputs are required).
What problem does this solve that makes it worth all those headaches?
I don't think I'm alone in finding this a useful tool. My style is for there to be a lot of VIs encapsulating details (such as Queues). I use the Producer/Consumer pattern a lot, and there are lots of Producers, sometimes two or three in a single VI feeding the same Queue, but typically they are in many different VIs. With "wires", I would need to pass a lot of Queue references between VIs. With Wireless Queues, it is pretty mnemonic, as this snippet shows. The icon tells me that (a) it's a Queue, (b) what goes on the Queue (Polhemus Samples, whatever they are), and (c) what operation I'm trying to do (Enqueue). This is a very "stable" VI (I actually have a Template for producing them -- just add the Input terminal and design an Icon), so there is no maintenance issue. If I need a little flexibility (say I want a Message Queue, but want to pass in the Message and its Variant Data separately), that's a trivial modification to the Model. There is little Code Duplication -- instead of laying down an "anonymous" Enqueue function, I put down my Wireless Queue VI (of the same size) that identifies the Queue by name (instead of by "colored wire" that you can follow to its named origin) and says the operation (yes, it's a tad bigger with the Enum on top, but Look, Ma, No Wires!). Given that I make heavy use of sub-VIs, tracibility is much improved here, as you don't have to trace wires back to Controls, then find where the VI with the Control is located, and continue tracing the wire. Not that it's such a big deal with Queues -- wires are much less "directional" than in usual LabVIEW Left-to-Right code, as an Enqueue sends data out through the reference to its input, not through an output. Finally, with a stable Wireless Queue VI, I don't have an issue with identifying required inputs -- it's intuitive (at least to me and my colleagues).
Bottom line -- I offered this here because the original poster was interested in queues and learning about some of the features and foibles they present. I thought this might further pique his/her interest. Feel free to use, or not, as it suits your style. I'll tell you that when I've taken a large project with multiple parallel communicating loops originally coded with wired Queues and replaced them with Wireless Queues, the Block Diagrams became much clearer, cleaner, easier to understand and follow, and just plain worked well.
BS
11-26-2014 03:37 AM
@Bob you are very much right .. that peeks my interest.
if i understand correctly, you just wrap your queue in a case structure and then in a vi.
is your Q unnamed because of example or do you manage with only one Q and have your element-type like string/enum + variant?
regards j (am he)
11-26-2014 08:58 AM
You know, you raise a good point that is a potential "flaw" in my Wireless Queue implementation. I was going to say that because my Queue was encapsulated in a single "named" VI that was callable "everywhere", I didn't need to name my Queue. That is certainly the case if I only call it with the Obtain action once -- the Queue is unique to the Action Engine. But what if I call Obtain more than once (which could be the case where asynchronous routines need to start "filling" the Queue and want to be sure that it exists first)? Certainly, if I use a Named Queue, there's no problem with doing this, but I'm not 100% certain that this would be true if the Queue were unnamed.
A Perfectly Safe Neat Addition to this routine would be to change the Obtain case to "auto-name" the Queue -- a name that should be unique would be the name of the Queue VI itself, which one could get from a VI Property Node.
It's interesting -- no matter how much I think I know about some aspect of LabVIEW (like Queues), there is always more to be learned, particularly from participation in Forums like this one!
Bob Schor
11-26-2014 09:33 AM - edited 11-26-2014 09:38 AM
You could use a generated GUID to name the queue, but since you've got them in an FGV anyway there are better solutions. One would be to get rid of the obtain function and instead have a case structure which operates using the "first call" primitive. That way whoever sends the message first also obtains the queue. This could cause issues if your FGV has a "release" function. Another option is that you use the comparison >> not a refnum function to determine if the queue was already obtained, and if the refnum is valid you don't obtain a new queue.
This brings me back to my previous point about maintainability. Even though you say you have a stable template, you've just found a bug. Over the years it sounds like you've made tons of these from that template. Do you go back and fix those others?
11-26-2014 09:51 AM
With any piece of code (including "wired" Queues), bugs can occur. In the case of a double "Obtain" call, I don't know if it will cause a problem, therefore I plan to test it and if it is an issue, to deal with it (probably using something similar to testing if the Queue already exists). There are really good reasons to leave the basic "Obtain/Release" logic intact, if for no other reason than to maintain a parallel with the "wired" variant. Furthermore, I'm quite certain that most of my code has only a single "Obtain" call, hence is "immune" to this potential issue.
But certainly, when/if I determine that this is a "flaw", I will certainly fix it in all of my code. It will be extremely easy to find them all, as there is a naming convention that I use that allows me to find all of my Wireless Queues using a simple Windows search. Compare this to looking at several LabVIEW Projects (some of which might be in pre-"Project" version of LabVIEW, e.g. LabVIEW 7.0) and looking for all of the wired Queues.
Again, this works for me, I like it, I think it has advantages that fit my programming style, and no serious drawbacks. I'm not trying to "sell" this idea, and am happy to stop beating this not-dead-yet horse.
Have a very happy Thanksgiving with your family and friends.
Bob Schor
11-26-2014 10:42 AM
Bob, if that's the method you like, there's another variant of it which you might like - you basically take your FGV and split it into several VIs. It basically works like this:
Some pros:
Some cons:
Haven't really tried it myself, because if I have singletons they're usually really singletons and have nothing else in the system with the same functionality. If I do want something like this which would be wireless, the API usually gets a name input which identifies the specific instance it works with, which it then loads. I expect it should work, though, if that's the style you prefer.
11-26-2014 11:24 AM
Dear Smithd,
You did me a real service! Although I do not (so far, anyway) "Obtain" the same Queue more than once, it is a possibility. I just wrote a test program to do the following: Obtain, Enqueue, Enqueue, Obtain, Enqueue, Enqueue, Release, and do the same cycle again (why not test "outside the box"?). Each Enqueue had a unique element (I used the unimaginative sequence 0, 1, 2, 3, 4, 5, 6, 7) and I had a parallel loop with two sequential Dequeue routines (set to exit when the Queue they were waiting on vanished, giving Error 1122).
When I tested my existing code, it broke -- I was looking for the Queue Status to show me an empty Queue on the Producer side, and it didn't happen! So I added to the Obtain code a check for an existing Queue reference, in which case "Do Nothing". Now everything works!
Note to interested readers -- for the purpose of exposition in an earlier post, I didn't show an additional output, "No Queue", of my Queue Action Engine, set when the Error Line has Error 1 (no Queue) -- I clear the error (using OpenG Filter Error) and can use the Boolean to, for example, have a "Wait for Queue to be Created" loop preceding my Dequeue Consumer Loop.
So how big a deal is fixing this in my entire code base? I've got 68 Projects (defined by a LabVIEW Project File -- I'm ignoring the LabVIEW 7.0 code which predates me, and is much too "messy" to worry about -- I've basically functionally replace it with "modern" LabVIEW code) containing 8683 VIs. There are 36 Wireless Queue VIs among them -- it took me all of about 2 minutes to compile these statistics (using searches for File Names). I'll have it all done before the beginning of next week, as I only need to worry about those 36 VIs, not where in the voluminous code base the Queues are used.
Another Good Lesson (which I, in fact, did, but not with sufficient rigor, as I didn't consider Double Obtains nor Double Releases) is to Always Write a Test Routine for Your Clever Algorithm. [I discovered/remembered I'd already written Test Wireless Queue, but that tested the ability of multiple Producers "feeding" the single Consumer, with, as the rest of my code, only a single Obtain, so today's Test Code became Test Wireless Queue #2].
BS
11-26-2014 11:29 AM
Dear tst,
Thanks for the suggestion -- it sounds like the OO approach to the problem (and I'm trying to learn when/how to use LVOOP). I will keep this in mind for future development!
BS
11-26-2014 11:52 AM
Bob_Schor wrote:
Thanks for the suggestion -- it sounds like the OO approach to the problem (and I'm trying to learn when/how to use LVOOP). I will keep this in mind for future development!
It is definitely NOT OO. OO would be writing the functionality once and then giving the specific instance to operate on to the function, which is exactly what the queue primitives do - you have a single function and one of its params is the queue ref.
The second thing I described (which I actually do) could be considered more OOPish, because I do give a specific instance to the function (as a name, and it finds the instance internally), but that requires a separate VI for each data type, which is again not exactly OO.