UNPKG

node-red-contrib-virtualmeter

Version:

Virtual Meter Node to be used within Corrently Ecosystem (German Electricity Grid)

204 lines (171 loc) 7.29 kB
const _ = require("lodash"); module.exports = function(RED) { let Influx = require('influx'); function PVPredictionNode(config) { RED.nodes.createNode(this,config); this.influxdbConfig = RED.nodes.getNode(config.influxdb); this.client = this.influxdbConfig.client; var node = this; node.on('input', async function(msg) { let query = 'SELECT mean("esolar") FROM "'+config.gsisolar+'" WHERE time>now()-7d GROUP BY time(1h) fill(previous)'; let results = await node.client.query(query, {}); let cleaned = []; let class_hour = {}; for(let i=0;i<results.length;i++) { if(results[i].mean !== null) { let date = new Date(results[i].time); results[i].hour = date.getHours(); cleaned.push(results[i]); } } const mean = function(aobj) { let sum = 0; let cnt = 0; let min=9999; let max=-9999; for(let i=0;i<aobj.length;i++) { if(min > aobj[i].mean) min = aobj[i].mean; if(max < aobj[i].mean) max = aobj[i].mean; sum += aobj[i].mean; cnt++; } return {mean: sum/cnt, min:min,max:max}; } const classStats = function(classification) { let sum = 0; let min = 999999999999; let max = -99999999999; let sum_stddev = 0; let cnt = 0; for (let c in classification) { if(c !== "stats") { for(let i=0;i<classification[c].length;i++) { sum += classification[c][i].mean; sum_stddev += classification[c][i].stddev; cnt ++; if(classification[c][i].min < min) min = classification[c][i].min; if(classification[c][i].max > max) max = classification[c][i].max; } } } classification.stats = { min:min, max:max, mean:sum/cnt, stddev: sum_stddev/cnt, cnt:cnt, sum:sum } } const classify = function(classobject,candidate,fieldname) { if(typeof classobject["c"+candidate[fieldname]] == "undefined") { classobject["c"+candidate[fieldname]] = []; } classobject["c"+candidate[fieldname]].push(candidate); } // build Classifications for(let i=0; i<cleaned.length;i++) { classify(class_hour,cleaned[i],'hour'); } classStats(class_hour); let energy_reading = 0; let energy_request = await node.client.query('select last("energy") from '+config.name,{}); if(energy_request.length > 0) { energy_reading = energy_request[0].last; } query = 'SELECT mean("power") FROM "'+config.name+'" WHERE time>now()-7d GROUP BY time(1h) fill(previous)'; results = await node.client.query(query, {}); cleaned = []; class_generation = {}; for(let i=0;i<results.length;i++) { if(results[i].mean !== null) { let date = new Date(results[i].time); results[i].hour = date.getHours(); cleaned.push(results[i]); } } for(let i=0; i<cleaned.length;i++) { classify(class_generation,cleaned[i],'hour'); } classStats(class_generation); class_hour.subclasses = {}; // Build SubClassification (Merged) for(let c in class_hour) { if(c !== "stats") { let class_hour_generation = {}; class_hour.subclasses[c] = {}; // Gehe die Generation in der Prognosestunde durch und schaue, welche Werte wir da haben if(typeof class_generation[c] !== "undefined") { class_hour_generation.power = mean(class_generation[c]); class_hour_generation.gsi = mean(class_hour[c]); } class_hour.subclasses[c].generation = class_hour_generation; } } // Generate simple prediction query = 'SELECT mean("esolar") FROM '+config.gsisolar+' where time<now()+72h and time>now() GROUP BY time(1h) fill(linear)'; results = await node.client.query(query, {}); let predictions = []; for(let i=0;i<results.length;i++) { let date = new Date(results[i].time); date.setMinutes(0); date.setSeconds(0); date.setMilliseconds(0); let ts = date.getTime(); let hour = new Date(ts).getHours(); let power =0; let gsi = results[i].mean; let history = class_hour.subclasses["c"+hour].generation; let srcs = []; if(history.gsi.min > gsi) { power = history.power.min; srcs.push("min"); } else if(history.gsi.max < gsi) { power = history.power.max; srcs.push("max"); } else { // Skalieren zwischen min und max let delta_gsi_history = history.gsi.max - history.gsi.min; let delta_gsi_min = gsi - history.gsi.min; if(delta_gsi_history == 0) { power = history.power.min; srcs.push("mean_min"); } else { let factor = delta_gsi_min / delta_gsi_history; power = history.power.min + ((history.power.max - history.power.min) * factor); srcs.push("mean"); } } energy_reading += power; let prediction = { time: new Date(ts), mean: power, energy: energy_reading, srcs: srcs }; predictions.push(prediction); } let batch = []; let measurement = '' if((typeof config.name !== 'undefined') && (config.name !== null)) { measurement = config.name+"_prediction"; } else { measurement = node.id+"_prediction"; } for(let i=0;i<predictions.length;i++) { batch.push({ measurement: measurement, fields: { power: predictions[i].mean, energy: predictions[i].energy }, timestamp: new Date(predictions[i].time).getTime() * 1000000 }); } msg.payload = {gsi:class_hour,generation:class_generation,predictions:predictions}; node.send([msg,{payload:batch}]); }); } RED.nodes.registerType("pvprediction",PVPredictionNode); }