10-21-2021 03:09 AM
@BertMcMahan wrote:
Theoretically I can imagine ways for you to dynamically load your potential configurable actors with each one providing a Config screen which can be dynamically loaded into a subpanel. Then your main actor wouldn't need to know ANYTHING about your called actors, but I'm not sure how I'd implement this in practice in a nice reusable way.
Personally, I basically do this. My actors** and classes generally have "Get Config as JSON" and "Set Config from JSON" methods, and the main actor can work with this JSON without knowing anything. Instead of a central "Config Editor", each individual actor can allow User control of settings, or the User can directly edit the config JSON file. This can be recursive, with SubActor config JSON embedded in the config of its Calling Actor.
**Messenger Library "actors", but this would work for AF Actors
10-21-2021 09:39 AM
@cbutcher wrote:
@WavePacket wrote:I'm not exactly sure what you mean by adapters/adapters. Do you mean that the test stations have the burden of writing the code to supply the required inputs to the database interface?
...
However, if the database interface is more "generic", for want of a better term, then I don't think that's necessarily a problem either. I understand a desire not to make Test System Actors/libraries closely coupled to the database (i.e. containing SQL statements, table names, whatever), but if you don't want either one to match the other, write some separate code to bridge the gap.
Ok, so a concrete example (link) of "bridging the gap" between a generic interface and a specific test system actor is:
Thus WatchdogDevice is an example of an "adapter." Am I understanding this right?
(If yes, the additional abstraction layer of having ConnectionWatchdog actor composed of a WatchdogDevice class seems to allow me to see connection events from many hardwares, providing that I write a WatchdogDevice for each hardware.)
10-21-2021 10:23 AM
Ok so my original thoughts though were influenced by this post from earlier in this thread:
@Taggart wrote:
Use Composition.
What I would do is take your parent class, your power meter class and create an actor for it. Call it power meter actor. Inside its private data, put a power meter class object. Make an accessor to set the exact type of powermeter. Then create a message for each of the methods on the power meter class. The actor class simply delegates to the power meter class...
I'm not claiming that the above advice was wrong, but perhaps my takeaway from it was. My takeaway was generally speaking for LVOOP & AF:
So then a few months rolled by and a specific software needs arose. I've got a database class, so let's roll through 1->4 and make a database actor.
However, recently I'm kind of getting a takeaway:
@Dhakkan wrote:
@WavePacket,
...
Your problem-domain specific actors...
@drjdpowell wrote:
Why do you have a "database actor"? ...
@cbutcher wrote:
@drjdpowell wrote:
Why do you have a "database actor"? Databases are a lot more sophisticated than some dumb hardware. They support multiple clients with transactions and ACID compliance. Why not use them directly?
I have more or less the same question.
You can create (in my opinion) a database interface if you want and inherit from it/them, but I'm not certain if an Actor is appropriate.
...
So, now my question has morphed into do actors:
I feel like every post I have to thank the people who are taking valuable time our of their days just to teach someone the may never meet. Thanks for the great contributions. I've certainly learned a lot already.
10-21-2021 10:30 AM
@Dhakkan wrote:
...
Hope this provides better clarity.
I realize this is a short reply to a post which probably took you much longer to write, but please know that it does provide more clarity and I studied your class diagram and reread your post many times. Thank you.
10-21-2021 10:34 AM
@WavePacket wrote:
WatchdogDevice -> "the bridge" if you will
- readCurrent() -> the generic interface)
- isConnected() -> the test software actor's need
Thus WatchdogDevice is an example of an "adapter." Am I understanding this right?
Bingo! By the way, Refactoring Guru gives an easier-to-understand explanation to the Gang of Four's original treatise. There, you will find the Adapter Pattern too.
@WavePacket wrote:(If yes, the additional abstraction layer of having ConnectionWatchdog actor composed of a WatchdogDevice class seems to allow me to see connection events from many hardwares, providing that I write a WatchdogDevice for each hardware.)
I'd say, 'it depends'. In a real scenario, you'd have to weigh in your design decisions around the overall requirements.
10-21-2021 02:45 PM
@Dhakkan wrote:
@BertMcMahan, Pleased to make your acquaintance virtually.
@BertMcMahan wrote:
I get that the caller actor shouldn't know about the multimeter, but I find this "breaks down" a bit when configuration comes into play. For example, if I was to configure the main program, at some point I have to say "OK you are using a multimeter on COM4" or whatever. I usually have my config editor in my main actor, so it winds up knowing about all kinds of parameters.
...
...
@Dmitry wrote:...
A Dependency Injection Container (AKA Assembler class) is, simply put, an object that knows how to instantiate and configure all other application objects and all their dependencies. ...
...
I'll have to admit that I'm also confused about this exchange. I'm not initially seeing the difference between the main actor knowing the config details (which @BertMcMahan seemed concerned about) and the assembler class (which @Dmitry seems to be saying solves @BertMcMahan's concern.)
Is the assembler class somehow reusable? It knows all the application details, so it seems not reusable...
10-21-2021 06:45 PM
@WavePacket wrote:
@Dhakkan wrote:
@BertMcMahan, Pleased to make your acquaintance virtually.
@BertMcMahan wrote:
I get that the caller actor shouldn't know about the multimeter, but I find this "breaks down" a bit when configuration comes into play. For example, if I was to configure the main program, at some point I have to say "OK you are using a multimeter on COM4" or whatever. I usually have my config editor in my main actor, so it winds up knowing about all kinds of parameters.
...
...
@Dmitry wrote:...
A Dependency Injection Container (AKA Assembler class) is, simply put, an object that knows how to instantiate and configure all other application objects and all their dependencies. ...
...
I'll have to admit that I'm also confused about this exchange. I'm not initially seeing the difference between the main actor knowing the config details (which @BertMcMahan seemed concerned about) and the assembler class (which @Dmitry seems to be saying solves @BertMcMahan's concern.)
Is the assembler class somehow reusable? It knows all the application details, so it seems not reusable...
Assembler class is application-specific (by definition). It knows which objects to create for a specific app. It also knows about COM4 (because it is aware where to get configuration data for each object (INI file, Database, something else) and how to make sense of it (XML, JSON, NI INI File Format, etc.). So you need to create and maintain an extra class. But the payoff - your Main Actor suddenly becomes reusable 🙂
Example: You have an Image Scanner Application with Scanner Class using a Camera object and a XY-Stage object.
Option 1: Your Scanner object is tasked with creating and configuring Camera and XY-Stage objects. It becomes statically coupled to a concrete Camera and XY-Stage make/model. In case marketing asks you to replace the camera with another make/model - you would have to edit your Scanner class source code as you need to create Camera object from a different class, which, most likely would have a different configuration cluster. Now you have two almost identical Scanner classes to maintain (fixing the same bug twice, adding a new feature twice, testing both classes, etc.). Bottom line - your Scanner class implementation is not reusable 'as is'. You have 'recycled' the original Scanner class code (saving on development effort), but increased your [future] code maintenance costs.
Option 2: Assembler class is tasked with loading all configuration data, creating sub-classes of required type and configuring them. When creating Scanner object it simply passes the Camera and the XY-Stage objects of the right type into Scanner object. Scanner class is not aware of (and does not care about) the type of its sub-objects because all Camera flavors implement the same [LVOOP] interface. Your Scanner class is now reusable (can be packaged as a PPL - no source code updates required anymore).
Why Option 2 is a better solution ? Because Assembler classes are typically small and straightforward to program/update compared to your Main Actor. This makes you a better (i.e. more productive) developer.
10-21-2021 07:00 PM
Ah, so you're saying you have one Assembler class that creates the Main Actor, populating its sub-classes with XY Stage and Scanner. Thus, the Assembler class doesn't do a whole lot of things, but it decouples all of the other things. Basically it just runs once, right?
For lots of my programs, "have the user edit a JSON file" won't work- they'd need a GUI, which itself would be called within the Main Actor. The GUI could then update all of the config settings and pass a Config object to the Main actor, who can implement methods to adjust config on the fly rather than having to be fully restarted. This is a method I've used in the past, but I don't love it since the GUI tends to be unweildy, having to know ALL settings for ALL of my different options.
I feel like the "right" way for my use case is to have any of my "configurable" items implement a "configurable" interface. Then, a general purpose settings GUI can look for all possible "configurable" items and can populate a list of items, which will, when selected, populate a subpanel with their own "configure me" windows. Of course, this method would require some sort of nesting feature as well as some sort of class to provide a "Which XY stage do you want?" prompt.
I've started looking into the Configuration Editor Framework but haven't got my head around it. It appears this is a good way to compile configuration GUI's, but I'll admit I haven't quite got my head around it yet, especially when it comes to actually getting the config info out of the CEF and into my main program.
10-21-2021 09:52 PM
I have been in the past (but have yet to really follow through to completion, so take with a mountain of salt) tempted to do the following (I think that LabVIEW's Options panel is perhaps similar either in principle or both principle and execution):
10-22-2021 02:23 AM
@BertMcMahan wrote:
For lots of my programs, "have the user edit a JSON file" won't work- they'd need a GUI, which itself would be called within the Main Actor. The GUI could then update all of the config settings and pass a Config object to the Main actor, who can implement methods to adjust config on the fly rather than having to be fully restarted. This is a method I've used in the past, but I don't love it since the GUI tends to be unweildy, having to know ALL settings for ALL of my different options.
.
An advantage of have configuration settings as controls in the individual subactors, other than it is very easy and avoids all this effort to have a separate config UI, is that some setting changes can take place immediately, giving the User realtime feedback on what they are changing. Direct control in the app is better than a separate editor window.