Align AXIS control flow and fix pause and program rendering
Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks. Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
# LinuxCNC AXIS Web Control Panel Source Logic
|
||||
|
||||
Status: source-locked implementation contract
|
||||
Status: source-locked contract; verified paths and open source gaps are recorded separately
|
||||
Scope: the selected `XYZAC TRT` Web/WASM panel and its LinuxCNC-compatible
|
||||
Worker command path
|
||||
Source baseline: LinuxCNC submodule `239dcf4284762ad7e9c65d390bc5524d855c2f8e`
|
||||
|
||||
执行顺序:**源程序核对 → 完善本流程文件 → Worker/WASM 实现 → Web 控件实现 → 逐控件验收**。
|
||||
本文件覆盖当前 XYZAC TRT Web 面板的全部交互控件,包括按钮、菜单、
|
||||
选项卡、单选框、复选框、滑块、输入框、文件选择和弹窗。下文的稳定控件 ID
|
||||
是实现与验收的连接点;源码中的桌面专属功能和 ABI 缺口必须保留明确记录,
|
||||
不得把“已列入文档”或“按钮已禁用”计为功能完成。
|
||||
|
||||
Relationship map: [`linuxcnc-axis-web-control-panel-source-logic.png`](linuxcnc-axis-web-control-panel-source-logic.png)
|
||||
|
||||
Reproducible graph source: [`linuxcnc-axis-web-control-panel-source-logic.dot`](linuxcnc-axis-web-control-panel-source-logic.dot)
|
||||
@@ -22,7 +28,8 @@ browser gesture
|
||||
-> React projection
|
||||
```
|
||||
|
||||
The React state is never an authority. A rejected Worker command must remain
|
||||
React machine state is never an authority; local presentation and input state
|
||||
do not carry that restriction. A rejected Worker command must remain
|
||||
rejected in the UI, with the source gate reason visible as the disabled-button
|
||||
title and in Diagnostics.
|
||||
|
||||
@@ -31,11 +38,14 @@ title and in Diagnostics.
|
||||
| Mark | Meaning |
|
||||
| --- | --- |
|
||||
| `SOURCE_EXACT` | Same user action, source handler, UI enable formula, command ordering and observable result as AXIS. |
|
||||
| `SOURCE_ADAPTER` | Browser/WASM transport differs, but the accepted action and observable machine semantics are the same. Any stronger Web safety gate is stated explicitly. |
|
||||
| `SOURCE_ADAPTER` | Browser/WASM transport or presentation differs. The tested machine semantics match the cited source for the explicitly stated domain; omitted domains and stronger Web gates are listed as gaps, not claimed as identical. |
|
||||
| `SOURCE_BLOCKED` | AXIS has the action, but the selected WASM or browser boundary cannot represent its canonical state. It stays disabled or absent and is never emulated with local state. |
|
||||
| `WEB_EXTENSION` | No AXIS control exists. The control must be visibly and textually separated from source parity and must not alter the meaning or gate of an AXIS action. |
|
||||
| `WEB_EXTENSION` | No corresponding native control or automatic flow exists. Its differences must be explicitly documented and never counted as source parity. Automatic file-load clearing has been withdrawn; current clearing follows the native explicit action. |
|
||||
|
||||
Only `SOURCE_EXACT` and `SOURCE_ADAPTER` count as implemented AXIS parity.
|
||||
These marks describe the intended mapping, not a blanket acceptance result.
|
||||
The explicit differences below remain open until their end-to-end tests pass;
|
||||
a passing source hash or an entry in the inventory does not close a difference.
|
||||
`SOURCE_BLOCKED` is an honest capability boundary. `WEB_EXTENSION` is never
|
||||
evidence of parity. If this document and the pinned source disagree, the pinned
|
||||
source wins and this document, its graph and the Web implementation must be
|
||||
@@ -78,6 +88,7 @@ fingerprinted so the audit fails on an unreviewed source drift:
|
||||
| --- | --- | --- |
|
||||
| `src/emc/usr_intf/axis/scripts/axis.py` | `328c038a1e8fefd952e53c8e398e3fa98b04602351c44318edd1b044e8f63ffa` | Handlers, mode transitions, keyboard bindings, preview and live-plot ownership |
|
||||
| `share/axis/tcl/axis.tcl` | `c9f0b4dc686717b37565f85fb0ab8cc8ded9cfa0028cc0aaaccdbcf808c4c524` | Widgets, menu/toolbar wiring and exact dynamic enable formulas |
|
||||
| `lib/python/glnav.py` | `b2d811a21954966655a40f107c2af217cc7d57bae0dc308dc4698f5859ed3f11` | Orthographic/perspective presets, rotation, camera framing |
|
||||
| `src/emc/task/emctask.cc` | pinned by submodule commit | Machine state edges and observed `ESTOP`/`ESTOP_RESET`/`ON` state |
|
||||
| `src/emc/task/emctaskmain.cc` | pinned by submodule commit | Task command acceptance, homing enforcement, run/pause/resume/abort and task-plan reopen/close |
|
||||
| `configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini` plus `remap_subs/{428,429,430}remap.ngc` | pinned by submodule commit | Selected axes, limits, speeds, startup file and switchkins remaps |
|
||||
@@ -144,7 +155,7 @@ families because their cardinality comes from the selected INI.
|
||||
| Optional Stop and Block Delete | `toggle_optional_stop`, `toggle_block_delete` | Visible `SOURCE_BLOCKED`; block delete's source wait, MANUAL switch and preview reload must not be approximated by a local checkbox |
|
||||
| MDI history clear/copy/paste/select/double-click/Up/Down | `clear_mdi_history`, `mdi_history_hist2clip`, `mdi_history_clip2hist`, `mdi_history_butt_1`, `mdi_history_double_butt_1`, `mdi_up_cmd`, `mdi_down_cmd` | Select/Up/Down/local history: `SOURCE_ADAPTER`; clipboard/file persistence variants are browser/host boundaries |
|
||||
| Calibration, HAL Configuration, HAL Meter, HAL Scope, LinuxCNC Status, Debug Level | external `exec` commands in Machine menu | Desktop-process boundary; not rendered and never mapped to Diagnostics as though it were the same action |
|
||||
| Home all/joint and Unhome all/joint | `home_all_joints`, `home_joint_number`, `unhome_all_joints`, `unhome_joint_number` | Home all/selected axis: `SOURCE_ADAPTER`; Unhome and joint-mode selector: `SOURCE_BLOCKED` |
|
||||
| Home all/joint and Unhome all/joint | `home_all_joints`, `home_joint_number`, `unhome_all_joints`, `unhome_joint_number` | Home all: `SOURCE_ADAPTER`; world-axis Home rejects for KINEMATICS_BOTH; individual joint selection and Unhome remain `SOURCE_BLOCKED` |
|
||||
| Zero P1..P9 and G92 coordinate systems | `clear_offset` | `SOURCE_BLOCKED`; Touch Off is not an alias for clearing a coordinate system |
|
||||
| Tool touch-off target workpiece/fixture | `toggle_tto_g11` selecting `G10 L10`/`G10 L11` | `SOURCE_BLOCKED` with Tool Touch Off; no local target state is exposed |
|
||||
| Top/Z2/Side/Front/Y2/Perspective, zoom and rotate | `set_view_*`, `zoomin`, `zoomout`, rotate Tcl command | `SOURCE_ADAPTER`; Y2 is toolbar-only in the native layout |
|
||||
@@ -154,7 +165,7 @@ families because their cardinality comes from the selected INI.
|
||||
| Clear Live Plot | `clear_live_plot` -> `LivePlotter.clear` -> `positionlogger.clear` | `SOURCE_ADAPTER`; this is the only native live-trace deletion action |
|
||||
| Axis/joint selection and jog increment | `ja_rbutton`, `jogincrement`, `activate_ja_widget` | XYZAC axis and increment selection: `SOURCE_ADAPTER`; native joint selection is `SOURCE_BLOCKED` |
|
||||
| Jog negative/positive and key jog | `jog_minus`, `jog_plus`, `jog_on`, `jog_off` | `SOURCE_ADAPTER`; press/release semantics and Manual-tab guard apply |
|
||||
| Home Axis, Touch Off, Tool Touch Off, Override Limits | corresponding `zerohome`/`override` widgets | Home/Touch Off: `SOURCE_ADAPTER`; Tool Touch Off/Override Limits: visible `SOURCE_BLOCKED` |
|
||||
| Home Axis, Touch Off, Tool Touch Off, Override Limits | corresponding `zerohome`/`override` widgets | World-axis Home reproduces source rejection for KINEMATICS_BOTH; Touch Off: selected-coordinate adapter; Tool Touch Off/Override Limits: visible `SOURCE_BLOCKED` |
|
||||
| Spindle CCW/Stop/CW, slower/faster, brake | `spindle`, `spindle_decrease`, `spindle_increase`, `spindle_constant`, `brake` | `SOURCE_ADAPTER`; button-state formulas and press/release ordering apply |
|
||||
| Mist and Flood | `mist`, `flood`, `mist_toggle`, `flood_toggle` | `SOURCE_ADAPTER`; independent canonical IO bits |
|
||||
| MDI entry, Go, Enter, history activation | `send_mdi`, `send_mdi_command` | `SOURCE_ADAPTER`; source trim, mode and adjacent-history de-duplication apply |
|
||||
@@ -186,6 +197,69 @@ command are blocked instead of being silently mapped to another operation.
|
||||
|
||||
## State Gates
|
||||
|
||||
### 源码复核修正(2026-09-08)
|
||||
|
||||
本节更正旧文档中把实现假设写成源码事实的内容;以下定义优先于历史报告。
|
||||
|
||||
1. **三个门控层分别记录。** `ui_gates` 只按 `axis.tcl::update_state`
|
||||
投影按钮公式;`gates` 为最终命令接收条件;React 只额外处理浏览器加载互斥。
|
||||
Run 在 ON+IDLE 时启用,即使尚未回零,点击后仍由 task 拒绝;Step 只要求
|
||||
ON+taskfile。MDI 输入/Go 的 UI 只要求 ON+队列未满,`send_mdi` 的
|
||||
`manual_ok` 不通过时保留输入且不提交。按钮可用不是命令必然成功。
|
||||
2. **OFF 是显示字符串。** `axis.tcl` 将 `STATE_ESTOP_RESET` 显示成 OFF。
|
||||
source task state 必须为 ESTOP、ESTOP_RESET、ON,不得由 `last_error=-904`
|
||||
猜测状态;清除错误或调整覆盖率不能改变机器的 source state。
|
||||
同样,task 的 FAULT 标签不能覆盖 IO 的 ready/estop 位变成假 ESTOP。
|
||||
3. **WAITING 必须可暂停。** `emctaskmain.cc` 在解释器读完、TP 尚未排空时
|
||||
保持 WAITING;Pause 保存原解释器状态,Resume 恢复该状态,不能把已读完
|
||||
的程序重新设为 READING。MDI 在其运动执行期间同样是活动任务。
|
||||
4. **Step 的三个分支。** `emctaskmain.cc:1059/1149/1224/1299`:IDLE 首次
|
||||
Step 从第 0 行启动并暂停;PAUSED 且有运动队列时调用 `emcTrajStep`,
|
||||
按执行 ID 推进并再次暂停;无排队运动时释放下一条 task 指令。
|
||||
`motion/control.c:2186` 在执行 ID 改变后暂停 TP,不能“解析一行后永久
|
||||
冻结”,也不能一次 Step 把全部已排队运动执行完。Web 单步的源码行解析
|
||||
与原生 NML 指令细分仍是需单独验证的适配边界,不能用行数相同替代运动断言。
|
||||
5. **回零类型不能混同。** `switchkins.c::kinematicsType()` 始终返回
|
||||
`KINEMATICS_BOTH`;M429 的 `active_type=0` 不代表 KINEMATICS_IDENTITY。
|
||||
`home_joint` 对世界坐标字母会拒绝,而数字 joint 允许回零。
|
||||
当前面板的轴字母按钮不能冒充 joint 模式;Home Axis 保留原 UI 公式,
|
||||
点击返回 `home_joint_requires_joint_mode`。Home All 仍可用。
|
||||
6. **自动恢复标签模式。** `axis.tcl::update_state` 在 ON+IDLE 的状态边沿,
|
||||
当前 task_mode 为 AUTO 时调用 `set_mode_from_tab`;显式 Open/Reload 完成后
|
||||
也调用它。Web 必须通过排队的 `SET_MODE` 恢复所选 Manual/MDI 页,不能只
|
||||
改标签样式;命令发出后不可让延迟的 effect 覆盖后续 Run。
|
||||
7. **Escape 是 task_stop。** 即使 Stop 按钮因 IDLE 禁用,Escape 仍能调用
|
||||
`c.abort()`,包括中止手动连续/增量点动和 MDI 运动。菜单/弹窗 Escape
|
||||
先结束当前 UI,文本编辑器不得屏蔽机器急停 F1。
|
||||
8. **主轴 direction 与输出 RPM 独立。** `motion.h::spindle_status_t.direction`
|
||||
是方向命令,覆盖率设为 0 不得把 CCW 投影成 CW;恢复覆盖率时必须恢复
|
||||
之前的未缩放速度。按钮按压态读取 WASM direction,调速量作用于未缩放
|
||||
的指令速度,再由覆盖率得到输出转速。
|
||||
9. **视图按钮必须改变对应内容。** `.toolbar.rotate` 只切换左键 Drag/Rotate,
|
||||
不启动自动旋转;`show_machine_speed` 显示规范速度,`show_extents` 显示
|
||||
canonical 路径范围;`show_machine_limits` 使用配置到 WASM 的真实 XYZ
|
||||
软限位;`show_offsets` 使用 G5x 平移偏置。所有显示均不改机器/程序状态。
|
||||
10. **预置投影。** `glnav.py:376-450` 的 Z/Z2/X/Y/Y2 为正交,P 为透视。
|
||||
Z2 是顶视图在屏幕平面旋转 -90 度,不是斜俯视。按源码矩阵,屏幕
|
||||
right/up 轴依次是 Z=`X/Y`、Z2=`Y/-X`、X=`Y/Z`、Y=`X/Z`、
|
||||
Y2=`Z/X`。浏览器坐标变换必须保持此映射。相机取景距离和尺寸缩放是
|
||||
显示适配,坐标标记/刀具图形不得在短程序上放大到遮挡路径。
|
||||
|
||||
### 尚未取得完整等价证据
|
||||
|
||||
| 功能 | 原生行为 / 当前边界 | 禁止的错误结论 |
|
||||
| --- | --- | --- |
|
||||
| Run 警告 | `run_warn` 对 XYZ 预览边界给出确认;当前 WASM 没有完整含刀偏 extents 警告事务 | 不得声称已复制 run_warn 的确认/取消交互 |
|
||||
| Joint/World、Home Axis | KINEMATICS_BOTH 需要 joint 数字选择与 FREE/TELEOP 状态;当前世界轴面板只支持 Home All | 不得用 switchkins 编号判断是否允许坐标轴回零 |
|
||||
| 速度滑块 | 原生线性/角速度独立,显示物理单位;当前 Jog/Max 的百分比与 6% 下限属于已公开的 Web 适配 | 不得把单一比例视为独立 angular jog 控制 |
|
||||
| 预览图层 | limits 现从 WASM XYZ 软限位 getter 读取;offsets 使用已实现的 G5x 平移;extents 使用 canonical 采样包围框 | G92/旋转/刀具偏置范围与原生文字标注仍未全量覆盖,不能称完整原生图层复刻 |
|
||||
| commanded/actual | 仿真当前两者相同,需要分别存在的 canonical 字段才可声明跟随误差显示 | 不得从 React 标签推导真实反馈差异 |
|
||||
| TOOL_TOUCH_OFF、rapidrate、optional stop、block delete、unhome、桌面工具 | 原文 ledger 中的具体 ABI/host 缺口继续有效 | SOURCE_BLOCKED 只表示已说明边界,不表示功能完成 |
|
||||
|
||||
以上差异不能通过修改流程文件降低为“源码本来如此”;必须保留来源、真实
|
||||
处理方式及未闭合原因。本次改善以已经存在的控件事务为范围,完整 LinuxCNC
|
||||
GUI/伺服移植仍不能由此宣称完成。
|
||||
|
||||
AXIS does not have one combined `RUNNING/STOPPED/READY` enum. Source parity is
|
||||
derived from independent LinuxCNC status dimensions; Web-friendly labels may be
|
||||
displayed, but they may not replace these dimensions as gate authority.
|
||||
@@ -249,6 +323,13 @@ The Web may disable an action while a browser-only asynchronous load is in
|
||||
flight. Any other gate stronger than this table must be identified as a Web
|
||||
safety adaptation, not attributed to AXIS.
|
||||
|
||||
`panel_snapshot.ui_gates` 是面向控件的源公式投影,`gates` 为命令接收门控。
|
||||
例如原生 abort 命令可随时接收,但 Stop 按钮必须 ON+非 IDLE;冷却命令可在
|
||||
执行过程中接收,但 Manual 页冷却复选框必须 ON+IDLE。不能直接把较宽的
|
||||
命令接收条件当作控件启用条件。Spindle 与 Touch Off 的源 handler 会自行
|
||||
切换模式,不能要求用户预先处于 MANUAL。F9/F10、B/b 用 `source_keyboard`
|
||||
传输标记保留不切换模式的源码分支;该标记不绕过 Worker/WASM 门控。
|
||||
|
||||
## Control Matrix
|
||||
|
||||
| Panel control | React command | Source flow and required result |
|
||||
@@ -271,7 +352,7 @@ safety adaptation, not attributed to AXIS.
|
||||
| Manual / MDI tabs | `SET_MODE` | `SOURCE_ADAPTER`: native tab callbacks are `ensure_manual` and `ensure_mdi`, including `set_motion_teleop` transitions. `axis.tcl::update_state` calls `set_mode_from_tab` only when returning to ON+IDLE from a non-idle/task-state transition while task mode is AUTO. No unrelated React effect may force a mode. |
|
||||
| Joint/World (teleop) mode | not exposed | AXIS View menu calls `set_teleop_mode`/`c.teleop_enable`; the selected WASM motion ABI has no canonical teleop setter, so Web does not pretend its coordinate mode is a joint/world toggle. |
|
||||
| Jog `-` / `+` | `JOG_START`, `SIM_STEP`, `JOG_STOP` | `SOURCE_ADAPTER`: source requires `manual_ok` and the visible Manual tab. Incremental sends one `JOG_INCREMENT`; Continuous sends one `JOG_CONTINUOUS` and motion continues until the deferred release issues `JOG_STOP`. Web servo sampling is transport/runtime work, not repeated user jog commands. |
|
||||
| Home Axis | `HOME {axis}` | `manual_ok`, `ensure_mode(MANUAL)`, then source joint-home command. Individual axis home is rejected outside identity kinematics, matching `home_joint`. |
|
||||
| Home Axis | `HOME {axis}` | `manual_ok`, then `home_joint` checks axis versus joint selection. The selected switchkins module is always KINEMATICS_BOTH, so a world-axis letter is rejected with `home_joint_requires_joint_mode`; M429 does not change this type. |
|
||||
| Home All / Re-home All | `HOME {all:true}` | `SOURCE_ADAPTER`: `manual_ok`, ensure MANUAL, if `all_homed()` ask the native re-home confirmation, then `set_motion_teleop(0)`, `c.home(-1)`, wait. Native text remains Home All; the Web `RE-HOME ALL` label is presentation-only and cannot trigger a hidden home during load. |
|
||||
| Touch Off | `TOUCH_OFF {axis,value,system,units}` | `SOURCE_ADAPTER`: source UI condition is the Touch Off formula above; task-layer MDI additionally enforces homing unless `NO_FORCE_HOMING`. Prompt for `P0 Current` through `P9 G59.3`, save mode, ensure MDI, scale linear units, issue `G10 L20 <P-token> <axis>[value*scale]`, wait, redraw/reload, restore saved mode and TELEOP. |
|
||||
| Tool Touch Off | `TOOL_TOUCH_OFF {axis,value,system,units}` | LinuxCNC source requires `G10 L10/L11` plus `G43`, but the selected WASM interpreter does not expose canonical tool-offset storage. The control is therefore disabled with `blocked_by_abi_tool_offsets`; it never sends a fake fixed `G43`. |
|
||||
@@ -305,9 +386,9 @@ history rows intentionally share the family ID `mdi-history-select`.
|
||||
| `menu-help-about`, `about-close` | `.menu.help` About entry and `.about.ok` | Local modal open/close | About visibility changes only |
|
||||
| `toolbar-estop` | `.toolbar.machine_estop` -> `estop_clicked` | `ESTOP {active}`; accepted from every task state | Toggle source E-stop edge; active edge aborts interpreter/TP |
|
||||
| `toolbar-power` | `.toolbar.machine_power` -> `onoff_clicked` | `POWER {active}`; source widget enabled whenever status is not ESTOP | Send ON only from ESTOP_RESET, otherwise OFF; stable post-off status normally projects ESTOP_RESET |
|
||||
| `toolbar-open`, `program-file-input` | `.toolbar.file_open` -> `open_file`/`open_file_guts` | File picker then `PROGRAM_LOAD`; interpreter must be IDLE | One replacement transaction; input itself is hidden and only the toolbar button opens it |
|
||||
| `toolbar-open`, `program-file-input` | `.toolbar.file_open` -> `open_file`/`open_file_guts` | File picker then `PROGRAM_LOAD`; interpreter must be IDLE | Replaces static program preview; retains Live Plot exactly as native Open |
|
||||
| `toolbar-load-case` | `WEB_EXTENSION`; uses INI `DISPLAY`, `HAL`, `KINS` and `OPEN_FILE` inputs | `LOAD_WORKSPACE` then Open adapter; IDLE plus browser async/queue exclusion | Reloads the selected exact case; never counts as an AXIS control or adds Power/Home/Run aliases |
|
||||
| `toolbar-reload` | `.toolbar.reload` -> `reload_file` | `PROGRAM_LOAD` of current editor source/path; IDLE taskfile required | Replaces preview/interpreter at line 0; this is the only visible commit action for edited source |
|
||||
| `toolbar-reload` | `.toolbar.reload` -> `reload_file` | `PROGRAM_LOAD` of current editor source/path; IDLE taskfile required | Replaces preview/interpreter at line 0; retains Live Plot whether or not source text changed |
|
||||
| `toolbar-run` | `.toolbar.program_run` -> `task_run` | `PROGRAM_RUN`; native widget ON+IDLE; task layer additionally requires a taskfile and homing unless `NO_FORCE_HOMING=1` | AUTO/READING from line 0; completed/aborted taskfile reopens directly |
|
||||
| `toolbar-step` | `.toolbar.program_step` -> `task_step` | `PROGRAM_STEP`; native widget ON+taskfile, then handler/task gates | AUTO_STEP at the next source boundary; do not add an IDLE-only UI gate |
|
||||
| `toolbar-pause-resume` | `.toolbar.program_pause` -> `task_pauseresume` | Native widget ON+non-IDLE; handler distinguishes `paused` and accepts AUTO/MDI | One source toggle whose icon/label and command follow canonical pause state and resume-inhibit |
|
||||
@@ -318,16 +399,16 @@ history rows intentionally share the family ID `mdi-history-select`.
|
||||
| `toolbar-rotate` | `.toolbar.rotate` | Local drag/rotate projection | Toggles pressed state and viewer interaction only |
|
||||
| `toolbar-clear-live-plot` | `clear_live_plot` -> `positionlogger.clear()` | Worker `VISUAL_CLEAR`; always visual-only | Advances `live_path_epoch`, clears Live Plot only and retains static preview |
|
||||
| `workspace-case-select` | Web selection of the INI/configuration input | Local selection; selected catalog is restricted to supported XYZAC case | Does not load or change machine state until `toolbar-load-case` |
|
||||
| `workspace-load-3d-chips` | Web packaged-file equivalent of `open_file_guts` | Fetch published `3D_Chips.ngc`, then `PROGRAM_LOAD`; IDLE only | Same canonical file replacement path as Open; no automatic Run/Home |
|
||||
| `workspace-load-3d-chips` | Web packaged-file equivalent of `open_file_guts` | Fetch published `3D_Chips.ngc`, then `PROGRAM_LOAD`; IDLE only | Same replacement path as Open; retains Live Plot, no automatic Run/Home |
|
||||
| `manual-home-all` | Machine Homing menu -> `home_all_joints` | `HOME {all:true}`; `manual_ok`, then MANUAL | Repeat asks source confirmation; Web Re-home label is non-authoritative presentation only |
|
||||
| `switchkins-0`, `switchkins-1`, `switchkins-2` | `M429`, `M428`, `M430` remaps and `motion.switchkins-type` | `KINEMATICS_SELECT`; ON, homed, IDLE, empty TP | Identity/XYZAC/User K canonical type, or stable gate reason |
|
||||
| `tab-manual`, `tab-mdi` | AXIS notebook F3/F5 `ensure_manual`/`ensure_mdi` | `SET_MODE` adapter; source `manual_ok` plus joint/teleop transitions apply | Tab selection plus acknowledged source-equivalent MANUAL/MDI and motion-mode transition |
|
||||
| `tab-manual`, `tab-mdi` | AXIS notebook F3/F5 `ensure_manual`/`ensure_mdi` | `SET_MODE` adapter under `manual_ok` | Acknowledged MANUAL/MDI task mode. Explicit FREE/TELEOP/joint switching is an unimplemented ABI boundary; the selected panel uses its homed world-axis motion adapter. |
|
||||
| `tab-diagnostics` | Web diagnostics boundary | Local tab only | Reads Worker state/gates; sends no command |
|
||||
| `tab-preview`, `tab-dro` | AXIS preview/DRO presentation | Local tab only | Changes visible projection; machine state unchanged |
|
||||
| `manual-axis-x`, `manual-axis-y`, `manual-axis-z`, `manual-axis-a`, `manual-axis-c` | AXIS axis radio group / `ja_rbutton` | Local axis selection | Supplies the selected axis to Jog/Home/Touch Off; sends no motion by itself |
|
||||
| `manual-axis-x`, `manual-axis-y`, `manual-axis-z`, `manual-axis-a`, `manual-axis-c` | AXIS axis radio group / `ja_rbutton` | Local axis selection; source manualgroup ON+IDLE | Supplies the selected axis to Jog/Home/Touch Off; sends no motion by itself |
|
||||
| `manual-jog-increment` | `jogincrement` combobox | Local choice: Continuous or configured increment | Determines `JOG_CONTINUOUS` versus `JOG_INCREMENT` payload only |
|
||||
| `manual-jog-negative`, `manual-jog-positive` | `jogminus`, `jogplus` -> `jog_on` | `JOG_START`; visible Manual tab and `manual_ok`; source chooses joint/teleop mode from kinematics+homing | Incremental submits once; Continuous remains active until one release/cancel `JOG_STOP` |
|
||||
| `manual-home-axis` | `home_joint` | `HOME {axis}`; `manual_ok`, MANUAL, identity kinematics for coordinate-axis home | Canonical joint home; repeated home asks confirmation |
|
||||
| `manual-home-axis` | `home_joint` | UI ON+IDLE; `HOME {axis}` checks KINEMATICS_BOTH | World-axis letter rejected before confirmation/mode mutation; individual numeric joint input remains unavailable |
|
||||
| `manual-touch-off` | `touch_off_system` | `TOUCH_OFF`; exact Touch Off UI formula, `manual_ok`, valid axis/value/system, plus task-layer homing policy | MDI `G10 L20`, wait, reload and restore saved task/motion mode |
|
||||
| `manual-tool-touch-off` | `touch_off_tool` | Permanently disabled for selected ABI | `blocked_by_abi_tool_offsets`; never approximated with `G92`/fixed `G43` |
|
||||
| `manual-override-limits` | `toggle_override_limits` | Permanently disabled for selected ABI | No state mutation; explanatory ABI title |
|
||||
@@ -335,8 +416,8 @@ history rows intentionally share the family ID `mdi-history-select`.
|
||||
| `manual-spindle-decrease`, `manual-spindle-increase` | `spindle_decrease/increase`; ButtonRelease -> `spindle_constant` | Enabled only while spindle direction is nonzero, matching `axis.tcl update_state` | Press sends speed edge without crossing through zero into the opposite direction; release/cancel sends `SPINDLE_CONSTANT` |
|
||||
| `manual-spindle-brake` | `brake` | `SPINDLE_BRAKE`; same manual source gate | Canonical brake state controls checkbox |
|
||||
| `manual-coolant-mist`, `manual-coolant-flood` | manual-group `mist`/`flood` controls | `COOLANT_COMMAND`; checkboxes use the source ON+IDLE manual-group gate | Independent canonical coolant bits; F7/F8 inverse the polled source value |
|
||||
| `mdi-command`, `mdi-submit` | MDI entry and `send_mdi` | Local text; submit/Enter sends `MDI_EXECUTE` under `manual_ok`/queue gate | Trimmed nonempty block, MDI mode, canonical interpreter result |
|
||||
| `mdi-history-select` | AXIS MDI history row selection | Local history selection | Copies selected prior command into entry; does not execute until Submit/Enter |
|
||||
| `mdi-command`, `mdi-submit` | MDI entry and `send_mdi` | UI ON+queue capacity; handler separately requires `manual_ok` | Disabled handler retains input; accepted trimmed block enters MDI and receives canonical interpreter result |
|
||||
| `mdi-history-select` | `mdi_history_butt_1`, `mdi_history_double_butt_1` | Single click selects; double-click submits through the same MDI gate | Single click copies the block only; double-click executes once, trims/clears entry and de-duplicates adjacent history |
|
||||
| `preview-panLeft`, `preview-panRight`, `preview-panUp`, `preview-panDown`, `preview-reset` | Web accessible equivalent of Gremlin mouse pan/reset | Local camera only | Moves/resets camera; no Worker command |
|
||||
| `dro-coordinate`, `dro-display`, `dro-units` | `coord_type`, `display_type`, `metric` | Local snapshot projection | Machine/Relative, Actual/Commanded and mm/inch display only |
|
||||
| `override-jog-speed` | `update_jog_slider_vel` | `SET_JOG_SCALE`; no native machine-state UI gate | Canonical linear jog value used by later jogs |
|
||||
@@ -366,8 +447,8 @@ React must derive its button gates from that state on the next snapshot.
|
||||
| `POWER(active=false)` | Source toolbar allows the toggle whenever status is not ESTOP, including active execution | Motion/task/IO abort, trajectory disables, volatile-home joints unhome, task plan synchronizes; taskfile retained | Observed state returns to ESTOP_RESET; nonvolatile homing is not falsely cleared by the UI |
|
||||
| `HOME(all/axis)` | AXIS homing UI: ON+IDLE; handler `manual_ok`, then MANUAL | Home authority updates `homed[]`; individual coordinate-axis home is rejected for non-identity/duplicate coordinates | AUTO's task layer may run only when all required joints are homed unless `NO_FORCE_HOMING=1` |
|
||||
| `PROGRAM_LOAD` | Native Open/Reload requires interpreter IDLE; Web additionally excludes active TP/MDI queue and concurrent parse | Save mode, ensure MANUAL, restore mode, set line 0, synchronize, open/replace taskfile and canon, parse status-derived preview, then apply tab mode | New taskfile/preview replace the old selection. Static preview replacement is independent of Live Plot data. |
|
||||
| `KINEMATICS_SELECT` | Machine on, homed, idle, TP queue empty, valid type 0/1/2 | M429/M428/M430 selection updates canonical kinematics and the HAL switchkins type | Identity is required for individual coordinate-axis homing; a non-empty queue blocks selection |
|
||||
| `SET_MODE(MANUAL)` | Source task rejects leaving active AUTO; tab handler also requires `manual_ok` | Task mode MANUAL plus source joint/teleop selection | Manual widgets are still controlled by ON+IDLE, not mode alone |
|
||||
| `KINEMATICS_SELECT` | Machine on, homed, idle, TP queue empty, valid type 0/1/2 | M429/M428/M430 selection updates canonical kinematics and the HAL switchkins type | KINEMATICS_BOTH remains unchanged; a non-empty queue blocks selection |
|
||||
| `SET_MODE(MANUAL)` | Source task rejects leaving active AUTO; tab handler also requires `manual_ok` | Task mode MANUAL acknowledged; explicit joint/teleop switching remains the stated boundary | Manual widgets are still controlled by ON+IDLE, not mode alone |
|
||||
| `SET_MODE(MDI)` | Tab handler requires `manual_ok`; task mode transition follows source motion-mode calls | Task mode MDI | MDI widgets use ON+queue-capacity; submission still calls `manual_ok` |
|
||||
| `PROGRAM_RUN` | Native toolbar formula is ON+IDLE and does not test taskfile. Web adds current-taskfile validation; task acceptance adds all-homed unless `NO_FORCE_HOMING=1`. Completed/aborted are valid idle starts. | `task_run`: warning, ensure AUTO, `AUTO_RUN(start_line)`, reset next start line to 0. Task layer reopens the retained file when plan is closed. | During non-IDLE, native Run relief is pressed and Open/Reload/Run/manual controls disable; on IDLE, direct Run is available again. |
|
||||
| `PROGRAM_PAUSE` | Source handler: AUTO plus READING/WAITING; combined toolbar: AUTO/MDI plus non-IDLE and not currently paused | Trajectory pauses, previous interpreter state is saved, interpreter becomes PAUSED | Combined toolbar remains enabled and becomes Resume; Stop, E-stop and Power remain enabled by their own formulas |
|
||||
@@ -404,6 +485,225 @@ the Worker gate remains final authority if a click races a status transition.
|
||||
|
||||
## Required Transactions
|
||||
|
||||
### 原生清图方式(2026-09-08 10:14,北京时间)
|
||||
|
||||
按最新要求“原生如何清除,Web 也同样方式清除”,本节取代早期自动加载清图
|
||||
扩展。以 `axis.py::reload_file/open_file_guts/clear_live_plot`、
|
||||
`LivePlotter.clear` 和 `axis.tcl:379/664` 为准:
|
||||
|
||||
| 操作 | 加工 Live Plot | 静态程序预览 |
|
||||
| --- | --- | --- |
|
||||
| Open、Reload(同文件或编辑后)、加载 Chips | 保留 | 替换为新文件解析结果 |
|
||||
| Run、再次 Run、Pause、Resume、Stop、正常结束 | 保留 | 保留 |
|
||||
| Clear Live Plot 工具栏按钮 / Ctrl+K | 清除;运行时重新累积后续实际运动 | 保留 |
|
||||
| 隐藏/显示 Live Plot | 仅改变可见性 | 不受影响 |
|
||||
|
||||
```text
|
||||
Clear Live Plot 按钮或 Ctrl+K
|
||||
-> axis.py::clear_live_plot -> LivePlotter.clear -> positionlogger.clear
|
||||
Web 对应:VISUAL_CLEAR -> Worker clearLivePlot -> live_path_epoch + 1
|
||||
-> React 删除旧加工轨迹及待播放样本,保留当前静态预览
|
||||
-> 暂停/停止时保持空白,运行时从后续 WASM 运动继续积累
|
||||
```
|
||||
|
||||
Web 不再随 PROGRAM_LOAD 自动清图,也不再发送
|
||||
`clear_live_plot_on_replace`。旧请求即使带有该扩展参数,加载路径也不再
|
||||
执行清图副作用。该退役参数不能成为隐藏的第二种清图入口。
|
||||
清图仍可在暂停时使用,且不能损坏 Resume/Stop 的通信序号和队列。
|
||||
|
||||
### 暂停后恢复与通信一致性(2026-09-08 09:50,北京时间)
|
||||
|
||||
本次可复现故障:`PROGRAM_PAUSE(seq=9)` 成功后,UI 仍允许的清图
|
||||
`VISUAL_CLEAR(seq=10)` 被旧通信层的 PAUSED 白名单拒绝,`last_seq` 停在 9。
|
||||
随后 Resume(seq=11)、Stop(seq=12) 均报 `protocol.sequence_gap`,机器永久
|
||||
停在暂停态。暂停后调节主轴倍率、执行 Step 或重复 Pause 也可能触发该问题。
|
||||
|
||||
源依据:`axis.py::task_pause/task_resume/task_pauseresume`;
|
||||
`emctaskmain.cc:2248-2286` 保存/恢复 `interpResumeState` 并调用
|
||||
`emcTrajPause/Resume`;AUTO+PAUSED 命令分支继续接收控制命令并按 task 规则
|
||||
处理。暂停不关闭 NML 通道。`axis.tcl::update_state` 在暂停时仍启用 Step、
|
||||
Resume、Stop,倍率和 Clear Live Plot 也没有暂停禁用公式。
|
||||
|
||||
```text
|
||||
合法、顺序正确的帧 -> 通信层接收 seq(READY/PAUSED 均保持连接)
|
||||
-> Worker ledger 与 task gate
|
||||
-> 接受:执行相应 WASM 命令 + canonical snapshot
|
||||
-> 拒绝:保留 gate_reason + 记录该 seq,不改变正在暂停/运行的运动
|
||||
-> 下一合法帧继续使用 seq+1
|
||||
|
||||
PAUSE -> 保存 READING/WAITING/MDI 的恢复状态,保留解释器与 TP 队列
|
||||
-> Clear / Override / Step / 被拒绝的命令不破坏通信序号
|
||||
-> RESUME -> 恢复原状态和原队列 -> 后续 SIM_STEP 的位置持续前进
|
||||
```
|
||||
|
||||
一致性约束:
|
||||
|
||||
- 通信 PAUSED 标记只表示已接收的暂停意图,不再维护另一套机床指令白名单。
|
||||
schema/session/sequence/tick/operation_id 的格式校验继续有效;格式错误或
|
||||
真正乱序帧仍拒绝,不能无条件跳过序号或自动重发副作用命令。
|
||||
- Worker task gate 继续拒绝运行/暂停中的 Load、MDI、Home、Jog 等不合法操作。
|
||||
“通信接收”不等于“机床执行”;拒绝必须保留错误和原机床状态。
|
||||
- UI 的非致命命令错误不能自行停止仍在运行的 WASM 采样。是否继续采样取决于
|
||||
canonical machine/task 状态;真正 ESTOP/OFF/FAULT/恢复过程仍停止采样。
|
||||
- Pause/Resume 期间只允许一个在途采样;拒绝命令、清图、倍率操作和重复点击
|
||||
后仍能继续。不得以 Reload/重置程序或清 TP 队列作为恢复手段。
|
||||
- 连续两次 Pause/Resume、WAITING(文件读完但 TP 未排空)、MDI 暂停、暂停中
|
||||
Step、暂停中清图/倍率、非法操作后 Resume、Resume 后 Stop 均需验证。
|
||||
- 按上方最新原生规则保留 Open/Reload 的加工轨迹;Chips 的批量绘图与
|
||||
实时推进、Home/Jog、主轴/冷却和 OPFS 偏好行为继续回归;不能为修复通信
|
||||
删除这些已实现功能或放宽其 task gate。
|
||||
|
||||
验收以实际命令 seq、错误、队列/位置、恢复后的多个连续采样为准。
|
||||
Resume 返回 acknowledged 或按钮图标改变,均不足以证明“继续执行”成功。
|
||||
|
||||
### 换程序轨迹与长程序性能(2026-09-08)
|
||||
|
||||
本次问题场景:执行第一份程序后,打开 `linuxcnc/nc_files/3D_Chips.ngc`,
|
||||
旧加工轨迹仍可见,且第二份程序运行变慢。
|
||||
|
||||
**源码事实与本次 Web 要求分别记录。** `axis.py::open_file_guts` 创建新
|
||||
`AxisCanon`,但不调用 `LivePlotter.clear()`;`clear_live_plot` 才调用
|
||||
positionlogger 清图。因此原生 Open/Reload 保留 Live Plot 不是源码错误。
|
||||
此前实现的自动换文件清图扩展已按 10:14 的最新原生要求退役。
|
||||
现在所有 Web PROGRAM_LOAD 均保持原生保留语义,清图由用户显式触发。
|
||||
|
||||
```text
|
||||
Web Open / 打包程序 / Reload
|
||||
-> PROGRAM_LOAD {source, path}
|
||||
-> 原生 Open 门控与解析,旧程序执行必须先结束
|
||||
-> 文件解析成功 -> 发布新静态预览和程序状态,live_path_epoch 保持原值
|
||||
-> React 替换静态刀路,保留加工轨迹
|
||||
取消 / gate 拒绝 / 解析失败 -> 不清加工轨迹
|
||||
Clear Live Plot / Ctrl+K -> 显式清除加工轨迹
|
||||
```
|
||||
|
||||
读取/解析期间,画布继续持有最近的 Worker 快照,不能将 `snapshot=null`
|
||||
临时投影成 `live_path_epoch=0` 而提前清图。加载成功或失败都保留 Live Plot;
|
||||
只有显式清图的 Worker 新 epoch 才删除轨迹。失败文件的静态预览可为空。
|
||||
|
||||
清图只修改可视历史,不改 task/TP/IO/坐标/偏置/Feed Override。当前程序
|
||||
Run 仍需用户单独操作,不能因加载文件而自动上电、回零或运行。
|
||||
|
||||
**执行时间依据。** 选定 INI 的 SERVO_PERIOD=1 ms、TASK CYCLE_TIME=10 ms。
|
||||
一次 `SIM_STEP {cycles:5}` 必须执行 5 个原有 task tick/50 个 servo tick;
|
||||
提升速度应减少每个 tick 的额外计算与绘图负担,不能放大位移、进给或
|
||||
逻辑步长。`3D_Chips.ngc` 的 F 值以实际文件为准,经 WASM TP 的配置速度
|
||||
和加速度限制;文件开头的注释不是替代 F 指令的依据。
|
||||
|
||||
性能核对要求:
|
||||
|
||||
- 主程序 source hash 在成功加载源文本时计算并保存;后续 snapshot 读取
|
||||
同一不可变文本的 hash,不得每个 servo/task tick 重扫整份 G-code。
|
||||
- 连续 servo 子周期内解释器没有变化时,不重复构造完整 program snapshot。
|
||||
- 静态预览将 canonical samples 的相邻顶点合并到少量 LineSegments;
|
||||
4684 段不能生成 4684 次静态 draw call;合并不得连接不相邻轨迹。
|
||||
- 原生 AXIS 程序文本位于带滚动条的固定面板。Web 移动端也必须固定编辑器/
|
||||
执行记录的 grid 行高,长程序只增加内部 scrollHeight,不能把整个页面
|
||||
撑到数万像素或一次渲染所有行号。截图检查同时验证页面高度和内部滚动。
|
||||
- 采样仍保持最多一个在途请求;不因 CPU 慢而叠加定时器或无限补发 tick。
|
||||
- 验收记录第一程序完成 → Chips 加载 → 执行 → 再加载 Chips 的用时、
|
||||
tick/墙钟比、Worker 请求延迟、绘图调用数;检查 Pause/Stop 响应和
|
||||
canonical 路径/位置/状态哈希,不能仅以肉眼感觉变快为证据。
|
||||
|
||||
从原点加载 Chips 有 4684 段;选定启动程序结束在 Z10 后再加载时,首条
|
||||
`G0 Z10` 为零位移,由 WASM 消去,预览为 4683 段。这是当前位置同步的
|
||||
正常结果,不能为满足固定段数而插入假轨迹或重置机床位置。
|
||||
|
||||
基线测量:同一运行时重复读取 200 次快照,139 字节程序约 42 ms,
|
||||
200509 字节 Chips 约 797 ms。该差异与重复文本哈希有关;最终性能结果
|
||||
以验收报告中的本机前后测量为准。
|
||||
|
||||
### 全部控件的事件与验收规则
|
||||
|
||||
上面的 Complete Web Control Inventory 是控件全集。每一行均继承以下规则,
|
||||
因此“只有点击回调,没有拒绝、结束或状态回显”的控件不满足本流程。
|
||||
|
||||
| 控件类别 | 事件入口与前置检查 | 成功路径 | 取消、拒绝和验收 |
|
||||
| --- | --- | --- | --- |
|
||||
| 机器、程序、回零、MDI、主轴、冷却、覆盖率、switchkins | 使用该行指定的 Worker gate;仅追加启动/文件读取中的异步互斥 | `operation_id → Worker → WASM → COMMAND_RESULT → panel_snapshot`;UI 使用最新快照回显 | 错误保留 `code/message_key/gate_reason` 和操作记录;不显示 acknowledged,不在 React 里补成功状态 |
|
||||
| Open、Reload、案例和打包程序 | 四条入口共用 `gates.load`,包含 PAUSED 与活动 TP 队列;Reload 还要求已有 taskfile | 文件读取完成后再次检查门控,再提交一次 `PROGRAM_LOAD` | 用户取消文件选择不发命令;门控已变化则不替换编辑器文本、路径或预览;解析失败属于已选新文件的源码错误 |
|
||||
| 点动和主轴调速按住按钮 | 主指针按下或聚焦按钮的 Space/Enter 按下;重复 keydown/第二指针不重复提交 | 记录本次手势的 axis/direction/increment 与 release 操作;按下和释放有固定命令顺序 | pointerup/cancel/leave/lost capture、keyup、窗口失焦、页面隐藏、离开 Manual 页只结束同一手势一次;release 早于 ACK 时仍按 start→stop 顺序处理 |
|
||||
| 坐标轴、步长、DRO、视图、图层、选项卡 | 使用表内本地显示状态;切换 Manual/MDI 另走 `SET_MODE` | 更新选择或投影,不能变更 canonical machine state | 每个选项均验证显示变化;隐藏 Live Plot 不得删除轨迹;旋转、平移和缩放须验证实际画布变化 |
|
||||
| 系统对刀、重复回零 | 源码门控通过后显示参数/确认对话框 | 确认后只发送一次 `TOUCH_OFF` 或 `HOME` | 取消不发运动/对刀命令;Home All 的原生 ensure MANUAL 在确认之前,Web 确认前模式处理属于适配差异;空白/非有限数字/非法 P 或 G 系统标识不提交 |
|
||||
| About 和菜单 | 本地打开、关闭、Escape;About 打开时焦点进入,关闭后返回入口 | 显示/隐藏相应内容 | 弹窗打开期间不触发普通机器快捷键;F1 急停仍可用;关闭动作不触发 Stop |
|
||||
| ABI/桌面边界控件 | 原生 disabled 属性与明确原因 | 无成功路径 | `SOURCE_BLOCKED` 不得出现机器命令或本地假开关;全部在浏览器清单中检查 |
|
||||
|
||||
菜单工具项、所有六种预置视图、五个轴选择、正负点动、两种冷却和所有滑块
|
||||
都逐一计入验收;不能仅用同组中的一个按钮代表整组。动态历史项只允许
|
||||
`mdi-history-select` 家族 ID 重复,其余 ID 必须唯一。
|
||||
|
||||
### MDI 输入和历史
|
||||
|
||||
来源:`axis.py:2413` (`mdi_up_cmd`)、`:2430` (`mdi_down_cmd`)、
|
||||
`:2448` (`send_mdi`)、`:2454` (`send_mdi_command`)、`:2541` (double-click)。
|
||||
历史上限来自 `axis.py:165` 的 1000 条,最后有一个空白输入位置。
|
||||
|
||||
```text
|
||||
Go / Enter / 历史双击
|
||||
-> manual_ok + MDI gate
|
||||
-> trim 非空命令(Web 对纯空白不提交)
|
||||
-> 清空输入、ensure MDI
|
||||
-> 若与上一条不同则追加历史(最多 1000 条,按旧到新排序)
|
||||
-> MDI_EXECUTE
|
||||
-> ACK / interpreter error + canonical snapshot
|
||||
单击历史 -> 仅选中并填入输入框
|
||||
Up / Down -> 在历史与末尾空白位置循环,不发送命令
|
||||
```
|
||||
|
||||
历史记录的是用户提交意图;解释器报错的命令也保留历史,不能据此判定执行成功。
|
||||
浏览器会话内历史属于本地输入状态;写入桌面历史文件是单独的 host boundary。
|
||||
`axis.tcl:1715/1724` 的 manualgroup 包含轴单选框,而不包含步长 combobox;
|
||||
mdigroup 包含输入框、Go 和历史列表。Web 必须连同历史列表一起投影 mdigroup
|
||||
门控;AUTO 活动但 UI 公式允许 MDI 时可以选历史,提交仍受 `manual_ok` 拒绝,
|
||||
不能清空输入或追加历史。机床关闭时该组整体禁用。
|
||||
|
||||
### 快捷键必须使用源码的大小写和修饰键
|
||||
|
||||
`axis.py:3124` 将 F1 绑定到 `all`,所以编辑器、MDI 输入和弹窗聚焦时 F1
|
||||
仍然有效;不得先因 INPUT/TEXTAREA/SELECT 聚焦而返回。非安全字母快捷键
|
||||
不抢占文本输入、浏览器 Ctrl/Meta/Alt 组合,也不得通过 `toLowerCase()`
|
||||
把没有源码绑定的大写字符转换成机器命令。`Shift-Home` 是清零,不是回零;
|
||||
该能力缺失时明确拒绝,不能落入 Home 分支。`o` 必须进入同一 Open 门控。
|
||||
|
||||
`axis.py:2871/2879` 中 F9/F10 都先检查 spindle direction:任何非零方向
|
||||
均发送 OFF,只有已停转时才分别发送 FORWARD/REVERSE。因此 F9 在 CCW
|
||||
运行时、F10 在 CW 运行时必须先停转。主轴方向单选按钮仍直接选择方向。
|
||||
F11/F12 的每次 Tk KeyPress 调用一次调速,源码并未屏蔽系统按键重复。
|
||||
Web 当前抑制重复 keydown 是显式浏览器输入适配,不属于源码限制。
|
||||
|
||||
### 连续点动的运动权威
|
||||
|
||||
`axis.py:3241/3268/3282` 定义按下、释放和失焦;
|
||||
`src/emc/motion/command.c:769` 的 `EMCMOT_JOG_CONT` 将目标设为该方向的
|
||||
点动限位,释放通过 abort 停止。Web 使用同一目标选择规则:
|
||||
|
||||
```text
|
||||
一次 JOG_START {axis, direction, continuous:true}
|
||||
-> Worker 将连续模式编码为 TP jog 的 increment=0
|
||||
-> C/WASM 从配置限位选择目标、使用配置速度/加速度提交一次轨迹
|
||||
-> 浏览器定时器仅发送 SIM_STEP,不再发送 JOG_START 或计算距离
|
||||
-> 一次 JOG_STOP -> C/WASM abort -> 队列清空 -> 停止采样
|
||||
```
|
||||
|
||||
正数 increment 保留单次增量点动语义,负值/非有限值拒绝。增量点动也持续
|
||||
采样直至 WASM 队列排空,不能只执行一个 tick 后悬停;释放不截断增量。
|
||||
选定案例的毫米/角度转换在 Worker 输入边界完成,A/C 不按 25.4 缩放。
|
||||
连续点动到达配置软限位后不得超限,反向点动必须仍可执行。
|
||||
这属于选定 TP 的 `SOURCE_ADAPTER`:当前 abort 使用 TP 清队列,完整原生
|
||||
free/teleop 伺服减速数值对照不属于已验证的按钮语义,不能据此宣称完整 motion parity。
|
||||
|
||||
### 验收证据与失败条件
|
||||
|
||||
| 证据 | 必须覆盖 | 失败判定 |
|
||||
| --- | --- | --- |
|
||||
| 源码契约检查 | Python/Tcl hash、处理函数、精确 gate、每个控件 ID | 源码漂移、漏 ID、出现未记录按钮 |
|
||||
| Worker + 实际 WASM | 正常命令、OFF/ESTOP/运行/暂停拒绝、连续/增量点动、错误回传、对刀、重复运行、Live Plot 生命周期 | 无 ACK 或 canonical 输出、拒绝变成功、轨迹越界或错误清图 |
|
||||
| 浏览器逐控件审计 | 全部控件及动态 About/历史;按下释放、失焦、取消、快捷键、门控与错误 | 未覆盖控件、重复 ID、按钮标题缺拒绝原因、控制操作仅改变本地状态 |
|
||||
| 桌面与移动端 | 实际 Three.js 画布像素、视图交互、资源加载、文字/布局边界 | 空白画布、资源错误、横向溢出或控件遮挡 |
|
||||
|
||||
运行命令与实际结果记录在 `doc-rtcp/xyzac-panel-button-parity-test-report.md`。
|
||||
本地浏览器证据必须标明本地 URL,不能写成已经部署或已验证线上版本。
|
||||
|
||||
### Startup program selection
|
||||
|
||||
```text
|
||||
@@ -498,6 +798,25 @@ display token, records the normalized `source_system`, and executes the same
|
||||
canonical P form. This keeps the source command and the interpreter encoding
|
||||
auditable without changing the UI behavior.
|
||||
|
||||
对刀的数据闭环以 `interp_convert.cc::convert_setup/convert_coordinate_system`
|
||||
为参照:G10 只修改指定 P 系统的偏置,不能移动机器当前位置。执行前先用
|
||||
TP 实际位置同步解释器;毫米/英寸只转换线性轴,A/C 使用角度值。`G59.1`、
|
||||
`G59.2`、`G59.3` 合法,`G54.1` 等非源码系统标识必须拒绝。
|
||||
|
||||
```text
|
||||
TP 实际位置 -> 解释器同步 -> G10 L20 Pn AxisValue
|
||||
-> WASM 保存九个坐标系偏置,机器位置不变
|
||||
-> 使用相同偏置、当前位置和模态状态重新生成静态 preview
|
||||
-> 发布偏置 + machine/relative 显示投影,live epoch 不变
|
||||
-> 恢复保存的 task mode(包括错误返回分支)
|
||||
```
|
||||
|
||||
文件 Reload/Open 和 Run 重开当前文件必须保留坐标偏置;临时预览解释器从
|
||||
WASM 快照初始化偏置,不能默认所有偏置为零。Worker 的 relative 投影只做
|
||||
`TP machine position - WASM active coordinate offset`,不得回写成运动目标。
|
||||
带 G92、坐标旋转或刀具长度的完整坐标变换仍属于对应 ABI 的能力边界,
|
||||
不得用这个平移投影冒充它们;此处验证当前选定案例的 G54..G59.3 平移偏置。
|
||||
|
||||
### Continuous jog
|
||||
|
||||
AXIS binds Button-1 to continuous jog and ButtonRelease to `jog_stop`.
|
||||
@@ -509,8 +828,8 @@ The Web equivalent keeps one active `{axis,direction}` gesture, sends one
|
||||
|
||||
### Toolpath display lifecycle
|
||||
|
||||
The panel renders two different path layers. They must never share an implicit
|
||||
clear condition:
|
||||
The panel renders two different path layers. Both the current Web panel and
|
||||
the native AXIS follow the lifecycles below:
|
||||
|
||||
| Layer | Source owner | Visible/retained | Cleared or replaced |
|
||||
| --- | --- | --- | --- |
|
||||
@@ -520,9 +839,8 @@ clear condition:
|
||||
The Worker publishes a monotonic `visual.live_path_epoch`. React may clear the
|
||||
machining Live Plot only when this value changes. Array identity, a full versus
|
||||
delta snapshot, Pause, Resume, Stop, task mode, queue depth and completion are
|
||||
not clear signals. To match the source, the epoch changes only for the explicit
|
||||
`VISUAL_CLEAR` adapter (or restoration of a previously persisted epoch); it
|
||||
must not change on a program lifecycle command.
|
||||
not clear signals. The epoch changes only for `VISUAL_CLEAR` (or restoration
|
||||
of a persisted epoch), never as an Open/Reload side effect.
|
||||
|
||||
```text
|
||||
PROGRAM_LOAD accepted -> live epoch unchanged -> replace static preview only
|
||||
@@ -539,11 +857,9 @@ If Clear Plot is used while motion is still active, history before the clear
|
||||
edge is removed and `positionlogger` semantics immediately begin accumulating
|
||||
the subsequent motion in the new epoch. At IDLE, the cleared trace stays empty.
|
||||
|
||||
This source-exact rule supersedes the earlier Web extension that cleared the
|
||||
trace on Run, Restart or Load. That extension contradicted native AXIS and is no
|
||||
longer part of this contract. A product variant that wants per-job traces must
|
||||
provide a separately named export/archive action; it may not change the AXIS
|
||||
Clear Live Plot lifecycle while claiming source parity.
|
||||
The native rule above is the current command contract. The previous automatic
|
||||
Web replacement-clear extension is retired, including requests sent by an old
|
||||
caller with its obsolete flag. Clear Live Plot is an explicit user operation.
|
||||
|
||||
## Implementation Invariants
|
||||
|
||||
@@ -563,8 +879,9 @@ Clear Live Plot lifecycle while claiming source parity.
|
||||
7. Executable program source/path is never part of UI preference persistence;
|
||||
refresh performs one INI-authorized startup load.
|
||||
8. Live Plot clearing is controlled only by the Worker `live_path_epoch`;
|
||||
React object/array replacement is never a lifecycle signal, and only the
|
||||
explicit `VISUAL_CLEAR` source adapter advances the epoch.
|
||||
React object/array replacement is never a lifecycle signal. The
|
||||
explicit `VISUAL_CLEAR` source adapter advances the epoch. Program loading
|
||||
does not call the clear operation, including same-file Reload and edited text.
|
||||
9. Every contract row carries or inherits one of `SOURCE_EXACT`,
|
||||
`SOURCE_ADAPTER`, `SOURCE_BLOCKED` or `WEB_EXTENSION`; extensions and blocked
|
||||
capabilities are never counted as source parity.
|
||||
@@ -582,6 +899,16 @@ rerun contract is covered by `web/worker/test_xyzac_program_rerun.mjs` and the
|
||||
published-site execution sequence is covered by
|
||||
`scripts/test_xyzac_program_execution_browser_strict.mjs`.
|
||||
The source-surface audit is enforced by `scripts/test_axis_control_contract.mjs`.
|
||||
The press/release, focus-loss, MDI history, modal keyboard and desktop/mobile
|
||||
canvas paths are checked by `scripts/test_axis_panel_source_flow_browser.mjs`.
|
||||
`web/worker/test_xyzac_source_widget_gates.mjs` checks UI-versus-command gates,
|
||||
OFF/ESTOP overrides and the source mode transitions against real WASM.
|
||||
`scripts/test_axis_source_ui_oracle.mjs` executes the original Tcl
|
||||
`update_state` procedure across 576 combinations and compares 13 widget
|
||||
predicates (7488 assertions), rather than comparing two rewritten formulas.
|
||||
`web/worker/test_axis_source_execution.mjs` verifies repeated Step, queued
|
||||
motion Step, WAITING/MDI Pause-Resume, source OFF state, world-axis home rejection
|
||||
and spindle override 0-to-100 restoration using the actual WASM runtime.
|
||||
The exhaustive control-ID audit is enforced by
|
||||
`scripts/test_axis_control_inventory.mjs` and
|
||||
`scripts/test_axis_control_inventory_browser.mjs`; the latter rejects any
|
||||
@@ -595,8 +922,9 @@ When LinuxCNC source behavior or the selected machine configuration changes:
|
||||
4. Update the React handler only after the Worker contract is verified.
|
||||
5. Add normal, blocked and recovery assertions to the focused test.
|
||||
6. Regenerate the relationship graph from its DOT source.
|
||||
7. Run the panel browser test, build the React bundle, and publish a new
|
||||
release directory without replacing the previous rollback target.
|
||||
7. Run the panel browser tests, build the React bundle and record the local
|
||||
preview URL. Publishing is a separate explicitly authorized action; local
|
||||
evidence must not be represented as a deployment.
|
||||
|
||||
The document deliberately records ABI blockers and source boundaries. A
|
||||
`verified` Web slice means the implemented path matches this contract; it does
|
||||
|
||||
Reference in New Issue
Block a user