Separat status och mottagningsräknare för varje kanal.

Senaste råmeddelande, fel och ignoreringsorsaker.
Pausa visning, rensa och kopiera logg.
Uppdateras utan att störa inställningsfält eller tidtagning.
This commit is contained in:
larssand
2026-09-08 18:37:47 +02:00
parent 8aedf83826
commit a0c3d3039e
15 changed files with 265 additions and 14 deletions

View File

@@ -0,0 +1,50 @@
export const DIAGNOSTIC_ROLES = ["main", "split", "pit_entry", "pit_exit"];
export function createDecoderDiagnostics(now = Date.now) {
const channels = new Map();
function channel(role) {
const key = DIAGNOSTIC_ROLES.includes(role) ? role : "main";
if (!channels.has(key)) channels.set(key, { role: key, status: "disconnected", url: "", connectedAt: null, lastReceivedAt: null, frames: 0, messages: 0, passings: 0, handled: 0, ignored: 0, invalid: 0, parseErrors: 0, lastFrame: "", log: [] });
return channels.get(key);
}
function log(entry, reason, sample = "") {
entry.log.push({ at: now(), reason, sample: String(sample).slice(0, 512) });
if (entry.log.length > 50) entry.log.splice(0, entry.log.length - 50);
}
return {
socket(role, status, url = "") {
const entry = channel(role);
entry.status = status;
if (url) entry.url = String(url);
if (status === "connected") entry.connectedAt = now();
log(entry, status);
},
frame(role, data) {
const entry = channel(role);
entry.frames++;
entry.lastReceivedAt = now();
entry.lastFrame = String(data).slice(0, 512);
},
parseError(role) {
const entry = channel(role);
entry.parseErrors++;
log(entry, "invalid_json", entry.lastFrame);
},
message(role, msg, reason, outcome = "ignored") {
const entry = channel(role);
entry.messages++;
if (String(msg?.msg || msg?.type || "").toUpperCase() === "PASSING") entry.passings++;
if (["handled", "ignored", "invalid"].includes(outcome)) entry[outcome]++;
let sample = "";
try { sample = JSON.stringify({ type: msg?.msg || msg?.type, transponder: msg?.transponder ?? msg?.tran_code, rtc_time: msg?.rtc_time }); } catch {}
log(entry, reason, sample);
},
snapshot() { return DIAGNOSTIC_ROLES.map(role => { const entry = channel(role); return { ...entry, log: entry.log.map(item => ({ ...item })) }; }); },
clear(role) {
const entry = channel(role);
for (const key of ["frames", "messages", "passings", "handled", "ignored", "invalid", "parseErrors"]) entry[key] = 0;
entry.lastFrame = "";
entry.log = [];
},
};
}
export const decoderDiagnostics = createDecoderDiagnostics();