Jump to content

Simple Event-Driven Queued State Machine with Front Panel Events and a Timer


Recommended Posts

Here is an example of a simple event-driven queued state machine with front panel events and a timer. Made in 7.1.1.

Features:

Makes a distinction between state, event and action paradigms

Allows reusing the same code (actions) within a VI.

Concentrates coding all the behavior information in one place, the Run State Machine action, which is more convenient than spreading it all over the place.

The events are implemented as variants with a tag for the event name. So, they can carry any data in addition to the event name itself (this feature is not used in this example). The event data can be decoded in the respective

Link to comment

Alright, I think I might be missing something here. I looked at the program attached in the 2nd post of this thread, and I think I'm a bit confused about its purpose. What is it that this is meant to provide? By reading the post and the title of the thread I was expecting a vi that could be used as a template to create a simple event based application. Now of course NI does provide examples of how to do this, so I was expecting something vastly superior to them.

My first impression when I opened the block diagram of the program was that it wasn't as simple as I was expecting. I see there is a lot going on, and I can follow most of it, however I guess I'm just missing something because I don't get the advantage to using this over a single queued event driven state machine. Another thing thats clouding my understanding of the code I think is the confusing routes of most of the wiring.

I'm hoping that someone can help explain what it is that makes this a more powerful queued state machine. I might also recommend straigthening up some of the wires where possible in hopes that maybe it will make the reasoning for using this as a simple event driven state machine template more clear to someone looking at it for the first time.

Dave

Link to comment
Alright, I think I might be missing something here. I looked at the program attached in the 2nd post of this thread, and I think I'm a bit confused about its purpose. What is it that this is meant to provide? By reading the post and the title of the thread I was expecting a vi that could be used as a template to create a simple event based application. Now of course NI does provide examples of how to do this, so I was expecting something vastly superior to them.

My first impression when I opened the block diagram of the program was that it wasn't as simple as I was expecting. I see there is a lot going on, and I can follow most of it, however I guess I'm just missing something because I don't get the advantage to using this over a single queued event driven state machine. Another thing thats clouding my understanding of the code I think is the confusing routes of most of the wiring.

I'm hoping that someone can help explain what it is that makes this a more powerful queued state machine. I might also recommend straigthening up some of the wires where possible in hopes that maybe it will make the reasoning for using this as a simple event driven state machine template more clear to someone looking at it for the first time.

Dave

Here is a package that, in addition to the example provided earlier, includes a very generic template and, also, another example of two asynchronously communicating VIs. The latter example demonstrates how you can build applications as a collections of asynchronously communicating modules based on the same suggested template.

I am not really sure which single queued pattern you are referring to. Anyways, the actions queue contains the list of actions scheduled to run as the result of the last event happened (this actions "to do" list can depend not only on the event happened but also on the state at that moment). Having a separate event queue enables the module to asynchronously accept events from FP or from another module while running some actions (which also can generate events) at the same time. Processing of the events is done in RTC (run-to-completion) manner: though the code can accept events at any moment, it doesn't check for the next event in the events queue until all the actions of the current transition are complete.

This pattern allows you to design and implement the module behavior in terms of states, events and actions by defining for each transition the target state and which actions to run depending on which event happened and which state the module is in at that moment.

This implementation adds functionality of a Mealy automaton, which allows you to associate (arbitrary number of) actions with transitions between states as opposed to the Moore automaton where actions can be assosiated only with entering a state (the standard NI SM pattern) Mealy automaton allows to implement the same functionality with fewer number of states than Moore. In practice, a combined model can be used. Please note that this pattern can be easily extended to have state entry actions and even state exit actions (actions always run when a certain state is entered/exited) in addition to transition actions, i.e. to support a combined model as well.

See here, for example, for more details:

http://en.wikipedia.org/wiki/Finite_state_machine

As mentioned before, due to the flat state space, this pattern is still much less powerful than a hierarchical state machine implemented in LabHSM toolkit.

Sorry, I really don't see where the wiring is confusing, but I will appreciate any corrections that would make it clearer.

Download File:post-1166-1163459532.zip

Link to comment
My first impression when I opened the block diagram of the program was that it wasn't as simple as I was expecting. I see there is a lot going on, and I can follow most of it, however I guess I'm just missing something because I don't get the advantage to using this over a single queued event driven state machine. Another thing thats clouding my understanding of the code I think is the confusing routes of most of the wiring.

I have to agree, I was expecting some kind of simple state machine toolkit, e.g. with methods to add state(s), get next state etc.

Sorry, I really don't see where the wiring is confusing, but I will appreciate any corrections that would make it clearer.

Regarding wiring, wires are going up/down, inside/outside of loops and this becomes pretty hard to follow.

I'm also missing shift registers and error handling within the loops.

As I said above, I think your template would benefit from encapsulation in logical methods.

It also seems like a good idea to bundle all your references as they seem to be more or less needed everywhere.

/J

Link to comment
I have to agree, I was expecting some kind of simple state machine toolkit, e.g. with methods to add state(s), get next state etc.

Well, sorry if didn't meet your expectations. Sorry, if this is not as simple as you expected too. I hoped that on this forum the majority of people would not consider it too complex.

I didn't quite get this about "methods to add state(s), get next state etc." The next state is always in the respective shift register. You can check it from within your actions but in general it's only the Run State Machine action that is supposed to do that. To add a state you add an item to the enum list in the state type definition. Cases in the main loop are not states (I know, in the pattern NI presents as THE statemachine they call them "states")! But in this pattern they are actions. Well, in the classic Moore pattern, the code in "the state" case is also nothing else than a bunch of actions associatied with entering the state really, not the state itself. Making each case to have a meaning of a separate "action" rather a "state" (actually, all the actions associated with entering the state") makes it possible to reuse the same actions' code in different transitions without subVIing them. How many times did you realize: "Oh, when this happens and we are in that state, we need to do this too!"

Regarding wiring, wires are going up/down, inside/outside of loops and this becomes pretty hard to follow.

Would you prefer them running behind other objects?

I'm also missing shift registers and error handling within the loops.

Nothing prevents you from adding additional shift registers for your data. The pattern itself uses one to store the next state.

It also seems like a good idea to bundle all your references as they seem to be more or less needed everywhere.

Well, I disagree that " they seem to be more or less needed everywhere". Within the main loop for example, Actions and Events queue refs are used only in the Run State Machine action (unless you want to peek or interfere into those queues from within your actions)

In any case, if somebody sees what exactly can be changed to improve this pattern I would love to see that code modified by you. Where exactly can we straighten the wires, where do we bundle/unbundle, etc?

Error handling: Well, yeah, I could have added generic error handling, but in many applications errors are handled (different actions run, different error states exist) differently depending on what that error is. Plus, this would have added more stuff to this pattern that is already perceived as too complex by some.

I would recommend to catch specific errors inside your specific actions and throw a corresponding specific (or generic) error event which would cause the module to go to a specific (or generic) error state. This would allow you also to handle requests for error reset differently for different errors (again, if needed, of course).

Link to comment

First, I'm sorry if I offended you in anyway by my previous post. It was not my intention, and I appologize if you felt offended.

Well, sorry if didn't meet your expectations. Sorry, if this is not as simple as you expected too. I hoped that on this forum the majority of people would not consider it too complex.

I think that a template should be simple to understand even if it performs a highly complex task under the hood.

So the tasks performed in your template are not too complex, but I felt that the usage could be simpler.

I didn't quite get this about "methods to add state(s), get next state etc." The next state is always in the respective shift register. You can check it from within your actions but in general it's only the Run State Machine action that is supposed to do that.

The reason for my comments is that I made a very similar approach back in LV6i.

In this approach I encapsulated the queue primitives and added error handling/idle state/priority(putting on front or on back), within the encapsulation VIs. This way the user don't have to know if I'm using queues/LV2 globals/... to pass states, so I'm free to make changes/updates when new "better" primitives (e.g. user events) are released from NI. For the end user the API will always be the same, regardless of the actual implementation.

Would you prefer them running behind other objects?

Nothing prevents you from adding additional shift registers for your data. The pattern itself uses one to store the next state.

I don't like to see wires get behind other BD objects, but as there are only 4-5 wires entering the loop this should be pretty easy to avoid. E.g. if the queue references that enters the loop also exist the loop then these wires can be used for the Destroy Queue actions instead.

Maybe it is just me, but I prefer to add shiftregisters for the error cluster even if the loop exists on the error.

Error handling: Well, yeah, I could have added generic error handling, but in many applications errors are handled (different actions run, different error states exist) differently depending on what that error is. Plus, this would have added more stuff to this pattern that is already perceived as too complex by some.

I would recommend to catch specific errors inside your specific actions and throw a corresponding specific (or generic) error event which would cause the module to go to a specific (or generic) error state. This would allow you also to handle requests for error reset differently for different errors (again, if needed, of course).

My point was that if your template does not exit the state loop on error, you might end up in a free running infinite loop (e.g. if the action queue fails to initiate for some reason).

In my approach I passed the error in a shift register and the GetNextState triggered on error and put the state machine in a error state.

/J

Link to comment

I like the general idea presented here (events causing actions to occur) and I use a similar approach in my programming. There are several limitations in the template presented, many of which have already been discussed. One that I find quite limiting is the inability to send data with enqueued actions. I attempted to clean up the template and add this data sending ability.

Download File:post-1519-1163630345.zip

Improvements:

  • Increased readability (judicious use of subVIs)
  • Reduce bottom loop to handling actions
  • SubVI for converting events to actions
  • Allows sending of events or actions through message queue
  • Allow data to be sent with actions (via variant attributes)
  • Queue errors will shut down the VI

I prefer errors to be handled by the actions, so only queue errors are used to shut down the loop. I used subVIs because hiding a lot of the functionality in subVIs makes for a cleaner look and a better place to modify behavior. The wires are also hopefully as suggested... I did not include examples, though the behavior should be apparent.

There are other aspects which I still do not like, but did not address. I don't like the queue naming as the mechanism to allow other VIs to communicate. I don't think using enums as the actions allows for complete flexibility, but they have some benefits. Strings might be more general purpose, plugin friendly. This implementation does not allow errors to interrupt a sequence of actions. All actions are performed regardless of the success of previous actions. But this was supposed to be simple, right?

This really can be a powerful method because you generally think about VI behavior this way. A message from another VI should cause something to happen. A front panel event should cause some things to happen. Often the behavior is dependent on what state your software is in. By having a subVI to convert a message or event to a list of actions, and allowing it to be state-dependent, you avoid a lot of problems associated with a strict QSM as seen in the examples. Problems include state explosion, handling transitions, code duplication, and just calling something a state that is really an action.

David

Link to comment

David:

Thank you very much for suggesting improvements to the EDQSM template. However, I can't agree with the suggested modifications.

First, subVIing any code that has references to the actions and states typedefs makes those subVIs module specific, i. e. the user will have to create and maintain separate copies of them for each particular module he creates of this template.

Second, I don't see it beneficial to explicitly manipulate (or encourage the user to do that) the actions queue from other actions (except for generic error handling maybe, where, for example, you may want to flush all the remaining actions when you encounter an error) Actions can be sources of events, but it's no business for an action to determine which actions to run on that event - that's the business of the SM logic itself.

Third, the Run-to-Completion (RTC) principle is very important.

Please see below an excerpt from an excellent book by Dr. Miro Samek, Practical Statecharts in C/C++. I strongly recommend reading at least Chapter 2 to anybody who is trying to implement any state machine pattern in LabVIEW or any other programming language.

"

2.1.7 Execution Model - Run-to-Completion Step

In practice, executing actions always takes some time to complete. The state machine therefore alternates between two modes: idle

Link to comment
First, subVIing any code that has references to the actions and states typedefs makes those subVIs module specific, i. e. the user will have to create and maintain separate copies of them for each particular module he creates of this template.

I understand the desire to have all of the code in a single VI, and it certainly was apparent that you were trying to do that. From the aspect of distributing a template and keeping things simple for people I concede there is a desire to keep it all in a tight package. But you already have two controls that are dangling onto the template that need to be copied around. Trying to design a template such that there are no generic subVIs (i.e. VIs that would be copied over to other projects) is certainly possible but it isn't necessary. Just make a .lvlib file that contains your project starting point. Or make some scripted interface to build your project skeleton (such as LabHSM for instance).

I prefer the subVI because otherwise the functionality is buried in the action engine. It makes the lower loop unnecessarily busy. I have seen people put the event structure in the state machine too and I don't like it for the same reason. It seems like a carry over from polling architectures. Having a subVI makes for one location for that code and more flexibility.

Second, I don't see it beneficial to explicitly manipulate (or encourage the user to do that) the actions queue from other actions (except for generic error handling maybe, where, for example, you may want to flush all the remaining actions when you encounter an error) Actions can be sources of events, but it's no business for an action to determine which actions to run on that event - that's the business of the SM logic itself.

I admit that I do "open up" the action queue. It wasn't really with the intent of inspection or manipulation. Mostly it is meant to add actions. It would be better to encapsulate it such that the queue is unavailable. Make a "Fire Event" subVI that handles creating the array of actions and sending them to the action queue. You would have to make it some sort of LV2 global and initialize it to keep the queue reference invisible, but that would allow for any implementation for the message passing. This has already been mentioned as a nice feature.

My first attempt at this type of architecture (which was inspired by your LabHSM, BTW) had some hybrid event-actions in the lower loop. The upper loop would pass events to the lower loop (with a prefix to make it readably obvious they were events). The case structure would handle events and actions. Events would contain the functionality general to that event and then some logic to decide what actions to enqueue. I felt that a subVI to handle event-to-action logic was unnecessary for my application. So it is somewhat similar to your template here, except that there is a case for every event instead of a single case to handle all events.

Having looked at your template, and rethinking my first attempt, I am inclined to think that the subVI for event-to-action logic is a better implementation. It cleans things up and provides a replacement for the hsm logic box from your LabHSM stuff. I prefer to have scripting engines that build skeletons and then I tweak them. That was one of my big complaints about the HSM code was that the logic was completely hidden. It has its pros and cons but as a programmer I prefer to see the code. I thought, as I was looking at your simple template here, that you were trying to create a simple template by removing pieces from the HSM template. The idea is still good but there are perhaps some carry overs left in there.

Third, the Run-to-Completion (RTC) principle is very important.

I didn't understand what you meant by Run-to-Completion. I thought you referred to the entire list of actions that is generated from an event. In your implementation (and mine) the list is completed without any option of flushing the queue upon error or anything of that sort.

The information you provided refers to running EACH ACTION to completion. I completely agree that this is preferrable. The complexity you would have to add to NOT do this would be staggering. The complexity to do it right is beyond my humble skills so I would never even attempt it.

I would like to hear your thoughts on what I wrote here. I am open to correction.

David

Link to comment

I am very glad I have somebody to discuss (and argue about) these things with! Thanks, David!

Trying to design a template such that there are no generic subVIs (i.e. VIs that would be copied over to other projects) is certainly possible but it isn't necessary.

I agree, but I am hesitant of creating non-generic, module-specific subVIs.

Just make a .lvlib file that contains your project starting point.

That's a possiblilty. Agree.

Or make some scripted interface to build your project skeleton (such as LabHSM for instance).

Well, that will be a little bit too much for a "simple", limited functionality, template, won't it? ;-)

I prefer the subVI because otherwise the functionality is buried in the action engine. It makes the lower loop unnecessarily busy. Having a subVI makes for one location for that code and more flexibility.

Well, all the behavior logic is concentrated in the Run State Machine action, so it is in one location and it doesn't get in your way at all while you are dealling with your custom actions. Alternatively, as in LabHSM, the entire behavior description can be stored in a data structure in a constant or an external file.

I admit that I do "open up" the action queue. It wasn't really with the intent of inspection or manipulation. Mostly it is meant to add actions. It would be better to encapsulate it such that the queue is unavailable. Make a "Fire Event" subVI that handles creating the array of actions and sending them to the action queue. You would have to make it some sort of LV2 global and initialize it to keep the queue reference invisible, but that would allow for any implementation for the message passing. This has already been mentioned as a nice feature.

Again, it's no business for any action to add other actions into the actions queue. This is the SM business! Which actions to run and which state to go next (event handling/processing) must be decided in one place in an RTC manner.

"Fire Event" can be created, but there won't be much to it beyond placing an event into the events queue (not actions!). Well, events carrying data as an attribute require one more function and two more wires and one more string constant (to name the attribute). In my first version of this template there was such subVI

I didn't understand what you meant by Run-to-Completion. I thought you referred to the entire list of actions that is generated from an event. In your implementation (and mine) the list is completed without any option of flushing the queue upon error or anything of that sort.

The information you provided refers to running EACH ACTION to completion. I completely agree that this is preferrable. The complexity you would have to add to NOT do this would be staggering. The complexity to do it right is beyond my humble skills so I would never even attempt it.

No, Run-to-Completion must be observed for the entire action sequence that is scheduled for the transition.

Yeah, you can flush the remaining actions on an error using some generic error handling code. However, that is a "shortcut". Think about this: the actions that had already been run before the error happened may have done something that the system is already not quite in the state it was in when we started the transition. But, since it hasn't managed to run all the scheduled actions (it stumbled on the error), it's not quite in the destination state either! If you decide to allow for interuption on error in the middle of a sequence of actions you must ensure that you undo (roll back) all the changes made by the actions since the start of the transition to make sure you are still in the state from which you started this unsuccessful transition.

Alternative way: The fact that some action resulted in an error can be looked at as an event. In a general case, if such event/error makes it necessary to flush the remaining actions, you'd better make that action the last in the transition, check for that error right within the action case and throw a corresponding event depending on which further branching happens - either the other original actions are run (no error) or the whatever necessary error handling actions are scheduled (if any) and the system is ordered to go into an error state (generic or specific).

Stanislav

Link to comment
I am very glad I have somebody to discuss (and argue about) these things with! Thanks, David!

You're welcome. I am also enjoying this discussion.

Of all the subVIs I added to the template, only one is non-generic. That is the event-to-action (SM) VI. The main template allows for events (FP and external message) and for actions. These are tied together with the SM VI, which acts as the interface mechanism between this producer consumer structure.

Well, all the behavior logic is concentrated in the Run State Machine action, so it is in one location and it doesn't get in your way at all while you are dealling with your custom actions. Alternatively, as in LabHSM, the entire behavior description can be stored in a data structure in a constant or an external file. "Fire Event" can be created, but there won't be much to it beyond placing an event into the events queue (not actions!).
I see it is in one place, but it does get in the way. What business does the SM have in the action case structure? It just complicates the lower loop. And if there is a subVI for converting events to actions, then you can enqueue actions in the subVI. This is the same as enqueueing them from the SM case.
Again, it's no business for any action to add other actions into the actions queue. This is the SM business! Which actions to run and which state to go next (event handling/processing) must be decided in one place in an RTC manner.

I am not arguing with you on this. Whether it is a subVI or a case, I agree that one place to handle event-to-action logic is preferable. But if it is so important to not allow this, why is the action queue there in the bottom loop? And why does the Run State Machine action look like any other action and it can add actions to the action queue? Having the action queue in the lower loop and then adding actions directly to it seems to be encouraging such behavior in other actions.

No, Run-to-Completion must be observed for the entire action sequence that is scheduled for the transition.

I have only programmed this way so far and I would have to see some driving need to do otherwise. I agree that it is a potential problem, but I don't think it is always a problem. Actions don't always change system state and sometimes actions in a sequence do depend on the success of previous actions. I think that most often this can be handled without aborting the process and thus preserving the RTC principle. Again, if this is so important then the action queue should not even be available in the action loop.

I will create another template (hopefully tomorrow) to illustrate some of my ideas.

David

Link to comment

Here is a small update to the template. I renamed it EDAM for Event Driven Action Machine. It is different enough from a State Machine architecture that I gave it a new name.

It encapsulates the action queue into a "Process Event" subVI that is responsible for converting events to actions based on state and then sending them to the action queue. Only the template and the Process Event VI need to be copied and modified for each new instance. The support VIs are generic and only require one copy. If you require different states and actions then they will need to be copied as well.

Putting the "Action Send" inside of the Process Event cleaned up the diagram a little, but it basically looks the same. The main difference in terms of what I was discussing is that the action queue is only used to read actions in the bottom loop. I considered making the "Get Next Action" a LV2 global as well and completely remove the action queue from the lower loop. But this would make another non-generic VI to be copied around.

One thing I am not liking is the copying of the template. Most often there would be multiple versions in memory. If you copied from a template you had already modified then the new copy will have links to the module specific VI and controls. If you then open these and "Save As" you can easily mis-link the new copies to the original template.

David

Download File:post-1519-1164156231.zip

Link to comment
It encapsulates the action queue into a "Process Event" subVI that is responsible for converting events to actions based on state and then sending them to the action queue. Only the template and the Process Event VI need to be copied and modified for each new instance. The support VIs are generic and only require one copy. If you require different states and actions then they will need to be copied as well.

Exactly. Different modules made of the template will have different, module-specific lists of states and actions, hence, i.e. different typedef files. That makes all your subVIs module specific.

Another thing: Out of the same RTC considerations, pulling the next event from the queue must be done only when the action queue becomes empty. Only then enqueuing new actions and possibly modifying the state based on the current state and the discovered event should happen. In your template the "Process Events" VI (what you suggest as an alternative place to store the code I have in the Run State Machine Action) is called in more than one place, not necessarily when the actions queue is empty and, finally, I don't even see the means to change the state in there!

Link to comment
Exactly. Different modules made of the template will have different, module-specific lists of states and actions, hence, i.e. different typedef files. That makes all your subVIs module specific.

I see the problem here. But if you do have different lists of states and actions, then you have the problem I described where you could incorrectly link the enums if you copy a template while a current template is in memory. Having anything module specific in the template would cause this problem.

Another thing: Out of the same RTC considerations, pulling the next event from the queue must be done only when the action queue becomes empty.

You are right. I had really hoped one could eliminate the event queue with the subVI, but I see that isn't likely.

How about this one? Download File:post-1519-1164406954.zip

Link to comment
I see the problem here. But if you do have different lists of states and actions, then you have the problem I described where you could incorrectly link the enums if you copy a template while a current template is in memory. Having anything module specific in the template would cause this problem.

While making a copy of the template that has typdefs one must folllow the order:

1. Open the template.

2. Save it as a custom named VI.

3. Open state and action typedefs from this new VI and save them as custom named ctl files.

After this point there is no risk of incorrect links. You can open the template itself again and even when it's open you can modify states and actions of your new VI - they will be loaded from stored into different ctl files.

This all becomes irrelevant in 8 if you use LV 8 libraries.

You are right. I had really hoped one could eliminate the event queue with the subVI, but I see that isn't likely.

How about this one? Download File:post-1519-1164406954.zip

OK, so what you have now is pretty close to my original.

The differences:

1.You put the SM logic into a module-specific subVI - fine if somebody wants to handle one more file per module.

2. You subVIed some queue initialization and really made it generic, but at a price - the type for the actions queue is variant now and you need extra conversions between variant and actions enum

3. You added a message queue, I left out for simplicity in my "simple" template (it is implemented in LabHSM). Fine. But all you do with it is just copying everything from it to the events queue. Having a message queue is beneficial for encapsulation. As I said before, to observe this principle, other modules shouldn't be able to write directly to the events queue. They should write to a message queue, but the recepient must validate the messages before generating corresponding events. The simplest is at least to check the message against a list of allowed (exported) ones. If it's not on the list it must be ignored (or you can even send a corresponding reply to the sender).

Please note that for maximum CPU efficiency it's better to read from the message queue in a separate loop (which can be in a subVI - like it is done in LabHSM) . You put your Process Message into the FP events loop and had to use a non-zero timeout there just to be able to check the message queue. This causes idle runs there!

Sorry, I didn't get the purpose of copying all the attributes of an event variant to the action variant. Events variants need (just one really) attribute to carry data in addition to the event name. Why would actions need attributes?

Link to comment

In fact, the correct order is (pre-8 where you can save as with options) :

1. Open the template

2. Save it as a custom named VI.

3. Close the original template

4. Open typedefs and save as

If you don't close the original template then the typedefs will be incorrectly linked. If you do use the 8.0 save where you create a new copy in memory, then you still have to replace the typedef in the new file. The key is step #3

OK, so what you have now is pretty close to my original. (I thought you would recognize it ;) )

The differences:

1.You put the SM logic into a module-specific subVI - fine if somebody wants to handle one more file per module.

2. You subVIed some queue initialization and really made it generic, but at a price - the type for the actions queue is variant now and you need extra conversions between variant and actions enum

3. You added a message queue, I left out for simplicity in my "simple" template (it is implemented in LabHSM). Fine. But all you do with it is just copying everything from it to the events queue. Having a message queue is beneficial for encapsulation. As I said before, to observe this principle, other modules shouldn't be able to write directly to the events queue. They should write to a message queue, but the recepient must validate the messages before generating corresponding events. The simplest is at least to check the message against a list of allowed (exported) ones. If it's not on the list it must be ignored (or you can even send a corresponding reply to the sender).

Please note that for maximum CPU efficiency it's better to read from the message queue in a separate loop (which can be in a subVI - like it is done in LabHSM) . You put your Process Message into the FP events loop and had to use a non-zero timeout there just to be able to check the message queue. This causes idle runs there!

Sorry, I didn't get the purpose of copying all the attributes of an event variant to the action variant. Events variants need (just one really) attribute to carry data in addition to the event name. Why would actions need attributes?

1. Yep

2. The action queue should be variant anyway so that it can carry information.

3. I made the message handling a subVI so that one could add all that checking logic.

note - The idle runs are just waiting on the event structure, which is very CPU friendly.

Sorry, but I don't understand how you can implement actions that have no information available to them. For example -- If you click on the Nth element, then the action should happen to the Nth element. But how does the action know which element you clicked? And if you send a message, might it not contain some attribute data that you need to use, not just to decide which action to fire, but also how to fire the action?

Some other differences is that the action queue reference does not enter the action case structure and the state only enters the actions (read-only). This helps to enforce the type of behavior you were wanting.

Link to comment
If you don't close the original template then the typedefs will be incorrectly linked. If you do use the 8.0 save where you create a new copy in memory, then you still have to replace the typedef in the new file. The key is step #3

I just checked in 7.1.1: Once you have saved a VI with another name, only the new VI is in memory, the original is unloaded, so you can't screw up the original template unless you modify the typedefs without saving them as separate copies and then open the template again.

Sorry, but I don't understand how you can implement actions that have no information available to them. For example -- If you click on the Nth element, then the action should happen to the Nth element.

Actions have access to all the data of the module, whether you keep it in shift registers of the main ("actions") loop, controls, or LV2 style globals. You may want to have a separate, say, shift register that always contains the last event data (if any).

2. The action queue should be variant anyway so that it can carry information.

Not neccessarily, if they can access all the needed data directly

note - The idle runs are just waiting on the event structure, which is very CPU friendly.

It's still better to have the event structure fire only when a FP event happens

But how does the action know which element you clicked? And if you send a message, might it not contain some attribute data that you need to use, not just to decide which action to fire, but also how to fire the action?

Action doesn't need to know what happened - the SM schedules actions to run depending on event and state. An event can carry data, which can be accessed by a processing action, but the action queue itself doesn't need to carry anything except actions names. By having our own event queue we can assign different events to different values of some control when it changes, or we can have one event but which in addition to its name carries some value which actions that process this event can decode and use (if, say, you have a "Last Event Data" shift register as I already suggested)

Link to comment

I hear nothing but negative, Stan. Don't you like anything in there? Even the nice straight wires? :P

I just checked in 7.1.1: Once you have saved a VI with another name, only the new VI is in memory, the original is unloaded, so you can't screw up the original template unless you modify the typedefs without saving them as separate copies and then open the template again.

But this will mis-link the original template VI if there is another VI in memory that calls it directly. And if you are using 8.x then you have a choice of how you save and you could leave it in memory. My only point here is that ANY module specific items make for potential mislinkages.

It's still better to have the event structure fire only when a FP event happens
Picky, picky. :laugh: That is a bit too purist. You are talking about front panel user speeds on blazing fast computers. This would have no effect on CPU usage nor program performance. It is so close that I wouldn't be surprised if a separate loop for the message queue actually used more CPU usage.
You may want to have a separate, say, shift register that always contains the last event data (if any).

YES. This is good. I don't get the "if any". Do you really want to create dozens of different events for all the possible FP configurations?

I really like the "Last Event Data" idea. That cleans up the code a bit and gives you your typdef'd queue back. I think this event data really is missing from your template.

post-1519-1164869903.gif?width=400

EDIT: I want to hear back whether you think anything is an improvement, such as:

  1. Confining the action case to true actions
  2. Not having the action queue enter the action case structure
  3. Not allowing any actions to update the state
  4. Providing the event data to the actions (I know you aren't too hot on this one. I think it is crucial to avoiding code duplication, however.)

Link to comment
EDIT: I want to hear back whether you think anything is an improvement, such as:
  1. Confining the action case to true actions
  2. Not having the action queue enter the action case structure
  3. Not allowing any actions to update the state
  4. Providing the event data to the actions (I know you aren't too hot on this one. I think it is crucial to avoiding code duplication, however.)

Thank you for your efforts, David! Sorry, no offense, but I don't see any fundamental improvement compared to my original template (Last Event Data and a separate message queue were left out by me for simplicity - they are present in LabHSM and the attached "extended" versions).

I found having special "actions" like "Run State Machine" and "Handle Generic Error" to be convenient places to put that code in. Therefore, I need actions queue and next state wire going into the actions case. You can do it differently, but I don't see much danger in it. It's just a code template after all. Of course, you can also screw up a lot of things if you want!

If somebody prefers your version - it's their choice. I (and hope some other people too) like mine better (not as much as LabHSM, of course!).

Putting SM code into a subVI is fine, if somebody likes that better, but I strongly disagree with your upper loop code, as I said before.

Please note that your state indicator shows next state. For long transitions I prefer to have it displayed the source state rather than the destination.

As far as event data is concerned, you gotta use common sense in defining whether to implement particular functionality you want to have with different events or one event that carries data (then you will need to do the corresponding branching in the processing action(s)). For example, Exit Requested or some event associated with some button may not have much to carry in addition to the name of the event itself! Of course, for several buttons, you always can have only one event Button Pushed which also will carry the name of the button as data. Well, I guess there can be cases when this is preferrable, but obviously there are ones when it's not. I allow for both.

Attached package contains 2 more, "extended" templates. They are still fundamentally less powerful than LabHSM, but I added generic error handling, Last Event Data register, and, in the second one, a separate message queue. Master Slave Example 2 shows how to utilize the extended template with a message queue. I also decided to return to using Post Event subVI.

Please also note that my stand-alone message handler waits on the messsage queue in a separate loop, so it doesn't waste any CPU time. I can remove the UI loop and still be able to accept messages - this makes sense for modules, for example, with which you want to communicate asynchronously, but, say, they never need to display their FPs and hence process user events (daemons)

Download File:post-1166-1165111815.zip

Link to comment
  • 1 month later...

Hi all

I have tried to use this beautiful :) pattern but without understand entirely.

I was confused by so much queues, how dose message queue works and what is his duty? The queue manipulate vis is not so clear without comment or description though we can open and read its diagram.

If I want to add my menu selection function to the pattern, to response all the customize menu item, how can I modify the pattern.

Please have look at attachment illustrating what I want to do.

post-7134-1169426284.jpg?width=400

Link to comment
If somebody prefers your version - it's their choice.

Stan,

I hope you understand that the only reason I was playing around with the template is because I really admired the way that LabHSM worked. When you posted a simplified version that was different from a simplified version I had created, I wanted to see where the differences were and what could be improved on. I learned a lot about why you made the template the way you did.

I don't want you to think I was offering mine as a 'superior' solution. I was only looking for an alternative solution that made more sense to me. This was for my own personal use and not as a way to see if more people preferred my version. (From the dearth of outside conversation while we conversed I gathered that no one was interested very much.)

David

I have tried to use this beautiful :) pattern but without understand entirely.

I was confused by so much queues, how dose message queue works and what is his duty? The queue manipulate vis is not so clear without comment or description though we can open and read its diagram.

The message queue is meant for other VIs running in parallel that want to communicate with each other. If you do not need this then you can safely delete the code dealing with the message queue.

I was not documenting the template very much because Stan understood where I was going. I knew I would be changing things so I didn't document it each time. I added some more documentation to the Process Event VI with the example I am uploading here.

If I want to add my menu selection function to the pattern, to response all the customize menu item, how can I modify the pattern.

The following example should illustrate this a bit more.

Download File:post-1519-1169509991.zip

David

Link to comment
Stan,

I hope you understand that the only reason I was playing around with the template is because I really admired the way that LabHSM worked. When you posted a simplified version that was different from a simplified version I had created, I wanted to see where the differences were and what could be improved on. I learned a lot about why you made the template the way you did.

I don't want you to think I was offering mine as a 'superior' solution. I was only looking for an alternative solution that made more sense to me. This was for my own personal use and not as a way to see if more people preferred my version. (From the dearth of outside conversation while we conversed I gathered that no one was interested very much.)

David

The message queue is meant for other VIs running in parallel that want to communicate with each other. If you do not need this then you can safely delete the code dealing with the message queue.

I was not documenting the template very much because Stan understood where I was going. I knew I would be changing things so I didn't document it each time. I added some more documentation to the Process Event VI with the example I am uploading here.

The following example should illustrate this a bit more.

Download File:post-1519-1169509991.zip

David

Hi dsaunders

Thanks very much for your fast reply.I need to pay more time to understand queue, previous I have had few knowledge and experience in queue.

In this application I need to control 3 instruments, please have a look for attachment of my diagram, I do not know whether it is right way to control instruments and keep their instrument handles during the whole program. could you give some suggestion on how to control instrument and make read,save and analyse in your "pretty EDAM pattern".

Rexxar

post-7134-1169550623.png?width=400

Link to comment
In this application I need to control 3 instruments, please have a look for attachment of my diagram, I do not know whether it is right way to control instruments and keep their instrument handles during the whole program. could you give some suggestion on how to control instrument and make read,save and analyse in your "pretty EDAM pattern".

Rexxar,

If you are asking whether shift registers will work to keep the instrument handles during the whole program, the answer is yes. You might consider incorporating the handles in a functional global. There is a shipping example with LabVIEW called "Acquire-Analyze-Present.vi". This shows a simple version of what you want to do.

Frankly the thread title (Simple Event-Driven Queued State Machine...) is a bit of a misnomer. It is simple compared to some techniques, to be sure, but it is the most complex "state machine" I have ever used. It is meant to be able to handle complex problems. It will certainly be able to solve your problem, and you could adapt the example I mentioned to the template. If your program is relatively simple (i.e. init instruments, take data, save data, analyze and present data) and you don't have much LabVIEW experience, it might be best to do the program modeled after the shipping example.

You might have noticed that I was hashing the template out as of two months ago. So I don't have a lot of documentation nor examples of using the template. Without an example to point you to, I can't help much. I can't give very good suggestions without a pretty full knowledge of what you are attempting, and even then I would have to map out a good chunk of your application. If you are determined to use the pretty pattern (feel free to :) ), it will take a bit of work on your part.

David

Link to comment
  • 2 years later...

It is still much less convenient and functional than the LabHSM toolkit:

The main deficiency is that the state space is flat (not hierarchical). The lack of ability to inherit behavior between states severely increases the effort needed to implement a behavior that is even moderately non-trivial, because many more transitions need to be described explicitly.

Editing the behavior with nested case structures is certainly less convenient than with the elegant LabHSM editor

Download File:post-1166-1162517218.zip

Does anyone have an example of a hierarchical state machine programmed with the same level of detail as the flat state machine examples provided here already? I'm sure that LabHSM is great for achieving fast and robust HSM programs, but it's a closed design and difficult to interpret what is going on "under the hood". For illustrative / educational purposes, something with nested case structures and shift registers might be better. Thanks,

Nate

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.