From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Queued State Machine Issue

I have been developing a VI that mimics a state machine in which a main wait state has multiple event triggered paths exiting that could then be constructed as state machines themselves.  However I don't think I can use any sort of flat sequeuces, etc. as I need the ability to interrupt and halt these smaller state machines and return back to the main wait state.  I found the example using a Queued State Machine with User Inputs and it looked like it would fit the bill of being able to execute subroutines using the queue while still allowing the user to execute an interrupt that would clear the queue therefore cancelling the subroutine call.

 

I implemented the same structure however unlike the example I've merged the event and case statement into the same while loop.  Where I'm stumbling is I've implemented one of the subroutines (called Forward) with simple text outputs to the front panel as to where I'm at.  I can get the front panel to show "I am at Forward" and typically on the next while loop iteration, the case statement moves to state Forward2 and I get "I am at Forward2."  The VI looks like it adds to the queue the next state "Forward3."  However when I would expect to see the next while loop iteration the VI simply halts and never advances to Forward3.  If I execute another front panel event (i.e. Shutdown) the VI will enter that event and processes it correctly.  Here's a screenshot of the main wait function.qsm_1.JPG

I'm assuming there must be something in regards to using queues that I've overlooked but I don't understand quite how the VI gets to Forward and Forward2 yet it then stops processing that state machine and waits on another user event to do anything.  If it only processed the state Forward then I would presume it was something to do with Forward2 not existing as an event condition but that doesn't seem to be the issue.  Any help or tips would be appreciated - this is my first attempt at using queues within LabView.

 

Thanks.

 

- Eric

0 Kudos
Message 1 of 10
(2,884 Views)

DATAFLOW!

 

The while loop won't iterate again until all the code inside of it completes.  That includes the event structure that is waiting on another event.

 

You need to have the event structure in a separate while loop.

Message 2 of 10
(2,879 Views)

Which raises the question, why did you decide to alter the example pattern and merge the two while loops? What advantages were you hoping to gain? Just curious.

0 Kudos
Message 3 of 10
(2,827 Views)

Also, why is there a sequence structure?

Bill
CLD
(Mid-Level minion.)
My support system ensures that I don't look totally incompetent.
Proud to say that I've progressed beyond knowing just enough to be dangerous. I now know enough to know that I have no clue about anything at all.
Humble author of the CLAD Nugget.
0 Kudos
Message 4 of 10
(2,823 Views)

^^^ I've split the event and case back into separate while loops and now I'm able to step through the states as expected.  Thanks.  I guess my question is that if the while loop is waiting on another event and the event of pressing the Forward button on the front panel tells the VI to queue state Forward, how is it getting to state Forward2 if there's no other events that occur?  Just curious.

 

^^ I had built a VI for my application with the separate while loops and when I doing some quick tests, the Cancel wasn't clearing the queue fast enough.  I thought by integrating them into the same while loop then I might have a better chance of halting the motors the VI controls quicker than with 2 while loops (and this seemed to be the case).  I also thought that the queue might not be cleared as fast when a Cancel was initiated as there are two while loops running without any link other than sharing the same queue reference.

 

^ Just for organization - I just like to put the initializations into one stage, move to a main() stage, and when the VI needs shut down move to a close stage to close all COM ports or instrument instances.

0 Kudos
Message 5 of 10
(2,805 Views)

@snuzzbff wrote:

<snip>

^ Just for organization - I just like to put the initializations into one stage, move to a main() stage, and when the VI needs shut down move to a close stage to close all COM ports or instrument instances.


Oh, I get it.  I do the same thing with initialization if I can't use dataflow to make things happen.  I just use one frame around the offending code, so the appearance threw me...


If you want your "Cancel" to happen immediately, you can use "Enqueue Element At Opposite End" and it will bump it to the head of the line.

Bill
CLD
(Mid-Level minion.)
My support system ensures that I don't look totally incompetent.
Proud to say that I've progressed beyond knowing just enough to be dangerous. I now know enough to know that I have no clue about anything at all.
Humble author of the CLAD Nugget.
0 Kudos
Message 6 of 10
(2,793 Views)

@snuzzbff wrote:

^^^ I've split the event and case back into separate while loops and now I'm able to step through the states as expected.  Thanks.  I guess my question is that if the while loop is waiting on another event and the event of pressing the Forward button on the front panel tells the VI to queue state Forward, how is it getting to state Forward2 if there's no other events that occur?  Just curious.

 



If your Forward state is enqueuing the Forward2 state, when the loop iteration ends, you go back to Wait on Dequeue, and there is the Forward2 element already sitting there waiting and will execute immediately.  If you don't enqueue anything, then it will just sit there and wait at the dequeue until something else gets queued up such as from within your event structure in its separate while loop.

0 Kudos
Message 7 of 10
(2,783 Views)

@RavensFan wrote:
If your Forward state is enqueuing the Forward2 state, when the loop iteration ends, you go back to Wait on Dequeue, and there is the Forward2 element already sitting there waiting and will execute immediately.  If you don't enqueue anything, then it will just sit there and wait at the dequeue until something else gets queued up such as from within your event structure in its separate while loop.

Okay so hopefully I don't sound rude or defensive with my reply here - just wanting to close the loop.  Smiley Happy  So there seems to be a difference in how LabView is executing vs. what LabView should theoretically do in execution.  And it is possible that I have missed some subtle wiring which causes the error/unexpected behavior but anyways here's where I'm getting hung up.

 

In the case of using a single while loop with the event and case structures inside of it as I've shown in the original post, I follow that the while loop will sit and wait unless it has an event to service or something to dequeue from the queue (let's call it Q) and that's exactly how I intended the VI to operate.  So if I trigger the event case of "Forward" then the event handler will put the state "Forward" into Q and this results in the case statement evaluating to "Forward" which inside it adds "Forward2" to Q and completes the case statement.  On the next while loop iteration, Q has something in it so it dequeues that element and state "Forward2" is executed which just like "Forward" enqueues an element (here, "Foward3") into Q and completes that case statement.  On the next while loop iteration, the expected execution would be Q dequeues "Forward3" and moving into that state which then again repeats the same process of queuing the next state into and so forth until it reaches the final step in the Forward state machine and moves back to a general waiting state.

 

Instead what I've observed in LabView is that "Forward2" is pulled from Q, "Forward3" is enqueued and then the while loop sits and waits until another event is triggered to queue something into Q.  So this is contrary to the above expected execution and the source of my confusion.  I was using the Get Status right before the Error wires are merged and even highlighting the execution seems to indicate Q has an element in it which would imply another while iteration to dequeue that element however instead the while loop sits and waits.

 

So maybe that helps...thought I would go into a bit more detail to explain what I'm seeing happen vs. what is expected.

 

^^ To add to that - I did actually start with just the simple Enqueue Element function but since I had a wait state I realized I was having states execute in non-sequential orders because wait states were inserted in between them.  So I changed all the events to use the Enqueue Element at Opposite End function and that fixed that issue but still didn't seem to fix the issue of having two while loops running without any sort of synchronization/relationship to each other outside of using the same queue reference.

 

- Eric

 

 

0 Kudos
Message 8 of 10
(2,745 Views)

Hi Eric,

 

The thing to keep in mind here is that the while loop will not finish/iterate until everything within the loop has executed.  This includes the event structure, so even though the queue/case structure has finished, the event structure must also execute before we can proceed to the next loop iteration.

 

In your scenario:

 

1)"So if I trigger the event case of "Forward" then the event handler will put the state "Forward" into Q and this results in the case statement evaluating to "Forward" which inside it adds "Forward2" to Q and completes the case statement."

 

This is correct - first the event structure, then the queue case execute, then we go to loop 2:

 

2)"On the next while loop iteration, Q has something in it so it dequeues that element and state "Forward2" is executed which just like "Forward" enqueues an element (here, "Foward3") into Q and completes that case statement.  "

 

This is also correct- the case structure has completed.  The event structure, however, has not.  If nothing triggers an event and a timeout has not been configured, this is where we stop.

 

3) On the next while loop iteration, the expected execution would be Q dequeues "Forward3" and moving into that state which then again repeats the same process of queuing the next state into and so forth until it reaches the final step in the Forward state machine and moves back to a general waiting state.

 

This is only true if something also triggers an event (and therefore allows the event structure to proceed/finish executing) for each iteration.

 

To rephrase your statement:  In the case of using a single while loop with the event and case structures inside of it as I've shown in the original post, the while loop will sit and wait unless it has an event to service or and something to dequeue from the queue.

 

Consider replacing the event structure with a wait function or even a subVI that performs some operation- this new function or subVI would have to complete in each iteration of the loop.

 

Remember that with dataflow, all inputs must be available before a function or structure can proceed.  Similarly, all outputs must be available for a loop iteration to complete. In your "one loop" VI, if you turn on execution highlighting you'll notice that the loop waits where the errors are merged- the error output from the event structure must be available for the function to proceed.

 

Hope that helps!

 

 

 

 

Tom L.
0 Kudos
Message 9 of 10
(2,741 Views)

@snuzzbff wrote:

1. In the case of using a single while loop with the event and case structures inside of it as I've shown in the original post, I follow that the while loop will sit and wait unless it has an event to service AND something to dequeue from the queue (let's call it Q) and that's exactly how I intended the VI to operate.  

 

2. So if I trigger the event case of "Forward" then the event handler will put the state "Forward" into Q and this results in the case statement evaluating to "Forward" which inside it adds "Forward2" to Q and completes the case statement.  On the next while loop iteration, Q has something in it so it dequeues that element and state "Forward2" is executed which just like "Forward" enqueues an element (here, "Foward3") into Q and completes that case statement.  On the next while loop iteration, the expected execution would be Q dequeues "Forward3" and moving into that state which then again repeats the same process of queuing the next state into and so forth until it reaches the final step in the Forward state machine and moves back to a general waiting state.

 

3. Instead what I've observed in LabView is that "Forward2" is pulled from Q, "Forward3" is enqueued and then the while loop sits and waits until another event is triggered to queue something into Q.  So this is contrary to the above expected execution and the source of my confusion.  I was using the Get Status right before the Error wires are merged and even highlighting the execution seems to indicate Q has an element in it which would imply another while iteration to dequeue that element however instead the while loop sits and waits.

 

- Eric

 



1. Read my correction and think about it. 🙂

 

2. See 1, it will dequeue during the same while loop.

 

3. This is expected behaviour, look at it in pseudo code:

While queue.error=0 {

    Wait for Event {

         Button 1: Queue(Forward)

         Button 2: Queue(Stop)

    }

   While queue.dequeue = null;

   Case Queue.dequeue {

         Forward: Perform forward

         Stop:Perform stop

    }

}

How often will that code loop? That's basically what you've implemented.

 

/Y

G# - Award winning reference based OOP for LV, for free! - Qestit VIPM GitHub

Qestit Systems
Certified-LabVIEW-Developer
0 Kudos
Message 10 of 10
(2,729 Views)