This commit is contained in:
chk
2026-06-10 11:20:10 +02:00
parent 3741ad9f6a
commit 9650dee02e
2 changed files with 261 additions and 14 deletions

View File

@@ -194,6 +194,144 @@ app.post('/api/estimate', async (req, res) => {
}
});
// ── Kalibrierung ─────────────────────────────────────────────────────────────
const calibDataDir = path.join(__dirname, '..', 'data', 'calibration');
/** Timestamp-String im Format YYYYMMDD_HHmmss */
function makeTimestamp() {
const now = new Date();
const p = (n, l = 2) => String(n).padStart(l, '0');
return `${now.getFullYear()}${p(now.getMonth() + 1)}${p(now.getDate())}_${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}`;
}
/** Neueste Kalibrierungs-Session (Verzeichnisname) oder null */
async function findLatestCalibSession() {
try {
await fsPromises.access(calibDataDir);
const entries = await fsPromises.readdir(calibDataDir, { withFileTypes: true });
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name).sort().reverse();
return dirs[0] ?? null;
} catch {
return null;
}
}
/** Liest meta.json einer Session */
async function readCalibMeta(sessionName) {
try {
const raw = await fsPromises.readFile(path.join(calibDataDir, sessionName, 'meta.json'), 'utf8');
return JSON.parse(raw);
} catch {
return null;
}
}
/** Schreibt meta.json einer Session */
async function writeCalibMeta(sessionName, meta) {
await fsPromises.writeFile(
path.join(calibDataDir, sessionName, 'meta.json'),
JSON.stringify(meta, null, 2),
'utf8'
);
}
/**
* Holt Snapshots aller verfügbaren Kameras und speichert sie im Session-Verzeichnis.
* Dateiname: {cameraId}_{setNr}.jpg (setNr = 001, 002, …)
*/
async function capturePhotos(sessionName) {
if (!WEBCAM_URL) throw new Error('WEBCAM_URL ist nicht konfiguriert keine Kameras erreichbar');
const wc = new WebcamClient(WEBCAM_URL);
const data = await wc.getCameras();
const cameraIds = (data.cameras ?? []).map(c => c.id);
if (cameraIds.length === 0) throw new Error('Keine Kameras vom WebCam-Service gemeldet');
// Nächste Set-Nummer bestimmen (höchste vorhandene + 1)
const sessionDir = path.join(calibDataDir, sessionName);
const existing = await fsPromises.readdir(sessionDir);
const maxSet = existing.reduce((max, f) => {
const m = f.match(/_(\d+)\.jpg$/);
return m ? Math.max(max, parseInt(m[1], 10)) : max;
}, 0);
const setNr = String(maxSet + 1).padStart(3, '0');
const savedFiles = [];
for (const camId of cameraIds) {
const response = await new WebcamClient(WEBCAM_URL).getSnapshot(camId, true);
const buffer = Buffer.from(await response.arrayBuffer());
const filename = `${camId}_${setNr}.jpg`;
await fsPromises.writeFile(path.join(sessionDir, filename), buffer);
savedFiles.push(filename);
}
return { cameraIds, savedFiles, setNr: parseInt(setNr, 10) };
}
/** GET /api/calibration/current — aktuelle Session-Info */
app.get('/api/calibration/current', async (req, res) => {
try {
const session = await findLatestCalibSession();
if (!session) return res.json({ session: null, meta: null });
const meta = await readCalibMeta(session);
return res.json({ session, meta });
} catch (err) {
return res.status(500).json({ error: String(err) });
}
});
/** POST /api/calibration/new — neue Session anlegen + erste Fotos */
app.post('/api/calibration/new', async (req, res) => {
try {
const ts = makeTimestamp();
const sessionDir = path.join(calibDataDir, ts);
await fsPromises.mkdir(sessionDir, { recursive: true });
const meta = { timestamp: ts, createdAt: new Date().toISOString(), cameras: [], imageSets: 0, imageCount: 0 };
await writeCalibMeta(ts, meta);
try {
const capture = await capturePhotos(ts);
meta.cameras = capture.cameraIds;
meta.imageSets = capture.setNr;
meta.imageCount = capture.setNr * capture.cameraIds.length;
await writeCalibMeta(ts, meta);
return res.json({ session: ts, meta, savedFiles: capture.savedFiles });
} catch (captureErr) {
// Session angelegt, aber Fotos nicht verfügbar → trotzdem OK zurück
return res.json({ session: ts, meta, warning: String(captureErr) });
}
} catch (err) {
return res.status(500).json({ error: String(err) });
}
});
/** POST /api/calibration/foto — weitere Fotos für aktuelle Session */
app.post('/api/calibration/foto', async (req, res) => {
try {
const session = await findLatestCalibSession();
if (!session) {
return res.status(400).json({ error: 'Keine Session vorhanden. Bitte zuerst "Neue Kalibrierung anlegen".' });
}
const capture = await capturePhotos(session);
const meta = await readCalibMeta(session) ?? {
timestamp: session, createdAt: new Date().toISOString(),
cameras: [], imageSets: 0, imageCount: 0
};
meta.cameras = capture.cameraIds;
meta.imageSets = capture.setNr;
meta.imageCount = capture.setNr * capture.cameraIds.length;
await writeCalibMeta(session, meta);
return res.json({ session, meta, savedFiles: capture.savedFiles });
} catch (err) {
return res.status(500).json({ error: String(err) });
}
});
async function checkServiceReachability(name, url) {
try {
const controller = new AbortController();