VI High 58 - How to Make Your State Machine Event-Based
We've been focusing a lot on state machines over the last few episodes, but we followed a polling scheme. This episode, learn how to make the state machine event-driven.
Need some LabVIEW training? Swing by http://sixclear.com/labviewtraining and check out Sixclear Lucid LabVIEW Fundamentals Training.
And keep up with us here:
http://facebook.com/sixclear
http://twitter.com/#!/sixclear
Now, some of you may have already asked, “Brian, what are you doing using a polling structure when you should be using an event-driven structure?” I know. I get it. Don’t worry. Don’t worry. We’re discussing it now.
This application is very simple. Really, we have two buttons. Basic Test and Stop. And when the state machine goes to the state, that’s where it takes the data and only there. So this application will be better served with an event structure in the idle case rather than this polling scheme. And we’ll implement that event structure in a second, but first we should ask, “Is it always wrong? Should we never use a polling scheme like this?” And the answer is there are definite cases where the polling scheme does work. The classic reason is that you have data coming in at a constant stream, so you need to poll data. So, perhaps you’re connected to an instrument, like an oscilloscope or some network that’s constantly giving you data. And you need to go at a certain pace in order to get data from that. So, maybe I’m going 100 times a second and pulling data off of an oscilloscope. And then I pipe that into the data pipeline and take care of it in other cases. In that case, since I’m already polling over and over again in a loop, it would make sense to go ahead and poll these simple buttons as well. However, in this case, I don’t. All the data is actually being taken in the cases here and here. So, let’s fix it and improve it as well.
Right now, we only go to basic test and stop. But let’s say that I also want the option of going to advance test. Pretty simple. I’ll make a copy of this, and I’ll call it “Advanced Test.” Of course, that’s just the Boolean text, I need to go to the label, too. Ok. We’ll align these to the left and smash them together. Very pleasing. So now, I’ll get rid of this case structure. Don’t need it. And instead, I’ll put down an event structure. And configure the first event, which will be the basic test. A value change is good. Ok. Put the basic test terminal in here since it’s a latch action Boolean. We always want to have those in the event structure. And, of course, we’ll go to the basic test.
Now, we can take advantage of what we just did and right-click and duplicate this event case. In so doing, it creates an extra basic test, but we’ll delete that. And instead, I’ll choose advanced test. Get rid of this. Choose advanced test and bring this terminal in here. And the last, which we won’t duplicate, will be stop. Because remember, our user can stop it from right here. In which case, we won’t actually go to the next state, so I don’t have to put anything here. Now, do we need this? Well, that depends. Do I need that for the other cases? In other words, do I need to wait for 10 milliseconds between these cases? Typically no, so I’ll get rid of it.
Now there’s one other thing we need to take care of. It’s a little tricky, and we haven’t had to deal with it before because all of our cases executed so quickly. The initialize in both the tests happen very quickly. But the problem stems from the fact that the status is being updated at the end of this case. So, when we initialize, for instance, we say initialize and tell the users that status really after we’ve initialized. But because it happened so quickly, we get away with it. However, once we move to an event-driven scheme, we’re going to see in our idle case that we’ll move to the idle case, but we won’t execute it. We’ll just wait here. So, the status will be whatever the last case executed. And really that’s the case with all of them. Status doesn’t say basic test until basic test is actually finished. Now, we’ve kept it quick and simple so far, so we haven’t really had to deal with it. But we should take a look now at a few ways of doing that. A clean and professional way would be to have multiple loops. So, one loop would handle our tests. Another loop would handle our user interface. Once we go down that road, we should ask ourselves how many other loops we should have. And that would lead us to a producer-consumer or queued-message handler type of architecture. So, we may not want to do that. Not that they’re very difficult, but they are the next step up in complexity. What we really want is to have this status be updated at the beginning of the case for all cases. The quick and simple way of doing it is just to make a local variable of it. A property node would also work if we’re going to be editing some other properties. But if we’re just editing the value, it’s not recommended because it requires a thread swap.
Let’s fix it. In my idle case, I’ll right-click and create a local variable of that status. And to ensure that it runs before this does, I’ll need to create some space first. And then move this here. Now floating this over here to the left of this does not ensure that it will go first. So, we’ll need some dataflow to make sure that this definitely goes first. A sequence frame will work. There we go. Now, that fixes that case. We’d have to do that for every other case as well: copy and paste that over. So, of course, ears pricking up, we should use a SubVI. But what am I making a SubVI of? Well, it should just be this really. And if I make a SubVI, I wouldn’t have to use this janky flat sequence structure. I could instead, put error clusters and pop it right on this error wire. So, let’s do that instead.
Right-click. Remove that sequence and just click once on the border. Edit. Create SubVI. And there we go. Go into the SubVI, we see that under the hood, we are using a property node. So what I said earlier about the thread swap still does apply. So, it’s up to you. In most cases if we’re not calling these property nodes a lot, this is ok. In other words, not calling them repeatedly in the loop over and over, so that we generate a lot of thread swaps. This, however, totally permissible. We said that we wanted those error clusters, so let’s put those in. Assign those and change our icon. You’re getting pretty good at this by now. And what we’re really doing is handling the UI, the user interface. So we’ll just say “UI,” and we’ll jump over to glyphs. An image of the front panel is actually pretty useful. So, I’ll just grab that. Ok. And save it as UI Handler. And put it on the error wire. Oops. It looks like I actually have an error in instead of an error out. Easily changed. We just right-click and change to an indicator. Now the complexity could grow because we’re using the user interface in each case. And we could also stand to use it at the beginning and in other places as well, but you get the idea. This is a valid use case of it. A very simple one, but this could also address a lot of other things on our user interface. For now, we’ll keep it simple. And to go put this somewhere else, I’ll just do one of the other cases, maybe the initialize. And we’ll go pull it out of here. And we’ll want that same reference that we had last time, so let’s just go grab it. We’ll leave a copy out there because we’ll use it again. Eventually we’ll go and change that in all cases and I can just pull the status really anywhere because it doesn’t need to be here anymore. So, I’ll just get rid of this, and I’ll go fix any other cases like I did here. You don’t have to watch.
All right. All fixed up. We can see that in every case, we’re updating the status. So, let’s go ahead and run it. Back to the front panel, hit the basic test, and remember the last episode we had programmed the basic test to fail with the voltage of 2.6. That sounds good, and we’ll jump back to the idle state. What about advanced? Well, look at this. The basic test failed with a voltage of 2.6. That clearly can’t be right. What went wrong? Well, going back to our idle state, this is the place where we go after all of our tests are done. Basic test, advanced test, notify, back to idle. Here we should be clearing out any past data. So, let’s do that. Clear out past data which is carried right here. So, let’s just delete that and make a fresh copy of it over here. Create a constant. We’ll make it a little smaller. View it as an icon. Let’s check that again. Ok. Advanced test. It says failed with a voltage of 0. Now, that’s right because we haven’t actually put anything into the data in our advanced test. So with the default being a false, the test failed.
Stopping it goes back. Just double check and put in a value for the advanced test. We’ll say it passed with a value of 10.1 and wire this out. Save and close it. Now remember that we need to go back to our notify SubVI, and I haven’t handled any reporting here for the advanced, but it’s a bit complicated because the advanced test is conditional. It only executes if the basic test passes and then now in our event structure implementation, it can execute without the basic test, so all that logic would have to be programmed in here, which we won’t do right now. Instead, we’ll just go ahead and test this. The advanced test will just say that it fails with a 0 since there’s no real reporting. So basic test, we get the notification. Advanced test, we get the notification as well. Again, it says basic test because we haven’t programmed the notify VI. And stop works. Ok. That does it for state machines. You can probably imagine me making some shameless plug for our online and in person training. So, if you’re thinking of that, we’ve done our job.