time weighted average
This flow input some data (e.g. from sensors) and then calculate the time weighted average and more of the data:
Output:
{"count":3,
"min":0,
"max":9,
"sum":9,
"median":0,
"avg_qty":3,
"avg_time":4.5}
[{"id":"364e384bacbf752f","type":"subflow","name":"timeWeightedAverage","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"440e4364ed92206b"}]}],"out":[{"x":700,"y":80,"wires":[{"id":"64c35f5cfe2c4613","port":0}]}],"env":[{"name":"threshold_s","type":"num","value":"60","ui":{"icon":"font-awesome/fa-clock-o","type":"input","opts":{"types":["num"]}}}],"meta":{"author":"[email protected]","license":"MIT"},"color":"#3FADB5","icon":"font-awesome/fa-clock-o"},{"id":"440e4364ed92206b","type":"function","z":"364e384bacbf752f","name":"store","func":"var sliding_windows_size = 10\n\n\n// Limit array just by size\nfunction limit_array_size (arr,size) {\n if (arr.length > size) {\n arr.shift()\n }\n return arr\n}\n\n// Limit array by age\nfunction removeOlderThan(timestamps, data, threshold) {\n const currentTime = Date.now();\n for (let i = timestamps.length - 1; i >= 0; i--) {\n if (currentTime - timestamps[i] > threshold) {\n timestamps.splice(i, 1);\n data.splice(i, 1);\n }\n }\n}\n\nvar data = []\nvar timestamps = []\n\ndata = context.get('data') || []\ntimestamps = context.get('timestamps') || []\n\nif (msg.topic == 'dump') { \n msg.payload = {}\n msg.payload[\"data\"] = data\n msg.payload[\"timestamps\"]= timestamps\n\n context.set('data', [])\n context.set('timestamps', [])\n\n} else {\n let payload = msg.payload\n let now = Date.now()\n data.push(payload)\n timestamps.push(now)\n\n const threshold_s = env.get(\"threshold_s\");\n const threshold_ms = threshold_s * 1000\n\n removeOlderThan(timestamps, data, threshold_ms);\n\n context.set('data',data)\n context.set('timestamps',timestamps)\n\n msg.payload = {}\n msg.payload[\"data\"] = data\n msg.payload[\"timestamps\"] = timestamps\n\n}\nreturn msg;\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":250,"y":80,"wires":[["64c35f5cfe2c4613"]]},{"id":"64c35f5cfe2c4613","type":"function","z":"364e384bacbf752f","name":"calculate stuff","func":"function timeWeightedAverage(data, timestamps) {\n // https://www.timescale.com/blog/what-time-weighted-averages-are-and-why-you-should-care/\n if (data.length !== timestamps.length) {\n node.warn(\"array lenght is not equal\");\n }\n \n let total_weighted_sum = 0.0;\n let total_duration = 0.0;\n let total_area = 0.0\n \n for (let i = 1; i < data.length; i++) {\n let d_t = timestamps[i] - timestamps[i - 1];\n let v_1 = data[i -1]\n let v_2 = data[i]\n let area = ((v_1+v_2))/2 * d_t\n total_area += area\n const duration = timestamps[i] - timestamps[i - 1];\n total_duration += duration;\n }\n \n const time_weighted_avg = total_area / total_duration;\n return time_weighted_avg;\n}\n\n\nfunction findMedian(arr) {\n // https://blog.stackademic.com/finding-the-median-of-an-array-in-javascript-82ff31b3f544\n arr.sort((a, b) => a - b);\n const middleIndex = Math.floor(arr.length / 2);\n\n if (arr.length % 2 === 0) {\n return (arr[middleIndex - 1] + arr[middleIndex]) / 2;\n } else {\n return arr[middleIndex];\n }\n}\n\nlet new_payload = {}\nconst data = msg.payload.data\nconst timestamps = msg.payload.timestamps\n\n\n//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max\nconst min = Math.min(...data)\nconst max = Math.max(...data)\n\n// https://stackoverflow.com/a/10624256\nconst sum = data.reduce((a, b) => a + b, 0);\n\n\nconst avg_qty = (sum / data.length) || 0;\nlet avg_time = timeWeightedAverage(data, timestamps)\n\n\nnew_payload[\"count\"] = timestamps.length \nnew_payload[\"min\"] = min\nnew_payload[\"max\"] = max\nnew_payload[\"sum\"] = sum\nnew_payload[\"median\"] = findMedian(data)\nnew_payload[\"avg_qty\"] = avg_qty\nnew_payload[\"avg_time\"] = avg_time || avg_qty\n\n\nmsg.payload = new_payload\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":80,"wires":[[]]},{"id":"09eaf4ef8ad167db","type":"subflow:364e384bacbf752f","z":"1f7cf46e446880ce","name":"","x":340,"y":60,"wires":[["afaa8a86d4888edc"]]},{"id":"a667ee47fc168685","type":"inject","z":"1f7cf46e446880ce","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"$floor($random() * 10)\t\t","payloadType":"jsonata","x":110,"y":60,"wires":[["09eaf4ef8ad167db"]]},{"id":"afaa8a86d4888edc","type":"debug","z":"1f7cf46e446880ce","name":"debug","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":530,"y":60,"wires":[]}]