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 ;
|
`pylontech-console.mjs --suffixe pack1` separe les CSV des deux lecteurs ;
|
||||||
`evcs_cellules` regarde 15 min (deux lecteurs non synchronises) et publie
|
`evcs_cellules` regarde 15 min (deux lecteurs non synchronises) et publie
|
||||||
`modules_lus`. Verifie : **9 modules en base, ecart max 2 mV**.
|
`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)*
|
*Document mis a jour le : 26/09/2026 — pack 1 sous surveillance cellule (poste yd-5CG6110DG0)*
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"refresh": 1,
|
"refresh": 1,
|
||||||
"includeAll": false,
|
"includeAll": false,
|
||||||
"multi": 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",
|
"format": "table",
|
||||||
"rawQuery": true,
|
"rawQuery": true,
|
||||||
"editorMode": "code",
|
"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": {
|
"gridPos": {
|
||||||
|
|||||||
@@ -49,3 +49,7 @@ services:
|
|||||||
- "600"
|
- "600"
|
||||||
- --modules
|
- --modules
|
||||||
- "1,2,3,4,5"
|
- "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',
|
statut: r.spread_max === null ? 'muet' : 'frais',
|
||||||
age_releve_min: age, seuil_perime_min: RELEVE_PERIME_MIN,
|
age_releve_min: age, seuil_perime_min: RELEVE_PERIME_MIN,
|
||||||
module_le_plus_sensible: r.weakest || null,
|
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
|
// 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
|
// lus (4 US5000 + 5 US2000C). Moins = un lecteur muet, l'ecart ne couvre
|
||||||
// alors qu'une partie du parc.
|
// alors qu'une partie du parc.
|
||||||
|
|||||||
@@ -1261,10 +1261,16 @@ vehicules = dashboard("evcs-vehicules", "Les vehicules", (
|
|||||||
VARIABLE_MODULE = {
|
VARIABLE_MODULE = {
|
||||||
"name": "module", "label": "Module", "type": "query", "datasource": PG,
|
"name": "module", "label": "Module", "type": "query", "datasource": PG,
|
||||||
"refresh": 1, "includeAll": False, "multi": False,
|
"refresh": 1, "includeAll": False, "multi": False,
|
||||||
"query": ("SELECT DISTINCT ON (module) "
|
# Repere = pack x 10 + rang (11 = pack 1 module 1, 21 = pack 2 module 1) :
|
||||||
"CASE WHEN rang IS NULL THEN module "
|
# chaque chaine numerote ses modules a partir de 1, le rang seul est ambigu.
|
||||||
"ELSE rang || ' \u00b7 ' || module END AS __text, "
|
"query": ("SELECT __text, __value FROM ("
|
||||||
"module AS __value FROM cellule ORDER BY module, ts DESC"),
|
"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.
|
# 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"
|
" SELECT module FROM cellule GROUP BY module\n"
|
||||||
" HAVING max(ts) > now() - interval '2 hours'),\n"
|
" HAVING max(ts) > now() - interval '2 hours'),\n"
|
||||||
"dernier AS (\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"
|
" WHERE module IN (SELECT module FROM vivants)\n"
|
||||||
" ORDER BY module, ts DESC),\n"
|
" ORDER BY module, ts DESC),\n"
|
||||||
"maintenant AS (\n"
|
"maintenant AS (\n"
|
||||||
@@ -1298,7 +1306,7 @@ batteries = dashboard("evcs-batteries", "Les batteries", (
|
|||||||
" SELECT module, ts, max(mv) - min(mv) AS ecart\n"
|
" SELECT module, ts, max(mv) - min(mv) AS ecart\n"
|
||||||
" FROM cellule WHERE $__timeFilter(ts) GROUP BY module, ts) x\n"
|
" FROM cellule WHERE $__timeFilter(ts) GROUP BY module, ts) x\n"
|
||||||
" GROUP BY module)\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"
|
" m.ecart AS \"Ecart maintenant\",\n"
|
||||||
" p.ecart_max AS \"Ecart max sur la periode\",\n"
|
" p.ecart_max AS \"Ecart max sur la periode\",\n"
|
||||||
" m.haute AS \"Plus haute\", m.basse AS \"Plus basse\",\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
|
// de 1, et un recablage du LVHUB peut tout renumeroter — la valeur figee
|
||||||
// mentirait sur l'historique. Absent des anciens releves => NULL.
|
// mentirait sur l'historique. Absent des anciens releves => NULL.
|
||||||
await client.query('ALTER TABLE cellule ADD COLUMN IF NOT EXISTS rang smallint');
|
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
|
// 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
|
// 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,
|
// 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.
|
// comme le courant : la position en fin de ligne n'est pas un contrat.
|
||||||
const iA = cols.indexOf('adresse');
|
const iA = cols.indexOf('adresse');
|
||||||
|
const pk = n.match(/-pack(\d)-suivi\.csv$/);
|
||||||
|
const pack = pk ? parseInt(pk[1], 10) : null;
|
||||||
const val = [];
|
const val = [];
|
||||||
for (const ligne of l.slice(1)) {
|
for (const ligne of l.slice(1)) {
|
||||||
const c = ligne.split(',');
|
const c = ligne.split(',');
|
||||||
@@ -1048,14 +1056,14 @@ async function ingererCellules(client) {
|
|||||||
for (let k = 0; k < 15; k++) {
|
for (let k = 0; k < 15; k++) {
|
||||||
const mv = Number(c[iC + k]);
|
const mv = Number(c[iC + k]);
|
||||||
if (!(mv > 1000)) continue;
|
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) {
|
for (let i = 0; i < val.length; i += 500) {
|
||||||
const lot = val.slice(i, 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(
|
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());
|
VALUES ${ph} ON CONFLICT (ts, module, cellule) DO NOTHING`, lot.flat());
|
||||||
lignes += r.rowCount;
|
lignes += r.rowCount;
|
||||||
}
|
}
|
||||||
@@ -1878,11 +1886,14 @@ async function publierCellulesHelpers(client) {
|
|||||||
-- ferait disparaitre un pack sur deux selon l'instant du calcul.
|
-- ferait disparaitre un pack sur deux selon l'instant du calcul.
|
||||||
recent AS (SELECT DISTINCT ON (module) module, ts FROM cellule
|
recent AS (SELECT DISTINCT ON (module) module, ts FROM cellule
|
||||||
WHERE ts > (SELECT t FROM mx) - interval '15 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
|
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
|
FROM cellule c JOIN recent rc ON rc.module = c.module AND rc.ts = c.ts
|
||||||
GROUP BY c.module)
|
GROUP BY c.module)
|
||||||
SELECT (SELECT max(ecart) FROM spreads) AS spread_max,
|
SELECT (SELECT max(ecart) FROM spreads) AS spread_max,
|
||||||
(SELECT module FROM spreads ORDER BY ecart DESC NULLS LAST LIMIT 1) AS weakest,
|
(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 round(EXTRACT(EPOCH FROM (now() - t)) / 60)::int FROM mx) AS age_min,
|
||||||
(SELECT count(*) FROM spreads)::int AS modules_lus`)
|
(SELECT count(*) FROM spreads)::int AS modules_lus`)
|
||||||
.catch(async (e) => { await muet('base injoignable').catch(() => {}); throw e; });
|
.catch(async (e) => { await muet('base injoignable').catch(() => {}); throw e; });
|
||||||
|
|||||||
Reference in New Issue
Block a user