diff --git a/HANDOFF.md b/HANDOFF.md index 8216d3b..739b7c1 100644 --- a/HANDOFF.md +++ b/HANDOFF.md @@ -4948,6 +4948,78 @@ joignable depuis le telephone. Et une session Claude, meme pilotable a distance, reste un poste de travail — ce n'est pas elle qui doit porter la surveillance de nuit, d'ou le rapport par courriel loge dans le service d'historisation. +### La sauvegarde « Synology NAS 1 » echoue depuis au moins TROIS MOIS +Signalee par une notification DSM apercue dans une capture. Faits, lus dans le +courriel d'echec : + +``` +Tache de sauvegarde : Synology NAS 1 +Destination : 10.10.10.22 / ARCHIVEFAMILLE / ARCHIVE_1_PHOTOS_2008_2015.hbk +Duree : 0 Second +``` + +**Duree zero** : la tache n'a pas pu demarrer, ce n'est pas une erreur de +donnees. Verifie en SSH sur 10.10.10.22 : le dossier partage +**`ARCHIVEFAMILLE` n'existe pas** — ni en clair, ni comme partage chiffre, ni +dans la configuration Samba. Les partages reels y sont `photo`, `video`, +`FILMS`, `PLONGEES`, `KEVIN`, `NetBackup`, `docker`, `projects`... + +La tache jumelle « Synology NAS 2 » vise `10.10.10.22 / video / +ARCHIVE_1_VIDEO.hbk` et **reussit** : l'hote de destination va bien, c'est le +dossier qui a disparu. + +Anciennete : dix notifications d'echec retrouvees, **zero reussite**, la plus +ancienne du **06/06/2026**. Les photos 2008-2015 de ARCHIVE (10.10.10.50) n'ont +donc aucune sauvegarde depuis au moins trois mois, et l'alerte quotidienne +partait dans une boite a 17 600 messages non lus. + +⚠️ Non corrige : 10.10.10.50 n'accepte pas ma cle SSH, et recreer un partage +puis reconfigurer Hyper Backup est une decision qui appartient au proprietaire. +Deux voies : recreer un dossier partage `ARCHIVEFAMILLE` sur 10.10.10.22, ou +repointer la tache vers un partage existant (`NetBackup` est le nom +conventionnel). Dans les deux cas il faudra une sauvegarde COMPLETE au premier +passage, le `.hbk` n'existant pas. + +### Les annotations Grafana etaient devenues illisibles +Sept jours d'historique produisaient une foret de pointilles orange masquant les +courbes. Deux corrections dans `tools/gen-grafana-dashboards.py` : + +1. **`enable: False`** — les evenements deviennent OPT-IN. Le bouton + « Evenements » en tete de tableau les rallume quand on veut le contexte d'un + incident. C'est ce bouton qui etait allume par defaut. +2. **Le genre `bilan` est ecarte** de l'annotation : a lui seul **26 %** des + lignes (36 sur 137), presque toutes des bascules de « production bridee » qui + changent plusieurs fois par heure. Un trait vertical doit marquer ce qui + ARRIVE — bascule du contacteur, vehicule branche, changement de mode ACM, + redemarrage, intervention manuelle — pas un drapeau qui clignote. + +Au passage : les deux panneaux qui COMPARENT les modules entre eux affichaient +**huit courbes dont quatre mortes**. Le releveur lit aussi les modules isoles +tant qu'ils sont sous tension ; le pack ancien a cesse d'emettre le 05/09 mais +restait trace sur une fenetre de sept jours. Filtre pose : « module ayant emis +dans les deux dernieres heures » — un critere d'activite, pas un prefixe de +code-barres, donc rebrancher un pack le fait revenir tout seul. +62/62 requetes toujours valides. + +### La marge de 50 % de cette nuit, verifiee +Question du proprietaire : est-elle justifiee pour un demi-parc ? Mesure de +05:05 (fin de la recharge, SOC 55) a 08:45 (croisement PV) : + +| | | +|---|---:| +| consommation maison | 2,57 kWh | +| production PV sur la periode | 0,35 kWh | +| **energie reellement a couvrir** | **2,22 kWh** | +| ce que le bilan avait reserve (49,4 % - plancher 22 %) | **2,65 kWh** | +| ce que la batterie a effectivement fourni | 1,00 kWh | + +**Marge de 19 %** entre ce qui etait reserve et ce qu'il fallait : c'est une +assurance, pas un gaspillage. La batterie n'a fourni que 1,00 kWh parce que le +reseau a porte la maison de 05:05 a 06:47 — mais la reserve devait couvrir le +cas ou le contacteur disparait a 05:05, et dans ce cas les 2,22 kWh etaient +tous a sa charge. Sur un parc de quatre modules (7,1 kWh utiles de 100 a 22 %), +2,65 kWh representent 37 % du parc. Justifie. + ### Reste ouvert - Detecteur de gel dans le service d'historisation : une grandeur qui ne bouge pas alors qu'une autre du meme bilan bouge = alerte. Non fait. diff --git a/deploy/historisation/grafana/dashboards/batteries.json b/deploy/historisation/grafana/dashboards/batteries.json index 3f0af9e..91ab4f0 100644 --- a/deploy/historisation/grafana/dashboards/batteries.json +++ b/deploy/historisation/grafana/dashboards/batteries.json @@ -60,14 +60,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] @@ -166,7 +166,7 @@ "format": "time_series", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\nFROM cellule WHERE $__timeFilter(ts)\nGROUP BY ts, module ORDER BY 1" + "rawSql": "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\nFROM cellule WHERE $__timeFilter(ts)\n AND module IN (SELECT module FROM cellule\n GROUP BY module HAVING max(ts) > now() - interval '2 hours')\nGROUP BY ts, module ORDER BY 1" } ], "gridPos": { @@ -223,7 +223,7 @@ "format": "time_series", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n max(mv) AS mv\nFROM cellule WHERE $__timeFilter(ts)\nGROUP BY ts, module ORDER BY 1" + "rawSql": "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n max(mv) AS mv\nFROM cellule WHERE $__timeFilter(ts)\n AND module IN (SELECT module FROM cellule\n GROUP BY module HAVING max(ts) > now() - interval '2 hours')\nGROUP BY ts, module ORDER BY 1" }, { "refId": "B", diff --git a/deploy/historisation/grafana/dashboards/nuit.json b/deploy/historisation/grafana/dashboards/nuit.json index cc6f787..f0b0a15 100644 --- a/deploy/historisation/grafana/dashboards/nuit.json +++ b/deploy/historisation/grafana/dashboards/nuit.json @@ -46,14 +46,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/deploy/historisation/grafana/dashboards/prevision.json b/deploy/historisation/grafana/dashboards/prevision.json index 7d9a9f3..39e61f2 100644 --- a/deploy/historisation/grafana/dashboards/prevision.json +++ b/deploy/historisation/grafana/dashboards/prevision.json @@ -46,14 +46,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/deploy/historisation/grafana/dashboards/solaire.json b/deploy/historisation/grafana/dashboards/solaire.json index 32467df..dd76758 100644 --- a/deploy/historisation/grafana/dashboards/solaire.json +++ b/deploy/historisation/grafana/dashboards/solaire.json @@ -46,14 +46,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/deploy/historisation/grafana/dashboards/systeme.json b/deploy/historisation/grafana/dashboards/systeme.json index 98a6234..aed90fd 100644 --- a/deploy/historisation/grafana/dashboards/systeme.json +++ b/deploy/historisation/grafana/dashboards/systeme.json @@ -89,14 +89,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/deploy/historisation/grafana/dashboards/telephone.json b/deploy/historisation/grafana/dashboards/telephone.json index 2a7fd0a..63d6190 100644 --- a/deploy/historisation/grafana/dashboards/telephone.json +++ b/deploy/historisation/grafana/dashboards/telephone.json @@ -46,14 +46,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/deploy/historisation/grafana/dashboards/vehicules.json b/deploy/historisation/grafana/dashboards/vehicules.json index b8042dd..597c1ae 100644 --- a/deploy/historisation/grafana/dashboards/vehicules.json +++ b/deploy/historisation/grafana/dashboards/vehicules.json @@ -46,14 +46,14 @@ "type": "grafana-postgresql-datasource", "uid": "evcs-pg" }, - "enable": true, + "enable": false, "iconColor": "semi-dark-orange", "hide": false, "target": { "format": "table", "rawQuery": true, "editorMode": "code", - "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) ORDER BY 1" + "rawSql": "SELECT ts AS time, resume AS text, genre || ',' || coalesce(source,'?') AS tags FROM evenement WHERE $__timeFilter(ts) AND genre <> 'bilan' ORDER BY 1" } } ] diff --git a/docs/mesures/batteries/202609052040-suivi.csv b/docs/mesures/batteries/202609052040-suivi.csv index f1a95ec..09818e9 100644 --- a/docs/mesures/batteries/202609052040-suivi.csv +++ b/docs/mesures/batteries/202609052040-suivi.csv @@ -448,3 +448,7 @@ heure_reelle,etat,courant_mA,c1_mV,c2_mV,c3_mV,c4_mV,c5_mV,c6_mV,c7_mV,c8_mV,c9_ 2026-09-06T13:31:10.870Z,Y230202C30371425,,3312,3312,3312,3312,3311,3311,3312,3311,3312,3312,3312,3312,3311,3312,3311,1 2026-09-06T13:31:10.997Z,Y230202C30371431,,3312,3312,3312,3312,3312,3312,3312,3312,3312,3310,3312,3312,3312,3312,3312,2 2026-09-06T13:31:11.122Z,Y230202C30371427,,3301,3302,3302,3302,3300,3300,3302,3302,3302,3301,3301,3303,3303,3303,3300,3 +2026-09-06T13:41:11.226Z,Y230202C30371426,,3312,3312,3311,3312,3312,3312,3312,3312,3310,3312,3312,3312,3312,3312,3311,2 +2026-09-06T13:41:11.324Z,Y230202C30371425,,3309,3310,3310,3310,3309,3309,3310,3310,3310,3310,3309,3310,3310,3310,3310,1 +2026-09-06T13:41:11.451Z,Y230202C30371431,,3310,3311,3311,3311,3311,3310,3311,3312,3311,3311,3310,3311,3312,3312,3311,2 +2026-09-06T13:41:11.577Z,Y230202C30371427,,3318,3318,3318,3317,3317,3318,3318,3317,3317,3317,3318,3318,3318,3318,3317,1 diff --git a/tools/gen-grafana-dashboards.py b/tools/gen-grafana-dashboards.py index 061fa03..3320707 100644 --- a/tools/gen-grafana-dashboards.py +++ b/tools/gen-grafana-dashboards.py @@ -153,12 +153,25 @@ def dashboard(uid, title, desc, panels, refresh="1m", frm="now-24h", variables=N {"builtIn": 1, "datasource": {"type": "grafana", "uid": "-- Grafana --"}, "enable": True, "hide": True, "name": "Annotations & Alerts", "iconColor": "rgba(0, 211, 255, 1)", "type": "dashboard"}, - {"name": "Evenements", "datasource": PG, "enable": True, + # Les evenements sont OPT-IN : `enable: False`. Ils rendent un + # graphique illisible des qu'ils sont nombreux — le 06/09, sept + # jours d'historique donnaient une foret de pointillies orange qui + # masquait les courbes. Le bouton « Evenements » en tete de tableau + # les rallume quand on veut le contexte d'un incident. + # + # Et meme allumes, on ecarte le genre `bilan` : a lui seul 26 % des + # lignes, presque toutes des bascules de « production bridee » qui + # changent plusieurs fois par heure. Ce qui merite un trait vertical + # est ce qui ARRIVE — une bascule du contacteur, un vehicule + # branche, un changement de mode de charge, un redemarrage, une + # intervention manuelle — pas un drapeau qui clignote. + {"name": "Evenements", "datasource": PG, "enable": False, "iconColor": "semi-dark-orange", "hide": False, "target": {"format": "table", "rawQuery": True, "editorMode": "code", "rawSql": "SELECT ts AS time, resume AS text, " "genre || ',' || coalesce(source,'?') AS tags " - "FROM evenement WHERE $__timeFilter(ts) ORDER BY 1"}}]}, + "FROM evenement WHERE $__timeFilter(ts) " + "AND genre <> 'bilan' ORDER BY 1"}}]}, "panels": panels} # ===================================================================== @@ -1109,9 +1122,18 @@ batteries = dashboard("evcs-batteries", "Les batteries", ( "custom.thresholdsStyle": {"mode": "dashed"}}}}, legend="right"), + # ⚠️ Ces deux panneaux COMPARENT les modules entre eux : ils ne doivent + # montrer que les modules ENCORE SUR LA CHAINE. Le releveur lit aussi les + # modules isoles tant qu'ils sont sous tension — le pack ancien a cesse + # d'emettre le 05/09 mais restait affiche sur une fenetre de sept jours, + # soit huit courbes dont quatre mortes. Le critere est « a emis dans les + # deux dernieres heures », pas un prefixe de code-barres : rebrancher un + # pack le fait revenir tout seul. ts("Ecart entre la plus haute et la plus basse", [ sql("A", "SELECT ts AS time, module AS metric, max(mv) - min(mv) AS ecart\n" "FROM cellule WHERE $__timeFilter(ts)\n" + " AND module IN (SELECT module FROM cellule\n" + " GROUP BY module HAVING max(ts) > now() - interval '2 hours')\n" "GROUP BY ts, module ORDER BY 1")], PG, 0, 10, 12, 8, unit="mvolt", desc="L'indicateur qui resume un module en une courbe. Un module sain de " @@ -1122,6 +1144,8 @@ batteries = dashboard("evcs-batteries", "Les batteries", ( sql("A", "SELECT ts AS time, module || ' — cellule meneuse' AS metric,\n" " max(mv) AS mv\n" "FROM cellule WHERE $__timeFilter(ts)\n" + " AND module IN (SELECT module FROM cellule\n" + " GROUP BY module HAVING max(ts) > now() - interval '2 hours')\n" "GROUP BY ts, module ORDER BY 1"), sql("B", "SELECT ts AS time, 'plafond de charge (A)' AS metric,\n" " valeur * 20 AS mv\n"