LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Moving Objects with Mouse using Dynamic Event Registration

 


JackDunaway wrote:

I don't like freewheeling loops unless there's some specific code you want executing in the timeout case


Thanks Jack.  Sometimes I wonder if I'm alone on this.  In that case, enjoy the freedom that follows deleting that "Idle" item in your enum 😄

  

 


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

 


You have a point.  Thankfully, because the structure of this type def is fundamental to the entire architecture, I've never had to change it.  That said, your solution of passing the state and data discretely is creative and does solve the issue.

 

 

 


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...)


Anytime there's asynchronous messaging, this becomes a challenge.  "Who told me to execute?"  We have a suite of fairly capable debug tools that can be called dynamically for analysis.  The Q ref's are passed to all loops in the beginning.

 

 


JackDunaway wrote:

(does anyone else do something similar?)

 


We, too, wrap all the Queue primitives since they're integral to error handling and the debug tools suite.  And, here, you are exercising your right to personal preference by using the Action Engine instead of discrete VIs.

 

 

Good discussion.


Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
Message 21 of 32
(2,412 Views)

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. Smiley Wink (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?

Message 22 of 32
(2,400 Views)

 


JackDunaway wrote:

 

...you caught me... Idle does have it's merits in some of the loops



Maybe I get away without one by using a Timer loop....  Like you, we share the same "command" type def between all the loops.

 

 


JackDunaway wrote:

 

?? 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:


 

MessageCommandWrapper.png

 

 

 


JackDunaway wrote:

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. 



Option 1:
Couple the queue refnum with the queue functions and create a separate AE for each queue refnum you use.
Drop the appropriate AE everyhere and wire an action.
"Find All" for queue operations having to do with a specific AE (e.g. GUI).
In our case this would be five AEs.

Option 2:
Use discrete wrapper VIs for Create Queue, Enqueue, Dequeue, Destroy Queue, etc.
When enqueueing to another loop, unbundle the reference and wire it in.
"Find All" for a specific queue operation (e.g. Enqueue).

Option 3:
Use queue specific discrete wrapper VIs for Create Queue, Enqueue, Dequeue, Destroy Queue, etc. (e.g. create and release queue reference each time -- except in Create Queue, just create).
"Find All" for a specific loop AND operation (e.g. Enqueue GUI).
Tradeoff: tiny performance hit by creating and releasing queue refnums

I haven't had the need to "Find" queue wrappers so far (maybe because of my debug tools?).  Either way, though, pushing an Enqueue outside the case structure for each loop (rather than dropping it with every enqueue) and wrapping it in a For Loop allows enqueueing multiple items or no items to the native queue with each loop iteration.  To enqueue to another loop, you drop an Enqueue at that point.

 

Message Edited by LabBEAN on 04-29-2010 09:40 AM

Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 23 of 32
(2,367 Views)

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
0 Kudos
Message 24 of 32
(2,353 Views)

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.


Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 25 of 32
(2,338 Views)

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

Retired Senior Automation Systems Architect with Data Science Automation LabVIEW Champion Knight of NI and Prepper LinkedIn Profile YouTube Channel
Message 26 of 32
(2,334 Views)

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

Retired Senior Automation Systems Architect with Data Science Automation LabVIEW Champion Knight of NI and Prepper LinkedIn Profile YouTube Channel
0 Kudos
Message 27 of 32
(2,333 Views)

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.)

 


Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 28 of 32
(2,331 Views)

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. 

Message Edited by LabBEAN on 04-29-2010 01:56 PM

Certified LabVIEW Architect
TestScript: Free Python/LabVIEW Connector

One global to rule them all,
One double-click to find them,
One interface to bring them all
and in the panel bind them.
0 Kudos
Message 29 of 32
(2,320 Views)

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??)?

 

QueueRef.png

 

On the other hand, if you have a Messaging Wrapper, it's easy to find all instances of talkers on the asynchronous messaging system:

 

SearchForMessageConstruct.png

 

 

I'm certainly open to suggestions, comments, questions, and insults - any architecture set in stone is doomed to fail eventually.

0 Kudos
Message 30 of 32
(2,311 Views)