Example Code

Queued State Machine with User Input

Code and Documents

Attachment

Introduction

Many times, when trying to decide on an architecture for an application, difficult decisions arise.  State machines are great for sequential processes, but fall short when requiring user input.  Producer/Consumer architectures are great for handing UI events, but are not so wonderful at handling preprogrammed sequential tasks.  Thus, it makes sense to combine these two architectures.  In this example, we have an architecture that responds to user input, but still retains the benefits of a state machine by following preprogrammed sequences.  This allows us many things.  First, we can interrupt the state machine at any time with more important tasks.  Secondly, we can introduce a master shutdown feature that will gracefully abort.  Thirdly, we can handle multiple "sub-state-machines."  This can be useful if several different processes share a few common states.  This allows you to create a state architecture that asks for user input in places where pre-programmed decision-making isn't possible.  Also, because this is event-based, the program does nothing while waiting for user input.  A traditional state machine would have to continually poll for commands, thus eating valuable processor but accomplishing nothing.

BD.png

Open and run the VI.  You will notice that a Queue is used to handle state information.  This is helpful, because we can queue up multiple states at once, thereby creating a sequence.  We also have the functionality to abort and execute more important (shutdown) cases.  Feel free to post questions.

Wes Pierce
Principal Engineer
Pierce Controls

Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.

Comments
rosat
Member
Member
on

very good architecture. thank you.

ABHINAV318619
Member
Member
on

very good architecture..

syrpimp
Member
Member
on

Great indeed ..Thank you Very Much...



Thank you & Best regards
syrpimp

=======================================================
“You must continue to gain expertise, but avoid thinking like an expert." -Denis Waitley
tomashejda
Member
Member
on

This pattern has a potential risk of get cycled or get into unwanted states. Let me explain it. When you click on button, an event occurs and typically an element is enqueued to the queue. But in the lower loop another element is enqueued (in this case Report or Process). So when you for example want to go to idle state by clicking on the Stop button, you go to idle state but then you go to report or process state because the element was enqueued behind the idle element as explained above. The solution is to check if the queue is empty before enqueuing the element in the lower loop:

If the queue is empty, it is safe to enqueue element in lower loop:

enque0.png

If it is not empty (this occurs when an element is enqueued in the upper loop due to event occurence), the element will not be enqueued:

enque1.png

MarkMeng
Member
Member
on

It's a good idea, but correct me if I am wrong.

I thought Dequeue process in the lower loop only occurs once in each while loop iternerate.

So we need not worry about getting into unwanted state because as soon as lower loop

dequeues Shutdown, it'll just go to shut down case in which lower loop terminates and the rest

queued states (like Report or Process) don't even matter.

Any thoughts?

tomashejda
Member
Member
on

Ok, let's say we start the VI and click on Start button. The sequence of queued items is as follows:

Initialize (upper loop) → Process (lower loop) → Process (lower loop, repeated until value of 25 reached) → Report.

Let's assume that we click on Stop button while process state is counting up the value. What

happens?

Well, the queue is flushed at first and than the Shutdown  is queued. Normally we assume that the VI should be stopped after that. Yes, in the way the VI is written, it works well, because the lower loop spends most of her iteration time on the Wait (ms) VI (100 ms) in contrast to the rest of the code (about 1 ms) so it is highly unlikely that we click on the Stop Button another time than the Wait VI is being executed. But imagine that we have a time consuming code in the Process state. Than the click on Stop button during that code execution gets highly probable. If you click on Stop button before the Process or Report State is enqueued, The Shutdown is enqueued at first and then the one additional Process or Report is enqueued. In this situation, the only thing we need to do to get to unwanted state is to replace true constant by false constant in shutdown case (for example if we need to go to idle rather than shutdown the application). The solution is to use the flush queue like in this architecture in Halt state but in more complex software I prefer my solution encapsulated in subVI and use this subVI in each state of the state machine of the low priority loop to completely avoid the risk of getting to unwanted state.

Contributors