import fs from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; const dir=path.resolve('reports/bridge-model-acceptance-20260921'); const tasks=JSON.parse(fs.readFileSync(path.join(dir,'current-tasks.json'),'utf8')); const models=JSON.parse(fs.readFileSync(path.join(dir,'source-models.json'),'utf8')); const source=JSON.parse(fs.readFileSync(path.join(dir,'source.json'),'utf8')); const runtime=JSON.parse(fs.readFileSync(path.join(dir,'new-model-runtime.json'),'utf8')); const originalPath=path.join(dir,'original-005-runtime.json'); const originalRuntime=fs.existsSync(originalPath)?JSON.parse(fs.readFileSync(originalPath,'utf8')):null; const data={ '005':[ [30,'简化实现','真实前泵可点选确认;故障由脚本预设,没有两个泵之间的诊断分支。'], [31,'基本对应','真实取力器开关可旋转断开;模型命名及原稿均提示该开关实物准确性仍需确认。'], [32,'基本对应','真实球阀手柄旋转;已验证交互,最终与管轴平行关系仍需实装确认。'], [33,'基本对应','同一取力器开关反向旋转复位,接通动作可完成。'], [34,'基本对应','真实盒盖与互救开关分两次操作;盒盖铰链、手柄固定端转轴已有。单泵驱动整车的结果动画未接入。'], ], '006':[ [39,'简化实现','发动机以颜色表示运行,取力器开关可断开;无发动机/液压动力联动。'], [40,'简化实现','双车、接口盖和两条真实软管存在;移动盖子后显示预成形软管,没有快接插入与锁紧动作。'], [41,'基本对应','使用保障车独立回油截止阀节点,可旋转打开;实装位置待专业确认。'], [42,'基本对应','使用真实盒盖和互救开关;与005是否同一开关,原表列为待澄清项。'], [43,'基本对应','两个独立滤油器出口球阀均绑定,必须分别完成操作。'], [44,'简化实现','保障车GLB只有整车、玻璃、接口盖、回油阀四个网格;取力器开关是代码补充件,供油靠软管变色表示。'], [45,'关键内容缺失','可移动示意手柄结束步骤,但没有“按正常步骤操作架桥车”的整机作业动作。不能据此验收正常作业已完成。'], ], '007':[ [47,'简化实现','停机着色及牛皮纸缩放已实现;未用裁剪工具沿真实结合面轮廓裁切。'], [48,'待资料确认','绑定2_07球阀开关02和3-02球阀开关,能关闭;是否对应“1号球阀”须确认编号。'], [49,'简化实现','进出油接头与两封口袋可独立操作;接头只有位移,缺大活动扳手旋松及包扎动作。'], [50,'简化实现','两组源网格拆成四颗真实螺栓,逐颗旋转拆下;无开口扳手动作,旋转行程为示意。'], [51,'基本对应','原位旧前泵可取下,安装位置保留。'], [52,'简化实现','从初裁纸切换为精裁纸垫几何;没有实际轮廓裁切过程。'], [53,'简化实现','纸垫可移动到取力器接口;贴合位置按偏移估算,尚缺结合面形状尺寸验收依据。'], [54,'基本对应','新泵归位并安装四颗螺栓;新旧泵复用同一源几何。不能据此验收扭矩或装配尺寸。'], [55,'简化实现','真实加油口盖随新泵移动,可打开;加油壶与油流是补充件,无油量计算及关盖动作。'], [56,'简化实现','取袋和接头归位可完成;没有“拧紧”的接头旋转及扳手动作。'], [57,'关键内容缺失','发动机变色、示意试验手柄可操作;没有整机试车工况、泄漏检查、压力响应与合格判据。'], ], '008':[ [59,'简化实现','补充布块移动和阀体变色;没有清洗剂、油污到洁净的材质变化及各管接头擦拭。'], [60,'简化实现','真实管接头移开、封口袋就位;无19号开口扳手及旋松过程。'], [61,'简化实现','四颗真实安装螺栓分别拆除并取阀;无8号内六角工具。'], [62,'简化实现','两个真实油堵分别封堵;擦净结合面仅为示意阀座平面变色。'], [64,'简化实现','同小腔,补充布块移动、阀体变色;缺去污材质和管接头清洗。'], [65,'简化实现','真实大腔接头和封口袋可操作;无19号扳手和接头旋松。'], [66,'简化实现','四颗真实安装螺栓与大腔阀体可拆除;无8号内六角工具。'], [67,'简化实现','两个真实油堵可封堵;阀座接触面仍是代码补充平面。'], [69,'简化实现','示意阀座变色及两个油堵移开;缺阀体、阀座双方的擦拭动作。'], [70,'简化实现','小腔来源阀换装到大腔位置,按四螺栓安装面换算朝向;可安装四颗螺栓,缺内六角工具。'], [71,'待资料确认','原表同时写连接拧紧与拆开包扎。当前执行取袋接管并要求记录依据,交教员核对;不能自动判为正确装配。'], [72,'待资料确认','复用第10步同一组四颗螺栓再次旋转。若固定螺栓和阀体连接螺栓是两组,当前缺第二组。'], [74,'简化实现','示意阀座变色及两个油堵移开;缺阀体和阀座双面擦拭。'], [75,'简化实现','大腔来源阀换装到小腔位置并调整朝向;四颗真实螺栓可装,缺内六角工具。'], [76,'待资料确认','与第11步相同的装/拆原文矛盾;保留人工依据与教员核对,不能当作已澄清。'], [77,'待资料确认','复用第14步四颗螺栓。是否另有一组四颗阀体连接螺栓仍待确认。'], ]}; const taskIds={'005':116,'006':87,'007':88,'008':89}; const rows=[]; for(const [code,notes] of Object.entries(data)){ const task=tasks.find(t=>String(t.id)===String(taskIds[code])); if(notes.length!==task.definitionSnapshot.steps.length)throw Error('Step coverage mismatch '+code); for(const [i,[row,grade,note]]of notes.entries()){ const step=task.definitionSnapshot.steps[i],ops=step.action.mechanism.steps; const matches=runtime.checks.filter(c=>c.name.startsWith(`task ${taskIds[code]} step ${i+1} operation `)); const sourceRow=source[0].rows.find(r=>r['B'+row]); rows.push({code,number:i+1,title:step.title,grade,note,source:`1-动画!B${row}:D${row}`,original:sourceRow?.['C'+row], operations:ops.map(o=>({targetId:o.targetId,kind:o.kind,effects:o.effects})), expectedOperations:ops.length,verifiedOperations:matches.length, runtimeComplete:matches.length===ops.length,screenshot:`task-${taskIds[code]}-step-${i+1}.png`}); } } const counts=Object.fromEntries(['基本对应','简化实现','待资料确认','关键内容缺失'].map(s=>[s,rows.filter(r=>r.grade===s).length])); const copiedModels=[]; for(const task of tasks.filter(t=>t.id!=='86'))for(const o of task.definitionSnapshot.scenario.sceneResource.snapshot.objects){ const supplied=models.find(m=>m.name===o.sourceName);if(!supplied)continue; const file=path.resolve('../unreal_tran_web/public'+o.resourceUrl); const sha256=crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'); if(sha256!==supplied.sha256)throw Error('Source model changed '+o.sourceName); if(!copiedModels.some(m=>m.source===o.sourceName))copiedModels.push({source:o.sourceName,file:o.resourceUrl,sha256}); } const report={date:'2026-09-21',counts,verifiedSteps:rows.filter(r=>r.runtimeComplete).length,totalSteps:39, verifiedOperations:rows.reduce((n,r)=>n+r.verifiedOperations,0),original005RuntimePass:originalRuntime?.pass===true, runtimePass:runtime.pass,copiedModels,rows, limitation:'仅教员预览真实点击/拖拽、准备与服务端步骤校验;不写学员成绩,不等同实装质量、工具尺寸、扭矩、压力和安全流程验收。'}; fs.writeFileSync(path.join(dir,'assessment.json'),JSON.stringify(report,null,2)); const summary=`# 四套脚本新模型逐步验收(2026-09-21)\n\n对照原始工作簿,检查当前接口任务定义、GLB节点与运行时代码,并用真实浏览器逐项操作。工作簿内容与2026-09-17提取件一致,本次重新读取并记录SHA-256。\n\n## 结论\n\n- 新模型39步:${report.verifiedSteps}/39步完成全部配置操作;浏览器总结果:${runtime.pass?'通过':'存在失败,见JSON'}。\n- 内容匹配:${Object.entries(counts).map(([k,v])=>k+' '+v+'步').join(';')}。这些是内容判断,不能用交互通关替代。\n- 005正式任务86仍使用旧“步骤模型.glb”;新模型005在验证任务116。006~008正式任务87~89已使用0916新模型。本次没有擅自更换任务版本。\n- 006第7步的整机正常作业、007第11步的试车验证,目前只有示意手柄,不能按原要求完成。\n- 008第11/15步装拆方向、12/16步是否两组螺栓、007第2步球阀编号,仍需专业资料确认。\n- 所有新GLB均无内嵌动画;局部机构动画由运行代码实现。提供的“开口扳手.glb”未接入当前场景,工具操作并非模型里自动具备。\n\n## 每一步\n\n|流程|步|步骤|内容匹配|操作验证|缺口/边界|原表|\n|---|---:|---|---|---|---|---|\n`; const table=rows.map(r=>`|${r.code}|${r.number}|${r.title}|${r.grade}|${r.verifiedOperations}/${r.expectedOperations}|${r.note}|${r.source}|`).join('\n'); const tail=`\n\n## 优先补齐\n\n1. 006最后一步、007试车:需明确整机作业内容及试车合格判据,再绑定真实机构联动,不能仅移动示意开关。\n2. 008两组螺栓及装拆矛盾、007球阀编号:请模型/业务人员确认后才能据此更改评分或动作。\n3. 接入已有开口扳手,补充内六角工具、接头旋紧动作;工具规格与接触点需要核验。\n4. 补齐纸垫真实结合面尺寸、清洗去污、加油口关闭;005补充独立后泵定位及单泵工作结果演示。\n5. 005从验证入口切换正式任务另走内容发布与模型换版,避免影响已有运行。\n\n## 讲解与背景范围\n\n背景和讲解页已存在,不能继续沿用旧报告“没有导入页面”的结论。但文字/背景图不等于液压原理图和动态供油回路;原表要求的液压原理图、三类环境素材及整机作业演示仍需单独验收。本次新数字人资源覆盖39个操作步骤。\n\n## 验证边界\n\n${report.limitation}\n\n新模型文件已逐项与用户提供原文件核对SHA-256。步骤截图、接口快照、原表提取、模型节点清单与实时操作结果都保存在本报告目录。\n`; fs.writeFileSync(path.join(dir,'验收报告.md'),summary+table+tail); const esc=s=>String(s??'').replaceAll('&','&').replaceAll('<','<').replaceAll('>','>').replaceAll('"','"'); const cards=rows.map(r=>`
${r.code} · 第${r.number}步${r.grade}

${esc(r.title)}

${esc(r.note)}

${r.source} · 已验证 ${r.verifiedOperations}/${r.expectedOperations} 项交互
查看原稿和动作目标

${esc(r.original)}

${esc(r.operations.map(o=>o.targetId+' · '+o.kind).join('\n'))}
${esc(r.title)}实际完成截图
`).join(''); fs.writeFileSync(path.join(dir,'index.html'),`四套脚本新模型逐步验收

005~008 · 新模型逐步验收

${report.verifiedSteps}/39 步完成配置操作;内容完整性另行判断

${Object.entries(counts).map(([k,v])=>k+' '+v+'步').join(' · ')}

005 正式任务86仍是旧模型;新模型验证入口为116。006~008正式任务已用新模型。

主要缺口:006整机正常作业、007试车;008装拆方向与螺栓分组待确认。交互通关不等同于实装检修合格。

完整 Markdown 报告 · 逐项证据
${cards}
`); console.log(JSON.stringify({verifiedSteps:report.verifiedSteps,counts,runtimePass:report.runtimePass,copiedModels:copiedModels.length}));