08-19-2021 04:42 AM - edited 08-19-2021 04:55 AM
After you have grown out of QMH (and all its single queue derivatives) and have implemented a real, correct Mealy state machine (like in EDQSM, for example), complete with separate queues for events/messages and actions, the notion of state not commingled with either of the two previous notions, and the facilities in your design pattern that allow you to vary the reaction of your module/actor depending not only on event/message it receives but also on state (its own and/or that of its perceived environment), you will realize very soon the fundamental limitations of the classical Finite State Machines (both Moore and Mealy) themselves:
•Flatness of the state model. Flat state machines do not provide the means to construct layers of abstraction. All states are equally visible and are considered to be at the same level of abstraction.
•Lack of support for concurrency leads to a combinatorial explosion in the number of states.
What's missing in the traditional state machines is the mechanism for factoring out the common behavior in order to share it across many states.
The most important innovation of hierarchical finite state machines (HSMs, HFSMs, statecharts) over classical FSMs is the hierarchical state nesting. The value of state nesting lies in avoiding repetitions, which are inevitable in the traditional "flat" FSM formalism and are the main reason for the "state-transition explosion" in FSMs. The semantics of state nesting allow substates to define only the differences of behavior from the superstates, thus promoting sharing and reusing behavior.
H(F)SM a.k.a Statechart features:
– Nested states
– Actions and Activities
– Guards
– History
– Concurrency
– Orthogonal Components
So the previous state diagram turns into:
This is what first LabHSM and (much) later Statechart Toolkit tried to implement.
What’s Wrong With Even Hierarchical Finite State Machines (Statecharts)?
Maintainability, Modularity, Reusability, Scalability
Adding or removing states is still hard because with every change in the state structure a lot of transitions may need to be revised too! The hierarchy resolves some problems, but a reactive HFSM still results in some sub graphs being fully connected with many possible transitions. Despite hierarchy of the states themselves, assuming n states, the n2 possible transitions of an HFSM rapidly turns to a single large monolithic structure that is very complex to debug, update and extend, let alone reuse. In particular, a long sequence of actions, with the possibility of going back in the sequence and re-execute a task that was undone by external agents (e.g. the environment), still requires a fully connected subgraph.
One-way Control Transfer
FSMs use state transitions, that transfer the control of the agent in a way that is very similar to the GOTO statement, that is now abolished in high level programming languages.
What's better than even Statecharts (HFSMs)? I am not telling you yet! I am working on a ... framework now that I suspect will become very ... upending.
08-21-2021 02:08 PM
@crossrulz wrote:
If you are referring to the actual reference wires, I would agree. I find it easier to contain the QMH in a library. Private to the library is an Action Engine which maintains the queue. I then have public methods that use the AE to send specific messages to the QMH. So what this does for me is I do not have reference wires running everywhere. I rely on the icon of the method and/or a look up string to tell me where the message is being sent to
I've been thinking ahead about what to do if I build a QMH for a particular device or system but then decide to have multiples of that device or system? How would I go about packaging an linking those QMH programs together? Is what you described above a solution to that?
I'm in the planning stages of a project that will let me exercise these design principles. A servo motor and some sensors hooked up to a micro controller (not a dev board) and connected to the main computer via Ethernet and controlled by a QMH program. Once that works, I want to build a second. It's not for work so I'll be able to share code and discuss the project on the forum.
Also, thanks for the serial video. That was so much help.
08-22-2021 02:24 AM - edited 08-22-2021 02:29 AM
@garthenar wrote:
I've been thinking ahead about what to do if I build a QMH for a particular device or system but then decide to have multiples of that device or system? How would I go about packaging an linking those QMH programs together? Is what you described above a solution to that?
DQMH ships with two types of modules: Singletons and Cloneables.
A cloneable module can be started multiple times in parallel, so you can have the same code running at the same time for each of your devices or systems.
http://delacor.com/documentation/dqmh-html/DQMHSingletonModulevsCloneableMo.html
edit: other frameworks like AF or Messenger Library offer similar functionality
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)
08-22-2021 04:19 AM - edited 08-22-2021 05:17 AM
It is extremely sad that proponents of the different forms of the single queued message handler (in particular, DQMH and "THE" Actor Framework) don't seem to even want to listen to what Norm Kirchner, Dr. DJ Powell, and I have been telling the Labview community for like 17 years now already. On top of all its... deficiencies inherited from QMH/"command" pattern AF has done a huge disservice to the community by actually repelling, scaring away Labview developers from the actor model, which is by itself absolutely great and much closer both to real world objects and to the original vision of Alan Kay ( the very man who coined in the term "object-oriented programming") than the later C++/Java and yes, LVOOP implementation of OOP with "castrated", "zombi", "passive" objects consisting of only data(attributes) and functions(methods) which are not real messages and need to be synchronously called by some external code for anything to happen to the object. "Traditional" OOP apologets like to claim its similarity to real life objects and often use cats and dogs as examples. But a real dog doesn't bark because some "God" main function/loop called the method Bark()! Neither it obediently, unconditionally and immediately barks just because somebody commanded it to bark. A real life dog barks as a REACTION to yes, maybe a command from the owner but also just because, say, a stranger appeared. But not always! Whether it barks or not also depends obviously on whether it is, say, sleeping at that moment or awake (state). Neither DQMH nor AF make it clear, facilitate, allow to implement explicitly this very essence of behavior, the dependence of actor's reaction on BOTH the received input (message/event/command/trigger) AND the state of the actor at the moment when the input is received. Both DQMH and AF still commingle the notions of state, event(message) and (re)action, do not have a queue for events/messages/commands/triggers separate from the main MHL (actions) queue, and do not enforce the Run to Completion principle by design as it has been done in my EDQSM and LabHSM, Norm Kirchner's TLB, and Dr. Powell's library. What it all leads to has been explained and demonstrated many times by us, three, but no, there are still people who insist that the FUNDAMENTALLY FLAWED QMH ("command") pattern and its derivatives are "good enough" tools for at least some projects for which, say, EDQSM, TLB or any other properly implemented actor driven/backed by queued Mealy (let alone hierarchical/statechart) state machine would be "too complex", an "overkill", etc. 🙄 They also like to say something like "if you use correctly and observe some do's and don'ts everything will be OK". No it won't! Haphazardly writing to the single "message handling" queue in multiple places, violations of the Run To Completion principle will eventually get you with rare intermittent iirreproducible bugs and very low maintainability. The failure to separate events/messages from actions that are in reality reactions to those messages, which in turn may (and in the general and most common case do) depend on the receiver's states (which are not the same as actions either) and are definitely the receiver's business, not the sender's, leads to tight coupling as just one of the nasty consequences (Hello, AF, again, with its message classes that contain not only the message itself but also the reaction to it!). To summarize, no, it is not a waste of time to study QMH and its flaws, but "waste of time" can become the least of your problems once you run into the other consequences of using QMH (and/or its derivatives like DQMH or Actor Framework) in a real even slightly non-trivial project.
08-22-2021 10:51 AM
@styrum wrote:
It is extremely sad that proponents of the different forms of the single queued message handler (in particular, DQMH and "THE" Actor Framework) don't seem to even want to listen to what Norm Kirchner, Dr. DJ Powell, and I have been telling the Labview community for like 17 years now already. On top of all its... deficiencies inherited from QMH/"command" pattern AF has done a huge disservice to the community by actually repelling, scaring away Labview developers from the actor model, which is by itself absolutely great and much closer both to real world objects and to the original vision of Alan Kay ( the very man who coined in the term "object-oriented programming") than the later C++/Java and yes, LVOOP implementation of OOP with "castrated", "zombi", "passive" objects consisting of only data(attributes) and functions(methods) which are not real messages and need to be synchronously called by some external code for anything to happen to the object. "Traditional" OOP apologets like to claim its similarity to real life objects and often use cats and dogs as examples. But a real dog doesn't bark because some "God" main function/loop called the method Bark()! Neither it obediently, unconditionally and immediately barks just because somebody commanded it to bark. A real life dog barks as a REACTION to yes, maybe a command from the owner but also just because, say, a stranger appeared. But not always! Whether it barks or not also depends obviously on whether it is, say, sleeping at that moment or awake (state). Neither DQMH nor AF make it clear, facilitate, allow to implement explicitly this very essence of behavior, the dependence of actor's reaction on BOTH the received input (message/event/command/trigger) AND the state of the actor at the moment when the input is received. Both DQMH and AF still commingle the notions of state, event(message) and (re)action, do not have a queue for events/messages/commands/triggers separate from the main MHL (actions) queue, and do not enforce the Run to Completion principle by design as it has been done in my EDQSM and LabHSM, Norm Kirchner's TLB, and Dr. Powell's library. What it all leads to has been explained and demonstrated many times by us, three, but no, there are still people who insist that the FUNDAMENTALLY FLAWED QMH ("command") pattern and its derivatives are "good enough" tools for at least some projects for which, say, EDQSM, TLB or any other properly implemented actor driven/backed by queued Mealy (let alone hierarchical/statechart) state machine would be "too complex", an "overkill", etc. 🙄 They also like to say something like "if you use correctly and observe some do's and don'ts everything will be OK". No it won't! Haphazardly writing to the single "message handling" queue in multiple places, violations of the Run To Completion principle will eventually get you with rare intermittent iirreproducible bugs and very low maintainability. The failure to separate events/messages from actions that are in reality reactions to those messages, which in turn may (and in the general and most common case do) depend on the receiver's states (which are not the same as actions either) and are definitely the receiver's business, not the sender's, leads to tight coupling as just one of the nasty consequences (Hello, AF, again, with its message classes that contain not only the message itself but also the reaction to it!). To summarize, no, it is not a waste of time to study QMH and its flaws, but "waste of time" can become the least of your problems once you run into the other consequences of using QMH (and/or its derivatives like DQMH or Actor Framework) in a real even slightly non-trivial project.
Technically, dog bark while they are sleeping, too.
08-22-2021 12:11 PM - edited 08-22-2021 12:16 PM
Sure, they can bark while sleeping too. But will you then claim that "dogs never sleep!" based on just that? The whole "state is evil" thing, the attempts to pretend it doesn't exist, to shove it "under the carpet", to avoid "side effects" when the program is made for those very "side effects", to build "ivory towers" of "purely functional" code were not a total "waste of time", of course. They gave us, in particular, immutability and functional programming which have their place (use cases). But it is the denial of the fundamental flaw of the "traditional" (C++/Java/LVOOP) OOP model and its patterns like QMH/"command" which haven't fixed but rather masked that flaw, the stubborness of its blind fans that lead to the "billion dollar disaster" of countless monolithic OOP projects, not statefulness as a fact of life and not the original Alan Kay's idea of OOP as building programs of components which are "mini-computers" themselves and communicate with each other by messages (synchronously calling a method of a "traditional" object is obviously not a realistic "representation" of sending it a message). He never meant that breaking programs into data and procedures should be replaced by always gluing them together into lifeless passive objects that can be only directly manipulated, not really (asynchronously and without any knowledge about the receiver's state) communicated with. Only sharing mutable state between objects should be avoided, not state itself!
https://link.medium.com/t99eCsXYVib
08-22-2021 02:22 PM
@styrum wrote:
Sure, they can bark while sleeping too. But will you then claim that "dogs never sleep!" based on just that? The whole "state is evil" thing, the attempts to pretend it doesn't exist, to shove it "under the carpet", to avoid "side effects" when the program is made for those very "side effects", to build "ivory towers" of "purely functional" code were not a total "waste of time", of course. They gave us, in particular, immutability and functional programming which have their place (use cases). But it is the denial of the fundamental flaw of the "traditional" (C++/Java/LVOOP) OOP model and its patterns like QMH/"command" which haven't fixed but rather masked that flaw, the stubborness of its blind fans that lead to the "billion dollar disaster" of countless monolithic OOP projects, not statefulness as a fact of life and not the original Alan Kay's idea of OOP as building programs of components which are "mini-computers" themselves and communicate with each other by messages (synchronously calling a method of a "traditional" object is obviously not a realistic "representation" of sending it a message). He never meant that breaking programs into data and procedures should be replaced by always gluing them together into lifeless passive objects that can be only directly manipulated, not really (asynchronously and without any knowledge about the receiver's state) communicated with. Only sharing mutable state between objects should be avoided, not state itself!
https://link.medium.com/t99eCsXYVib
I think you should consider "decaf".
08-22-2021 06:32 PM
I did look at DCAF recently, maybe not very thoroughly though.
The very first shock: no primary project provider for its own configuration files in such elaborate framework - you can't just double-click on such a file to open it in the editor, you must open the editor first and then open a particular config from within it!
"Modules" are still "dead"/"passive" and animated by external "engines" instead of making each module an actor/"engine".
Using the "tag bus" for communications between modules instead of buffered (queued) asynchronous messaging may lead to the same dreaded mutable state sharing.
The configuration files don't abstract or store the behavior information for the modules themselves (no mapping of possible events and states combinations to (re)actions and next states in any form). It is still completely left to the programmer how to do the "processing" part between reading the inputs and writing to the outputs within each module.
Justifying it by RT needs they resort to continuous (timed) polling everywhere and suggest to implement any event-driven processing desired (like blocking reads from event queues) somewhere "in parallel" to the framework.
The framework looks abandoned by NI: no dedicated web page, only the forum is left.
08-23-2021 12:26 AM
Fair enough. I don't use DQMH much so that example could be good. The example that ships from NI as never been useful to me.
08-23-2021 12:26 AM
@styrum wrote:
I did look at DCAF recently, maybe not very thoroughly though.
Touche, that was awesome!
The very first shock: no primary project provider for its own configuration files in such elaborate framework - you can't just double-click on such a file to open it in the editor, you must open the editor first and then open a particular config from within it!
"Modules" are still "dead"/"passive" and animated by external "engines" instead of making each module an actor/"engine".
Using the "tag bus" for communications between modules instead of buffered (queued) asynchronous messaging may lead to the same dreaded mutable state sharing.
The configuration files don't abstract or store the behavior information for the modules themselves (no mapping of possible events and states combinations to (re)actions and next states in any form). It is still completely left to the programmer how to do the "processing" part between reading the inputs and writing to the outputs within each module.
Justifying it by RT needs they resort to continuous (timed) polling everywhere and suggest to implement any event-driven processing desired (like blocking reads from event queues) somewhere "in parallel" to the framework.
The framework looks abandoned by NI: no dedicated web page, only the forum is left.