Dynamic UDT-Driven Dashboard with Nested Flex Repeaters: Sizing/Scaling Help

Question:

How to make nested flex containers scale to be their child's child size?

Background:

I am building a flexible dashboard to display the properties of batched chemical processing equipment. We can think of these machines as a composition of tanks, which each contain a variety of components (pumps, heaters, fans, pH sensors, etc.). And each of these components has some signals like temperature, differential pressure, current, rpm, etc. Some components (even of the same type) have more signals than others, and the composition of these components varies tank-to-tank. This makes creating a custom view for each component and tank an extremely manual process. Lets automate it.

I have created a nice UDT structure to describe this machinery where discrete and analog signal UDTs are combined by component UDTs, which get combined to create different tank (abstracted to modules) UDTs. Now, given these UDTs, a few different handcrafted views for my analog and discrete signal UDTs (think like a gaugeView, thermometerView, ledDisplayView, etc.), and a UDT property pointing to which one the signal UDT should use (can be defaulted to the simple ledDisplayView), I should be able to create any combination of tanks and components completely for free. I have successfully created this, but unfortunately, due to some workarounds, I have been unable to figure out how to effectively scale my components so it is usable in a real environment.

My approach so far:

The core of my approach relies on flex repeaters pointing to a generic ComponentHost, which has 2 props: viewPath and tagPath. Inside the ComponentHost is an embedded view which points to a view of viewPath and passes tagPath into that view. (This is required to work around flexRepeaters only pointing to a single view). The following script can be used to look at all the signal tags in your component and generate a view in the flex repeater:

componentsPath = str(value).rstrip("/") + "/Signals"
instances = []

results = system.tag.browse(
    componentsPath,
    {"tagType": "UdtInstance"}
).getResults()


for tag in results:
    udtPath = str(tag["fullPath"])
    viewPath = system.tag.readBlocking([udtPath + "/Parameters.ViewPath"])[0].value
    if viewPath is not None: # or viewPath is not "":
        instances.append({
            "tagPath": str(tag["fullPath"]),
            "viewPath": viewPath
        })

return instances

Now this will produce a single view with all the signal UDTs you defined, so now just repeat this for all the components in a module. (You must use a different flex repeater view and ComponentHost to prevent nesting a view inside itself.)

componentsPath = str(value).rstrip("/") + "/Components"
instances = []

results = system.tag.browse(
    componentsPath,
    {"tagType": "UdtInstance"}
).getResults()


for tag in results:
    udtPath = str(tag["fullPath"])
    viewPath = system.tag.readBlocking([udtPath + "/Parameters.ViewPath"])[0].value
    if viewPath is not None: # or viewPath is not "":
        instances.append({
            "tagPath": str(tag["fullPath"]),
            "viewPath": viewPath
        })

return instances

Beautiful, this is great, a little janky and kinda weird; the duplicate components and flex repeaters are not ideal, but we have all done worse things in Ignition. My problems occur when I try and make all my views scalable so that the Component view takes up just the space required by its signal views, and the tank view nicely tabulates those component views into something viewable by an engineer's laptop or a TV dashboard on the floor. Ideally, each flex repeater will take up just the space of its children, and the children will scale to fit the size of the screen (based on their aspect ratio and wrap constraints). Unfortunately, I have not been able to achieve this without getting into some really hacky CSS stuff. Could I please get some advice on how to approach this styling problem? My assumption is the crux of my problem is flex views want to scale their children to themselves, not vice versa, and my ComponentHost views are messing up my sizing even more.

What I have Tried:
So my first goal is to make each flex repeater take up exactly the space of its children. To this end, I made all my signal views coordinate containers in percent mode and my ComponentHosts Coordinate views with the embedded view's width:1 height:1 useDefaultWidth:false useDefaultHeight:false. Unfortunately, no matter how much I would mess with the grow, shrink, basis on the flex container, the flex container would squish my views into tiny strips.

Next, I tried making my ComponentHosts flex views, but that also didn't work, so I reverted back to Coordinate Containers.

I currently have my ComponentHosts and FlexRepeaters with useDefaultWidth:true useDefaultHeight:true. This eliminates all scaling, so it will not work in production, but does validate that the underlying machinery works.

Can anyone please recommend what combination of styling parameters will work?