feat: update algorithm integrations and point cloud overlays
This commit is contained in:
@@ -283,7 +283,10 @@ router.post('/sample/batch', async (req, res) => {
|
||||
);
|
||||
|
||||
const totalSampled = sampledPerFile.reduce((s, pts) => s + pts.length, 0);
|
||||
const binBuf = Buffer.allocUnsafe(totalSampled * 16);
|
||||
// 如果任意点有颜色,则输出 28 字节/点(x,y,z,error,r,g,b 各 float32);否则 16 字节
|
||||
const hasColor = sampledPerFile.some((pts) => pts.some((p) => p.r !== undefined));
|
||||
const bytesPerPoint = hasColor ? 28 : 16;
|
||||
const binBuf = Buffer.allocUnsafe(totalSampled * bytesPerPoint);
|
||||
let offset = 0;
|
||||
for (const pts of sampledPerFile) {
|
||||
for (const p of pts) {
|
||||
@@ -291,7 +294,12 @@ router.post('/sample/batch', async (req, res) => {
|
||||
binBuf.writeFloatLE(p.y, offset + 4);
|
||||
binBuf.writeFloatLE(p.z, offset + 8);
|
||||
binBuf.writeFloatLE(p.error, offset + 12);
|
||||
offset += 16;
|
||||
if (hasColor) {
|
||||
binBuf.writeFloatLE((p.r ?? 128) / 255, offset + 16);
|
||||
binBuf.writeFloatLE((p.g ?? 128) / 255, offset + 20);
|
||||
binBuf.writeFloatLE((p.b ?? 128) / 255, offset + 24);
|
||||
}
|
||||
offset += bytesPerPoint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,6 +312,7 @@ router.post('/sample/batch', async (req, res) => {
|
||||
console.log(`[FileRoutes] Batch done: ${totalSampled} pts, ${(binBuf.length / 1e6).toFixed(1)} MB`);
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.setHeader('X-Point-Count', totalSampled);
|
||||
res.setHeader('X-Has-Color', hasColor ? '1' : '0');
|
||||
res.setHeader('X-File-Stats', JSON.stringify(fileStats));
|
||||
res.send(binBuf);
|
||||
} catch (err) {
|
||||
@@ -401,7 +410,45 @@ router.get('/read/analysis/:taskId/:resultId/*', async (req, res) => {
|
||||
res.sendFile(filePath);
|
||||
});
|
||||
|
||||
// ─── 插件热重载 ──────────────────────────────────────────────────────────────
|
||||
// ─── 分析结果重新同步 ────────────────────────────────────────────────────────
|
||||
// POST /api/files/resync/analysis/:taskId/:resultId
|
||||
// 把磁盘上的 result.json 重新同步到数据库,用于手动修复 overlay3d 等字段。
|
||||
router.post('/resync/analysis/:taskId/:resultId', async (req, res) => {
|
||||
const taskId = parseInt(req.params.taskId, 10);
|
||||
const resultId = parseInt(req.params.resultId, 10);
|
||||
if (Number.isNaN(taskId) || Number.isNaN(resultId)) {
|
||||
return res.status(400).json({ error: 'invalid params' });
|
||||
}
|
||||
const row = await db.getTaskAnalysisResultById(resultId);
|
||||
if (!row || row.taskId !== taskId) {
|
||||
return res.status(404).json({ error: 'not found' });
|
||||
}
|
||||
const resultJsonPath = path.join(TASKS_DIR, String(taskId), 'analysis', String(resultId), 'result.json');
|
||||
if (!fs.existsSync(resultJsonPath)) {
|
||||
return res.status(404).json({ error: 'result.json missing' });
|
||||
}
|
||||
try {
|
||||
const result = JSON.parse(fs.readFileSync(resultJsonPath, 'utf8'));
|
||||
const existingRd = (row.resultData as Record<string, unknown>) ?? {};
|
||||
const mergedRd = {
|
||||
...existingRd,
|
||||
message: result.message ?? existingRd.message,
|
||||
measurements: result.measurements ?? existingRd.measurements,
|
||||
errorStats: result.errorStats ?? existingRd.errorStats,
|
||||
overlay3d: result.overlay3d ?? existingRd.overlay3d,
|
||||
images: result.images ?? existingRd.images,
|
||||
files: result.files ?? existingRd.files,
|
||||
extra: result.extra ?? existingRd.extra,
|
||||
schemaVersion: result.schemaVersion ?? existingRd.schemaVersion,
|
||||
};
|
||||
await db.updateTaskAnalysisResult(resultId, { resultData: mergedRd });
|
||||
res.json({ success: true, overlay3d: result.overlay3d });
|
||||
} catch (e) {
|
||||
res.status(500).json({ error: String(e) });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// POST /api/files/plugins/reload
|
||||
// 重新从磁盘扫描 manifests/*.json,无需重启服务器。
|
||||
// 用于更新算法版本后立即生效。
|
||||
|
||||
Reference in New Issue
Block a user