From 08a1cee1b16b0a2553db796305998bc2dfa750f3 Mon Sep 17 00:00:00 2001 From: Tallagrand Thierry Date: Sat, 26 Sep 2026 00:41:47 +0200 Subject: [PATCH] Releve du pack 1 (US5000) sur yd-5CG6110DG0 : service, regle udev, suffixe de fichier Le cable du pack 1 est a puce Prolific, sans pilote sur DSM : lu par un portable a noyau recent. --suffixe pack1 separe les CSV des deux lecteurs ; evcs_cellules regarde 15 min (deux lecteurs non synchronises) et publie modules_lus (9 attendus). Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z --- .../releve-cellules/pack1/90-pylontech.rules | 2 ++ .../pack1/releve-pack1.service | 28 +++++++++++++++++++ tools/diagnostics-agent.mjs | 4 +++ tools/historise-evcs.mjs | 8 ++++-- tools/pylontech-console.mjs | 8 +++++- 5 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 deploy/releve-cellules/pack1/90-pylontech.rules create mode 100644 deploy/releve-cellules/pack1/releve-pack1.service diff --git a/deploy/releve-cellules/pack1/90-pylontech.rules b/deploy/releve-cellules/pack1/90-pylontech.rules new file mode 100644 index 0000000..b23548f --- /dev/null +++ b/deploy/releve-cellules/pack1/90-pylontech.rules @@ -0,0 +1,2 @@ +# Cable Pylontech du pack 1 (Prolific PL2303, 26/09/2026) : nom stable par numero de serie +SUBSYSTEM=="tty", ATTRS{idVendor}=="067b", ATTRS{idProduct}=="23a3", ATTRS{serial}=="BJDAb2A7N11", SYMLINK+="pylontech-pack1", GROUP="dialout", MODE="0660" diff --git a/deploy/releve-cellules/pack1/releve-pack1.service b/deploy/releve-cellules/pack1/releve-pack1.service new file mode 100644 index 0000000..d169d77 --- /dev/null +++ b/deploy/releve-cellules/pack1/releve-pack1.service @@ -0,0 +1,28 @@ +# Releve des cellules du PACK 1 (4 x US5000) sur yd-5CG6110DG0 (10.10.10.61). +# Le pack 2 est lu par le conteneur du Synology .17 ; le pack 1 ne peut pas +# l'etre : son cable est a puce Prolific PL2303 (067b:23a3), sans pilote sur DSM. +# Ce portable (noyau 7.0) le reconnait. +# +# Installation : sudo cp releve-pack1.service /etc/systemd/system/ && +# sudo systemctl daemon-reload && sudo systemctl enable --now releve-pack1 +[Unit] +Description=Releve des cellules Pylontech du pack 1 (US5000) +Wants=network-online.target +After=network-online.target remote-fs.target +# Le lecteur lit son code et ecrit ses CSV dans le depot, monte en NFS. +RequiresMountsFor=/mnt/projects/EVCS/EVCS + +[Service] +User=tmzf5455 +WorkingDirectory=/mnt/projects/EVCS/EVCS +# /dev/pylontech-pack1 : lien stable pose par 90-pylontech.rules d'apres le +# NUMERO DE SERIE du cable, jamais d'apres l'ordre d'enumeration. +# --suffixe pack1 : fichiers ...-pack1-suivi.csv, distincts de ceux du pack 2. +ExecStart=/usr/bin/node tools/pylontech-console.mjs --port /dev/pylontech-pack1 --suivi 600 --modules 1,2,3,4 --suffixe pack1 +# Cable absent, port fige (garde anti-blocage du lecteur), NFS coupe, coupure +# de courant : on relance, sans limite. Le portable n'a plus de batterie. +Restart=always +RestartSec=30 + +[Install] +WantedBy=multi-user.target diff --git a/tools/diagnostics-agent.mjs b/tools/diagnostics-agent.mjs index 7a235c6..86a1bcc 100644 --- a/tools/diagnostics-agent.mjs +++ b/tools/diagnostics-agent.mjs @@ -132,6 +132,10 @@ export function etatCellules(r) { statut: r.spread_max === null ? 'muet' : 'frais', age_releve_min: age, seuil_perime_min: RELEVE_PERIME_MIN, module_le_plus_sensible: r.weakest || null, + // Nombre de modules dans le releve : 9 attendus quand les deux packs sont + // lus (4 US5000 + 5 US2000C). Moins = un lecteur muet, l'ecart ne couvre + // alors qu'une partie du parc. + ...(r.modules_lus !== undefined ? { modules_lus: r.modules_lus } : {}), }, }; } diff --git a/tools/historise-evcs.mjs b/tools/historise-evcs.mjs index c51174a..2236afe 100644 --- a/tools/historise-evcs.mjs +++ b/tools/historise-evcs.mjs @@ -1873,14 +1873,18 @@ async function publierCellulesHelpers(client) { if (!client) { await muet('base injoignable'); return 0; } const { rows: [r] } = await client.query( `WITH mx AS (SELECT max(ts) AS t FROM cellule), + -- 15 min et non 5 (26/09) : deux lecteurs, un par pack, chacun toutes les + -- 10 min et sans synchronisation ; une fenetre plus courte que le pas + -- ferait disparaitre un pack sur deux selon l'instant du calcul. recent AS (SELECT DISTINCT ON (module) module, ts FROM cellule - WHERE ts > (SELECT t FROM mx) - interval '5 minutes' ORDER BY module, ts DESC), + WHERE ts > (SELECT t FROM mx) - interval '15 minutes' ORDER BY module, ts DESC), spreads AS (SELECT c.module, max(c.mv) - min(c.mv) AS ecart FROM cellule c JOIN recent rc ON rc.module = c.module AND rc.ts = c.ts GROUP BY c.module) SELECT (SELECT max(ecart) FROM spreads) AS spread_max, (SELECT module FROM spreads ORDER BY ecart DESC NULLS LAST LIMIT 1) AS weakest, - (SELECT round(EXTRACT(EPOCH FROM (now() - t)) / 60)::int FROM mx) AS age_min`) + (SELECT round(EXTRACT(EPOCH FROM (now() - t)) / 60)::int FROM mx) AS age_min, + (SELECT count(*) FROM spreads)::int AS modules_lus`) .catch(async (e) => { await muet('base injoignable').catch(() => {}); throw e; }); // Capteur unique pour l'agent (25/09) : releve perime => ni ecart ni module // visibles (G13b du banc OpenClaw). Publie avant le test de table vide, pour diff --git a/tools/pylontech-console.mjs b/tools/pylontech-console.mjs index 3ae62c5..371f113 100644 --- a/tools/pylontech-console.mjs +++ b/tools/pylontech-console.mjs @@ -15,6 +15,7 @@ // node tools/pylontech-console.mjs --port /dev/ttyUSB0 --commande help // node tools/pylontech-console.mjs --port /dev/ttyUSB0 --historique // node tools/pylontech-console.mjs --port /dev/ttyUSB0 --suivi 30 +// node tools/pylontech-console.mjs --port /dev/pylontech-pack1 --suivi 600 --modules 1,2,3,4 --suffixe pack1 import fs from 'node:fs'; import path from 'node:path'; @@ -371,7 +372,12 @@ async function main() { if (arg('suivi')) { const pas = Math.max(10, +(arg('suivi', '30')) || 30); - const nom = `${horodateFichier()}-suivi.csv`; + // --suffixe pack1 : deux lecteurs (un par pack, sur deux machines depuis le + // 26/09) ecrivent dans le MEME dossier ; sans suffixe, deux demarrages dans la + // meme minute melangeraient leurs lignes dans un seul fichier. Le nom finit + // toujours par -suivi.csv, seul motif que lit l'ingestion. + const suffixe = arg('suffixe') ? `-${String(arg('suffixe')).replace(/[^\w-]/g, '')}` : ''; + const nom = `${horodateFichier()}${suffixe}-suivi.csv`; const f = path.join(DOSSIER, nom); fs.mkdirSync(DOSSIER, { recursive: true }); if (!fs.existsSync(f)) {