I was attempting to show the status of our production lines., so that operators/managers can see at a glance how the lines are performing live. @bakman2 directed me towards Mermaid.
This is an example flow for one of our production zones
[{"id":"c54dada3a8f273e1","type":"inject","z":"aeb8a73b96be29c0","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":1,\"MODULE\":\"SNOU\",\"TARGET\":15000,\"LEVEL\":0,\"STATUS\":\"grey\",\"STATE\":0,\"PPM\":0,\"INS\":0,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":2,\"MODULE\":\"SNO2\",\"TARGET\":15000,\"LEVEL\":94,\"STATUS\":\"green\",\"STATE\":0,\"OUTS\":13784,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":3,\"MODULE\":\"SPR1\",\"TARGET\":3750,\"LEVEL\":191,\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":6998,\"LOSS\":93,\"REJECT\":93},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":4,\"MODULE\":\"SPR2\",\"TARGET\":3750,\"LEVEL\":169,\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":6200,\"LOSS\":98,\"REJECT\":98},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":5,\"MODULE\":\"SPR3\",\"TARGET\":3750,\"LEVEL\":0,\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":6,\"MODULE\":\"SPR4\",\"TARGET\":3750,\"LEVEL\":0,\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":7,\"MODULE\":\"CSA\",\"TARGET\":15000,\"LEVEL\":0,\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0,\"LOSS\":0,\"REJECT\":null},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":8,\"MODULE\":\"FILT\",\"TARGET\":15000,\"LEVEL\":89,\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":13128,\"LOSS\":295,\"REJECT\":295},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":9,\"MODULE\":\"FIL2\",\"TARGET\":15000,\"LEVEL\":0,\"STATUS\":\"grey\",\"STATE\":0,\"OUTS\":0,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":10,\"MODULE\":\"COVE\",\"TARGET\":7500,\"LEVEL\":113,\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":8306,\"LOSS\":181,\"REJECT\":85},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":11,\"MODULE\":\"COV2\",\"TARGET\":7500,\"LEVEL\":67,\"STATUS\":\"orange\",\"STATE\":10,\"OUTS\":4922,\"LOSS\":17,\"REJECT\":8},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":12,\"MODULE\":\"GROS\",\"TARGET\":15000,\"LEVEL\":91,\"STATUS\":\"green\",\"STATE\":10,\"OUTS\":13360,\"LOSS\":62,\"REJECT\":62},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":99,\"MODULE\":\"COVA\",\"TARGET\":15000,\"LEVEL\":0,\"STATUS\":\"\",\"STATE\":0,\"OUTS\":840,\"LOSS\":0,\"REJECT\":0},{\"SHIFT\":0,\"ZONE\":1,\"ORDINAL\":99,\"MODULE\":\"FILA\",\"TARGET\":15000,\"LEVEL\":0,\"STATUS\":\"\",\"STATE\":0,\"OUTS\":0,\"LOSS\":0,\"REJECT\":0}]","payloadType":"json","x":150,"y":160,"wires":[["ecb066bc52ad3528"]]},{"id":"d8d36b2c0c022c20","type":"ui-template","z":"aeb8a73b96be29c0","group":"","page":"","ui":"4740393b964cb7af","name":"css","order":0,"width":0,"height":0,"head":"","format":"\n.scrap {\n font-size: 1.2rem ;\n color: orange !important;\n}\n\n.vision {\n font-size: 1.2rem ;\n color: steelblue !important;\n}","storeOutMessages":true,"passthru":true,"resendOnRefresh":true,"templateScope":"site:style","className":"","x":530,"y":60,"wires":[[]]},{"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\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 Z2([Zone 2])\n\n\n S1 an2@-->|{{msg.payload[2].OUTS}}</br> <span class=\"scrap\">{{msg.payload[2].REJECT}}</span>| J0\n S2 an3@-->|{{msg.payload[3].OUTS}}</br> <span class=\"scrap\">{{msg.payload[3].REJECT}}</span>| J0\n S3 an4@-->|{{msg.payload[4].OUTS}}</br> <span class=\"scrap\">{{msg.payload[4].REJECT}}</span>| J0\n J0 an17@--> J1\n S4 an5@-->|{{msg.payload[5].OUTS}}</br> <span class=\"scrap\">{{msg.payload[5].REJECT}}</span>| J0 an12@---> CS\n CS an6@--->|{{msg.payload[6].OUTS}}</br> <span class=\"scrap\">{{msg.payload[6].LOSS}}</span>| J1\n \n J1 an13@---> F1 an7@--->|{{msg.payload[7].OUTS}}</br> <span class=\"scrap\">{{msg.payload[7].REJECT}}</span>| J2\n J1 an14@---> F2 an8@--->|{{msg.payload[8].OUTS}}</br> <span class=\"scrap\">{{msg.payload[8].REJECT}}</span>| J2\n FA an19@<-->|{{msg.payload[13].OUTS}}| J2\n CA an20@<-->|{{msg.payload[12].OUTS}}| J3\n J2 an15@---> C1 an9@--->|{{msg.payload[9].OUTS}}</br> <span class=\"scrap\">{{msg.payload[9].REJECT}}</span>| J3\n J2 an16@---> C2 an10@--->|{{msg.payload[10].OUTS}}</br> <span class=\"scrap\">{{msg.payload[10].REJECT}}</span>| J3\n J3 an18@---> GL\n\n GL an11@---|{{msg.payload[11].OUTS}}</br> <span class=\"scrap\">{{msg.payload[11].REJECT}}</span>| Z2\n \n J0@{ shape: f-circ}\n J1@{ shape: f-circ}\n J2@{ shape: f-circ}\n J3@{ shape: f-circ}\n CA@{ shape: dbl-circ, label: \"COVA\" }\n FA@{ shape: dbl-circ, label: \"FILA\" }\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 an18@{ animate: {{msg.payload[9].STATE || msg.payload[10].STATE }} }\n an19@{ animate: {{msg.payload[13].OUTS > 0 }} }\n an20@{ animate: {{msg.payload[12].OUTS > 0 }} }\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 classDef dark fill:#424242,stroke:white,color:white\n \n \n linkStyle default stroke:grey,stroke-width:4px,color:white\n \n style Z2 fill:#424242,stroke:white,stroke-width:1px,color:white\n style CA fill:steelblue,stroke:white,stroke-width:1px,color:white\n style FA fill:steelblue,stroke:white,stroke-width:1px,color:white\n \n```\n","className":"","x":630,"y":280,"wires":[[]]},{"id":"e51375b83bba0a55","type":"ui-markdown","z":"aeb8a73b96be29c0","group":"efbce469212daff6","name":"S1","order":1,"width":"0","height":"0","content":"```mermaid\n\n\n%%{\n init: {\n \"theme\": \"dark\",\n \"themeVariables\": {\n \"fontSize\": \"1.0rem\",\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\n\ngraph LR\n ST1--> ST2 --> ST3 --> ST4 --> ST5 --> ST6 --> ST7\n \n ST1([NOT RUNNING]):::grey\n ST2([MINOR DELAYS]):::orange\n ST3([SEVERE DELAYS]):::red\n ST4([GOOD SERVICE]):::green\n ST5([ACCUMULATOR]):::accum\n ST6([GOOD OUTS]):::outs\n ST7([SCRAP]):::reject\n \n \n \n classDef grey fill:#808080,stroke:grey,color:white\n classDef green fill:#5cd65c,stroke:grey,color:#fff\n classDef orange fill:#ffc800,stroke:grey,color:black\n classDef red fill:#e00,stroke:#a00,color:white\n classDef outs fill:#424242,stroke:grey,color:white\n classDef reject fill:#424242,stroke:grey,color:orange\n \n classDef accum fill:steelblue,stroke:grey,color:white\n\n\n \n \n linkStyle default stroke:#424242,stroke-width:4px\n \n \n```\n","className":"","x":630,"y":220,"wires":[[]]},{"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":"efbce469212daff6","type":"ui-group","name":"Zone 1","page":"9b525f55b1e35863","width":"12","height":1,"order":1,"showTitle":false,"className":"","visible":"true","disabled":"false","groupType":"default"},{"id":"9b525f55b1e35863","type":"ui-page","name":"Visualisation","ui":"4740393b964cb7af","path":"/page1","icon":"home","layout":"tabs","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":"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":"8b9d5ced0eaf606c","type":"global-config","env":[],"modules":{"@flowfuse/node-red-dashboard":"1.29.0"}}]
Animated links show if the station is currently in a Run state, while the colours indicate the output status of the station. I've used the London Tube status for inspiration
Maybe others will find this useful
