UI verbessern 2

This commit is contained in:
chk
2026-06-14 18:38:33 +02:00
parent c23fbf75f2
commit fc4d2b3c84
4 changed files with 98 additions and 22 deletions

View File

@@ -890,14 +890,20 @@ function computeAndShowYAxis() {
// ── Daten laden ─────────────────────────────────────────────────────────────── // ── Daten laden ───────────────────────────────────────────────────────────────
/** Haupt-Run laden. Im Homing-Mode: immer neuester Homing-Run, kein Dropdown. */ /**
async function loadData() { * Haupt-Run laden.
* Im Homing-Mode: lädt spezifischen Run per runDir-Parameter (kein Dropdown).
* @param {string|null} specificRunDir Timestamp des Runs (nur im Homing-Mode genutzt)
*/
async function loadData(specificRunDir = null) {
const statusEl = document.getElementById('status'); const statusEl = document.getElementById('status');
statusEl.textContent = 'Laden …'; statusEl.textContent = 'Laden …';
let url; let url;
if (IS_HOMING) { if (IS_HOMING) {
url = '/api/board/latest?from=homing'; url = specificRunDir
? `/api/board/latest?from=homing&run=${encodeURIComponent(specificRunDir)}`
: '/api/board/latest?from=homing';
} else { } else {
const selRun = document.getElementById('sel-run-primary')?.value ?? ''; const selRun = document.getElementById('sel-run-primary')?.value ?? '';
url = selRun url = selRun
@@ -1071,7 +1077,11 @@ async function initAll() {
await loadPositionC(); // setzt _positionCFremdMarkers + berechnet Y-Achse await loadPositionC(); // setzt _positionCFremdMarkers + berechnet Y-Achse
} }
initAll(); if (IS_HOMING) {
document.getElementById('status').textContent = '→ Homing-Run starten …';
} else {
initAll();
}
document.getElementById('btnReload').addEventListener('click', initAll); document.getElementById('btnReload').addEventListener('click', initAll);
document.getElementById('sel-run-primary')?.addEventListener('change', async () => { document.getElementById('sel-run-primary')?.addEventListener('change', async () => {
await loadData(); await loadData();
@@ -1084,6 +1094,9 @@ document.getElementById('sel-run-compare')?.addEventListener('change', async ()
document.getElementById('sel-run-c')?.addEventListener('change', loadPositionC); document.getElementById('sel-run-c')?.addEventListener('change', loadPositionC);
window.addEventListener('message', async (e) => { window.addEventListener('message', async (e) => {
if (e.data?.type === 'reload') await initAll(); if (e.data?.type === 'reload') await initAll();
if (e.data?.type === 'load-homing-run' && IS_HOMING) {
await loadData(e.data.runDir);
}
if (e.data?.type === 'homing-state' && IS_HOMING) { if (e.data?.type === 'homing-state' && IS_HOMING) {
_homingAngles = e.data.state; _homingAngles = e.data.state;
if (_currentRobot) buildSkeletonFK(_currentRobot, _homingAngles); if (_currentRobot) buildSkeletonFK(_currentRobot, _homingAngles);

View File

@@ -378,30 +378,87 @@ function showHomingResult(state) {
} }
async function loadHomingImages(runDir) { async function loadHomingImages(runDir) {
const display = document.getElementById('snapshot-info-picture'); if (!runDir) return;
if (!display || !runDir) return;
try { try {
const res = await fetch(`/api/homing/run-data?run=${encodeURIComponent(runDir)}`); const res = await fetch(`/api/homing/run-data?run=${encodeURIComponent(runDir)}`);
if (!res.ok) return; if (!res.ok) return;
const data = await res.json(); const data = await res.json();
const debugImages = (data.images ?? []).filter(img => /debug/i.test(img.filename)); // ── Debug-Bilder ──────────────────────────────────────────────────────────
let html = '<div style="display:flex;flex-direction:column;gap:12px;margin-top:8px">'; const display = document.getElementById('snapshot-info-picture');
for (const img of debugImages) { if (display) {
html += `<figure style="margin:0"> const debugImages = (data.images ?? []).filter(img => /debug/i.test(img.filename));
<img src="data:image/jpeg;base64,${img.contentBase64}" let html = '<div style="display:flex;flex-direction:column;gap:12px;margin-top:8px">';
style="width:100%;max-width:760px;height:auto;border:1px solid #1f2937;border-radius:4px;display:block" for (const img of debugImages) {
alt="${img.filename}"> html += `<figure style="margin:0">
<figcaption style="font-size:11px;color:#64748b;margin-top:3px"> <img src="data:image/jpeg;base64,${img.contentBase64}"
${img.filename} style="width:100%;max-width:760px;height:auto;border:1px solid #1f2937;border-radius:4px;display:block"
</figcaption> alt="${img.filename}">
</figure>`; <figcaption style="font-size:11px;color:#64748b;margin-top:3px">
${img.filename}
</figcaption>
</figure>`;
}
html += '</div>';
display.innerHTML = html;
} }
html += '</div>';
display.innerHTML = html; // ── Marker-CSV-Tabelle ────────────────────────────────────────────────────
if (data.csvContent) renderHomingCsv(data.csvContent, runDir);
} catch { /* nicht kritisch */ } } catch { /* nicht kritisch */ }
} }
function renderHomingCsv(csvContent, runDir) {
const table = document.getElementById('snapshot-table');
const infoEl = document.getElementById('snapshot-info');
if (!table) return;
const lines = csvContent.trim().split(/\r?\n/).filter(Boolean);
if (lines.length < 2) return;
const headers = lines[0].split(',').map(h => h.trim());
const rows = lines.slice(1).map(line => {
const cells = line.split(',');
const obj = {};
headers.forEach((h, i) => {
const raw = (cells[i] ?? '').trim();
const num = Number(raw);
obj[h] = raw !== '' && Number.isFinite(num) ? num : raw;
});
return obj;
});
if (infoEl) infoEl.textContent = `aruco_marker_poses.csv · ${runDir} · ${rows.length} Marker`;
table.innerHTML = '';
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
headers.forEach(h => {
const th = document.createElement('th');
th.textContent = h;
headerRow.appendChild(th);
});
thead.appendChild(headerRow);
table.appendChild(thead);
const tbody = document.createElement('tbody');
rows.forEach(row => {
const tr = document.createElement('tr');
headers.forEach(h => {
const td = document.createElement('td');
let v = row[h];
if (typeof v === 'number') {
v = /^(marker_id|id|num_cameras|seen_by)$/.test(h) ? Math.round(v) : v.toFixed(1);
}
td.textContent = v ?? '';
tr.appendChild(td);
});
tbody.appendChild(tr);
});
table.appendChild(tbody);
}
async function runHoming() { async function runHoming() {
// UI zurücksetzen // UI zurücksetzen
clearTextarea('log'); clearTextarea('log');
@@ -487,7 +544,7 @@ async function runHoming() {
if (evt.runDir) await loadHomingImages(evt.runDir); if (evt.runDir) await loadHomingImages(evt.runDir);
const frame = document.getElementById('board-viewer-frame'); const frame = document.getElementById('board-viewer-frame');
if (frame?.contentWindow) { if (frame?.contentWindow) {
frame.contentWindow.postMessage({ type: 'reload' }, '*'); frame.contentWindow.postMessage({ type: 'load-homing-run', runDir: evt.runDir }, '*');
if (evt.state) { if (evt.state) {
frame.contentWindow.postMessage({ type: 'homing-state', state: evt.state }, '*'); frame.contentWindow.postMessage({ type: 'homing-state', state: evt.state }, '*');
} }

View File

@@ -89,7 +89,7 @@
<iframe <iframe
id="board-viewer-frame" id="board-viewer-frame"
src="/boardViewer.html?mode=homing" src="/boardViewer.html?mode=homing"
style="width:100%;height:600px;border:1px solid #334155;border-radius:6px;background:#0d0f13;display:block;margin-top:12px" style="width:100%;height:1200px;border:1px solid #334155;border-radius:6px;background:#0d0f13;display:block;margin-top:12px"
title="Board-Viewer" title="Board-Viewer"
></iframe> ></iframe>
</div> </div>

View File

@@ -839,7 +839,13 @@ app.get('/api/homing/run-data', async (req, res) => {
} catch {} } catch {}
} }
return res.json({ runDir: runName, images, finalState }); // aruco_marker_poses.csv für Snapshot-CSV-Tabelle
let csvContent = null;
try {
csvContent = await fsPromises.readFile(path.join(runDir, 'aruco_marker_poses.csv'), 'utf8');
} catch {}
return res.json({ runDir: runName, images, finalState, csvContent });
} catch (err) { } catch (err) {
return res.status(500).json({ error: String(err) }); return res.status(500).json({ error: String(err) });
} }