Visual Production Line

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

zone1

I have no idea what i am looking at but it looks cool​:sweat_smile:. Just for fun, mermaid also has support for gitgraphs, which you could ‘abuse’ to create ‘tube’ maps

Well the stations are process stations which convert raw material into parts which eventually becomes finished products
I did look at gitgraph which is very cool too, but I settled on the standard graphs for now