API für .npz empfang

This commit is contained in:
chk
2026-06-10 09:36:43 +02:00
parent fec36149bf
commit 81f28fcea6
3 changed files with 104 additions and 5 deletions

View File

@@ -1,8 +1,16 @@
'use strict';
const express = require('express');
const fs = require('fs');
const path = require('path');
const { readJpegWidth } = require('./cameraSwitch');
// Datum → "YYYYMMDD_HHMMSS"
function formatTs(date) {
const p = n => String(n).padStart(2, '0');
return `${date.getFullYear()}${p(date.getMonth() + 1)}${p(date.getDate())}_${p(date.getHours())}${p(date.getMinutes())}${p(date.getSeconds())}`;
}
// Stabile Schnittstellen für Viewer und Homing-Projekt lesen NUR aus den
// CameraSwitch-Instanzen (RAM-Puffer + Event-Stream). Kein Gerätezugriff hier,
// keine go2rtc-Abhängigkeit mehr.
@@ -190,9 +198,10 @@ function createStreamRouter(switches) {
return router;
}
// GET /api/cameras → Kamera-Metadaten (ohne device-Pfad)
// GET /api/cameras/:id/calibration → .npz aus RAM (geladen beim Start)
function createCamerasRouter(cameras, calibrations = {}) {
// GET /api/cameras → Kamera-Metadaten (ohne device-Pfad)
// GET /api/cameras/:id/calibration → .npz aus RAM (geladen beim Start)
// PUT /api/cameras/:id/calibration → neue .npz ablegen; schreibt Archiv + aktuell
function createCamerasRouter(cameras, calibrations = {}, { calibDir = null } = {}) {
const router = express.Router();
router.get('/', (_req, res) => {
@@ -216,6 +225,49 @@ function createCamerasRouter(cameras, calibrations = {}) {
res.end(buf);
});
// PUT: Homing-Prozess liefert neue Kalibrierung.
// Speichert: calibration_YYYYMMDD_HHMMSS.npz (Archiv)
// + calibration.npz (aktuell, Kopie des Archivs)
// Migration: existiert nur calibration.npz (noch kein Archiv), wird es
// anhand seines mtime in calibration_<ts>.npz umbenannt.
router.put('/:id/calibration',
express.raw({ type: 'application/octet-stream', limit: '10mb' }),
(req, res) => {
const { id } = req.params;
if (!cameras.some(c => c.id === id)) {
return res.status(404).json({ error: `Unbekannte Kamera: ${id}` });
}
const buf = req.body;
if (!Buffer.isBuffer(buf) || buf.length === 0) {
return res.status(400).json({ error: 'Body muss eine .npz-Datei als application/octet-stream sein' });
}
if (!calibDir) {
return res.status(500).json({ error: 'calibDir nicht konfiguriert' });
}
const dir = path.join(calibDir, id);
const mainPath = path.join(dir, 'calibration.npz');
fs.mkdirSync(dir, { recursive: true });
// Migration: vorhandenes calibration.npz ohne Timestamp ins Archiv überführen
const hasArchive = fs.readdirSync(dir).some(f => /^calibration_\d{8}_\d{6}\.npz$/.test(f));
if (!hasArchive && fs.existsSync(mainPath)) {
const mtime = fs.statSync(mainPath).mtime;
fs.renameSync(mainPath, path.join(dir, `calibration_${formatTs(mtime)}.npz`));
}
// Neue Datei: Archiv-Kopie + aktuell
const tsName = `calibration_${formatTs(new Date())}.npz`;
fs.writeFileSync(path.join(dir, tsName), buf);
fs.writeFileSync(mainPath, buf);
// In-Memory sofort aktualisieren → GET liefert sofort die neue Datei
calibrations[id] = buf;
res.json({ id, saved: tsName, size: buf.length, calibrationUrl: `/api/cameras/${id}/calibration` });
},
);
return router;
}