Fix paused SIM_STEP resume sequencing

This commit is contained in:
2026-09-02 11:40:28 +08:00
parent 32adda73d6
commit cd0286a3d6
6 changed files with 71 additions and 14 deletions

View File

@@ -57,7 +57,7 @@
},
"published_browser_smoke": {
"url": "https://49.232.250.35:58010/",
"release": "20260902-linuxcnc-wasm-continuous-r10",
"release": "20260902-linuxcnc-wasm-pause-resume-r11",
"status": "verified",
"startup_pause_resume_stop": "verified",
"pause_tick": 140,
@@ -73,15 +73,25 @@
"screenshot_sha256": "c93fd23e648e49bfa9359af245372f03f3fccee123497909f3dcc005f5a59749",
"browser_errors": 0
},
"pause_resume_runtime": {
"before_pause": {"line": 62, "tick": 140, "queue_depth": 1, "velocity": 28.2842712475},
"paused": {"line": 62, "tick": 145, "queue_depth": 1, "velocity": 28.2842712475},
"paused_after_1_5s": {"tick": 145},
"resumed": {"line": 62, "tick": 160, "queue_depth": 1, "velocity": 4.96408252063},
"after_resume": {"line": 68, "tick": 360, "queue_depth": 2, "velocity": 32.829526006},
"status": "verified",
"browser_errors": 0,
"screenshot_sha256": "9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7"
},
"screenshot_sha256": {
"authority": "e58205228a04b386c7b7514bf03c71df7b53f66f2d9ebe2292931b36ef0edb55",
"running": "7fc474d0cb31a0fdd4afb198061e9812c753684e3d2963cc004ec4483c5c4990",
"switchkins": "920b85ad7fd6451dfb134255ffdcc4f03a7aa615ebf486c63cde8582e17af195"
},
"resource_sha256": {
"index-Byst0JhC.js": "26d3f21f496c8223218761ba641dab8b4f36fd18892f260c7bf5b5cfa83cafec",
"sim-worker-entry-BTVfbu6g.js": "adf2ac5621ba388408f31ceab4bbcbfa6ba2923577d8eb69aa79ea54f3be0134",
"linuxcnc-sim-core-B2786u4k.wasm": "3163f5ff63c5f3e1d2c07d1d948fce328bf3ad919eee188e432f7ba57468fc71"
"index-EXbw4yiL.js": "6759738df285bc5e2f5c3fb84043dc09fb82292bbc1c3c1011a558c8654c96ab",
"sim-worker-entry-DjXIMZ0D.js": "190515af943d70e93037e88a5cf63b3b8e9cda675a8338cfc1fb4bb954bfd685",
"linuxcnc-sim-core-D3Qw0e1g.wasm": "f1b1961279cf8ff7ef2e3fcd0068b3ba79de57abe1e56d5c32b16d17568b9536"
},
"authority_dom": {
"data-axis-authority": "linuxcnc-wasm-worker",

View File

@@ -172,6 +172,26 @@ bash -n run-5axis-rtcp.sh
本轮连续性修复后,`3D_Chips.ngc` 3000 tick 执行到第 337 行,`running_queue_gaps=0`;这证明长距离运行中的中间停顿已不再由 Worker 队列饥饿制造。
暂停/恢复修复:暂停期间若有一个 `SIM_STEP` 已在浏览器与 Worker 之间传输,WASM protocol 现在接受该帧为稳定无动作轮询,不会消耗错误序号;随后 `PROGRAM_RESUME` 正常恢复 source TP。
发布站点 r11 暂停/恢复验证:
```text
url: https://49.232.250.35:58010/
release: 20260902-linuxcnc-wasm-pause-resume-r11
resource_sha256: index-EXbw4yiL.js 6759738df285bc5e2f5c3fb84043dc09fb82292bbc1c3c1011a558c8654c96ab
sim-worker-entry-DjXIMZ0D.js 190515af943d70e93037e88a5cf63b3b8e9cda675a8338cfc1fb4bb954bfd685
linuxcnc-sim-core-D3Qw0e1g.wasm f1b1961279cf8ff7ef2e3fcd0068b3ba79de57abe1e56d5c32b16d17568b9536
before_pause: line=62, tick=140, queue_depth=1, velocity=28.2842712475
paused: line=62, tick=145, queue_depth=1, velocity=28.2842712475
paused_after_1.5s: tick=145 (stable)
resumed: line=62, tick=160, queue_depth=1, velocity=4.96408252063
after_resume: line=68, tick=360, queue_depth=2, velocity=32.8295260060
PROGRAM_RESUME: acknowledged; browser_errors: 0
screenshot: /tmp/published-pause-resume-final-r11c.png
screenshot_sha256: 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7
```
发布站点 r10 连续性验证:
```text
@@ -249,6 +269,7 @@ wasm_io_c_source 09bce1430d6f71702ca0b3bf29ac50db377a6f232ce0d1a9b2c1eaa
panel_button_test_source 80782fc1f85f88beb48ec3dfaf638a93f2edb912e694840199dbff504477fe74
switchkins_direct_test_output 24e6cb8ec1195a97f71294b49217db0cbc0d7c3ecfa4bc8aa244ba3921c816a7
panel_r10_output 9bddcd8b0219925e00598e081dc7a4ed0f4868e8822a21f0ccd08d8e9d355d45
pause_resume_browser_screenshot 9d1660fd043dd0628ff2989a3e139a3239acd6386d141657a5834c9d2f1bc5a7
```
报告对应提交前工作树保留了既有 coverage 大文件和 LinuxCNC 子模块改动;本轮提交包含 WASM TP/IO/HOME authority、无 JS JOG 运动计算、前端 pending 投影、长程序快照性能修正、聚合测试和本报告。

View File

@@ -49,6 +49,7 @@ static int state_accepts_kind(uint32_t current, const char *kind) {
return is_kind(kind, "PROGRAM_RESUME") || is_kind(kind, "RESUME") ||
is_kind(kind, "PROGRAM_ABORT") || is_kind(kind, "ESTOP") ||
is_kind(kind, "POWER") || is_kind(kind, "SET_OVERRIDE") ||
is_kind(kind, "SIM_STEP") ||
is_kind(kind, "PING") || is_kind(kind, "SNAPSHOT_REQUEST") ||
is_kind(kind, "DESTROY");
}

View File

@@ -14,16 +14,17 @@ int main(void) {
assert(sim_protocol_accept(1, 4, "RESET", "reset-machine", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_last_seq() == 4);
assert(sim_protocol_accept(1, 5, "PROGRAM_PAUSE", "op-4", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 6, "MDI_EXECUTE", "op-5", 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 6, "PROGRAM_ABORT", "op-5", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 6, "SIM_STEP", "op-5-step", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 7, "MDI_EXECUTE", "op-5", 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 7, "PROGRAM_ABORT", "op-5", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_state_value() == SIM_PROTOCOL_READY);
assert(sim_protocol_accept(1, 7, "PROGRAM_PAUSE", "op-6", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 8, "PROGRAM_RESUME", "op-7", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(2, 9, "PING", NULL, 2) == SIM_PROTOCOL_ERR_SCHEMA);
assert(sim_protocol_accept(1, 9, "PING", NULL, 1) == SIM_PROTOCOL_ERR_TICK);
assert(sim_protocol_accept(1, 9, "DESTROY", NULL, 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 10, "PING", NULL, 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 11, "RESET", "reset-1", 0) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 8, "PROGRAM_PAUSE", "op-6", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 9, "PROGRAM_RESUME", "op-7", 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(2, 10, "PING", NULL, 2) == SIM_PROTOCOL_ERR_SCHEMA);
assert(sim_protocol_accept(1, 10, "PING", NULL, 1) == SIM_PROTOCOL_ERR_TICK);
assert(sim_protocol_accept(1, 10, "DESTROY", NULL, 2) == SIM_PROTOCOL_OK);
assert(sim_protocol_accept(1, 11, "PING", NULL, 2) == SIM_PROTOCOL_ERR_STATE);
assert(sim_protocol_accept(1, 12, "RESET", "reset-1", 0) == SIM_PROTOCOL_OK);
assert(sim_protocol_state_value() == SIM_PROTOCOL_NEW);
assert(sim_protocol_set_tick(4) == SIM_PROTOCOL_ERR_TICK);
return 0;

View File

@@ -1188,7 +1188,8 @@ export class SimSession {
});
}
if (Number.isSafeInteger(input.simulation_tick) && input.simulation_tick >= this.tick) this.tick = input.simulation_tick;
const eventTick = input.kind === "SIM_STEP" && Number.isSafeInteger(input.payload?.cycles)
const pausedStep = input.kind === "SIM_STEP" && this.xyzacProgram && (this.task.state.state === "PAUSED" || this.xyzacProgram.snapshot().status === 3);
const eventTick = input.kind === "SIM_STEP" && Number.isSafeInteger(input.payload?.cycles) && !pausedStep
? this.tick + input.payload.cycles : this.tick;
try {
this.timeline.appendEvent(input.seq, eventTick, input.kind, input.payload ?? {});
@@ -1535,6 +1536,26 @@ export class SimSession {
const cycles = input.payload?.cycles;
const limit = validateLimit("max_tick_step", cycles);
if (limit.status !== "accepted") return finish(this.envelope("ERROR", input, { ...limit, operation_id: input.operation_id ?? null, simulation_tick: this.tick, retryable: false }), "failed");
// A poll may already be in flight when PROGRAM_PAUSE reaches the
// worker. LinuxCNC keeps the paused task/TP state stable; accept that
// transport frame as a no-op instead of rejecting it at the protocol
// layer and leaving the next PROGRAM_RESUME with a sequence gap.
if (this.xyzacProgram && (this.task.state.state === "PAUSED" || this.xyzacProgram.snapshot().status === 3)) {
const pausedProgram = this.xyzacProgram.snapshot();
return finish(this.envelope("COMMAND_RESULT", input, {
status: "verified",
cycles,
program_steps: [],
execution_trace: [],
snapshot_mode: input.payload?.response === "motion_delta" ? "motion_delta" : "full",
program: pausedProgram,
task: this.task.export(),
wasm_motion: this.wasmMotion.read(),
motion: this.wasmMotionProjection(),
motion_authority: "wasm",
operation_id: input.operation_id ?? null,
}), "verified");
}
let program_steps = [];
const motion_trace = [];
let sourceLookaheadReady = false;

View File

@@ -30,6 +30,9 @@ const cursorPaused = paused.payload.program.current_line;
const pausedSnapshot = send("SNAPSHOT_REQUEST", "paused-snapshot");
assert.equal(pausedSnapshot.payload.task.state, "PAUSED");
assert.equal(pausedSnapshot.payload.panel_snapshot.program.current_line, cursorPaused);
const pausedPoll = send("SIM_STEP", "paused-poll", { cycles: 5, response: "motion_delta" });
assert.equal(pausedPoll.payload.task.state, "PAUSED");
assert.equal(pausedPoll.payload.program.program_status, "paused");
const resumed = send("PROGRAM_RESUME", "resume");
assert.equal(resumed.payload.task.state, "RUNNING");
const second = send("SIM_STEP", "step-2", { cycles: 1 });