LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Multiple Event Structures triggered from one event...

Is it okay to have multiple Event Structures triggered from one event?

 

For instance:

  1. A user presses a button
  2. That triggers the GUI loop and the Main Control loop to do something.

Also where do you put the terminal that is triggering the events?

  1. In the GUI loop
  2. In the Main Control loop
  3. Does it really matter?

 

I know this works but is it a good idea?

 

Should I do this instead?

  1. A user presses a button
  2. That triggers the GUI loop
  3. The GUI loop triggers the Main Control loop
========================
=== Engineer Ambiguously ===
========================
0 Kudos
Message 1 of 12
(4,025 Views)

@RTSLVU wrote:

Should I do this instead?

  1. A user presses a button
  2. That triggers the GUI loop
  3. The GUI loop triggers the Main Control loop

Yes, for the sake of decoupling parts of your code you should be doing this instead of having multiple loops registered for a GUI event.  Now if you were sending a User Event to multiple Event Structures, that is a completely different story since the User Event is more of a messaging mechanism than a GUI handler.


GCentral
There are only two ways to tell somebody thanks: Kudos and Marked Solutions
Unofficial Forum Rules and Guidelines
"Not that we are sufficient in ourselves to claim anything as coming from us, but our sufficiency is from God" - 2 Corinthians 3:5
Message 2 of 12
(4,007 Views)

I agree with Crossrulz on both points (though most of the time I don't use User Events).

 

Bob Schor

0 Kudos
Message 3 of 12
(4,002 Views)

Hello!

 

If the question is pertaining to the possibility, then my answer would be: Yes, handling one event in multiple event loops is possible, but only when using user events (see the manual entry of the "Create User Event" function for a code example of user events). Using multiple event structures on their own to catch the same event, e.g. a button press, does not work: The event is caught by only one of the event structures.

 

As far as my experience goes, it does not matter where the terminal of the button resides if you just want to catch the event. It is, however, good practice to put it near to where it is used. If you put it inside the event structure that e.g. catches its value change, the new value should already be availabe inside the case, which to me seems beneficial and intuitive. If you are using the user event system to forward your button press, I would recommend putting the terminal close to where you are actually emitting the user event. If you are using a dedicated GUI loop, I would emit from that loop and then have the main loop / state machine react to that event, just as your bottom example.

 

Your last example sounds quite like the idea behind a producer-consumer loop. It is not only a good idea, but actually such a great idea that labview offers this as a pre-made template vi accessible when creating a new file. Also, check out the tutorial here: http://www.ni.com/tutorial/3023/en/

 

Best,

0 Kudos
Message 4 of 12
(3,999 Views)

@LLindenbauer wrote:

Using multiple event structures on their own to catch the same event, e.g. a button press, does not work: The event is caught by only one of the event structures.

 


This is incorrect. While it's probably a "code smell" it will work, sometimes. *Filter* events will only be read by one structure, but not *notify* events. See this KB article:

 

https://knowledge.ni.com/KnowledgeArticleDetails?id=kA00Z0000019KnjSAE&l=en-US

 

Also try this snippet. Both structures will see the button press.

 

2event.png

 

That said: I agree with everyone else that it's poor practice to actually DO this. For the snippet above, for example, you could only read the value of Button in one event structure (since it's configured as a Latch action). You could read that the button WAS pressed, and you can use the NewVal terminal to see the new value, but the actual "Button" terminal (which needs to be read to reset the Latch action) can only go in one place.

 

To the OP, a producer-consumer is definitely the way to go here. Just because you *can* do it doesn't mean you *should* do it 🙂 A message handling architecture will be WAY better.

 

Imagine this scenario: your GUI loop and your DAQ loop are both monitoring the button press for "Stop acquiring data". Let's pretend your DAQ code is attached to a device that takes 5 seconds to return data, and for some unfortunate reason your DAQ loop blocks during each data read. The user clicks the "Stop acquiring" button. The GUI loop now thinks the acquisition is stopped. The DAQ loop doesn't even see the button press for 5 seconds, so your loops are now out of sync. You'll have to add code to message between the loops to fix this sync issue anyway, so might as well do it from the start.

 

Now you have your GUI loop catch the button press and can send a message to the DAQ loop to stop taking data. And the fun thing is, now your DAQ loop can be used in other code as well, since it's just tied to a queue/notifier/User Event/channel wire/Actor Framework message/etc, so it's now decoupled from the GUI loop. Benefits all around!

 

I can't say that it's NEVER a good idea to have multiple loops access the same event, but I can say I've never needed it (not that I'm the authority on this, lol). I would try very hard to not do this unless I had a really great reason to do it. Possible reasons might be a global shutdown signal or a GUI toolkit that perhaps changed the color of a button on mouseover (like in an XControl or QControl).

Message 5 of 12
(3,972 Views)

Thanks everyone, I didn't think what I was doing was a good idea even though it works...

 

I am not sure how a Producer/Consumer fits in here... Actually I am using Prod/Con in my DAQ loop but don't understand how I would use one in this case.

 

Basically I am trying to refactor a "universal" test program I wrote years ago that is a simple State Machine design. Where everything is rather linear and all of the individual steps are time based. Into a more flexible architecture so test steps can be based on conditions and values.

 

Although my understanding of Notifiers is less than Prod/Con I can almost see where something like that could be useful passing information between loops.

========================
=== Engineer Ambiguously ===
========================
0 Kudos
Message 6 of 12
(3,926 Views)

"Producer/consumer" is probably the wrong term for it. You want a "messaging" architecture. I think the Delacor DQMH has a pretty solid reputation, though I've never used it. I'm a big fan of the Actor Framework for this kind of thing but it has a pretty steep learning curve, and you need to be proficient with OOP before you tackle that one.

 

Think "producer/consumer" of *messages*, not of *data*. The GUI loop would "produce" messages and the DAQ loop would "consume" them. Probably a poor choice of words 🙂

 

And unless your message rate is really slow, don't use Notifiers, use Queues.

 

Another tidbit that gets lost sometimes: Your loop should still be a state machine, but remember: incoming messages are NOT states! Keep your state internal to the state machine. Try to think of messages as "requests" for the state machine to do something, not a task it will be "told" to perform.

 

For a DAQ example: a message would be something like "start" or "stop", whereas states would be "idle", "acquiring", "error", etc. In the Idle state, getting a Stop message should do nothing. In the Acquiring state, a "Start" message would do nothing. (Just examples, you might want to return an error or something).

0 Kudos
Message 7 of 12
(3,914 Views)

@OP

I use User Events to communicate between loops. It works really well for me. I like using them for their flexibility. For example, I can have a DAQ loop that acquires DAQmx data, it uses the "N Samples" Event, in the same event structure I can also receive messages for other loops, no separate queues, channels, etc.

 


@BertMcMahan wrote:

 

Another tidbit that gets lost sometimes: Your loop should still be a state machine, but remember: incoming messages are NOT states! Keep your state internal to the state machine. Try to think of messages as "requests" for the state machine to do something, not a task it will be "told" to perform.

 

For a DAQ example: a message would be something like "start" or "stop", whereas states would be "idle", "acquiring", "error", etc. In the Idle state, getting a Stop message should do nothing. In the Acquiring state, a "Start" message would do nothing. (Just examples, you might want to return an error or something).


@BertMcMahan

 

I think I understand what you are saying, but don't know how you would implement it. If your message doesn't go to a state, what would it do? Using your example, assume I am in an Idle State. I send the Start command, which starts the DAQ. If the message didn't go to the Start state where does it go?

 

mcduff

 

 

0 Kudos
Message 8 of 12
(3,883 Views)

There is some overlap but it's important to keep the distinction, at least conceptually. Basically, you want your state machine to receive a message and respond accordingly. In this example, I'd use something akin to the following (ignoring error handling for simplicity):

 

States:

-Not acquiring data

-Acquiring data

 

Messages from GUI to DAQ (implemented as a single user event with an enum/variant message/payload structure):

-Start acquiring data

-Stop acquiring data

-Exit

 

Messages from DAQ to GUI (probably implemented the same way):

-New data available (payload would be a waveform of some new data)

I'd keep my State in a local shift register along with other "local" stuff, if any.

 

I also like to use the N Samples Acquired user event, so I'd probably set all of this up with an event structure.

 

It's important to remember that a "state" is usually better represented as a concept, rather than thinking of a "state" as a Case Selector value. The state machine, as a whole, "has" a current state, and "does things" depending on what that State is. While it CAN be a single case selector value, it's not always the best way to go.

 

Continuing this example, I'd have a While loop around my Event structure. That event structure would have event cases of "N samples available" and "User event message received".

 

Let's talk about User messages first. In that event case, you use the enum to decide what message came in. Deciding what to do with that message depends on what state you're in, so you need to read your State internal variable (value in the shift register) and the message. Both will need case structures; it's up to you to decide what to do first. I'd probably default to first using a case structure on the Message type. So within the User Event that handles messages, you have:

Case structure: Message type

-Message: Start acquiring data

---Case structure on current State:

-----Current State: Acquiring data: Do nothing (you're already acquiring data)

-----Current State: Not acquiring data: Call Initialize DAQ subVI.

--------If No Error: New State = Acquiring Data, plus configure N Samples event to become "live" now.

--------If Error: New State = Not acquiring data

-Message: Stop acquiring data

---Case structure on current State:

-----Current State: Acquiring Data: Call Stop DAQ subVI. New State value: Not acquiring data

-----Current State: Not acquiring data: Do nothing (already not taking data)

-Message: Exit

-Always just call Stop DAQ subVI and clear errors (it will error if not acquiring, but that's OK, we're stopping. Stop Event Structure.

 

Basically, you want to avoid the trap of calling a message handler a state machine. Look up the myriad discussions on the pitfalls of the Queued State Machine- it's a flawed design from the start. A State Machine with a Queue is great, and a Queued Message Handler is great, but if you have a loop that accepts incoming messages, then runs a specific case structure frame all the time, that's not a state machine. That's just a complicated way to call subVI's. Notice above that "Start" just calls a subVI; there's no reason to make the whole loop go to a "Start" case structure value. In a more complicated system, you might have "Initializing" be a State, but if "Initializing" leads to "Acquiring" every single time, and nothing else ever leads to Acquiring, there's no need to make them separate States.

 

If a given structure calls a specific VI when sent a specific message, every time, just use the subVI.

 

I hope this makes sense. If not I could probably make a quick example. (Looking back I probably could've made the example in the time it took me to write this out!)

 

Message 9 of 12
(3,876 Views)

@BertMcMahan wrote:

Thanks for the in-depth explanation. It will take a while to digest. Not being a programmer (no formal programming education, I am a chemist by training) I am always on the look out as to what I am doing wrong.

 

I like to "wire" together JKI State Machines, a string based message handler, with user events. You are making me think about my message "payload". Thanks.

 

mcduff

0 Kudos
Message 10 of 12
(3,861 Views)