04-13-2023 12:38 PM
Thank you for attaching your code. From your example, it appears to me that you are in the "earliest stages" of learning LabVIEW, so I'm going to perhaps give you "more than you want to know now" in an attempt to show you (a) the power of LabVIEW, (b) some beginning concepts that you might not yet know, (c) some things that you know something about, but need to know a bit more, and (d) some really new stuff, but that is fairly easy to learn. I'll show a Snippet of working LabVIEW 2021 code in this post, and will also attach the VI that I used. Ideally, I would have made a Project with this simple VI as it contains a user-defined Enum, because (A Rule!) Every Enum Needs to be TypeDef'ed. [Why is this? Because when you want to change the Enum, here the State of the State Machine, you change just the Enum and "Apply changes" to all the places the Enum is used in the Project].
This Timed Wait Demo has 6 States: Initialize (basically does nothing here), Idle (a "resting state" after Initialize), Start (does stuff when the "Start" button is pressed), Step 1 (this, in fact, is the entire State machine, which here does nothing, but segues into the Wait state), Wait (which waits for 20 seconds, 0.1 sec at a time to let other things, like the Start or Stop button, "do something"), and Stop.
The Demo uses two parallel loops, running concurrently, in a Design Pattern that I call the "Boss-Worker Design Pattern". The "Boss" Loop is a standard LabVIEW Event Loop (which you might not yet know about). The Event Structure and Loop are relatively unique constructs in LabVIEW, which sit idle, doing nothing and taking no CPU time, until a registered "Event" takes place. One very important Event is a "Value Changed" Event for a Front Panel Control, here the two Boolean "Start" and "Stop" controls. You'll notice these are "Rectangular Booleans" from the Front Panel Palette (there is no "Start" button -- I used the "OK" button, changed its name to "Start", and opened its Properties to change the Off text Property to "Start"). When Start is released (because its mechanical action is "Latch when Released"), its value (which is True when it is "released from being pushed down", "True" is registered, the "Start" Value Change event is entered, and the State Enum value "Start" is put into a Messenger Channel Writer (more on that in just a minute). Boolean controls with the "Latch when Released" hold their value until they are read, at which point they go back to their default value, which for Booleans, is conventionally "False". [Funny story -- I didn't realize this, and set a Boolean Control to use "True" as its default value. Guess what happened when I tried to turn it on ...].
Now I have to (briefly) talk about Asynchronous Channel Wires, introduced in LabVIEW 2016. These are used to "violate the Third Law of Data Flow", namely that data don't go into or out of a Structure (such as a While Loop) until everything inside the Structure executes. Notice that the "pipe" coming out of the Messenger Writer goes "over the edge" of the Event Structure and its surrounding While Loop -- as soon as data enters the pipe, it is available everywhere else the Pipe goes. Messenger Channels can "pool" their Messages, the only restriction being "fluid flow", the data flows from Channel Writers (with the little triangle on the left side) to Channel Readers (with the little triangle on the right side). There's only one Channel Reader in this Demo, just inside the Worker's While Loop. It takes whatever is placed on the Channel Wire (essentially the next State) and outputs it as a conventional LabVIEW Variable (in this case, a thin blue line showing an integer value).
When you get this VI and look at the other States, you'll see that the "Step1" State puts the number 20000 (20,000 milliseconds = 20 seconds) on the Shift Register that gets wired to the "ms Timer" Front Panel. It also puts "Wait" onto a Channel Writer, so the next State is the illustrated Wait State. If there is still time on the Timer, it subtracts 0.1 seconds (I used a shorter time than 1 second to make this a bit more responsive -- since nothing else is going on, you could probably make it even shorter, even 1 ms, without worry), and wires the incoming "Wait" State into the Channel Writer to repeat the Wait. The False case resets the Timer to 0 and puts Idle into the Channel Writer to take us back to the "Idle" State.
There's little left to tell, except to talk about what happens when you push the "Stop" button. The Boss Loop has another Value Change case for the Stop Button. It does almost exactly what the visible "Start" case does, except that (a) it wires "Stop" into the Channel Writer, and (b) runs a wire out of the Event Loop and down to the Stop Indicator of the While Loop, which means the pushing the Stop button stops the Boss Loop. But the Worker Loop is still running -- it gets the Stop message and take us to the "Stop" case, where we wire a "True" to the tunnel that goes to the Worker's While Loop. So the Worker "obeys" the Boss's command to stop, and the program stops, even if the Wait timer is still "counting down".
Here's the Snippet:
Bob Schor
04-13-2023 03:11 PM
Wow. Thank you for taking the time to write all of this up. There's definitely a lot of helpful information in the VI that you sent. I'm going to use the "Boss-Worker Design Pattern" in my main VI, which will be the only user interface in the test program. One last question, and I'm sure there is a good reason for this, but why use the Asynchronous Channel Wire over the local variable?
04-13-2023 06:31 PM
As many engineers will tell you, sending signals over "wires" rather than "through the air" is more reliable, easier to see the connections, and easier to explain. The same goes for the Laboratory Virtual Instrument Engineering Workbench, or LabVIEW (hence the name) -- "wires" and "Channel Wires" show you where the data are flowing, with nothing "floating through the air".
The major problem with Local Variables, which seems almost trivial in small pieces of code, where there are few "hidden" code (i.e. no Case Statements) and few structures, everything fitting neatly on a small screen, if you see a "local variable" somewhere, you have to "hunt and search", sometimes in three dimensions (width and height of the display, with too many VIs growing well outside the boundaries of large screens, plus a third "depth" dimension of Case, Event, and other "stacked" structures, looking for matching local variable terminals.
As you gain expertise in LabVIEW, and start working with larger and more complicated LabVIEW routines, you will begin to gain an appreciation of good LabVIEW "style". Being able to visually "see" (and appreciate) the flow of data means abandoning local variables, particularly if "wire" possibilities are available.
Bob Schor
Champion of Channel Wires
04-18-2023 11:10 AM
That makes sense to me. Thanks again for all of your help.