You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

96 rivejä
13 KiB

  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import crypto from 'node:crypto';
  4. const dir=path.resolve('reports/bridge-model-acceptance-20260921');
  5. const tasks=JSON.parse(fs.readFileSync(path.join(dir,'current-tasks.json'),'utf8'));
  6. const models=JSON.parse(fs.readFileSync(path.join(dir,'source-models.json'),'utf8'));
  7. const source=JSON.parse(fs.readFileSync(path.join(dir,'source.json'),'utf8'));
  8. const runtime=JSON.parse(fs.readFileSync(path.join(dir,'new-model-runtime.json'),'utf8'));
  9. const originalPath=path.join(dir,'original-005-runtime.json');
  10. const originalRuntime=fs.existsSync(originalPath)?JSON.parse(fs.readFileSync(originalPath,'utf8')):null;
  11. const data={
  12. '005':[
  13. [30,'简化实现','真实前泵可点选确认;故障由脚本预设,没有两个泵之间的诊断分支。'],
  14. [31,'基本对应','真实取力器开关可旋转断开;模型命名及原稿均提示该开关实物准确性仍需确认。'],
  15. [32,'基本对应','真实球阀手柄旋转;已验证交互,最终与管轴平行关系仍需实装确认。'],
  16. [33,'基本对应','同一取力器开关反向旋转复位,接通动作可完成。'],
  17. [34,'基本对应','真实盒盖与互救开关分两次操作;盒盖铰链、手柄固定端转轴已有。单泵驱动整车的结果动画未接入。'],
  18. ],
  19. '006':[
  20. [39,'简化实现','发动机以颜色表示运行,取力器开关可断开;无发动机/液压动力联动。'],
  21. [40,'简化实现','双车、接口盖和两条真实软管存在;移动盖子后显示预成形软管,没有快接插入与锁紧动作。'],
  22. [41,'基本对应','使用保障车独立回油截止阀节点,可旋转打开;实装位置待专业确认。'],
  23. [42,'基本对应','使用真实盒盖和互救开关;与005是否同一开关,原表列为待澄清项。'],
  24. [43,'基本对应','两个独立滤油器出口球阀均绑定,必须分别完成操作。'],
  25. [44,'简化实现','保障车GLB只有整车、玻璃、接口盖、回油阀四个网格;取力器开关是代码补充件,供油靠软管变色表示。'],
  26. [45,'关键内容缺失','可移动示意手柄结束步骤,但没有“按正常步骤操作架桥车”的整机作业动作。不能据此验收正常作业已完成。'],
  27. ],
  28. '007':[
  29. [47,'简化实现','停机着色及牛皮纸缩放已实现;未用裁剪工具沿真实结合面轮廓裁切。'],
  30. [48,'待资料确认','绑定2_07球阀开关02和3-02球阀开关,能关闭;是否对应“1号球阀”须确认编号。'],
  31. [49,'简化实现','进出油接头与两封口袋可独立操作;接头只有位移,缺大活动扳手旋松及包扎动作。'],
  32. [50,'简化实现','两组源网格拆成四颗真实螺栓,逐颗旋转拆下;无开口扳手动作,旋转行程为示意。'],
  33. [51,'基本对应','原位旧前泵可取下,安装位置保留。'],
  34. [52,'简化实现','从初裁纸切换为精裁纸垫几何;没有实际轮廓裁切过程。'],
  35. [53,'简化实现','纸垫可移动到取力器接口;贴合位置按偏移估算,尚缺结合面形状尺寸验收依据。'],
  36. [54,'基本对应','新泵归位并安装四颗螺栓;新旧泵复用同一源几何。不能据此验收扭矩或装配尺寸。'],
  37. [55,'简化实现','真实加油口盖随新泵移动,可打开;加油壶与油流是补充件,无油量计算及关盖动作。'],
  38. [56,'简化实现','取袋和接头归位可完成;没有“拧紧”的接头旋转及扳手动作。'],
  39. [57,'关键内容缺失','发动机变色、示意试验手柄可操作;没有整机试车工况、泄漏检查、压力响应与合格判据。'],
  40. ],
  41. '008':[
  42. [59,'简化实现','补充布块移动和阀体变色;没有清洗剂、油污到洁净的材质变化及各管接头擦拭。'],
  43. [60,'简化实现','真实管接头移开、封口袋就位;无19号开口扳手及旋松过程。'],
  44. [61,'简化实现','四颗真实安装螺栓分别拆除并取阀;无8号内六角工具。'],
  45. [62,'简化实现','两个真实油堵分别封堵;擦净结合面仅为示意阀座平面变色。'],
  46. [64,'简化实现','同小腔,补充布块移动、阀体变色;缺去污材质和管接头清洗。'],
  47. [65,'简化实现','真实大腔接头和封口袋可操作;无19号扳手和接头旋松。'],
  48. [66,'简化实现','四颗真实安装螺栓与大腔阀体可拆除;无8号内六角工具。'],
  49. [67,'简化实现','两个真实油堵可封堵;阀座接触面仍是代码补充平面。'],
  50. [69,'简化实现','示意阀座变色及两个油堵移开;缺阀体、阀座双方的擦拭动作。'],
  51. [70,'简化实现','小腔来源阀换装到大腔位置,按四螺栓安装面换算朝向;可安装四颗螺栓,缺内六角工具。'],
  52. [71,'待资料确认','原表同时写连接拧紧与拆开包扎。当前执行取袋接管并要求记录依据,交教员核对;不能自动判为正确装配。'],
  53. [72,'待资料确认','复用第10步同一组四颗螺栓再次旋转。若固定螺栓和阀体连接螺栓是两组,当前缺第二组。'],
  54. [74,'简化实现','示意阀座变色及两个油堵移开;缺阀体和阀座双面擦拭。'],
  55. [75,'简化实现','大腔来源阀换装到小腔位置并调整朝向;四颗真实螺栓可装,缺内六角工具。'],
  56. [76,'待资料确认','与第11步相同的装/拆原文矛盾;保留人工依据与教员核对,不能当作已澄清。'],
  57. [77,'待资料确认','复用第14步四颗螺栓。是否另有一组四颗阀体连接螺栓仍待确认。'],
  58. ]};
  59. const taskIds={'005':116,'006':87,'007':88,'008':89};
  60. const rows=[];
  61. for(const [code,notes] of Object.entries(data)){
  62. const task=tasks.find(t=>String(t.id)===String(taskIds[code]));
  63. if(notes.length!==task.definitionSnapshot.steps.length)throw Error('Step coverage mismatch '+code);
  64. for(const [i,[row,grade,note]]of notes.entries()){
  65. const step=task.definitionSnapshot.steps[i],ops=step.action.mechanism.steps;
  66. const matches=runtime.checks.filter(c=>c.name.startsWith(`task ${taskIds[code]} step ${i+1} operation `));
  67. const sourceRow=source[0].rows.find(r=>r['B'+row]);
  68. rows.push({code,number:i+1,title:step.title,grade,note,source:`1-动画!B${row}:D${row}`,original:sourceRow?.['C'+row],
  69. operations:ops.map(o=>({targetId:o.targetId,kind:o.kind,effects:o.effects})),
  70. expectedOperations:ops.length,verifiedOperations:matches.length,
  71. runtimeComplete:matches.length===ops.length,screenshot:`task-${taskIds[code]}-step-${i+1}.png`});
  72. }
  73. }
  74. const counts=Object.fromEntries(['基本对应','简化实现','待资料确认','关键内容缺失'].map(s=>[s,rows.filter(r=>r.grade===s).length]));
  75. const copiedModels=[];
  76. for(const task of tasks.filter(t=>t.id!=='86'))for(const o of task.definitionSnapshot.scenario.sceneResource.snapshot.objects){
  77. const supplied=models.find(m=>m.name===o.sourceName);if(!supplied)continue;
  78. const file=path.resolve('../unreal_tran_web/public'+o.resourceUrl);
  79. const sha256=crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
  80. if(sha256!==supplied.sha256)throw Error('Source model changed '+o.sourceName);
  81. if(!copiedModels.some(m=>m.source===o.sourceName))copiedModels.push({source:o.sourceName,file:o.resourceUrl,sha256});
  82. }
  83. const report={date:'2026-09-21',counts,verifiedSteps:rows.filter(r=>r.runtimeComplete).length,totalSteps:39,
  84. verifiedOperations:rows.reduce((n,r)=>n+r.verifiedOperations,0),original005RuntimePass:originalRuntime?.pass===true,
  85. runtimePass:runtime.pass,copiedModels,rows,
  86. limitation:'仅教员预览真实点击/拖拽、准备与服务端步骤校验;不写学员成绩,不等同实装质量、工具尺寸、扭矩、压力和安全流程验收。'};
  87. fs.writeFileSync(path.join(dir,'assessment.json'),JSON.stringify(report,null,2));
  88. 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`;
  89. const table=rows.map(r=>`|${r.code}|${r.number}|${r.title}|${r.grade}|${r.verifiedOperations}/${r.expectedOperations}|${r.note}|${r.source}|`).join('\n');
  90. 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`;
  91. fs.writeFileSync(path.join(dir,'验收报告.md'),summary+table+tail);
  92. const esc=s=>String(s??'').replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('>','&gt;').replaceAll('"','&quot;');
  93. const cards=rows.map(r=>`<article data-code="${r.code}" data-grade="${r.grade}"><header><b>${r.code} · 第${r.number}步</b><span>${r.grade}</span></header><h2>${esc(r.title)}</h2><p>${esc(r.note)}</p><small>${r.source} · 已验证 ${r.verifiedOperations}/${r.expectedOperations} 项交互</small><details><summary>查看原稿和动作目标</summary><p>${esc(r.original)}</p><pre>${esc(r.operations.map(o=>o.targetId+' · '+o.kind).join('\n'))}</pre></details><a href="${r.screenshot}"><img loading="lazy" src="${r.screenshot}" alt="${esc(r.title)}实际完成截图"></a></article>`).join('');
  94. fs.writeFileSync(path.join(dir,'index.html'),`<!doctype html><html lang="zh-CN"><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>四套脚本新模型逐步验收</title><style>body{margin:28px auto;padding:0 22px;max-width:1250px;background:#eff5f3;color:#173e36;font:16px/1.7 system-ui}h1{font-size:27px}.notice,article{background:white;border:1px solid #cbdfd8;border-radius:12px;padding:20px;margin:18px 0}nav{position:sticky;top:0;background:#eff5f3;padding:12px 0;display:flex;gap:12px}select{font:inherit;padding:6px 12px}main{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}article{margin:0}header{display:flex;justify-content:space-between;gap:10px}header span{color:#96621c}h2{font-size:19px}small{color:#58776d}img{width:100%;margin-top:15px}pre{white-space:pre-wrap;font-size:13px}a{color:#00856c}[hidden]{display:none}@media(max-width:760px){main{grid-template-columns:1fr}}</style><h1>005~008 · 新模型逐步验收</h1><div class="notice"><b>${report.verifiedSteps}/39 步完成配置操作;内容完整性另行判断</b><p>${Object.entries(counts).map(([k,v])=>k+' '+v+'步').join(' · ')}</p><p>005 正式任务86仍是旧模型;新模型验证入口为116。006~008正式任务已用新模型。</p><p>主要缺口:006整机正常作业、007试车;008装拆方向与螺栓分组待确认。交互通关不等同于实装检修合格。</p><a href="验收报告.md">完整 Markdown 报告</a> · <a href="assessment.json">逐项证据</a></div><nav><select id="code"><option value="">全部流程</option>${Object.keys(data).map(c=>'<option>'+c+'</option>').join('')}</select><select id="grade"><option value="">全部结论</option>${Object.keys(counts).map(g=>'<option>'+g+'</option>').join('')}</select></nav><main>${cards}</main><script>function filter(){document.querySelectorAll('article').forEach(a=>a.hidden=(code.value&&code.value!==a.dataset.code)||(grade.value&&grade.value!==a.dataset.grade))}code.onchange=grade.onchange=filter</script></html>`);
  95. console.log(JSON.stringify({verifiedSteps:report.verifiedSteps,counts,runtimePass:report.runtimePass,copiedModels:copiedModels.length}));