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.
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.
04-22-2010 08:29 AM
LabBEAN wrote:Were you thinking of unregistering while handling the event that you caught and then re-registering?
No, and I'm guessing this doesn't work as you expect. One problem that immediately comes to mind can be illustrated like this: Add a 2 second wait before unregistering. Presumably all the events that happened before the unregister are still processed, thus missing the point.
Allusion to the Brain, LabVIEW Everywhere (Touch Panel, PDA, RT, FPGA, ADI Blackfin, ...), or both?
I used to have a habit or changing my avatars regularly and using some relevant sentence as a signature. At some point I got stuck with the Brain.
04-22-2010 08:49 AM
tst wrote:
No, and I'm guessing this doesn't work as you expect. One problem that immediately comes to mind can be illustrated like this: Add a 2 second wait
before unregistering. Presumably all the events that happened before the unregister are still processed, thus missing the point.
Execution order:
Dynamic Event Registration for Value Change event on sliders
Inside the Value Change event case, unregister for the Value Change events (no other code inside that event case)
Go read sliders and then enqueue hardware loop to write new DAQmx task values
After writing is complete enqueue back to the GUI loop to re-register the value change event
I first started using this method about 3 years ago, and I don't *think* I've seen any issues with it...
04-23-2010 03:42 AM
04-23-2010 09:18 AM
I haven't tried anything else, such as checking whether this clears other events from the queue (although hopefully it doesn't) or seeing what happens if you register another control for the same event.
04-23-2010 09:21 AM
JackDunaway wrote:
Finally, when a "Mouse Up" has been detected, that signals we no longer need to respond to the Mouse Move events, so wiring another Null ref (of the same type) into the Register for Events node cancels that previous registration's event queue, trashing any events that have not yet been handled. (That's an anecdotal observation, can someone confirm?)
tst wrote:
OK, a quick test seems to show that registering a null reference for an event flushes all those events from the queue, which is why this works.
Yep, one of the only functions we have available to the underlying event queue is "Flush Queue". It would be nice to have some more control over that queue - still waiting to hear back from AQ here (maybe his superiors have put a muzzle on him...)
04-23-2010 12:37 PM - edited 04-23-2010 12:42 PM
LabBEAN wrote:
1. I understand Producer Loops and Consumer Loops, but what's a Producer/Consumer loop? That being said, why the GUI Producer/Consumer Loop? It seems like an unnecessary middleman between GUI Producer and Hardware Consumer.
2. How do you force the loop iteration of the GUI Producer? With a "Ready" or "Finished" variable and a "Val (Sgnl)" event?
I would need to see this architecture in situ to agree with the 3 loops vs two, and the local with the necessity of the forced iteration. There's really no need to pass the dynamic events ref anywhere outside of the GUI Producer loop - re-registration of the Slider: Val Change event could happen within the "Finished" forced iteration.
Also, using the "Unregister for Events" seems excessive - I would prefer the cleaner syntax of wiring in a null ref to the "Register for Events" terminal - it is self-documenting by saying "I'm still holding on to this event registration ref, just nulling it out for a while when I don't want to listen". The "Unregister for Events" is more of a cue for "see-ya, shuttin' down".
04-23-2010 01:52 PM
A lot of this is probably personal preference, but after 8 years now, this is the architecture I've settled into.
GUI Producer = Event Handler
Pushing the event structure into it's own loop allows it to self throttle. The loop waits until an event occurs (the timeout case never executes). UI activity, like pressing a Boolean with "latch until released" mechanical behavior, is handled instantly (no need to finish a state and then enqueue a state to check the Event Structure). This is personal preference, but I also prefer to debug loops that aren't constantly executing "Do Nothing" cases like Timeout, Default, etc.
JackDunaway wrote:
1. I understand Producer Loops and Consumer Loops, but what's a Producer/Consumer loop?
GUI Producer/Consumer » Dequeues states from itself and other loops and enqueues states to itself and other loops
It "consumes", for example, from the Hardware loop and might display live data (e.g. sensor values) to the user, or a multitude of other tasks. It also "produces" to the Hardware, Timer, Data, etc. loops when parallel processing is necessary (e.g. File I/O, graphing, DAQ). This is the "main" loop that kicks everybody off and shuts them down.
JackDunaway wrote:
...why the GUI Producer/Consumer Loop? It seems like an unnecessary middleman between GUI Producer and Hardware Consumer.
I don't like having code in the Event Structure. Sometimes the GUI Producer/Consumer is acting like a "middle-man", but other times there are a variety of states that need to execute in various loops before enqueing down to the Hardware loop. For the sake of "sameness" and code readability, internally, we follow the guideline "keep code out of the event structure". That keeps developers from getting tempted to do too much in there. In fact, there isn't even a shift register on the GUI Producer for that very reason. It needs to be very responsive and not get bogged down processing, making decisions, handling errors, etc. This makes the code very easy to follow. Because the loop is NOT freewheeling, it's easy to debug as well. We only dequeue a state when we need to run that state (no "Do Nothing" states necessary here either).
JackDunaway wrote:
2. How do you force the loop iteration of the GUI Producer? With a "Ready" or "Finished" variable and a "Val (Sgnl)" event?
User event, Val(Sgnl), etc.
There's really no need to pass the dynamic events ref anywhere outside of the GUI Producer loop - re-registration of the Slider: Val Change event could happen within the "Finished" forced iteration.
The local is necessary because of the architecture that I've just described.
I guess I've just thought of these as two independent functions: unregister and iterate surrounding while loop. I also use "iterate" to shut the loop down when closing for a reason other than a user action (e.g. panel close). I suppose doing an extra "unregister" wouldn't hurt anything in that case. Currently only the GUI Producer/Consumer tells the GUI Producer to iterate, so when the Hardware Producer/Consumer finishes the DAQmx generation (for example), it has to tell the GUI Producer/Consumer. Again, this keeps the code structured -- only certain areas of the code have access to other areas. So, I do the unregistering in the "Unregister for Events" since I'm passing through that state anyhow and that's what seemed logical to me at the time. "Unregister" should happen immediately, so it's in the loop. The other is less critical since the ES won't get it's new event subscription until the loop iterates.
Aside: Maybe you're picturing actual "loops" on one huge diagram like my mock-up image... The other loops are in subVIs or kicked off by VI Server.
using the "Unregister for Events" seems excessive - I would prefer the cleaner syntax of wiring in a null ref to the "Register for Events" terminal
04-23-2010 01:55 PM
04-28-2010 12:33 PM
LabBEAN wrote:
This is personal preference, but I also prefer to debug loops that aren't constantly executing "Do Nothing" cases like Timeout, Default, etc...
Agreed. I don't like freewheeling loops unless there's some specific code you want executing in the timeout case. Set -1 for event timeout and delete the Timeout case.
LabBEAN wrote:
I don't like having code in the Event Structure....
OK, here's really our only fundamental difference. I prefer having rich event structures that "do a lot". If I can fit all the synchronous actions into the event structure, then I do it. Notable exceptions that come to mind are database operations, kicking off modals for user input/notification, and networking operations - those are deferred to an asynchronous consumer. As a general rule, if the task can be completed within 50-100msec, I keep it in the event structure. Keeping a "snappy" UI means something that will respond within 100-200msec - it doesn't need to enqueue actions within 1microsec of a button press.
LabBEAN wrote:
Maybe you're picturing actual "loops" on one huge diagram like my mock-up image... The other loops are in subVIs or kicked off by VI Server.....
Actually, no, I did not see a big diagram. I also find it more elegant to sometimes have a separate consumer process, especially when there are multiple producers. But with that being said, how do you pass around the dynamic events ref (if not by local)?
04-28-2010 12:45 PM - edited 04-28-2010 12:49 PM
LabBEAN wrote:
Anthony Lukindo posted something similar here. There's one small flaw that I comment about at the bottom of the article.
There's also another flaw... dropping a typedef'd constant on the BD and modifying it's value. If the structure of the typedef is ever changed, the values of all BD constants are not guaranteed to survive. And there's one more flaw... it's going to be difficult to trace the source of producer/consumer actions if the two loops are in separate VI's (not to mention how the ref will be passed around, who creates/destroys the ref, etc...)
***EDIT: Don't get me wrong, Anthony's level of detail is amazing. I'm nitpicking here.***
This method handles both problems by wrapping the queue ref, making it easy to find all operations that happen on the queue (just "Find All Callers" in project, or "Find All Instances" if your top level plus dynamic calls are open) The four actions are Create, Send, GetRef, and Destroy... your imagination can fill in the other case structures. (does anyone else do something similar?)