added Function to create CAN-Traces from WebUI
Some checks failed
CI-Build/Kettenoeler/pipeline/head There was a failure building this commit
Some checks failed
CI-Build/Kettenoeler/pipeline/head There was a failure building this commit
This commit is contained in:
@@ -5,6 +5,106 @@ var statusMapping;
|
||||
var staticMapping;
|
||||
var overlay;
|
||||
|
||||
let traceActive = false;
|
||||
let traceMode = null;
|
||||
let traceFileName = "";
|
||||
let traceUseFsAccess = false;
|
||||
|
||||
// File-System-Access-Stream (Chromium)
|
||||
let traceWriter = null;
|
||||
let traceEncoder = null;
|
||||
let traceWriteQueue = Promise.resolve(); // für geordnete Writes
|
||||
|
||||
// Fallback: In-Memory-Sammeln (für Blob-Download bei STOP)
|
||||
let traceMemParts = [];
|
||||
|
||||
// Textarea & Status
|
||||
const TRACE_MAX_CHARS = 200000; // ~200 KB für die Anzeige
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
function nowIsoCompact() {
|
||||
return new Date().toISOString().replace(/[:.]/g, "-");
|
||||
}
|
||||
function genTraceFileName(mode) {
|
||||
return `cantrace-${mode}-${nowIsoCompact()}.log`;
|
||||
}
|
||||
|
||||
function setTraceUI(active, mode, infoText) {
|
||||
traceActive = !!active;
|
||||
traceMode = active ? mode : null;
|
||||
|
||||
const btnRaw = $("trace-start");
|
||||
const btnObd = $("trace-start-obd");
|
||||
const btnStop = $("trace-stop");
|
||||
const status = $("trace-status");
|
||||
|
||||
if (btnRaw) btnRaw.disabled = active;
|
||||
if (btnObd) btnObd.disabled = active;
|
||||
if (btnStop) btnStop.disabled = !active;
|
||||
|
||||
if (status)
|
||||
status.textContent =
|
||||
infoText || (active ? `Trace aktiv (${mode})` : "Trace inaktiv");
|
||||
}
|
||||
|
||||
function traceClear() {
|
||||
const out = $("trace-out");
|
||||
if (out) out.value = "";
|
||||
}
|
||||
|
||||
function traceAppend(text) {
|
||||
const out = $("trace-out");
|
||||
if (!out || !text) return;
|
||||
out.value += text;
|
||||
if (out.value.length > TRACE_MAX_CHARS) {
|
||||
out.value = out.value.slice(-TRACE_MAX_CHARS);
|
||||
}
|
||||
out.scrollTop = out.scrollHeight;
|
||||
}
|
||||
|
||||
function triggerBlobDownload(filename, blob) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function parseKv(s) {
|
||||
const out = Object.create(null);
|
||||
s.split(";").forEach((part) => {
|
||||
const eq = part.indexOf("=");
|
||||
if (eq > 0) {
|
||||
const k = part.slice(0, eq).trim();
|
||||
const v = part.slice(eq + 1).trim();
|
||||
if (k) out[k] = v;
|
||||
}
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
// geordnete Writes auf File System Access Writer
|
||||
function writeToFs(chunk) {
|
||||
if (!traceUseFsAccess || !traceWriter) return;
|
||||
const data = traceEncoder
|
||||
? traceEncoder.encode(chunk)
|
||||
: new TextEncoder().encode(chunk);
|
||||
traceWriteQueue = traceWriteQueue
|
||||
.then(() => traceWriter.write(data))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
// Ihr JavaScript-Code hier, einschließlich der onLoad-Funktion
|
||||
overlay = document.getElementById("overlay");
|
||||
@@ -45,16 +145,32 @@ function initSettingInputs() {
|
||||
|
||||
function onOpen(event) {
|
||||
console.log("Connection opened");
|
||||
setTraceUI(false, null, "Verbunden – Trace inaktiv");
|
||||
}
|
||||
|
||||
function onClose(event) {
|
||||
console.log("Connection closed");
|
||||
setTimeout(initWebSocket, 1000);
|
||||
overlay.style.display = "flex";
|
||||
|
||||
// Falls Trace noch aktiv war: lokal finalisieren
|
||||
if (traceActive) {
|
||||
const note = "Trace beendet (Verbindung getrennt)";
|
||||
if (traceUseFsAccess && traceWriter) {
|
||||
traceWriteQueue.then(() => traceWriter.close()).catch(console.error);
|
||||
traceWriter = null;
|
||||
} else if (traceMemParts.length) {
|
||||
const blob = new Blob(traceMemParts, { type: "text/plain" });
|
||||
triggerBlobDownload(traceFileName || "cantrace.log", blob);
|
||||
traceMemParts = [];
|
||||
}
|
||||
setTraceUI(false, null, note);
|
||||
showNotification(note, "warning");
|
||||
}
|
||||
}
|
||||
|
||||
function sendButton(event) {
|
||||
var targetElement = event.target;
|
||||
async function sendButton(event) {
|
||||
const targetElement = event.target;
|
||||
|
||||
if (
|
||||
targetElement.classList.contains("confirm") &&
|
||||
@@ -62,7 +178,46 @@ function sendButton(event) {
|
||||
)
|
||||
return;
|
||||
|
||||
websocket_sendevent("btn-" + targetElement.id, targetElement.value);
|
||||
const wsid = targetElement.dataset.wsid || targetElement.id; // z.B. "trace-start"
|
||||
const val = targetElement.value || "";
|
||||
|
||||
// File-Ziel *vor* dem WS-Start öffnen (nur bei trace-start; wegen User-Gesture!)
|
||||
if (wsid === "trace-start") {
|
||||
const mode = val || "raw";
|
||||
traceFileName = genTraceFileName(mode);
|
||||
|
||||
// Anzeige schon mal leeren
|
||||
traceClear();
|
||||
setTraceUI(false, null, "Trace wird gestartet…");
|
||||
|
||||
traceUseFsAccess = false;
|
||||
traceWriter = null;
|
||||
traceEncoder = null;
|
||||
traceWriteQueue = Promise.resolve();
|
||||
traceMemParts = []; // Fallback-Puffer leeren
|
||||
|
||||
if (window.showSaveFilePicker) {
|
||||
try {
|
||||
const fh = await showSaveFilePicker({
|
||||
suggestedName: traceFileName,
|
||||
types: [
|
||||
{
|
||||
description: "Text Log",
|
||||
accept: { "text/plain": [".log", ".txt"] },
|
||||
},
|
||||
],
|
||||
});
|
||||
traceWriter = await fh.createWritable();
|
||||
traceEncoder = new TextEncoder();
|
||||
traceUseFsAccess = true;
|
||||
} catch (e) {
|
||||
// Nutzer hat evtl. abgebrochen → Fallback in RAM
|
||||
traceUseFsAccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
websocket_sendevent("btn-" + wsid, val);
|
||||
}
|
||||
|
||||
function onMessage(event) {
|
||||
@@ -101,6 +256,58 @@ function onMessage(event) {
|
||||
fillValuesToHTML(result);
|
||||
overlay.style.display = "none";
|
||||
}
|
||||
// --- Trace: Start ---
|
||||
else if (data.startsWith("STARTTRACE;")) {
|
||||
const kv = parseKv(data.slice(11)); // mode=..., ts=...
|
||||
const mode = kv.mode || "?";
|
||||
setTraceUI(true, mode, `Trace gestartet (${mode})`);
|
||||
|
||||
// Fallback: wenn kein FS-Access → in RAM sammeln
|
||||
// (sonst haben wir traceWriter bereits im Klick vorbereitet)
|
||||
}
|
||||
// --- Trace: Lines (ggf. mehrere in einer WS-Nachricht) ---
|
||||
else if (data.startsWith("TRACELINE;")) {
|
||||
const payload = data.replace(/TRACELINE;/g, ""); // reiner Text inkl. '\n'
|
||||
traceAppend(payload);
|
||||
if (traceUseFsAccess && traceWriter) {
|
||||
writeToFs(payload);
|
||||
} else {
|
||||
traceMemParts.push(payload);
|
||||
}
|
||||
}
|
||||
// --- Trace: Stop/Summary ---
|
||||
else if (data.startsWith("STOPTRACE;")) {
|
||||
const kv = parseKv(data.slice(10));
|
||||
const msg = `Trace beendet (${kv.mode || "?"}), Zeilen=${
|
||||
kv.lines || "0"
|
||||
}, Drops=${kv.drops || "0"}${kv.reason ? ", Grund=" + kv.reason : ""}`;
|
||||
|
||||
// Datei finalisieren
|
||||
if (traceUseFsAccess && traceWriter) {
|
||||
traceWriteQueue.then(() => traceWriter.close()).catch(console.error);
|
||||
traceWriter = null;
|
||||
} else if (traceMemParts.length) {
|
||||
const blob = new Blob(traceMemParts, { type: "text/plain" });
|
||||
triggerBlobDownload(traceFileName || "cantrace.log", blob);
|
||||
traceMemParts = [];
|
||||
}
|
||||
|
||||
setTraceUI(false, null, msg);
|
||||
showNotification(msg, "info");
|
||||
}
|
||||
// --- Busy/Fehler/Ack ---
|
||||
else if (data.startsWith("TRACEBUSY;")) {
|
||||
const kv = parseKv(data.slice(10));
|
||||
const owner = kv.owner ? " (Owner #" + kv.owner + ")" : "";
|
||||
showNotification("Trace bereits aktiv" + owner, "warning");
|
||||
} else if (data.startsWith("TRACEERROR;")) {
|
||||
const kv = parseKv(data.slice(11));
|
||||
showNotification("Trace-Fehler: " + (kv.msg || "unbekannt"), "danger");
|
||||
} else if (data.startsWith("TRACEACK;")) {
|
||||
// optional
|
||||
const kv = parseKv(data.slice(9));
|
||||
console.log("TRACEACK", kv);
|
||||
}
|
||||
}
|
||||
|
||||
function createMapping(mappingString) {
|
||||
|
Reference in New Issue
Block a user