ARTOF - soil pressure sampler

Description

The Node-red flow registers the data from the soil pressure sampler to an InfluxDB instance. The flow uses the Redis API of the Agricultural Robot Taskmap Operation Framework (ARTOF) to communicate with a robot platform and the S7 communication (Siemens) to communicate with the Programmable Logic Controller (PLC) of the soil pressure sampler.

This flow was used as materials in the scientific publication:

<will follow soon>
  1. The Redis flow reads the platform's relevant information.
  2. The Influx flow logs this information periodically to an InfluxDB instance.
  3. The Penetrometer flow monitors if the soil pressure sampler has executed a point measurement; it reads and georeferences this information and logs it to an InfluxDB instance.

Third-party flows

The deploy the flows there is required:

  1. The installation of the nodes
  1. The deployment on a computer with ARTOF installed, having the /var/lib/ilvo/settings.json file installed, which includes the parameters for the ARTOF Redis API connections.
  2. The configuration of the Influxdb instance in the Influx and Penetrometer flow.
  3. An S7 communication interface on the PLC on the soil pressure sampler.
[{"id":"be4e0bffe7b14ed5","type":"tab","label":"Redis","disabled":false,"info":"","env":[]},{"id":"37c61670c96fdccb","type":"tab","label":"Influx","disabled":false,"info":"","env":[]},{"id":"bf6bf5e0fef34d67","type":"tab","label":"Penetrometer","disabled":false,"info":"","env":[]},{"id":"4ca778f604106427","type":"subflow","name":"Get redis variables","info":"","category":"","in":[{"x":340,"y":140,"wires":[{"id":"ab9848b89a4e4305"}]}],"out":[{"x":1460,"y":440,"wires":[{"id":"599857a5b59daf93","port":0}]},{"x":640,"y":100,"wires":[{"id":"602d816bc8353af4","port":0},{"id":"d7d14fa3b253f2cd","port":0}]}],"env":[],"meta":{},"color":"#DDAA99"},{"id":"25725a04e8cdc424","type":"postgreSQLConfig","name":"[email protected]:5432/tv115_access_control","host":"dbtv115-2.ilvo.be","hostFieldType":"str","port":"5432","portFieldType":"num","database":"tv115_access_control","databaseFieldType":"str","ssl":"true","sslFieldType":"bool","applicationName":"","applicationNameType":"str","max":"10","maxFieldType":"num","idle":"1000","idleFieldType":"num","connectionTimeout":"10000","connectionTimeoutFieldType":"num","user":"tv115_access_control_user","userFieldType":"str","password":"u3qPU61Ex8(Y","passwordFieldType":"str"},{"id":"63b49a5eaf7b2bc8","type":"s7comm","ip":"192.168.1.100","port":"102","rack":"0","slot":"1","payload":[{"S7_Type":"DB","S7_DBnum":"17","S7_Datatype":"B","S7_Offset":"0","S7_BitOffset":"1","S7_Quantity":"1620","S7_Name":"Data"}]},{"id":"ed4a5192bb4c3284","type":"s7comm","ip":"192.168.1.100","port":"102","rack":"0","slot":"1","payload":[{"S7_Type":"DB","S7_DBnum":"16","S7_Datatype":"B","S7_Offset":"8192","S7_BitOffset":"0","S7_Quantity":"2","S7_Name":""}]},{"id":"f5a3b8ce7e48b6b1","type":"s7comm","ip":"192.168.1.1","port":"102","rack":"0","slot":"1","payload":[{"S7_Type":"DB","S7_DBnum":"5","S7_Datatype":"X","S7_Offset":"30","S7_BitOffset":"1","S7_Quantity":"1","S7_Name":"fb"},{"S7_Type":"DB","S7_DBnum":"5","S7_Datatype":"X","S7_Offset":"30","S7_BitOffset":"2","S7_Quantity":"1","S7_Name":"rb"}]},{"id":"eee51326e00bb95f","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"https://dbtv115.ilvo.be:8086","name":"influxdb","usetls":false,"tls":"","influxdbVersion":"2.0","url":"","timeout":"","rejectUnauthorized":true},{"id":"4e7d71f7ea3dbdc4","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"database","name":"Test_bucket","usetls":false,"tls":"","influxdbVersion":"2.0","url":"https://influx.ilvo.be","timeout":"10","rejectUnauthorized":true},{"id":"73bf5d6106b6cde3","type":"redis-config","name":"Local","options":"{}","cluster":false,"optionsType":"json"},{"id":"b98938dabf220a71","type":"file in","z":"4ca778f604106427","name":"config.json","filename":"/var/lib/ilvo/config.json","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":490,"y":300,"wires":[["602d816bc8353af4"]]},{"id":"602d816bc8353af4","type":"json","z":"4ca778f604106427","name":"","property":"payload","action":"","pretty":false,"x":630,"y":300,"wires":[["a4b2658af34b4893"]]},{"id":"ab9848b89a4e4305","type":"file in","z":"4ca778f604106427","name":"types.json","filename":"/var/lib/ilvo/types.json","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":480,"y":220,"wires":[["d7d14fa3b253f2cd"]]},{"id":"a4b2658af34b4893","type":"split","z":"4ca778f604106427","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":770,"y":300,"wires":[["0c3f136e3a011b77"]]},{"id":"0c3f136e3a011b77","type":"switch","z":"4ca778f604106427","name":"","property":"parts.key","propertyType":"msg","rules":[{"t":"eq","v":"variables","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":910,"y":300,"wires":[["7ff55cdd388e2f55"]]},{"id":"ef3d8abab9ddb2b3","type":"function","z":"4ca778f604106427","name":"Add key to topic","func":"msg.topic = msg.topic + msg.parts.key + \".\"\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":400,"wires":[["1c6f5ab545a491ac"]]},{"id":"7ff55cdd388e2f55","type":"split","z":"4ca778f604106427","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":490,"y":400,"wires":[["ef3d8abab9ddb2b3"]]},{"id":"1c6f5ab545a491ac","type":"switch","z":"4ca778f604106427","name":"","property":"payload","propertyType":"msg","rules":[{"t":"istype","v":"object","vt":"object"},{"t":"istype","v":"string","vt":"string"}],"checkall":"true","repair":false,"outputs":2,"x":570,"y":500,"wires":[["7ff55cdd388e2f55"],["ca1b58fa26bddb5c"]]},{"id":"2c4347a1529e236b","type":"change","z":"4ca778f604106427","name":"","rules":[{"t":"set","p":"types","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":800,"y":220,"wires":[["b98938dabf220a71"]]},{"id":"ca1b58fa26bddb5c","type":"function","z":"4ca778f604106427","name":"Get Types","func":"var types = flow.get(\"types\");\nmsg.payload = types[msg.payload];\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":750,"y":500,"wires":[["7f1e65dd828d9dee"]]},{"id":"d7d14fa3b253f2cd","type":"json","z":"4ca778f604106427","name":"","property":"payload","action":"","pretty":false,"x":630,"y":220,"wires":[["2c4347a1529e236b"]]},{"id":"7f1e65dd828d9dee","type":"split","z":"4ca778f604106427","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":910,"y":500,"wires":[["384cac41345cfb3b"]]},{"id":"599857a5b59daf93","type":"join","z":"4ca778f604106427","name":"","mode":"custom","build":"array","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":false,"timeout":"1","count":"","reduceRight":false,"reduceExp":"","reduceInit":"","reduceInitType":"num","reduceFixup":"","x":1250,"y":500,"wires":[[]]},{"id":"384cac41345cfb3b","type":"function","z":"4ca778f604106427","name":"Get Types","func":"var types = flow.get(\"types\");\nvar nested = types[msg.payload];\nvar nestedVar = msg.payload; \nvar topic = msg.topic;\nvar parts = msg.parts.key;\ndelete msg.parts;\n\n\n\nif (nested){\n    for (const [key, value] of Object.entries(nested)) {\n        msg.payload = topic + parts + \".\" + key\n        node.send(msg);\n    }\n}else{\n    var key = nestedVar\n    if (key.includes(\"array\")) {\n        \n        const splitStr = key.split(\" of \")[0];\n        let arraySize = parseInt(splitStr.slice(splitStr.indexOf(\"[\") + 1, splitStr.indexOf(\"]\")));\n                        for (let i = 0; i < arraySize; i++) {\n                            let variableName = topic + parts + \".\" + i.toString();\n                            msg.payload = variableName\n                            \n                            node.send(msg);\n        }\n       \n    }else{\n        msg.payload = topic + parts\n        return msg\n    }\n\n   \n   \n}\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1070,"y":500,"wires":[["599857a5b59daf93"]]},{"id":"911b7020c2b54112","type":"inject","z":"be4e0bffe7b14ed5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"10","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":230,"y":100,"wires":[["5b7c13b2c742af04","b51844e730488ab5"]]},{"id":"5b7c13b2c742af04","type":"subflow:4ca778f604106427","z":"be4e0bffe7b14ed5","name":"","x":490,"y":100,"wires":[["bca4e6b2ceb7cf53"],[]]},{"id":"bca4e6b2ceb7cf53","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"redis_variables","pt":"flow","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":850,"y":100,"wires":[[]]},{"id":"a7663ea12c9abe1f","type":"function","z":"be4e0bffe7b14ed5","name":"Add value to key","func":"var array = msg.payload\nvar variables = msg.variables;\nmsg.payload = {};\nfor (let index = 0; index < array.length; index++) {\n    const element = array[index];\n    \n    msg.payload[variables[index]] = array[index]\n    \n}\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":610,"y":360,"wires":[["7bbf840480ad7173"]]},{"id":"7fb9269de8d4462e","type":"inject","z":"be4e0bffe7b14ed5","name":"","props":[{"p":"topic","vt":"str"},{"p":"payload"}],"repeat":"0.05","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"[\"robot.ref.state\"]","payloadType":"json","x":190,"y":300,"wires":[["7a1185c9b8c5dc07"]]},{"id":"c32bba259c33d2c3","type":"json","z":"be4e0bffe7b14ed5","name":"","property":"payload","action":"","pretty":false,"x":570,"y":300,"wires":[["38acae46d9efed74"]]},{"id":"e440ad720d596756","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"robot_ref_state","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"robot_ref_state","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1040,"y":300,"wires":[["a91d2446dc38bc9d"]]},{"id":"bfbbc3701da95ee7","type":"json","z":"be4e0bffe7b14ed5","name":"","property":"payload","action":"","pretty":false,"x":590,"y":500,"wires":[["372a4b846b8ee8da"]]},{"id":"0fe7483209d0135b","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"impl_states","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":500,"wires":[[]]},{"id":"7bbf840480ad7173","type":"function","z":"be4e0bffe7b14ed5","name":"Turn to Object","func":"var object = msg.payload;\n\nvar result = {};\nvar currentJson = {}\n\nif(object[\"pc.execution.notification\"] == \"false\"){\n  delete object[\"pc.execution.notification\"];\n}\nfor (const [key, value] of Object.entries(object)) {\n    \n    var levels = key.split(\".\");\n    var current = \"\"\n    var next= {}\n    for (let i = levels.length; i >= 0; i--) {\n\n      var level = levels[i];\n      \n      if (i == levels.length){\n        if (isNaN(value)){\n         \n          if (value == \"false\"){\n            current = false;\n          } else if (value == \"true\"){\n            current = true;\n\n          }else {\n            current = `\"${value.toString()}\"`;\n\n          }\n        }else{\n          \n          current = value;\n        }\n        \n        \n      } else{\n\n        current = `{\"${level}\":${current}}`\n\n      } \n        \n    }\n\n  currentJson = JSON.parse(current)\n  _.merge(result, currentJson)\n\n    \n\n}\n\n\nmsg.payload = result ;\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"_","module":"lodash"}],"x":820,"y":360,"wires":[["94a2da4ed7b4b3b8","a756dc5bcb8d4ae4"]]},{"id":"2673f835db5f229a","type":"json","z":"be4e0bffe7b14ed5","name":"","property":"payload","action":"","pretty":false,"x":630,"y":160,"wires":[["7183510cb9f2c36c"]]},{"id":"b51844e730488ab5","type":"file in","z":"be4e0bffe7b14ed5","name":"settings.json","filename":"/var/lib/ilvo/settings.json","filenameType":"str","format":"utf8","chunk":false,"sendError":false,"encoding":"none","allProps":false,"x":470,"y":160,"wires":[["2673f835db5f229a"]]},{"id":"7183510cb9f2c36c","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"framework_settings","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":870,"y":160,"wires":[[]]},{"id":"38acae46d9efed74","type":"function","z":"be4e0bffe7b14ed5","name":"xy to lonlat","func":"/*var S2 = s2Geometry.S2;\nvar level = 28;*/\n\nconst framework_settings = global.get(\"framework_settings\");\n\nvar utmZone = framework_settings.gps.utm_zone;\nvar utmEast = msg.payload.T[0]; // Example UTM easting\nvar utmNorth = msg.payload.T[1]; // Example UTM northing\n\n//Calculate distance delta\nvar prevUTM = flow.get(\"prevUTM\") || [0,0]\nvar curUTM = msg.payload.T\nvar distUTM = Math.sqrt((prevUTM[0]-curUTM[0])**2 + (prevUTM[1]-curUTM[1])**2)\n\n//Calculate angle delta.\nvar prevAngle = flow.get(\"prevAngle\") || 0\nvar curAngle = msg.payload.R[2];\nvar angleDelta = Math.abs(curAngle - prevAngle);\n\n// Define the projection for UTM 31N\nproj4.defs(\"EPSG:32631\", \"+proj=utm +zone=31 +ellps=WGS84 +datum=WGS84 +units=m +no_defs\");\n\n// Transform the UTM coordinates to latitude and longitude\nvar lonlat = proj4(\"EPSG:32631\", \"EPSG:4326\", [utmEast, utmNorth]);\n\n// Extract the latitude and longitude\nmsg.payload.longitude = lonlat[0];\nmsg.payload.latitude = lonlat[1];\n\n/*var key = S2.latLngToKey(lonlat[1], lonlat[0], level);\nvar id = S2.keyToId(key);\nmsg.payload.S2id= id;*/\n\n\nif(distUTM > 0.2){\n    flow.set(\"prevUTM\", curUTM);\n    return msg;\n}else if(angleDelta > 5){\n    flow.set(\"prevAngle\", curAngle);\n\n}\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"proj4","module":"proj4"},{"var":"s2Geometry","module":"s2-geometry"}],"x":790,"y":300,"wires":[["e440ad720d596756"]]},{"id":"372a4b846b8ee8da","type":"function","z":"be4e0bffe7b14ed5","name":"xy to lonlat implement","func":"const framework_settings = global.get(\"framework_settings\");\nconst robot_ref_state = global.get(\"robot_ref_state\");\n\nvar object = msg.payload;\nfor (const [key, value] of Object.entries(object)) {\n\n    var sections = object[key].sections\n\n    for (let index = 0; index < sections.length; index++) {\n        const section = sections[index];\n        \n        /*var utmZone = framework_settings.gps.utm_zone;\n        var utmEast = section.T[0]; // Example UTM easting\n        var utmNorth = section.T[1]; // Example UTM northing\n\n        // Define the projection for UTM 31N\n        proj4.defs(\"EPSG:32631\", \"+proj=utm +zone=31 +ellps=WGS84 +datum=WGS84 +units=m +no_defs\");\n\n        // Transform the UTM coordinates to latitude and longitude\n        var lonlat = proj4(\"EPSG:32631\", \"EPSG:4326\", [utmEast, utmNorth]);\n        */\n        // Extract the latitude and longitude\n        section.state = {}\n        section.state.latitude = (section.latlng[0][0] + section.latlng[1][0] + section.latlng[2][0] + section.latlng[3][0]) / 4;\n        section.state.longitude = (section.latlng[0][1] + section.latlng[1][1] + section.latlng[2][1] + section.latlng[3][1]) / 4;\n        var x = (section.xy[0][0] + section.xy[1][0] + section.xy[2][0] + section.xy[3][0]) / 4;\n        var y = (section.xy[0][1] + section.xy[1][1] + section.xy[2][1] + section.xy[3][1]) / 4;\n        section.state.T = [x, y, robot_ref_state.T[2]];\n        section.state.R = robot_ref_state.R;\n\n    }\n}\nmsg.payload = object;\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"proj4","module":"proj4"}],"x":840,"y":500,"wires":[["0fe7483209d0135b"]]},{"id":"a91d2446dc38bc9d","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"redis_variables","tot":"flow"},{"t":"set","p":"variables","pt":"msg","to":"redis_variables","tot":"flow"},{"t":"set","p":"framework_settings","pt":"msg","to":"framework_settings","tot":"global"}],"action":"","property":"","from":"","to":"","reg":false,"x":180,"y":360,"wires":[["e30a9f1486357fbe"]]},{"id":"94a2da4ed7b4b3b8","type":"link out","z":"be4e0bffe7b14ed5","name":"link out 1","mode":"link","links":["76e8a0f5dbe67304"],"x":1175,"y":360,"wires":[]},{"id":"d01868571bc12b57","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"[\"implement.states\"]","tot":"json"}],"action":"","property":"","from":"","to":"","reg":false,"x":180,"y":500,"wires":[["3e4099629850bf7b"]]},{"id":"6d60ac51fd412dd8","type":"inject","z":"be4e0bffe7b14ed5","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"0.1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":170,"y":440,"wires":[["d01868571bc12b57"]]},{"id":"a756dc5bcb8d4ae4","type":"change","z":"be4e0bffe7b14ed5","name":"","rules":[{"t":"set","p":"redis_values","pt":"global","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1080,"y":400,"wires":[[]]},{"id":"e30a9f1486357fbe","type":"redis-command","z":"be4e0bffe7b14ed5","server":"73bf5d6106b6cde3","command":"MGET","name":"MGET","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":370,"y":360,"wires":[["a7663ea12c9abe1f"]]},{"id":"7a1185c9b8c5dc07","type":"redis-command","z":"be4e0bffe7b14ed5","server":"73bf5d6106b6cde3","command":"JSON.GET","name":"JSON.GET","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":300,"wires":[["c32bba259c33d2c3"]]},{"id":"3e4099629850bf7b","type":"redis-command","z":"be4e0bffe7b14ed5","server":"73bf5d6106b6cde3","command":"JSON.GET","name":"JSON.GET","topic":"","params":"[]","paramsType":"json","payloadType":"json","block":false,"x":390,"y":500,"wires":[["bfbbc3701da95ee7"]]},{"id":"94c11dc7018e9495","type":"function","z":"37c61670c96fdccb","name":"PC","func":"const framework_settings = msg.framework_settings;\nconst activeField = msg.payload.pc.field.name;\nconst dataProvider = \"PC\";\nvar object = msg.payload.pc;\nvar array = [];\nvar timestamp = msg.timestamp;\nfor (const [key, value] of Object.entries(object)) {\n   value.time = timestamp\n    array.push( [value, \n        {\n            \"provider\": dataProvider,\n            \"process\": key,\n            \"robot\": framework_settings.name,\n            \"activeField\": activeField,\n            \"state\": msg.current_state\n        }\n    ]\n    )\n\n}\nmsg.payload = array;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":200,"wires":[["b6cbb7cd9759a5c5"]]},{"id":"83876b51fd1673af","type":"function","z":"37c61670c96fdccb","name":"PLC","func":"const framework_settings = msg.framework_settings;\nconst dataProvider = \"PLC\";\nconst activeField = msg.payload.pc.field.name;\nvar objectmonitor = msg.payload.plc.monitor;\nvar objectcontrol = msg.payload.plc.control;\n\nvar array = [];\nvar timestamp = msg.timestamp;\nfor (const [key, value] of Object.entries(objectmonitor)) {\n    var data = {\n        \"time\": timestamp\n    }\n    \n        for (const [key2, value2] of Object.entries(value)) {\n            if (typeof value2 === 'object'){\n                for (const [key3, value3] of Object.entries(value2)) {\n                    data[key2 +\"_\"+ key3] = value3;\n                }\n            }else{\n                data[key2] = value2;\n            }\n        }\n    \n    array.push( [\n        data\n        , \n        {\n            \"provider\": dataProvider,\n            \"action\": \"monitor\",\n            \"device\": key,\n            \"robot\": framework_settings.name,\n            \"activeField\": activeField,\n            \"state\": msg.current_state\n\n        }\n    ]\n    )\n\n}\n\nfor (const [key, value] of Object.entries(objectcontrol)) {\n    var data2 = {\n        \"time\": timestamp\n    }\n        for (const [key2, value2] of Object.entries(value)) {\n            if (typeof value2 === 'object'){\n                for (const [key3, value3] of Object.entries(value2)) {\n                    data2[key2 +\"_\"+ key3] = value3;\n                }\n            }else{\n                data2[key2] = value2;\n            }\n        }\n\n    array.push( [\n        data2\n        , \n        {\n            \"provider\": dataProvider,\n            \"action\": \"control\",\n            \"device\": key,\n            \"robot\": framework_settings.name,\n            \"activeField\": activeField,\n            \"state\": msg.current_state\n\n        }\n    ]\n    )\n\n}\n\nmsg.payload = array;\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":390,"y":240,"wires":[["b6cbb7cd9759a5c5"]]},{"id":"8fa59b5a5b69e67a","type":"batch","z":"37c61670c96fdccb","name":"","mode":"interval","count":10,"overlap":0,"interval":"10","allowEmptySequence":false,"topics":[],"x":700,"y":220,"wires":[["0eb2a050a38c527c"]]},{"id":"0eb2a050a38c527c","type":"join","z":"37c61670c96fdccb","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":850,"y":220,"wires":[["f37821071279a6e6"]]},{"id":"b6cbb7cd9759a5c5","type":"split","z":"37c61670c96fdccb","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":550,"y":220,"wires":[["8fa59b5a5b69e67a"]]},{"id":"5ea7ddeb74d3069e","type":"function","z":"37c61670c96fdccb","name":"Add lon, lat,state","func":"const robot_ref_state = msg.robot_ref_state;\nconst states = msg.payload.plc.monitor.state;\nmsg.payload.pc.gps.longitude = robot_ref_state.longitude;\nmsg.payload.pc.gps.latitude = robot_ref_state.latitude;\n\n\n\nfor (const state in states){\n    if (states[state] == true){\n        msg.current_state = state;\n    }\n}\nmsg.timestamp = new Date();\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":190,"y":220,"wires":[["94c11dc7018e9495","83876b51fd1673af"]]},{"id":"76e8a0f5dbe67304","type":"link in","z":"37c61670c96fdccb","name":"link in 1","links":["94a2da4ed7b4b3b8"],"x":55,"y":220,"wires":[["5ea7ddeb74d3069e"]]},{"id":"f37821071279a6e6","type":"influxdb out","z":"37c61670c96fdccb","influxdb":"eee51326e00bb95f","name":"","measurement":"robots","precision":"","retentionPolicy":"","database":"database","precisionV18FluxV20":"ms","retentionPolicyV18Flux":"","org":"<organization>","bucket":"<bucket>","x":1040,"y":220,"wires":[]},{"id":"9b2cdbad82a1fa5f","type":"split","z":"bf6bf5e0fef34d67","name":"","splt":"\\n","spltType":"str","arraySplt":"8","arraySpltType":"len","stream":false,"addname":"","x":170,"y":200,"wires":[["1bb4e631f6670529","f33b3f5ba07af327"]]},{"id":"68bc1826a32b2952","type":"function","z":"bf6bf5e0fef34d67","name":"add meta data","func":"var redis_values = global.get(\"redis_values\");\nvar impl_states = global.get(\"impl_states\");\nvar timestamp = msg.timestamp - msg.totalTime + msg.payload.time\n\nvar metadata = \n\nmsg.payload = [\n    [\n    {\n        \"time\": timestamp,\n        \"force\": msg.payload.force,\n\n        }, {\n            \"unit\": \"MPa\",\n            \"field\": redis_values.pc.field.name,\n         \n\n\n        }\n],\n    [\n        {\n            \"time\": timestamp,\n            \"Z\": msg.payload.Depth,\n            \"X\": msg.X,\n\n        }, {\n            \"unit\": \"mm\",\n            \"field\": redis_values.pc.field.name,\n\n\n        }\n    ],\n    [\n        {\n            \"time\": timestamp,\n            \"latitude\": 0,\n            \"longitude\": 0,\n           /* \"series_latitude\": Math.round(impl_states.penetrometer.sections[0].state.latitude * 100000) / 100000,\n            \"series_longitude\": Math.round(impl_states.penetrometer.sections[0].state.longitude * 100000) / 100000 */\n\n            \"series_latitude\": impl_states.penetrometer.sections[0].state.latitude,\n            \"series_longitude\":impl_states.penetrometer.sections[0].state.longitude ,\n            //\"series_ID\": (Math.round(impl_states.penetrometer.sections[0].state.latitude * 100000000) / 100000000 + \"-\" + Math.round(impl_states.penetrometer.sections[0].state.longitude * 100000000) / 100000000).replaceAll(\".\",\"_\"),\n\n        }, {\n            \"unit\": \"°\",\n            \"field\": redis_values.pc.field.name,\n        }\n    ],\n]\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":860,"y":200,"wires":[["0a5425f5bf041ae7"]]},{"id":"67fbc8efd4806817","type":"change","z":"bf6bf5e0fef34d67","name":"Get value","rules":[{"t":"set","p":"payload","pt":"msg","to":"payload.value","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":560,"y":120,"wires":[["0e16129fac19b508"]]},{"id":"9daed32227cd28d0","type":"rbe","z":"bf6bf5e0fef34d67","name":"Busy change?","func":"rbei","gap":"","start":"","inout":"out","septopics":true,"property":"payload.Busy","topi":"topic","x":1060,"y":120,"wires":[["e583281c1827b2e6"]]},{"id":"e583281c1827b2e6","type":"switch","z":"bf6bf5e0fef34d67","name":"Busy = false","property":"payload.Busy","propertyType":"msg","rules":[{"t":"false"}],"checkall":"true","repair":false,"outputs":1,"x":1230,"y":120,"wires":[["16242c828067b910"]]},{"id":"18b3ce885e900f44","type":"inject","z":"bf6bf5e0fef34d67","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"1","crontab":"","once":true,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":210,"y":120,"wires":[["83e94b6555bc0cf2"]]},{"id":"16242c828067b910","type":"change","z":"bf6bf5e0fef34d67","name":"Get Data","rules":[{"t":"set","p":"busy","pt":"msg","to":"payload.busy","tot":"msg","dc":true},{"t":"set","p":"totalTime","pt":"msg","to":"payload.totalTime","tot":"msg"},{"t":"set","p":"X","pt":"msg","to":"payload.X","tot":"msg","dc":true},{"t":"set","p":"payload","pt":"msg","to":"payload.Data","tot":"msg","dc":true},{"t":"set","p":"timestamp","pt":"msg","to":"","tot":"date"}],"action":"","property":"","from":"","to":"","reg":false,"x":1380,"y":120,"wires":[["9b2cdbad82a1fa5f"]]},{"id":"1bb4e631f6670529","type":"switch","z":"bf6bf5e0fef34d67","name":"","property":"payload[0]","propertyType":"msg","rules":[{"t":"neq","v":"0","vt":"num"}],"checkall":"true","repair":false,"outputs":1,"x":350,"y":200,"wires":[["f45f8b844221fd62"]]},{"id":"0a5425f5bf041ae7","type":"function","z":"bf6bf5e0fef34d67","name":"Vectors to affine","func":"\nvar impl_states = global.get(\"impl_states\");\nvar startOffset = -35; // cm\nvar sideShift = msg.payload[1][0].X + startOffset;\n\n// Penetrometer ref state\nvar penetrometerRefState = {};\npenetrometerRefState.R = impl_states.penetrometer.sections[0].state.R; // R vector van redis\npenetrometerRefState.R = [penetrometerRefState.R[0] * (Math.PI / 180.0), penetrometerRefState.R[1] * (Math.PI / 180.0), penetrometerRefState.R[2] * (Math.PI / 180.0)];\npenetrometerRefState.T = impl_states.penetrometer.sections[0].state.T; // T vector van redis\n\n\nconst [x, y, z] = penetrometerRefState.R;\nconst cosPitch = Math.cos(x);\nconst sinPitch = Math.sin(x);\nconst cosRoll = Math.cos(y);\nconst sinRoll = Math.sin(y); \nconst cosYaw = Math.cos(z);\nconst sinYaw = Math.sin(z);\n\npenetrometerRefState.affine = [\n        [cosYaw * cosPitch, cosYaw * sinPitch * sinRoll - sinYaw * cosRoll, cosYaw * sinPitch * cosRoll + sinYaw * sinRoll, penetrometerRefState.T[0]],\n        [sinYaw * cosPitch, sinYaw * sinPitch * sinRoll + cosYaw * cosRoll, sinYaw * cosRoll * sinPitch - cosYaw * sinRoll, penetrometerRefState.T[1]],\n        [-sinPitch, cosPitch * sinRoll, cosPitch * cosRoll, penetrometerRefState.T[2]],\n        [0, 0, 0, 1]\n];\n\n// Penetrometer Transform\nvar penetrometerTransform = {};\npenetrometerTransform.affine = [\n        [1, 0, 0, -sideShift / 100],\n        [0, 1, 0, 0],\n        [0, 0, 1, 0],\n        [0, 0, 0, 1]\n];\n\n// Multiplication\nconst { Matrix } = mlMatrix;\n\nvar refPoint = new Matrix(penetrometerRefState.affine);\nvar shiftTransform = new Matrix(penetrometerTransform.affine)\n\nvar penPoint = refPoint.mmul(shiftTransform);\n\nvar xPen = penPoint.get(0, 3);\nvar yPen = penPoint.get(1, 3);\n\n// Define the projection for UTM 31N\nproj4.defs(\"EPSG:32631\", \"+proj=utm +zone=31 +ellps=WGS84 +datum=WGS84 +units=m +no_defs\");\n\n// Transform the UTM coordinates to latitude and longitude\nvar lonlat = proj4(\"EPSG:32631\", \"EPSG:4326\", [xPen, yPen]);\n\n// Extract the latitude and longitude\nmsg.payload[2][0].longitude = lonlat[0];\nmsg.payload[2][0].latitude = lonlat[1];\nmsg.refPoint = refPoint\n\nmsg.shiftTransform = shiftTransform;\nmsg.penPoint = penPoint;\n//msg.payload[0].penState = {\"R\": penetrometerRefState.R,\"T\": penetrometerRefState.T};\n\nreturn msg;\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"mlMatrix","module":"ml-matrix"},{"var":"proj4","module":"proj4"}],"x":1060,"y":200,"wires":[["7cf1c11abde6889d"]]},{"id":"f33b3f5ba07af327","type":"function","z":"bf6bf5e0fef34d67","name":"Check when done.","func":"if( msg.parts.index >= msg.parts.count - 1){\n    msg.complete = true;\n    return msg;\n}\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":280,"wires":[["f45f8b844221fd62"]]},{"id":"7cf1c11abde6889d","type":"join","z":"bf6bf5e0fef34d67","name":"","mode":"auto","build":"object","property":"payload","propertyType":"msg","key":"topic","joiner":"\\n","joinerType":"str","accumulate":"false","timeout":"","count":"","reduceRight":false,"x":1230,"y":200,"wires":[["6d8ae649ac79391e"]]},{"id":"83e94b6555bc0cf2","type":"s7comm read","z":"bf6bf5e0fef34d67","connection":"63b49a5eaf7b2bc8","payload":"{\"S7_Type\":\"DB\",\"S7_DBnum\":\"17\",\"S7_Datatype\":\"B\",\"S7_Offset\":\"0\",\"S7_BitOffset\":\"1\",\"S7_Quantity\":\"1620\",\"S7_Name\":\"Data\"}","s7Name":"Data","topic":"","name":"","signalSetted":false,"none":"true","repeat":"","once":false,"x":400,"y":120,"wires":[["67fbc8efd4806817"]]},{"id":"68c589edf10abc56","type":"buffer-parser","z":"bf6bf5e0fef34d67","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"bool","name":"Busy","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"int16be","name":"X","offset":2,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"int16be","name":"totalTime","offset":4,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"byte","name":"Data","offset":6,"length":2020,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"return","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":890,"y":120,"wires":[["9daed32227cd28d0"]]},{"id":"37798813cd48fa31","type":"buffer-parser","z":"bf6bf5e0fef34d67","name":"","data":"payload","dataType":"msg","specification":"spec","specificationType":"ui","items":[{"type":"floatbe","name":"force","offset":0,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"uint16be","name":"Depth","offset":4,"length":1,"offsetbit":0,"scale":"1","mask":""},{"type":"uint16be","name":"time","offset":6,"length":1,"offsetbit":0,"scale":"1","mask":""}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","resultType":"keyvalue","resultTypeType":"return","multipleResult":false,"fanOutMultipleResult":false,"setTopic":true,"outputs":1,"x":690,"y":200,"wires":[["68bc1826a32b2952"]]},{"id":"0e16129fac19b508","type":"buffer-maker","z":"bf6bf5e0fef34d67","name":"Array to buffer","specification":"spec","specificationType":"ui","items":[{"name":"item1","type":"uint8","length":2026,"dataType":"msg","data":"payload"}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","x":720,"y":120,"wires":[["68c589edf10abc56"]]},{"id":"f45f8b844221fd62","type":"buffer-maker","z":"bf6bf5e0fef34d67","name":"","specification":"spec","specificationType":"ui","items":[{"name":"item1","type":"byte","length":8,"dataType":"msg","data":"payload"}],"swap1":"","swap2":"","swap3":"","swap1Type":"swap","swap2Type":"swap","swap3Type":"swap","msgProperty":"payload","msgPropertyType":"str","x":530,"y":200,"wires":[["37798813cd48fa31"]]},{"id":"6d8ae649ac79391e","type":"influxdb out","z":"bf6bf5e0fef34d67","influxdb":"eee51326e00bb95f","name":"","measurement":"penetrometer","precision":"","retentionPolicy":"","database":"database","precisionV18FluxV20":"ms","retentionPolicyV18Flux":"","org":"<organization>","bucket":"<bucket>","x":1460,"y":200,"wires":[]}]

Flow Info

Created 3 months, 1 week ago
Rating: not yet rated

Actions

Rate:

Node Types

Core
  • batch (x1)
  • change (x10)
  • file in (x3)
  • function (x13)
  • inject (x4)
  • join (x3)
  • json (x5)
  • link in (x1)
  • link out (x1)
  • rbe (x1)
  • split (x5)
  • switch (x4)
Other
  • buffer-maker (x2)
  • buffer-parser (x2)
  • influxdb (x2)
  • influxdb out (x2)
  • postgreSQLConfig (x1)
  • redis-command (x3)
  • redis-config (x1)
  • s7comm (x3)
  • s7comm read (x1)
  • subflow (x1)
  • subflow:4ca778f604106427 (x1)
  • tab (x3)

Tags

  • agriculture
  • soil-pressure-sampling
  • robotics
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option