04-28-2010 01:33 PM
JackDunaway wrote:
I don't like freewheeling loops unless there's some specific code you want executing in the timeout case
JackDunaway wrote:
But with that being said, how do you pass around the dynamic events ref (if not by local)?
P=Producer, C=Consumer
Local variable. The event structure is on the diagram of the main VI, not inside a subVI. The same is true for the the Data (graphing, processing, file I/O) loop (a subVI of GUI that shows it's panel). Event structures nested in subVIs seem to obfuscate the code. So, the P/C loop is beside the Producer loop (if the diagram has a Producer loop). Pulling the event structure away from the P/C allows us not to freewheel. The trade-off is that the Producer doesn't have access to the P/C shift register. So, if you're doing "rich" processing that requires P/C data or data from a previous iteration of the Producer, it must be done in the P/C (which makes the code easier to follow and provides a location for other loops to enqueue that same P/C state). That said, of course, I code within the event structure for Filter Events.
The subVIs I was referring to exist on the GUI (P/C, P) diagram or are kicked off by VI Server: Data (P/C, P), Hardware (P/C), Timer (P/C), and Login (P/C, P).
JackDunaway wrote:
There's also another flaw... dropping a typedef'd constant on the BD and modifying it's value. If the structure of the typedef is ever changed, the values of all BD constants are not guaranteed to survive
JackDunaway wrote:
it's going to be difficult to trace the source of producer/consumer actions if the two loops are in separate VI's (not to mention how the ref will be passed around, who creates/destroys the ref, etc...)
JackDunaway wrote:
(does anyone else do something similar?)
Good discussion.
04-28-2010 02:42 PM
LabBEAN wrote:
In that case, enjoy the freedom that follows deleting that "Idle" item in your enum 😄
I noticed that right before posting the VI, almost changed it to "Initialize" to hide the name, but figured nobody would catch it. Well, you caught me. (Actually, Idle does have it's merits in some of the loops. This command typedef services many similar processes, some of which have a desirable freewheeling Idle state)
LabBEAN wrote:
The Q ref's are passed to all loops in the beginning.
That's how I used to do it before wrapping the Q ref's in AE's. Readability has become a breeze - even for the inherently difficult task of tracing asynchronous messaging - by using the "Find All" functions. You will notice on the Idea Exchange that I have suggested many improvements for these "Find" functions that would make such tasks more powerful and enjoyable.
LabBEAN wrote:
And, here, you are exercising your right to personal preference by using the Action Engine instead of discrete VIs.
?? Action engines are discrete VI's. What are you calling a discrete VI here?
04-29-2010 08:35 AM - edited 04-29-2010 08:40 AM
...you caught me... Idle does have it's merits in some of the loops
?? Action engines are discrete VI's. What are you calling a discrete VI here?
"The four actions are Create, Send, GetRef, and Destroy". These could also be 4 discrete VIs. Action Engines, although useful at times (e.g. is my USB debug key plugged in -- well this would technically be an LV2), violate the principles of Loose Coupling and High Cohesion. In this case, the only data that your AE is providing these functions is the queue refnum:
That's how I used to do it before wrapping the Q ref's in AE's. Readability has become a breeze - even for the inherently difficult task of tracing asynchronous messaging - by using the "Find All" functions.
04-29-2010 11:57 AM - edited 04-29-2010 11:59 AM
LabBEAN wrote:
Action Engines, although useful at times ... violate the principles of Loose Coupling and High Cohesion.
What? Just the opposite!
(Keep in mind, the AE does not store the DATA, it stores a reference to the messaging construct [queue, notifier, user event] which stores the data. There's no data coupling between two VI's that each use the "Send" function of the AE.)
Right now, all of the caller VI's of that wrapper are abstracted from the Queue Ref, except for the consumer (who, consequently, is always the only caller of the "GetRef" function). The top-level VI that Creates and Destroys is not tightly coupled due to the queue ref, and the VI's that "Send" messages are also abstracted from the Queue Ref (i.e., no need to pass the Queue ref as an input to the connector pane, or even worse, using a Control Value Set method or a global ref).
When my top level is running, I can open a new VI, drop that AE on the VI and configure a "Send" message and run it, injecting a message into the running program. There's no way you could have that loose coupling if you're passing around your Queue refs from the top-level creation into the SubVI producers and consumers.
For messaging, tight coupling can be imposed due to the Queue ref passing in and out of connector panes. Get loose by "going wireless" with the messaging protocol wrappers.
04-29-2010 12:29 PM
Create, GetRef, and Destroy each require ZERO inputs since queue references can be obtained by the queue name, which is an architecture property and doesn't change at run-time. You could argue that the queue reference should be passed in (actually you argued against it), so that would be 1 input each. Instead they have 3. Multi-function VIs obfuscate the code. Determining which inputs are required for each function is not intuitive and complicates debug for other developers. If "command" is a required input, which logically it should be since you can't "Send" without a "command", then it ends up being required for the other functions of the AE that don't need it. This causes Tight Coupling.
Highest cohesion is achieved by the use of discrete VIs: 1 function per VI.
High Cohesion and Loose Coupling are separate yet related concepts. Good design patterns exhibit both.
04-29-2010 12:32 PM
JackDunaway wrote:
LabBEAN wrote:
Action Engines, although useful at times ... violate the principles of Loose Coupling and High Cohesion.
What? Just the opposite!
(Keep in mind, the AE does not store the DATA, it stores a reference to the messaging construct [queue, notifier, user event] which stores the data. There's no data coupling between two VI's that each use the "Send" function of the AE.)
Right now, all of the caller VI's of that wrapper are abstracted from the Queue Ref, except for the consumer (who, consequently, is always the only caller of the "GetRef" function). The top-level VI that Creates and Destroys is not tightly coupled due to the queue ref, and the VI's that "Send" messages are also abstracted from the Queue Ref (i.e., no need to pass the Queue ref as an input to the connector pane, or even worse, using a Control Value Set method or a global ref).
When my top level is running, I can open a new VI, drop that AE on the VI and configure a "Send" message and run it, injecting a message into the running program. There's no way you could have that loose coupling if you're passing around your Queue refs from the top-level creation into the SubVI producers and consumers.
For messaging, tight coupling can be imposed due to the Queue ref passing in and out of connector panes. Get loose by "going wireless" with the messaging protocol wrappers.
Message Edited by JackDunaway on 04-29-2010 11:59 AM
From what i understand of "Loose Coupling" and "High Cohesion" I have to come down on the side of LabBEAN unless I have misread what you are doing inside the AEs. This is my thought. If I have to pass a different type value via the queue, I'd have to mod the queue and all VI's using the queue and the queue ref. If the code changes it should be retested and could affect the operation of whatever is using it. THat sounds like "tight coupling" from what understand.
On the other hand if the queues were burried in LVOOP classes, then I could create a child class and over-ride VIs in the child class that could use a different queue than the parent.
Any app that used the Parent class's methods to communicate will not see a change and the parent class will not have to be re-tested. Only the new child class would require testing. So in that case there is loose coupling.
As to "high cohesion" I'm thinking you get that with BOTH the AE in wrappers and the LVOOP approach.
Please feel free to lecture me on anything I said wrong there since my education was Physics, Material Science, and Electrical Engineering and all of the "gang of Four" stuff was being taught/written in another building across campus from where I hung out.
Ben
04-29-2010 12:34 PM
LabBEAN wrote:...
Highest cohesion is achieved by the use of discrete VIs: 1 function per VI.
High Cohesion and Loose Coupling are separate yet related concepts. Good design patterns exhibit both.
So a wrapper for each AE method is consistant with "High Cohesion" correct?
Ben
04-29-2010 12:42 PM
JackDunaway wrote:
Get loose by "going wireless" with the messaging protocol wrappers
See Option 3 above to optimize for less wires 😄
(I think I still like Option 2, though.)
04-29-2010 12:53 PM - edited 04-29-2010 12:56 PM
Ben wrote:
So a wrapper for each AE method is consistant with "High Cohesion" correct?
So let me first say I'm speaking from the experience of seeing some nasty AEs -- AEs that keep all this data on the shift register for no apparent purpose. Creating wrappers in that case just seems like extra work. Why not call the subVIs in each of the AE cases directly?
By creating wrappers for AEs, we've masked the public problem, but we still have a private one. Maybe that's okay...
If you have to protect the queue reference, then the only option is an AE (or LVCLASS). Get the reference inside the AE and don't pass it out. But, in the case discussed here, he's passing out the reference.
I guess I just like following the "rules" whenever possible so that the code is consistent in case other developers have to take a peek.
04-29-2010 01:11 PM
OK, I am beginning to understand where you're coming from, let's see if I can reconcile why I like these "weakly cohesive" AE's for the benefit of a "loosely coupled" application architecture:
You are arguing that the message wrapper itself has weak cohesion, and is "loosely defined" since it's unclear exactly when the inputs should be wired. We agree that taking my AE and splitting it into 4 discrete VI's would be ridiculous, because there are 4 primitives that do the job! And those primitives are very strictly defined, with no questions for "unused inputs" (since there aren't any). So basically, if we nix the weakly cohesive AE in favor of the strongly defined primitives, we end up passing the control refs from Top Level Creator, to SubVI, to SubVI.... And if you have a SubPanel architecture (I do), you end up having to pass the Control References via "Control Value Set" method to the VI inserted into the panel (which I really dislike for it's fragility).
Instead, wrap those Control Refs in an AE, and you have no data coupling (where coupling is due to dataflow of the Control Ref wires going in and out of connector panes) between your top level VI's and user VI's inserted into SubPanels. (By the way, named queues would be one method to get around this type of coupling, but it doesn't work for Notifiers and User Events)
Using the "loosely defined connector panes" of the Messaging AE's is simply where familiarity and training comes in: I have lots of those messaging wrappers, and they all behave the same with those same four actions. I can be guaranteed that almost all the time there will be one Create, one GetRef (the consumer), and one Destroy. All the rest are Sends (which, consequently, Send is the default input - I leave that input unwired since it is used most - and familiarity and training tells me that's a "Send"). Also, I can do a "Find All" on that VI, and immediately jump to every single message sender. THAT's the biggest advantage of this entire construct - being able to very quickly find all instances of who is talking to who in the entire application architecture.
For instance, if you pass in a control ref into a SubVI and it goes into a large case structure, what all cases send messages (or destroys the queue, or sneaks a few items out, or passes into another VI which does some of the above??)?
On the other hand, if you have a Messaging Wrapper, it's easy to find all instances of talkers on the asynchronous messaging system:
I'm certainly open to suggestions, comments, questions, and insults - any architecture set in stone is doomed to fail eventually.