From 3587f236da24f75c4260b3fc671c918cf490532e Mon Sep 17 00:00:00 2001 From: Tallagrand Thierry Date: Wed, 16 Sep 2026 13:19:02 +0200 Subject: [PATCH] Le courant par module entre dans les CSV de releve MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La colonne courant_mA etait vide depuis la mise en service : le mode --modules interroge chaque adresse avec soh , qui donne les quinze tensions mais AUCUN courant. La source juste est pwr, qui rend courant, tension et SOC pour TOUS les modules en un seul appel — une commande serie par cycle au lieu d'une par module. ⚠️ LE TABLEAU pwr N'A PAS LES MEMES COLONNES SELON LA FAMILLE DE MODULES. Mesure du 16/09 : les US5000 (firmware B69.26) intercalent Tlow.Id, Thigh.Id, Vlow.Id et Vhigh.Id que les US2000C (B69.13) n'ont pas. Un parseur a index figes lit donc n'importe quoi des qu'on change de pack — c'est la meme famille de piege que le trou de 44 h. Les colonnes sont resolues PAR LEUR NOM dans l'en-tete. Les trois utiles (Volt, Curr, Coulomb) precedent Time, dont la valeur contient une espace, donc le decoupage positionnel reste valable. Echec de pwr non bloquant : on ecrit la ligne sans courant plutot que de perdre les tensions, qui sont l'essentiel du releve. Verifie sur cinq modules, trois cycles : courant et SOC par module dans le journal et dans le CSV. Pack 2 a 13:18 — 9,5 + 9,4 + 9,0 + 9,2 + 9,1 = 46,2 A, SOC 82/79/83/81/87 %. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj --- tools/pylontech-console.mjs | 48 ++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tools/pylontech-console.mjs b/tools/pylontech-console.mjs index 63895d3..164a2c5 100644 --- a/tools/pylontech-console.mjs +++ b/tools/pylontech-console.mjs @@ -332,6 +332,43 @@ async function main() { return; } + // --------------------------------------------------------------------- + // COURANT PAR MODULE — un seul `pwr` pour toute la chaine + // --------------------------------------------------------------------- + // `soh ` donne les quinze tensions mais AUCUN courant : c'est pourquoi la + // colonne courant_mA du CSV est restee vide depuis la mise en service. + // `pwr` donne courant, tension et SOC pour TOUS les modules en un appel — + // une commande serie par cycle au lieu d'une par module. + // + // ⚠️ LE TABLEAU N'A PAS LES MEMES COLONNES SELON LA FAMILLE DE MODULES. + // Mesure du 16/09 : les US5000 (firmware B69.26) intercalent Tlow.Id, + // Thigh.Id, Vlow.Id et Vhigh.Id que les US2000C (B69.13) n'ont pas. Un + // parseur a index figes lit donc n'importe quoi des qu'on change de pack. + // On resout les colonnes PAR LEUR NOM, dans l'en-tete. + // + // Les colonnes utiles (Volt, Curr, Coulomb) precedent toutes `Time`, dont la + // valeur contient une espace — le decoupage positionnel reste donc valable. + function courantsParModule(txt) { + const lignes = txt.split('\n').map((l) => l.replace(/\r/g, '').trimEnd()); + const iEntete = lignes.findIndex((l) => /^\s*Power\s+Volt\s+Curr\b/.test(l)); + if (iEntete === -1) return new Map(); + const cols = lignes[iEntete].trim().split(/\s+/); + const iCurr = cols.indexOf('Curr'); + const iVolt = cols.indexOf('Volt'); + const iSoc = cols.indexOf('Coulomb'); + if (iCurr === -1) return new Map(); + const m = new Map(); + for (const l of lignes.slice(iEntete + 1)) { + const c = l.trim().split(/\s+/); + const ad = parseInt(c[0], 10); + if (!Number.isFinite(ad) || c[iCurr] === undefined || c[iCurr] === '-') continue; + const mA = parseInt(c[iCurr], 10); + if (!Number.isFinite(mA)) continue; + m.set(ad, { mA, mV: parseInt(c[iVolt], 10) || null, soc: iSoc > -1 ? c[iSoc] : null }); + } + return m; + } + if (arg('suivi')) { const pas = Math.max(10, +(arg('suivi', '30')) || 30); const nom = `${horodateFichier()}-suivi.csv`; @@ -370,6 +407,12 @@ async function main() { console.log('Ctrl-C pour arreter.\n'); for (;;) { if (adresses.length) { + // Un `pwr` pour toute la chaine, avant la boucle par module. Si la + // lecture echoue on continue sans courant plutot que de perdre les + // tensions, qui sont l'essentiel du releve. + let puis = new Map(); + try { puis = courantsParModule(await commande(l, 'pwr', 10000)); } + catch { /* on garde les tensions */ } for (const ad of adresses) { const t = await commande(l, `soh ${ad}`, 10000); // `soh` : " " @@ -381,9 +424,12 @@ async function main() { if (c.length < 10) { console.log(`${horodate()} module ${ad} : illisible`); continue; } const v = c.map((x) => x.mV); const hi = Math.max(...v), lo = Math.min(...v); - fs.appendFileSync(f, [new Date().toISOString(), codes.get(ad), '', ...v, hi - lo].join(',') + '\n'); + const p = puis.get(ad); + fs.appendFileSync(f, [new Date().toISOString(), codes.get(ad), + p ? p.mA : '', ...v, hi - lo].join(',') + '\n'); console.log(`${horodate()} ${codes.get(ad).slice(-4)} haute ${hi} mV (cellule ${c.find((x) => x.mV === hi).n})` + ` basse ${lo} mV ecart ${String(hi - lo).padStart(3)} mV` + + (p ? ` ${(p.mA / 1000).toFixed(1)} A ${p.soc || ''}` : '') + (hi >= 3600 ? ' NIVEAU 4' : hi >= 3550 ? ' NIVEAU 3, le courant se reduit' : '')); } await new Promise((r) => setTimeout(r, pas * 1000));