This commit is contained in:
chk
2026-06-10 11:52:03 +02:00
parent e923cb8dbd
commit f1b20a2dc8

View File

@@ -346,64 +346,84 @@ const PYTHON_BIN = process.env.PYTHON_BIN || 'python3';
const calibScriptPath = path.join(__dirname, '..', 'scripts', 'callibriate.py');
app.post('/api/calibration/compute', async (req, res) => {
const { camera } = req.body ?? {};
if (!camera) return res.status(400).json({ error: '"camera" parameter fehlt' });
try {
const { camera } = req.body ?? {};
if (!camera) return res.status(400).json({ error: '"camera" parameter fehlt' });
const session = await findLatestCalibSession();
if (!session) return res.status(400).json({ error: 'Keine Kalibrierungs-Session vorhanden' });
const session = await findLatestCalibSession();
if (!session) return res.status(400).json({ error: 'Keine Kalibrierungs-Session vorhanden' });
const sessionDir = path.join(calibDataDir, session);
const sessionDir = path.join(calibDataDir, session);
// SSE-Header
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
// SSE-Header erst NACH den Validierungen senden
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.flushHeaders();
const send = (obj) => res.write(`data: ${JSON.stringify(obj)}\n\n`);
// Schreibt nur wenn die Verbindung noch offen ist
const send = (obj) => {
if (!res.writableEnded) res.write(`data: ${JSON.stringify(obj)}\n\n`);
};
send({ type: 'log', text: `▶ Session: ${session}` });
send({ type: 'log', text: `▶ Kamera: ${camera}` });
send({ type: 'log', text: `▶ Script: ${calibScriptPath}` });
send({ type: 'log', text: '' });
send({ type: 'log', text: `▶ Session: ${session}` });
send({ type: 'log', text: `▶ Kamera: ${camera}` });
send({ type: 'log', text: `▶ Script: ${calibScriptPath}` });
send({ type: 'log', text: '' });
const proc = spawn(PYTHON_BIN, [
calibScriptPath,
'--camera', camera,
'--input-dir', sessionDir,
'--output-dir', sessionDir,
]);
// -u = unbuffered (Python gibt jede Zeile sofort aus)
const proc = spawn(PYTHON_BIN, [
'-u',
calibScriptPath,
'--camera', camera,
'--input-dir', sessionDir,
'--output-dir', sessionDir,
]);
// stdout zeilenweise weiterleiten
let stdoutBuf = '';
proc.stdout.on('data', (chunk) => {
stdoutBuf += chunk.toString();
const lines = stdoutBuf.split('\n');
stdoutBuf = lines.pop(); // letztes (unvollständiges) Fragment behalten
for (const line of lines) send({ type: 'log', text: line });
});
let stdoutBuf = '';
proc.stdout.on('data', (chunk) => {
stdoutBuf += chunk.toString();
const lines = stdoutBuf.split('\n');
stdoutBuf = lines.pop();
for (const line of lines) send({ type: 'log', text: line });
});
// stderr als Warnung weiterleiten
let stderrBuf = '';
proc.stderr.on('data', (chunk) => {
stderrBuf += chunk.toString();
const lines = stderrBuf.split('\n');
stderrBuf = lines.pop();
for (const line of lines) send({ type: 'log', text: `[stderr] ${line}` });
});
let stderrBuf = '';
proc.stderr.on('data', (chunk) => {
stderrBuf += chunk.toString();
const lines = stderrBuf.split('\n');
stderrBuf = lines.pop();
for (const line of lines) send({ type: 'log', text: `[stderr] ${line}` });
});
proc.on('error', (err) => {
send({ type: 'log', text: `Fehler beim Starten: ${err.message}` });
send({ type: 'done', exitCode: -1 });
res.end();
});
proc.on('error', (err) => {
console.error('calibration/compute spawn error:', err);
send({ type: 'log', text: `Fehler beim Starten: ${err.message}` });
send({ type: 'done', exitCode: -1 });
if (!res.writableEnded) res.end();
});
proc.on('close', (code) => {
if (stdoutBuf) send({ type: 'log', text: stdoutBuf }); // Rest ausgeben
if (stderrBuf) send({ type: 'log', text: `[stderr] ${stderrBuf}` });
send({ type: 'done', exitCode: code });
res.end();
});
proc.on('close', (code) => {
if (stdoutBuf) send({ type: 'log', text: stdoutBuf });
if (stderrBuf) send({ type: 'log', text: `[stderr] ${stderrBuf}` });
send({ type: 'done', exitCode: code ?? -1 });
if (!res.writableEnded) res.end();
});
} catch (err) {
// Fehler VOR flushHeaders → normaler JSON-Fehler
// Fehler NACH flushHeaders → SSE-Fehlerevent + close
console.error('calibration/compute error:', err);
if (!res.headersSent) {
res.status(500).json({ error: String(err) });
} else {
try {
res.write(`data: ${JSON.stringify({ type: 'log', text: `Server-Fehler: ${err.message}` })}\n\n`);
res.write(`data: ${JSON.stringify({ type: 'done', exitCode: -1 })}\n\n`);
res.end();
} catch { /* Verbindung bereits geschlossen */ }
}
}
});
async function checkServiceReachability(name, url) {