Jump to content

State machines


Recommended Posts

I'm using an event structure inside a while loop with a class and a queue input in a shift register.

Am I right that I shouldn't signal an event from another event?

For example:

Event 1 triggers event 2 and then event 3.

Event 2 triggers event 4

Thus, if I trigger event 1 the result sequence of events will be: 1,2,3,4 instead of what I would like to logically which is 1,2,4,3.

1. What is the right way to use a state machine?

2. How do I add automation over a state machine (change values and trigger events as if a user pressed a combination of options, analyzed the results and did something complex).

The current way I do it is by adding a wait for idle vi in a parallel loop that sends the next event once the state machine reaches the idle state (and thus I know the previous sequence is done).

The problem is that this way is far from reliable since it might still contain race conditions.

I tried using queues to organize the data flow, however, I found handling them might be a bit tricky. For example, if I add to a queue and along the program I forget to remove the message (I added the message twice by mistake somewhere) the queue might use more and more memory until the program becomes unstable and crash. Using the new analyzing tools I can find the problem yet it is still a bit of a headache.

I hope there is a classical simple solution documented in one of your wonderful posts.

Link to comment

You are on the right track but i would suggest adding complexity to your code, in the hopes that it will make it more reusable, and easier to understand. I usually use a while loop, with a case statement in it, and then a event structure in the default case. (I believe the JKI template uses a modification of this).

Another way to make this design is to open the LabVIEW template, Queue Message Handler and add an event structure to the default case.

What this allows you to do is have most of your code in the cases of the case statement and not the event structure. So say you have a button that does some function, say call another sub VI for a dialog. You can have the button's change value event call three cases "Hide UI", "Show Dialog", and "Show UI". Now if you have any other functions that need to use the "Hide UI" or "Show UI" then you just need to call the cases. So for you there could be four cases "Event 1 Steps"..."Event 4 Steps". Of course I would recommend using better naming for the events. Then you can call any number of steps in any order, under any event case.

I believe this to be the more right way, but one way is to do as you said, where you have a value signaling property on a control that calls an event. I feel like this is harder to follow, and harder to debug.

Link to comment
I believe this to be the more right way, but one way is to do as you said, where you have a value signaling property on a control that calls an event. I feel like this is harder to follow, and harder to debug.

I agree.

If I find myself in this position then it means that I want to call code in the Event Handler.

Simply refactoring it out into e.g. it's own case, solves the problem.

Link to comment

Do you mean that the user triggers the event while each event usually just triggers a case?

This way my main code is in cases and if I need to trigger another case from one case I won't be using value signaling.

However, each case will still have the problem I described: if case one needs to run case 2 and 3 during its execution then those cases will still be triggered when case 1 is done.

If I understand you correctly you want either the cases to be pure so they won't need to call another case until they are finished or you wanted me to copy all the sequence of operations into a single complex case which might cause multiple copies of the same code (with small changes or bug corrections between them).

As I said, I'm working on a VERY large scale program with >1000 vis.

The solution I had is turning each event content into a sub vi and if I need to create an event/case that calls another event then I won't call that event but rather place it's sub vi inside my event's sub vi.

However, this solution also has it's drawbacks: I have to pass to this sub vi many connectors with data that it is sometimes problematic to pass (local variables for example) and if I need to call this vi from parallel loops then I need it to be reentrant which might also bring along some other problems.

Could you tell me of a post that describes your solution in details with examples?

Link to comment

I believe this to be the more right way, but one way is to do as you said, where you have a value signaling property on a control that calls an event. I feel like this is harder to follow, and harder to debug.

+1 But we all do it :P . I have had mixed success with using Event Structures as state-machines (UI becoming unresponsive due to lengthy code execution, getting UI updates because it's locked in another frame somewhere etc). I tend now to use the event structures only for select user events and FP events, and message a proper state-machine via queues and/or notifiers (stop, start, pause etc).

Additionally, I tend to dynamically load state-machines because if all else fails, you can just crowbar it without your app freezing.

Edited by ShaunR
Link to comment

Am I right that I shouldn't signal an event from another event?

For example:

Event 1 triggers event 2 and then event 3.

Event 2 triggers event 4

Thus, if I trigger event 1 the result sequence of events will be: 1,2,3,4 instead of what I would like to logically which is 1,2,4,3.

....

Would it be possible for you to provide a real example of this?

I don't describe state-based behavior quite the same way. When I design a state machine I define states and the behaviors the system has for those states in response to various triggers (external or internal). If the system has different behavior in response to a trigger, then I have a new state.

If you describe the behavior that you want then I will show you what I mean....

Link to comment

I've been using this kind of architecture in LV now for about 12 years and have found, as I have introduced the Event Structure over time, that it can pose a problem depending on how it's used. If you use the Event Structure to queue a message to execute state machines and have the actual "action" done in those state machines, if works much better. In general the Event Structure rides the UI thread and depending on what processes are called during the prior event, the UI can very definitely hang, With simple architectures it might work but my product uses over 1,600 VIs that I crafted, and then all of the calls to vi.lib, etc that I didn't craft, so it's rather large, and convoluted intentionally to hide the IP. So ShaunR is IMHO right on target with his suggestions to you.

Link to comment

Could you tell me of a post that describes your solution in details with examples?

I didn't think a snippet would work properly so here is a quick example of what I was thinking. It just has four buttons, exit and three for triggering events. Event 1 fires one event, Event 2 fires 2 and Event 3 fires 3 events in the predefined order. What the events actually does isn't important but in this example it shows a dialog saying what the previous event was (if there was one) and what the current event is without using any local or global variables.

Queued Message Example.vi

Link to comment

Thanks for the vi.

I have a few problems with this solution:

1. First of all it doesn't implement MVC; the GUI will freeze while a previous event is handled, so, I separate the loops with a producer consumer.

2. It still doesn't answer my main issue. Event 3 should have called event 3, then event 1, then event 1 again since this is what event 2 does and then event two, yet, the event case 3 doesn't trigger the event but simply calls the cases. The logic of an event is separated in your way into two parts: predefined sequence of operations and an action. I want to call an event, analyze it's output and then call another event, how will you do it? Can a case trigger an event or is it not allowed?

3. There is a great chance you'll have a typo in the events names (even just a simple lower/upper case) and it's hard to change an event's name even if you turn it into a typedef. Besides that, it is hard to debug queued state machines even after I add a proper logger, error hendler and a good stop mechanism.

Paul, I attached a vi with an example of the bad way to use an event loop.

press "add and multiply" (result is 2 - numeric fields are set to 1,2,3) (x=(x+1)*2)

press "call add and multiply and then div" (result is 3.333 instead of 2 since div was called first) (x=(x/3+1)*2 != x=(x+1)*2/3))

my current solution is having each event in a sub vi and instead of signaling an event in "add and multiply" and in "call add and multiply and then div" I simply add that event's sub vi.

bad event loop coding.vi

Link to comment

I think the bit of info you are missing is that it is an Event Queue. By using the val (sig) you are simply placing which cases to execute at the tail of the queue. It's not like event branching (as you would expect from, say a PLC). So when you execute two val (sig) within a case, you are adding two instructions to the queue. It is not until the loop goes round again, that the queue is re-read and the case at the head of the queue is taken and executed.

Edited by ShaunR
Link to comment

Shaun,

I understand that this is how it works, yet, the state machine hooovah sent behaves the same way.

My question was: what is the proper way to do the operation I gave in my example using a state machine / event loop?

Neither do I want to signal events nor do I want to queue string commands since in both ways the logical order of operation is broken.

Replacing the code with sub vis solves it yet I feel that it is not optimal.

I'll try and explain my motivation again.

Regular cases are the basic operation of the system, however, the cases that call other cases are a higher level of operation, a kind of automation in order to make my system idiot proof. I could either set this automation using the sub vis or I could use a parallel loop that waits for the first loop to idle between operations.

Is there a more elegant way to implement it without the down side of the limitations of a sub vi or the risk of race condition in the parallel loop?

Thanks in advance.

Link to comment

As I wrote in my second post, you might have to pass a lot of wires or the data you need is limited to the caller like in the case of local variables or events. Besides that, you might have to deal with reantrant problems or race conditions / neck / parallel issues.

Link to comment
  • 2 weeks later...

I found some time to look at the code in post #9. (One of my colleagues has 2011 installed.)

I realize ShaunR and O-o already pointed out why this doesn't work (the event handler processes events in the order it receives them).

From a high-level view, if I analyze this as a statemachine, I say there are two triggers, one for addAndMultiply, and one for addAndMultiplyThenDivide. Since the controller always responds to a given trigger in the same way (i.e,, with the same behavior), there is only one state. So we have one state that handles two triggers. There isn't then a need for a statemachine here, but if I did implement it thus, within OneState (for me this is a class but it needn't) be, I would call three model methods. For example OneState::addAndMultiply() would call Calculator::add and Calculator::multiply(). Similarly, OneState::addAndMultiplyThenDivide would call Calculator::add, Calculator::multiply(), and Calculator::divide.

I realize that isn't what O_o's plan is, but I think there are a couple things to keep in mind, most importantly that when we are talking about state machines we need to distinguish between triggers (what are asking the system to do) and the state-based behavior (what the system does in response to the triggers) that varies between the various states and is the reason for using a state machine.

Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.