From 163a68ff4291f30c1bdb5696ad704d92654d13f5 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Tue, 16 Jun 2026 22:03:06 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86=E4=BB=BF=E7=9C=9F=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=A8=8B=E5=BA=8F=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 结论:将内置 G-code 测试程序从 simulation-app.js 移入 runtime/ui/simulation/programs/,建立一程序一模块的目录化程序库,并保持页面 API 与验证 gate 兼容。 --- PROJECT_COMPLETION_TRACKER.md | 1 + text15.txt | 43 +++++++ wasm-port/README.md | 3 + .../axis-style-simulation-implementation.md | 13 ++- .../ui/simulation/programs/arc-g2-g3.js | 14 +++ .../ui/simulation/programs/drill-g81.js | 16 +++ .../simulation/programs/incremental-loop.js | 15 +++ .../runtime/ui/simulation/programs/index.js | 28 +++++ .../ui/simulation/programs/pocket-z.js | 17 +++ .../ui/simulation/programs/square-linear.js | 15 +++ .../runtime/ui/simulation/simulation-app.js | 108 +++--------------- .../node/verify_real_simulation_programs.mjs | 15 +++ 12 files changed, 193 insertions(+), 95 deletions(-) create mode 100644 wasm-port/runtime/ui/simulation/programs/arc-g2-g3.js create mode 100644 wasm-port/runtime/ui/simulation/programs/drill-g81.js create mode 100644 wasm-port/runtime/ui/simulation/programs/incremental-loop.js create mode 100644 wasm-port/runtime/ui/simulation/programs/index.js create mode 100644 wasm-port/runtime/ui/simulation/programs/pocket-z.js create mode 100644 wasm-port/runtime/ui/simulation/programs/square-linear.js diff --git a/PROJECT_COMPLETION_TRACKER.md b/PROJECT_COMPLETION_TRACKER.md index 16a53f7..91f6254 100644 --- a/PROJECT_COMPLETION_TRACKER.md +++ b/PROJECT_COMPLETION_TRACKER.md @@ -42,6 +42,7 @@ The project is complete for the current scope when all of the following are true | Real browser simulation priority | Active first priority | Future batches should prioritize the real UI/browser CNC simulation page, with visible machine/session loading, G-code program state, LinuxCNC-backed interpreter execution, machine readiness/status panels, toolpath/preview rendering from LinuxCNC-produced output or validated runtime events, and browser smoke coverage. First entry point: `wasm-port/runtime/ui/simulation/index.html`; gate: `wasm-port/tests/browser/verify_real_simulation_browser.sh`. See `wasm-port/docs/real-browser-simulation-priority.md`. | | Real browser simulation page | MVP active | `runtime/ui/simulation/index.html` loads in Chromium, exposes a selector for multiple LinuxCNC-backed test programs, runs them through interpreter WASM, and now includes reset/step/play/finish playback over LinuxCNC canonical motion events. It highlights active G-code and motion rows, updates live axis readout, moves the toolhead, and grows the executed SVG toolpath. Current program set covers square contour, Z pocket contour, incremental loop, G2/G3 arcs, and G81 drilling. | | AXIS-style simulation shell | Phase 1 active | `runtime/ui/simulation/index.html` now uses an AXIS-inspired titlebar, menubar, toolbar, Manual/MDI tabs, Preview/DRO tabs, G-code pane, machine-state pane, and status bar while preserving current LinuxCNC-backed execution and playback APIs. Browser smoke verifies shell regions, tab switching, toolbar playback, and existing program execution. | +| Simulation test program library | Active | Built-in browser simulation programs live in `wasm-port/runtime/ui/simulation/programs/`, one module per program, exported through `programs/index.js`; `verify_real_simulation_programs.sh` checks directory-backed inventory, IDs, defaults, and playback contracts. | | Vendor/source guard | Stable | `verify_vendor_sync.sh` and standalone semantic guard are required every batch. | | Interpreter WASM smoke | Stable | `interp_wasm_node_smoke=ok`. | | Sim config inventory | Stable with skips | `executed=28`, `passed=28`, `skipped=131`, `unexpected_fail=0`; `docs/sim-configs-coverage-handoff.md` records the release-gate baseline. | diff --git a/text15.txt b/text15.txt index d331301..c3b0219 100644 --- a/text15.txt +++ b/text15.txt @@ -1844,3 +1844,46 @@ real_simulation_programs_node_smoke=ok - Program execution 仍通过 `createLinuxCncInterpSdk()` / LinuxCNC WASM; - Preview、DRO、G-code active line 和 playback 都来自 LinuxCNC-produced canonical motion event; - 禁用的 manual/spindle/coolant/jog 控件只显示 blocked/readiness 状态,不伪造机器动作。 + +八十三、2026-06-16 继续执行记录:仿真测试程序目录化 + +用户要求把程序移动到新目录,方便后续新增其它测试程序。本批把 simulation 内置 G-code 程序 +从 `simulation-app.js` 拆出,建立独立程序库目录。 + +本批新增/调整: + +- 新增目录: + - `runtime/ui/simulation/programs/`; +- 每个测试程序独立一个模块: + - `square-linear.js`; + - `pocket-z.js`; + - `incremental-loop.js`; + - `arc-g2-g3.js`; + - `drill-g81.js`; +- 新增 `runtime/ui/simulation/programs/index.js` 统一导出: + - `SIMULATION_TEST_PROGRAMS`; + - `DEFAULT_SIMULATION_PROGRAM_ID`; + - `DEFAULT_SIMULATION_PROGRAM`; + - `getSimulationTestPrograms()`; + - `getSimulationTestProgram(programId)`; +- `simulation-app.js` 改为从 `programs/index.js` 导入并 re-export,保持原有页面/API/test import + 兼容; +- `verify_real_simulation_programs.mjs` 新增目录化校验: + - `programs/*.js` 文件数必须等于程序数量; + - simulation-app re-export 必须与 directory-backed inventory 一致; + - 默认程序和 ID 唯一性保持不变; +- README、AXIS-style implementation docs、PROJECT_COMPLETION_TRACKER 同步记录程序目录约定。 + +后续新增测试程序规则: + +1. 在 `runtime/ui/simulation/programs/` 新增一个 `*.js` 模块; +2. 模块导出 `{ id, label, category, text, expectedMotionTypes }`; +3. 在 `programs/index.js` 导入并加入 `SIMULATION_TEST_PROGRAMS`; +4. 跑 `wasm-port/tests/ui/node/verify_real_simulation_programs.sh` 和 + `SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_real_simulation_browser.sh`。 + +本批仍保持 LinuxCNC 语义边界: + +- 程序目录只保存 G-code fixture text 和 expected LinuxCNC canonical motion type 断言; +- 执行仍通过 LinuxCNC-backed WASM SDK; +- JS 没有新增 G-code 解释、planner 或 machine semantics。 diff --git a/wasm-port/README.md b/wasm-port/README.md index 4995244..f57c521 100644 --- a/wasm-port/README.md +++ b/wasm-port/README.md @@ -26,6 +26,9 @@ toolhead over the executed toolpath. The current UI direction is the AXIS-style simulation shell in `docs/axis-style-simulation-implementation.md`; Phase 1 is implemented in `runtime/ui/simulation/index.html`. +Built-in simulation test programs live under +`runtime/ui/simulation/programs/` and are exported by +`runtime/ui/simulation/programs/index.js`. The project release gate for the current scope is: diff --git a/wasm-port/docs/axis-style-simulation-implementation.md b/wasm-port/docs/axis-style-simulation-implementation.md index e76dde9..de5cc3b 100644 --- a/wasm-port/docs/axis-style-simulation-implementation.md +++ b/wasm-port/docs/axis-style-simulation-implementation.md @@ -55,7 +55,7 @@ Browser JavaScript must not implement: The current simulation page already has: - LinuxCNC interpreter WASM execution through `createLinuxCncInterpSdk()`; -- selectable test programs; +- selectable test programs under `runtime/ui/simulation/programs/`; - canonical output rendering; - program-line rendering; - motion table; @@ -134,6 +134,17 @@ Upgrade the G-code pane and execution controls: - highlight active G-code line and current motion row; - show run status, source program, motion count, and current frame. +Built-in test programs must live in: + +```text +runtime/ui/simulation/programs/ +``` + +Each program should have its own module and be exported through +`runtime/ui/simulation/programs/index.js`. The simulation renderer should keep +importing the inventory through the program index rather than embedding G-code +fixtures in `simulation-app.js`. + Validation: - browser smoke runs at least one linear program, one arc program, and one diff --git a/wasm-port/runtime/ui/simulation/programs/arc-g2-g3.js b/wasm-port/runtime/ui/simulation/programs/arc-g2-g3.js new file mode 100644 index 0000000..36266ea --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/arc-g2-g3.js @@ -0,0 +1,14 @@ +export const arcG2G3Program = { + id: "arc-g2-g3", + label: "G2/G3 arc path", + category: "Arc", + text: [ + "G90 G17 G0 X0 Y0 Z0", + "G91.1", + "G2 X1 Y1 I1 J0 F80", + "G3 X0 Y0 I0 J-1", + "M2", + "", + ].join("\n"), + expectedMotionTypes: ["STRAIGHT_TRAVERSE", "ARC_FEED"], +}; diff --git a/wasm-port/runtime/ui/simulation/programs/drill-g81.js b/wasm-port/runtime/ui/simulation/programs/drill-g81.js new file mode 100644 index 0000000..8f43fa0 --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/drill-g81.js @@ -0,0 +1,16 @@ +export const drillG81Program = { + id: "drill-g81", + label: "G81 drill pattern", + category: "Cycle", + text: [ + "G90 G17 G0 X0 Y0 Z1", + "G81 X0.5 Y0.5 Z-0.25 R0.1 F20", + "X1.0 Y0.5", + "X1.0 Y1.0", + "G80", + "G0 X0 Y0 Z1", + "M2", + "", + ].join("\n"), + expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], +}; diff --git a/wasm-port/runtime/ui/simulation/programs/incremental-loop.js b/wasm-port/runtime/ui/simulation/programs/incremental-loop.js new file mode 100644 index 0000000..d9ae7e5 --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/incremental-loop.js @@ -0,0 +1,15 @@ +export const incrementalLoopProgram = { + id: "incremental-loop", + label: "Incremental loop", + category: "Modal", + text: [ + "G90 G17 G0 X0 Y0 Z0", + "G91 G1 X0.5 Y0 F60", + "G1 X0 Y0.5", + "G1 X-0.5 Y0", + "G90 G0 X0 Y0 Z0", + "M2", + "", + ].join("\n"), + expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], +}; diff --git a/wasm-port/runtime/ui/simulation/programs/index.js b/wasm-port/runtime/ui/simulation/programs/index.js new file mode 100644 index 0000000..5cf9e51 --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/index.js @@ -0,0 +1,28 @@ +import { arcG2G3Program } from "./arc-g2-g3.js"; +import { drillG81Program } from "./drill-g81.js"; +import { incrementalLoopProgram } from "./incremental-loop.js"; +import { pocketZProgram } from "./pocket-z.js"; +import { squareLinearProgram } from "./square-linear.js"; + +export const SIMULATION_TEST_PROGRAMS = [ + squareLinearProgram, + pocketZProgram, + incrementalLoopProgram, + arcG2G3Program, + drillG81Program, +]; + +export const DEFAULT_SIMULATION_PROGRAM_ID = SIMULATION_TEST_PROGRAMS[0].id; +export const DEFAULT_SIMULATION_PROGRAM = SIMULATION_TEST_PROGRAMS[0].text; + +export function getSimulationTestPrograms() { + return SIMULATION_TEST_PROGRAMS.map((program) => ({ ...program })); +} + +export function getSimulationTestProgram(programId = DEFAULT_SIMULATION_PROGRAM_ID) { + const program = SIMULATION_TEST_PROGRAMS.find(({ id }) => id === programId); + if (!program) { + throw new Error(`unknown simulation test program: ${programId}`); + } + return program; +} diff --git a/wasm-port/runtime/ui/simulation/programs/pocket-z.js b/wasm-port/runtime/ui/simulation/programs/pocket-z.js new file mode 100644 index 0000000..e0d34b0 --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/pocket-z.js @@ -0,0 +1,17 @@ +export const pocketZProgram = { + id: "pocket-z", + label: "Pocket with Z moves", + category: "Linear", + text: [ + "G90 G17 G0 X0 Y0 Z1", + "G1 Z-0.25 F40", + "G1 X2 Y0 F120", + "G1 X2 Y1", + "G1 X0 Y1", + "G1 X0 Y0", + "G0 Z1", + "M2", + "", + ].join("\n"), + expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], +}; diff --git a/wasm-port/runtime/ui/simulation/programs/square-linear.js b/wasm-port/runtime/ui/simulation/programs/square-linear.js new file mode 100644 index 0000000..f24e856 --- /dev/null +++ b/wasm-port/runtime/ui/simulation/programs/square-linear.js @@ -0,0 +1,15 @@ +export const squareLinearProgram = { + id: "square-linear", + label: "Square contour", + category: "Linear", + text: [ + "G90 G17 G0 X0 Y0 Z0", + "G1 X1 Y0 Z0 F100", + "G1 X1 Y1 Z0", + "G1 X0 Y1 Z0", + "G1 X0 Y0 Z0", + "M2", + "", + ].join("\n"), + expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], +}; diff --git a/wasm-port/runtime/ui/simulation/simulation-app.js b/wasm-port/runtime/ui/simulation/simulation-app.js index 0bda02d..e79d7c8 100644 --- a/wasm-port/runtime/ui/simulation/simulation-app.js +++ b/wasm-port/runtime/ui/simulation/simulation-app.js @@ -1,87 +1,19 @@ import { createLinuxCncInterpSdk } from "../../sdk/src/index.js"; +import { + DEFAULT_SIMULATION_PROGRAM, + DEFAULT_SIMULATION_PROGRAM_ID, + SIMULATION_TEST_PROGRAMS, + getSimulationTestProgram, + getSimulationTestPrograms, +} from "./programs/index.js"; -export const SIMULATION_TEST_PROGRAMS = [ - { - id: "square-linear", - label: "Square contour", - category: "Linear", - text: [ - "G90 G17 G0 X0 Y0 Z0", - "G1 X1 Y0 Z0 F100", - "G1 X1 Y1 Z0", - "G1 X0 Y1 Z0", - "G1 X0 Y0 Z0", - "M2", - "", - ].join("\n"), - expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], - }, - { - id: "pocket-z", - label: "Pocket with Z moves", - category: "Linear", - text: [ - "G90 G17 G0 X0 Y0 Z1", - "G1 Z-0.25 F40", - "G1 X2 Y0 F120", - "G1 X2 Y1", - "G1 X0 Y1", - "G1 X0 Y0", - "G0 Z1", - "M2", - "", - ].join("\n"), - expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], - }, - { - id: "incremental-loop", - label: "Incremental loop", - category: "Modal", - text: [ - "G90 G17 G0 X0 Y0 Z0", - "G91 G1 X0.5 Y0 F60", - "G1 X0 Y0.5", - "G1 X-0.5 Y0", - "G90 G0 X0 Y0 Z0", - "M2", - "", - ].join("\n"), - expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], - }, - { - id: "arc-g2-g3", - label: "G2/G3 arc path", - category: "Arc", - text: [ - "G90 G17 G0 X0 Y0 Z0", - "G91.1", - "G2 X1 Y1 I1 J0 F80", - "G3 X0 Y0 I0 J-1", - "M2", - "", - ].join("\n"), - expectedMotionTypes: ["STRAIGHT_TRAVERSE", "ARC_FEED"], - }, - { - id: "drill-g81", - label: "G81 drill pattern", - category: "Cycle", - text: [ - "G90 G17 G0 X0 Y0 Z1", - "G81 X0.5 Y0.5 Z-0.25 R0.1 F20", - "X1.0 Y0.5", - "X1.0 Y1.0", - "G80", - "G0 X0 Y0 Z1", - "M2", - "", - ].join("\n"), - expectedMotionTypes: ["STRAIGHT_TRAVERSE", "STRAIGHT_FEED"], - }, -]; - -export const DEFAULT_SIMULATION_PROGRAM_ID = SIMULATION_TEST_PROGRAMS[0].id; -export const DEFAULT_SIMULATION_PROGRAM = SIMULATION_TEST_PROGRAMS[0].text; +export { + DEFAULT_SIMULATION_PROGRAM, + DEFAULT_SIMULATION_PROGRAM_ID, + SIMULATION_TEST_PROGRAMS, + getSimulationTestProgram, + getSimulationTestPrograms, +}; const AXES = ["x", "y", "z", "a", "b", "c", "u", "v", "w"]; const PLANE_AXIS_MAP = { @@ -383,18 +315,6 @@ export function renderSimulationState(documentRef, state) { renderSimulationPlaybackFrame(documentRef, state, state.motion.length - 1); } -export function getSimulationTestPrograms() { - return SIMULATION_TEST_PROGRAMS.map((program) => ({ ...program })); -} - -export function getSimulationTestProgram(programId = DEFAULT_SIMULATION_PROGRAM_ID) { - const program = SIMULATION_TEST_PROGRAMS.find(({ id }) => id === programId); - if (!program) { - throw new Error(`unknown simulation test program: ${programId}`); - } - return program; -} - export async function runRealBrowserSimulation({ documentRef = document, programId = DEFAULT_SIMULATION_PROGRAM_ID, diff --git a/wasm-port/tests/ui/node/verify_real_simulation_programs.mjs b/wasm-port/tests/ui/node/verify_real_simulation_programs.mjs index b1b5b97..a9cf5e2 100644 --- a/wasm-port/tests/ui/node/verify_real_simulation_programs.mjs +++ b/wasm-port/tests/ui/node/verify_real_simulation_programs.mjs @@ -1,4 +1,7 @@ import assert from "node:assert/strict"; +import { readdirSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; import { DEFAULT_SIMULATION_PROGRAM, @@ -10,12 +13,24 @@ import { getSimulationTestPrograms, parseLinuxCncCanonicalMotion, } from "../../../runtime/ui/simulation/simulation-app.js"; +import { + SIMULATION_TEST_PROGRAMS as directoryPrograms, +} from "../../../runtime/ui/simulation/programs/index.js"; const programs = getSimulationTestPrograms(); +const root = resolve(dirname(fileURLToPath(import.meta.url)), "../../.."); +const programDir = resolve(root, "runtime/ui/simulation/programs"); +const programFiles = readdirSync(programDir).filter((file) => file.endsWith(".js") && file !== "index.js"); assert.equal(DEFAULT_SIMULATION_PROGRAM_ID, "square-linear"); assert.equal(getSimulationTestProgram(DEFAULT_SIMULATION_PROGRAM_ID).text, DEFAULT_SIMULATION_PROGRAM); assert.ok(programs.length >= 5, "real simulation program inventory should cover multiple examples"); +assert.equal(programFiles.length, programs.length, "each simulation test program should live in its own programs/*.js file"); +assert.deepEqual( + directoryPrograms.map(({ id }) => id), + programs.map(({ id }) => id), + "simulation-app should re-export the directory-backed program inventory", +); for (const program of programs) { assert.ok(program.id, "program id should be present");