diff --git a/public/calibration.js b/public/calibration.js index 57770a9..785cfc6 100644 --- a/public/calibration.js +++ b/public/calibration.js @@ -431,11 +431,16 @@ function initBoard() { result.innerHTML = `❌ ${data.error ?? `HTTP ${r.status}`}`; return; } if (data.numChanged === 0) { - result.innerHTML = 'Keine Marker im Z-Bereich gefunden.'; return; + result.innerHTML = 'Keine Marker im Z-Bereich gefunden (weder in robot.json noch im letzten 3b-Run).'; return; } - const moved = data.changes.filter(c => c.oldLink !== c.newLink).length; - result.innerHTML = `✅ ${data.numChanged} Marker geändert` + - (moved ? `, ${moved} verschoben` : '') + `.` + + const added = data.changes.filter(c => c.action === 'added').length; + const moved = data.changes.filter(c => c.action === 'updated' && c.oldLink !== c.newLink).length; + const updated = data.changes.filter(c => c.action === 'updated').length - moved; + const parts = []; + if (added) parts.push(`${added} neu hinzugefügt`); + if (moved) parts.push(`${moved} verschoben`); + if (updated) parts.push(`${updated} aktualisiert`); + result.innerHTML = `✅ ${data.numChanged} Marker: ${parts.join(', ')}.` + ` IDs: ${data.changes.map(c => c.markerId).join(', ')}`; loadBoardTable(); } catch (err) { diff --git a/server/editRobot.js b/server/editRobot.js index 960bf89..4fe5588 100644 --- a/server/editRobot.js +++ b/server/editRobot.js @@ -23,31 +23,32 @@ async function writeRobot(robotPath, data) { * Weist allen Markern, deren z-Position (mm) zwischen zMin und zMax liegt, * das angegebene Set und/oder den angegebenen Link zu. * - * - set (optional): Setzt den set-Wert des Markers. - * - link (optional): Verschiebt den Marker in diesen Link (wird ggf. angelegt). + * - set (optional): Setzt den set-Wert des Markers. + * - link (optional): Verschiebt den Marker in diesen Link (wird ggf. angelegt). + * - extraMarkers (optional): Triangulierte Marker aus aruco_marker_poses.json – + * werden als neue Einträge in robot.json hinzugefügt, + * wenn sie noch nicht vorhanden sind. * * Gibt { numChanged, changes[] } zurück. - * changes[]: { markerId, oldLink, newLink, oldSet, newSet } + * changes[]: { markerId, action, oldLink, newLink, oldSet, newSet } + * action: 'updated' | 'added' */ -export async function assignByZRange(robotPath, { zMin, zMax, set, link }) { +export async function assignByZRange(robotPath, { zMin, zMax, set, link, extraMarkers = [] }) { const robot = await readRobot(robotPath); const links = robot.links ?? {}; const changes = []; - // Snapshot aller Marker (mit aktuellem Link-Name) – vor der Mutation + const zLo = Number(zMin); + const zHi = Number(zMax); + + // ── Teil 1: Bestehende robot.json-Marker aktualisieren / verschieben ────────── const snapshot = []; for (const [linkName, linkData] of Object.entries(links)) { for (const marker of (linkData.markers ?? [])) { - if (Array.isArray(marker.position)) { - snapshot.push({ id: marker.id, currentLink: linkName }); - } + if (Array.isArray(marker.position)) snapshot.push({ id: marker.id, currentLink: linkName }); } } - // Nur Marker im Z-Fenster bearbeiten - const zLo = Number(zMin); - const zHi = Number(zMax); - for (const { id, currentLink } of snapshot) { const srcLinkData = links[currentLink]; const idx = (srcLinkData?.markers ?? []).findIndex(m => Number(m.id) === id); @@ -58,6 +59,7 @@ export async function assignByZRange(robotPath, { zMin, zMax, set, link }) { if (z < zLo || z > zHi) continue; const change = { + action: 'updated', markerId: marker.id, oldLink: currentLink, oldSet: marker.set ?? '', @@ -65,20 +67,12 @@ export async function assignByZRange(robotPath, { zMin, zMax, set, link }) { newSet: marker.set ?? '', }; - // Set setzen - if (set !== undefined && set !== '') { - marker.set = set; - change.newSet = set; - } + if (set !== undefined && set !== '') { marker.set = set; change.newSet = set; } - // Link wechseln if (link && link !== currentLink) { - // Aus altem Link entfernen srcLinkData.markers.splice(idx, 1); - - // Ziel-Link anlegen falls noch nicht vorhanden - if (!links[link]) links[link] = { markers: [] }; - if (!links[link].markers) links[link].markers = []; + if (!links[link]) links[link] = { markers: [] }; + if (!links[link].markers) links[link].markers = []; links[link].markers.push(marker); change.newLink = link; } @@ -86,6 +80,45 @@ export async function assignByZRange(robotPath, { zMin, zMax, set, link }) { changes.push(change); } + // ── Teil 2: Neue Marker aus 3b-Triangulation einfügen (noch nicht in robot.json) ── + // Diese haben keine Position in robot.json – wir übernehmen die gemessene Position. + if (link && extraMarkers.length > 0) { + const knownIds = new Set(); + for (const ld of Object.values(links)) { + for (const m of (ld.markers ?? [])) knownIds.add(Number(m.id)); + } + + for (const em of extraMarkers) { + const emId = Number(em.marker_id); + const emPos = em.position_mm; // [x_mm, y_mm, z_mm] robot-Koordinaten + if (knownIds.has(emId)) continue; // bereits in robot.json (evtl. gerade hinzugefügt) + if (!Array.isArray(emPos) || emPos.length < 3) continue; + + const z = Number(emPos[2]); + if (z < zLo || z > zHi) continue; + + const newMarker = { + id: emId, + position: emPos.map(v => Math.round(Number(v) * 100) / 100), // 2 Dezimalstellen + }; + if (set) newMarker.set = set; + + if (!links[link]) links[link] = { markers: [] }; + if (!links[link].markers) links[link].markers = []; + links[link].markers.push(newMarker); + knownIds.add(emId); + + changes.push({ + action: 'added', + markerId: emId, + oldLink: null, + oldSet: '', + newLink: link, + newSet: set ?? '', + }); + } + } + if (changes.length > 0) { robot.links = links; await writeRobot(robotPath, robot); diff --git a/server/server.js b/server/server.js index ddc4931..6c1f070 100755 --- a/server/server.js +++ b/server/server.js @@ -706,8 +706,23 @@ app.post('/api/robot/assign-by-z', async (req, res) => { if (!set && !link) { return res.status(400).json({ error: 'Mindestens set oder link muss angegeben werden' }); } - const result = await assignByZRange(ROBOT_JSON, { zMin, zMax, set, link }); - console.log(`robot/assign-by-z z=[${zMin}..${zMax}] set="${set}" link="${link}" → ${result.numChanged} geändert`); + + // Triangulierte Marker aus dem letzten Board-Run als Zusatzquelle für + // Marker, die noch nicht in robot.json stehen (z.B. neu entdeckte Marker) + let extraMarkers = []; + try { + const latestRun = await findLatestBoardRun(); + if (latestRun) { + const posesPath = path.join(boardDataDir, latestRun, 'aruco_marker_poses.json'); + const poses = JSON.parse(await fsPromises.readFile(posesPath, 'utf8')); + extraMarkers = poses.markers ?? []; + } + } catch { /* kein 3b-Output vorhanden – nur bestehende robot.json-Marker bearbeiten */ } + + const result = await assignByZRange(ROBOT_JSON, { zMin, zMax, set, link, extraMarkers }); + const added = result.changes.filter(c => c.action === 'added').length; + const updated = result.changes.filter(c => c.action === 'updated').length; + console.log(`robot/assign-by-z z=[${zMin}..${zMax}] set="${set}" link="${link}" → ${updated} aktualisiert, ${added} neu (von ${extraMarkers.length} 3b-Markern)`); return res.json(result); } catch (err) { console.error('robot/assign-by-z error:', err);