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:
@@ -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