LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Semaphores and queue together. Is it necessary?

Standard State Machine (Producer Loop)

This is what Felix is talking about when he referred to Damien's posts.  Start with a Standard State Machine.  Add a producer loop with a message queue.  In the state machine (consumer) loop, add an Idle case that handles all message dequeuing.  This image illustrates the concept.

 

StandardStateMachine 2_BD.png

 

 

 


Hi Daklu,

 

Is there any reason why the queue above cannot pass the state machine enum instead of a string which is then used to find the enum?

------------------------------------------------------------------------------------------------------
"Everything should be made as simple as possible but no simpler"
0 Kudos
Message 21 of 41
(2,102 Views)

Lucither wrote:

 


Hi Daklu,

 

Is there any reason why the queue above cannot pass the state machine enum instead of a string which is then used to find the enum?


Yes - Laziness when creating the Queue Smiley Wink

CLD; LabVIEW since 8.0, Currently have LabVIEW 2015 SP1, 2018SP1 & 2020 installed
0 Kudos
Message 22 of 41
(2,100 Views)

Lucither wrote:

Is there any reason why the queue above cannot pass the state machine enum instead of a string which is then used to find the enum?


Not inherently.  However, passing the state enum directly can impose some limits on the flexibility and clarity of your code.  For a completely contrived example, suppose the app has a Save Data button on the UI.  Before actually attempting to save the data, we want to make sure the data is valid.  How do we implement that?

 

A common response to this problem is to add a CheckIfDataIsValid state and have the UI send that state instead of the SaveData state.  The CheckIfDataIsValid state can then trigger the SaveData state if it confirms the data is valid.  This solution certainly works.  I don't particularly like it.  It tends to lead to state explosion and function machines rather than state machines, which IMO are much more difficult to follow.

 

A better solution is to create a CheckIfDataIsValid sub vi and put it in the SaveData state, casing out the rest of the save process if there is no data to save.  This creates a situation where the SaveData state doesn't always save the data, which can compromise clarity and hinder future extension.  What happens if I have situations where I want to save the data regardless of its validity?

 

My preferred solution is to check if the data is valid before invoking the SaveData state.  I'd implement this by sending a "SaveButtonPressed" message to the state loop.  The SaveButtonPressed message handling case in the Idle state calls the CheckIfDataIsValid sub vi and invoke the SaveData state only if the data is valid.

 

Getting back to your question, I could send the SaveData enum and still do the same checking in the message handler before deciding if the SaveData state really should be invoked.  I think this just leads to confusion.  If the UI is sending a state to the state machine, there is an expectation that the state is invoked.  Sending the state directly also couples the UI to the state machine.  If I want to give the user an option to save data regardless of its validity I have to create another state, SaveInvalidData.  Now I have a SaveData state and a SaveInvalidData state, which essentially do the exact same thing.

 

The whole point of having a UI loop and a Processing loop is to decouple the UI from the execution.  The obvious benefit is that the UI will continue to be responsive while the process is executing.  The less obvious (and arguably greater) benefit is the developer can modify the UI and the state machine independently.  Using the state enum as the message itself eliminates that benefit.

 

Granted, a lot of this comes down to personal preference and how you decompose your problem.  I generally decompose problems in very modular terms.  To me, the state machine is an abstraction of the core functionality of the application--the engine.  Each state in the state machine is a public method exposed by the engine.  LoadTestConfiguration, SaveData, and ExecuteTest are examples of core functional components.  They should be states.  CheckIfDataIsValid is the responsibility of the application's supporting code, not the engine, and should not be a state.

Message Edited by Daklu on 04-20-2010 07:34 AM
Message 23 of 41
(2,085 Views)

Hi Daklu,

 

I understand where your coming from, nice clear response. Has given me something to think about as i have to admit that sometimes i fall into the trap of writing 'Function machines' as you have called it. Just another quick question though. Why do you use a string value. You could still implement what you are saying but by using a seperate enum typedef specifically for the queue. I always thought it was better to use an enum for attaching to a case selector then a string.

 

I hope you dont think i am just trying to catch you out on something, i am genuinly interested, in case i am missing something.

 

Rgs,

 

Lucither

Message Edited by Lucither on 04-20-2010 10:35 AM
------------------------------------------------------------------------------------------------------
"Everything should be made as simple as possible but no simpler"
0 Kudos
Message 24 of 41
(2,067 Views)

Lucither wrote:

Why do you use a string value. You could still implement what you are saying but by using a seperate enum typedef specifically for the queue. I always thought it was better to use an enum for attaching to a case selector then a string.


In the sample I posted I left the message as a string simply because it didn't affect what the sample was intended to illustrate.  If you're more comfortable passing an enum instead of a string that's fine--just make sure you have a message enum and a state enum.

 

Using an enum is safer in that you don't have to worry about misspelling one of the messages.  Whether or not it's better is a subjective evaluation.  Sometimes (for quick and dirty vis or examples) it's simply not worth it (to me) to set up a typedef specifically for message names.  In situations where other vis will be posting messages on the queue and you don't want to have to refer back to the case structure it makes more sense to code defensively and use a typedeffed enum.

 

Truth be told, when I set up a message queue for an application the message always has the ability to carry data with the it.  For small apps I'll create a message typedef that includes a message name field (string or enum) and a data field (variant or string.)  More commonly I'll have a message class and create subclasses for each kind of message that can be posted.

Message 25 of 41
(2,049 Views)

Hi Daklu,

 

Thanks for the response. All makes sense. I like the idea of always having a data field included with your enum. When you talk about a ' Message Class' and creating subclasses, are you referring to LVOOP?

 

Sorry if it sounds like im hounding you. Truth be told i am relatively new to Labview. I have a software background in embedded controllers and .NET but am new to Labview. As such i like to pick peoples brains on here where i can. I can then pass on there knowledge as my ownSmiley TongueI have seen mention on here about LVOOP but not actually looked into it yet. I am familier with OOP from programming in .NET but thought it would be best to concentrate on getting stronger at the regular labview first.

 

Rgs,

 

Lucither

------------------------------------------------------------------------------------------------------
"Everything should be made as simple as possible but no simpler"
Message 26 of 41
(2,071 Views)

Lucither wrote:

Has given me something to think about as i have to admit that sometimes i fall into the trap of writing 'Function machines' as you have called it.


I think everyone does; it's part of the learning process.  Below is a diagram of a QSM I had a hand in creating just last year.  (Of course, I didn't create the diagram until it became too hard to follow the execution in code.)  This is just the diagram for the Start Test button!  This project is what prompted me to examine the QSM and look for better ways to implement it.

 

State Diagram.png

Message 27 of 41
(2,061 Views)

Lucither wrote:

Thanks for the response. All makes sense. I like the idea of always having a data field included with your enum. When you talk about a ' Message Class' and creating subclasses, are you referring to LVOOP?


Yep.  One comment about LVOOP... if you're familiar with OOP in .Net you'll probably find LVOOP a bit primitive.  There are no interfaces, no generics, very little object-based api, etc.  LVOOP is still relatively new and where .Net is wholely based on objects, LVOOP appears to be regarded as a bolt-on addition for "advanced" developers.


Lucither wrote:
Sorry if it sounds like im hounding you. Truth be told i am relatively new to Labview. I have a software background in embedded controllers and .NET but am new to Labview. As such i like to pick peoples brains on here where i can.


No worries.  We were all new at some point.  I've done (and still do) more than my fair share of "hounding" other developers.  To paraphrase the quote... "If I can see far it's because I'm standing on the shoulders of giants."  Be sure to check out LAVA too.

Message 28 of 41
(2,056 Views)

Hello guys.

 

First of all I would like to thanks everyone for the messages and I am glad that my thread started such big discussion about this topic. After reading all the messages I did some changes in my code and removed the semaphore and kept using the queue function (and trusting everything on it). All the enqueues are done inside the producer loop (only) using an array with enums (each element of the array corresponds to a state in the state machine). Everything is working very well and the code is very clean (the most important thing, in my opinion).

 

By the way, I have a question about the wires used for the queue. If we thing about the wires and the data flow present in our VIs, the data is always going from the left to the right. Ok, ok, we have some situations like feedback nodes and etc. But most of the times is going from left to right. If we think about what is happening with the queue we realize that the wires have a different data flow.

 

For example: if the producer loop enqueue something, this data will go from right to the left, get out from the producer loop, and then go to the right to the consumer loop. Am I right thinking this way? I know that this will not change anything in my code, I am just curious about that.

 

Thanks

 

Dan07

 

 

0 Kudos
Message 29 of 41
(2,008 Views)

Data always flows from the source to the sink, whether it is to the right left, up or down.  It is just easier to understand your code if you make it line up so that it looks like the data flows from left to right.

 

So I don't know what you mean by it flowing from right to left without seeing a more recent image of your code.

 

With a queue, you basically have a wireless data flow.  The queue's reference wire doesn't contain any real data other than a "pointer" or reference to a set of memory locations.  (I know this may start another argument about whether it is a pointer or not, but I'm not concerned about that.)  That reference is just an identifier so that the enqueue element functions can place data into memory in order at a location, and the dequeue element functions know what memory location to pull the data out in order.  So you can think of it as a wireless jump from the enqueue function to the dequeue function.

0 Kudos
Message 30 of 41
(1,996 Views)