Les requetes s'executaient, les grandeurs etaient fausses

Trois defauts trouves en LISANT les tableaux, qu'aucune des 19 requetes ne
signalait puisqu'elles s'executaient toutes sans erreur.

1. `historise-evcs.mjs` horodate chaque mesure avec son propre last_updated —
   le vrai instant de la mesure, et c'est le bon choix. Mais deux grandeurs ne
   partagent alors presque jamais le meme ts : 2656 lignes pour 2656 instants
   distincts. Mes GROUP BY ts rendaient une ligne par grandeur avec une seule
   colonne remplie, donc la courbe "Excedent" — la grandeur meme du §36 —
   etait VIDE sur les 2650 lignes.

2. Le service n'ecrit que les changements, donc un regroupement a la minute
   laisse des trous : la difference n'existait que 73 minutes sur 434. Report
   de la derniere valeur connue, ce que fait aussi le controleur en temps
   reel. 430/435 apres.

3. L'energie horaire par sum(valeur)/60 suppose 60 echantillons par heure ; il
   y en a eu 26 a 47. Integration ponderee par la duree, plafonnee a 300 s
   pour qu'un arret du service n'extrapole pas une valeur perimee. ~9 %
   d'ecart. Le "25 a 58 %" que j'avais d'abord annonce etait faux : il
   comparait la somme a la moyenne, qui ignore la duree.

Plus deux corrections d'affichage : la colonne `jour` s'affichait
"2026-09-04 02:00:00" (date nue rendue comme instant UTC), et le decompte des
chargeurs MPPT est de six entites publiees, pas sept.

Lecon : verifier-dashboards.mjs prouve qu'une requete s'EXECUTE, pas qu'elle
rend la grandeur voulue — une colonne integralement nulle passe sans un mot.
Ce sont les courbes regardees a l'ecran qui ont attrape les trois.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
This commit is contained in:
Tallagrand Thierry
2026-09-04 08:48:42 +02:00
parent d001cbbf2a
commit 64aee4bfcc
5 changed files with 121 additions and 43 deletions

View File

@@ -74,7 +74,7 @@
"format": "table",
"rawQuery": true,
"editorMode": "code",
"rawSql": "WITH du_matin AS (\n SELECT DISTINCT ON (jour) jour, publie_a, horaire\n FROM prevision\n WHERE publie_a::date = jour\n AND jour BETWEEN $__timeFrom()::date AND $__timeTo()::date\n ORDER BY jour, publie_a ASC\n),\nprevu AS (\n SELECT (jour + (h - 1) * interval '1 hour') AS heure,\n horaire[h] AS kwh\n FROM du_matin, generate_subscripts(horaire, 1) AS h\n),\nproduit AS (\n -- Un echantillon toutes les 60 s : chaque point vaut 1/60 h.\n SELECT date_trunc('hour', ts) AS heure,\n sum(valeur) / 60.0 / 1000.0 AS kwh\n FROM mesure\n WHERE cle = 'sensor.victron_pv_power_system' AND $__timeFilter(ts)\n GROUP BY 1\n)\nSELECT coalesce(prevu.heure, produit.heure) AS time,\n round(prevu.kwh::numeric, 2) AS \"Prevu le matin meme\",\n round(produit.kwh::numeric, 2) AS \"Reellement produit\"\nFROM prevu FULL OUTER JOIN produit USING (heure)\nWHERE coalesce(prevu.heure, produit.heure) BETWEEN $__timeFrom() AND $__timeTo()\nORDER BY 1"
"rawSql": "WITH du_matin AS (\n SELECT DISTINCT ON (jour) jour, publie_a, horaire\n FROM prevision\n WHERE publie_a::date = jour\n AND jour BETWEEN $__timeFrom()::date AND $__timeTo()::date\n ORDER BY jour, publie_a ASC\n),\nprevu AS (\n SELECT (jour + (h - 1) * interval '1 hour') AS heure,\n horaire[h] AS kwh\n FROM du_matin, generate_subscripts(horaire, 1) AS h\n),\n-- INTEGRATION PONDEREE PAR LA DUREE, et non somme/60.\n-- Le service n'ecrit une mesure que lorsqu'elle change : compter les\n-- echantillons revient a supposer qu'il y en a 60 par heure alors qu'il y\n-- en a eu 26 a 47 le 04/09 : l'energie etait sous-estimee d'environ 9 %.\n-- Chaque point vaut donc son intervalle jusqu'au suivant, plafonne a 300 s\n-- pour qu'un arret du service n'extrapole pas une valeur perimee.\npas AS (\n SELECT ts, valeur,\n LEAST(EXTRACT(epoch FROM (lead(ts) OVER (ORDER BY ts) - ts)), 300) AS dt\n FROM mesure\n WHERE cle = 'sensor.victron_pv_power_system' AND $__timeFilter(ts)\n),\nproduit AS (\n SELECT date_trunc('hour', ts) AS heure,\n sum(valeur * coalesce(dt, 0)) / 3600.0 / 1000.0 AS kwh\n FROM pas GROUP BY 1\n)\nSELECT to_char(coalesce(prevu.heure, produit.heure), 'DD/MM HH24') || ' h' AS \"Heure\",\n round(prevu.kwh::numeric, 2) AS \"Prevu le matin meme\",\n round(produit.kwh::numeric, 2) AS \"Reellement produit\"\nFROM prevu FULL OUTER JOIN produit USING (heure)\nWHERE coalesce(prevu.heure, produit.heure) BETWEEN $__timeFrom() AND $__timeTo()\nORDER BY coalesce(prevu.heure, produit.heure)"
}
],
"gridPos": {
@@ -205,7 +205,7 @@
"format": "table",
"rawQuery": true,
"editorMode": "code",
"rawSql": "SELECT publie_a AS \"Publiee le\", jour AS \"Pour le jour\",\n round(total_kwh::numeric, 1) AS \"Total annonce (kWh)\",\n round((horaire[8] + horaire[9] + horaire[10] + horaire[11])::numeric, 2) AS \"07-11 h\",\n round((horaire[12] + horaire[13] + horaire[14] + horaire[15])::numeric, 2) AS \"11-15 h\",\n round((horaire[16] + horaire[17] + horaire[18] + horaire[19])::numeric, 2) AS \"15-19 h\"\nFROM prevision\nWHERE $__timeFilter(publie_a)\nORDER BY publie_a DESC, jour"
"rawSql": "SELECT publie_a AS \"Publiee le\",\n to_char(jour, 'DD/MM') AS \"Pour le jour\",\n round(total_kwh::numeric, 1) AS \"Total annonce (kWh)\",\n round((horaire[8] + horaire[9] + horaire[10] + horaire[11])::numeric, 2) AS \"07-11 h\",\n round((horaire[12] + horaire[13] + horaire[14] + horaire[15])::numeric, 2) AS \"11-15 h\",\n round((horaire[16] + horaire[17] + horaire[18] + horaire[19])::numeric, 2) AS \"15-19 h\"\nFROM prevision\nWHERE $__timeFilter(publie_a)\nORDER BY publie_a DESC, jour"
}
],
"gridPos": {