Visualising a Production line

Is there a nice way to visualise stations on a manufacturing line.

I've created a London tube status type idea for all the zones in the factory using the ui-gauge (tile), but I'd like to show a visual representation of the stations within each zone.
I could use ui-gauge for each station but what ui element can I use to connect them. Also I'd like to be able to change the shape of the tile gauge to a circle for example
Any ideas on how I might approach this?

I have something similar (see below),I have different rooms(zones) in my house and show in below UI

  1. Which room/zones has an active Motion-Sensor. I can enable/disable the motion sensor when clicking on it)
  2. the 2nd ui is showing whenever a motion is triggered in any of these rooms

As you can see, you can easily use REG/AMBER/GREEN status as well in the icons

Just for context, in my example the Title/Sub-title in the UI is displaying the room and time when a tile was changed last (to show last event)

Very nice. Looking to do something like that with similar visual indicator of status colour/icon

Ideally I want do something like this, but I guess I can do it with just tile gauges without the connectors

Now if I can customise the shape,I'd be very happy :grinning_face:

I'm not an expert, but a few guys here are using SVG for this kind of flow visualisation ... maybe search the forum for it

Ha, ha - I was just going to suggest that. You beat me to it.

I think Bart's node is just for Dashboard-1, but there maybe other versions for D-2.

Or you could just place the widgets on a background image that you create.

Here's an example of Colin's suggestion.


My CPU monitoring dashboard using Bart's SVG node. The LEDs change colour depending on the temperature reading. The grey one at the top indicates 192.168.1.158 is off-line. The Temp readings and IP addresses are superimposed on the photo of my old mini-server rack.

I believe Bart's node had an SVG editor so you could create your own graphical shape.

Looks like there is a limited version for D2, but no longer being developed https://github.com/bartbutenaers/node-red-dashboard-2-ui-svg

Now that's a possibility. Didn't think of that

Probably worth exploring its capabilities as the stuff Bart produced is/was brilliant.

You can use the markdown node with mermaid formatting, quick example (AI converted your image into mermaid format)

In a markdown node you can put:


```mermaid
graph LR
    S1((S1)):::s1_color
    S2((S2)):::green
    
    subgraph Box[" "]
        S3((S3)):::red
        S4((S4)):::green
    end
    
    S5((S5)):::green
    S6((S6)):::green
    
    S1 -->|{{msg.payload.s1.value}}| Box
    S2 -->|1520| Box
    Box -->|200| S5
    Box -->|1320| S5
    S5 -->|1170| S6
    
    classDef s1_color fill:{{msg.payload.s1.color}},stroke:#666,color:#fff
    classDef green fill:#82b366,stroke:#6a9654,color:#fff
    classDef red fill:#e00,stroke:#a00,color:#fff

The color and value (for S1) are coming from a function node:

const o = {}

o.s1 = {}
o.s1.value = 120
o.s1.color = "#ff3300"

msg.payload = o
return msg;

The shapes, connections and colors/fonts can be all customized and even animated.

example flow

[
    {
        "id": "ecb066bc52ad3528",
        "type": "ui-markdown",
        "z": "122fc9cab06e5091",
        "group": "376057a66a602d2e",
        "name": "",
        "order": 1,
        "width": 0,
        "height": 0,
        "content": "```mermaid\ngraph LR\n    S1((S1)):::s1_color\n    S2((S2)):::green\n    \n    subgraph Box[\" \"]\n        S3((S3)):::red\n        S4((S4)):::green\n    end\n    \n    S5((S5)):::green\n    S6((S6)):::green\n    \n    S1 -->|{{msg.payload.s1.value}}| Box\n    S2 -->|1520| Box\n    Box -->|200| S5\n    Box -->|1320| S5\n    S5 -->|1170| S6\n    \n    classDef s1_color fill:{{msg.payload.s1.color}},stroke:#666,color:#fff\n    classDef green fill:#82b366,stroke:#6a9654,color:#fff\n    classDef red fill:#e00,stroke:#a00,color:#fff\n```",
        "className": "",
        "x": 490,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "06139bc01b526831",
        "type": "inject",
        "z": "122fc9cab06e5091",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": false,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "",
        "payloadType": "date",
        "x": 180,
        "y": 320,
        "wires": [
            [
                "978e9ef0dc625d62"
            ]
        ]
    },
    {
        "id": "978e9ef0dc625d62",
        "type": "function",
        "z": "122fc9cab06e5091",
        "name": "some values",
        "func": "const o = {}\n\no.s1 = {}\no.s1.value = 120\no.s1.color = \"#ff3300\"\n\nmsg.payload = o\nreturn msg;",
        "outputs": 1,
        "timeout": 0,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 330,
        "y": 320,
        "wires": [
            [
                "ecb066bc52ad3528",
                "3f8723e17b41f707"
            ]
        ]
    },
    {
        "id": "376057a66a602d2e",
        "type": "ui-group",
        "name": "Group 1",
        "page": "722d26d5f962a5bf",
        "width": 6,
        "height": 1,
        "order": 1,
        "showTitle": true,
        "className": "",
        "visible": true,
        "disabled": false,
        "groupType": "default"
    },
    {
        "id": "722d26d5f962a5bf",
        "type": "ui-page",
        "name": "Page 1",
        "ui": "79e3f392c899e28b",
        "path": "/page1",
        "icon": "home",
        "layout": "grid",
        "theme": "9fef7ae69829ce44",
        "breakpoints": [
            {
                "name": "Default",
                "px": 0,
                "cols": 3
            },
            {
                "name": "Tablet",
                "px": 576,
                "cols": 6
            },
            {
                "name": "Small Desktop",
                "px": 768,
                "cols": 9
            },
            {
                "name": "Desktop",
                "px": 1024,
                "cols": 12
            }
        ],
        "order": 1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "79e3f392c899e28b",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 1,
        "showDisconnectNotification": true,
        "allowInstall": false
    },
    {
        "id": "9fef7ae69829ce44",
        "type": "ui-theme",
        "name": "Default Theme",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094CE",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "density": "default",
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    },
    {
        "id": "f08a209a16379eed",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.2"
        }
    }
]

You're a star :star:
I think that will do exactly what I want
I'll mark as solution when I get it all working
Thanks

If you still want to give a svg solution a try, the easiest way now is to use uibuilder to display the svg. uibuilder can take care of all the interactive stuff.
Here you can find an example showing an interactive floorplan.

Thanks, that looks interesting. Haven't used ui-builder yet

I'm usually around if you have any questions. :smiley:

If you like, you can still use tiles... CSS is your friend for shaping them. It may not be the best for your situation but I wanted to make sure others who find this know how CSS classes can (somewhat) easily customize built-in widgets.

[
    {
        "id": "de34d0275741fe5c",
        "type": "ui-gauge",
        "z": "14b49a9c9496432b",
        "name": "tile 2",
        "group": "efbce469212daff6",
        "order": 2,
        "value": "payload",
        "valueType": "msg",
        "width": "1",
        "height": "2",
        "gtype": "gauge-tile",
        "gstyle": "needle",
        "title": "Tile 2",
        "alwaysShowTitle": false,
        "floatingTitlePosition": "top-left",
        "units": "units",
        "icon": "",
        "prefix": "",
        "suffix": "",
        "segments": [
            {
                "from": "0",
                "color": "#5cd65c",
                "text": "",
                "textType": "label"
            },
            {
                "from": "4",
                "color": "#ffc800",
                "text": "",
                "textType": "label"
            },
            {
                "from": "7",
                "color": "#ea5353",
                "text": "",
                "textType": "label"
            }
        ],
        "min": 0,
        "max": 10,
        "sizeThickness": 16,
        "sizeGap": 4,
        "sizeKeyThickness": 8,
        "styleRounded": true,
        "styleGlow": false,
        "className": "rounded-tile",
        "x": 490,
        "y": 480,
        "wires": [
            []
        ]
    },
    {
        "id": "c39f8cf7c63c5374",
        "type": "ui-gauge",
        "z": "14b49a9c9496432b",
        "name": "tile 1",
        "group": "efbce469212daff6",
        "order": 1,
        "value": "payload",
        "valueType": "msg",
        "width": "1",
        "height": "2",
        "gtype": "gauge-tile",
        "gstyle": "needle",
        "title": "Tile 1",
        "alwaysShowTitle": false,
        "floatingTitlePosition": "top-left",
        "units": "units",
        "icon": "",
        "prefix": "",
        "suffix": "",
        "segments": [
            {
                "from": "0",
                "color": "#5cd65c",
                "text": "",
                "textType": "label"
            },
            {
                "from": "4",
                "color": "#ffc800",
                "text": "",
                "textType": "label"
            },
            {
                "from": "7",
                "color": "#ea5353",
                "text": "",
                "textType": "label"
            }
        ],
        "min": 0,
        "max": 10,
        "sizeThickness": 16,
        "sizeGap": 4,
        "sizeKeyThickness": 8,
        "styleRounded": true,
        "styleGlow": false,
        "className": "rounded-tile",
        "x": 490,
        "y": 440,
        "wires": [
            []
        ]
    },
    {
        "id": "99044c78c25adcf7",
        "type": "inject",
        "z": "14b49a9c9496432b",
        "name": "",
        "props": [
            {
                "p": "payload"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "1",
        "payloadType": "num",
        "x": 290,
        "y": 420,
        "wires": [
            [
                "c39f8cf7c63c5374"
            ]
        ]
    },
    {
        "id": "fc6a1dfda7765f2b",
        "type": "inject",
        "z": "14b49a9c9496432b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "4",
        "payloadType": "num",
        "x": 290,
        "y": 480,
        "wires": [
            [
                "de34d0275741fe5c"
            ]
        ]
    },
    {
        "id": "d8d36b2c0c022c20",
        "type": "ui-template",
        "z": "14b49a9c9496432b",
        "group": "",
        "page": "",
        "ui": "01cb2bb666191bef",
        "name": "css",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": ".rounded-tile div {\n    border-radius: 50%;\n    height: 80px;\n    width: 80px;\n}",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "site:style",
        "className": "",
        "x": 410,
        "y": 280,
        "wires": [
            []
        ]
    },
    {
        "id": "9c86cc294dae11c3",
        "type": "ui-gauge",
        "z": "14b49a9c9496432b",
        "name": "tile 3",
        "group": "efbce469212daff6",
        "order": 3,
        "value": "payload",
        "valueType": "msg",
        "width": "1",
        "height": "2",
        "gtype": "gauge-tile",
        "gstyle": "needle",
        "title": "Tile 3",
        "alwaysShowTitle": false,
        "floatingTitlePosition": "top-left",
        "units": "units",
        "icon": "",
        "prefix": "",
        "suffix": "",
        "segments": [
            {
                "from": "0",
                "color": "#5cd65c",
                "text": "",
                "textType": "label"
            },
            {
                "from": "4",
                "color": "#ffc800",
                "text": "",
                "textType": "label"
            },
            {
                "from": "7",
                "color": "#ea5353",
                "text": "",
                "textType": "label"
            }
        ],
        "min": 0,
        "max": 10,
        "sizeThickness": 16,
        "sizeGap": 4,
        "sizeKeyThickness": 8,
        "styleRounded": true,
        "styleGlow": false,
        "className": "rounded-tile",
        "x": 490,
        "y": 520,
        "wires": [
            []
        ]
    },
    {
        "id": "2a6f646ff2951070",
        "type": "inject",
        "z": "14b49a9c9496432b",
        "name": "",
        "props": [
            {
                "p": "payload"
            },
            {
                "p": "topic",
                "vt": "str"
            }
        ],
        "repeat": "",
        "crontab": "",
        "once": true,
        "onceDelay": 0.1,
        "topic": "",
        "payload": "7",
        "payloadType": "num",
        "x": 290,
        "y": 540,
        "wires": [
            [
                "9c86cc294dae11c3"
            ]
        ]
    },
    {
        "id": "efbce469212daff6",
        "type": "ui-group",
        "name": "Group 1",
        "page": "9b525f55b1e35863",
        "width": 6,
        "height": 1,
        "order": 1,
        "showTitle": true,
        "className": "",
        "visible": true,
        "disabled": false,
        "groupType": "default"
    },
    {
        "id": "01cb2bb666191bef",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "navigationStyle": "default",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 1,
        "showDisconnectNotification": true,
        "allowInstall": false
    },
    {
        "id": "9b525f55b1e35863",
        "type": "ui-page",
        "name": "Page 1",
        "ui": "01cb2bb666191bef",
        "path": "/page1",
        "icon": "home",
        "layout": "grid",
        "theme": "370f48f6497327e6",
        "breakpoints": [
            {
                "name": "Default",
                "px": 0,
                "cols": 3
            },
            {
                "name": "Tablet",
                "px": 576,
                "cols": 6
            },
            {
                "name": "Small Desktop",
                "px": 768,
                "cols": 9
            },
            {
                "name": "Desktop",
                "px": 1024,
                "cols": 12
            }
        ],
        "order": 1,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "370f48f6497327e6",
        "type": "ui-theme",
        "name": "Default Theme",
        "colors": {
            "surface": "#ffffff",
            "primary": "#0094CE",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "density": "default",
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px"
        }
    },
    {
        "id": "39466a3280681199",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.2"
        }
    }
]

Very nice. I definitely have a use for that

So I got my visualisation working using Mermaid

I'm passing in the the stations outs, state and current running state in msg.payload

I'm using colours to indicate how well the station is running
Animated links to show if the station is actually in a run state

Overall I'm pretty pleased with the outcome
Thanks to everyone who took the time to help
I'll mark bakman2's reply as the solution

mermaid

And here's a working flow

[{"id":"ecb066bc52ad3528","type":"ui-markdown","z":"aeb8a73b96be29c0","group":"efbce469212daff6","name":"Z1","order":2,"width":"0","height":"0","content":"```mermaid\n\n\n%%{\n  init: {\n    \"theme\": \"dark\",\n    \"themeVariables\": {\n        \"fontSize\": \"1.5rem\",\n        \"edgeLabelBackground\": \"#424242\",\n        \"edgeLabelColor\": \"white\"\n    },\n\n    \n    \"logLevel\": \"info\",\n    \"htmlLabels\": true,\n    \"flowchart\": {\n      \"curve\": \"linear\"\n    },\n    \"sequence\": {\n      \"mirrorActors\": true\n    }\n  }\n}%%\n\n\ngraph LR\n    S1(({{msg.payload[2].MODULE}})):::{{msg.payload[2].STATUS}}\n    S2(({{msg.payload[3].MODULE}})):::{{msg.payload[3].STATUS}}\n    S3(({{msg.payload[4].MODULE}})):::{{msg.payload[4].STATUS}}\n    S4(({{msg.payload[5].MODULE}})):::{{msg.payload[5].STATUS}}\n    CS(({{msg.payload[6].MODULE}})):::{{msg.payload[6].STATUS}}\n    F1(({{msg.payload[7].MODULE}})):::{{msg.payload[7].STATUS}}\n    F2(({{msg.payload[8].MODULE}})):::{{msg.payload[8].STATUS}}\n    C1(({{msg.payload[9].MODULE}})):::{{msg.payload[9].STATUS}}\n    C2(({{msg.payload[10].MODULE}})):::{{msg.payload[10].STATUS}}\n    GL(({{msg.payload[11].MODULE}})):::{{msg.payload[11].STATUS}}\n    \n    Z2((Zone 2))\n\n    \n    S1 an2@-->|{{msg.payload[2].OUTS}}| J0\n    S2 an3@-->|{{msg.payload[3].OUTS}}| J0\n    S3 an4@-->|{{msg.payload[4].OUTS}}| J0\n    J0 an17@--> J1\n    S4 an5@-->|{{msg.payload[5].OUTS}}| J0 an12@---> CS\n\n    CS an6@--->|{{msg.payload[6].OUTS}}| J1\n    \n    J1 an13@---> F1 an7@--->|{{msg.payload[7].OUTS}}| J2\n    J1 an14@---> F2 an8@--->|{{msg.payload[8].OUTS}}| J2\n    J2 an15@---> C1 an9@--->|{{msg.payload[9].OUTS}}| GL\n    J2 an16@---> C2 an10@--->|{{msg.payload[10].OUTS}}| GL\n    GL an11@--->|{{msg.payload[11].OUTS}} | Z2\n    \n    J0@{ shape: f-circ}\n    J1@{ shape: f-circ}\n    J2@{ shape: f-circ}\n\n    \n    an2@{ animate: {{msg.payload[2].STATE}} }\n    an3@{ animate: {{msg.payload[3].STATE}} }\n    an4@{ animate: {{msg.payload[4].STATE}} }\n    an5@{ animate: {{msg.payload[5].STATE}} }\n    an6@{ animate: {{msg.payload[6].STATE}} }\n    an7@{ animate: {{msg.payload[7].STATE}} }\n    an8@{ animate: {{msg.payload[8].STATE}} }\n    an9@{ animate: {{msg.payload[9].STATE}} }\n    an10@{ animate: {{msg.payload[10].STATE}} }\n    an11@{ animate: {{msg.payload[11].STATE}} }\n    an12@{ animate: {{msg.payload[2].STATE || msg.payload[3].STATE || msg.payload[4].STATE || msg.payload[5].STATE}} }\n    an13@{ animate: {{msg.payload[2].STATE || msg.payload[3].STATE || msg.payload[4].STATE || msg.payload[5].STATE}} }\n    an14@{ animate: {{msg.payload[2].STATE || msg.payload[3].STATE || msg.payload[4].STATE || msg.payload[5].STATE}} }\n    an15@{ animate: {{msg.payload[7].STATE || msg.payload[8].STATE }} }\n    an16@{ animate: {{msg.payload[7].STATE || msg.payload[8].STATE }} }\n    an17@{ animate: {{(msg.payload[2].STATE || msg.payload[3].STATE || msg.payload[4].STATE || msg.payload[5].STATE)  && msg.payload[6].OUTS == 0 }} }\n\n  \n\n    \n    classDef grey fill:#808080,stroke:#6a9654,color:#fff\n    classDef green fill:#5cd65c,stroke:#6a9654,color:#fff\n    classDef orange fill:#ffc800,stroke:#6a9654,color:black\n    classDef red fill:#e00,stroke:#a00,color:#fff\n\n    \n    \n    linkStyle default stroke:grey,stroke-width:4px\n   \n    style Z2 fill:#424242,stroke:white,stroke-width:1px,color:white\n  \n```\n","className":"","x":890,"y":280,"wires":[[]]},{"id":"c54dada3a8f273e1","type":"inject","z":"aeb8a73b96be29c0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"MODULE\":\"X1\",\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0},{\"MODULE\":\"X2\",\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":11974},{\"MODULE\":\"SP1\",\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":5605},{\"MODULE\":\"SP2\",\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":6111},{\"ORDINAL\":5,\"MODULE\":\"SP3\",\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0},{\"ORDINAL\":6,\"MODULE\":\"SP4\",\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0},{\"MODULE\":\"CS1\",\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0},{\"MODULE\":\"FT1\",\"STATUS\":\"orange\",\"STATE\":10,\"OUTS\":11687},{\"MODULE\":\"FT2\",\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0},{\"MODULE\":\"CO1\",\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":8278},{\"MODULE\":\"CO2\",\"STATUS\":\"orange\",\"STATE\":10,\"OUTS\":4779},{\"MODULE\":\"GL1\",\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":13416}]","payloadType":"json","x":730,"y":280,"wires":[["ecb066bc52ad3528"]]},{"id":"efbce469212daff6","type":"ui-group","name":"Group 1","page":"9b525f55b1e35863","width":"12","height":1,"order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"9b525f55b1e35863","type":"ui-page","name":"Page 1","ui":"4740393b964cb7af","path":"/page1","icon":"home","layout":"grid","theme":"11edfaf96d0baec1","breakpoints":[{"name":"Default","px":"0","cols":"3"},{"name":"Tablet","px":"576","cols":"6"},{"name":"Small Desktop","px":"768","cols":"9"},{"name":"Desktop","px":"1024","cols":"12"}],"order":2,"className":"","visible":"true","disabled":"false"},{"id":"4740393b964cb7af","type":"ui-base","name":"Playground","path":"/dashboard","appIcon":"","includeClientData":true,"acceptsClientConfig":["ui-notification","ui-control","ui-dropdown","ui-chart","ui-gauge","ui-button","ui-template","ui-text","ui-table","ui-form","ui-switch","ui-text-input"],"showPathInSidebar":false,"headerContent":"page","navigationStyle":"fixed","titleBarStyle":"hidden","showReconnectNotification":true,"notificationDisplayTime":"1","showDisconnectNotification":true,"allowInstall":true},{"id":"11edfaf96d0baec1","type":"ui-theme","name":"Dark1","colors":{"surface":"#424242","primary":"#0094ce","bgPage":"#424242","groupBg":"#424242","groupOutline":"#424242"},"sizes":{"density":"default","pagePadding":"2px","groupGap":"6px","groupBorderRadius":"4px","widgetGap":"10px"}},{"id":"654c97f62568a8bd","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.29.0"}}]

That is very neat ...

Added an updated flow in Share my Projects https://discourse.nodered.org/t/visual-production-line/100856