Files
linuxcncWebWASM/docs/linuxcnc-web-wasm-3axis-tutorial-control-panel-implementation.md

286 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 3axis Tutorial Web 控制面板源程序对标实施方案
## 目标
为固定案例 `linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini` 建立与 LinuxCNC AXIS 控制面板可观察行为一致的 Web 控制面板。
唯一运行时数据来源是 LinuxCNC 源码编译得到的 WASM core,以及由 Dedicated Worker 投影的 authoritative snapshot/event。React 只能发送命令、显示状态和呈现诊断;不得在浏览器端重新推导任务状态、运动轨迹、DRO、工具状态、门禁或错误结果。
本方案是固定案例的控制面板实施合同,不宣称全量 LinuxCNC、实时硬件或 release candidate。机器可读的源语义规则见 [`coverage/linuxcnc-source-semantics-contract.json`](../coverage/linuxcnc-source-semantics-contract.json),共享实现见 [`web/workspace/linuxcnc-source-semantics.mjs`](../web/workspace/linuxcnc-source-semantics.mjs)。
## 硬约束和功能矩阵
以下约束是所有 PANEL 任务的共同通过条件:
1. 网页控制面板的唯一数据来源是 LinuxCNC WASM;React 不得生成、修正或覆盖 canonical state。
2. 状态机严格对标 LinuxCNC 源程序;状态数值、迁移顺序、错误码和 gate reason 必须有 source line 和 WASM/Worker evidence。
3. 所有按钮逻辑制约必须与 LinuxCNC AXIS 源程序一致;按钮 disabled/enabled、模式切换、ESTOP/limit/fault gate 不允许由 UI 自行猜测。
4. 刀具路径预览必须来自 WASM canonical motion events,并与原生 LinuxCNC 的 segment 顺序、source line、端点、圆弧中心/法向、turn、units 和最终 hash 一致;没有 WASM event 时必须 fail-closed。
| # | 功能 | LinuxCNC 原生 | WASM 支持 | Web 实现状态 | 优先级 | 主要任务 |
|---:|---|:---:|:---:|---|:---:|---|
| 1 | 加载数控程序 | ✅ | ✅ | `PANEL-07` | P0 | `PROGRAM_LOAD`、exact source/hash、diagnostic |
| 2 | 刀具路径预览 | ✅ | ✅ | `PANEL-11` | P0 | WASM motion event、segment golden、no JS preview |
| 3 | 取消急停 | ✅ | ✅ | `PANEL-05` | P0 | `ESTOP_RESET`/recover source state transition |
| 4 | 上电 | ✅ | ✅ | `PANEL-05` | P0 | `STATE_OFF -> STATE_ON` and gate |
| 5 | 复位 | ✅ | ✅ | `PANEL-05`/`PANEL-12` | P0 | task reset、fault clear、checkpoint reconciliation |
| 6 | HOME 回零 | ✅ | ✅ | `PANEL-06` | P0 | individual/all home、home sequence、home-limit ABI |
| 7 | 执行 G 代码 | ✅ | ✅ | `PANEL-07` | P0 | run/pause/resume/step/abort/restart |
| 8 | 实时路径显示 | ✅ | ✅ | `PANEL-10` | P0 | authoritative pose/toolpath -> Three.js |
| 9 | 实时坐标显示 | ✅ | ✅ | `PANEL-08` | P0 | commanded/actual/relative DRO、units |
| 10 | MDI 功能 | ✅ | ✅ | `PANEL-07` | P1 | MDI mode、queue limit、active program rejection |
| 11 | JOG 功能 | ✅ | ✅ | `PANEL-06` | P1 | manual mode、continuous/incremental、stop |
| 12 | 按钮逻辑制约 | ✅ | ✅ | `PANEL-03` | P0 | complete AXIS gate matrix and error reasons |
表中的“Web 实现状态”在任务完成前只能是 `queued`、`in_progress` 或 `blocked`;现有 selected-case smoke/Worker evidence 不得自动把矩阵项标记为 `verified`。
### 功能项统一验收证据
每个矩阵项必须同时保存:LinuxCNC source path/line/hash、WASM export/state field、Worker command/event、React control ID、normal/boundary/error/ESTOP/recovery sample、native/WASM/Worker logical tick、输入/输出/state/event hash 和 self-hashed report。P0 项缺任一项即保持 `blocked`;P1 项不能阻塞 P0 实施,但不能被宣称完成。
## 源程序映射
| Web 控制面板区域 | LinuxCNC 源输入 | WASM/Worker 唯一来源 | React 允许行为 | 禁止行为 |
|---|---|---|---|---|
| 电源、急停、状态灯 | `axis.py` 状态更新、`linuxcnc` `STATE_*`/`MODE_*`/`INTERP_*` | `sim_task` canonical + `axis_state` | 显示、发送命令、显示 gate reason | 本地切换 ON/ESTOP |
| File/Program | `axis.py` `open_file_guts`, `reload_file`, `task_run` | WASM interpreter events + Worker program state | 编辑器输入、发送 LOAD/RUN/RESTART | JS 解析 G-code 或伪造当前行 |
| MDI | `axis.py` `ensure_mode`, `send_mdi_command` | WASM interpreter/task mode/queue | 提交文本、显示拒绝 | 本地判断 MDI 成功 |
| Manual/Jog/Home | `axis.py` `jog_plus`, `jog_minus`, `home_all_joints`, `home_joint` | WASM motion queue + home/limit ABI | 按钮、方向、速度输入 | JS 插值、修改 position/home |
| DRO/active codes | `axis.py` `update`, `task_modename`, `motion_modename`, `interp_statename` | WASM task/interpreter/motion snapshot | 格式化显示 | 从命令文本推导状态 |
| Feed/spindle override | `axis.py` `set_feedrate`, `set_spindlerate` | WASM/Worker device canonical | slider/number input | 直接修改 React store |
| Spindle/coolant/tool/probe | `axis.py` manual controls、案例 HAL、`sim_io` | WASM IO/device canonical | 显示、发送命令、fault recovery | mock device response |
| Preview/3D | `axis.py` preview/canon、案例 Vismach executable | WASM canonical motion + Worker visual snapshot | Three.js 渲染 | JS G-code preview/几何 authority |
| 配置诊断 | `linuxcnc-source-semantics-contract.v1` | Worker startup snapshot | 显示 `TASK_READY`/`BLOCKED_*`、source/line | basename 替换、隐藏缺失资源 |
源代码锚点:
- AXIS startup/open-file:`linuxcnc/src/emc/usr_intf/axis/scripts/axis.py` 约 3991-3998 行。
- AXIS status projection:同文件 `update`、`task_modename`、`motion_modename`、`interp_statename`。
- AXIS command gates:同文件 `ensure_mode`、`task_run`、`task_pause`、`task_resume`、`task_stop`、`jog_*`、`home_*`、`estop_clicked`。
- 参数文件和程序前缀:`linuxcnc/src/emc/rs274ngc/rs274ngc_pre.cc`、`linuxcnc/src/emc/task/emctask.cc`。
- 案例 UI/HAL/Vismach:`linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini`、`.hal` 和 `3axis-tutorial`。
## 控制面板数据契约
Worker 每次响应必须能追溯到同一个 `context_id`、`session_id`、`logical_tick` 和 `state_hash`。控制面板消费以下投影字段:
```yaml
snapshot:
task: wasm sim_task canonical state
program: WASM interpreter/program state
motion: WASM motion queue state
home_limit: WASM kinematics home/limit state
hal: static/virtual HAL projection with source diagnostics
devices: WASM sim_io/tool/spindle/coolant/probe state
configuration: startup state, exact resources, optional_missing, runtime_fixtures
axis_state: additive AXIS state/mode/interpreter view
visual: pose, joints, toolpath and Vismach pose projection
state_hash: hash of the canonical projection
```
每个命令必须携带 `operation_id`,并产生 pending/result/error event。UI gate 只读取上一份 authoritative snapshot;命令被拒绝时显示 Worker 返回的 stable error code、message key、source/line 和 gate reason。错误、ESTOP、limit、following-error、资源缺失和能力边界必须可见,不能变成静默 disabled。
### 分层接口责任
| 层 | 必须提供 | 允许缓存 | 不得提供 |
|---|---|---|---|
| LinuxCNC source/native recorder | 常量、状态迁移、命令顺序、错误/拒绝语义、segment golden | stdout/log/screenshot hash | 浏览器状态 |
| `wasm-core` | canonical task/interpreter/motion/home-limit/HAL/IO state、fixed tick、canonical event | raw ABI bytes、read-only sample | UI 文案或按钮 gate |
| Dedicated Worker | command ledger、session lifecycle、snapshot/event 转发、diagnostic/source line、checkpoint/trace | 序列化协议和 operation trace | JS authority mirror、JS G-code 解析 |
| React/Web panel | command input、snapshot projection、accessibility、visual rendering | 最近一份 snapshot 用于显示 | 改写 state、推导成功、推导轨迹 |
任何层违反责任都必须在测试中以 `authority_violation` 失败;“结果看起来一致”不能替代唯一来源证明。
### 面板命令 envelope
所有 P0/P1 命令使用已有 `protocol.v1` envelope,不为 UI 单独发明协议:
```json
{
"schema_version": 1,
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
"session_id": "axis3-panel",
"seq": 42,
"operation_id": "axis-program-run-000042",
"kind": "PROGRAM_RUN",
"payload": {}
}
```
响应至少包含 `kind`、`seq`、`operation_id`、`logical_tick`、`payload.task`、`payload.program`、`payload.motion`、`payload.axis_state`、`payload.state_hash`。错误响应必须包含 `code`、`message_key`、`source`/`line`(若有)、`gate_reason` 和拒绝时的完整 snapshot。
## 严格状态机和按钮真值表
源常量和 AXIS 视图必须使用 [`docs/linuxcnc-web-wasm-3axis-tutorial-state-machine.md`](linuxcnc-web-wasm-3axis-tutorial-state-machine.md) 中的数值,不得只使用同名字符串:
| AXIS 状态 | LinuxCNC source state | 允许的面板操作 | 必须拒绝 |
|---|---|---|---|
| `ESTOP` | `STATE_ESTOP` | cancel ESTOP、显示诊断 | power/home/run/MDI/JOG |
| `ESTOP_RESET` | `STATE_ESTOP_RESET` | power on、ESTOP | run/home/MDI/JOG |
| `OFF` | `STATE_OFF` | power/reset、ESTOP | run/home/MDI/JOG |
| `ON`/`IDLE` | `STATE_ON + INTERP_IDLE` | load/run/MDI/manual/home/jog/device | 与当前 mode/limit/fault 冲突的操作 |
| `HOME` | source home transient | 等待 home result、ESTOP | run/MDI/重复 home |
| `RUNNING` | `INTERP_READING/WAITING` | pause/abort/ESTOP | home/jog/MDI/load |
| `PAUSED` | `INTERP_PAUSED` | resume/abort/ESTOP/step(按源 gate) | home/jog/普通 MDI |
按钮 gate 的可执行真值样本必须至少包含:
| 操作 | 正常条件 | 拒绝样本 |
|---|---|---|
| 加载程序 | `ON + INTERP_IDLE` 或源允许的 manual load | `OFF`、`RUNNING`、资源缺失 |
| 预览 | WASM motion events 已存在 | WASM event 缺失、解析错误 |
| 取消急停 | `ESTOP`/`ESTOP_RESET` 按源状态转换 | snapshot 过期、seq/tick 错误 |
| 上电 | `ESTOP_RESET -> ON` 或源定义 reset 流程 | `ESTOP` 未 reset、fault latch |
| 复位 | abort/clear/recover 的源顺序 | active motion、坏 checkpoint |
| HOME | `ON + MANUAL + INTERP_IDLE` | `OFF`、AUTO/MDI、limit/fault、未满足 previous home |
| G-code run | `ON + AUTO + IDLE + taskfile` | 无程序、MDI、paused、ESTOP |
| 实时路径/DRO | snapshot tick/hash 可验证 | snapshot stale、state hash mismatch |
| MDI | `ON + MDI` 且 queue 未满 | AUTO active、queue full、ESTOP |
| JOG | `ON + MANUAL + IDLE` | AUTO/MDI、未 home、limit/fault |
## 刀具路径预览一致性算法
预览和执行都必须使用 WASM interpreter canonical motion event;Web 不得将原始 G-code 再解析一遍。每个 segment 的比较字段固定为:
```yaml
segment:
source_line: integer
motion_type: rapid|linear|arc
start: pose(x,y,z,a,b,c,u,v,w)
end: pose(x,y,z,a,b,c,u,v,w)
center: pose_or_null
normal: vector_or_null
turn: integer
velocity: finite_number
units: mm|inch
plane: G17|G18|G19
sequence: integer
```
验收顺序:
1. native recorder 使用固定程序和固定 units/modal 初始状态生成 segment JSONL。
2. WASM `sim_interpreter_read_motion` 生成同字段事件;Worker 只排序/封装,不补字段。
3. 比较 segment count、source line、顺序、字段值和 canonical hash;浮点比较使用 source/ABI 规定的 exact或明确 tolerance,不能用截图相似度替代。
4. Three.js 只渲染已比较的 `visual` projection;canvas 非空是必要条件,不是轨迹正确性的充分条件。
5. 任意字段首差必须记录 `first_divergent_segment`、`source_line`、`logical_tick`、`field`、`native`、`wasm`,并阻止 preview/execute 继续宣称一致。
禁止条件:WASM event buffer 为空时调用 JS `previewGcode`;用当前 DRO 终点反推完整路径;用 native screenshot 通过 Web segment hash;用墙钟时间推进路径。
## 功能矩阵报告模板
每个功能项使用同一结构,便于控制面板任务和后续自动聚合:
```yaml
feature_id: PANEL-07
matrix_item: execute_gcode
priority: P0
status: queued|in_progress|verified|blocked
source:
paths: []
lines: []
sha256: []
authority:
wasm_exports: []
canonical_fields: []
worker:
commands: []
events: []
react:
control_ids: []
projection_fields: []
samples:
normal: []
boundary: []
error: []
estop_or_fault: []
recovery: []
comparison:
native_hash: null
wasm_hash: null
worker_hash: null
first_divergent_tick: null
stop_reason: null
report_sha256: null
```
`verified` 只允许在 `source`、`authority`、`worker`、`react`、5 类 samples、comparison 和 self-hash 全部存在时使用。任何 `REAL-EXCLUDED`、`SOURCE_BLOCKED`、`WASM_CAPABILITY_BLOCKED` 都必须保持 `blocked`,并带 failure sample。
## 面板布局和交互范围
桌面和移动布局都必须保持以下固定区域,不因动态文字改变尺寸:
1. 顶栏:案例名、WASM capability、连接/session、`TASK_READY`/`BLOCKED`、ESTOP 和 recover。
2. 左侧状态:machine power、task mode、interp state、program status、current line、logical tick、state hash。
3. 中央机床视图:Three.js Vismach 3-axis model、tool pose、joints、limits、toolpath、axis labels。
4. 右侧 DRO:commanded/actual/relative 的 X/Y/Z、unit、feed/spindle override、active G/M codes。
5. 下方 tabs:Manual、MDI、Program、Diagnostics;每个 tab 的控件 gate 来自 Worker snapshot。
6. 操作条:Home all/individual、jog +/-/stop、Run/Pause/Resume/Step/Abort/Restart、spindle/coolant/tool/probe/ESTOP。
7. 诊断面板:按事件顺序显示 source path、line、code、reason、first divergent tick、recovery status。
熟悉符号使用 AXIS 源图标或 lucide icon;按钮必须有 accessible name、disabled reason 和 keyboard path。控件不能仅通过颜色表示 fault/ESTOP。
## 功能缺口移植流程
当 WASM/Worker 当前能力不满足 AXIS 行为时,严格按以下顺序处理:
1. 从 LinuxCNC 源文件定位函数、状态常量、命令顺序和错误语义,记录 source path、line、source hash。
2. 判断是否属于纯仿真可移植行为。实时内核、真实硬件、宿主动态 loader、宿主 GUI/Python 进程只能登记 `REAL-EXCLUDED`/`HOST_EXCLUDED`,不能伪造 WASM parity。
3. 先增加 native source sample 和失败测试,再在 `wasm-core` 增加 C ABI/状态实现;ABI 变更必须建立新 context,不能就地改锁定 ABI。
4. 增加真实 WASM module 测试,再接 `web/worker` adapter;Worker 只投影 WASM canonical,不保留 JS authority 镜像。
5. 最后接 React 命令/渲染,补 normal、boundary、error、non-finite、capacity、pause/resume、ESTOP、recovery 样本。
6. 记录首个 divergent phase/tick、native/WASM/Worker hash、checkpoint/trace/recovery hash,更新 self-hashed report。
## 详细实施任务
### P0/P1 执行顺序
先完成 P0 的 authority、状态机、按钮 gate、程序加载/执行、preview、实时路径和实时坐标,再实现 P1 的 MDI/JOG 扩展。P0 任务之间按 `PANEL-01 -> PANEL-02 -> PANEL-03 -> PANEL-05 -> PANEL-06/PANEL-07 -> PANEL-08/PANEL-10/PANEL-11 -> PANEL-14 -> PANEL-15` 执行;P1 的 MDI/JOG 只能复用同一 WASM/Worker authority,不能另建一套 UI state。
P0 完成的硬条件:12 项矩阵中的 1-9、12 有 native source reference、真实 WASM module、Worker sequence 和 React evidence;刀具路径 preview 与 native canonical segments 的 hash/端点一致;所有按钮 gate 在正常、运行、暂停、MDI、ESTOP、limit、fault、未 home 和 queue-full 样本中一致。P1 完成的硬条件:10-11 的 MDI/JOG 与 P0 状态机、queue、home/limit、ESTOP gate 共用同一 snapshot,不能产生 divergent tick。
| ID | 任务 | 主要实现 | 直接源输入 | 验收输出 | 依赖 |
|---|---|---|---|---|---|
| `PANEL-01` | 源控件 inventory | 把 AXIS 菜单、toolbar、快捷键、Manual/MDI/Auto、DRO、诊断控件登记为稳定 IDs | `axis.py`、AXIS Tcl UI、案例 INI | source inventory + control ID contract | 无 |
| `PANEL-02` | snapshot projection contract | 固定 task/program/motion/home/hal/devices/configuration/axis_state/visual 字段、tick/hash 和 event envelope | `sim-session.mjs`、T4/T3 reports | schema-compatible projection test | 01 |
| `PANEL-03` | source gate matrix | 逐命令复刻 AXIS `ensure_mode`、状态和 gate;禁止 React 自判定 | `axis.py` command functions、`axis-contract.mjs` | gate matrix normal/error/ESTOP test | 01/02 |
| `PANEL-04` | startup/config panel | 显示 exact resources、optional `OPEN_FILE`/graphic、HAL/resource/INI diagnostics 和 runtime fixtures | source semantics contract、案例 INI/HAL | `TASK_READY`/`BLOCKED_*` UI test | 02 |
| `PANEL-05` | power/ESTOP/recover | 对齐 `STATE_OFF/ON/ESTOP/ESTOP_RESET` 和 recover transition | `axis.py`、`sim_task`、`sim_io` | transition/event/hash report | 02/03 |
| `PANEL-06` | Manual/Home/Jog | 对齐 individual/all home、continuous/incremental jog、stop、joint/teleop gate | `axis.py` `jog_*`/`home_*`、T4 home-limit | native/WASM/Worker sequence report | 03/05 |
| `PANEL-07` | Program/MDI | 对齐 open/load/reload/run/pause/resume/step/abort/restart/MDI queue and active-program rejection | `axis.py`、WASM interpreter/task | program control and current-line report | 03/05 |
| `PANEL-08` | DRO/active codes | 对齐 actual/commanded/relative、units、feed/spindle、active G/M/modal display | AXIS `update`/stat mixins、WASM snapshot | DRO golden and unit switch test | 02/06/07 |
| `PANEL-09` | device panels | 对齐 spindle ramp/at-speed, coolant, tool prepare/change, probe, limit/FERROR faults |案例 HAL、`sim_io`、tool-IO report | device fault/recovery report | 05/06/07 |
| `PANEL-10` | Vismach/3D | 按 `3axis-tutorial` Python model 和 HAL pins 实现 geometry/pose/tool continuity | 案例 executable、Vismach source、Three.js view | canvas pixel/nonblank/pose report | 02/06/08/09 |
| `PANEL-11` | editor/preview | Worker WASM preview、source line highlighting、active G-code 和 diagnostics | `sim_interpreter`、AXIS editor/preview | no-JS-preview test + source-line parity | 07/08 |
| `PANEL-12` | persistence/recovery UI | checkpoint, journal, trace import, `.var/.bak/position/tool.tbl` restart and fail-closed recovery | T4/T6 reports、workspace recovery | recovered endpoint and no duplicate side effects | 05/07/09 |
| `PANEL-13` | keyboard/accessibility/layout | AXIS shortcut mapping、focus order、ARIA、desktop/mobile no-overlap | AXIS shortcut table、React source | DOM/accessibility/visual report | 01/03/08/10/11 |
| `PANEL-14` | native/Worker/UI recorder | 同一 operation sequence、same logical ticks、first divergence and hash comparison | native runner、Dedicated Worker、React | sequence ledger and diff report | 05..13 |
| `PANEL-15` | selected-case acceptance | 汇总所有面板区域、normal/boundary/error/recovery 和 release boundaries | all PANEL evidence | self-hashed closure report | 14 |
## 验收和停止条件
通过条件:每个 PANEL row 有 source reference、WASM authority、Worker test、UI evidence、state/event hash 和 recovery sample;同一操作序列 native/WASM/Worker/UI 的首个差异可定位;控制面板无 JS authority、无 JS G-code preview、无 basename 替换。
保持 `blocked`:缺 source/oracle/resource、WASM ABI 不足、首个 divergent tick 未定位、Worker snapshot/hash 不稳定、恢复重复副作用、只在 Chromium 可见但 browser minimum 未锁定,或只能靠 mock/墙钟/宿主能力通过。
停止发布:不把固定案例 verified 写成全量 LinuxCNC parity;Firefox/Safari、OPFS 物理崩溃、实时硬件、官方 oracle、许可证和 owner release gate 继续保持 `STOP_RELEASE`。
## 证据命令
```bash
python3 scripts/validate_baseline.py
python3 scripts/validate_task_pointer.py nextTask1.md --lock context-lock.json
python3 scripts/test_source_semantics_contract.py
node web/workspace/test_source_semantics.mjs
node web/workspace/test_sim_case_loader.mjs
node web/worker/test_sim_case_matrix_s8_06.mjs
node web/worker/test_wasm_canonical_state.mjs
node web/worker/test_wasm_canonical_state_checkpoint.mjs
node web/worker/test_axis3_selected_case.mjs
node web/worker/test_axis3_visible_completion.mjs
npm --prefix web/react run typecheck
npm --prefix web/react run build
```
每个 PANEL 任务必须增加专属测试和 self-hashed report;未完成的任务保留 `queued`/`blocked`,不能从 UI smoke 直接升级为 parity。