LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Where should settings modules live and be called in a program tree?

Solved!
Go to solution

Hello all,

 

I am somewhat new to DQMH and for my first project I've decided to make my testbed software compatible with future projects so I am making it as modular as I can. Modules contained within include Camera, Stage (which is a cloneable abstraction layer for a Zaber stage. Cloneable as to allow multiple axes in the future), Test, and a Model module. Eventually once I make the UI I'll have a whole MVC architecture going on and I'll make that good and nice. Camera, Stage, and Test each have their own settings which are required, and I have made a Camera Settings and a Stage Settings (cloneable) module as well (haven't gotten to test yet). These are required for reading the ini files containing the VISA sessions, baud rates, microstep sizes, IMAQdx settings, etc.

 

Without the data from the ini files, there's really nothing for the hardware modules to use, so I would like to have the gathering of settings information in the init message case. From what I can see, I have two options as to how to lay out the program. My initial thought is to have the modules for settings launched by each individual hardware module.

Screenshot 2023-09-06 095832.png

 or I can have settings be given to the hardware modules by main. This avoids having a bunch of layers, and sticks with the philosophy of having everything sent through the controlling module.

Screenshot 2023-09-05 114647.png

 

I'm really not sure what the smart way to do this is. Especially with the cloneable stage module where each clone will need to be given a single clone of the settings module. 

 

Additionally, I'm not sure how much work the init message should be doing in terms of loading these ini files. Good design philosophy tells me that it makes the module more atomic to have a separate config case, but realistically, there should be no reason for a stage module to change which hardware stage it's connected to, so I don't really want to include a configuration in the API when it can all be done at module startup.

0 Kudos
Message 1 of 11
(2,186 Views)

Hello nokaroa,

 

on behalf of the DQMH Consortium, thank you for looking into DQMH and giving it a try!

 

First, you might get more answers in the DQMH-specific forums, so perhaps consider posting there in the future. 

 

As for your questions, I can talk about how we do things at HSE:

 

  • No code whatsoever is added to the "Initialize" case of any module unless for a very good reason (we've just recently discussed this here)
  • All of our modules have a "Configure" request which we can call either dynamically or manually, at exactly the point in time we want to (i.e. when all required resources have been started or similar)
  • The "Configure" request does not have parameters and serves only as a trigger - all modules know where to find their configuration data
  • For cloneables, we can hand over an identifier at module start (i.e. to the "Start Module" VI) so the module can eg. find a specific section in a config file to pull its instance configuration

 

You can see all these things in action in our open-source HSE Application Template. Feel free to take a look at it, perhaps there are some ideas for you to take away.

 

Hope that helps!

 

Cheers,
Joerg




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (Developer Experience that makes you smile )


0 Kudos
Message 2 of 11
(2,118 Views)

First, you might get more answers in the DQMH-specific forums, so perhaps consider posting there in the future. 

Noted, I had no idea there was a specific forum for it. I also posted the same question on Lavag.org because I wasn't sure where would be better.


 

  • No code whatsoever is added to the "Initialize" case of any module unless for a very good reason (we've just recently discussed this here)

This is interesting. Your response there is that you'd have one module launch other modules, and pass the event registrations to those modules as needed? To me that seems like it would create dependencies where the submodules wouldn't be able to run standalone? I could be wrong, but isn't a core idea of DQMH to allow modules to be able to run independently of the module that's calling them in the final program? For example, if A calls B and C, and C calls D, B should be able to run without A and C should be able to run without A or B running, but D needs C to be running. If A gets the event registration references from B and passes them to C, that seems to break encapsulation for me? 

I think I'm just confused by how much encapsulation is really needed.

 

 

 

  • All of our modules have a "Configure" request which we can call either dynamically or manually, at exactly the point in time we want to (i.e. when all required resources have been started or similar)
  • The "Configure" request does not have parameters and serves only as a trigger - all modules know where to find their configuration data

Okay, that's one way I've been attempting to solve the problem. My configure requests have been taking in configuration data however. If I've got 3 IMAQdx compatible cameras on my computer, and I want to launch the camera module, I need a way of determining the VISA and all of the relevant settings, so that has been solved by having a settings module which reads the config files and passes the information. But this is kind of the core of my question: 

Should the configure request cause the initialization of the settings module? I can't have just a default setting for a camera module because it would fail if the correct camera is not connected and in development, that's always possible. In one module, I get the settings module from inside of the configuration event, and in another, I call configure and pass in the settings cluster which has been instantiated from outside the module. I can't anticipate the pitfalls of doing it my way as opposed to your way.

 

  • For cloneables, we can hand over an identifier at module start (i.e. to the "Start Module" VI) so the module can eg. find a specific section in a config file to pull its instance configuration

Do you mean modify the start module VI? I have been very hesitant to modify any VIs that DQMH scripting creates other than main.vi, but if that's how you do it then I guess it can't be that bad of a practice.

 

The approach I came up with (since writing the original question) is to have an axis manager module that launches the clonable axes and the clonable settings modules, and stores the Module ID for both in a map. But the main module that launches the axis manager is still responsible for getting the sections from the config file and passing them to the manager's "launch axis" request. 

 

 

Thank you for your time. I'll look through your template for inspiration. I feel like I'm asking a bunch of questions rolled into one so I appreciate your help as I try to manage this. It's a bit overwhelming. 

0 Kudos
Message 3 of 11
(2,090 Views)

Hi,

 

To add a voice, we recently redesigned our application skeleton.

And we applied the exact same technique that Joerg described in its previous message.

Previously, we had a centralized configuration module (as you describe in your post). But ir creates coupling between modules.

We had to create PPL from modules to create a plugin arch, and we had dependencies everywhere on the configuration module... not very confortable !

 

So we changed that to adopt a configuration per module. Each module is handling its own configuration (JSON R/W, file change detection, ...).

Now each module is stand alone again.

 

We have a specific module taking care of exposing the settings to the user. It allows better UX and filtering of what settings can be changed or not by the user.

CLA, CTA, LV Champion
View Cyril Gambini's profile on LinkedIn
This post is made under CC BY 4.0 DEED licensing
0 Kudos
Message 4 of 11
(2,089 Views)

Hi CyGa, 

Thank you for your input. If each module is in charge of handling its own config, could you describe how you solve (or would solve) multiple hardware configurations that use the same module? 

My application has a potential for multiple zaber stages, and since I'm making a template right now, I don't want to hard code the names of any stages or duplicate the code by creating a new module.  

0 Kudos
Message 5 of 11
(2,078 Views)

First, we've made module templates to target these 4 items :

  1. Process - Singleton
  2. Process - Cloneable
  3. Screen - Singleton
  4. Screen - Cloneable

 

The case you describe would fit into Process - Cloneable template.

In this template, configuration files are storing arrays of configuration.

Typically, we would have one module per instrument type (let's say 'Stage') ; instrument diversity is handled within the module by creating a factory.

So, to control 3 stages (for example), I'll have 3 Process - Cloneable stages modules being launched.

They will all refer to the same config file and read it independantly.

This configuration file will contain an array of individual configuration.

Modules will load the array and get the configuration by indexing it using their clone ID (-1).

 

Configuration will contain a VISA COM port ofr example, and so on.

Who ever is starting first will read the first element in the array and reserve the VISA port accordingly.

And so on for the following modules.

 

If I launch more modules than elements in my array, module without configuration element start with a default conf and add it into the file.

They will notify they started with a defualt conf.

It will be up to us developer to set the values correctly into the JSON.

Or it will be up to the user to go to the configuration UI and changed the default conf to something relevant.

CLA, CTA, LV Champion
View Cyril Gambini's profile on LinkedIn
This post is made under CC BY 4.0 DEED licensing
0 Kudos
Message 6 of 11
(2,072 Views)

So if I'm understanding you correctly, the configuration file might have a section of visa ports as opposed to a section for each stage?

I'm not sure that approach would necessarily work for me. It restricts me to always using the same hardware configured in the same order, correct? Which is unfortunately not how my development works since I can't be writing the LV in the environment with the final hardware. 

0 Kudos
Message 7 of 11
(2,065 Views)

I was maybe not clear.

My configuration would look like this :

 

{
"ConfigurationArray": [
     {

      Port:"COM5"

      Bauds:115200

     },

    {

      Port:"COM3"

      Bauds:19600

     },

    {

      Port:"COM12"

      Bauds:115200

     },

]
}

 

Here clone 1 would control a stage on COM5, clone 2 would control a stage on COM3, clone 3 would control a stage on COM12.

Each clone gets their conf dynamically and will be in charge of controlling a specific instrument.

 

If you want to know which clone is controlling which stage, then I would suggest creating a specific request and have a controller module with a LUT to translate a stage name (for example) into a clone number. That controller could then perform requests on the accurate clone.

Another less elegant way is to send a request to all clones (ID -1) and let the clone decide if it is adressed to them.

CLA, CTA, LV Champion
View Cyril Gambini's profile on LinkedIn
This post is made under CC BY 4.0 DEED licensing
0 Kudos
Message 8 of 11
(2,060 Views)
Solution
Accepted by Nokaroa

@Nokaroa wrote:
  • No code whatsoever is added to the "Initialize" case of any module unless for a very good reason (we've just recently discussed this here)

This is interesting. Your response there is that you'd have one module launch other modules, and pass the event registrations to those modules as needed? To me that seems like it would create dependencies where the submodules wouldn't be able to run standalone?


Sorry for being confusing - I wasn't thinking about dependencies here, but rather about the startup mechanism of a DQMH module and its timing. If you delay the execution of the "Initialise" case - or, more specifically, if it takes more than 5 seconds for your module to get to and execute the "Synchronize Caller Events.vi", your Start Module VI will report a timeout error:

 

Bildschirmfoto 2023-09-08 um 17.18.14.png

 

This is an extremely-hard-to-track-down error that has cost some of our customers a good amount of sweat and tears. Hence our rule not to put anything into the Initialise case at all, to be on the safe side.

 

Now, as for your question about dependencies. "If A gets the event registration references from B and passes them to C, that seems to break encapsulation for me?" - it does not if you are very clear about not reusing the typedef file from B but recreating another typedef file of the same datatype in C. That way, you can have that kind of dynamic event registration without a static linkage between B and C.

 


@Nokaroa wrote:

  • All of our modules have a "Configure" request which we can call either dynamically or manually, at exactly the point in time we want to (i.e. when all required resources have been started or similar)
  • The "Configure" request does not have parameters and serves only as a trigger - all modules know where to find their configuration data

[..] Should the configure request cause the initialization of the settings module? I can't have just a default setting for a camera module because it would fail if the correct camera is not connected and in development, that's always possible. In one module, I get the settings module from inside of the configuration event, and in another, I call configure and pass in the settings cluster which has been instantiated from outside the module. I can't anticipate the pitfalls of doing it my way as opposed to your way.


In addition to the "Configure" request, we will most likely have an "Initialize HW" or "Connect" or similar request, which will do the actual communication/initialization. So these are two distinct events. Sometimes it doesn't matter, but separation of concerns is always good, and we wouldn't want a "Configure and Initialize" request now, would we 😜

 

The reason for our "Configure" request not having any parameters is that we wanted it to be generic, so we can call it dynamically from within our framework/template. If you do not have that requirement, feel free to add parameters as you need them.

 


@Nokaroa wrote:

  • For cloneables, we can hand over an identifier at module start (i.e. to the "Start Module" VI) so the module can eg. find a specific section in a config file to pull its instance configuration

Do you mean modify the start module VI? I have been very hesitant to modify any VIs that DQMH scripting creates other than main.vi, but if that's how you do it then I guess it can't be that bad of a practice.

I fear this only means that we can get away with it 😉

 

Seriously, though, yes - this is what we do. I think the screenshot explains it quite well: We hand over a string "Config Section" via the Start Module.vi to the Main.vi. In the module, the Configure MHL will read in a config.ini file, and then use that "Config Section" string to identify the section of the file we want to use in that specific clone.

 

Bildschirmfoto 2023-09-08 um 17.36.41.png

The screenshot is taken from our DB module (which uses the HSE DB library), where we have different database configurations in a single .ini file, and tell that specific clone which one to use.

 

(we also inject the dependency for the database driver class - SQLite in this case - which is a quite handy way to keep the DQMH module itself free of dependencies to various drivers like SQLite, .Net etc).

 

Bildschirmfoto 2023-09-08 um 17.48.31.png




DSH Pragmatic Software Development Workshops (Fab, Steve, Brian and me)
Release Automation Tools for LabVIEW (CI/CD integration with LabVIEW)
HSE Discord Server (Discuss our free and commercial tools and services)
DQMH® (Developer Experience that makes you smile )


0 Kudos
Message 9 of 11
(2,052 Views)

Okay, I like your solution and I'll try separating init, configure, and hardware init. All of that makes sense, and I'll just be sure that calling applications are forced to read everything in the correct order (lots of "is initialized?" type flags I guess). That seems more flexible to me than @CyGa's solution of reading things in a specific order from an array in the config.

 

As per my original original question, it seems like the concept I had for settings modules might just not be necessary, so I will try to adapt my application to not use them, or at least to not rely on them. A change setting request (for each individual setting, or a dynamic one that takes in setting names) should work fine and if a calling module needs to change the settings it can do it that way. 

 

Thanks

0 Kudos
Message 10 of 11
(2,033 Views)