diff --git a/tools/rapport-nuit.mjs b/tools/rapport-nuit.mjs index 0d13b0a..625d195 100644 --- a/tools/rapport-nuit.mjs +++ b/tools/rapport-nuit.mjs @@ -76,23 +76,30 @@ export async function texteRapport(client, jour = new Date()) { AND ts >= $1 AND ts < $2 ORDER BY ts`, [debut, fin]); const fermetures = bascules.filter((b) => b.apres === 'on'); + // Integration ponderee par l'intervalle REEL entre points (cap 300 s pour + // qu'un trou n'extrapole rien), et fenetre plafonnee a now(). Le rapport + // tourne a 06:47 alors que la fenetre va jusqu'a 10:00 : l'ancienne methode + // (grille generate_series 1 min + derniere valeur connue + sum/60000) + // reportait la derniere puissance connue sur les ~3 h FUTURES de la fenetre + // et surestimait l'import (3,8 kWh reconstitue au lieu de 1,6 mesure, nuit du + // 24/09, signale par la session SPARK via les compteurs d'energie Victron). + // NE JAMAIS revenir a sum(x)/60000 sur une grille qui depasse maintenant : + // elle integre du futur. La ponderation par lead(ts) n'a pas de point apres + // le dernier echantillon, donc elle n'extrapole jamais. const { rows: [e] } = await q( - `WITH mn AS (SELECT generate_series($1::timestamptz, $2::timestamptz, interval '1 minute') t), - j AS (SELECT mn.t, - (SELECT valeur FROM mesure m WHERE m.cle = 'sensor.victron_grid_power_l1' - AND m.ts <= mn.t ORDER BY m.ts DESC LIMIT 1) g, - (SELECT valeur FROM mesure m WHERE m.cle = 'sensor.victron_evcs_ac_power' - AND m.ts <= mn.t ORDER BY m.ts DESC LIMIT 1) v, - (SELECT valeur FROM mesure m WHERE m.cle = 'sensor.victron_battery_power' - AND m.ts <= mn.t ORDER BY m.ts DESC LIMIT 1) b - FROM mn) - SELECT coalesce(sum(g) FILTER (WHERE g > 0), 0) / 60000.0 AS importe, - coalesce(-sum(g) FILTER (WHERE g < 0), 0) / 60000.0 AS injecte, - coalesce(sum(v) FILTER (WHERE v > 0), 0) / 60000.0 AS voiture, - coalesce(sum(b) FILTER (WHERE b > 0), 0) / 60000.0 AS batterie_chargee, - count(*) FILTER (WHERE g < 0) AS points_negatifs, - min(g) AS pire_injection - FROM j`, [debut, fin]); + `WITH b AS (SELECT $1::timestamptz d, LEAST($2::timestamptz, now()) f), + g AS (SELECT valeur v, LEAST(EXTRACT(EPOCH FROM (lead(ts) OVER (ORDER BY ts) - ts)), 300) dt + FROM mesure, b WHERE cle = 'sensor.victron_grid_power_l1' AND ts >= d AND ts < f), + ev AS (SELECT valeur v, LEAST(EXTRACT(EPOCH FROM (lead(ts) OVER (ORDER BY ts) - ts)), 300) dt + FROM mesure, b WHERE cle = 'sensor.victron_evcs_ac_power' AND ts >= d AND ts < f), + ba AS (SELECT valeur v, LEAST(EXTRACT(EPOCH FROM (lead(ts) OVER (ORDER BY ts) - ts)), 300) dt + FROM mesure, b WHERE cle = 'sensor.victron_battery_power' AND ts >= d AND ts < f) + SELECT (SELECT coalesce(sum(GREATEST(v,0)*dt) FILTER (WHERE dt IS NOT NULL),0)/3600000.0 FROM g) AS importe, + (SELECT coalesce(sum(GREATEST(-v,0)*dt) FILTER (WHERE dt IS NOT NULL),0)/3600000.0 FROM g) AS injecte, + (SELECT coalesce(sum(GREATEST(v,0)*dt) FILTER (WHERE dt IS NOT NULL),0)/3600000.0 FROM ev) AS voiture, + (SELECT coalesce(sum(GREATEST(v,0)*dt) FILTER (WHERE dt IS NOT NULL),0)/3600000.0 FROM ba) AS batterie_chargee, + (SELECT count(*) FROM g WHERE v < 0) AS points_negatifs, + (SELECT min(v) FROM g) AS pire_injection`, [debut, fin]); if (!fermetures.length) { dire('Le contacteur est reste ouvert toute la nuit : aucune energie achetee au reseau.');