diff --git a/text17.txt b/text17.txt index d5b92fd..ee85481 100644 --- a/text17.txt +++ b/text17.txt @@ -1113,3 +1113,80 @@ git diff --check ok 让用户可在页面上输入 release artifact URL 与 browser diagnostics URL 后直接运行验证。 3. 为 motion matrix 增加 manifest-backed source check,把 matrix 关联的 sim config source files 与 vendored LinuxCNC manifest 做显式一致性验证。 + +### 十二、2026-06-17 Virtual HAL Session Diagnostics Artifact Linkage + +继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL +完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把 real +simulation page 保存的 virtual HAL session snapshot 与 release diagnostics artifact 做联动, +使保存后的 Web CNC 仿真会话可直接输出 release-ready diagnostics evidence。 + +新增实质能力: + +- real simulation page 新增 session diagnostics artifact builder: + - `createVirtualHalSessionDiagnosticsArtifact(snapshot)`; + - 从保存的 session snapshot 读取 `payload.virtualHal`; + - 复用 snapshot 中的 virtual HAL state、simulation replacement、source compliance、 + sim-config coverage、halcmd fixtures、motion controller matrix; + - 使用 `createProjectReleaseBrowserDiagnosticsArtifactValidation()` 对 snapshot + 派生 diagnostics artifact 做 release 级验证; +- `exportDiagnosticsArtifact()` 新增: + - `virtualHalSessionDiagnostics`; + - 未保存 session 时返回 `phase: "not-saved"` 和 missing reason; + - 已保存或恢复 session 后返回 `phase: "release-ready"`; +- Browser simulation API 新增: + - `exportVirtualHalSessionDiagnosticsArtifact()`; +- `saveVirtualHalSessionSnapshot()` 返回值新增: + - `releaseDiagnosticsReady`; + - `sessionDiagnostics`; +- `restoreVirtualHalSessionSnapshot()` 返回值新增: + - `releaseDiagnosticsReady`; + - `sessionDiagnostics`; +- AXIS simulation docs 同步公开 + `exportVirtualHalSessionDiagnosticsArtifact()`,并说明 saved virtual HAL session + snapshot 会进入 browser diagnostics artifact,形成 release-ready evidence。 + +新增/扩展验证: + +- `real_simulation_page_smoke.html` 覆盖: + - 页面 API 暴露 `exportVirtualHalSessionDiagnosticsArtifact()`; + - 保存 virtual HAL session 后 `releaseDiagnosticsReady === true`; + - 保存返回的 `sessionDiagnostics.validation.ready === true`; + - 保存返回的 session diagnostics 包含 complete motion controller matrix; + - `exportVirtualHalSessionDiagnosticsArtifact()` 输出 `phase === "release-ready"`; + - `exportDiagnosticsArtifact()` 内嵌 `virtualHalSessionDiagnostics.ready === true`; + - restore 后 session diagnostics 仍保持 release-ready; +- `verify_axis_style_simulation_docs.mjs` 覆盖新 API 文档契约; +- `verify_sdk_surface.sh` 继续覆盖 release diagnostics validation 与 virtual HAL source + evidence。 + +关键验证: + +```text +axis_style_simulation_docs_node_smoke=ok +browser_real_simulation_page_smoke=ok +sdk_surface_node_smoke=ok +project_release_gate_manifest_node_smoke=ok +project_release_artifact_url_workflow_node_smoke=ok +project_batch_acceptance_workflow_node_smoke=ok +project_batch_acceptance_artifact_node_smoke=ok +git diff --check ok +``` + +语义边界: + +- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义; +- session diagnostics artifact 只复用保存时已有的 source-derived virtual HAL reports; +- release validation 只验证 Web 仿真所需 HAL、halcmd、motion controller evidence; +- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native + HAL module ABI; +- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM。 + +下一步工作内容: + +1. 将 ini-panel workflow overview 的 diagnostics URL workflow 接入固定页面表单/按钮, + 让用户可在页面上输入 release artifact URL 与 browser diagnostics URL 后直接运行验证。 +2. 为 motion matrix 增加 manifest-backed source check,把 matrix 关联的 sim config + source files 与 vendored LinuxCNC manifest 做显式一致性验证。 +3. 将 virtual HAL session diagnostics artifact 纳入 release artifact URL workflow 的示例 + fixtures,使 URL workflow 同时覆盖 live diagnostics 与 saved-session diagnostics。 diff --git a/wasm-port/docs/axis-style-simulation-implementation.md b/wasm-port/docs/axis-style-simulation-implementation.md index 8cc9deb..80bf76a 100644 --- a/wasm-port/docs/axis-style-simulation-implementation.md +++ b/wasm-port/docs/axis-style-simulation-implementation.md @@ -373,7 +373,11 @@ Current implemented behavior: state; - browser API exposes `getDiagnosticsState()`, `getStatusHistory()`, `getToolTableSummary()`, `getLimitsHomeState()`, - `exportDiagnosticsArtifact()`, and `handleAxisShortcut(event)`; + `exportDiagnosticsArtifact()`, `exportVirtualHalSessionDiagnosticsArtifact()`, + and `handleAxisShortcut(event)`; +- saved virtual HAL session snapshots are linked back into the browser + diagnostics artifact as release-ready virtual HAL source, sim-config, + halcmd-fixture, and motion-matrix evidence; - browser smoke covers run and play/pause keyboard shortcuts after waiting for their async UI/API side effects; - browser smoke verifies blocked readiness before OPFS files exist, then writes @@ -458,6 +462,7 @@ Required methods: - `getToolTableSummary()`; - `getLimitsHomeState()`; - `exportDiagnosticsArtifact()`; +- `exportVirtualHalSessionDiagnosticsArtifact()`; - `handleAxisShortcut(event)`; - `applyAxisViewFromUrl(search)`; diff --git a/wasm-port/runtime/ui/simulation/index.html b/wasm-port/runtime/ui/simulation/index.html index 28669f8..e6e52b0 100644 --- a/wasm-port/runtime/ui/simulation/index.html +++ b/wasm-port/runtime/ui/simulation/index.html @@ -1800,6 +1800,7 @@ M2 import { createLinuxCncIniSdk, createLinuxCncInterpSdk, + createProjectReleaseBrowserDiagnosticsArtifactValidation, } from "../../sdk/src/index.js"; import { DEFAULT_SIMULATION_PROGRAM_ID, @@ -1921,6 +1922,7 @@ M2 status: "Virtual HAL session not saved", revision: 0, restored: false, + releaseDiagnosticsReady: false, }; let machineReadinessState = { machineId: "axis-browser-sim", @@ -2613,6 +2615,7 @@ M2 virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(), virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(), virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(), + virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(), statusHistory: getStatusHistory(), mdiHistory: getMdiHistory(), recentPrograms: getRecentPrograms(), @@ -2627,6 +2630,44 @@ M2 }; } + function createVirtualHalSessionDiagnosticsArtifact(snapshot = virtualHalSessionState.snapshot ?? null) { + if (!snapshot?.payload?.virtualHal) { + return { + apiName: "real-browser-simulation-virtual-hal-session-diagnostics", + artifactVersion: 1, + ready: false, + phase: "not-saved", + missing: ["virtualHalSessionSnapshot"], + }; + } + const virtualHal = snapshot.payload.virtualHal; + const diagnosticsArtifact = { + apiName: "real-browser-simulation-diagnostics-artifact", + artifactVersion: 1, + virtualHal: virtualHal.state, + virtualHalSimulationReplacement: virtualHal.diagnostics, + virtualHalSourceCompliance: virtualHal.sourceCompliance, + virtualHalSimConfigSourceCoverage: virtualHal.simConfigSourceCoverage, + virtualHalCommandScriptFixtures: virtualHal.commandScriptFixtures, + virtualHalMotionControllerMatrix: virtualHal.motionControllerMatrix, + }; + const validation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact); + return { + apiName: "real-browser-simulation-virtual-hal-session-diagnostics", + artifactVersion: 1, + ready: validation.ready === true, + phase: validation.ready === true ? "release-ready" : "blocked", + sessionId: snapshot.sessionId, + machineId: snapshot.payload.machineId, + createdAt: snapshot.createdAt, + metadata: { ...(snapshot.metadata ?? {}) }, + files: { ...(snapshot.payload.files ?? {}) }, + virtualHalRevision: virtualHal.state.revision, + validation, + diagnosticsArtifact, + }; + } + function recordError(error, context = "runtime") { const message = `${context}: ${error?.message ?? error}`; renderDiagnostics({ lastError: message }); @@ -3549,6 +3590,7 @@ M2 virtualHalRevision: virtualHal.state.revision, }, }); + const sessionDiagnostics = createVirtualHalSessionDiagnosticsArtifact(snapshot); pushStatusHistory("hal-session", `saved ${session.sessionId}/${session.filename}`); return setVirtualHalSessionState({ ready: true, @@ -3559,6 +3601,8 @@ M2 status: `Saved HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`, revision: virtualHal.state.revision, restored: false, + releaseDiagnosticsReady: sessionDiagnostics.ready === true, + sessionDiagnostics, snapshot, }); } @@ -3571,6 +3615,7 @@ M2 virtualHalState = restoreVirtualHalStateFromSessionSnapshot(snapshot); renderVirtualHalState(); renderAxisStatusbar(); + const sessionDiagnostics = createVirtualHalSessionDiagnosticsArtifact(snapshot); pushStatusHistory("hal-session", `restored ${session.sessionId}/${session.filename}`); return setVirtualHalSessionState({ ready: true, @@ -3581,6 +3626,8 @@ M2 status: `Restored HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`, revision: virtualHalState.revision, restored: true, + releaseDiagnosticsReady: sessionDiagnostics.ready === true, + sessionDiagnostics, snapshot, }); } @@ -3975,6 +4022,7 @@ M2 getVirtualHalSessionState: () => ({ ...virtualHalSessionState }), saveVirtualHalSessionSnapshot, restoreVirtualHalSessionSnapshot, + exportVirtualHalSessionDiagnosticsArtifact: () => createVirtualHalSessionDiagnosticsArtifact(), getRunMode: () => syncRunMode(), getRunControlState: () => renderRunControlState(), getAxisStatusbarState: () => renderAxisStatusbar(), diff --git a/wasm-port/tests/browser/real_simulation_page_smoke.html b/wasm-port/tests/browser/real_simulation_page_smoke.html index 958632e..59d70fd 100644 --- a/wasm-port/tests/browser/real_simulation_page_smoke.html +++ b/wasm-port/tests/browser/real_simulation_page_smoke.html @@ -252,7 +252,7 @@ if (!api?.getMachineSessionLoadState || !api?.loadReadyMachineSession) { throw new Error("simulation API missing AXIS machine/session load controls"); } - if (!api?.getVirtualHalSessionState || !api?.saveVirtualHalSessionSnapshot || !api?.restoreVirtualHalSessionSnapshot) { + if (!api?.getVirtualHalSessionState || !api?.saveVirtualHalSessionSnapshot || !api?.restoreVirtualHalSessionSnapshot || !api?.exportVirtualHalSessionDiagnosticsArtifact) { throw new Error("simulation API missing virtual HAL session persistence controls"); } if (!api?.getRunMode || !api?.getRunControlState || !api?.setUseLoadedSession || !api?.getRunSummary) { @@ -747,11 +747,29 @@ savedVirtualHalSession.revision < virtualHalcmdResult.state.revision || !savedVirtualHalSession.snapshot?.payload?.virtualHal?.sourceCompliance?.complete || !savedVirtualHalSession.snapshot?.payload?.virtualHal?.motionControllerMatrix?.complete || + savedVirtualHalSession.releaseDiagnosticsReady !== true || + savedVirtualHalSession.sessionDiagnostics?.ready !== true || + savedVirtualHalSession.sessionDiagnostics?.validation?.ready !== true || + savedVirtualHalSession.sessionDiagnostics?.validation?.motionControllerMatrixReady !== true || + savedVirtualHalSession.sessionDiagnostics?.diagnosticsArtifact?.virtualHalMotionControllerMatrix?.complete !== true || doc.body.dataset.virtualHalSessionPhase !== "saved" || !doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Saved HAL session") ) { throw new Error(`AXIS-style virtual HAL session save failed: ${JSON.stringify(savedVirtualHalSession)}`); } + const savedSessionDiagnosticsArtifact = api.exportVirtualHalSessionDiagnosticsArtifact(); + const diagnosticsWithSavedSession = api.exportDiagnosticsArtifact(); + if ( + savedSessionDiagnosticsArtifact.ready !== true || + savedSessionDiagnosticsArtifact.phase !== "release-ready" || + savedSessionDiagnosticsArtifact.sessionId !== "axis-browser-virtual-hal-session" || + savedSessionDiagnosticsArtifact.validation?.ready !== true || + diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.ready !== true || + diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.validation?.ready !== true || + diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.diagnosticsArtifact?.virtualHalSourceCompliance?.complete !== true + ) { + throw new Error(`AXIS-style virtual HAL session diagnostics did not become release-ready: ${JSON.stringify(diagnosticsWithSavedSession.virtualHalSessionDiagnostics)}`); + } api.executeVirtualHalcmd("setp axis.x.pos-cmd 9.5\nloadusr post-save-component"); if (api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 9.5) { throw new Error("AXIS-style virtual HAL pre-restore mutation did not apply"); @@ -766,6 +784,9 @@ api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 1.75 || api.getVirtualHalState().hal.loadedComponents.some(({ component }) => component === "post-save-component") || !api.getVirtualHalState().hal.functions.some(({ name }) => name === "motion-controller") || + restoredVirtualHalSession.releaseDiagnosticsReady !== true || + restoredVirtualHalSession.sessionDiagnostics?.ready !== true || + restoredVirtualHalSession.sessionDiagnostics?.validation?.ready !== true || doc.body.dataset.virtualHalSessionPhase !== "restored" || !doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Restored HAL session") ) { diff --git a/wasm-port/tests/docs/node/verify_axis_style_simulation_docs.mjs b/wasm-port/tests/docs/node/verify_axis_style_simulation_docs.mjs index 9a1595e..ba823c1 100644 --- a/wasm-port/tests/docs/node/verify_axis_style_simulation_docs.mjs +++ b/wasm-port/tests/docs/node/verify_axis_style_simulation_docs.mjs @@ -22,6 +22,7 @@ for (const required of [ "getRecentPrograms()", "Three.js `^0.183.2`", "exportDiagnosticsArtifact()", + "exportVirtualHalSessionDiagnosticsArtifact()", "getLimitsHomeState()", "getAxisStatusbarState()", "applyAxisViewFromUrl(search)",