LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Using Dynamic User Events "across" VIs

In this attached simple LLB, I employed Aviolaitis strategy in an example for deploying user events between VIs.
To demonstrate:

1) Run VI
2) Click 'Open SubVI' button which open 'test_subvi.vi'
3) In the image on the front panel of test_subvi.vi, move the cursor to any point on the image;
this will cause a user event to fire in main that inverts the waveform.
4) Similarly, if the user clicks the 'Stop' button in the test_main.vi, a user event will fire in the subVI
that closes the test_subvi.vi front panel and stops the subVI


Here is the issue:

If I want to fire a similar event in the main that I did in the subVI, in this case 'Invert' which will
invert the waveform, if the user event has been triggered from the subVI, it will not trigger from
the main VI. Instead, the timeout case in the main is executed. No error is present upon 'Generate User Event.'

My first thought was that I needed to reregister the user event in the main VI after triggered
from the subVI. So I tried reregistering the event in the Invert event case in
'test_main.vi' prior to generating the event. Still did not work.

I wanted to know what the explanation for the current behavior might be and how it might be
rectified. I have plenty of workarounds but I wanted to know if there is any way this can be
made to work with some simple steps.

Code is LV 7.1.

Thanks,

Don
0 Kudos
Message 1 of 17
(5,845 Views)

Don,

If I understand the issue, then I have a relatively easy solution that seems to make sense. 

You create and register for the events a single time.  So as soon as your Manager is called the events are registered for, however they are registered a single time.  So when you launch the SubVI it basically steals the event from the main VI.  There is only 1 registered event between the 2 VIs.

To fix it is pretty easy.  Whenever you are calling your manager in an init style you register for the events.  So in the example simply move the register out of the case structure.  Now each VI has its on instance of the registered events.  You have registered to watch the single event in multiple locations. 

Once I did that it seemed to work great.

Message 2 of 17
(5,840 Views)

Hi Evan:

 

I must be missing something.  Because I think I effectively tried what you are saying by the

0 Kudos
Message 3 of 17
(5,824 Views)

Sorry that last message came thru as it did (don't know why it did that!).  Anyhow, if you see the version I have below, it effectively does what you say, reregistering the event.  I tried this originally but did not rewire to the dynamic event terminal in main and I think that was the problem.  By doing this, it seems that has allowed things to work.  Now, however, the 'Stop' button in Main does not stop the subVI using the same user event strategy.  Since I can move back and forth between mousing up in subvi and getting an invert to work in main because of reregistration, it is not clear to me why the 'Stop' User Event from main does not stop the subvi.  I seem to need another eyes to check this if you don't mind.

 

Given all these headaches, it is probably just better to work with queues as others have suggested.  I just believed User Events were a cool technology and wanted to try to extract maximum capability.

 

Sincerely,

 

Don

0 Kudos
Message 4 of 17
(5,817 Views)

Don,

I took a look at your code and here is the problem.  Every time you call the manager with an init you create a new User event.  Below explains why that breaks your code.

So you start main and get Stop1, and register it, so main is watching for stop1.  You now open the SubVI and it calls the manager and gets Stop1 back, so it is also watching for Stop1.  In main you hit stop, which calls the manager with an Init, so you get a NEW stop user event stop2.  You then fire Stop2.  The problem is that the Sub was registered against stop1.  Basically you have a user event leak.

Now, this is not a problem with the invert because of this.

You run main and get invert1.  You open the sub and get invert1.  Now in the main if you hit invert you create a new Invert event, invert2.  But it works because you wire the events to the event structure so now main is watching for invert2.  Now over to the subVI, when you cause the invert it looks into the Manager, but does NOT call an init so there is no new user event.  So it simply fires the Invert2 event and is all good.

So basically you need to always return the same Stop user event, and not create the new Stop2 user event.  Or figure a way to update the subVI to know a new Stop has been created.  A down side to continually creating new user events is that you end up leaking them.  What you really want to do is only create the user events 1 time.  Then give all the subVIs those references.  If the subVI wants to watch for them it should register the events on its own.  You could remove the register from your Manager, and simply place that code at the init stage of your V

Message 5 of 17
(5,813 Views)
I think what I am trying to do then is too messy with user events.  I have reformatted by using a single queue communicating across VIs. 
 
Note that there may be a cleaner way to shut down the subVI when the user presses 'stop' from the main VI.  Right now, in the main, the 'stop' will abort the subvi and close its front panel.  I'm still experimenting.
 
Note also that I normally handle all actions in one state machine event loop, but I decided to get some practice with the producer-consumer architecture.
 
Sincerely,
 
Don
0 Kudos
Message 6 of 17
(5,780 Views)
Don,
 
As far as the switch to queues from user events, it just boils down to what fits your application best and what works.  The queued producer/consumer is a great architecture to use.  In some of mine I actually mix it with user events, but that is a different story.
 
I have a suggestion for you though if you interested.  I would simplify the data type of the queue and make it a strict type defed enum.  The enum then has all the states that you want to watch.  If you have an event that should then fire multiple events you simply Q the events up 1 at a time in a loop in the producer.  The reason for this is that in your code you can only stop after all the events have finished.  There may be times that you want stop due to an error, or redirect the consumer. 
 
So if it simply consumes 1 item at a time you can place items at the head of the queue, such as the stop.  This also allows you to remove the for loop you have because it becomes un-needed.
 
I realize you code is an exampe so I am not sure how this translates, but I generally have a big type defed cluster in my consumer loop that has all the data I will need.  I then use shift registers to pass it around.  I do not use the queue to transfer the data.  In my mind the producer simply creates events and the consumer acts on them.  Therefore the consumer is the only loop that really has a cluster of settings.
 
Let me know if that made any sense or if you have any questions.
Message 7 of 17
(5,767 Views)

I did not address error handling in this simple example.  Normally if an error is incurred, the program would be directed to an error case by inserting a 'go to error case' action into the queue.

 

If you don't mind, refactor my simple VI to better explain your handling of producer-consumer as you described in the last message and post.

 

Thanks,

 

Don

0 Kudos
Message 8 of 17
(5,761 Views)

...ps.  Would also be interested to know where it makes sense to also use User Events within a queue-driven architecture if you can define actions you want in the enum type def and just feed them to the queue for execution when needed.

 

Thanks again,

 

Don

0 Kudos
Message 9 of 17
(5,757 Views)
Without incorporating the error handling to be able to insert errors at the top of the queue stack, I think I implemented your suggestions.  This all looks similar to my old single-loop state machine architectures of years back where I used two shift registers, one for references and one for controls, and bundled / unbundled by name as needed.  One thing you need to be wary of.  You can break LabVIEW if the controls type def is large enough and you need to pass it to a subVI.  I wrote a whole thread on this some years back at info-labview user group.
 
A few questions:
 
1) do you recommend producer-consumer architecture also for subvis that show their front panel, and if so, do we need to have a separate named queue for each subvi different from the main VI (else how do we not encounter race situation between different 'Dequeue Element' fxs associated with the same named queue and running in parallel?)?
 
2) Is there any way to remotely (from another VI) trigger an event structure case? I don't know of one or maybe I am just having a bad day.  I would like to find a way to more gracefully close and stop the subVI from the main VI than my present method (aborting VI).  The only way I could envision doing this while keeping the same event-based architecture of the subVI is through this abort.  But if I could trigger an event (such as 'Stop' in the subVI), a more graceful stoppage could occur.
 
Sincerely,
 
Don
0 Kudos
Message 10 of 17
(5,730 Views)