LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Is QMH obsolete? Am I wasteing my time?

Solved!
Go to solution

@FireFist-Redhawk wrote:
If an inexperienced user is starting to get into LV, race conditions are just something they're gonna have to learn about sooner or later. I would almost argue that all frameworks are equally susceptible to race conditions if the developer is someone who has no idea what race conditions are.

 


My theory is that because most LabVIEW programmers learn on race-condition-weak architectures, they come to believe that race conditions are a fact of life.  I have multiple clients that are part-time developers; when they are using my DEV template from Messenger Library they have no issues with race conditions, while when they have built something like the NI-QMH they have invariably have terrible trouble with horrible race conditions.

 


@FireFist-Redhawk wrote:

If all you need is two loops to achieve what you want, then it's really not that bad. 


You never need two loops; that's the flaw.  The "brains" of your module should be one loop. 

 

I realize I can't convince people, but it really is bad.  You've accepted that careful disciple or a toolkit of race-condition duct tape is the normal state of things.   

0 Kudos
Message 61 of 99
(1,507 Views)

@drjdpowell wrote:

Interestingly, I read that and didn't find anything much to criticize.  But that is because it only documents an Event loop and one or more Queue loops that do tasks like acquiring data.  It doesn't actually specify, as I have always seen everyone do and is part of Sam's "Dos and Don'ts", that one Queue loop should be the central "brains" of the app, with all State Data and all action routed through it.   It would be entirely possible to develop the template in a stronger direction, with the Event loop as the "brain" and all Queued loops as focused, simple "Helper loops".  One would have to add State Data to the Event Loop along with a User Event to receive messages.  Add some error handling, with all errors handled in the Event loop, and you have a simple architecture that isn't that bad.  My "DEV Template" in "Messenger Library" is basically a developed version of that.  

 

BUT, the described architecture is not complete enough to actually use, and the actual template generated makes further design choices, with the single Queue loop acting as brain (including the error handling).   This is the poor choice that is not documented, and I have never seen justification for.  It's the choice that makes this design so vulnerable to race conditions in the hands of the inexperienced, and causes the experienced to have to follow limiting rules to avoid problems.


That is actually a valid criticism. I've often thought tha DQMH could get away without the queue and the message handling loop and just do everything in the Event Loop.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 62 of 99
(1,485 Views)

@Taggart wrote:
That is actually a valid criticism. I've often thought tha DQMH could get away without the queue and the message handling loop and just do everything in the Event Loop.

If you want to try, I recommend a structure like this (ignore the Messenger Library comm stuff, as you would adapt it into the DQMH communication structure).  This is the "Instrument" module from the YouTube Example project that comes with Messenger Library.

2021-08-10 16_49_41-Instrument.lvclass_Actor.vi_6640001 (clone) Block Diagram.png

0 Kudos
Message 63 of 99
(1,472 Views)

Build using a modified version of Norm's TLB state machine as well as our own publish/subscribe messaging system, this is what our primary tasks look like. There are two loops which are executed before and after the main loop which are for initialization and tear down operations. The lower large loop is the state machine. The case structure on the left is for the various action while the case structure on the right is the state transition logic which handles the events and messages. the upper loop with the event structure is used to catch UI events and which will trigger a system message which is handled by the queue which feeds the state transition logic. It's queue is our publish/subscribe messaging queue. Our messages types/names are string based. Via a naming convention we can route the messages locally to only this VI or through the message router. The naming convention supports directed messages to single tasks as well as broadcasting them to all subscribers. the queue that feeds the "action" case statement can only have actions enqueued by the state transition case statement. Control loops that do not have a UI will not include the event loop. We have found this to be a very robust framework.

 

TLB-ExecEngine.png



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
0 Kudos
Message 64 of 99
(1,467 Views)

Thinking back to Fab's design decisions presentation, I think the reasoning for the 2 separate loops was to seperate Events coming in from the outside world from the messages getting queued up. Although I think what you show also accomplishes that.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 65 of 99
(1,455 Views)

@Taggart wrote:
I've often thought tha DQMH could get away without the queue and the message handling loop and just do everything in the Event Loop.

I won't go down the rabbit hole of the QMH-and-numbers-of-loops discussion. I agree with some things and disagree with others. No minds will be changed in this thread, and there's nothing to win here.

 

But I will say this: Another great thing about DQMH is that you can actually implement these things if you'd like! 

 

You can create a DQMH template with the MHL removed, and the scripting tools will still work. You only need to move the code inside the "Initialize" and the "Exit" MHL cases outside of the MHL and you can safely delete the MHL.

 

Of course, there will be more manual plumbing to be done, like recreating anything that the scripting would do for you in the MHL (like error handling in general, or sending the notification for requests with reply). If you remove the Message Queue altogether, there will be more manual scripting involved for cloneables.

 

I'm sure you could use the Messenger Library inside a DQMH module, and/or inherit from the Message Queue to change how it works, etc. etc. 




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (The Future of Team-Based LabVIEW Development)


Message 66 of 99
(1,412 Views)

@Mark_Yedinak wrote:

Build using a modified version of Norm's TLB state machine as well as our own publish/subscribe messaging system, this is what our primary tasks look like. There are two loops which are executed before and after the main loop which are for initialization and tear down operations. The lower large loop is the state machine. The case structure on the left is for the various action while the case structure on the right is the state transition logic which handles the events and messages. the upper loop with the event structure is used to catch UI events and which will trigger a system message which is handled by the queue which feeds the state transition logic. It's queue is our publish/subscribe messaging queue...

 

TLB-ExecEngine.png


Ah, you've prompted me to go look at Norm's The Rebirth of The LabVIEW State Machine talk.  He talks a lot about the issues of the standard QMH structure.   This was referred to as a "Queued State Machine" back in the day, and he demonstrates some of the problems if you don't follow Sam's "Dos and Don'ts".  I liked this slide:

The Evil.png

 

He attacks the problem by developing the design into a true Finite State Machine, and introduces "Actions" and "Triggers" Queues.  He talks about the advantage of the Action Queue being private, which is that same as my private Action Stack in Messenger Library.

 

Note to Mark: looking at your image, you seem to have injected your external Messages into the "Action Queue", rather than into the "Trigger Queue" that UI Events go through.  That seems to destroy the advantages of a private Action Queue, since it is no longer private.  You have the advantages for UI Events, but not for Messages from other modules.  I would have thought all external input should enter as triggers.

0 Kudos
Message 67 of 99
(1,379 Views)

@drjdpowell wrote:

Ah, you've prompted me to go look at Norm's The Rebirth of The LabVIEW State Machine talk.  He talks a lot about the issues of the standard QMH structure.   This was referred to as a "Queued State Machine" back in the day, and he demonstrates some of the problems if you don't follow Sam's "Dos and Don'ts".  I liked this slide:

 

He attacks the problem by developing the design into a true Finite State Machine, and introduces "Actions" and "Triggers" Queues.  He talks about the advantage of the Action Queue being private, which is that same as my private Action Stack in Messenger Library.

 

Note to Mark: looking at your image, you seem to have injected your external Messages into the "Action Queue", rather than into the "Trigger Queue" that UI Events go through.  That seems to destroy the advantages of a private Action Queue, since it is no longer private.  You have the advantages for UI Events, but not for Messages from other modules.  I would have thought all external input should enter as triggers.


No, all external messages are received on the Trigger queue. The only thing feeding the Action queue is the "Triggers" case statement. External messages are received via the Trigger queue as well as local triggers such as UI events or periodic triggers which are sent via timer tasks. This particular example doesn't have any timers but we have the concept of periodic triggers which are fired off from very simple timers tasks that do nothing but send the periodic trigger. The main state machine can start/stops the timer tasks as needed. Even built in self correction in the timer tasks to avoid clock drift over extended periods of time.



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
0 Kudos
Message 68 of 99
(1,373 Views)

@Mark_Yedinak wrote:

@drjdpowell wrote:

 

Note to Mark: looking at your image, you seem to have injected your external Messages into the "Action Queue", rather than into the "Trigger Queue" that UI Events go through.  That seems to destroy the advantages of a private Action Queue, since it is no longer private.  You have the advantages for UI Events, but not for Messages from other modules.  I would have thought all external input should enter as triggers.

No, all external messages are received on the Trigger queue. The only thing feeding the Action queue is the "Triggers" case statement. External messages are received via the Trigger queue as well as local triggers such as UI events or periodic triggers which are sent via timer tasks. 


Ah, sorry, hard to diagnose a picture.  I saw the enum <Display Execution> being enqueued in the Event Loop, and knew your messages were string based, and assumed they were handled by the other string Queue.  But I see now that the Dequeue in the State Machine section is outputting a string.

 

 

0 Kudos
Message 69 of 99
(1,363 Views)

Yes, our queues accept a string or an enum for the input. If an ENUM is the input it simply converts it to a string. It sort of offers the best of both worlds. The enums allow us to manage messages names a little easier yet we still get the flexibility of using strings for the message names. The message queue has a standard LabVIEW queue and a small TCP server for receiving messages. External messages are placed in the LabVIEW queue. When posting a message, if the message name is enclosed in '<' and '>', it does not get sent to the message router since our naming convention indicates that is an internal message. Without the '<' and '>', the message is sent to the message broker for delivery. So, we do introduce a small bit of latency for messages/triggers if the trigger can be triggered both internally and externally since the message will have to go through the message router.



Mark Yedinak
Certified LabVIEW Architect
LabVIEW Champion

"Does anyone know where the love of God goes when the waves turn the minutes to hours?"
Wreck of the Edmund Fitzgerald - Gordon Lightfoot
0 Kudos
Message 70 of 99
(1,355 Views)