Design
This commit is contained in:
138
server/server.js
138
server/server.js
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user