const fs = require('fs'); const path = require('path'); const { exec } = require('child_process'); const { logSnapshot } = require('./log'); function snapshot(outDir, cam0, cam1, ws){ if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true }); const picDate = Date.now(); const name0 = `snapshot_video0_${picDate}.jpg`; const name1 = `snapshot_video1_${picDate}.jpg`; cam0.snapshot(path.join(outDir, name0)); cam1.snapshot(path.join(outDir, name1)); strFile0 = path.join(outDir, name0); strFile1 = path.join(outDir, name1); const relUrl = `/snapshots/${name0}`; const relUrlApp = `/snapshots/${name0.replace('.jpg','_two_cam_annotated.jpg')}`; // The Python postprocessor writes an overlay named "_two_cam_overlay.png" and a CSV named "_two_cam.csv" const relOverlay = `/snapshots/${name0.replace('.jpg','_two_cam_overlay.png')}`; const relOverlayCSV = `/snapshots/${name0.replace('.jpg','_two_cam.csv')}`; const annotatedPath = path.join(outDir, name0.replace('.jpg','_two_cam_annotated.jpg')); const overlayPath = path.join(outDir, name0.replace('.jpg','_two_cam_overlay.png')); const csvPath = path.join(outDir, name0.replace('.jpg','_two_cam.csv')); //const command = `python3 /usr/src/app/programs/readTwoImages.py -i ${strFile0} -i ${strFile1} -npz /usr/src/app/data/settings/callibration_cam0.npz -npz /usr/src/app/data/settings/callibration_cam1.npz -settings /usr/src/app/data/settings/settings.json`; const command = `python3 /usr/src/app/programs/readTwoImages.py -i ${strFile0} -i ${strFile1} -npz /usr/src/app/data/settings/callibration_cam0.npz -npz /usr/src/app/data/settings/callibration_cam1.npz -settings /usr/src/app/data/settings/settings1m.json`; console.log("Executing Python " + command); // Run the Python post-processing and send the snapshot response only // after the annotated files are present to avoid transient 404s in the browser. exec(command, (error, stdout, stderr) => { try { if (error) { console.error(`Error: ${error.message}`); // Capture which generated files actually exist for debugging const files = { annotated: fs.existsSync(annotatedPath), overlay: fs.existsSync(overlayPath), csv: fs.existsSync(csvPath) }; // Log full details server-side for diagnosis const detailed = { type: 'snapshot', ok: false, error: error.message, stdout: String(stdout).slice(0, 4096), stderr: String(stderr).slice(0, 4096), files }; logSnapshot(command, JSON.stringify(detailed)); // Send a short, user-friendly error to the client (no large stdout/stderr) const shortError = String(stderr || error.message || '').includes('Corrupt JPEG') ? 'postprocessor failed: corrupt JPEG input' : 'postprocessor failed'; try { ws.send(JSON.stringify({ type: 'snapshot', ok: false, error: shortError })); } catch (e) {} return; } if (stderr) { // Log stderr but don't fail outright; sometimes tools output warnings on stderr. if (String(stderr).trim()) console.error(`Stderr: ${stderr}`); } console.log(`Output:\n${stdout}`); // Wait up to ~1s (10 * 100ms) for the generated files to appear on disk. const waitForFiles = (paths, attempts = 10, delayMs = 100) => new Promise((resolve) => { let tries = 0; (function poll() { const ok = paths.every(p => fs.existsSync(p)); if (ok || tries >= attempts) return resolve(ok); tries++; setTimeout(poll, delayMs); })(); }); waitForFiles([annotatedPath, overlayPath, csvPath]).then((found) => { if (!found) { const files = { annotated: fs.existsSync(annotatedPath), overlay: fs.existsSync(overlayPath), csv: fs.existsSync(csvPath) }; // Log details server-side const detailed = { type: 'snapshot', ok: false, url: relUrl, urlApp: relUrlApp, overlay: relOverlay, overlayCSV: relOverlayCSV, files }; logSnapshot(command, JSON.stringify(detailed)); // Send a concise error to the client try { ws.send(JSON.stringify({ type: 'snapshot', ok: false, error: 'postprocessor incomplete (missing outputs)' })); } catch (e) {} return; } const response = JSON.stringify({ type: 'snapshot', ok: found, url: relUrl, urlApp: relUrlApp, overlay: relOverlay, overlayCSV: relOverlayCSV }); logSnapshot(command, response); try { ws.send(response); } catch (e) {} }).catch((waitErr) => { console.error('waitForFiles failed:', waitErr); const response = JSON.stringify({ type: 'snapshot', ok: false, error: String(waitErr) }); logSnapshot(command, response); try { ws.send(response); } catch (e) {} }); } catch (handlerErr) { console.error('snapshot handler error:', handlerErr); const response = JSON.stringify({ type: 'snapshot', ok: false, error: String(handlerErr) }); logSnapshot(command, response); try { ws.send(response); } catch (e) {} } }); } module.exports = { snapshot };