Clear stale toolpaths on program replacement

This commit is contained in:
2026-09-02 13:55:45 +08:00
parent ee2c992328
commit 8b7fc5221b
4 changed files with 75 additions and 3 deletions

View File

@@ -32,6 +32,15 @@
], ],
"status": "verified" "status": "verified"
}, },
"program_preview_replacement": {
"test": "node --experimental-strip-types web/worker/test_xyzac_program_replace.mjs",
"first_program_segments": 1,
"failed_program_segments": 0,
"second_program_segments": 1,
"old_preview_retained_after_failure": false,
"browser_screenshot_sha256": "c3e0a5b992381e7b6cb26f98a30338935df5239c875755727fc678911452ac24",
"status": "verified"
},
"switchkins_direct_matrix": { "switchkins_direct_matrix": {
"command": "node --experimental-strip-types web/worker/test_xyzac_trt_case.mjs", "command": "node --experimental-strip-types web/worker/test_xyzac_trt_case.mjs",
"sequence": [0, 1, 2, 0, 1, 2, 0], "sequence": [0, 1, 2, 0, 1, 2, 0],
@@ -75,7 +84,7 @@
}, },
"published_browser_smoke": { "published_browser_smoke": {
"url": "https://49.232.250.35:58010/", "url": "https://49.232.250.35:58010/",
"release": "20260902-linuxcnc-wasm-live-parameters-r14", "release": "20260902-linuxcnc-wasm-program-replace-r15",
"status": "verified", "status": "verified",
"startup_pause_resume_stop": "verified", "startup_pause_resume_stop": "verified",
"pause_tick": 140, "pause_tick": 140,
@@ -120,8 +129,8 @@
"switchkins": "920b85ad7fd6451dfb134255ffdcc4f03a7aa615ebf486c63cde8582e17af195" "switchkins": "920b85ad7fd6451dfb134255ffdcc4f03a7aa615ebf486c63cde8582e17af195"
}, },
"resource_sha256": { "resource_sha256": {
"index-CDC5yZJC.js": "c3a6aa009e091d5fcb2098c7254313d987229abc1e4765052edb15b465ca0fc4", "index-DUB3ldSh.js": "4d71a2eb0b162608d7a47b63274cf274a57b1c2f2620b69af139b12c2ba22cce",
"sim-worker-entry-BQKHHWpv.js": "e72d80d9ae0fd679d92d3efbad4dba1eef55f9ce7b54bcd06e4a0d644c3cfb1a", "sim-worker-entry-yfjiq6MN.js": "a0cf75522ddc9c6712126de86937ec89f24574bf0e0dad3dbddc6d807fb352b7",
"linuxcnc-sim-core-fuIvKetC.wasm": "ad4e17ecbf73afbad0d9f069ab20ba3c66b6cc4d75ecdb2b3182673490ee916d" "linuxcnc-sim-core-fuIvKetC.wasm": "ad4e17ecbf73afbad0d9f069ab20ba3c66b6cc4d75ecdb2b3182673490ee916d"
}, },
"authority_dom": { "authority_dom": {

View File

@@ -55,6 +55,8 @@ AXIS 源控件清单:菜单 80、工具栏 20、快捷键 10,共 110 条,
实时参数区显示:主轴实际转速 `speed_rpm`、主轴指令转速 `command_rpm`、G-code `S` 值、TP 实际进给 `velocity_mm_min`、G-code `F` 值、TP 速度/上限、F/S override、队列深度、活动段和当前执行行。所有值来自 WASM program/TP/IO 快照,参数区的 `data-xyzac-parameter-source` 为 `linuxcnc-wasm`;未取得快照时显示 `-`。 实时参数区显示:主轴实际转速 `speed_rpm`、主轴指令转速 `command_rpm`、G-code `S` 值、TP 实际进给 `velocity_mm_min`、G-code `F` 值、TP 速度/上限、F/S override、队列深度、活动段和当前执行行。所有值来自 WASM program/TP/IO 快照,参数区的 `data-xyzac-parameter-source` 为 `linuxcnc-wasm`;未取得快照时显示 `-`。
程序预览替换规则:`PROGRAM_LOAD` 开始时先清空旧的 canonical preview;如果 WASM 解析或子程序加载失败,错误响应中的路径仍为空,不会把旧程序刀路重新投影出来。新的 WASM preview 成功后才替换为新程序路径。
在 WASM 快照尚未到达前,界面显示 `WASM PENDING` 并禁用运动操作;不会使用 JS 默认轴值、任务状态或 override 值。浏览器定时器只发送 `SIM_STEP` 采样请求,不执行 G-code、轨迹、运动学或设备状态计算。React/Three 中的定时器、相机、网格、刀具网格和文本格式化均为显示层,不产生或修改任何 CNC 状态。 在 WASM 快照尚未到达前,界面显示 `WASM PENDING` 并禁用运动操作;不会使用 JS 默认轴值、任务状态或 override 值。浏览器定时器只发送 `SIM_STEP` 采样请求,不执行 G-code、轨迹、运动学或设备状态计算。React/Three 中的定时器、相机、网格、刀具网格和文本格式化均为显示层,不产生或修改任何 CNC 状态。
## 按钮/控件矩阵 ## 按钮/控件矩阵
@@ -225,6 +227,22 @@ screenshot: /tmp/published-live-parameters-r14.png
screenshot_sha256: 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e screenshot_sha256: 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e
``` ```
发布站点 r15(程序预览原子替换)验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-program-replace-r15
initial preview: 29 segments
invalid PROGRAM_LOAD: preview 0 segments, error shown, old path not retained
valid PROGRAM_LOAD: preview 1 new segment
browser_errors: 0
screenshot: /tmp/published-program-replace-r15.png
screenshot_sha256: c3e0a5b992381e7b6cb26f98a30338935df5239c875755727fc678911452ac24
resource_sha256: index-DUB3ldSh.js 4d71a2eb0b162608d7a47b63274cf274a57b1c2f2620b69af139b12c2ba22cce
sim-worker-entry-yfjiq6MN.js a0cf75522ddc9c6712126de86937ec89f24574bf0e0dad3dbddc6d807fb352b7
linuxcnc-sim-core-fuIvKetC.wasm ad4e17ecbf73afbad0d9f069ab20ba3c66b6cc4d75ecdb2b3182673490ee916d
```
发布站点 r10 连续性验证: 发布站点 r10 连续性验证:
```text ```text
@@ -304,6 +322,7 @@ switchkins_direct_test_output 24e6cb8ec1195a97f71294b49217db0cbc0d7c3ecfa4bc8aa2
panel_r10_output 9bddcd8b0219925e00598e081dc7a4ed0f4868e8822a21f0ccd08d8e9d355d45 panel_r10_output 9bddcd8b0219925e00598e081dc7a4ed0f4868e8822a21f0ccd08d8e9d355d45
pause_resume_browser_screenshot 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7 pause_resume_browser_screenshot 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7
live_parameters_browser_screenshot 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e live_parameters_browser_screenshot 7f5307c913d8bc4e60c2a31c29aacc9afd1b0d91fac4a4f22e8a64fc2ea37a1e
program_replace_browser_screenshot c3e0a5b992381e7b6cb26f98a30338935df5239c875755727fc678911452ac24
``` ```
报告对应提交前工作树保留了既有 coverage 大文件和 LinuxCNC 子模块改动;本轮提交包含 WASM TP/IO/HOME authority、无 JS JOG 运动计算、前端 pending 投影、长程序快照性能修正、聚合测试和本报告。 报告对应提交前工作树保留了既有 coverage 大文件和 LinuxCNC 子模块改动;本轮提交包含 WASM TP/IO/HOME authority、无 JS JOG 运动计算、前端 pending 投影、长程序快照性能修正、聚合测试和本报告。

View File

@@ -1926,6 +1926,11 @@ export class SimSession {
case "PROGRAM_LOAD": { case "PROGRAM_LOAD": {
if (["RUNNING", "PAUSED"].includes(this.task.state.state)) return finish(this.error(input, "task_state", "program_load_while_active"), "failed"); if (["RUNNING", "PAUSED"].includes(this.task.state.state)) return finish(this.error(input, "task_state", "program_load_while_active"), "failed");
const source = input.payload?.source ?? input.payload?.text ?? input.payload?.program_source; const source = input.payload?.source ?? input.payload?.text ?? input.payload?.program_source;
// PROGRAM_LOAD is a replacement transaction. Clear the previous
// canonical preview before asking the WASM interpreter to parse the
// new source, so a parse/subroutine error cannot re-publish the old
// toolpath as if it belonged to the requested file.
this.previewState = null;
if (this.xyzacProgram) { if (this.xyzacProgram) {
this.pendingXyzacMotion = null; this.pendingXyzacMotion = null;
this.pendingProgramControl = 0; this.pendingProgramControl = 0;

View File

@@ -0,0 +1,39 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import createModule from "../../build-wasm/linuxcnc-sim-core.js";
import { loadSimulationCase, toWorkerConfiguration } from "../workspace/sim-case-loader.mjs";
import { SimSession, CONTEXT_ID } from "./sim-session.mjs";
const CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini";
const fetchImpl = async (url) => {
try { const body = await readFile(new URL(`../../${String(url).replace(/^\//, "")}`, import.meta.url)); return { ok: true, text: async () => body.toString("utf8") }; }
catch { return { ok: false, text: async () => "" }; }
};
const bundle = await loadSimulationCase({ path: CASE, fetchImpl });
const core = await createModule({ wasmBinary: await readFile(new URL("../../build-wasm/linuxcnc-sim-core.wasm", import.meta.url)) });
const session = new SimSession(core, { sessionId: "xyzac-program-replace" });
const base = { schema_version: 1, context_id: CONTEXT_ID, session_id: session.sessionId };
let seq = 0;
const send = (kind, payload = {}) => session.accept({ ...base, kind, seq: ++seq, operation_id: `replace-${seq}`, payload });
send("HELLO"); send("SESSION_ATTACH");
assert.equal(send("LOAD_WORKSPACE", { root_path: "/workspace", configuration: toWorkerConfiguration(bundle) }).payload.status, "verified");
const first = send("PROGRAM_LOAD", { path: "first.ngc", source: "G21\nG1 X10 Y5 Z2 F100\nM2\n" });
assert.equal(first.payload.preview.segments.length, 1);
assert.equal(first.payload.panel_snapshot.visual.toolpath.length, 1);
const failed = send("PROGRAM_LOAD", { path: "invalid.ngc", source: "G999\n" });
assert.equal(failed.kind, "ERROR");
assert.equal(failed.payload.panel_snapshot.visual.toolpath.length, 0);
assert.equal(session.previewState, null);
const second = send("PROGRAM_LOAD", { path: "second.ngc", source: "G21\nG1 X1 Y2 Z3 F100\nM2\n" });
assert.equal(second.payload.program.path, "second.ngc");
assert.notEqual(second.payload.program.source_hash, first.payload.program.source_hash);
assert.equal(second.payload.panel_snapshot.visual.toolpath.length, 1);
console.log(JSON.stringify({
schema: "xyzac-program-replacement.v1",
status: "verified",
first_segments: first.payload.panel_snapshot.visual.toolpath.length,
failed_segments: failed.payload.panel_snapshot.visual.toolpath.length,
second_segments: second.payload.panel_snapshot.visual.toolpath.length,
old_preview_retained_after_failure: false,
}));