Repere des modules = pack x 10 + rang (11 = pack 1 module 1, 21 = pack 2 module 1)
Colonne cellule.pack deduite du nom du fichier de releve ; Grafana et sensor.evcs_cellules l'affichent. Historique renseigne depuis le 24/09. 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:
@@ -678,6 +678,13 @@ veille neutralises. Trois defauts trouves en le mettant en service :
|
||||
`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**.
|
||||
**Repere des modules (choix du proprietaire) : pack x 10 + rang** — 11..14 =
|
||||
US5000 du pack 1, 21..25 = US2000C du pack 2 (chaque chaine numerote a partir de
|
||||
1, le rang seul etait ambigu). Colonne `cellule.pack` deduite du nom du fichier
|
||||
(`-pack1-` / `-pack2-`, le Synology ecrit desormais avec `--suffixe pack2`) ;
|
||||
historique renseigne seulement depuis le 24/09 (pack 2) et le 26/09 (pack 1),
|
||||
NULL avant faute de certitude. Grafana (colonne « Repere », menu des modules) et
|
||||
`sensor.evcs_cellules` (`repere_le_plus_sensible`) l'affichent.
|
||||
|
||||
---
|
||||
*Document mis a jour le : 26/09/2026 — pack 1 sous surveillance cellule (poste yd-5CG6110DG0)*
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"refresh": 1,
|
||||
"includeAll": false,
|
||||
"multi": false,
|
||||
"query": "SELECT DISTINCT ON (module) CASE WHEN rang IS NULL THEN module ELSE rang || ' · ' || module END AS __text, module AS __value FROM cellule ORDER BY module, ts DESC"
|
||||
"query": "SELECT __text, __value FROM (SELECT DISTINCT ON (module) CASE WHEN pack IS NOT NULL AND rang IS NOT NULL THEN (pack * 10 + rang) || ' · ' || module WHEN rang IS NOT NULL THEN rang || ' · ' || module ELSE module END AS __text, module AS __value FROM cellule ORDER BY module, ts DESC) v ORDER BY __text"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -91,7 +91,7 @@
|
||||
"format": "table",
|
||||
"rawQuery": true,
|
||||
"editorMode": "code",
|
||||
"rawSql": "WITH vivants AS (\n SELECT module FROM cellule GROUP BY module\n HAVING max(ts) > now() - interval '2 hours'),\ndernier AS (\n SELECT DISTINCT ON (module) module, ts, rang FROM cellule\n WHERE module IN (SELECT module FROM vivants)\n ORDER BY module, ts DESC),\nmaintenant AS (\n SELECT c.module, max(c.mv) - min(c.mv) AS ecart,\n max(c.mv) AS haute, min(c.mv) AS basse,\n max(c.courant_ma) AS ma, d.ts, d.rang\n FROM cellule c JOIN dernier d ON d.module = c.module AND d.ts = c.ts\n GROUP BY c.module, d.ts, d.rang),\nperiode AS (\n SELECT module, max(ecart) AS ecart_max FROM (\n SELECT module, ts, max(mv) - min(mv) AS ecart\n FROM cellule WHERE $__timeFilter(ts) GROUP BY module, ts) x\n GROUP BY module)\nSELECT m.rang AS \"Rang\", m.module AS \"Module\",\n m.ecart AS \"Ecart maintenant\",\n p.ecart_max AS \"Ecart max sur la periode\",\n m.haute AS \"Plus haute\", m.basse AS \"Plus basse\",\n round(m.ma / 1000.0, 1) AS \"Courant (A)\",\n to_char(m.ts, 'DD/MM HH24:MI') AS \"Dernier releve\"\n FROM maintenant m LEFT JOIN periode p USING (module)\n ORDER BY m.ecart DESC"
|
||||
"rawSql": "WITH vivants AS (\n SELECT module FROM cellule GROUP BY module\n HAVING max(ts) > now() - interval '2 hours'),\ndernier AS (\n SELECT DISTINCT ON (module) module, ts,\n CASE WHEN pack IS NOT NULL AND rang IS NOT NULL THEN pack * 10 + rang\n ELSE rang END AS rang FROM cellule\n WHERE module IN (SELECT module FROM vivants)\n ORDER BY module, ts DESC),\nmaintenant AS (\n SELECT c.module, max(c.mv) - min(c.mv) AS ecart,\n max(c.mv) AS haute, min(c.mv) AS basse,\n max(c.courant_ma) AS ma, d.ts, d.rang\n FROM cellule c JOIN dernier d ON d.module = c.module AND d.ts = c.ts\n GROUP BY c.module, d.ts, d.rang),\nperiode AS (\n SELECT module, max(ecart) AS ecart_max FROM (\n SELECT module, ts, max(mv) - min(mv) AS ecart\n FROM cellule WHERE $__timeFilter(ts) GROUP BY module, ts) x\n GROUP BY module)\nSELECT m.rang AS \"Repere\", m.module AS \"Module\",\n m.ecart AS \"Ecart maintenant\",\n p.ecart_max AS \"Ecart max sur la periode\",\n m.haute AS \"Plus haute\", m.basse AS \"Plus basse\",\n round(m.ma / 1000.0, 1) AS \"Courant (A)\",\n to_char(m.ts, 'DD/MM HH24:MI') AS \"Dernier releve\"\n FROM maintenant m LEFT JOIN periode p USING (module)\n ORDER BY m.ecart DESC"
|
||||
}
|
||||
],
|
||||
"gridPos": {
|
||||
|
||||
@@ -49,3 +49,7 @@ services:
|
||||
- "600"
|
||||
- --modules
|
||||
- "1,2,3,4,5"
|
||||
# Fichiers ...-pack2-suivi.csv : l'ingestion en deduit le pack, et le
|
||||
# repere affiche pack x 10 + rang (21..25 ici, 11..14 pour les US5000).
|
||||
- --suffixe
|
||||
- pack2
|
||||
|
||||
@@ -132,6 +132,8 @@ 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,
|
||||
// Repere pack x 10 + rang (11 = pack 1 module 1, 21 = pack 2 module 1).
|
||||
...(r.weakest_repere ? { repere_le_plus_sensible: Number(r.weakest_repere) } : {}),
|
||||
// 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.
|
||||
|
||||
@@ -1261,10 +1261,16 @@ vehicules = dashboard("evcs-vehicules", "Les vehicules", (
|
||||
VARIABLE_MODULE = {
|
||||
"name": "module", "label": "Module", "type": "query", "datasource": PG,
|
||||
"refresh": 1, "includeAll": False, "multi": False,
|
||||
"query": ("SELECT DISTINCT ON (module) "
|
||||
"CASE WHEN rang IS NULL THEN module "
|
||||
"ELSE rang || ' \u00b7 ' || module END AS __text, "
|
||||
"module AS __value FROM cellule ORDER BY module, ts DESC"),
|
||||
# Repere = pack x 10 + rang (11 = pack 1 module 1, 21 = pack 2 module 1) :
|
||||
# chaque chaine numerote ses modules a partir de 1, le rang seul est ambigu.
|
||||
"query": ("SELECT __text, __value FROM ("
|
||||
"SELECT DISTINCT ON (module) "
|
||||
"CASE WHEN pack IS NOT NULL AND rang IS NOT NULL "
|
||||
"THEN (pack * 10 + rang) || ' \u00b7 ' || module "
|
||||
"WHEN rang IS NOT NULL THEN rang || ' \u00b7 ' || module "
|
||||
"ELSE module END AS __text, "
|
||||
"module AS __value FROM cellule ORDER BY module, ts DESC) v "
|
||||
"ORDER BY __text"),
|
||||
}
|
||||
|
||||
# Echelle d'alarme du materiel, lue dans `sysinfo` du LVHUB le 05/09/2026.
|
||||
@@ -1284,7 +1290,9 @@ batteries = dashboard("evcs-batteries", "Les batteries", (
|
||||
" SELECT module FROM cellule GROUP BY module\n"
|
||||
" HAVING max(ts) > now() - interval '2 hours'),\n"
|
||||
"dernier AS (\n"
|
||||
" SELECT DISTINCT ON (module) module, ts, rang FROM cellule\n"
|
||||
" SELECT DISTINCT ON (module) module, ts,\n"
|
||||
" CASE WHEN pack IS NOT NULL AND rang IS NOT NULL THEN pack * 10 + rang\n"
|
||||
" ELSE rang END AS rang FROM cellule\n"
|
||||
" WHERE module IN (SELECT module FROM vivants)\n"
|
||||
" ORDER BY module, ts DESC),\n"
|
||||
"maintenant AS (\n"
|
||||
@@ -1298,7 +1306,7 @@ batteries = dashboard("evcs-batteries", "Les batteries", (
|
||||
" SELECT module, ts, max(mv) - min(mv) AS ecart\n"
|
||||
" FROM cellule WHERE $__timeFilter(ts) GROUP BY module, ts) x\n"
|
||||
" GROUP BY module)\n"
|
||||
"SELECT m.rang AS \"Rang\", m.module AS \"Module\",\n"
|
||||
"SELECT m.rang AS \"Repere\", m.module AS \"Module\",\n"
|
||||
" m.ecart AS \"Ecart maintenant\",\n"
|
||||
" p.ecart_max AS \"Ecart max sur la periode\",\n"
|
||||
" m.haute AS \"Plus haute\", m.basse AS \"Plus basse\",\n"
|
||||
|
||||
@@ -662,6 +662,12 @@ async function migrer(client) {
|
||||
// de 1, et un recablage du LVHUB peut tout renumeroter — la valeur figee
|
||||
// mentirait sur l'historique. Absent des anciens releves => NULL.
|
||||
await client.query('ALTER TABLE cellule ADD COLUMN IF NOT EXISTS rang smallint');
|
||||
// Le PACK d'origine (26/09/2026), lu dans le nom du fichier (-pack1-, -pack2-).
|
||||
// Depuis que deux lecteurs relevent deux chaines, le rang seul est ambigu :
|
||||
// chaque chaine numerote ses modules a partir de 1. Repere affiche =
|
||||
// pack x 10 + rang (11 = pack 1 module 1, 21 = pack 2 module 1), choix du
|
||||
// proprietaire. NULL pour les releves anterieurs dont le pack n'est pas sur.
|
||||
await client.query('ALTER TABLE cellule ADD COLUMN IF NOT EXISTS pack smallint');
|
||||
|
||||
// Ce que le CONSEILLER a observe. Ses helpers sont ecrases toutes les
|
||||
// quinze minutes : sans cette table, un signalement grave emis a 3 h du
|
||||
@@ -1034,6 +1040,8 @@ async function ingererCellules(client) {
|
||||
// Le rang dans la chaine, ajoute au releve le 18/09/2026 — par son NOM,
|
||||
// comme le courant : la position en fin de ligne n'est pas un contrat.
|
||||
const iA = cols.indexOf('adresse');
|
||||
const pk = n.match(/-pack(\d)-suivi\.csv$/);
|
||||
const pack = pk ? parseInt(pk[1], 10) : null;
|
||||
const val = [];
|
||||
for (const ligne of l.slice(1)) {
|
||||
const c = ligne.split(',');
|
||||
@@ -1048,14 +1056,14 @@ async function ingererCellules(client) {
|
||||
for (let k = 0; k < 15; k++) {
|
||||
const mv = Number(c[iC + k]);
|
||||
if (!(mv > 1000)) continue;
|
||||
val.push([c[0], c[1], k + 1, mv, null, courant, null, 'suivi', rang]);
|
||||
val.push([c[0], c[1], k + 1, mv, null, courant, null, 'suivi', rang, pack]);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < val.length; i += 500) {
|
||||
const lot = val.slice(i, i + 500);
|
||||
const ph = lot.map((_, k) => `($${k * 9 + 1},$${k * 9 + 2},$${k * 9 + 3},$${k * 9 + 4},$${k * 9 + 5},$${k * 9 + 6},$${k * 9 + 7},$${k * 9 + 8},$${k * 9 + 9})`).join(',');
|
||||
const ph = lot.map((_, k) => `(${Array.from({ length: 10 }, (_, j) => `$${k * 10 + j + 1}`).join(',')})`).join(',');
|
||||
const r = await client.query(
|
||||
`INSERT INTO cellule (ts, module, cellule, mv, soc, courant_ma, etat, source, rang)
|
||||
`INSERT INTO cellule (ts, module, cellule, mv, soc, courant_ma, etat, source, rang, pack)
|
||||
VALUES ${ph} ON CONFLICT (ts, module, cellule) DO NOTHING`, lot.flat());
|
||||
lignes += r.rowCount;
|
||||
}
|
||||
@@ -1878,11 +1886,14 @@ async function publierCellulesHelpers(client) {
|
||||
-- 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 '15 minutes' ORDER BY module, ts DESC),
|
||||
spreads AS (SELECT c.module, max(c.mv) - min(c.mv) AS ecart
|
||||
spreads AS (SELECT c.module, max(c.mv) - min(c.mv) AS ecart,
|
||||
max(CASE WHEN c.pack IS NOT NULL AND c.rang IS NOT NULL
|
||||
THEN c.pack * 10 + c.rang END) AS repere
|
||||
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 repere FROM spreads ORDER BY ecart DESC NULLS LAST LIMIT 1) AS weakest_repere,
|
||||
(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; });
|
||||
|
||||
Reference in New Issue
Block a user