Files
EVCS/tools/preparer-archive-pylontech.mjs
Tallagrand Thierry 8cd4e1437c Les rapports entrent dans l'archive
Un distributeur qui transmet au constructeur ne doit dependre d'aucun
lien exterieur, et le destinataire final lit peut-etre le chinois
plutot que le francais. Les trois versions HTML rejoignent donc
l'envoi, et le courrier decrit sa piece jointe au lieu de proposer de
la fournir.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-05 20:49:12 +02:00

163 lines
7.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// Rassemble tout ce qui concerne les BMS Pylontech en une archive lisible par
// un tiers : un dossier par module, nomme par son code-barres, plus un
// manifeste trilingue qui dit quel module est quoi et ou regarder en premier.
//
// node tools/preparer-archive-pylontech.mjs
//
// Le tri se fait sur le CONTENU des fichiers (code-barres lu dedans), jamais
// sur leur nom : les exports BatteryView et les miens ne se nomment pas pareil.
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
const SRC = 'docs/mesures/batteries';
const OUT = `docs/mesures/archive-pylontech-${new Date().toISOString().slice(0, 10).replace(/-/g, '')}`;
// Etat de chaque module, tel qu'etabli le 05/09/2026. Sert au manifeste.
const MODULES = {
K220803C30160322: { pack: '2022', adr: 1, cellules: '6, 7, 12, 14', med: 108, ev: 850, etat: 'atteint' },
K220803C30160323: { pack: '2022', adr: 2, cellules: '—', med: 6, ev: 0, etat: 'temoin' },
K220803C30160319: { pack: '2022', adr: 3, cellules: '2, 6, 10, 12, 15', med: 45, ev: 688, etat: 'atteint' },
K220803C30160324: { pack: '2022', adr: 4, cellules: '15', med: 57, ev: 900, etat: 'atteint' },
Y230202C30371426: { pack: '2023', adr: 1, cellules: '—', med: 2, ev: 19, etat: 'sain' },
Y230202C30371425: { pack: '2023', adr: 2, cellules: '—', med: 2, ev: 12, etat: 'sain' },
Y230202C30371431: { pack: '2023', adr: 3, cellules: '—', med: 2, ev: 6, etat: 'sain' },
Y230202C30371427: { pack: '2023', adr: 4, cellules: '—', med: 2, ev: 1, etat: 'sain' },
};
const lire = (f) => {
const b = fs.readFileSync(f);
return b.toString(b[0] === 0xff && b[1] === 0xfe ? 'utf16le' : 'utf8');
};
fs.rmSync(OUT, { recursive: true, force: true });
fs.mkdirSync(OUT, { recursive: true });
const classe = {};
let communs = 0;
for (const n of fs.readdirSync(SRC)) {
const f = path.join(SRC, n);
if (!fs.statSync(f).isFile()) continue;
let bc = null;
for (const code of Object.keys(MODULES)) {
if (n.includes(code)) { bc = code; break; }
}
if (!bc && /\.(txt|csv)$/i.test(n)) {
const t = lire(f).slice(0, 200000);
for (const code of Object.keys(MODULES)) if (t.includes(code)) { bc = code; break; }
}
const dest = bc ? path.join(OUT, `${MODULES[bc].pack}-${bc}`) : path.join(OUT, 'commun');
fs.mkdirSync(dest, { recursive: true });
fs.copyFileSync(f, path.join(dest, n));
if (bc) (classe[bc] ??= []).push(n); else communs++;
}
const ligne = (bc) => {
const m = MODULES[bc];
return `| ${bc} | ${m.pack} | ${m.adr} | ${m.cellules} | ${m.med} mV | ${m.ev} |`;
};
const ordre = Object.keys(MODULES);
const tableau = ordre.map(ligne).join('\n');
fs.writeFileSync(path.join(OUT, 'LISEZ-MOI.md'), `# Relevés BMS Pylontech — installation Victron ESS
*Généré le ${new Date().toLocaleDateString('fr-FR')}*
## Français
Huit modules US2000C en deux packs de quatre reliés par un LV-HUB. Toutes les
valeurs ci-dessous sortent des BMS eux-mêmes, lues sur leur console série
(\`info\`, \`stat\`, \`soh\`, \`data … history\`, \`data … event\`).
| Code-barres | Pack | Adr. | Cellules déviantes | Écart médian | Événements |
|---|---|---:|---|---:|---:|
${tableau}
**Où regarder en premier** : \`2022-K220803C30160324\`, fichier
\`*-evenements-distant-*.csv\`, ligne du 23/07/2026 17:20 — la cellule 15 est à
2963 mV **à 84 % de charge** sous 6,5 A, quand ses quatorze voisines sont à
3291-3303 mV.
**Ce qui écarte une cause d'installation** : le module K220803C30160323 partage
la chaîne, les courants et les journées de deux modules atteints, et n'a
enregistré **aucun** événement en seize mois.
⚠️ **Horodatages** : les horloges internes des BMS avançaient de 6 h 09 à 6 h 13
au moment des relevés. **Tous les fichiers CSV de ce dossier sont déjà corrigés
à l'heure réelle (Europe/Paris)**. Les exports BatteryView \`*.txt\`, eux, portent
l'heure brute du module.
## English
Eight US2000C modules in two packs of four joined by an LV-HUB. Every value
below comes from the BMS units themselves, read over their serial console.
**Where to look first**: \`2022-K220803C30160324\`, file
\`*-evenements-distant-*.csv\`, row of 2026-07-23 17:20 — cell 15 sits at
2963 mV **at 84 % state of charge** under only 6.5 A, while its fourteen
neighbours are at 3291-3303 mV.
**What rules out an installation cause**: module K220803C30160323 shares the
chain, the currents and the days of two affected modules, and has recorded
**zero** events in sixteen months.
⚠️ **Timestamps**: the BMS internal clocks ran 6 h 09 to 6 h 13 fast. **All CSV
files here are already corrected to real local time (Europe/Paris).** The
BatteryView \`*.txt\` exports carry the raw module clock.
## 中文
八个 US2000C 模块,分为两组,每组四个,通过 LV-HUB 连接。以下所有数值均直接
读取自各模块 BMS 的串口控制台。
**建议优先查看**:\`2022-K220803C30160324\` 目录下的
\`*-evenements-distant-*.csv\`,2026-07-23 17:20 这一行 —— 在 **84 % 荷电状态**
下、仅 6.5 A 放电电流时,15 号电芯降至 2963 mV,而其余十四颗仍在
3291-3303 mV。
**可排除安装或使用原因的依据**:模块 K220803C30160323 与两个受损模块位于同一
通信链,经历相同电流与相同工况,十六个月内**没有任何**事件记录。
⚠️ **时间戳**:采集时各 BMS 内部时钟快 6 小时 09 分至 6 小时 13 分。**本目录中
所有 CSV 文件均已校正为真实当地时间(欧洲/巴黎)**;BatteryView 导出的
\`*.txt\` 文件仍为模块原始时钟。
## Fichiers / Files / 文件
- \`*-histo-distant-*.csv\` — historique, un point toutes les 30 min, 15 tensions de cellule
- \`*-evenements-distant-*.csv\` — journal d'événements, à la seconde, déclenchements uniquement
- \`*-console.txt\` — sorties \`info\`, \`stat\`, \`soh\`, \`pwr\` verbatim
- \`*_history_*.txt\`, \`*_event_*.txt\` — exports BatteryView d'origine (UTF-16)
- \`rapport/\` — le rapport d'analyse en français, anglais et chinois (HTML,
s'ouvre dans n'importe quel navigateur, sans connexion)
- \`commun/\` — sessions console couvrant plusieurs modules a la fois, relevés
simultanés des trois modules du pack 2022, et captures d'écran BatteryView
`);
// Les rapports rejoignent l'archive : un distributeur qui transmet au
// constructeur ne doit dependre d'aucun lien exterieur, et le destinataire
// final lit peut-etre le chinois plutot que le francais.
const RAPPORTS = [
['docs/pages/rapport-batteries.html', 'rapport-FR.html'],
['docs/pages/battery-report.html', 'report-EN.html'],
['docs/pages/battery-report-zh.html', 'report-ZH.html'],
];
const dossierRapports = path.join(OUT, 'rapport');
fs.mkdirSync(dossierRapports, { recursive: true });
for (const [src, nom] of RAPPORTS) {
if (fs.existsSync(src)) fs.copyFileSync(src, path.join(dossierRapports, nom));
else console.log(` ⚠️ rapport absent : ${src}`);
}
console.log(`Archive : ${OUT}`);
for (const bc of ordre) {
const n = (classe[bc] || []).length;
console.log(` ${MODULES[bc].pack}-${bc} ${String(n).padStart(2)} fichier(s) ${MODULES[bc].etat}`);
}
console.log(` commun ${String(communs).padStart(2)} fichier(s)`);
const tgz = `${OUT}.tar.gz`;
execFileSync('tar', ['czf', tgz, '-C', path.dirname(OUT), path.basename(OUT)]);
console.log(`\nArchive compressee : ${tgz} (${(fs.statSync(tgz).size / 1048576).toFixed(1)} Mo)`);