diff --git a/flow.json b/flow.json index 0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc..a641887c2a165e65d91fe3d0909db4c498dcd5cd 100644 --- a/flow.json +++ b/flow.json @@ -1 +1,2671 @@ -[] \ No newline at end of file +[ + { + "id": "27ca1ab3d111d118", + "type": "tab", + "label": "Connection & warehouse", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "593bb738e7507707", + "type": "tab", + "label": "Capabilities list", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "2bc19b2f7b518cb2", + "type": "tab", + "label": "Form", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "f96e455ab747ba2b", + "type": "tab", + "label": "Product feasibility", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "1320790a4546b4fd", + "type": "tab", + "label": "Proposition", + "disabled": false, + "info": "", + "env": [] + }, + { + "id": "8f5d8ce91dd46f18", + "type": "junction", + "z": "1320790a4546b4fd", + "x": 320, + "y": 380, + "wires": [ + [ + "0f6401f582448d0c" + ] + ] + }, + { + "id": "2282c53e6f2d220c", + "type": "mqtt-broker", + "name": "RaspberryPi", + "broker": "192.168.200.163", + "port": "8883", + "tls": "5850cb65c596909b", + "clientid": "", + "autoConnect": true, + "usetls": true, + "protocolVersion": "4", + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "birthMsg": {}, + "closeTopic": "", + "closeQos": "0", + "closePayload": "", + "closeMsg": {}, + "willTopic": "", + "willQos": "0", + "willPayload": "", + "willMsg": {}, + "userProps": "", + "sessionExpiry": "" + }, + { + "id": "dbb23360d33d8fb4", + "type": "ui_tab", + "name": "Home", + "icon": "", + "disabled": false, + "hidden": false + }, + { + "id": "1c4f96d6011aaaa5", + "type": "ui_base", + "theme": { + "name": "theme-light", + "lightTheme": { + "default": "#0094CE", + "baseColor": "#0094CE", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "darkTheme": { + "default": "#097479", + "baseColor": "#4e6f83", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "edited": true, + "reset": false + }, + "customTheme": { + "name": "Untitled Theme 1", + "default": "#4B7930", + "baseColor": "#4B7930", + "baseFont": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif", + "reset": false + }, + "themeState": { + "base-color": { + "default": "#0094CE", + "value": "#0094CE", + "edited": true + }, + "page-titlebar-backgroundColor": { + "value": "#0094CE", + "edited": false + }, + "page-backgroundColor": { + "value": "#fafafa", + "edited": false + }, + "page-sidebar-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "group-textColor": { + "value": "#1bbfff", + "edited": false + }, + "group-borderColor": { + "value": "#ffffff", + "edited": false + }, + "group-backgroundColor": { + "value": "#ffffff", + "edited": false + }, + "widget-textColor": { + "value": "#111111", + "edited": false + }, + "widget-backgroundColor": { + "value": "#0094ce", + "edited": false + }, + "widget-borderColor": { + "value": "#ffffff", + "edited": false + }, + "base-font": { + "value": "-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif" + } + }, + "angularTheme": { + "primary": "indigo", + "accents": "blue", + "warn": "red", + "background": "grey", + "palette": "light" + } + }, + "site": { + "name": "Node-RED Dashboard", + "hideToolbar": "false", + "allowSwipe": "false", + "lockMenu": "false", + "allowTempTheme": "true", + "dateFormat": "DD/MM/YYYY", + "sizes": { + "sx": 48, + "sy": 48, + "gx": 6, + "gy": 6, + "cx": 6, + "cy": 6, + "px": 0, + "py": 0 + } + } + }, + { + "id": "80a80175907e2ec4", + "type": "ui_group", + "name": "Capabilities", + "tab": "0ffad0ef9167997d", + "order": 2, + "disp": true, + "width": "8", + "collapse": false, + "className": "" + }, + { + "id": "df9ec70032434b59", + "type": "mqtt-broker", + "name": "local (secure)", + "broker": "localhost", + "port": "8883", + "tls": "ceab5cf497e7e421", + "clientid": "", + "autoConnect": true, + "usetls": true, + "protocolVersion": "4", + "keepalive": "60", + "cleansession": true, + "birthTopic": "", + "birthQos": "0", + "birthPayload": "", + "birthMsg": {}, + "closeTopic": "", + "closeQos": "0", + "closePayload": "", + "closeMsg": {}, + "willTopic": "", + "willQos": "0", + "willPayload": "", + "willMsg": {}, + "userProps": "", + "sessionExpiry": "" + }, + { + "id": "f46a108f2a7fd47d", + "type": "ui_group", + "name": "Information", + "tab": "0ffad0ef9167997d", + "order": 2, + "disp": true, + "width": "16", + "collapse": false, + "className": "" + }, + { + "id": "f0a0e923d8d71887", + "type": "ui_tab", + "name": "Data", + "icon": "", + "disabled": false, + "hidden": true + }, + { + "id": "f71415c794c6ce3f", + "type": "ui_group", + "name": "Variables", + "tab": "f0a0e923d8d71887", + "order": 1, + "disp": true, + "width": "8", + "collapse": false, + "className": "" + }, + { + "id": "498001e703259453", + "type": "ui_group", + "name": "Live", + "tab": "f0a0e923d8d71887", + "order": 2, + "disp": true, + "width": "12", + "collapse": false, + "className": "" + }, + { + "id": "f42eda3bec77a0ec", + "type": "ui_group", + "name": "Enter your own values", + "tab": "ae7ff6cddbcae331", + "order": 2, + "disp": true, + "width": "12", + "collapse": false, + "className": "" + }, + { + "id": "316f43488033c495", + "type": "ui_group", + "name": "Connected clients", + "tab": "61216f89242a96ef", + "order": 2, + "disp": true, + "width": "8", + "collapse": false, + "className": "" + }, + { + "id": "ae7ff6cddbcae331", + "type": "ui_tab", + "name": "Workpiece order", + "icon": "", + "disabled": false, + "hidden": false + }, + { + "id": "056d3115f1f3b8b2", + "type": "ui_group", + "name": "nothing", + "tab": "dbb23360d33d8fb4", + "order": 1, + "disp": true, + "width": "10", + "collapse": false, + "className": "" + }, + { + "id": "1bea595dc9db51b7", + "type": "ui_group", + "name": "Products", + "tab": "ae7ff6cddbcae331", + "order": 2, + "disp": true, + "width": "10", + "collapse": false, + "className": "" + }, + { + "id": "11c65e700ad5a74d", + "type": "ui_tab", + "name": "Proposition", + "icon": "", + "disabled": false, + "hidden": true + }, + { + "id": "859d724c411b6b56", + "type": "ui_group", + "name": "Your product", + "tab": "11c65e700ad5a74d", + "order": 1, + "disp": true, + "width": "10", + "collapse": false, + "className": "" + }, + { + "id": "fbf688fde095656a", + "type": "ui_tab", + "name": "Confirmation", + "icon": "", + "disabled": false, + "hidden": true + }, + { + "id": "25611d1488853208", + "type": "ui_group", + "name": "Thank you for your order", + "tab": "fbf688fde095656a", + "order": 1, + "disp": true, + "width": "6", + "collapse": false, + "className": "" + }, + { + "id": "0ffad0ef9167997d", + "type": "ui_tab", + "name": "Capabilities", + "icon": "dashboard", + "disabled": false, + "hidden": true + }, + { + "id": "61216f89242a96ef", + "type": "ui_tab", + "name": "Clients", + "icon": "", + "disabled": false, + "hidden": false + }, + { + "id": "5850cb65c596909b", + "type": "tls-config", + "name": "schlag config", + "cert": "", + "key": "", + "ca": "", + "certname": "", + "keyname": "", + "caname": "", + "servername": "", + "verifyservercert": false, + "alpnprotocol": "" + }, + { + "id": "ceab5cf497e7e421", + "type": "tls-config", + "name": "shlag config", + "cert": "", + "key": "", + "ca": "", + "certname": "", + "keyname": "", + "caname": "", + "servername": "", + "verifyservercert": false, + "alpnprotocol": "" + }, + { + "id": "b0f715d01c382afe", + "type": "mqtt in", + "z": "27ca1ab3d111d118", + "name": "mqtt", + "topic": "+", + "qos": "2", + "datatype": "auto-detect", + "broker": "2282c53e6f2d220c", + "nl": false, + "rap": true, + "rh": 0, + "inputs": 0, + "x": 90, + "y": 220, + "wires": [ + [ + "2a070a302599348a" + ] + ] + }, + { + "id": "0f6571cbd5a08e12", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "store status", + "func": "// for now => has a payload = connected\nconst isConnected = Boolean(msg.payload);\nconst map = flow.get('clients');\nconst client = {\n description: msg.payload.identification.id,\n title: msg.payload.idShort,\n isConnected: isConnected\n};\n\nmap.set(msg.payload.identification.id, client);\nmsg.payload = map;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nflow.set('clients', new Map());", + "finalize": "", + "libs": [], + "x": 430, + "y": 220, + "wires": [ + [ + "1d11aac3329ba98d" + ] + ] + }, + { + "id": "2a070a302599348a", + "type": "switch", + "z": "27ca1ab3d111d118", + "name": "model type", + "property": "payload.modelType.name", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "AssetAdministrationShell", + "vt": "str" + }, + { + "t": "eq", + "v": "Submodel", + "vt": "str" + }, + { + "t": "else" + } + ], + "checkall": "false", + "repair": false, + "outputs": 3, + "x": 250, + "y": 220, + "wires": [ + [ + "0f6571cbd5a08e12", + "356d262113f2172e" + ], + [ + "c6f4da7bfcc9ee69" + ], + [ + "37acdbd7d61f880a" + ] + ] + }, + { + "id": "1d11aac3329ba98d", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "retrieve connected", + "func": "msg.payload = [...msg.payload]\n .filter(([_, value]) => value.isConnected)\n .map(([_, value]) => value);\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 610, + "y": 220, + "wires": [ + [ + "36db99b4430e55d1" + ] + ] + }, + { + "id": "36db99b4430e55d1", + "type": "ui_list", + "z": "27ca1ab3d111d118", + "group": "316f43488033c495", + "name": "list", + "order": 1, + "width": "8", + "height": "10", + "lineType": "three", + "actionType": "click", + "allowHTML": false, + "outputs": 1, + "topic": "", + "x": 790, + "y": 220, + "wires": [ + [ + "f1662dd7df4e450c" + ] + ] + }, + { + "id": "f1662dd7df4e450c", + "type": "link out", + "z": "27ca1ab3d111d118", + "name": "link out - clicked asset", + "mode": "link", + "links": [ + "8729880c79bd594e" + ], + "x": 875, + "y": 220, + "wires": [] + }, + { + "id": "356d262113f2172e", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "asset → submodels", + "func": "/** @type {Map<string, string[]>} */\nconst map = global.get('submodelMap');\nconst submodels = msg.payload.submodels.flatMap(obj =>\n obj.keys.map(key => key.value)\n);\n\nmap.set(msg.payload.identification.id, submodels);", + "outputs": 0, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set('submodelMap', new Map());", + "finalize": "", + "libs": [], + "x": 470, + "y": 180, + "wires": [] + }, + { + "id": "c6f4da7bfcc9ee69", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "store submodel", + "func": "const map = global.get('submodels');\nmap.set(msg.payload.identification.id, msg.payload);\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set('submodels', new Map());", + "finalize": "", + "libs": [], + "x": 460, + "y": 260, + "wires": [ + [ + "e69ab0ee6ab78025" + ] + ] + }, + { + "id": "21862df7667fcb11", + "type": "inject", + "z": "27ca1ab3d111d118", + "name": "reset", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0", + "topic": "", + "payload": "[]", + "payloadType": "json", + "x": 650, + "y": 180, + "wires": [ + [ + "36db99b4430e55d1" + ] + ] + }, + { + "id": "dd5f965e.fb70b8", + "type": "ui_template", + "z": "27ca1ab3d111d118", + "group": "", + "name": "css slsmkfùmzoqf", + "order": 4, + "width": 0, + "height": 0, + "format": "<style>\n .ce-block__content {\n max-width: none !important;\n margin: 0 1% !important;\n }\n\n p.nr-dashboard-cardtitle {\n text-decoration: underline;\n }\n\n .ce-block__content h1 {\n font-size: none !important;\n }\n\n .popup {\n padding: 10px !important;\n }\n\n ui-card-panel {\n padding: 10px !important;\n }\n\n</style>", + "storeOutMessages": true, + "fwdInMessages": true, + "resendOnRefresh": false, + "templateScope": "global", + "className": "", + "x": 630, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "325dffad2dd74522", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "concept description", + "func": "/** @type {Map<string, any>} */\nconst concepts = global.get('concepts');\n\nmsg.payload.forEach(concept => {\n const id = concept.identification.id;\n concepts.set(id, concept);\n})", + "outputs": 0, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set('concepts', new Map());", + "finalize": "", + "libs": [], + "x": 610, + "y": 300, + "wires": [] + }, + { + "id": "37acdbd7d61f880a", + "type": "switch", + "z": "27ca1ab3d111d118", + "name": "concept filter", + "property": "topic", + "propertyType": "msg", + "rules": [ + { + "t": "regex", + "v": "concept.*descriptions?.*", + "vt": "str", + "case": true + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 430, + "y": 300, + "wires": [ + [ + "325dffad2dd74522" + ] + ] + }, + { + "id": "63318fe2d16dfbd0", + "type": "function", + "z": "27ca1ab3d111d118", + "name": "store capabilities", + "func": "const capabilities = global.get('capabilities');\nconst subcaps = msg.payload.submodelElements\n .filter(element =>\n element.modelType.name === 'RelationshipElement'\n )\n\nsubcaps.forEach(element =>\n capabilities.set(\n element.first.keys.at(1).value, \n element.second.keys.at(0).value\n )\n);\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set('capabilities', new Map());", + "finalize": "", + "libs": [], + "x": 790, + "y": 260, + "wires": [ + [ + "f92afa1f37cc46cb" + ] + ] + }, + { + "id": "e69ab0ee6ab78025", + "type": "switch", + "z": "27ca1ab3d111d118", + "name": "cap filter", + "property": "payload.idShort", + "propertyType": "msg", + "rules": [ + { + "t": "eq", + "v": "Capabilities", + "vt": "str" + } + ], + "checkall": "true", + "repair": false, + "outputs": 1, + "x": 620, + "y": 260, + "wires": [ + [ + "63318fe2d16dfbd0" + ] + ] + }, + { + "id": "9ace58fe9a8ef6eb", + "type": "inject", + "z": "27ca1ab3d111d118", + "name": "", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0.5", + "topic": "", + "payload": "Home", + "payloadType": "str", + "x": 110, + "y": 80, + "wires": [ + [ + "089217f817d51501" + ] + ] + }, + { + "id": "e8ece81e1163d14a", + "type": "ui_ui_control", + "z": "27ca1ab3d111d118", + "name": "", + "events": "all", + "x": 420, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "0f773f94330e38ae", + "type": "ui_button", + "z": "27ca1ab3d111d118", + "name": "", + "group": "056d3115f1f3b8b2", + "order": 2, + "width": 0, + "height": 0, + "passthru": false, + "label": "Clients", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "Clients", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 270, + "y": 40, + "wires": [ + [ + "e8ece81e1163d14a" + ] + ] + }, + { + "id": "58e1536359af4b69", + "type": "ui_button", + "z": "27ca1ab3d111d118", + "name": "", + "group": "056d3115f1f3b8b2", + "order": 3, + "width": 0, + "height": 0, + "passthru": false, + "label": "Workpiece order", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "Workpiece order", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 240, + "y": 120, + "wires": [ + [ + "e8ece81e1163d14a" + ] + ] + }, + { + "id": "089217f817d51501", + "type": "ui_button", + "z": "27ca1ab3d111d118", + "name": "", + "group": "316f43488033c495", + "order": 2, + "width": 0, + "height": 0, + "passthru": true, + "label": "Back home", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "Home", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 250, + "y": 80, + "wires": [ + [ + "e8ece81e1163d14a" + ] + ] + }, + { + "id": "f92afa1f37cc46cb", + "type": "link out", + "z": "27ca1ab3d111d118", + "name": "dmdfsfkodmdfkg", + "mode": "link", + "links": [ + "b8640315f79bbc2c", + "58be501a101c8482" + ], + "x": 915, + "y": 260, + "wires": [] + }, + { + "id": "8729880c79bd594e", + "type": "link in", + "z": "593bb738e7507707", + "name": "link in 1", + "links": [ + "f1662dd7df4e450c", + "c9bfe0162fe24c52" + ], + "x": 85, + "y": 160, + "wires": [ + [ + "689f243db86e15b3" + ] + ] + }, + { + "id": "0d77bbe26c501f2a", + "type": "inject", + "z": "593bb738e7507707", + "name": "reset", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0", + "topic": "", + "payload": "[{\"title\": \"Select a client\"}]", + "payloadType": "json", + "x": 250, + "y": 80, + "wires": [ + [ + "bf4ce6760659e9da" + ] + ] + }, + { + "id": "bf4ce6760659e9da", + "type": "ui_list", + "z": "593bb738e7507707", + "group": "80a80175907e2ec4", + "name": "", + "order": 1, + "width": "8", + "height": "10", + "lineType": "three", + "actionType": "click", + "allowHTML": false, + "outputs": 1, + "topic": "", + "x": 430, + "y": 160, + "wires": [ + [ + "ef433e7595d5c4f2" + ] + ] + }, + { + "id": "689f243db86e15b3", + "type": "function", + "z": "593bb738e7507707", + "name": "list from submodel", + "func": "/** @type {Map<string, any>} */\nconst submodels = global.get('submodels');\n\n// extracts the capabilities submodel and retrieve\n// the submodels associated with a relationship element,\n// building an array of object for the list node\nmsg.payload = global.get('submodelMap')\n .get(msg.payload.description)\n .map(id => submodels.get(id))\n .find(submodel => submodel.idShort === 'Capabilities')\n .submodelElements\n .filter(element =>\n element.modelType.name === 'RelationshipElement'\n )\n .map(element => ({\n title: element.first.keys.at(1).value,\n description: element.second.keys.at(0).value\n }));\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 210, + "y": 160, + "wires": [ + [ + "bf4ce6760659e9da", + "8d7f127f6e8b4203" + ] + ] + }, + { + "id": "a28b244bb8e51bc4", + "type": "ui_ui_control", + "z": "593bb738e7507707", + "name": "", + "events": "change", + "x": 640, + "y": 120, + "wires": [ + [] + ] + }, + { + "id": "8d7f127f6e8b4203", + "type": "change", + "z": "593bb738e7507707", + "name": "change to caps.", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "Capabilities", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 460, + "y": 120, + "wires": [ + [ + "a28b244bb8e51bc4" + ] + ] + }, + { + "id": "a594eb612218b2e3", + "type": "ui_button", + "z": "593bb738e7507707", + "name": "", + "group": "80a80175907e2ec4", + "order": 1, + "width": 0, + "height": 0, + "passthru": false, + "label": "Back to list", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "arrow_back", + "payload": "", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 230, + "y": 120, + "wires": [ + [ + "d2c70a9d17d17501", + "bf4ce6760659e9da" + ] + ] + }, + { + "id": "d2c70a9d17d17501", + "type": "change", + "z": "593bb738e7507707", + "name": "change to home", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "Clients", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 460, + "y": 80, + "wires": [ + [ + "a28b244bb8e51bc4" + ] + ] + }, + { + "id": "b91eb42511d8b336", + "type": "catch", + "z": "593bb738e7507707", + "name": "", + "scope": [ + "689f243db86e15b3" + ], + "uncaught": false, + "x": 110, + "y": 200, + "wires": [ + [ + "ef0e0e5a4eeaad5a" + ] + ] + }, + { + "id": "ac9f9fa8573bdbeb", + "type": "ui_toast", + "z": "593bb738e7507707", + "position": "dialog", + "displayTime": "3", + "highlight": "", + "sendall": false, + "outputs": 1, + "ok": "OK", + "cancel": "", + "raw": true, + "className": "popup", + "topic": "Error", + "name": "", + "x": 410, + "y": 200, + "wires": [ + [] + ] + }, + { + "id": "ef0e0e5a4eeaad5a", + "type": "change", + "z": "593bb738e7507707", + "name": "dialog text", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "Could not retrieve the capabilities of the asset.<br>Make sure this asset shares a \"Capabilities\" submodel containing its functionalities.", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 250, + "y": 200, + "wires": [ + [ + "ac9f9fa8573bdbeb" + ] + ] + }, + { + "id": "ef433e7595d5c4f2", + "type": "function", + "z": "593bb738e7507707", + "name": "formatting", + "func": "const submodel = global.get('submodels').get(msg.payload.description);\n\n/** @type {{time: number;blocks: {id: string;type: string;data: {text: any;level: number;};}[];version: string;}} */\nconst content = {\n time: new Date().getTime(),\n blocks: [\n {\n id: \"header\",\n type: \"header\",\n data: {\n text: `<u class=\"cdx-underline\">${msg.payload.title}</u>`,\n level: 1\n },\n },\n ],\n version: \"2.26.5\"\n}\n\nmsg.payload = content;\nmsg.topic = \"render\";\nmsg.submodel = submodel;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nflow.set('clicked', undefined)", + "finalize": "", + "libs": [], + "x": 560, + "y": 160, + "wires": [ + [ + "1e55e4a8898e2f68" + ] + ] + }, + { + "id": "ac616b75994208a7", + "type": "inject", + "z": "593bb738e7507707", + "name": "reset", + "props": [ + { + "p": "payload" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0", + "topic": "render", + "payload": "{\"blocks\":[]}", + "payloadType": "json", + "x": 570, + "y": 200, + "wires": [ + [ + "dc020b3060cb7c2a" + ] + ] + }, + { + "id": "1e55e4a8898e2f68", + "type": "function", + "z": "593bb738e7507707", + "name": "editor message", + "func": "const collection = /SubmodelElement(Collection|List)/g;\nconst tab = level => \"ㅤㅤ\".repeat(level - 2);\n\n/**\n * Transform PascalCase text into a normal sentence\n */\nconst niceText = text =>\n text.replaceAll(/(?<=.)[A-Z]/g, match =>\n ` ${match.toLocaleLowerCase()}`\n );\n\n/**\n * Returns an Editor.js paragraph block for a\n * submodel element\n */\nconst valueBlock = (element, level) => ({\n id: crypto.randomUUID(),\n type: \"paragraph\",\n data: {\n text: `${tab(level - 1)}${niceText(element.idShort)}: ${global.get('text')(element)}`\n }\n});\n\n/**\n * Returns an Editor.js header block for\n * a submodel element\n */\nconst headerBlock = (element, level) => ({\n id: crypto.randomUUID(),\n type: \"header\",\n data: {\n text: `${tab(level)}${niceText(element.idShort)}`,\n level: Math.min(level, 6)\n }\n});\n\n/**\n * Returns an Editor.js blank paragraph block\n */\nconst blankBlock = {\n id: crypto.randomUUID(),\n type: \"paragraph\",\n data: {\n text: \" \"\n }\n};\n\nconst table = msg.payload.blocks;\n\n/**\n * Function that stores Editor.js-compatible objects\n * into ann array, in order to display\n * information about a submodel.\n * Indents blocks for better visibility.\n * @param {[]} elements SubmodelElements fields of a Submodel\n * @param {number} level the level of indentation of the blocks\n * @param {[]} arr an array to be filled with the blocks\n */\nconst better = (elements, level, arr) => {\n elements.forEach(element => {\n if (element.modelType.name.match(collection)) {\n // if (!element.value.every(e => e.category.toLocaleLowerCase() === \"variable\")) {\n arr.push(headerBlock(element, level));\n better(element.value, level + 1, arr);\n arr.push(blankBlock);\n // }\n } else /** if (element.category.toLocaleLowerCase() !== \"variable\") */ {\n arr.push(valueBlock(element, level));\n }\n });\n};\n// -> uncomment to filter out variables\n\nbetter(msg.submodel.submodelElements, 2, table);\nmsg.payload.blocks = table;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nglobal.set(\"text\", element => {\n if (element.value === \"\") {\n return \"no information\"\n }\n\n const getUnit = element => {\n try {\n const conceptId = element.semanticId.keys.at(0).value;\n const concept = global.get('concepts').get(conceptId);\n return concept.embeddedDataSpecifications.at(0).dataSpecificationContent.unit;\n } catch (error) {\n return \"\";\n }\n }\n\n const richText = element => {\n const type = element.valueType.dataObjectType.name;\n if (type.toLocaleLowerCase() === \"boolean\" && element.value.toLocaleLowerCase() === \"no\") {\n return `Not ${element.idShort.toLocaleLowerCase()}`;\n } else if (type.toLocaleLowerCase() === \"boolean\" && element.value.toLocaleLowerCase() === \"yes\") {\n return `${element.idShort}`;\n } else {\n return element.value\n }\n }\n\n let res = \"\";\n\n switch (element.modelType.name.toLocaleLowerCase()) {\n case \"property\":\n res += richText(element);\n break;\n\n case \"multilanguageproperty\":\n res += `${element.value}`;\n break;\n\n case \"range\":\n res += `from ${element.min} to ${element.max}`;\n break;\n\n case \"file\":\n res += `${element.mimeType.split(\"/\")[1]} file (${element.value})`;\n break;\n\n default:\n res += `idk`;\n break;\n }\n\n return `${res} ${getUnit(element)}`\n});", + "finalize": "", + "libs": [ + { + "var": "crypto", + "module": "crypto" + } + ], + "x": 720, + "y": 160, + "wires": [ + [ + "13dd2a0c6fc2cccd" + ] + ] + }, + { + "id": "dc020b3060cb7c2a", + "type": "ui_button", + "z": "593bb738e7507707", + "name": "", + "group": "f46a108f2a7fd47d", + "order": 2, + "width": "16", + "height": "1", + "passthru": true, + "label": "Clear", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "", + "payloadType": "str", + "topic": "render", + "topicType": "str", + "x": 750, + "y": 200, + "wires": [ + [ + "20662e50b107246b", + "13dd2a0c6fc2cccd" + ] + ] + }, + { + "id": "20662e50b107246b", + "type": "change", + "z": "593bb738e7507707", + "name": "", + "rules": [ + { + "t": "set", + "p": "clicked", + "pt": "flow", + "to": "null", + "tot": "json" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 920, + "y": 200, + "wires": [ + [] + ] + }, + { + "id": "13dd2a0c6fc2cccd", + "type": "ui_editor", + "z": "593bb738e7507707", + "group": "f46a108f2a7fd47d", + "order": 1, + "width": "16", + "height": "10", + "name": "", + "readonly": true, + "monitorUpdate": false, + "outputs": 1, + "x": 890, + "y": 160, + "wires": [ + [] + ] + }, + { + "id": "a81b3b9b740bc85f", + "type": "inject", + "z": "2bc19b2f7b518cb2", + "name": "base stuff", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": "0.1", + "topic": "", + "payload": "[{\"Cutting\":{\"capability\":\"Cutting\",\"submodels\":[]}},{\"Engraving\":{\"capability\":\"Engraving\",\"submodels\":[]}},{\"Painting\":{\"capability\":\"Painting\",\"submodels\":[]}}]", + "payloadType": "json", + "x": 280, + "y": 80, + "wires": [ + [ + "b17f15ef905a8689" + ] + ] + }, + { + "id": "b17f15ef905a8689", + "type": "function", + "z": "2bc19b2f7b518cb2", + "name": "fill dropdown", + "func": "/** @type {Map<string, string>} */\nconst capabilities = global.get('capabilities');\n// capabilities.set('Bending', 'url');\nconst items = msg.payload;\n\n// with the base dropdown config we have :\n// -> add submodels to capabilities already here\n// -> add cap+submodel if it's not\ncapabilities.forEach((value, key) => {\n const idx = items.findIndex(obj => obj[key]?.submodels.length === 0);\n if (idx >= 0) {\n items[idx][key].submodels.push(value);\n } else {\n const obj = {};\n obj[key] = {\n capability: key,\n submodels: [value]\n };\n items.push(obj);\n }\n});\n\nflow.set('registeredCapabilities', items);\nmsg.options = items;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "// Code added here will be run once\n// whenever the node is started.\nflow.set('registeredCapabilities', []);", + "finalize": "", + "libs": [], + "x": 450, + "y": 80, + "wires": [ + [ + "fd5ce40b493557db" + ] + ] + }, + { + "id": "97386f3e3179aa1b", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "selected", + "pt": "flow", + "to": "payload", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 780, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "6656b321ad96cfa2", + "type": "inject", + "z": "2bc19b2f7b518cb2", + "name": "other stuff", + "props": [ + { + "p": "options", + "v": "[\"Wood\",\"Plastic\",\"Metal\", \"Acrylic\"]", + "vt": "json" + }, + { + "p": "topic", + "vt": "str" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": 0.1, + "topic": "", + "x": 270, + "y": 120, + "wires": [ + [ + "3f175d56e52394c5" + ] + ] + }, + { + "id": "aff2658dc26c28e8", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "workablematerial", + "pt": "flow", + "to": "payload", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 650, + "y": 120, + "wires": [ + [] + ] + }, + { + "id": "08fd977d41ee497c", + "type": "function", + "z": "2bc19b2f7b518cb2", + "name": "payload factory", + "func": "const canBeNothing = /registeredCapabilities|colour/g\n\nflow.keys().forEach(key => {\n if (key.match(canBeNothing)) return;\n const value = flow.get(key);\n if (!value || value.length === 0) {\n throw Error();\n }\n msg.payload[key] = value;\n});\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 400, + "y": 420, + "wires": [ + [ + "dc4a14cbb5fee37f", + "7a07356f3fce36ae" + ] + ] + }, + { + "id": "02bdca349f0277af", + "type": "function", + "z": "2bc19b2f7b518cb2", + "name": "store in flow idk", + "func": "const number = Number(msg.payload);\nflow.set(msg.topic, number);\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 640, + "y": 200, + "wires": [ + [] + ] + }, + { + "id": "80725051792ea031", + "type": "inject", + "z": "2bc19b2f7b518cb2", + "name": "", + "props": [ + { + "p": "payload" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": 0.1, + "topic": "", + "payload": "", + "payloadType": "str", + "x": 290, + "y": 200, + "wires": [ + [ + "e3d8b7d3acb18df8", + "373b1ad758e116ca", + "4ec9cd6fe1155089" + ] + ] + }, + { + "id": "dc4a14cbb5fee37f", + "type": "debug", + "z": "2bc19b2f7b518cb2", + "name": "debug 1", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "payload", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 580, + "y": 400, + "wires": [] + }, + { + "id": "612fd715cb7e599d", + "type": "catch", + "z": "2bc19b2f7b518cb2", + "name": "", + "scope": [ + "08fd977d41ee497c" + ], + "uncaught": false, + "x": 230, + "y": 460, + "wires": [ + [ + "322a84b449820b68" + ] + ] + }, + { + "id": "322a84b449820b68", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "fill err", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "fill everything :)", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 370, + "y": 460, + "wires": [ + [ + "cbcd197b7e536209" + ] + ] + }, + { + "id": "cbcd197b7e536209", + "type": "ui_toast", + "z": "2bc19b2f7b518cb2", + "position": "dialog", + "displayTime": "3", + "highlight": "", + "sendall": true, + "outputs": 1, + "ok": "OK", + "cancel": "", + "raw": false, + "className": "popup", + "topic": "", + "name": "", + "x": 550, + "y": 460, + "wires": [ + [] + ] + }, + { + "id": "fd5ce40b493557db", + "type": "ui_dropdown", + "z": "2bc19b2f7b518cb2", + "name": "capabilities", + "label": "", + "tooltip": "", + "place": "Select option*", + "group": "f42eda3bec77a0ec", + "order": 1, + "width": 6, + "height": 1, + "passthru": false, + "multiple": true, + "options": [ + { + "label": "", + "value": "", + "type": "str" + } + ], + "payload": "", + "topic": "topic", + "topicType": "msg", + "className": "", + "x": 610, + "y": 80, + "wires": [ + [ + "97386f3e3179aa1b" + ] + ] + }, + { + "id": "e3d8b7d3acb18df8", + "type": "ui_text_input", + "z": "2bc19b2f7b518cb2", + "name": "length", + "label": "Length (mm)*", + "tooltip": "", + "group": "f42eda3bec77a0ec", + "order": 4, + "width": 4, + "height": 1, + "passthru": true, + "mode": "number", + "delay": "10", + "topic": "length", + "sendOnBlur": true, + "className": "", + "topicType": "str", + "x": 430, + "y": 160, + "wires": [ + [ + "02bdca349f0277af" + ] + ] + }, + { + "id": "373b1ad758e116ca", + "type": "ui_text_input", + "z": "2bc19b2f7b518cb2", + "name": "width", + "label": "Width (mm)*", + "tooltip": "", + "group": "f42eda3bec77a0ec", + "order": 5, + "width": 4, + "height": 1, + "passthru": true, + "mode": "number", + "delay": "10", + "topic": "width", + "sendOnBlur": false, + "className": "", + "topicType": "str", + "x": 430, + "y": 200, + "wires": [ + [ + "02bdca349f0277af" + ] + ] + }, + { + "id": "4ec9cd6fe1155089", + "type": "ui_text_input", + "z": "2bc19b2f7b518cb2", + "name": "thickness", + "label": "Thickness (mm)*", + "tooltip": "", + "group": "f42eda3bec77a0ec", + "order": 6, + "width": 4, + "height": 1, + "passthru": true, + "mode": "number", + "delay": "10", + "topic": "thickness", + "sendOnBlur": false, + "className": "", + "topicType": "str", + "x": 440, + "y": 240, + "wires": [ + [ + "02bdca349f0277af" + ] + ] + }, + { + "id": "3f175d56e52394c5", + "type": "ui_dropdown", + "z": "2bc19b2f7b518cb2", + "name": "materials", + "label": "", + "tooltip": "", + "place": "Select material*", + "group": "f42eda3bec77a0ec", + "order": 2, + "width": 6, + "height": 1, + "passthru": true, + "multiple": false, + "options": [ + { + "label": "", + "value": "", + "type": "str" + } + ], + "payload": "", + "topic": "topic", + "topicType": "msg", + "className": "", + "x": 440, + "y": 120, + "wires": [ + [ + "aff2658dc26c28e8" + ] + ] + }, + { + "id": "be52a6ec742aae62", + "type": "ui_button", + "z": "2bc19b2f7b518cb2", + "name": "", + "group": "f42eda3bec77a0ec", + "order": 7, + "width": 0, + "height": 0, + "passthru": false, + "label": "submit", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "{}", + "payloadType": "json", + "topic": "topic", + "topicType": "msg", + "x": 230, + "y": 420, + "wires": [ + [ + "08fd977d41ee497c" + ] + ] + }, + { + "id": "7a07356f3fce36ae", + "type": "link out", + "z": "2bc19b2f7b518cb2", + "name": "link out 1", + "mode": "link", + "links": [ + "2a48607dce78b96a", + "cc23bdf032d1e4c5" + ], + "x": 835, + "y": 420, + "wires": [] + }, + { + "id": "cc936221d2a30b89", + "type": "ui_list", + "z": "2bc19b2f7b518cb2", + "group": "1bea595dc9db51b7", + "name": "", + "order": 1, + "width": 0, + "height": 0, + "lineType": "two", + "actionType": "click", + "allowHTML": false, + "outputs": 1, + "topic": "", + "x": 530, + "y": 500, + "wires": [ + [ + "8b014741fffd6f93", + "15dd31c7abeb942a" + ] + ] + }, + { + "id": "6b8e2ad1c05cb496", + "type": "inject", + "z": "2bc19b2f7b518cb2", + "name": "", + "props": [ + { + "p": "payload" + } + ], + "repeat": "5", + "crontab": "", + "once": true, + "onceDelay": "0.5", + "topic": "", + "payload": "[{\"title\":\"E12 Room Plaque\",\"description\":\"Piece of wood engraved with \\\"E12\\\"\",\"workpiece\":{\"length\":120,\"width\":120,\"thickness\":10,\"selected\":[{\"capability\":\"Engraving\",\"submodels\":[]}],\"workablematerial\":\"Wood\"}},{\"title\":\"Protective piece of plastic\",\"description\":\"Plastic piece that protects from lasers\",\"workpiece\":{\"length\":200,\"width\":80,\"thickness\":5,\"selected\":[{\"capability\":\"Cutting\",\"submodels\":[]},{\"capability\":\"Engraving\",\"submodels\":[]}],\"workablematerial\":\"Acrylic\"}},{\"title\":\"Painted piece of metal\",\"description\":\"Piece of metal with red paint after some engraving\",\"workpiece\":{\"length\":12,\"width\":20,\"thickness\":60,\"colour\":\"Red\",\"selected\":[{\"capability\":\"Engraving\",\"submodels\":[]},{\"capability\":\"Painting\",\"submodels\":[]}],\"workablematerial\":\"Metal\"}}]", + "payloadType": "json", + "x": 230, + "y": 500, + "wires": [ + [ + "95da606fa064c3bb" + ] + ] + }, + { + "id": "95da606fa064c3bb", + "type": "function", + "z": "2bc19b2f7b518cb2", + "name": "fill the list", + "func": "const stuff = flow.get('registeredCapabilities');\n\nmsg.payload.forEach(obj => {\n obj.workpiece.selected.forEach(hm =>\n hm.submodels = stuff.find(\n cap => cap[hm.capability]\n )[hm.capability].submodels\n )\n});\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 500, + "wires": [ + [ + "cc936221d2a30b89" + ] + ] + }, + { + "id": "8b014741fffd6f93", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "payload.workpiece", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 700, + "y": 500, + "wires": [ + [ + "7a07356f3fce36ae" + ] + ] + }, + { + "id": "811b39730262f4c3", + "type": "inject", + "z": "2bc19b2f7b518cb2", + "name": "colour list!", + "props": [ + { + "p": "options", + "v": "[\"Red\",\"Yellow\",\"Turquoise\",\"No colour\"]", + "vt": "json" + } + ], + "repeat": "", + "crontab": "", + "once": true, + "onceDelay": 0.1, + "topic": "", + "x": 290, + "y": 280, + "wires": [ + [ + "e73e2445b47c515c" + ] + ] + }, + { + "id": "e73e2445b47c515c", + "type": "ui_dropdown", + "z": "2bc19b2f7b518cb2", + "name": "colours", + "label": "", + "tooltip": "", + "place": "Select a colour", + "group": "f42eda3bec77a0ec", + "order": 3, + "width": 12, + "height": 1, + "passthru": false, + "multiple": false, + "options": [ + { + "label": "", + "value": "", + "type": "str" + } + ], + "payload": "", + "topic": "topic", + "topicType": "msg", + "className": "", + "x": 440, + "y": 280, + "wires": [ + [ + "3c78dd5bac3ccc2b" + ] + ] + }, + { + "id": "3c78dd5bac3ccc2b", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "colour", + "pt": "flow", + "to": "payload", + "tot": "msg" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 620, + "y": 280, + "wires": [ + [] + ] + }, + { + "id": "b8640315f79bbc2c", + "type": "link in", + "z": "2bc19b2f7b518cb2", + "name": "link in 8", + "links": [ + "f92afa1f37cc46cb" + ], + "x": 145, + "y": 40, + "wires": [ + [ + "3429169128e30ca3" + ] + ] + }, + { + "id": "3429169128e30ca3", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "[{\"Cutting\":{\"capability\":\"Cutting\",\"submodels\":[]}},{\"Engraving\":{\"capability\":\"Engraving\",\"submodels\":[]}},{\"Painting\":{\"capability\":\"Painting\",\"submodels\":[]}}]", + "tot": "json" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 260, + "y": 40, + "wires": [ + [ + "b17f15ef905a8689" + ] + ] + }, + { + "id": "97777a9dd42807be", + "type": "debug", + "z": "2bc19b2f7b518cb2", + "name": "debug 5", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 680, + "y": 540, + "wires": [] + }, + { + "id": "15dd31c7abeb942a", + "type": "debug", + "z": "2bc19b2f7b518cb2", + "name": "debug 5", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "false", + "statusVal": "", + "statusType": "auto", + "x": 680, + "y": 540, + "wires": [] + }, + { + "id": "58be501a101c8482", + "type": "link in", + "z": "2bc19b2f7b518cb2", + "name": "link in 9", + "links": [ + "f92afa1f37cc46cb" + ], + "x": 85, + "y": 540, + "wires": [ + [ + "027f7b9e6c21d994" + ] + ] + }, + { + "id": "027f7b9e6c21d994", + "type": "change", + "z": "2bc19b2f7b518cb2", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "[{\"title\":\"E12 Room Plaque\",\"description\":\"Piece of wood engraved with \\\"E12\\\"\",\"workpiece\":{\"length\":120,\"width\":120,\"thickness\":10,\"selected\":[{\"capability\":\"Engraving\",\"submodels\":[]}],\"workablematerial\":\"Wood\"}},{\"title\":\"Protective piece of plastic\",\"description\":\"Plastic piece that protects from lasers\",\"workpiece\":{\"length\":200,\"width\":80,\"thickness\":5,\"selected\":[{\"capability\":\"Cutting\",\"submodels\":[]},{\"capability\":\"Engraving\",\"submodels\":[]}],\"workablematerial\":\"Acrylic\"}},{\"title\":\"Painted piece of metal\",\"description\":\"Piece of metal with red paint after some engraving\",\"workpiece\":{\"length\":12,\"width\":20,\"thickness\":60,\"colour\":\"Red\",\"selected\":[{\"capability\":\"Engraving\",\"submodels\":[]},{\"capability\":\"Painting\",\"submodels\":[]}],\"workablematerial\":\"Metal\"}}]", + "tot": "json" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 200, + "y": 540, + "wires": [ + [ + "95da606fa064c3bb" + ] + ] + }, + { + "id": "2a48607dce78b96a", + "type": "link in", + "z": "f96e455ab747ba2b", + "name": "link in 5", + "links": [ + "7a07356f3fce36ae" + ], + "x": 55, + "y": 80, + "wires": [ + [ + "61434116aba62c5e" + ] + ] + }, + { + "id": "61434116aba62c5e", + "type": "function", + "z": "f96e455ab747ba2b", + "name": "available filter", + "func": "const available = [], unavailable = [];\n\nmsg.payload.selected.forEach(wantedCapacity => {\n if (wantedCapacity.submodels.length > 0) {\n available.push(wantedCapacity);\n } else {\n unavailable.push(wantedCapacity);\n }\n});\n\ndelete msg.payload.selected;\nmsg.available = available;\nmsg.unavailable = unavailable;\nmsg.baseInput = msg.payload;\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 180, + "y": 80, + "wires": [ + [ + "8e4eaf8f3761dd09" + ] + ] + }, + { + "id": "b8399d7f9cf6bf2c", + "type": "function", + "z": "f96e455ab747ba2b", + "name": "find an element", + "func": "const coll = /SubmodelElement(Collection|List)/g;\nconst submodels = global.get('submodels');\n\n/**\n * Allows to find a specific element with provided\n * id within a SubmodelElement.\n */\nconst findElement = (elements, id) => {\n for(const element of elements) {\n if (element.idShort.toLocaleLowerCase() === id.toLocaleLowerCase()) {\n return element;\n }\n if (element.modelType.name.match(coll)) {\n const find = findElement(element.value, id);\n if (find) return find;\n }\n }\n}\n\n// keep originally entered value for later comparision\nconst enteredValue = msg.payload;\nmsg.payload = [];\n\n// look through available capabilities to map them\nmsg.available.forEach(available => {\n const mapped = available.submodels.map(submodel => {\n const find = findElement(\n submodels.get(submodel).submodelElements,\n msg.key\n );\n if (find) find.enteredValue = enteredValue;\n return find;\n });\n if (mapped.every(obj => obj)) {\n available.submodels = mapped;\n msg.payload.push(available);\n }\n});\n\nnode.warn(msg);\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 400, + "y": 140, + "wires": [ + [ + "c1ef5b072db03648" + ] + ] + }, + { + "id": "8e4eaf8f3761dd09", + "type": "split", + "z": "f96e455ab747ba2b", + "name": "", + "splt": "\\n", + "spltType": "str", + "arraySplt": 1, + "arraySpltType": "len", + "stream": false, + "addname": "key", + "x": 330, + "y": 80, + "wires": [ + [ + "b8399d7f9cf6bf2c" + ] + ] + }, + { + "id": "cb2883b1acc66248", + "type": "join", + "z": "f96e455ab747ba2b", + "name": "", + "mode": "auto", + "build": "object", + "property": "payload", + "propertyType": "msg", + "key": "topic", + "joiner": "\\n", + "joinerType": "str", + "accumulate": true, + "timeout": "", + "count": "", + "reduceRight": false, + "reduceExp": "", + "reduceInit": "", + "reduceInitType": "", + "reduceFixup": "", + "x": 570, + "y": 200, + "wires": [ + [ + "cbb098a2c48230a6" + ] + ] + }, + { + "id": "c1ef5b072db03648", + "type": "function", + "z": "f96e455ab747ba2b", + "name": "check requirements", + "func": "/**\n * Check whether the value entered by the client is\n * acceptable depending on the model type of the submodel\n */\nconst checkValue = submodel => {\n const type = submodel.modelType?.name;\n const enteredValue = submodel.enteredValue;\n\n if (type.match(/SubmodelElement(Collection|List)/g)) {\n return Boolean(\n submodel.value.find(element =>\n element.value.toLocaleLowerCase() === enteredValue.toLocaleLowerCase()\n )\n )\n }\n if (type.toLocaleLowerCase() === 'range') {\n return enteredValue > Number(submodel.min) \n && enteredValue < Number(submodel.max);\n }\n return submodel.value === enteredValue;\n}\n\n// among all the submodels that match the demand,\n// at least one must be able to process it (i.e. all\n// technical requirements are met)\nmsg.payload.forEach(obj => {\n const mapped = obj.submodels.map(submodel => \n checkValue(submodel)\n );\n obj.possible = mapped.includes(true);\n});\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 590, + "y": 140, + "wires": [ + [ + "cb2883b1acc66248" + ] + ] + }, + { + "id": "cbb098a2c48230a6", + "type": "function", + "z": "f96e455ab747ba2b", + "name": "final check", + "func": "// product can be ordered if every entries have at least\n// one capability associated to it, and if for every\n// capability, it's possible to do it (least english sentence ever made)\nlet doable = \n Object.entries(msg.payload).every(([_key, value]) => {\n if (!value || !value.length) return false;\n else return value.every(obj => obj.possible);\n })\n\n// if the unavailable array of submodel has at least\n// one element, then it's no doable, even if none of the\n// characteritics of the product require it/them\nmsg.doable = doable && msg.unavailable.length === 0;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 710, + "y": 200, + "wires": [ + [ + "d925daf28fc2f63d", + "63da2e1e2126c8e2" + ] + ] + }, + { + "id": "d925daf28fc2f63d", + "type": "debug", + "z": "f96e455ab747ba2b", + "name": "debug 2", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 880, + "y": 240, + "wires": [] + }, + { + "id": "63da2e1e2126c8e2", + "type": "link out", + "z": "f96e455ab747ba2b", + "name": "link out 2", + "mode": "link", + "links": [ + "0430c5df11230321" + ], + "x": 835, + "y": 200, + "wires": [] + }, + { + "id": "0430c5df11230321", + "type": "link in", + "z": "1320790a4546b4fd", + "name": "link in 6", + "links": [ + "63da2e1e2126c8e2" + ], + "x": 55, + "y": 80, + "wires": [ + [ + "d0ed24a50df785b3" + ] + ] + }, + { + "id": "d0ed24a50df785b3", + "type": "switch", + "z": "1320790a4546b4fd", + "name": "", + "property": "doable", + "propertyType": "msg", + "rules": [ + { + "t": "true" + }, + { + "t": "false" + } + ], + "checkall": "false", + "repair": false, + "outputs": 2, + "x": 160, + "y": 80, + "wires": [ + [ + "edd695b68f3090a3" + ], + [ + "8b057070221eb604" + ] + ] + }, + { + "id": "edd695b68f3090a3", + "type": "change", + "z": "1320790a4546b4fd", + "name": "", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "[msg.baseInput]", + "tot": "jsonata" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 330, + "y": 80, + "wires": [ + [ + "9535db9724297388" + ] + ] + }, + { + "id": "8b057070221eb604", + "type": "function", + "z": "1320790a4546b4fd", + "name": "unavailable list & filter blocking", + "func": "const blocking = {};\n\n// delete impossible entries and list blocking ones\nObject.entries(msg.payload).forEach(([key, value]) => {\n if (value.length === 0) {\n delete msg.payload[key];\n delete msg.baseInput[key];\n } \n else if (value.some(obj => !obj.possible))\n blocking[key] = value;\n \n});\n\n// store blocking characteritics to find a solution\nmsg.blocking = blocking;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 210, + "y": 140, + "wires": [ + [ + "5624a09fb7ba3735" + ] + ] + }, + { + "id": "5624a09fb7ba3735", + "type": "function", + "z": "1320790a4546b4fd", + "name": "find a solution!", + "func": "/**\n * Return an array of possible value(s) for a given\n * submodel, based on the model type & entered value.\n */\nconst findAlt = (submodel, value) => {\n const type = submodel.modelType?.name;\n const enteredValue = submodel.enteredValue;\n\n if (type.match(/SubmodelElement(Collection|List)/g)) {\n return submodel.value.map(element => element.value)\n }\n if (type.toLocaleLowerCase() === 'range') {\n const max = Number(submodel.max), min = Number(submodel.min);\n return enteredValue > max ? \n [Math.min(value, max) || max] : \n [Math.max(value, min) || min];\n }\n return submodel.value;\n}\n\n// put every value of base input inside an array\nObject.entries(msg.baseInput)\n .forEach(([key, val]) => msg.baseInput[key] = [val]);\n\n// if there's just stuff that was unavailable,\n// can send changed base input!\nif (!Object.keys(msg.blocking).length) {\n return msg;\n}\n\n// otherwise, if technical properties were not\n// met, find alternatives for each blocking value\nObject.entries(msg.blocking).forEach(([key, val]) => {\n // keep track of previously found value in order\n // to make sure a correct value in not overwritten\n // by a bad one\n let stuff = [];\n val.forEach(cap => {\n cap.submodels.forEach(sub => {\n const find = findAlt(sub, stuff.at(0));\n stuff = find;\n msg.baseInput[key] = find;\n });\n });\n});\n\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 380, + "y": 200, + "wires": [ + [ + "d03ec6057ef1ad69", + "e09f506e27da74e5" + ] + ] + }, + { + "id": "d03ec6057ef1ad69", + "type": "function", + "z": "1320790a4546b4fd", + "name": "gathering", + "func": "const propositions = [];\n/**\n * Function that takes multiple arrays and returns the\n * cartesian products of all of them\n */\nconst cartesian = (...a) => a.reduce((a, b) => a.flatMap(d => b.map(e => [d, e].flat())));\n\n// cartesian product of all possible values\n// to create as much proposals as possible!\ncartesian(...Object.values(msg.baseInput)).forEach(arr => {\n node.warn(arr);\n const copy = {...msg.baseInput};\n let i = 0;\n Object.keys(copy).forEach(key => copy[key] = arr.at(i++));\n propositions.push(copy);\n})\n\nmsg.payload = propositions;\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 480, + "y": 140, + "wires": [ + [ + "9535db9724297388" + ] + ] + }, + { + "id": "aec0270a6b3a57e9", + "type": "debug", + "z": "1320790a4546b4fd", + "name": "debug 3", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 860, + "y": 120, + "wires": [] + }, + { + "id": "8f998ce344f595e0", + "type": "ui_list", + "z": "1320790a4546b4fd", + "group": "859d724c411b6b56", + "name": "proposition list", + "order": 2, + "width": "0", + "height": "0", + "lineType": "three", + "actionType": "click", + "allowHTML": false, + "outputs": 1, + "topic": "", + "x": 400, + "y": 420, + "wires": [ + [ + "86cd9cacec78bd7e" + ] + ] + }, + { + "id": "e09f506e27da74e5", + "type": "debug", + "z": "1320790a4546b4fd", + "name": "debug 4", + "active": false, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 560, + "y": 200, + "wires": [] + }, + { + "id": "9535db9724297388", + "type": "function", + "z": "1320790a4546b4fd", + "name": "proposition list", + "func": "// propositions into a list :)\nmsg.payload = msg.payload.map(proposition => ({\n title: `${msg.available.map(av => av.capability).join(', ')}`,\n description: `${JSON.stringify(proposition)}`\n}));\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "libs": [], + "x": 680, + "y": 80, + "wires": [ + [ + "aec0270a6b3a57e9", + "b14a72981ce166b8" + ] + ] + }, + { + "id": "b506aa1a68dc8cee", + "type": "link in", + "z": "1320790a4546b4fd", + "name": "link in 7", + "links": [ + "b14a72981ce166b8" + ], + "x": 225, + "y": 360, + "wires": [ + [ + "ef4dd18cabed24db", + "e14a5947be3648b4", + "8f998ce344f595e0" + ] + ] + }, + { + "id": "b14a72981ce166b8", + "type": "link out", + "z": "1320790a4546b4fd", + "name": "link out 3", + "mode": "link", + "links": [ + "b506aa1a68dc8cee" + ], + "x": 815, + "y": 80, + "wires": [] + }, + { + "id": "ef4dd18cabed24db", + "type": "switch", + "z": "1320790a4546b4fd", + "name": "", + "property": "doable", + "propertyType": "msg", + "rules": [ + { + "t": "true" + }, + { + "t": "false" + } + ], + "checkall": "true", + "repair": false, + "outputs": 2, + "x": 350, + "y": 320, + "wires": [ + [ + "0b0821ecc3f62621" + ], + [ + "d9c4214abbf157df" + ] + ] + }, + { + "id": "b09b0367e9a0a29a", + "type": "ui_ui_control", + "z": "1320790a4546b4fd", + "name": "", + "events": "all", + "x": 740, + "y": 460, + "wires": [ + [] + ] + }, + { + "id": "e14a5947be3648b4", + "type": "change", + "z": "1320790a4546b4fd", + "name": "proposition", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "Proposition", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 390, + "y": 460, + "wires": [ + [ + "b09b0367e9a0a29a" + ] + ] + }, + { + "id": "af92141334e5796b", + "type": "ui_text", + "z": "1320790a4546b4fd", + "group": "859d724c411b6b56", + "order": 1, + "width": 0, + "height": 0, + "name": "", + "label": "", + "format": "{{msg.payload}}", + "layout": "col-center", + "className": "", + "x": 650, + "y": 340, + "wires": [] + }, + { + "id": "7b6db2d58d8d3cf7", + "type": "ui_button", + "z": "1320790a4546b4fd", + "name": "back", + "group": "859d724c411b6b56", + "order": 3, + "width": 0, + "height": 0, + "passthru": false, + "label": "Back to order", + "tooltip": "", + "color": "", + "bgcolor": "", + "className": "", + "icon": "", + "payload": "Workpiece order", + "payloadType": "str", + "topic": "topic", + "topicType": "msg", + "x": 590, + "y": 500, + "wires": [ + [ + "b09b0367e9a0a29a" + ] + ] + }, + { + "id": "d9c4214abbf157df", + "type": "change", + "z": "1320790a4546b4fd", + "name": "alt", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "The product that you requested is not available, but here are some alternatives that are similar to what you asked", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 510, + "y": 340, + "wires": [ + [ + "af92141334e5796b" + ] + ] + }, + { + "id": "0b0821ecc3f62621", + "type": "change", + "z": "1320790a4546b4fd", + "name": "good!", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "The product that you requested is available, please review your order", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 510, + "y": 300, + "wires": [ + [ + "af92141334e5796b" + ] + ] + }, + { + "id": "02eedb0262828d82", + "type": "catch", + "z": "1320790a4546b4fd", + "name": "", + "scope": [ + "d03ec6057ef1ad69" + ], + "uncaught": false, + "x": 70, + "y": 420, + "wires": [ + [ + "90f1b50212148ae0" + ] + ] + }, + { + "id": "90f1b50212148ae0", + "type": "change", + "z": "1320790a4546b4fd", + "name": "err", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "[]", + "tot": "json" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 190, + "y": 420, + "wires": [ + [ + "8f998ce344f595e0", + "8f5d8ce91dd46f18", + "e14a5947be3648b4" + ] + ] + }, + { + "id": "0f6401f582448d0c", + "type": "change", + "z": "1320790a4546b4fd", + "name": "not good", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "The product that you requested is not available as of now. Please order something else!", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 500, + "y": 380, + "wires": [ + [ + "af92141334e5796b" + ] + ] + }, + { + "id": "86cd9cacec78bd7e", + "type": "change", + "z": "1320790a4546b4fd", + "name": "confirmation", + "rules": [ + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "Confirmation", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 570, + "y": 420, + "wires": [ + [ + "b09b0367e9a0a29a", + "3eedc2dd60786ae3" + ] + ] + }, + { + "id": "3eedc2dd60786ae3", + "type": "ui_text", + "z": "1320790a4546b4fd", + "group": "25611d1488853208", + "order": 0, + "width": 0, + "height": 0, + "name": "", + "label": "", + "format": "Your product has been ordered. Thank you!", + "layout": "col-center", + "className": "", + "x": 730, + "y": 420, + "wires": [] + } +] \ No newline at end of file diff --git a/flow_cred.json b/flow_cred.json index 9e26dfeeb6e641a33dae4961196235bdb965b21b..e466cd43f78e6260fbd10ffdaa72d08f0b2d4cfe 100644 --- a/flow_cred.json +++ b/flow_cred.json @@ -1 +1,8 @@ -{} \ No newline at end of file +{ + "2282c53e6f2d220c": { + "user": "industrie", + "password": "industrie" + }, + "df9ec70032434b59": {}, + "5850cb65c596909b": {} +} \ No newline at end of file diff --git a/package.json b/package.json index 0e68ad406b5d5c21634aec88b0dafa96cbc3a2db..890741c4d638bf41a8f8e43ae8df87d1cccb4606 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,11 @@ "name": "DF-Dashboard", "description": "A dashboard for the digital factory of the Hochschule Emden/Leer", "version": "0.0.1", - "dependencies": {}, + "dependencies": { + "node-red-dashboard": "3.4.0", + "node-red-node-ui-list": "0.3.6", + "node-red-contrib-ui-editor": "0.0.1" + }, "node-red": { "settings": { "flowFile": "flow.json",