DQMH Consortium Toolkits Discussions

cancel
Showing results for 
Search instead for 
Did you mean: 

Roast my Design

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

  1. While adding more settings to the typedef, the FP of SubVis were getting bigger and bigger. This should be avoided.
  2. Having all data in every subVI was extremely handy: I want to continue this.
  3. Having all data in every SubVI was extremely unhandy for debugging: I want to be able to see where data is read and written.
  4. Having all data in every SubVI can result in a performance issue: I don’t care about that at the moment.
  5. use what a call native LV datatypes for a settings. e.g. Read Temperature Unit returns an enum while Set Temperature Range needs a dbl min value and a dbl max value
  6. Change, delete or add a setting: minimize possibilities to make errors & minimize effort
  7. High type security at edit time: when changing an enum to a string all Vis using that setting should brake. Of course this has limits: Changing a dbl to a unsigned long results in a correction dot. Changing only the label may not result in anything.

 

This is how I tried to achieve my aims:

  1. create a class Config Module instead of a typedef
  2. wire config class into subVI
  3. create getter and setter methods like Config.Get Temperature Range()
  4. try to build an abstraction layer (the LVOOP Classes), so that later e.g. data value references could be used to implement the same functionality with higher performance, may be with GDS?
  5. create setting classes with getter and setter like TemperatureUnit.set(enum unit)
  6. This results in a few things
    1. create abstract setting class which requires descendants to override and same thought with abstract configuration class
    2. use dynamic dispatch at different places
  7. use setting classes as parameters in requests and broadcasts when DQMH modules communicate

 

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 ConfigInit 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 SettingRead Setting

In the second step a private VI is used for reading the correct Setting Class of the Config.

Read Setting out of ConfigRead Setting out of Config

And of course a Setting has Getter and Setter Methods:

 

 

Getter & SetterGetter & 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 ConfigUpdate Setting in Config

So lets start the journey through the calling flow… In the GUI the user changes a setting.

GUI Setting Changed on FPGUI 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 CancelGUI 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 SaveGUI 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 SettingCheck 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 EqualSetting - is Equal

 

Setting - Request Config Change SettingSetting - 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 SettingConfig 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 BroadcastConfig - 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 BroadcastSetting - 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 SettingModule 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 :-)Roast my Design 🙂


Proud developer at Hampel Software Engineering where we turn '404 not found' into '200 OK'. Join us on Discord
Message 1 of 15
(4,163 Views)

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.

 

 

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 2 of 15
(4,106 Views)

Hey Sam,

 

are you talking about the new map and set structures in LV 2019?

  • Since we would need 1 map for each datatype - how could we store different maps in one set?
  • Which advantages do you expect to change the underlying data structure?

 

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:

  • one DQMH Module Template which will be used to create 1 Configuration Module for each DQMH Module used in an application
  • saves and loads configuration
  • receives requests to change a setting
  • broadcasts a new setting
  • low costs in changes
    • reusable, generic code
      • e.g. when clicking "save in preferences GUI" it is easy to tell which setting was changed and which wasn't
    • type security in edit time: broken VIs when the datatype is changed or the setting is deleted
    • clear way of how to add new settings
  • easy to understand, no hidden complexity

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?

 


Proud developer at Hampel Software Engineering where we turn '404 not found' into '200 OK'. Join us on Discord
0 Kudos
Message 3 of 15
(3,998 Views)

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.

 

 

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 4 of 15
(3,992 Views)

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

Message 5 of 15
(3,948 Views)

Yeah after watching your recent presentation at GDevCon2, I have begun pondering something similar.

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 6 of 15
(3,919 Views)

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

0 Kudos
Message 7 of 15
(3,907 Views)

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.

0 Kudos
Message 8 of 15
(3,882 Views)

hence the age-old question - "Do you really want the user mucking around in configuration files in a text editor?"

Sam Taggart
CLA, CPI, CTD, LabVIEW Champion
DQMH Trusted Advisor
Read about my thoughts on Software Development at sasworkshops.com/blog
GCentral
0 Kudos
Message 9 of 15
(3,874 Views)

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 😉

malleableOOP.png

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.

 

 

DDD.PNG

 

 

May be some hints, links, etc.? What am I doing wrong?


Proud developer at Hampel Software Engineering where we turn '404 not found' into '200 OK'. Join us on Discord
0 Kudos
Message 10 of 15
(3,799 Views)