LabVIEW

cancel
Showing results for 
Search instead for 
Did you mean: 

Question on how to load different controls/indicators into main program

Solved!
Go to solution

Hey guys,

 

I've got a question about how best to go about making my program more modular. I've been putting off fixing this for a while, but now I need to take a stab at it. I have a fairly large (at least to me) control/data acquisition program that I made using the channeled message hander architecture.

 

My problem is that I have multiple test rigs in my lab that all need the same basic type of program (manipulate controls and gather data). Right now I simply make a copy of the main VI and modify the controls/indicators to fit my other test rig. That's been ok for now because I only have 2 test rigs, but pretty soon I'm going to need more and more versions of the front panel as we bring other test rigs online. This means that anytime I want to add a feature (or fix a bug), I have to go into every single copy of the program and make the same changes. What I really need is one main program that I can load different control/indicator combinations into depending on which test rig I want to use.

 

I tried in the past to replace the controls/indicators on my "Controls" tab with a separate VI that I called in using a subpanel, then using references in the spots where I actually do things with the controls, but I could never get it to work. Deadlines forced me to just move on with the "many copies" approach so that we could just get our systems up and running. Before I get back into trying to fix this I wanted to check with you guys to make sure that I'm approaching this the right way. Is making different versions of the image below for each test stand in a unique VI that gets called in via a subpanel the proper approach to fixing my issue or is there a better way? 

Frank_Rice_1-1763397475363.png

 

I've attached all of my VIs in a zip folder. If there are any issues with opening the files let me know and I'll fix it. Also, if you take a look and notice something that I've done in a dumb way, please point it out. I'm sure that I did a lot of things stupidly and would love the opportunity to learn better ways to do it.

 

Thanks,

Frank

 

 

0 Kudos
Message 1 of 6
(365 Views)

Congratulations on building a very neat CHM system.  Most of my code since LabVIEW 2016 have used the CMH, though nothing as complex as yours.

 

It took me a while to get your code to load -- it appeared as though the main VI (baby_RDRE_main) was not in the Project.  Once I found it and clicked on it, it loaded, and flagged a Channel Wire error.

 

I've not used the Read with Abort method for Read Channel Messagers (top left function in Control Loop), but all Write Channel Messenger functions that are wired to it must also use "Write with Abort" methods.  You need to go through your code and find all the Write Messenger Channel functions (starting with the "Initialize Message" one outside the Control Loop) and change them to "Write Message with Abort", wire the (required) abort (f) tab (to false).  I'm not sure why you use the Abort feature -- indeed, after fixing all the Write Messenger with Write with Abort, I got rid of the Error but now see that many of the "inside the Control Loop" Write Messenger functions are not the "Write with Abort" method!  I'm not sure where the Abort variety should be called (with the abort (f) input wired True.

 

The other error was in "arm system for autoesequence" -- the Main Tab Control need an appropriate input from the TypeDef, which the TypeDef that was wired to it was not.  There was a TypeDef-ed constant, "Controls", wired to it, but this had an Error so I "Diagram-disabled" the (now-disconnected) control and the Main VI now has no errors.

 

See if you can duplicate what I described, and see how it runs on your hardware.  Long Live the Channel Message Handler!

 

Bob (CMH) Schor

0 Kudos
Message 2 of 6
(324 Views)

Hey Bob,

 

Thanks for the advice. Your forum posts really inspired most of this haha.

 

Not sure why it was throwing any errors, must of been how I packaged everything up into the ZIP file. I used the Write with Abort to send the shutdown flag to all of the loops. However, looking at it now, I could have just used the tag messenger like I did for all of the other ones. That's weird that it flags a Channel Wire Error for you though, I don't receive that even with the combination of "Write with Abort" and "Write" functions.

 

Do you have any advice on how to use this main program and load in different controls/indicators to the FP?

 

Frank

 

0 Kudos
Message 3 of 6
(271 Views)
Solution
Accepted by topic author Frank_Rice

I won't start analyzing your code now, but in general, I would agree that splitting it is a good idea. I can describe some of what we have which has some similarities, and hopefully you can apply what's relevant to you. I'll write down some points, probably in a random order:

 

  1. We try not to have the details of the hardware communication fixed in the code. In your code, for example, this can be seen in the DO changed case, where the IO is tightly coupled to the task and you have code for building the tasks, etc. Instead, I would suggest moving all of this to configuration files, which will include things like the hardware details, calibration data, etc. This can as simple as a CSV file for each type (e.g. DO and AO would have different columnns) with one row for each tag, or it can be JSON, a database, etc. For a concrete example from your code, you can have a tag called "Camera" and its hardware configuration might be "Task_DO_0::Ch0". Have this in a fixed format with code to parse and handle it and you won't need the specific code.
  2. Similarly, I would suggest moving all of the hardware communication code out of the main VI. It can be its own process running in a separate VI. Here too, the complexity can vary depending on things like what kind of different communication methods you have (e.g. do you have only DAQmx?). Certainly, combining these two can make it so you don't have to rewrite and modify the same code for each system.
  3. I would suggest that rather than using the controls directly to have your IO values, you use a system which lets you have data associated with each tag (for example, its current value, configuration data, hardware config, etc.). You can load this data from the config files we mentioned earlier and set/read it accordingly. For example, when you set the value of a control on the FP, you can set the value of the relevant tag and then read it in order to pass it out to the hardware. Imagine, for instance, that you have an AO class, where you have a "Set AO" VI, which will take a tag and a value and store it and a "Get AO" VI, which will take a tag and return its value (and also the HW calibrated value? Or you could have that as a separate VI).
    Here's an example of such an API. I don't know if this example is good or not (I would suggest considering having a map of DVRs to objects or clusters), but it can at least give you a direction.
  4. Once you have that, you can then also separate your UI handling code into a reusable VI: it can list all the controls in the UI VI and register for their events and update them. If you have the label as the tag you can just use that to know what to work with. You can get the control type and whether it's a control or indicator to know what to do with it (or you could have a prefix/suffix to handle the type, or you could look the tag up in your different lists to know what type it is).

 

This is a bit of wall of text, and it certainly hides some work behind it, but it can get you to the point where the code handling this can be reusable and the specific handling is confined to the system-specific UI VI and configuration.


___________________
Try to take over the world!
0 Kudos
Message 4 of 6
(216 Views)

Hey tst,

 

Thanks for your help. I think you just made some things click in my head that I can run with.

 

1. This is already how I load in the configurations. I have this excel sheet with all of our different test configurations. At startup I pick the sheets I need to combine and process them into a config cluster that I use to connect to the hardware. I'm pretty sure this is exactly what you're talking about.

 

2. This part is what really just clicked in my head (if I understand correctly). So my code is doing a lot of things, but most of the stuff is the same between test rigs (importing auto sequences, opening plots, toggling recording, etc). Really the only thing that changes between my programs are the DO/AO controls and AI indicators. 

 

If I understand what you're saying correctly, I should take all of my actual hardware interactions and put that into a sub VI. I could then load this subVI into a subpanel on my main, but I wouldn't have to mess with passing any control references back to the main VI. This would mean that I could have one copy of my main that I could add features to as I want to expand my program. To make different programs, I would just need to make a new (much smaller) subVI that has unique controls/indicators set up in a similar fashion to what I have now.

 

3. This is pretty interesting, but it seems like a pretty big redesign. I'm going to look into it a bit more, but I might try what I wrote above (unless you tell me that's stupid) first.

 

4. If I create sub VIs using a event handler for all of my AO/DO writes I can run that inside my main VI correct? I won't have to worry about registering for dynamic events and passing references between the VIs since the events only happen inside their respective VI.

 

Thank you again for the help, you just gave me a whole new approach to tackling my problem.

0 Kudos
Message 5 of 6
(193 Views)

@Frank_Rice wrote:

 

1. This is already how I load in the configurations. I have this excel sheet with all of our different test configurations. At startup I pick the sheets I need to combine and process them into a config cluster that I use to connect to the hardware. I'm pretty sure this is exactly what you're talking about.

Yes, your Excel file looks like it has the relevant info. You just need to set it in a format the program can load.

 


@Frank_Rice wrote:

 

3. This is pretty interesting, but it seems like a pretty big redesign. I'm going to look into it a bit more, but I might try what I wrote above (unless you tell me that's stupid) first.


Maybe this can help:

 

IO handlingIO handling

 

Think of each colored sequence as a VI. Obviously this is missing a lot of details, but it shows the skeleton of how something like this can work. Note that the heart of is the Get DVR VI, which takes the tag and returns a DVR to that tag. It needs to create and maintain the list of those DVRs. This essentially gives you a named safe global object (at least safe from race conditions. Things like deadlocks are still possible), so different places in the code can work with it (the UI handler can set the AO tag value and the HW handler can get it).

 

Note that here you no longer have a direct dependence in the code on the specific setup. Ideally, everything was moved to the configuration files and to the IO UI VI, which doesn't actually need any code.

 

If your logic really is the same and the only difference is in the config and UI, then you can even have a single executable and just have the config and the VI as something you place in the relevant folder, so that the EXE can load the one which is relevant for the particular system.

 

Note that these things don't actually need to be VIs, but it just makes the code cleaner. The important thing is the separation. Also, things can obviously be implemented differently (for instance, you could run separate copies of the HW comm VI for different tasks or for handling communication with non-DAQmx devices, you could create the UI using a webview control and HTML/JS, I personally prefer complex behavior like this to be separated from the UI itself, so I would suggest using Mouse Down? instead of Value Change and update the value of the controls from the current value of the tags, etc.).


___________________
Try to take over the world!
Message 6 of 6
(151 Views)