From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Is it possible to get the element data type of a Queue from itself?

Solved!
Go to solution

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?

0 Kudos
Message 21 of 30
(1,205 Views)

@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).

 

Queue Call.png 

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

0 Kudos
Message 22 of 30
(1,191 Views)

@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)


If Tetris has taught me anything, it's errors pile up and accomplishments disappear.
0 Kudos
Message 23 of 30
(1,158 Views)

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

0 Kudos
Message 24 of 30
(1,146 Views)

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?

0 Kudos
Message 25 of 30
(1,132 Views)

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

0 Kudos
Message 26 of 30
(1,119 Views)

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:

 

  1. Create an lvlib to contain these VIs.
  2. Create a typedef in the lvlib for the data type.
  3. Create something to hold the queue ref and add it to the lvlib. This could be a private global, an FGV, etc.
  4. Create a VI for each action in your FGV and give it the relevant inputs and outputs. Inside each VI (with the exception of the obtain), you start by using what you created in the previous line to get the queue ref.
  5. Duplicate this lvlib for each queue that you want, change the typedef and icon and you're basically done.

Some pros:

 

  1. Each action is a VI which has clear inputs and outputs dedicated to it.
  2. Finding where you perform a specific action becomes easier.
  3. The typedef is now managed along with the other files instead of as an external file.
  4. You don't need the actions typedef.
  5. You can set the identity for the queue by setting the lvlib icon, which applies to all the VIs.

Some cons:

  1. Creating it might be more difficult (although in practice, it's probably easier - you can either do a save as copy on the lvlib or you can create a small tool which will do it for you and also allow you to edit the icon and description, etc., at the same time).
  2. It can be more difficult to find out all the places where you interact with the queue, because you have to check several VIs.
  3. You get more files, which takes longer to load, etc. There are also potential editor stability issues with lvlibs.
  4. You would have to write it so that it functions correctly (e.g. getting the queue reference, setting reentrancy, etc.).
  5. You get a lot of code duplication, but that happens with your current FGV too.

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.


___________________
Try to take over the world!
0 Kudos
Message 27 of 30
(1,105 Views)

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

0 Kudos
Message 28 of 30
(1,099 Views)

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

0 Kudos
Message 29 of 30
(1,098 Views)

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.


___________________
Try to take over the world!
0 Kudos
Message 30 of 30
(1,094 Views)