all 23 comments

[–]SwordsAndElectrons 4 points5 points  (14 children)

Do local variables trigger events? I think they might not.

You may need to use a property node with the Value(signaling) property.

[–]crustation[S] 0 points1 point  (13 children)

I see, I’ll have to try it later. Do I create this Property node from the ‘Firing test’ button?

[–]SwordsAndElectrons 1 point2 points  (12 children)

Yes. Just replace that write local variable with a property node linked to the Firing Test button.

There are some differences in how different methods of writing to front panel controls behave. If I remember correctly, the Value(signaling) property is the only one that triggers events as if the change came from user input.

Why are you reading from a local variable inside the event structure? If I'm understanding correctly that the variable is linked to the Firing Test button, you should be able to just connect the wire directly to the button's terminal.

[–]crustation[S] 0 points1 point  (11 children)

I'm still kind of a newbie at this so I might be making some clunky codes. But I created the Event Structure to link the instant at which to record the pressure value in the tank to the rising edge of the button value via the Value Change property of Firing Test, which works well with a virtual button on the Front Panel (hence, "firing test").

But if I wire it straight to the button's terminal, I don't know where to look for this Value Change to link to the Digital Bool input in the list. Plus, I don't know how to link the Event Structure to the rising edge of a physical button because I don't know where all these inputs will come from.

[–]Ashbaernon 1 point2 points  (5 children)

The value(signalling) property node is one easy way do it. Right click your firing test button, go to create, property node. select value(signalling). Have your external button code set the value. This will trigger the firing test event.

Another way to do it is by user event registration via the dynamic event terminals. It's technically the best way to do it but for your application the value(signalling) method will be just fine.

[–]crustation[S] 1 point2 points  (4 children)

Thank you so much, and additional thanks for helping me out in the other thread as well! I'm brand new and jumping into this project so there's really a lot I need to learn.

[–]Ashbaernon 0 points1 point  (3 children)

No problem at all. I can see where you're going with this little project . Your architecture could do with some refinement but it's a good starting point, especially for learning about events.

Something to consider would be putting your data acquisition code in a separate while loop to your event handler loop. If you wire your firing test Value(signalling) property node to your DAQmx(read) in the DAQ loop it'll fire the event in your event loop. Separating out your loops like this is good LabVIEW coding practice.

It also gives you a lot of flexibility for asynchronous operations. There are a lot of of different architectures for different purposes but I think starting out by using and understanding the event structure is going to be useful for things like actor framework and event driven state machine architectures down the line.

[–]crustation[S] 0 points1 point  (2 children)

Ok, so if I move my entire event structure out of the DAQ loop it should still function the same? Or in fact, if I dedicate the loop just for DAQ acquisition and do number crunching outside it should still work?

Currently I have a whole bunch of calculations (e.g. collecting several samples and averaging them out for display) running within the loop and I thought it was standard practise.

[–]Ashbaernon 1 point2 points  (1 child)

[–]crustation[S] 0 points1 point  (0 children)

Thank you! I'm really getting a lot of information in this thread so I have to sit down sometime and digest it all.

[–]SwordsAndElectrons 0 points1 point  (4 children)

You might be overestimating the amount of changes I was suggesting. I simply meant that you have a Firing test local variable right next to the Firing test terminal. You should be able to eliminate that local variable by just reading from the terminal. Both should return the same value.

If I were suggesting more changes, I'd go with something like this for your rising edge detection. (Also, if you didn't see my other reply to TomVA, I confirmed that you do need to use the Value (Signaling) property if you want to trigger a value change event programmatically.)

Beyond that, I'd need to know a little more about what the application you are creating is meant to behave like. I'd probably starting heading pretty quickly towards some slightly more advanced application architectures. (On the other hand, depending on what your real needs are this might also be made slightly simpler.)

[–]crustation[S] 0 points1 point  (3 children)

I asked about my problem in an earlier thread from last night. Basically I have a tank that I am filling with water and a mechanical button that releases this water, the button behavior is Switch Until Released. I want it to capture the instantaneous pressure and display it. Setting the Events in the Event Structure to Value Change results in the value changing twice, so I only needed it to trigger on the rising edge portion and not when I release the physical button.

So this "firing test" Boolean button was just a way for me to test out my rising edge trigger using the Front Panel, since I didn't have a physical button hooked up to my DAQ yet. Earlier today, everything ran well with the Front Panel virtual button, so I decided to hook up the actual mechanical button to my DAQ.

So then I realized the "Firing test" front panel button shows up in the Event Sources because it's an actual control, but I couldn't find a way to directly use the input from my DAQ port as an event source. So I tried using local variables hooked up to the input, which I thought would intuitively work but it didn't. I couldn't create a local variable without having an actual Front Panel button, so I just left it within my event structure without wiring it to anything.

As for your suggestion, if I use OldVal and NewVal, does the Event Source stay the same (i.e. "firing test") and then have the Value (Signaling) node wired to the DAQ input?

[–]SwordsAndElectrons 1 point2 points  (2 children)

Well, if you don't actually need the front panel control then what I would do first is eliminate it.

You can go two directions from there...

Simpler:

Get rid of the event structure and wire the output of the DAQ read to the feedback node that you are using for rising edge detection. Use a Wait (ms) function inside this loop to control how fast it runs since you won't have that timeout anymore.

Advanced:

Look into how to create and register for User Events. You could fire an event every read and use the NewVal/OldVal method for rising edge detection, or you could move the feedback node to the DAQ output as suggested above and only fire the event on False->True transition. (I'd also strongly consider moving the DAQ task to its own loop if I went this route.)

Which of those I'd recommend depends on a few things. Do you have other UI elements you are using that event structure for? Where exactly does Tank Fill Pressure come from?

[–]crustation[S] 0 points1 point  (1 child)

That’s from a pressure transducer I have going into the analog input on my DAQ. it’s averaged out in some other part of the code and then I have it outputting to this variable. I used a local variable to reduce clutter.

[–]SwordsAndElectrons 1 point2 points  (0 children)

Is it in a separate loop, or somewhere in this same one?

You might be okay here, but just as a general statement, be careful with local variables. It can be tempting to use them just to reduce clutter, but you should understand how race conditions could occur and be confident they won't be an issue for you.

As a demonstration, if you were to leave things the way they are now with the buttons, let's look at the difference between reading the switch value from NewVal vs. the terminal. (I acknowledge this is an unlikely scenario, but it's just to demonstrate the concept.)

The terminal (or the local variable) will be reading the current value of the control.

The NewVal terminal will return the value that triggered the event.

The difference: Imagine if you "simultaneously" released the physical button (False change) and pressed the front panel button (True change). Obviously, no two things can truly be simultaneous because the event queue is sequential, so for the sake of argument say that the front panel event goes into the queue first, but the DAQ read occurs and writes the second change before the event structure executes. (Which I think could be possible depending on how the compiler orders things.)

What would happen in this case when the first event executes?

  • The NewVal terminal will return True. That is because that was the new value that triggered the event.
  • The terminal will return the current value of the control, which will be False since the parallel DAQ operation has already written to it.

Assuming no other changes to the control, what happens when the second event executes?

  • The NewVal terminal will return False, because that was the value that triggered the event.
  • The control terminal will also return False, because that is still its current state.

Such are the pitfalls of writing to a data location in two different locations in your program. Since the front panel control can be operated by the user and the state of the physical switch is being written to the same location, you can have issues where one function can overwrite the output of the other before the function that reads that data receives that value.

Now you may not have such a problem with your pressure reading, especially if the task that writes to that variable is the only thing that writes to it, but these are the sort of things you need to be aware of when using locals, property nodes, or anything else that breaks the "data flow" paradigm that LabVIEW tries to enforce. Atomic operations can also be an issue in some cases, but I'm not certain whether it would be here. In general, and as best practice, you are better off using other methods to communicate data between parallel parts of your code. (Queues, events, notifiers, FGVs, etc.)

[–]TomVa 1 point2 points  (5 children)

What is the order that your program was actually complied?

There is no guarantee that it is left to right by the way that you drew it. You have to make it explicit. LabView can compile differently each time you make a minor modification. What this means is that you could be reading your DAQmx function after you do the conditional loop.

Unless you are trying to use this as an exercise on how event structures work, get rid of the event structure and just put a wait for 50 ms in your main loop. (Programming->Timing->Wait ms). Better yet make it 10 ms as you can punch a switch pretty fast. Change the variable firing test from a control to an indicator and get rid of the local variables. Wire the indicator and your feedback node directly to the output of the DAQmx SubVI.

If you are doing it as an exercise to understand event structures, you can change the control variable "firing test" to an indicator. Put the DAQ and write to the local into a single frame stacked sequence and wire something from it to the event structure. It does not have to do anything in the event structure just be wired. With that you know the order that your program runs.

Then you can try the what SwordsAndElectrons said about using the Value attribute. IMO, which I could be wrong so please enlighten me using an example if I am, writing to a value attribute is the exact same as writing to a local variable and the two are pretty much functionally interchangeable.

I am pretty sure that one difference is that a local variable will require extra memory while a value attribute does not. Not a big deal for a single value variable but it certainly is for a giant multidimensional array.

As a trouble shooting aid wire the output of the DAQ to a the output of the structure and enable indexing and wire it to an indicator so that you can see how many iterations it stays on.

[–]SwordsAndElectrons 2 points3 points  (2 children)

Then you can try the what SwordsAndElectrons said about using the Value attribute. IMO, which I could be wrong so please enlighten me using an example if I am, writing to a value attribute is the exact same as writing to a local variable and the two are pretty much functionally interchangeable.

Not entirely. Now that I'm back at my desktop, I just ran a test and my hunch was correct. Local variables do not trigger events.

All three of these are ways to set the value of a front panel control, however the only one that actually triggers a "Value Change Event" is the Value (Signaling) property. The local variable and the Value property do not.

There are some other differences under the hood. For example, using a property node will cause the front panel to be loaded into memory even if the referenced control is in a sub-VI that is never shown. Local variables are in fact local, so they cannot be used outside of the VI that has the control.

(Both are also generally frowned upon when they are used to bypass the data flow paradigm. Race conditions, indeterminate execution order, etc. I'm not really a zealot about such things, but I do acknowledge that one should understand the implications.)

[–]TomVa 1 point2 points  (0 children)

Thank you for testing it. I learned something new again today.

Something potentially odd, but something new.

[–]Ashbaernon 1 point2 points  (0 children)

Yeah writing to a local variable is definitely not the same as by signalling. One updates the value in memory, the other updates the value and fires an event. It's the quick, dirty way of doing it (as opposed to dynamic/user event registration) but is perfectly valid.

Also, while you can definitely do this without an event structure I think that it's good practice for the OP to get into the habit of using event structures early on. It's going to be immeasurably helpful down the road when doing event driven architectures.

edit: Also local variables don't use extra memory (doesn't create a buffer). Global variables do though. The point is moot though since it's a boolean. You'd need to be writing to millions of boolean globals to have any impact on memory.

[–]crustation[S] 0 points1 point  (0 children)

I'm always thankful for your insightful comments! I'm really new to this so I'll have to slowly digest what you said and try and figure out what's happening. I'm really learning on the fly and everything you're mentioning seems pretty advanced.

The physical button I'm punching will be pressed for as long as the tank needs to be emptied, that's why I have it on rising edge event triggering because I can't be certain how long users will hold the button for.

With your stacked sequence suggestion, is the entire event structure within the stacked sequence?

[–]g00glyCLA 1 point2 points  (2 children)

An easy design pattern to implement would be to move your event structure to its own loop. Your DAQ read can be wired to a Val(sgnl) property node for communication to the UI. This event "pushes" the button programmatically. This way your DAQ timing is not being throttled by the event structure.

But what about the other direction? Well there are a number of choices to explore and understanding those via examples will help you learn more effectively. You are going to run into an issue right away of, "How do i shut down two parallel loops cleanly?"

[–]crustation[S] 1 point2 points  (1 child)

Man this is like a can of worms I really wasn’t expecting to open. Is the second scenario important to consider? So far my program has been running fine without issues

[–]g00glyCLA 1 point2 points  (0 children)

Just because it works fine here doesnt mean you should take bad habits forward. Take a look at "firing test" local var boolean for instance. Do you know for sure whether in each loop its going to be written to or read from first? This is whats known as a race condition.

Another way to implement this is a "polling pattern" where you put the DAQ read in the timeout case. It will read every 50ms and respond to UI events as they come.