diff --git a/public/boardViewer.html b/public/boardViewer.html index 9e51b0a..a9f83d8 100644 --- a/public/boardViewer.html +++ b/public/boardViewer.html @@ -890,14 +890,20 @@ function computeAndShowYAxis() { // ── 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'); statusEl.textContent = 'Laden …'; let url; 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 { const selRun = document.getElementById('sel-run-primary')?.value ?? ''; url = selRun @@ -1071,7 +1077,11 @@ async function initAll() { 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('sel-run-primary')?.addEventListener('change', async () => { await loadData(); @@ -1084,6 +1094,9 @@ document.getElementById('sel-run-compare')?.addEventListener('change', async () document.getElementById('sel-run-c')?.addEventListener('change', loadPositionC); window.addEventListener('message', async (e) => { 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) { _homingAngles = e.data.state; if (_currentRobot) buildSkeletonFK(_currentRobot, _homingAngles); diff --git a/public/client.js b/public/client.js index 1f4b467..b1221c4 100755 --- a/public/client.js +++ b/public/client.js @@ -378,30 +378,87 @@ function showHomingResult(state) { } async function loadHomingImages(runDir) { - const display = document.getElementById('snapshot-info-picture'); - if (!display || !runDir) return; + if (!runDir) return; try { const res = await fetch(`/api/homing/run-data?run=${encodeURIComponent(runDir)}`); if (!res.ok) return; const data = await res.json(); - const debugImages = (data.images ?? []).filter(img => /debug/i.test(img.filename)); - let html = '
'; - for (const img of debugImages) { - html += `
- ${img.filename} -
- ${img.filename} -
-
`; + // ── Debug-Bilder ────────────────────────────────────────────────────────── + const display = document.getElementById('snapshot-info-picture'); + if (display) { + const debugImages = (data.images ?? []).filter(img => /debug/i.test(img.filename)); + let html = '
'; + for (const img of debugImages) { + html += `
+ ${img.filename} +
+ ${img.filename} +
+
`; + } + html += '
'; + display.innerHTML = html; } - html += '
'; - display.innerHTML = html; + + // ── Marker-CSV-Tabelle ──────────────────────────────────────────────────── + if (data.csvContent) renderHomingCsv(data.csvContent, runDir); } 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() { // UI zurücksetzen clearTextarea('log'); @@ -487,7 +544,7 @@ async function runHoming() { if (evt.runDir) await loadHomingImages(evt.runDir); const frame = document.getElementById('board-viewer-frame'); if (frame?.contentWindow) { - frame.contentWindow.postMessage({ type: 'reload' }, '*'); + frame.contentWindow.postMessage({ type: 'load-homing-run', runDir: evt.runDir }, '*'); if (evt.state) { frame.contentWindow.postMessage({ type: 'homing-state', state: evt.state }, '*'); } diff --git a/public/index.html b/public/index.html index 5822f70..b0bc400 100755 --- a/public/index.html +++ b/public/index.html @@ -89,7 +89,7 @@ diff --git a/server/server.js b/server/server.js index 8b8118f..745a7ce 100755 --- a/server/server.js +++ b/server/server.js @@ -839,7 +839,13 @@ app.get('/api/homing/run-data', async (req, res) => { } 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) { return res.status(500).json({ error: String(err) }); }