04-30-2013 06:24 PM
Couldn't resist playing with this variant a bit more... got a new revision that eliminates all of the mucking about with asynchronous VIs. Instead, I use the XControl approach of a VI that is only run when it has events to process and when it is done processing the events, it quits.
This new version is still saved in LV 2012, but now uses only features that could save back all the way to, oh, somewhere around LV 8.0, I think.
You likely still won't want to use this variant, but it's a lot cleaner and might be inspirational to some of you who like to play with these types of architectures.
05-01-2013 01:33 AM
If I go quiet it's not because I'm sulking, I need a bigger harddrive to load LabVIEW 2012 to look at the examples.
I know that action engines are not perfectly implemented in LabVIEW (we never call them that I hasten to add!). I have some ideas on functionally encapsulated OOP in LabVIEW (pure flights of fantasy) which is what we were aiming to achieve all those years ago, if anyone is interested I'll put the effort in and stick it on my blog or in the ideas exchange. It's a fair amount of effort tho'.
Steve
Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop
05-01-2013 11:16 AM
Similarly, if I go quiet, it's because this whole discussion is related-to-but-not-the-same-as the job I actually get paid to do, so it may go back burner for a while if other tasks heat up. 🙂
05-01-2013 06:49 PM - last edited on 05-03-2013 09:39 AM by JordanG
But before I go quiet...
Here are 13 (THIRTEEN) different implementations of what I'll call "potential hardware APIs". They are all generally "by reference" themes, except the last one that shows how you can talk to some hardware and avoid reference types entirely (I have a fondness for the way the VISA API works, which is entirely not the same as saying I have a fondness for VISA, of which I know just enough to be dangerous).
Unzip and open "Demo.vi" and then scroll through it. I start with Steve's action engine and proceed through different variations, some useful, some horrrific, one barely plausible, a couple dangerous, two that I actually use myself (but I don't mention which ones those are... I'm not campaigning for one over the other, just laying out options with pros and cons).
They are numbered 0 through 0xB, with number 1.5 stuck in as the only non-integer. Sorry... that's just how this turned out. If people find this information useful, I may clean it up.
And if you have any key variations that I missed, please post here.
The first attachment is saved in LV 2012.
The second attachment is saved in LV 9.0. All the examples work in 9.0 EXCEPT number 6 and 7 which use features that didn't exist back then.
[Attachments were removed and modified. The modified attachments can be found in AristosQueue's next post.]
05-02-2013 07:18 AM
@AristosQueue (NI) wrote:
BUT I like the idea of having all the code for a given "instrument" in the same diagram. There's actually something quite nice about seeing the serialization for a by reference object -- when I have to use such -- in one location and being able to get a clean place to see the order in which functions are called, and having one place to drop probes or place asserts. It's a good idea... in theory. I just don't think LV has anywhere near the right tools for implementing it today.
I've done similar things in the past, although in my case they weren't singletons. Effectively, they were actors who communicated through user events, so you get all the code in a single diagram, but the wrappers for sending the commands had the separate conpanes (which I agree with wholeheartedly) and each message had a separate event case, just like your example. The main problem I remember with that is that as the number of messages grows, the registration becomes unwieldy and you can become tempted to go down the path of "combine messages with the same data type to a single case and add a name to the message", which effectively brings you back to having to type in the correct names, etc.
As for what I think Steve's main point is - I tend to do the same only in cases where I'm really sure I will have a singleton. For example, my DB access is usually a set of by-ref VIs where each project gets wrappers for a couple of the VIs, so that all the VIs in the same project access the same connection with zero external config, so as far as the user is concerned, there is no DB. The way this works for me is that each of the wrappers starts by calling an action engine which manages the actual connection.
Regarding the race condition in your implementation 6, I assume you're talking about the lack of dependency between registering the event and writing the event refs to the global (I'm ignoring the fact that you didn't check for errors like you did in the other implementations). If someone tried to run another VI in parallel, it could be possible in theory to successfully generate an event before the registration occurred, and thus the event would be lost. There is also a bug there where if you call the init VI again (or generally call VIs out of order), you will get undesired behavior. In the case of the DB access I mentioned earlier, this is handled by the internal action engine which is responsible for error recovery (although it still requires explicit init to get the connection info).
05-02-2013 01:50 PM
Did some cleanup of the demos and made a couple tweaks in responses to some feedback I got.
05-02-2013 07:59 PM - edited 05-02-2013 08:04 PM
Thanks Steve and Steve.
Pardon me for chiming in. I'm going to argue with my betters. Yup! And I'll present something else.
Steve (AQ) Each of those smell like a "God Object" Anti- pattern. Why should the calling project need to know anything about "How" or "WHAT" will provide Fn(x) on Project "Y"? Yes, If I want one "thing" to perform similar functions on multiple projects I need a driver! Lets explore a Power Supply. NI DCPWR is a good driver for a large number of instruments. But MyProject needs a CV Supply between 0 and 5VDC at low current.
The Class defines itself. Any device capable of 0-5VDC at low current. A "Resource Module" (Wireless AE) can define that supply as a (GPIB, SER, TCP/IP, USB TMC, DAQMX AO) resource and provide a single AE that does the functional encapsulation. The caller doesn't need to know a thing about how this MyProject fullfills the requirement of "Generating a DC Volatage between 0 and 5VDC at low current." If I need to change it just change the Class private data. (Swap VISA for DAQmx or ????)
The only requirement is that the device meets the needs of the experiment (in this case providing a limited stimulus but a response measuring device is just as valid)
Steve (Swatts) you didn't speak enough on functional encapsulation! Does MyProject need a CI source as well as a CV Source? NO? well why wouuld we expose any CI capability to MyProject? That arbitrarilly limits the gear I can use and makes MyProject harder to validate since I have "Capabilities" that I do not use! (We won't mention the side benefit that if the project scope suddenly changes you have real work to do to justify the cost of validating the new capability)
Here is a DAQmx Implementation Of A Resouce Module for supplying 0-5VDC at Low Current for MyProject
AQ, Yes, with more complecated equpiment you can press the bounds of wiring to an icon. That's when you go to nirvana and create wrappers for each method that expose the correct con pane (and required terminals) for the method. Oh, I havent fixed the itty bitty terminals on the Icon inside the wrappers? Have you seen a VI? Yup no need to show the icon inside the wrappers at all! inside the wrappers we can have those huge, ugly, yellow VIs with only the connections of interest shown! YES a dynamic "con pane" for each Method exists today!
Now lets do talk about integration. Since all my gear is wrapped in a RM and any function I want is a method away I can establish any static state of my system and monitor the responses in debug time.
Not to mention its a "DROP-DEAD SIMPLE" construct! You don't need a CS degree to get it. You don't need to understand much besides Select Command press Run to troubleshoot a system with dead gear. Within the scope of a single project. And that's the key!
<B> Function <B/>
This vi encapulates all actions to generate 0-5VDC on MyProject
Methods
Null
Init
Config
Enable
Read
Close
<B> Author </B>
J. Bohrer
R/S
05-03-2013 05:18 PM
As for what I think Steve's main point is - I tend to do the same only in cases where I'm really sure I will have a singleton.
Actually, Steve's dislike of excess coloured wires cluttering the diagram, and this conversation on techniques, has inspired me to eliminate the wire from one of my APIs. It's actually very much NOT a singleton (there's four instances running in the project I did this week, for example), however, there is only one in use in any one "actor" (as its a system for actors to publish messages for other actors). As such, I can eliminate the wire, and uniquely identify the instance required by the top-level VI in the Call Chain. My component is architected as a async VI sitting behind a message queue, so I used the top-level VI name as a queue name. The Queue name serves the same function as the global variable in AQ's examples.
I thought I'd mention this, as it can be applied in any framework that uses dynamically called VI's, allowing a separate "semi-singleton" for every such VI.
05-04-2013 01:07 PM
"Steve (Swatts) you didn't speak enough on functional encapsulation! Does MyProject need a CI source as well as a CV Source? NO? well why wouuld we expose any CI capability to MyProject? That arbitrarilly limits the gear I can use and makes MyProject harder to validate since I have "Capabilities" that I do not use! (We won't mention the side benefit that if the project scope suddenly changes you have real work to do to justify the cost of validating the new capability)"
That's because writing a blog gives me the freedom to throw out ideas that are not fully formed, I found this to be a fascinating way of tying up old ideas, I'm horribly selfish and after I've dusted myself off find the discussions really educational. This particular discussion has effectively raked over 3 forth-coming blog entries where I was going to talk about block diagrams, design decisions and their consequences and is a simple solution more scaleable than a complex solution with built in scaleability. Oh well back to the drawing board.
Our take on the scope thing is that the component (in our parlance) is tested and validated fully for all it's declared functionality, adding new functionality shouldn't affect the other functions (if your coupling and cohesion are reasonably well implemented). If you want to limit the scope then just delete them from the case statement and adjust the enum accordingly (this should be done at the start of the project or you'll be in a world of hurt).
AQ - thanks for comprehensive list, truly above and beyond. I'd add polymorphism to the action engine and the re-entrant technique is one we employ every now and again.
Much Love
Steve
Steve
Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop
05-04-2013 01:15 PM - edited 05-04-2013 01:17 PM
I used that naming technique originally too, but every now and again it failed. This appeared to be when I was debugging, it changed the order in the call chain, I didn't look too hard into it, in typical lazy fashion I just swapped to a method that worked. It was LV2011
Steve
Opportunity to learn from experienced developers / entrepeneurs (Fab,Joerg and Brian amongst them):
DSH Pragmatic Software Development Workshop