07-29-2019 04:50 AM
Hello DQMH Fans,
I started to think about optimizing my data flow regarding settings here and will continue here in more detail and focused on the idea of the Settings Editor in the DQMH CML example project. So this post is about design and best practices. And yes, it is very long. I hope at least a few of you will find the time to read through this and give me some feedback.
While developing my current solution I learned a lot about LVOOP, which I’ve not used before. Mistakes have been made - please tell me about them!
What this is about & the big picture: Every application will consist of one or more DQMH modules. Each module will probably need some settings (e.g. temperature unit, temperature range, etc). I wanted to create a DQMH template to create a Config Module, which is also a DQMH Module. A template to minimize effort and to code common functionality like save and read configuration to/from XML file only once. A template to rule them all…
As discussed here, I think there should be more DQMH Templates available. Would a Config Module be interesting for the community? Has it enough functionality while being generic enough to meet different requirements? Please share your thoughts.
So, one logical module can consist of multiple DQMH modules. All of them must be able to communicate with the Config Module. This is a tree view: Upper modules call lower modules when they initialize (request start module). Upper modules sent requests to lower modules (request give data), so they must know them. Lower modules sent broadcasts to upper modules so they mustn’t know them (broadcast new data). This seems to be best practice to achieve low coupling. Furthermore it is possible to test e.g. the Module Data Provider with the Module HAL and the Config Module without starting the rest of modules.Modules Overview (yEd Graph Editor https://www.yworks.com/products/yed)It would be possible to run multiple different GUIs (7” Touch, 23” Monitor, Android Web App?) for displaying and controlling the same logical module.
Now let’s start with the setting and configuration part. I refer to a setting as a setting and name a bundle of settings a config, just to be clearer in naming things. The Config Module is headless and stores the config of a module in its MHL shift register. The Config Module offers requests to change all settings individually and then sends broadcasts to all modules which are interested to inform them, that a particularly setting has changed.Communicate Setting Change https://sequencediagram.org/
The GUI just updates all FP objects, because it mustn’t know more in detail. Module A must know exactly which settings were changed when hitting save in the GUI Options Window.
Does this make sense until here? Well, on it goes…
Now I have built something and I would love to get some feedback before it continues growing and getting back will be harder. So, here are my aims in improving the Settings Editor of the DQMH-CML example (no order, figures just for reference)
This is how I tried to achieve my aims:
First I’ll describe the very Low Level VIs and second we’ll have a look at the flow through the application.
When a Config initializes it puts its Settings into its array. I don’t like that I have to manually remember which index represents which settings. But later I need an array for dynamic dispatch. That’s why I cannot use a cluster. Any hints to optimize welcome! The Array is of the type Abstract Config.
Init Config
Read a Setting from a Config uses three steps: Since we want to wire this VI to FP-elements it returns native LabView Datatypes and has to be public.
Read Setting
In the second step a private VI is used for reading the correct Setting Class of the Config.
Read Setting out of Config
And of course a Setting has Getter and Setter Methods:
Getter & Setter
Writing a value to a setting is easier. Since changing a Setting will be performed via a DQMH request, we’ll just put that Setting Class in the typdef of that Request. So we can directly wire that Setting Class into the Configs array.
Update Setting in Config
So lets start the journey through the calling flow… In the GUI the user changes a setting.
GUI Setting Changed on FP
The native LV values are written to a setting implementation class which is send to the MHL. There it updates a copy of the configuration. The GUI holds two Config classes: one represents the current config of the module and one the current state of the FP. First we only update the second one.
When the user hits cancel his changes to the FP will be undone. So read out the values of the original Config and write the values to the FP objects through local variables. The Copy will be overridden with the old values.
GUI Cancel
But when the user wants to save his changes, we must compare all settings in the two configs to make the corresponding requests to the settings editor. Therefore we call the abstract VI Check Changes And Request Config Change Setting. Abstract, because multiple GUIs could use different Config Modules, each of them implementing that method. So that case looks always the same, but the shift register of a different GUI would hold a different Config Class Implementation.
GUI Save
Lets look at the implementation of that VI of a concrete Module Config. For dynamic dispatch it has an input of its own class. The second input is an abstract class. That’s why we see a correction dot in the previous picture. But inside that concrete implementation we’re pretty shure that also the second Config must be of type Module Config so we can use To More Specific Class. From this point downwards I’ve not implemented error handling as you’ll see. That of course has to be done.Check Changes And Request Config Change Setting
So what has to happen is easy: If the new and old Setting have the same values (are equal), nothing must happen. But if changes have been made, we want to request the Config Module to set that particular setting to its new value. So we call the two abstract Vis isEqual and Request Config Change Setting. Now the magic of dynamic dispatch leads to the concrete implementation of these two Settings methods
Setting - is Equal
Setting - Request Config Change Setting
The Setting itself calls the Request to the Config Module, yeah! Now we start again at the top of a DQMH Module, the Config Module:
The Config Module hast a request case for every setting. In the EHL it converts the setting implementation to an abstract setting and sends it to the MHL. Every request to change a setting calls the same MHL-case. There the Config’s method Update Module Setting And Broadcast is called.Config receives Request to Change a Setting
Here you see the Config’s method Update Module Setting And Broadcast.vi, it just calls the abstract Method Update Setting And Broadcast which is implemented in every Setting Class.
Config - Update Module Setting And BroadcastUnfortunately, I wasn’t able to make this VI (Method of Config Class) override (an abstract Method of Abstract Config), because I got the error “One or more of the inputs to this tunnel or shift register does not originate at the dynamic input front panel terminal”. I couldn’t solve this. Any hints welcome!
Additionally, to updating the Config it calls the Config’s broadcasts event to inform other Modules.
Setting - Update Setting And BroadcastSo we’re at the bottom of the Configs Module and finally our Main Module receives the new Setting.
Module receives the new SettingSo thanks for reading my longest post ever made. I hope that those who read until here enjoyed it a bit, may be even have seen something interesting, probably have seen some points to do it better. I would be very thankful for any critic and recommendations.
Is my approach oversized for what it can perform? Overelaborated and not readable? How do you communicate settings in your application?
Roast my Design 🙂
07-29-2019 05:43 PM
didn't read everything, but read the first page or so. I have been playing around with a similar idea for distributing configuration data. The easiest way I've thought of to create a generic configuration is to make it a map. Then your config model can have 1 configuration updated broadcast that broadcasts the entire map. Each module can then pull off what it needs.
You could get fancy with it and broadcast a map of maps, 1 for each module. To handle different datatypes you could have an object wrapping a set of maps - 1 for each datatype.
If you are worried about data copies wrap it all in an object and make it all reference based.
I have actually implemented this yet, just mulling it around in my head, but there should be enough there to get you started and point you in the right direction.
08-27-2019 07:24 AM
Hey Sam,
are you talking about the new map and set structures in LV 2019?
And to everyone,
I'm sorry my post apparently was so long that most people didn't want to go through it. I'll try to make my requirements and goals shorter this time:
In my presented design I've implemented the functionality. But it seems a bit too complex and oversized to me... How did you solve that problems in your applications?
08-27-2019 07:51 AM
Yes I am referring to the sets and maps in LV2019
My original thought was you could make a config object that contains 1 map for each datatype you want to support.
In the Retrieve Config Item method, you would make that a vim. Have them pass in a default value to use (if it does not exist in the map). Use that datatype and a type specialization structure to look in the correct map.
09-12-2019 03:59 PM
@AlexElb wrote:
In my presented design I've implemented the functionality. But it seems a bit too complex and oversized to me... How did you solve that problems in your applications?
Personally I just use JSON for config info. Each component, be it a "module"/"actor" thingie or just an "object" will have "Get Config as JSON" and "Set Config from JSON" messages or methods. No dedicated configuration components; each component embeds the JSON config of its subcomponents inside its JSON config. No classes, and no reuse code beyond the JSON library itself (JSONtext). Very, very easy to add new settings, though no type safety.
09-13-2019 07:30 AM
Yeah after watching your recent presentation at GDevCon2, I have begun pondering something similar.
09-13-2019 08:07 AM - edited 09-13-2019 08:08 AM
Just my two cents here: at some point I have stepped into using JSON for configuration purposes, but I have soon stumbled into the shortcomings described here. However, I have not yet moved into alternatives like TOML (suggested in that document).
EDIT: there is a MIT-licensed LabVIEW TOML library
09-13-2019 05:29 PM
There's really more than one type of "configuration" information. Possibly complex setting information, written by computer and read by computer, with a possible minor edit by a human, is the JSON strength. More simple configuration, especially if it is written by a human, with comments, can work better in a more INI style. I think the OP is doing the former.
09-13-2019 05:39 PM
hence the age-old question - "Do you really want the user mucking around in configuration files in a text editor?"
09-19-2019 09:18 AM
Hello alltogether,
For my files I just use Flattern from/to XML, which seems to work fine until now.
I know its not a DQMH based question, but I'm still struggeling and need some help with some OOP stuff. First, thanks for the Idea to use malleable VIMs and a type specialization structure, I learned something new! I was able to build one map for each datatype. Although it is not exactly what I want, I'll futher inspect that option.
But let me please ask two questions:
How can I have a parent class method called getValue which returns a variant and a child class method called getValue which returns another typedef? As I continue reading I've found that this is called overloading and is not possible in LV. While it seems logical that overriding must have the same pane connectors it doesn't satisfy my needs 😉
I was very happy when I got this malleable VIM runnable with two classes implementing get and set methods, which can be called with one VIM. But unfortunatly that breaks, if both Classes inheritet from my Abstract Setting Class: For the VIM the get and set VIs have to be called the same, but for inheritance they can't have the same names and different connector panes...
Second question is about "double dynamic dispatch": I have a VI "A" with two abstract inputs "Config" and "Setting". In runtime I want to bundle two child classes to that VI, but I can only make one input dynamic dispatch. So lets say a make the Config-In dynamic. Inside VI "A" there is a Setting-VI "B" which has also dynamic dispatch. But I guess LV will won't call the setting-child implementation "C" of "B" because when entering in VI "A" the setting-child is already upcastet to a setting.
May be some hints, links, etc.? What am I doing wrong?