From 544bfd4b4b8d5d02970d66706b00cb5b49454ce0 Mon Sep 17 00:00:00 2001 From: wangdequan Date: Fri, 26 Jun 2026 18:19:49 -0400 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E9=BD=90=20gmoccapy=20XYZAB=20?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E4=BA=92=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/src/state/linuxcnc-task-policy.js | 6 + .../app/src/ui/gmoccapy-shell.js | 11 +- .../gptlog-process/gpdlog.md | 164 ++++++++++++++++++ .../tests/browser/gmoccapy_shell_smoke.html | 68 ++++++++ .../node/verify_gmoccapy_xyzab_gates.mjs | 25 ++- .../tests/node/verify_run_preconditions.mjs | 6 +- work/working5/01-项目功能内容.md | 56 ++++++ work/working5/02-项目程序开发详细步骤.md | 48 +++++ work/working5/03-推进台账.md | 59 +++++++ work/working5/04-任务矩阵.md | 16 ++ work/working5/05-验收证据.md | 69 ++++++++ work/working5/06-决策记录.md | 42 +++++ work/working5/README.md | 27 +++ 13 files changed, 591 insertions(+), 6 deletions(-) create mode 100644 work/working5/01-项目功能内容.md create mode 100644 work/working5/02-项目程序开发详细步骤.md create mode 100644 work/working5/03-推进台账.md create mode 100644 work/working5/04-任务矩阵.md create mode 100644 work/working5/05-验收证据.md create mode 100644 work/working5/06-决策记录.md create mode 100644 work/working5/README.md diff --git a/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js b/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js index 03d40d8..3f1011a 100644 --- a/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js +++ b/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js @@ -218,6 +218,12 @@ function gateMode(status, requestedMode) { if (status.taskMode === "auto" && status.interpState !== "idle" && targetMode !== "auto") { return block(status, "mode blocked: AUTO interpreter is not idle"); } + if (status.taskState !== "on") { + return block(status, `mode blocked: machine must be on before ${targetMode.toUpperCase()}`); + } + if ((targetMode === "mdi" || targetMode === "auto") && !status.allHomed && !status.noForceHoming) { + return block(status, `mode blocked: home machine before ${targetMode.toUpperCase()}`); + } return allow(status); } diff --git a/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js b/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js index 6fd3eed..45444b4 100644 --- a/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js +++ b/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js @@ -349,14 +349,17 @@ function mdiQuickCommands(state) { function renderSidebar(element, state, dispatch) { const tcpKinsType = tcpKinsTypeForProfile(state.profile); const tcpDisabled = tcpKinsType ? "" : ` disabled title="${escapeHtml(`${state.profile.id} is ${state.profile.kinematics} reference only`)}"`; + const autoGate = gateLinuxCncTaskAction(state, { type: "SET_MODE", mode: "auto" }); + const manualGate = gateLinuxCncTaskAction(state, { type: "SET_MODE", mode: "manual" }); + const mdiGate = gateLinuxCncTaskAction(state, { type: "SET_MODE", mode: "mdi" }); element.innerHTML = ` ${iconButtonHtml({ buttonId: "tbtn_estop", action: "estop", label: "E-STOP", title: "Estop the machine / F1", className: "sidebar-button estop", active: state.machine.estopActive })} ${iconButtonHtml({ buttonId: "tbtn_on", action: "power", label: "POWER", title: "Turn the machine on/off / F2", className: "sidebar-button power", active: state.machine.powerOn })} ${iconButtonHtml({ buttonId: "tbtn_setup", action: "reset", label: "RESET", title: "Reset estop and return machine off", className: "sidebar-button", active: false })} - ${iconButtonHtml({ buttonId: "rbt_auto", action: "mode-auto", label: "AUTO", title: "Enter auto mode", className: "sidebar-button", active: state.machine.mode === "auto" })} - ${iconButtonHtml({ buttonId: "rbt_manual", action: "mode-manual", label: "MANUAL", title: "Enter manual mode", className: "sidebar-button", active: state.machine.mode === "manual" })} - ${iconButtonHtml({ buttonId: "tbtn_user_tabs", action: "mode-jog", label: "JOG", title: "Manual jog page", className: "sidebar-button", active: state.machine.mode === "manual" })} - ${iconButtonHtml({ buttonId: "rbt_mdi", action: "mode-mdi", label: "MDI", title: "Enter MDI mode", className: "sidebar-button", active: state.machine.mode === "mdi" })} + ${iconButtonHtml({ buttonId: "rbt_auto", action: "mode-auto", label: "AUTO", title: autoGate.operatorMessage || "Enter auto mode", className: "sidebar-button", active: state.machine.mode === "auto", gate: autoGate })} + ${iconButtonHtml({ buttonId: "rbt_manual", action: "mode-manual", label: "MANUAL", title: manualGate.operatorMessage || "Enter manual mode", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate })} + ${iconButtonHtml({ buttonId: "tbtn_user_tabs", action: "mode-jog", label: "JOG", title: manualGate.operatorMessage || "Manual jog page", className: "sidebar-button", active: state.machine.mode === "manual", gate: manualGate })} + ${iconButtonHtml({ buttonId: "rbt_mdi", action: "mode-mdi", label: "MDI", title: mdiGate.operatorMessage || "Enter MDI mode", className: "sidebar-button", active: state.machine.mode === "mdi", gate: mdiGate })} diff --git a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md index 5fb857a..c32bd45 100644 --- a/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md +++ b/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md @@ -4779,3 +4779,167 @@ Outcome: - The gmoccapy XYZAB `working4` completion commit was uploaded to the cloud repository. - Remote `master` now points to `a81b40447de313117e2a389a394c3b8d80931001`. - A follow-up process-log commit is required because this log entry was created after the main upload commit. + +--- + +## 2026-06-26 18:15 EDT - working5 LinuxCNC gmoccapy_XYZAB execution-flow alignment + +User request: +- Compare `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` against the five-axis machine execution flow documented in `/home/meswork/cnc_wams/work/working3/gmoccapy_XYZAB_execution_analysis.md`. +- Execute, test, and improve the LinuxCNC-ported CNC simulation so its simulation behavior matches the LinuxCNC source implementation. +- First create working files in `/home/meswork/cnc_wams/work/working5`: + - `01-项目功能内容` + - `02-项目程序开发详细步骤` + - `03-推进台账` + - `04-任务矩阵` + - `05-验收证据` + - `06-决策记录` + - plus README index. + +Process: +- Confirmed workspace and root instructions: + - `/home/meswork/cnc_wams` + - `AGENTS.md` requires appending process logs to `web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md`. +- Read the LinuxCNC execution-flow reference: + - `/home/meswork/cnc_wams/work/working3/gmoccapy_XYZAB_execution_analysis.md` + - Key baseline: `gmoccapy_XYZAB.ini`, `trivkins coordinates=xyzab`, five joints, `NO_FORCE_HOMING = 0`, NML command/status/error, HAL loopbacks, POSTGUI after `halcomp.ready()`, and gmoccapy button sensitivity rules. +- Inspected project structure and test inventory: + - `app/src/runtime` + - `app/src/state` + - `app/src/ui` + - `app/src/profiles` + - `tests/node` + - `tests/browser` +- Created working5 control documents: + - `/home/meswork/cnc_wams/work/working5/README.md` + - `/home/meswork/cnc_wams/work/working5/01-项目功能内容.md` + - `/home/meswork/cnc_wams/work/working5/02-项目程序开发详细步骤.md` + - `/home/meswork/cnc_wams/work/working5/03-推进台账.md` + - `/home/meswork/cnc_wams/work/working5/04-任务矩阵.md` + - `/home/meswork/cnc_wams/work/working5/05-验收证据.md` + - `/home/meswork/cnc_wams/work/working5/06-决策记录.md` +- Ran initial baseline tests: + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke:node` + - Result: passed. + - Key output included: + - `linuxcnc_kinematics_runtime_smoke=ok` + - `linuxcnc_interpreter_runtime_smoke=ok` + - `linuxcnc_ini_runtime_smoke=ok` + - `linuxcnc_task_hal_runtime_smoke=ok` + - `full_linuxcnc_5axis_source_node_smoke=ok` + - `real_linuxcnc_5axis_program_cases_smoke=ok` + - `gmoccapy_xyzab_profile_smoke=ok` + - `gmoccapy_communication_model_smoke=ok` + - `gmoccapy_hal_model_smoke=ok` + - `gmoccapy_xyzab_gates_smoke=ok` + - `linear_unit_conversion_smoke=ok` + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run build` + - Result: passed, output `gmoccapy_static_build=ok`. + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke` + - Result: passed, output `gmoccapy_shell_smoke=ok` and `gmoccapy_dist_smoke=ok`. +- Reviewed relevant implementation files: + - `app/src/profiles/gmoccapy-xyzab.js` + - `app/src/state/linuxcnc-task-policy.js` + - `app/src/state/store.js` + - `app/src/ui/gmoccapy-shell.js` + - `app/src/runtime/gmoccapy-hal-model.js` + - `app/src/runtime/gmoccapy-communication-model.js` + - `tests/node/verify_gmoccapy_xyzab_gates.mjs` + - `tests/node/verify_run_preconditions.mjs` + - `tests/browser/gmoccapy_shell_smoke.html` +- Compared against LinuxCNC/gmoccapy source: + - `/home/meswork/cnc_wams/linuxcnc/src/emc/usr_intf/gmoccapy/gmoccapy.py` + - Observed native behavior: + - `on_hal_status_state_off()` disables `rbt_manual`, `rbt_mdi`, `rbt_auto`, and motion/execution controls. + - `on_hal_status_state_on()` enables Manual and base machine controls. + - With `NO_FORCE_HOMING = 0`, MDI/Auto remain unavailable until all axes are homed. + - Setup-page exit logic restores: + - estop/off: no mode buttons available + - on but not homed: only Manual available + - on and homed or no-force-homing: Manual/MDI/Auto available +- Found a Web behavior gap: + - Existing store/policy blocked `RUN` and `RUN_MDI` in invalid states. + - But mode buttons could still be clicked earlier than gmoccapy allows. + - Initial Web policy allowed mode changes that native gmoccapy would represent as insensitive buttons. +- Implemented fixes: + - In `app/src/state/linuxcnc-task-policy.js`: + - Added `SET_MODE` gate requiring `taskState === "on"` for Manual/Jog/MDI/Auto. + - Added homing gate for Auto/MDI when `NO_FORCE_HOMING = 0`. + - Preserved Auto-running protection that blocks leaving Auto while interpreter is not idle. + - In `app/src/ui/gmoccapy-shell.js`: + - Bound Manual/Jog/Auto/MDI sidebar mode buttons to `gateLinuxCncTaskAction`. + - Buttons now expose disabled state, `data-command-ready`, `aria-disabled`, and tooltip reason from the same task policy used by store dispatch. + - In `tests/node/verify_gmoccapy_xyzab_gates.mjs`: + - Added assertions that down/off state blocks Manual/MDI/Auto mode changes. + - Added assertions that power-on but unhomed state allows Manual but blocks Auto/MDI. + - Added assertions that after homing Auto mode is allowed. + - In `tests/node/verify_run_preconditions.mjs`: + - Updated expected behavior: trying Auto before homing leaves mode in Manual and run gate continues to say switch to Auto first. + - In `tests/browser/gmoccapy_shell_smoke.html`: + - Added DOM assertions for gmoccapy mode button sensitivity: + - initially powered off: Manual/Jog/Auto/MDI disabled + - powered on but unhomed: Manual/Jog enabled, Auto/MDI disabled with homing reason + - homed: Auto/MDI enabled. +- Ran targeted validation: + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_run_preconditions.mjs && bash ../tests/browser/verify_gmoccapy_shell_browser.sh` + - Result: passed. + - Output: + - `gmoccapy_xyzab_gates_smoke=ok` + - `run_preconditions_ini_profile_smoke=ok` + - `run_preconditions_kinematics_smoke=ok` + - `run_preconditions_machine_file_smoke=ok` + - `gmoccapy_shell_smoke=ok` +- Ran final full regression: + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke:node` + - Result: passed. + - `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run build && npm run smoke` + - Result: passed. + - Output: + - `gmoccapy_static_build=ok` + - `gmoccapy_shell_smoke=ok` + - `gmoccapy_dist_smoke=ok` +- Ran quality checks: + - `git diff --check` in `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` + - Result: passed. + - `git diff --check` in `/home/meswork/cnc_wams` + - Result: passed. +- Updated working5 documentation: + - `03-推进台账.md` now records R1 setup and R2 mode-interlock fix. + - `04-任务矩阵.md` marks current pass complete for baseline profile/task/HAL/interpreter/UI verification. + - `05-验收证据.md` records command evidence and outputs. + - `06-决策记录.md` records why mode gating moved into `SET_MODE` and UI disabled state. +- Started a local static server: + - Command: `python3 -m http.server 4173` + - Directory: `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` + - Confirmed with: + - `curl -I --max-time 2 http://127.0.0.1:4173/` + - HTTP 200 OK. + - `ss -ltnp 'sport = :4173'` + - Listener: `python3`, PID `24983`. + +Files changed: +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs` +- `/home/meswork/cnc_wams/work/working5/README.md` +- `/home/meswork/cnc_wams/work/working5/01-项目功能内容.md` +- `/home/meswork/cnc_wams/work/working5/02-项目程序开发详细步骤.md` +- `/home/meswork/cnc_wams/work/working5/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working5/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working5/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working5/06-决策记录.md` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/gptlog-process/gpdlog.md` + +Git/worktree notes: +- `git status --short --branch` from `/home/meswork/cnc_wams` shows modified Web simulation files and new `work/working5/`. +- There is an unrelated untracked file already visible outside this task: + - `备忘/备忘3.txt` + - It was not modified. + +Outcome: +- Created the requested working5 documentation set and README index. +- Executed and passed Node, build, browser, and targeted gmoccapy gate regressions. +- Fixed a real LinuxCNC/gmoccapy behavior mismatch: mode buttons now follow native `STATE_OFF`, `STATE_ON`, homing, and interpreter-idle sensitivity semantics. +- Local app server is available at `http://127.0.0.1:4173/`. diff --git a/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html b/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html index ea3702f..a0ce0de 100644 --- a/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html +++ b/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html @@ -303,6 +303,38 @@ if (!doc.querySelector('[data-machine-state="summary"]')?.textContent.includes("power off")) { throw new Error("initial machine state should show power off"); } + const initialModeManualButton = doc.querySelector('[data-action="mode-manual"]'); + const initialModeJogButton = doc.querySelector('[data-action="mode-jog"]'); + const initialModeAutoButton = doc.querySelector('[data-action="mode-auto"]'); + const initialModeMdiButton = doc.querySelector('[data-action="mode-mdi"]'); + if ( + !initialModeManualButton?.disabled || + initialModeManualButton.dataset.commandReady !== "false" || + initialModeManualButton.getAttribute("aria-disabled") !== "true" + ) { + throw new Error("MANUAL mode button should be disabled before machine power on"); + } + if ( + !initialModeJogButton?.disabled || + initialModeJogButton.dataset.commandReady !== "false" || + initialModeJogButton.getAttribute("aria-disabled") !== "true" + ) { + throw new Error("JOG mode button should be disabled before machine power on"); + } + if ( + !initialModeAutoButton?.disabled || + initialModeAutoButton.dataset.commandReady !== "false" || + initialModeAutoButton.getAttribute("aria-disabled") !== "true" + ) { + throw new Error("AUTO mode button should be disabled before machine power on"); + } + if ( + !initialModeMdiButton?.disabled || + initialModeMdiButton.dataset.commandReady !== "false" || + initialModeMdiButton.getAttribute("aria-disabled") !== "true" + ) { + throw new Error("MDI mode button should be disabled before machine power on"); + } const initiallyBlockedRunButton = doc.querySelector('[data-action="RUN"]'); if ( @@ -325,8 +357,44 @@ if (!doc.querySelector('[data-machine-state="summary"]')?.textContent.includes("power on")) { throw new Error("machine state did not render power on"); } + const unhomedModeManualButton = doc.querySelector('[data-action="mode-manual"]'); + const unhomedModeJogButton = doc.querySelector('[data-action="mode-jog"]'); + const unhomedModeAutoButton = doc.querySelector('[data-action="mode-auto"]'); + const unhomedModeMdiButton = doc.querySelector('[data-action="mode-mdi"]'); + if ( + unhomedModeManualButton?.disabled || + unhomedModeManualButton.dataset.commandReady !== "true" || + unhomedModeJogButton?.disabled || + unhomedModeJogButton.dataset.commandReady !== "true" + ) { + throw new Error("MANUAL and JOG mode buttons should be enabled after power on"); + } + if ( + !unhomedModeAutoButton?.disabled || + unhomedModeAutoButton.dataset.commandReady !== "false" || + !unhomedModeAutoButton.title.includes("home machine before AUTO") + ) { + throw new Error("AUTO mode button should remain disabled until homed"); + } + if ( + !unhomedModeMdiButton?.disabled || + unhomedModeMdiButton.dataset.commandReady !== "false" || + !unhomedModeMdiButton.title.includes("home machine before MDI") + ) { + throw new Error("MDI mode button should remain disabled until homed"); + } doc.querySelector('[data-action="HOME"]').click(); await wait(50); + const homedModeAutoButton = doc.querySelector('[data-action="mode-auto"]'); + const homedModeMdiButton = doc.querySelector('[data-action="mode-mdi"]'); + if ( + homedModeAutoButton?.disabled || + homedModeAutoButton.dataset.commandReady !== "true" || + homedModeMdiButton?.disabled || + homedModeMdiButton.dataset.commandReady !== "true" + ) { + throw new Error("AUTO and MDI mode buttons should be enabled after homing"); + } doc.querySelector('[data-action="mode-auto"]').click(); await wait(50); if (!doc.querySelector('[data-linuxcnc-task-policy="gates"]')?.textContent.includes("auto")) { diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs index 2c11b40..c374cf1 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs @@ -15,18 +15,41 @@ let gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: machine must be on"); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "manual" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: machine must be on before MANUAL"); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "mdi" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: machine must be on before MDI"); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "auto" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: machine must be on before AUTO"); + store.dispatch({ type: "TOGGLE_POWER" }); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "manual" }); +assert.equal(gate.allowed, true); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: switch to auto mode first"); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "auto" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: home machine before AUTO"); store.dispatch({ type: "SET_MODE", mode: "auto" }); +assert.equal(store.getState().machine.mode, "manual"); +assert.equal(store.getState().operatorMessage, "mode blocked: home machine before AUTO"); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); -assert.equal(gate.operatorMessage, "run blocked: home machine first"); +assert.equal(gate.operatorMessage, "run blocked: switch to auto mode first"); + +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "mdi" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: home machine before MDI"); store.dispatch({ type: "SET_MODE", mode: "manual" }); store.dispatch({ type: "HOME" }); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "auto" }); +assert.equal(gate.allowed, true); store.dispatch({ type: "SET_MODE", mode: "auto" }); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); diff --git a/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs b/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs index b86a5cd..6aa22b1 100644 --- a/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs +++ b/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs @@ -134,10 +134,14 @@ gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); assert.equal(gate.operatorMessage, "run blocked: switch to auto mode first"); +gate = gateLinuxCncTaskAction(store.getState(), { type: "SET_MODE", mode: "auto" }); +assert.equal(gate.allowed, false); +assert.equal(gate.operatorMessage, "mode blocked: home machine before AUTO"); store.dispatch({ type: "SET_MODE", mode: "auto" }); +assert.equal(store.getState().machine.mode, "manual"); gate = gateLinuxCncTaskAction(store.getState(), { type: "RUN" }); assert.equal(gate.allowed, false); -assert.equal(gate.operatorMessage, "run blocked: home machine first"); +assert.equal(gate.operatorMessage, "run blocked: switch to auto mode first"); store.dispatch({ type: "HOME" }); store.dispatch({ type: "SET_MODE", mode: "manual" }); diff --git a/work/working5/01-项目功能内容.md b/work/working5/01-项目功能内容.md new file mode 100644 index 0000000..46e409b --- /dev/null +++ b/work/working5/01-项目功能内容.md @@ -0,0 +1,56 @@ +# 01-项目功能内容 + +## 目标 + +以 LinuxCNC `gmoccapy_XYZAB.ini` 五轴仿真配置为行为基准,执行、测试并完善 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan`。数控系统仿真功能应尽量与 LinuxCNC 源程序行为一致;发现不一致时,在 Web 项目中修复并补充验收证据。 + +## 对标范围 + +### 启动与配置 + +- INI 解析:`[DISPLAY]`、`[TASK]`、`[EMCMOT]`、`[TRAJ]`、`[KINS]`、`[HAL]`、`[RS274NGC]`、`[PYTHON]` 等关键段。 +- 启动顺序:`linuxcncsvr`、RTAPI/HAL、`milltask`、`halui`、HALFILE、`halcmd start`、`gmoccapy`、POSTGUI_HALFILE。 +- 五轴配置:`COORDINATES = X Y Z A B`、`JOINTS = 5`、`trivkins coordinates=xyzab`。 + +### 任务、状态与互锁 + +- NML 命令、状态、错误通道的 Web 等效模型。 +- `STATE_ESTOP`、`STATE_ESTOP_RESET`、`STATE_OFF`、`STATE_ON`。 +- `MODE_MANUAL`、`MODE_MDI`、`MODE_AUTO`。 +- `INTERP_IDLE`、运行、暂停、停止、abort。 +- `NO_FORCE_HOMING = 0` 下的全轴回零互锁。 +- 自动运行、MDI、点动、回零、主轴、冷却、倍率、限位、换刀互锁。 + +### HAL 与仿真 IO + +- `core_sim_XYZAB.hal`:motmod、五轴位置反馈回环、急停回环、换刀回环、主轴/冷却信号。 +- `spindle_sim.hal`:速度斜坡、编码器反馈、`spindle.0.at-speed`。 +- `simulated_home.hal`:X/Y/Z 回零开关模拟。 +- `gmoccapy_postgui.hal`:GUI HAL pins、主轴反馈条、到速 LED、刀补显示、换刀回环。 +- `HALUI = halui` 默认入口。 + +### G-code 解释与五轴运动 + +- RS274NGC 解释器行为。 +- MDI 命令发送与状态回读。 +- 自动运行文件、暂停、继续、单步、停止、从行运行。 +- `M6`、`M61` REMAP 相关换刀语义。 +- 五轴轨迹、单位换算、RTCP/运动学边界。 + +### gmoccapy 界面等效行为 + +- 周期轮询:`[DISPLAY] CYCLE_TIME = 100` ms。 +- 按钮/页面敏感状态互锁。 +- DRO、程序进度、主轴反馈、冷却液、工具/刀补、错误消息。 +- 外部 HAL 硬按钮和点动 pin 驱动 GUI 后再通过命令通道进入任务层。 + +## 当前项目入口 + +- Web 应用:`/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app` +- 核心运行时:`app/src/runtime` +- gmoccapy UI:`app/src/ui` +- 状态存储:`app/src/state` +- 机床配置/profile:`app/src/profiles` +- Node 验证:`tests/node` +- 浏览器验证:`tests/browser` + diff --git a/work/working5/02-项目程序开发详细步骤.md b/work/working5/02-项目程序开发详细步骤.md new file mode 100644 index 0000000..a9b3db8 --- /dev/null +++ b/work/working5/02-项目程序开发详细步骤.md @@ -0,0 +1,48 @@ +# 02-项目程序开发详细步骤 + +## 步骤 1:建立工作控制文件 + +- 创建 README、功能内容、开发步骤、推进台账、任务矩阵、验收证据、决策记录。 +- 把任务按 LinuxCNC 执行流程拆分为可验收条目。 + +## 步骤 2:读取基准与现有实现 + +- 读取 `work/working3/gmoccapy_XYZAB_execution_analysis.md`。 +- 读取 Web 项目 runtime、state、profile、ui 和测试文件。 +- 将 LinuxCNC 执行链路映射到 Web 项目模块。 + +## 步骤 3:执行现有测试 + +- 在 `app` 下运行 `npm run smoke:node`。 +- 运行构建:`npm run build`。 +- 运行浏览器冒烟:`npm run smoke`。 +- 如有失败,先定位是否为环境、测试夹具或功能差异。 + +## 步骤 4:差异分析 + +按任务矩阵逐项检查: + +- INI/HAL/profile 是否覆盖 `gmoccapy_XYZAB.ini` 的五轴语义。 +- task/HAL runtime 是否覆盖状态机、互锁、回零、运行、暂停、停止、换刀、主轴、冷却、倍率。 +- interpreter/kinematics/session 是否覆盖 LinuxCNC 解释和五轴运行边界。 +- UI 是否按 gmoccapy 规则启停按钮并显示状态。 + +## 步骤 5:修改实现 + +- 优先修复已有测试暴露的问题。 +- 对未覆盖但属于 LinuxCNC 基准行为的差异,补测试后修实现。 +- 修改范围保持在对应 runtime/state/ui/profile/tests 文件内,避免无关重构。 + +## 步骤 6:回归验收 + +- 重新运行相关最小测试。 +- 运行完整 Node 冒烟测试。 +- 对 UI/浏览器改动运行构建与浏览器冒烟。 +- 把命令、结果、证据路径写入 `05-验收证据.md`。 + +## 步骤 7:更新台账和任务矩阵 + +- 更新已完成任务状态。 +- 写清本轮修改文件、验证命令和下一步。 +- 对重要取舍写入 `06-决策记录.md`。 + diff --git a/work/working5/03-推进台账.md b/work/working5/03-推进台账.md new file mode 100644 index 0000000..08f1578 --- /dev/null +++ b/work/working5/03-推进台账.md @@ -0,0 +1,59 @@ +# 03-推进台账 + +## 2026-06-26 R1:启动 working5 对标推进 + +做了什么: + +- 创建 working5 工作文件结构。 +- 建立与 `gmoccapy_XYZAB.ini` 执行流程对齐的任务矩阵和验收记录模板。 +- 开始读取 LinuxCNC 执行分析和 Web 项目结构。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/work/working5/README.md` +- `/home/meswork/cnc_wams/work/working5/01-项目功能内容.md` +- `/home/meswork/cnc_wams/work/working5/02-项目程序开发详细步骤.md` +- `/home/meswork/cnc_wams/work/working5/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working5/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working5/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working5/06-决策记录.md` + +验证了什么: + +- 待执行现有 Node、构建和浏览器冒烟测试。 + +下一步是什么: + +- 执行 `npm run smoke:node`,根据失败项定位 LinuxCNC 行为差异。 + +## 2026-06-26 R2:基线验证与 gmoccapy 模式互锁修复 + +做了什么: + +- 执行现有 Node、构建、浏览器冒烟测试,确认项目基线可运行。 +- 对照 `gmoccapy_XYZAB_execution_analysis.md` 和 `gmoccapy.py` 的 `STATE_OFF`、`STATE_ON`、`all_homed` 逻辑,发现 Web 侧允许提前点击 Manual/MDI/Auto 模式按钮,store 虽能拦截部分运行动作,但 UI 敏感状态不完全等同 gmoccapy。 +- 修改 task policy:`SET_MODE` 在机床未上电时禁止 Manual/MDI/Auto/Jog;机床上电但未回零时只允许 Manual/Jog,禁止 MDI/Auto;Auto 运行中仍禁止离开 Auto。 +- 修改 gmoccapy Web shell:右侧 Manual/Jog/Auto/MDI 模式按钮统一使用 `gateLinuxCncTaskAction`,以 `disabled`、`data-command-ready`、`aria-disabled` 和 title 展示互锁原因。 +- 补充 Node 和浏览器测试,锁定下电、上电未回零、回零后三个阶段的模式按钮行为。 + +改了哪些文件: + +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/state/linuxcnc-task-policy.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app/src/ui/gmoccapy-shell.js` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_gmoccapy_xyzab_gates.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/node/verify_run_preconditions.mjs` +- `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/tests/browser/gmoccapy_shell_smoke.html` +- `/home/meswork/cnc_wams/work/working5/03-推进台账.md` +- `/home/meswork/cnc_wams/work/working5/04-任务矩阵.md` +- `/home/meswork/cnc_wams/work/working5/05-验收证据.md` +- `/home/meswork/cnc_wams/work/working5/06-决策记录.md` + +验证了什么: + +- `node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_run_preconditions.mjs && bash ../tests/browser/verify_gmoccapy_shell_browser.sh` 通过。 +- `npm run smoke:node` 通过。 +- `npm run build && npm run smoke` 通过。 + +下一步是什么: + +- 下一轮可继续按任务矩阵深入核对 Tool/M6/M61 手动换刀对话框、限位 override、optional stop/block delete HAL pin 交叉行为等更细粒度 gmoccapy 行为。 diff --git a/work/working5/04-任务矩阵.md b/work/working5/04-任务矩阵.md new file mode 100644 index 0000000..dccc79a --- /dev/null +++ b/work/working5/04-任务矩阵.md @@ -0,0 +1,16 @@ +# 04-任务矩阵 + +| 任务编号 | 任务 | 状态 | 验收标准 | +| --- | --- | --- | --- | +| W5-001 | 创建 working5 工作控制文件 | 完成 | README 和 01-06 文件存在,内容可指导后续推进 | +| W5-010 | 建立 LinuxCNC 执行流程到 Web 模块映射 | 完成 | 已映射到 profile、task policy、HAL model、communication model、interpreter/runtime、UI shell 和 tests | +| W5-020 | 执行现有 Node 冒烟测试 | 完成 | `npm run smoke:node` 通过 | +| W5-030 | 执行构建测试 | 完成 | `npm run build` 通过 | +| W5-040 | 执行浏览器冒烟测试 | 完成 | `npm run smoke` 通过 | +| W5-100 | 校验 gmoccapy XYZAB profile | 完成 | `verify_gmoccapy_xyzab_profile.mjs`、完整 Node 冒烟通过 | +| W5-110 | 校验 task 状态机和互锁 | 完成 | 修复并验证 Manual/MDI/Auto/Jog 模式按钮在下电、未回零、已回零状态下与 gmoccapy 一致 | +| W5-120 | 校验 HAL runtime | 完成 | `verify_gmoccapy_hal_model.mjs`、`verify_linuxcnc_task_hal_runtime.mjs`、完整 Node 冒烟通过 | +| W5-130 | 校验 G-code interpreter/runtime | 完成 | 自动运行、MDI、暂停/继续/停止、M6/M61 引用边界测试随完整 Node 冒烟通过 | +| W5-140 | 校验五轴运动和单位换算 | 完成 | kinematics、real LinuxCNC 5-axis cases、linear unit conversion 测试随完整 Node 冒烟通过 | +| W5-150 | 校验 gmoccapy UI 通信和按钮门控 | 完成 | 浏览器 smoke 验证 mode gate、NML/HAL 诊断、POSTGUI 顺序、按钮状态 | +| W5-900 | 更新验收证据和决策记录 | 完成 | 命令、结果、修改原因和后续项已记录 | diff --git a/work/working5/05-验收证据.md b/work/working5/05-验收证据.md new file mode 100644 index 0000000..3161b04 --- /dev/null +++ b/work/working5/05-验收证据.md @@ -0,0 +1,69 @@ +# 05-验收证据 + +## 证据索引 + +| 证据编号 | 任务编号 | 类型 | 命令/页面/文件 | 结果 | +| --- | --- | --- | --- | --- | +| EV-W5-001 | W5-001 | 文件 | `/home/meswork/cnc_wams/work/working5` | 已创建 README 和 01-06 工作控制文件 | +| EV-W5-020 | W5-020/W5-100/W5-110/W5-120/W5-130/W5-140 | 命令 | `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke:node` | 通过,输出包含 `gmoccapy_xyzab_gates_smoke=ok`、`gmoccapy_hal_model_smoke=ok`、`real_linuxcnc_5axis_program_cases_smoke=ok`、`linear_unit_conversion_smoke=ok` | +| EV-W5-030 | W5-030 | 命令 | `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run build` | 通过,输出 `gmoccapy_static_build=ok` | +| EV-W5-040 | W5-040/W5-150 | 命令 | `cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app && npm run smoke` | 通过,输出 `gmoccapy_shell_smoke=ok`、`gmoccapy_dist_smoke=ok` | +| EV-W5-110 | W5-110/W5-150 | 命令 | `node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_run_preconditions.mjs && bash ../tests/browser/verify_gmoccapy_shell_browser.sh` | 通过,验证下电禁用 Manual/Jog/MDI/Auto,上电未回零只允许 Manual/Jog,回零后允许 Auto/MDI | + +## 命令记录 + +### 2026-06-26 R2 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke:node +``` + +结果:通过。 + +关键输出摘要: + +- `linuxcnc_kinematics_runtime_smoke=ok` +- `linuxcnc_interpreter_runtime_smoke=ok` +- `linuxcnc_task_hal_runtime_smoke=ok` +- `full_linuxcnc_5axis_source_node_smoke=ok` +- `real_linuxcnc_5axis_program_cases_smoke=ok` +- `gmoccapy_xyzab_profile_smoke=ok` +- `gmoccapy_communication_model_smoke=ok` +- `gmoccapy_hal_model_smoke=ok` +- `gmoccapy_xyzab_gates_smoke=ok` +- `linear_unit_conversion_smoke=ok` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run build +``` + +结果:通过,输出 `gmoccapy_static_build=ok`。 + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +npm run smoke +``` + +结果:通过,输出: + +- `gmoccapy_shell_smoke=ok` +- `gmoccapy_dist_smoke=ok` + +```bash +cd /home/meswork/cnc_wams/web-rtcp-5axis-sim-plan/app +node ../tests/node/verify_gmoccapy_xyzab_gates.mjs && node ../tests/node/verify_run_preconditions.mjs && bash ../tests/browser/verify_gmoccapy_shell_browser.sh +``` + +结果:通过,输出: + +- `gmoccapy_xyzab_gates_smoke=ok` +- `run_preconditions_ini_profile_smoke=ok` +- `run_preconditions_kinematics_smoke=ok` +- `run_preconditions_machine_file_smoke=ok` +- `gmoccapy_shell_smoke=ok` + +## 页面/截图/report/job 记录 + +本轮未生成截图、PDF、job_id 或 report_id。浏览器验收由 headless Chromium smoke 脚本完成。 diff --git a/work/working5/06-决策记录.md b/work/working5/06-决策记录.md new file mode 100644 index 0000000..92bd29d --- /dev/null +++ b/work/working5/06-决策记录.md @@ -0,0 +1,42 @@ +# 06-决策记录 + +## DR-W5-001:以 gmoccapy_XYZAB 执行流程作为本轮验收主线 + +日期:2026-06-26 + +决策: + +- 本轮不只验证图形 RTCP 轨迹,而是按 LinuxCNC 启动、INI/HAL、task/NML、interpreter、gmoccapy UI 通信和互锁全链路推进。 + +原因: + +- 用户指定的基准文件覆盖了 `gmoccapy_XYZAB.ini` 五轴仿真从启动到界面通信的完整执行流程。 +- 若只看轨迹显示,容易遗漏回零、状态机、换刀、主轴到速、冷却、UI gate 等数控系统仿真关键行为。 + +影响: + +- 任务矩阵按 LinuxCNC 行为拆分。 +- 任何实现修改都需要补充对应测试或验收证据。 + +## DR-W5-002:模式按钮门控前移到 `SET_MODE` 和 UI disabled 状态 + +日期:2026-06-26 + +决策: + +- Web 侧 `SET_MODE` 统一按 LinuxCNC/gmoccapy 状态门控。 +- `STATE_OFF` 或未上电时,Manual/Jog/MDI/Auto 都禁止切换。 +- `STATE_ON` 但未全轴回零且 `NO_FORCE_HOMING = 0` 时,只允许 Manual/Jog,禁止 MDI/Auto。 +- 回零后 MDI/Auto 恢复可用;Auto 解释器非 idle 时仍禁止离开 Auto。 + +原因: + +- LinuxCNC `gmoccapy.py` 的 `on_hal_status_state_off()` 会禁用 `rbt_manual`、`rbt_mdi`、`rbt_auto`。 +- `on_hal_status_state_on()` 先只启用 `rbt_manual`,退出设置页时也明确区分“未回零只 Manual 可用”和“已回零三种模式可用”。 +- 旧 Web 实现主要在 `RUN` 和 `RUN_MDI` 时拦截,模式按钮本身仍可点击,不符合 gmoccapy 的按钮敏感状态。 + +影响: + +- `app/src/state/linuxcnc-task-policy.js` 成为模式切换和执行动作的统一互锁入口。 +- `app/src/ui/gmoccapy-shell.js` 侧边模式按钮使用同一 gate 输出 disabled、`data-command-ready` 和 `aria-disabled`。 +- Node 和浏览器测试增加下电、未回零、已回零三个阶段的断言。 diff --git a/work/working5/README.md b/work/working5/README.md new file mode 100644 index 0000000..659dc75 --- /dev/null +++ b/work/working5/README.md @@ -0,0 +1,27 @@ +# working5 README 索引 + +本目录用于推进 `/home/meswork/cnc_wams/web-rtcp-5axis-sim-plan` 与 LinuxCNC `gmoccapy_XYZAB.ini` 五轴仿真执行流程的一致性验证、修复和验收。 + +对标基准: + +- `/home/meswork/cnc_wams/work/working3/gmoccapy_XYZAB_execution_analysis.md` +- `/home/meswork/cnc_wams/linuxcnc/configs/sim/gmoccapy/gmoccapy_XYZAB.ini` +- `/home/meswork/cnc_wams/linuxcnc/src/emc/usr_intf/gmoccapy/` +- `/home/meswork/cnc_wams/linuxcnc/configs/sim/gmoccapy/*.hal` + +工作文件: + +1. [01-项目功能内容.md](01-项目功能内容.md) +2. [02-项目程序开发详细步骤.md](02-项目程序开发详细步骤.md) +3. [03-推进台账.md](03-推进台账.md) +4. [04-任务矩阵.md](04-任务矩阵.md) +5. [05-验收证据.md](05-验收证据.md) +6. [06-决策记录.md](06-决策记录.md) + +推进规则: + +- 先按 `04-任务矩阵.md` 选择任务,避免重复做同一项。 +- 每轮结束更新 `03-推进台账.md`,记录做了什么、改了哪些文件、验证了什么、下一步是什么。 +- 所有命令、页面、报告、截图、测试输出摘要写入 `05-验收证据.md`。 +- 影响架构、兼容性或验收口径的判断写入 `06-决策记录.md`。 +