Pack 1 (US5000) sous surveillance cellule : repli bat, essais code-barres, boot tolerant
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011uJ47NxgfpGnuuMr3f2n7Z
This commit is contained in:
17
HANDOFF.md
17
HANDOFF.md
@@ -664,5 +664,20 @@ Le rebranchement du cable FTDI a revele deux defauts, corriges par une garde
|
|||||||
(ttyUSB0 -> ttyUSB1). Nom stable `/dev/pylontech-pack2` par numero de serie du
|
(ttyUSB0 -> ttyUSB1). Nom stable `/dev/pylontech-pack2` par numero de serie du
|
||||||
cable, conteneur recree si le port change. Verifie : 5 modules relus a 16:53.
|
cable, conteneur recree si le port change. Verifie : 5 modules relus a 16:53.
|
||||||
|
|
||||||
|
### 26/09 (nuit) — le pack 1 est enfin sous surveillance cellule
|
||||||
|
Nouveau poste **`yd-5CG6110DG0`** (HP EliteBook 840 G3, 10.10.10.61, IP reservee
|
||||||
|
DHCP, Ubuntu 26.04, BIOS mis a jour N75 01.61 -> 01.62 « Critical »), dedie au
|
||||||
|
cable Prolific du pack 1. Service `releve-pack1` (deploy/releve-cellules/pack1/),
|
||||||
|
lien udev `/dev/pylontech-pack1` par numero de serie, depot en NFS, rabat et
|
||||||
|
veille neutralises. Trois defauts trouves en le mettant en service :
|
||||||
|
- **course au boot** : reseau declare pret avant de l'etre, montage NFS rate,
|
||||||
|
service jamais relance (Restart= ne couvre pas un echec de dependance) ->
|
||||||
|
plus de RequiresMountsFor, verification en ExecStartPre ;
|
||||||
|
- **US5000 sans `soh`** (« Unknown command ») -> repli sur `bat <n>` ;
|
||||||
|
- **code-barres parfois non lu au demarrage** -> trois essais par adresse.
|
||||||
|
`pylontech-console.mjs --suffixe pack1` separe les CSV des deux lecteurs ;
|
||||||
|
`evcs_cellules` regarde 15 min (deux lecteurs non synchronises) et publie
|
||||||
|
`modules_lus`. Verifie : **9 modules en base, ecart max 2 mV**.
|
||||||
|
|
||||||
---
|
---
|
||||||
*Document mis a jour le : 25/09/2026 — agent vocal, Grafana par chargeur, garde du releve cellules*
|
*Document mis a jour le : 26/09/2026 — pack 1 sous surveillance cellule (poste yd-5CG6110DG0)*
|
||||||
|
|||||||
@@ -9,11 +9,16 @@
|
|||||||
Description=Releve des cellules Pylontech du pack 1 (US5000)
|
Description=Releve des cellules Pylontech du pack 1 (US5000)
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
After=network-online.target remote-fs.target
|
After=network-online.target remote-fs.target
|
||||||
# Le lecteur lit son code et ecrit ses CSV dans le depot, monte en NFS.
|
# PAS de RequiresMountsFor= : au boot du 26/09, NetworkManager a declare le
|
||||||
RequiresMountsFor=/mnt/projects/EVCS/EVCS
|
# reseau pret 0,8 s avant qu'il le soit vraiment, le montage NFS a echoue
|
||||||
|
# (« Network is unreachable ») et le service, dependance en echec, n'a JAMAIS
|
||||||
|
# ete relance — Restart= ne couvre pas un echec de dependance. On verifie donc
|
||||||
|
# le depot dans ExecStartPre (l'acces declenche l'automontage) : un echec y est
|
||||||
|
# un echec du service, que Restart=always rattrape toutes les 30 s.
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=tmzf5455
|
User=tmzf5455
|
||||||
|
ExecStartPre=/usr/bin/test -r /mnt/projects/EVCS/EVCS/tools/pylontech-console.mjs
|
||||||
WorkingDirectory=/mnt/projects/EVCS/EVCS
|
WorkingDirectory=/mnt/projects/EVCS/EVCS
|
||||||
# /dev/pylontech-pack1 : lien stable pose par 90-pylontech.rules d'apres le
|
# /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.
|
# NUMERO DE SERIE du cable, jamais d'apres l'ordre d'enumeration.
|
||||||
|
|||||||
@@ -121,10 +121,20 @@ par un LV-Hub.**
|
|||||||
il a disparu avec elles.
|
il a disparu avec elles.
|
||||||
|
|
||||||
Cellules du pack 2 mesurées à **3-6 mV d'écart**, SOH 99, limites BMS 250 A
|
Cellules du pack 2 mesurées à **3-6 mV d'écart**, SOH 99, limites BMS 250 A
|
||||||
charge / 250 A décharge, 53,2 V. ⚠️ Les cellules du **pack 1 (US5000) ne sont
|
charge / 250 A décharge, 53,2 V. **Depuis le 26/09/2026, les deux packs sont sous
|
||||||
pas sous surveillance continue** : le relevé série est sur le pack 2 et les
|
surveillance cellule continue** (les trames étendues du Cerbo restent mortes
|
||||||
trames étendues du Cerbo sont mortes depuis le 15/09 (HANDOFF §69/§74). 2e câble
|
depuis le 15/09, HANDOFF §69/§74) :
|
||||||
console en commande.
|
- **pack 2** (5 × US2000C) : câble FTDI sur le Synology .17, conteneur
|
||||||
|
`evcs-releve-cellules` (`deploy/releve-cellules/`) ;
|
||||||
|
- **pack 1** (4 × US5000) : câble à puce Prolific PL2303, **sans pilote sur
|
||||||
|
DSM**, lu par le portable **`yd-5CG6110DG0`** (10.10.10.61, EliteBook 840 G3),
|
||||||
|
service `releve-pack1` (`deploy/releve-cellules/pack1/`). Écart mesuré au
|
||||||
|
démarrage : 1 à 2 mV. Le firmware US5000 (B69.26) ne connaît pas `soh` : le
|
||||||
|
lecteur lit alors `bat <n>`. Leur horloge interne a ~6 h d'avance (sans effet :
|
||||||
|
le relevé horodate avec l'heure de la machine).
|
||||||
|
`sensor.evcs_cellules` attribut `modules_lus` : **9 attendus** ; moins = un
|
||||||
|
lecteur muet. Un câble FTDI de remplacement pour le pack 1 est commandé (il
|
||||||
|
permettrait de tout lire depuis le Synology).
|
||||||
|
|
||||||
**Capacité RE-MESURÉE le 20/09** (`tools/mesure-courbe-batterie.mjs`, HANDOFF
|
**Capacité RE-MESURÉE le 20/09** (`tools/mesure-courbe-batterie.mjs`, HANDOFF
|
||||||
§74) : courbe **plate à 0,30 kWh/point, sans genou**, réserve utile **~23 kWh**
|
§74) : courbe **plate à 0,30 kWh/point, sans genou**, réserve utile **~23 kWh**
|
||||||
|
|||||||
@@ -397,8 +397,14 @@ async function main() {
|
|||||||
// exactement le piege ouvert quand le cable a change de pack le 05/09.
|
// exactement le piege ouvert quand le cable a change de pack le 05/09.
|
||||||
const codes = new Map();
|
const codes = new Map();
|
||||||
for (const ad of adresses) {
|
for (const ad of adresses) {
|
||||||
const info = await commande(l, `info ${ad}`, 8000);
|
// Trois essais par adresse (26/09) : sur les US5000, `info 4` est revenu
|
||||||
const bc = (info.match(/Barcode\s*:\s*(\S+)/) || [])[1];
|
// une fois sans code-barres au demarrage — ligne pas encore videe. Un seul
|
||||||
|
// rate ne doit pas faire perdre un cycle entier au service.
|
||||||
|
let bc = null;
|
||||||
|
for (let essai = 0; essai < 3 && !bc; essai++) {
|
||||||
|
const info = await commande(l, `info ${ad}`, 8000);
|
||||||
|
bc = (info.match(/Barcode\s*:\s*(\S+)/) || [])[1] || null;
|
||||||
|
}
|
||||||
if (!bc) {
|
if (!bc) {
|
||||||
console.log(`Code-barres illisible pour l'adresse ${ad} : on n'enregistre pas a l'aveugle.`);
|
console.log(`Code-barres illisible pour l'adresse ${ad} : on n'enregistre pas a l'aveugle.`);
|
||||||
console.log('Relancer la commande — la ligne aura eu le temps de se vider.');
|
console.log('Relancer la commande — la ligne aura eu le temps de se vider.');
|
||||||
@@ -411,6 +417,7 @@ async function main() {
|
|||||||
console.log('L\'heure enregistree est l\'heure REELLE de cette machine, pas celle du module');
|
console.log('L\'heure enregistree est l\'heure REELLE de cette machine, pas celle du module');
|
||||||
console.log('(l\'horloge interne du module derive de plusieurs heures).');
|
console.log('(l\'horloge interne du module derive de plusieurs heures).');
|
||||||
console.log('Ctrl-C pour arreter.\n');
|
console.log('Ctrl-C pour arreter.\n');
|
||||||
|
let sansSoh = false; // passe a vrai si le firmware ignore `soh` (US5000)
|
||||||
// Garde anti-blocage : si la ligne serie se fige (adaptateur qui ne repond
|
// Garde anti-blocage : si la ligne serie se fige (adaptateur qui ne repond
|
||||||
// plus, module eteint), les lectures reviennent VIDES sans jamais lever
|
// plus, module eteint), les lectures reviennent VIDES sans jamais lever
|
||||||
// d'erreur (lireJusqua resout au timeout au lieu de rejeter), et le CSV
|
// d'erreur (lireJusqua resout au timeout au lieu de rejeter), et le CSV
|
||||||
@@ -436,12 +443,25 @@ async function main() {
|
|||||||
try { puis = courantsParModule(await commande(l, 'pwr', 10000)); }
|
try { puis = courantsParModule(await commande(l, 'pwr', 10000)); }
|
||||||
catch { /* on garde les tensions */ }
|
catch { /* on garde les tensions */ }
|
||||||
for (const ad of adresses) {
|
for (const ad of adresses) {
|
||||||
const t = await commande(l, `soh ${ad}`, 10000);
|
|
||||||
// `soh` : "<n0based> <mV> <SOHCount> <statut>"
|
|
||||||
const c = [];
|
const c = [];
|
||||||
for (const ligne of t.split('\n')) {
|
if (!sansSoh) {
|
||||||
const m = ligne.trim().match(/^(\d{1,2})\s+(\d{4})\s+(\d+)\s+(\w+)/);
|
const t = await commande(l, `soh ${ad}`, 10000);
|
||||||
if (m) c.push({ n: +m[1] + 1, mV: +m[2] });
|
// `soh` : "<n0based> <mV> <SOHCount> <statut>"
|
||||||
|
for (const ligne of t.split('\n')) {
|
||||||
|
const m = ligne.trim().match(/^(\d{1,2})\s+(\d{4})\s+(\d+)\s+(\w+)/);
|
||||||
|
if (m) c.push({ n: +m[1] + 1, mV: +m[2] });
|
||||||
|
}
|
||||||
|
// ⚠️ Le firmware des US5000 (B69.26, 26/09/2026) repond « Unknown
|
||||||
|
// command 'soh' ». On bascule alors sur `bat`, pour toute la session.
|
||||||
|
if (/Unknown command/i.test(t)) sansSoh = true;
|
||||||
|
}
|
||||||
|
if (sansSoh) {
|
||||||
|
// `bat` : "<n0based> <mV> <mA> <temp> <etat> ..." (une ligne par cellule)
|
||||||
|
const t = await commande(l, `bat ${ad}`, 10000);
|
||||||
|
for (const ligne of t.split('\n')) {
|
||||||
|
const m = ligne.trim().match(/^(\d{1,2})\s+(\d{4})\s+(-?\d+)\s+(-?\d+)\s+([A-Za-z]+)/);
|
||||||
|
if (m) c.push({ n: +m[1] + 1, mV: +m[2] });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (c.length < 10) { console.log(`${horodate()} module ${ad} : illisible`); continue; }
|
if (c.length < 10) { console.log(`${horodate()} module ${ad} : illisible`); continue; }
|
||||||
const v = c.map((x) => x.mV);
|
const v = c.map((x) => x.mV);
|
||||||
|
|||||||
Reference in New Issue
Block a user