Advice sought: am I dynamically updating UDElements the smartest way?

Hi - I’m creating a Dashboard to monitor the status of a scientific instrument (a telescope array) with many components. I’ve got a prototype Dashboard put together which I’m testing now, but when looking at my code I’m scratching my head wondering if I’m doing things in an optimal way. I’m a Universal Dashboard newbie (and I’m a scientist not a developer so am in a bit over my head) and if I’m doing something in a really silly way I’d like to fix it now before I go much further. Could I lean upon the wisdom of this group for your opinion?

A screenshot of my dashboard (such as it is) is shown here:

The part of my code that I’m rather dubious about is being used to make (and update, once a second) the colored boxes shown at the top, in the ‘Status’ card. The colors represent the state of various components of the instrument (white=inactive, green=active, red=error). Each of these boxes is a UDElement, and the colors are set by polling a cached data structure once every second. The cached data structure is itself constructed by polling a relational database (also once a second) but that’s done independently using a scheduled endpoint that is defined at the start of my script. (This approach was chosen because several other cards use the information from the database too, so I wanted to minimize the number of times we poll it).

The specific bit of my project that has me worried is this snippet, where I generate the card with the status lights and dynamically alter the colors according to the device states:

New-UDCard -Id StatusPanelCard -Title "Status" -Content {

    New-UDElement -Tag 'div' -AutoRefresh -RefreshInterval 1 -Endpoint {
        
        New-UDLayout -Columns 4 -Content {
            ForEach ($device in ($Cache:BackgroundColor).keys) {
                New-UDElement -Tag "p" -Attributes @{ 
                    style = @{"background-color" = ($Cache:BackgroundColor)[$device]; 
                              "border-color" = "black"; 
                              "border-style" = "solid"; 
                              "text-align" = "center";
                               width = '95%' 
                            }
                } -Content { $device } -Id $device
            } 
        }
    }
    
}

The colors of the boxes are stored in a hash, $Cache:BackgroundColor, and the keys of the hash are the device names which I’m using as IDs for the UDElements.

Although I’m creating 7 UDElements (the colored boxes) each second, I have all these New-UDElement calls wrapped in the endpoint of an outer New-UDElement so I only refresh a single endpoint (which is the technique suggested here: https://ironmansoftware.com/best-practices-for-universal-dashboard-performance/). However, it seems to me that making New-UDElements so many times (seven new elements each second) just to update the colors of some boxes might be a bit of overkill. Would it be smarter to just create them all once at the start of the script and then update their attributes (without re-creating the elements) each second instead? I have tried that approach (e.g. using Set-UDElement in the part of my code that updates the database) but can’t seem to get things to work. But it occurred to me that before spending more time trying to figure out why I can’t seem to change the colors dynamically using Set-UDElement, I should probably first ask here to see if people who are experienced with Universal Dashboard think my whole approach is silly. Is there an ‘obvious’ better approach to this problem that I should be adopting?

Thank you for any advice or suggestions - I’m having tremendous fun using Universal Dashboard but I’m painfully aware that this doesn’t mean I know what I’m doing!

2 Likes

Hey @robertoabraham!

Glad you are enjoying using UD! First off, this is a very cool use-case I haven’t seen before. I’m excited to see what you build.

Short answer: I think you are doing this the right way.

Long answer:

Using the single endpoint means that you perform a single HTTP request from the client to the server and execute a single PS scriptblock. You are sending more data back to the client because you are recreating the elements each time. I measured it to be about 1.5Kb per request with a request time of about 20ms, locally.

If you use the Set-UDElement method, you need to keep in mind that the Attributes aren’t cumulative. So you need to pass down all the attributes again. Every call to Set-UDElement makes a call via websockets from the server to the client’s browser. This means that for each element you are passing down about 325 bytes of data times the number of elements. So in this case it can actually end up being more data because of the packet metadata for the websocket.

This is how I setup that test.

     New-UDElement -Tag 'div' -id 'Outer' -AutoRefresh -RefreshInterval 1 -Endpoint {
          try {
               ForEach ($device in ($Cache:Items)) {
                    Set-UDElement -Attributes @{ style = @{"background-color" = $device; 
                    "border-color" = "black"; 
                    "border-style" = "solid"; 
                    "text-align" = "center";
                     width = '95%' 
                  } } -Id $device 
               }
          }
          catch {
               
          }
     }

     New-UDLayout -Columns 4 -Content {
          ForEach ($device in ($Cache:Items)) {
              New-UDElement -Tag "p" -Attributes @{ 
                  style = @{"background-color" = $device; 
                            "border-color" = "black"; 
                            "border-style" = "solid"; 
                            "text-align" = "center";
                             width = '95%' 
                          }
              } -Content { $device } -Id $device
          } 
      }

So overall, I think you are doing it the right way as UD is designed today. I think UD could be optimized to make setting a single attribute with Set-UDElement possible and this would likely provide better performance.

Thank you so much Adam! That’s incredibly helpful and I’m relieved I’m doing things in a sensible way, so I’ll proceed with my Plan A.

Universal Dashboard is truly amazing and you should be very proud of it!

3 Likes