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":[]}]

Flow Info

Created 1 year, 2 months ago
Rating: 5 1

Owner

Actions

Rate:

Node Types

Core
  • debug (x1)
  • function (x2)
  • inject (x1)
Other
  • subflow (x1)
  • subflow:364e384bacbf752f (x1)

Tags

  • avarage
  • min
  • max
  • median
Copy this flow JSON to your clipboard and then import into Node-RED using the Import From > Clipboard (Ctrl-I) menu option