diff --git a/docker-compose.yaml b/docker-compose.yaml index d1ca47c..d040ed3 100755 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -17,5 +17,5 @@ services: ports: - "2093:2093" command: > - /bin/bash -lc "npm ci || npm install && node server/server.js" + /bin/bash -lc "apt-get update -qq && apt-get install -y --no-install-recommends python3-pip && pip3 install --quiet --no-cache-dir opencv-python-headless numpy && npm ci || npm install && node server/server.js" restart: unless-stopped diff --git a/public/calibration.html b/public/calibration.html index 3fa5157..8f62262 100644 --- a/public/calibration.html +++ b/public/calibration.html @@ -235,7 +235,7 @@ - + @@ -451,6 +451,27 @@ } }); + // "NPZ speichern" → an Webcam-Service übertragen + document.getElementById('btn-upload-npz').addEventListener('click', async () => { + const camera = document.getElementById('cam-select-calib').value; + if (!camera) { logC('⚠ Bitte zuerst eine Kamera auswählen.'); return; } + + logC(`NPZ wird an Webcam-Service übertragen (${camera}) …`); + try { + const r = await fetch('/api/calibration/upload-npz', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ camera }), + }); + const d = await r.json(); + if (d.error) { logC(`❌ Fehler: ${d.error}`); return; } + logC(`✅ Gespeichert: ${d.webcam?.saved} (${d.size} Bytes)`); + logC(` calibrationUrl: ${d.webcam?.calibrationUrl}`); + } catch (err) { + logC(`❌ Fehler: ${err}`); + } + }); + // "Kalibrierung berechnen" – SSE-Stream lesen document.getElementById('btn-compute-calib').addEventListener('click', async () => { const camera = document.getElementById('cam-select-calib').value; diff --git a/server/server.js b/server/server.js index 107e284..0ea7090 100755 --- a/server/server.js +++ b/server/server.js @@ -426,6 +426,54 @@ app.post('/api/calibration/compute', async (req, res) => { } }); +/** + * POST /api/calibration/upload-npz + * Liest {camera}_calibration.npz aus der aktuellen Session und + * schickt sie per PUT an den Webcam-Service. + * Body: { camera: "cam0" } + */ +app.post('/api/calibration/upload-npz', async (req, res) => { + try { + const { camera } = req.body ?? {}; + if (!camera) return res.status(400).json({ error: '"camera" parameter fehlt' }); + if (!WEBCAM_URL) return res.status(501).json({ error: 'WEBCAM_URL ist nicht konfiguriert' }); + + const session = await findLatestCalibSession(); + if (!session) return res.status(400).json({ error: 'Keine Kalibrierungs-Session vorhanden' }); + + const npzPath = path.join(calibDataDir, session, `${camera}_calibration.npz`); + + try { + await fsPromises.access(npzPath); + } catch { + return res.status(404).json({ + error: `Datei nicht gefunden: ${camera}_calibration.npz — bitte zuerst "Kalibrierung berechnen".` + }); + } + + const npzData = await fsPromises.readFile(npzPath); + const putUrl = new URL(`/api/cameras/${camera}/calibration`, WEBCAM_URL).toString(); + + const putRes = await fetch(putUrl, { + method: 'PUT', + headers: { 'Content-Type': 'application/octet-stream' }, + body: npzData, + }); + + if (!putRes.ok) { + const text = await putRes.text(); + return res.status(putRes.status).json({ error: `Webcam-Service: ${putRes.status} – ${text}` }); + } + + const result = await putRes.json(); + return res.json({ ok: true, camera, session, size: npzData.length, webcam: result }); + + } catch (err) { + console.error('calibration/upload-npz error:', err); + return res.status(500).json({ error: String(err) }); + } +}); + async function checkServiceReachability(name, url) { try { const controller = new AbortController();