Files
linuxcncWebWASM/docs/linuxcnc-axis-web-control-panel-source-logic.md

90 KiB
Raw Blame History

LinuxCNC AXIS Web Control Panel Source Logic

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

Reproducible graph source: linuxcnc-axis-web-control-panel-source-logic.dot

This document is the control-flow authority for the Web panel. A button is implemented only when its complete path is defined here:

browser gesture
  -> React handler
  -> AxisOperationController operation
  -> SimSession command gate
  -> LinuxCNC/WASM task, interpreter, TP or IO authority
  -> canonical snapshot
  -> React projection

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.

"Source parity" has a strict meaning in this document:

Mark Meaning
SOURCE_EXACT Same user action, source handler, UI enable formula, command ordering and observable result as AXIS.
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 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 updated together.

The panel does not add a startup/run alias. The source-defined machine sequence is explicit: release E-stop when needed, power on, home, then press the single toolbar Run control (or the source R shortcut).

Native AXIS selects its startup program in this exact priority order: positional command-line argument, AXIS_OPEN_FILE, [DISPLAY] OPEN_FILE, then the built-in lathe/mill sample. The browser has no AXIS process argument or process environment override, so the selected INI's [DISPLAY] OPEN_FILE is the first applicable source authority. For xyzac-trt.ini it selects ./demos/xyzac_switchkins.ngc. Browser UI persistence must not insert a new higher-priority executable source and must never schedule a hidden second PROGRAM_LOAD after the configured file appears.

AXIS has both Machine/View menu bindings and toolbar/notebook bindings. The Web surface keeps the toolbar as the single machine-command entry point and the notebook tabs as the single view entry point. This removes duplicate buttons; the command, gate and source symbol for each retained entry are unchanged.

Source References

The primary GUI source is linuxcnc/src/emc/usr_intf/axis/scripts/axis.py. The accepted command and post-completion/abort behavior are also checked against linuxcnc/src/emc/task/emctaskmain.cc; the Web panel must not stop at the Python button handler when the LinuxCNC task layer defines an additional transition.

Source-lock manifest

The source baseline is the submodule commit above, not a LinuxCNC version name or an approximate UI description. The two primary GUI files are additionally fingerprinted so the audit fails on an unreviewed source drift:

Source Baseline SHA-256 Normative responsibility
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
src/emc/motion/homing.c ee73d38f2feb8d5523b9a733cb17d635d3904667aaa219f8a02d7fbffee1ba39 Native joint homing and sequence state machines, compiled into WASM
src/emc/motion/simple_tp.c 5a0026f4d5bdd9d1bc55290c6ea9c399abd664633b69413c307f7353953d2603 Native free-mode joint motion during homing
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

The current local LinuxCNC tree contains unrelated heartbeat instrumentation and an INTRO_GRAPHIC substitution. Neither changes the AXIS control flow specified here; they are not used to redefine parity.

AXIS source symbol Role
manual_ok, ensure_mode Polling, task-state and mode preconditions
estop_clicked, onoff_clicked E-stop and machine power edges
open_file_guts File replacement transaction and mode restoration
task_run, task_run_line, task_step, task_pause, task_resume, task_pauseresume, task_stop Program execution lifecycle
send_mdi, send_mdi_command MDI history, mode and command submission
home_all_joints, home_joint Homing and joint/kinematics restrictions
touch_off_system, touch_off_tool Coordinate/tool offset dialogs and G10/G43 sequence
spindle, spindle_forward_toggle, spindle_backward_toggle, spindle_increase, spindle_decrease, spindle_constant Spindle direction, speed and brake controls
mist_toggle, flood_toggle, brake_on, brake_off Auxiliary IO controls
toggle_override_limits, cycle_view Limit override and preview view selection
update_state, set_mode_from_tab in axis.tcl Exact widget enable/disable rules and idle mode restoration

The widget wiring is in linuxcnc/share/axis/tcl/axis.tcl: File/Machine/View/ Help menus are at lines 55-464, toolbar bindings at 490-669, manual and MDI controls at 839-1288, sliders at 1396-1538, control groups at 1715-1726 and the normative update_state formulas at 1767-1888.

Source Surface Inventory

This inventory separates source controls from the smaller Web surface. A source-only or host-bound entry is documented explicitly rather than silently replaced by a different action.

Source surface Source entries Web treatment
File menu Open, Recent Files, Edit, Reload, Save As, Properties, Edit Tool Data, Reload Tool Data, Ladder Editor, Quit Open and Reload are SOURCE_ADAPTER toolbar entries. Edit Tool Data and Quit are visible SOURCE_BLOCKED entries. The remaining host/file actions are recorded below and are not silently aliased.
Machine menu E-stop, Power, Run, Run from selected line, Step, Pause, Resume, Stop, Optional Stop, Block Delete, MDI history actions, six desktop tools, Homing, Unhoming, ten zero-coordinate actions and two tool-touch modes E-stop/Power/Run/Step/Pause/Resume/Stop/Home/Touch Off are represented; every omitted or ABI-blocked source action is recorded below.
View menu Five preset views, inches/mm, program/rapid/alpha/live/tool/extents/offset/limit/velocity/DTG layers, grid, large DRO font, PyVCP, commanded/actual, machine/relative, joint/world and Clear Live Plot The menu has five views; the toolbar additionally has inverted-front Y2, for six toolbar presets. Supported projections are SOURCE_ADAPTER; every omitted view action is recorded below.
Toolbar E-stop, Power, Open, Reload, Run, Step, Pause/Resume, Stop, Block Delete, Optional Stop, zoom, six views, rotate, Clear Plot Same source order and icon assets are retained for supported controls; unsupported controls are disabled with a source/ABI reason.
Manual notebook Axis/joint selector, continuous/increment jog, Home Axis, Touch Off, Tool Touch Off, Override Limits, spindle direction/stop/speed/brake and coolant XYZAC axis selector, jog, Home Axis, Touch Off, spindle and coolant are represented. Joint selection, Tool Touch Off and Override Limits are explicitly bounded by the selected configuration/ABI. Tool and probe state is read-only; Prepare/Change/Arm/Disarm are IO/HAL operations, not AXIS main-panel controls.
Web case strip no AXIS equivalent Load exact XYZAC TRT case is a Web workspace/bootstrap control, not an AXIS source button. It may only call the same documented LOAD_WORKSPACE + PROGRAM_LOAD transaction and must not introduce a second Run/Power/Home flow.
MDI notebook MDI entry, Go, history selection/double-click, Up/Down history, Ctrl-M/H/Shift-H clipboard operations Entry, Go and local history are represented; clipboard/file history operations are host/UI-only and are not sent as machine commands.
Help menu/dialogs About AXIS, Quick Reference, dialog close buttons and website link About is SOURCE_ADAPTER. Quick Reference and the external website link are not rendered; neither is mapped to a machine action.
Bottom controls Linear jog speed, angular jog speed, max velocity, spindle override, feed override and rapid override Linear jog, max velocity, spindle and feed are adapters. Angular jog and rapid override are SOURCE_BLOCKED; one must never stand in for the other.
Browser UI persistence AXIS preferences do not override [DISPLAY] OPEN_FILE with executable source text OPFS may persist presentation preferences such as view, zoom, tabs, units and selected axis. It must not persist or automatically load G-code source/path.

The Web surface may remove duplicate menu entries, but it may not change the meaning of a retained command. This is why Machine/View menus are not rendered when the same source action already has a toolbar or notebook entry.

Exhaustive native-action ledger

This ledger accounts for every interactive family constructed by axis.tcl or bound by axis.py. Separators, labels, scrollbars and read-only status text are not actions. Dynamically generated joint and grid entries are counted as families because their cardinality comes from the selected INI.

Native action family Source symbols Parity disposition
File Open/Recent/Edit/Reload/Save/Properties open_file, open_file_name, edit_program, reload_file, save_gcode, gcode_properties Open/Reload: SOURCE_ADAPTER; Recent/Edit/Save/Properties: browser file/host boundary, not rendered
Tool table edit/reload, Ladder Editor, Quit edit_tooltable, reload_tool_table, classicladder, destroy Tool editor/Quit: visible SOURCE_BLOCKED; reload/Ladder: host boundary, not rendered
E-stop and Power estop_clicked, onoff_clicked SOURCE_ADAPTER; exact edge selection and source widget gates apply
Run, Run from line, Step, Pause, Resume, Stop task_run, task_run_line, task_step, task_pause, task_resume, task_pauseresume, task_stop Run/Step/toggle Pause-Resume/Stop: SOURCE_ADAPTER; Run from line is SOURCE_BLOCKED until source-line selection is canonical
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 and numeric Home Joint: SOURCE_ADAPTER, native homemod execution. World-axis requests reject for BOTH. Unhome remains unavailable.
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
Inches/MM, commanded/actual, machine/relative metric, display_type, coord_type SOURCE_ADAPTER; display projection only
Show program, rapids, alpha, live plot, tool, extents, offsets, limits, velocity, DTG toggle_show_*, toggle_program_alpha Program/live/extents/offsets/limits/velocity have adapters; rapids, alpha, tool toggle and DTG are not rendered
Grid Off/custom/INI increments, large DRO font, PyVCP, Joint/World set_grid_size, set_grid_size_custom, toggle_dro_large_font, toggle_show_pyvcppanel, set_teleop_mode Not rendered; Joint/World is SOURCE_BLOCKED, presentation-only settings are not aliased
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/increment selection and dedicated numeric homing-joint selection: SOURCE_ADAPTER; full joint-mode jogging remains unavailable
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 home_joint, home_joint_number, touch-off/override widgets Home Joint uses explicit numeric selection and native homemod. Touch Off is a coordinate adapter; Tool Touch Off/Override Limits remain 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
Linear/angular jog speed, max velocity, spindle/feed/rapid override update_*slider_vel, set_maxvel, set_spindlerate, set_feedrate, set_rapidrate Linear jog/max velocity/spindle/feed: SOURCE_ADAPTER; angular jog/rapid: SOURCE_BLOCKED
About, Quick Reference, clear notifications About/keys dialogs and Ctrl-Space binding About: local adapter; Quick Reference and notification-clear are not rendered
Case selector/load, packaged 3D Chips, Diagnostics, pan/reset buttons, source editor no AXIS widget equivalent WEB_EXTENSION; may not be counted as AXIS parity

Source Keyboard Contract

The following bindings are copied from axis.py lines 3109-3186 and 3194-3327. The source binds lowercase Tk event sequences even though Quick Reference displays several letters in uppercase. Keys requiring an unavailable command are blocked instead of being silently mapped to another operation.

Key Source handler Web treatment
F1, F2, lowercase o, r, t, p, s, Escape, Ctrl-r, F7-F12 E-stop, power, open, run, step, pause, resume, stop, reload, coolant and spindle handlers Worker-backed commands where the corresponding control is supported. Display labels remain O/R/T/P/S.
uppercase R, F task_reverse, task_forward Source controls are recorded as blocked because the selected WASM program ABI has no reverse/forward command; they are never mapped to Run.
Home, Ctrl-Home, Shift-Home, End, Ctrl-End selected joint home, home all, zero selected G54 axis, system touch off, tool touch off Home uses numeric joint selection (and Home All uses the native configured joint sequences); system Touch Off has an adapter; zero-G54 and Tool Touch Off are blocked.
lowercase x, y, z, a Select the first four source axes Axis selection adapter. Native AXIS does not bind lowercase c to C-axis selection; c selects continuous jog. C remains selectable only through its radio control.
-, =, arrows/Page keys/[/] and their Shift variants jog_on_map/jog_off_map or selected-axis jog_minus/jog_plus Jog key semantics require keydown start and keyup stop. Pointer controls are supported; unavailable key bindings are not reassigned.
lowercase c, lowercase i, uppercase I Continuous, next incremental and previous incremental jog modes Represented by the jog increment selector; no second machine-mode state is created.
`, digits 0-9 Select joints in joint mode; otherwise set feed override from 0% to 100% Joint-mode selection is blocked. Feed changes, if bound, must use the same mode-dependent meaning.
,/.; </>; ;/' Linear jog speed; angular jog speed; maximum velocity Linear jog/max velocity have adapters; angular jog speed is blocked and must not alias linear speed.
v, d, !, @, #, $ Cycle view, rotate, units, display type, coordinate type, teleop mode View/rotate/units/display/coordinate actions are projected; teleop mode is explicitly unavailable.
uppercase B, lowercase b brake_on, brake_off Must preserve the distinct source edges; Quick Reference describes them as Shift-B/B.
Ctrl-K clear_live_plot Worker visual-only VISUAL_CLEAR.
Ctrl-S, Ctrl-M/H/Shift-H, F3, F4, F5, Ctrl-Space Save G-code, MDI history, notebook selection and notification clear Save/clipboard actions are host boundaries; tabs are local navigation plus their documented mode callback; notification clear has no machine effect.

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. 回零采用原生 homemod。 switchkins.c::kinematicsType() 始终返回 KINEMATICS_BOTH,M429 不改变这一类型。世界轴字母不能作为回零目标; manual-home-joint-select 明确选择数字关节,manual-home-axis 保留 ID, 按钮显示 Home Joint,提交 HOME {joint:0..4}。Home All 提交 HOME {all:true}。WASM 直接编译源 homing.c 和 simple_tp.c, 不再预览 home-limit 后排一条协调 TP 回零直线。完整语义见下方 Native Homing。
  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 回零执行使用原生 FREE/homemod;数字关节选择用于 Home Joint,轴选择仍用于世界坐标 Jog/Touch Off 不得用 X/Y/Z/A/C 偷换 joint,也不得把 M429 当作 KINEMATICS_IDENTITY。完整 Joint/World 点动切换尚未提供。
速度滑块 原生线性/角速度独立,显示物理单位;当前 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.

Source dimension Exact values used by AXIS Meaning
task_state STATE_ESTOP, STATE_ESTOP_RESET, STATE_OFF, STATE_ON Machine E-stop/power state. emctask.cc::determineState() publishes only ESTOP, ESTOP_RESET or ON; STATE_OFF is an accepted command value and is documented by emcmodule.cc as not used as a stable status value.
task_mode MODE_MANUAL, MODE_AUTO, MODE_MDI Current task command domain.
interp_state INTERP_IDLE, INTERP_READING, INTERP_PAUSED, INTERP_WAITING Interpreter lifecycle used by axis.tcl::update_state.
task_paused / paused Boolean pause projection Used by task_resume and toolbar relief/icon projection.
exec_state DONE, ERROR, motion/IO/delay/system-command wait states Task executor progress; it is not interchangeable with interp_state.
motion_mode TRAJ_MODE_FREE, TRAJ_MODE_COORD, TRAJ_MODE_TELEOP Joint/world motion selection and touch-off availability.
taskfile, homed[], on_any_limit, queued_mdi_commands, spindle[].direction Independent status fields Additional source gates; none may be inferred from a React label.

The source helper definitions are exact:

running = task_mode in {AUTO, MDI} and interp_state != IDLE

manual_ok = task_state == ON
            and not running
            and (interp_state == IDLE
                 or (task_mode == MDI
                     and queued_mdi_commands < max_queued_mdi_commands))

ensure_mode(m, *p) polls, returns if the current task mode is an accepted mode, otherwise sends c.mode(m), waits for completion and polls again. It is a command transaction, not a React state assignment and not a success check that may be skipped.

Source-exact widget enable formulas

These expressions are copied from axis.tcl::update_state; handler and task checks still apply after a widget is enabled.

Native widget/family Exact AXIS UI enable condition
E-stop Always enabled
Power task_state != STATE_ESTOP
Open interp_state == INTERP_IDLE
Reload interp_state == INTERP_IDLE && taskfile != ""
Run task_state == STATE_ON && interp_state == INTERP_IDLE
Step task_state == STATE_ON && taskfile != ""
Pause menu task_state == STATE_ON && interp_state in {INTERP_READING, INTERP_WAITING}
Resume menu task_state == STATE_ON && interp_state == INTERP_PAUSED
Pause/Resume toolbar task_state == STATE_ON && interp_state != INTERP_IDLE
Stop task_state == STATE_ON && interp_state != INTERP_IDLE
Run from selected line task_state == STATE_ON && interp_state == INTERP_IDLE && highlight_line != -1
Homing/Unhoming/Zero-coordinate menus task_state == STATE_ON && interp_state == INTERP_IDLE
Manual group task_state == STATE_ON && interp_state == INTERP_IDLE
MDI group task_state == STATE_ON && queued_mdi_commands < max_queued_mdi_commands
Touch Off `task_state == STATE_ON && interp_state == INTERP_IDLE && (motion_mode != TRAJ_MODE_FREE
Tool Touch Off Touch Off condition plus a loaded tool
Override Limits on_any_limit
Spindle slower/faster task_state == STATE_ON && interp_state == INTERP_IDLE && spindle.direction != 0
Feed/spindle/rapid override and max-velocity sliders No update_state disable formula; setters send their command directly. INI and command-layer range/enable rules still apply.
View controls and Clear Live Plot No machine-state gate

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
Emergency Stop / Cancel ESTOP {active} SOURCE_ADAPTER: estop_clicked sends STATE_ESTOP_RESET only when status is STATE_ESTOP; every other status sends STATE_ESTOP. emcTaskSetState(ESTOP) aborts motion, spindle, task and IO, disables trajectory, applies volatile unhome and synchronizes the task plan.
Power on / off POWER {active} SOURCE_ADAPTER: onoff_clicked sends STATE_ON only from STATE_ESTOP_RESET; otherwise it sends STATE_OFF. Power off aborts motion/task/IO and volatile homes; the subsequently observed source state is normally STATE_ESTOP_RESET, not stable STATE_OFF. The selected taskfile path remains status data.
Open G-code file browser file + PROGRAM_LOAD SOURCE_ADAPTER: native UI gate is interpreter IDLE. open_file_guts polls, saves mode, ensures MANUAL, processes an optional filter, restores saved mode, resets start line/highlight, task_plan_synch + wait, program_open, replaces the text/canon, constructs status-derived preview init codes, parses, redraws, then calls set_mode_from_tab. A parse error is reported against the newly selected file; AXIS does not roll back to the previous canon.
Load case / packaged program / edited source LOAD_WORKSPACE and/or PROGRAM_LOAD WEB_EXTENSION entry points. Once a file is selected they must use the Open/Reload adapter and may add a browser-load/queue exclusion while asynchronous parsing is active. Edited text is committed only through Reload; these controls are not native AXIS buttons.
Switchkins Identity / TCP: XYZAC / User K KINEMATICS_SELECT {type} SOURCE_ADAPTER for the selected INI's HALUI MDI commands: M429 -> type 0, M428 -> type 1, M430 -> type 2; each remap executes M68 E3 Q<type> then M66 E0 L0 and verifies motion.switchkins-type in task context. The Web's homed/idle/empty-TP gate is a stated safety strengthening around the source MDI/task gate.
Run PROGRAM_RUN run_warn, ensure_mode(AUTO), AUTO_RUN from the selected start line, then periodic task/TP ticks. Normal AXIS Run uses line 0. In emctaskmain.cc, an ended or aborted task plan is closed; the next EMC_TASK_PLAN_RUN reopens the current taskfile and runs from line 0. Therefore Run after completed or aborted must work directly and must not require a separate Reload/Restart click.
Run from selected line not exposed AXIS task_run_line requires an editor highlight and source AUTO_RUN start line. The Web editor has no source-line selection command yet, so no partial/fake button is rendered.
Reverse / Forward not exposed AXIS task_reverse/task_forward are keyboard-only source actions; the selected WASM program ABI has no corresponding command and Web preserves the distinction from Run.
Step PROGRAM_STEP SOURCE_ADAPTER: source toolbar gate is ON plus taskfile, not interpreter IDLE. task_step clears highlight/runs warning unless already AUTO+IDLE, ensures AUTO and sends AUTO_STEP. Task-layer homing remains authoritative.
Pause / Resume PROGRAM_PAUSE / PROGRAM_RESUME SOURCE_ADAPTER: task_pause accepts AUTO plus READING/WAITING. task_resume requires paused plus mode AUTO or MDI. The combined toolbar calls task_pauseresume, accepts AUTO or MDI, blocks resume when resume_inhibit, and otherwise pauses any non-IDLE interpreter. Web scheduling must serialize the edge but cannot change these state conditions.
Stop PROGRAM_ABORT AXIS enables Stop only while the interpreter is non-idle; then it highlights the active line, issues c.abort(), waits for completion, and clears the abort edge. The Worker may accept an internal abort edge in more states, but the Web button follows the AXIS UI gate.
Block delete / optional pause disabled LinuxCNC source has these controls, but the selected WASM ABI does not expose their canonical state. They are visible as disabled, with blocked_by_abi, and never emulate a local toggle.
Preview view, zoom, rotate, clear plot local view or VISUAL_CLEAR SOURCE_ADAPTER: view operations do not change task/interpreter/TP/IO. Ctrl-K maps clear_live_plot() -> live_plotter.clear() -> positionlogger.clear() and clears only Live Plot; it never deletes the loaded AxisCanon preview. Native Run, Stop, completion and Open/Reload do not call this clear path.
DRO coordinate/display/units local projection Maps AXIS coord_type, display_type and metric variables; values remain Worker snapshot projections.
Preview layers local projection SOURCE_ADAPTER only where the source variable exists: path=show_program, live=show_live_plot, limits=show_machine_limits, velocity=show_machine_speed, offsets=show_offsets, extents=show_extents. AXIS also has rapids, alpha, tool and DTG controls that the Web does not expose. Toggling visibility never deletes layer data.
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; WASM now exposes motion mode for homing and task transitions; the full joint/world jog toggle is still not rendered.
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 Joint HOME {joint} SOURCE_ADAPTER: select numeric joint, confirm re-home if applicable, ensure MANUAL, native FREE + do_home_joint(j), then servo-driven do_homing/simple_tp_update. Letter-axis requests reject for BOTH.
Home All / Re-home All HOME {all:true} SOURCE_ADAPTER: manual_ok, ensure MANUAL, re-home confirmation, native FREE + do_home_joint(-1). ACK means accepted; only native homing/homed/home_state establish completion. HOME_SEQUENCE comes from INI.
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.
Override limits disabled SOURCE_BLOCKED: widget enable requires on_any_limit; handler separately requires interpreter IDLE. If already overridden it ensures AUTO; otherwise it ensures MANUAL and calls c.override_limits(). The selected WASM ABI cannot represent this canonical path.
Spindle CCW / stop / CW SPINDLE_COMMAND SOURCE_ADAPTER: manual_ok, ensure MANUAL, then c.spindle(0) or c.spindle(direction, default_spindle_speed). Pressed state comes from canonical spindle[0].direction, not an RPM-sign inference.
Spindle - / + SPINDLE_COMMAND {delta} → SPINDLE_CONSTANT on release AXIS binds Button-1 to spindle_decrease/spindle_increase and ButtonRelease to spindle_constant; Web preserves this press/release sequence.
Brake SPINDLE_BRAKE Source brake command, gated by manual_ok.
Mist / Flood COOLANT_COMMAND SOURCE_ADAPTER: manual checkboxes belong to the ON+IDLE manual group and call c.mist(value)/c.flood(value) directly. F7/F8 poll status and send the inverse canonical bit.
Tool / probe status read-only AXIS does not put Prepare/Change/Arm/Disarm buttons in this manual notebook. Tool changes remain interpreter/MDI and IO/HAL operations. Web displays canonical tool/probe state but exposes no extra main-panel command buttons.
Jog Speed SET_JOG_SCALE SOURCE_ADAPTER: the slider changes AXIS's linear jog-speed value used by subsequent jog commands; it is not itself a motion command and has no update_state power gate.
Angular Jog Speed not exposed AXIS has a separate ajogspeed control for rotary joints; the selected WASM TP exposes one shared jog scale only, so the Web panel does not alias linear Jog Speed to this control.
Feed Override SET_OVERRIDE SOURCE_ADAPTER: set_feedrate sends c.feedrate(percent/100) without a machine-state UI gate. Selected INI MAX_FEED_OVERRIDE=2 makes the range 0-200%. Web debouncing may coalesce pointer events but must preserve the last ordered value.
Spindle Override SET_SPINDLE_OVERRIDE SOURCE_ADAPTER: set_spindlerate sends c.spindleoverride(percent/100) without an update_state power gate. Its maximum defaults to MAX_FEED_OVERRIDE when MAX_SPINDLE_OVERRIDE is absent.
Max Velocity SET_MAX_VELOCITY SOURCE_ADAPTER: set_maxvel converts display units and sends c.maxvel(value/60) without an update_state power gate. The selected WASM TP exposes this as its velocity limit/scale.
Rapid Override disabled AXIS set_rapidrate calls c.rapidrate and is distinct from Max Velocity. The selected WASM TP has no rapid-scale setter, so the Web control is disabled with blocked_by_abi_rapidrate.
MDI command/history MDI_EXECUTE SOURCE_ADAPTER: send_mdi validates manual_ok; nonempty text is trimmed, the entry cleared, MDI mode ensured, and the command is inserted unless it equals the immediately preceding history entry, then c.mdi(command) is sent. A single click selects history; native double-click executes it.
File tool-data editor / Quit disabled AXIS launches a host editor or terminates the desktop process. Browser controls remain visibly disabled with host_boundary_tool_editor / host_boundary_process_quit; clicking them cannot report a false success.
About AXIS local dialog SOURCE_ADAPTER: mirrors the source .about open/OK-close action and changes no Worker/task/TP/IO state. The native website link is a separate external-host action.

Complete Web Control Inventory

Every rendered actionable element has a stable data-axis-control value. This table is exhaustive for the selected XYZAC panel. A control may not be added to React until its ID, source mapping, gate and result are added here. Repeated MDI history rows intentionally share the family ID mdi-history-select.

Control ID(s) Source mapping Operation and gate Accepted result / disabled behavior
menu-file, menu-help axis.tcl File/Help cascades Local menu disclosure; always available Opens/closes menu only; no machine state change
menu-file-edit-tool-data, menu-file-quit edit_tooltable, quit host process operations Permanently disabled in a browser host_boundary_tool_editor / host_boundary_process_quit; no fake status success
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 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; 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
toolbar-stop .toolbar.program_stop -> task_stop PROGRAM_ABORT; native widget ON+non-IDLE Highlight AUTO running line when nonzero, abort, wait, retain selected taskfile
toolbar-block-delete, toolbar-optional-pause Source toolbar toggles Permanently disabled for selected ABI blocked_by_abi; no React-only toggle
toolbar-zoom-in, toolbar-zoom-out zoomin, zoomout Local preview camera only Bounded camera zoom; task/TP unchanged
toolbar-view-z, toolbar-view-z2, toolbar-view-x, toolbar-view-y, toolbar-view-y2, toolbar-view-p set_view_z/z2/x/y/y2/p Local source view selection Exactly one pressed view; opens Preview tab; machine state unchanged
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 replacement path as Open; retains Live Plot, no automatic Run/Home
manual-home-all home_all_joints HOME {all:true}; ON+IDLE, no concurrent homing/jog Native homemod selects the configured sequences. No artificial coordinated return-to-zero motion.
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 ensure_manual / ensure_mdi SET_MODE; manual_ok and motion exclusion Task mode acknowledgement and WASM motion mode are monitored separately; no local tab is mode authority.
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; source manualgroup ON+IDLE Supplies the selected world axis to Jog/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-joint-select Native numeric joint Homing selection Local integer 0..4; same homing gate Selects only the target joint; no motion command.
manual-home-axis home_joint / numeric Homing menu HOME {joint}; ON+IDLE and no concurrent motion Label Home Joint; numeric target only. Native state and motor offset determine the result.
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
manual-spindle-ccw, manual-spindle-stop, manual-spindle-cw spindle radio group -> spindle SPINDLE_COMMAND; manual_ok, MANUAL Canonical signed RPM/direction determines pressed state
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 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
override-feed set_feedrate Debounced SET_OVERRIDE; no native update_state power gate; selected INI range 0-200% Canonical feed override, including while AUTO where command layer permits
override-spindle set_spindlerate SET_SPINDLE_OVERRIDE; no native update_state power gate Canonical spindle override
override-max-velocity set_maxvel SET_MAX_VELOCITY; no native update_state power gate Canonical TP velocity limit/scale
override-rapid set_rapidrate Permanently disabled for selected ABI blocked_by_abi_rapidrate; never aliases Max Velocity
preview-layer-path, preview-layer-live, preview-layer-limits, preview-layer-velocity, preview-layer-offsets, preview-layer-extents AXIS View visibility variables Local visibility only; path maps show_program Shows/hides existing snapshot data without deleting or mutating it
program-editor WEB_EXTENSION, not AXIS external Edit Edits pending source text locally Does not change loaded interpreter until the Reload adapter is accepted

Tool/probe IO operations (TOOL_PREPARE, TOOL_PREPARED, TOOL_CHANGE, TOOL_CHANGED, PROBE_CONFIG) remain Worker capabilities for interpreter, MDI, HAL and focused IO tests. They are deliberately absent from the Web main panel because no corresponding AXIS main-panel buttons exist.

Button Dependency Matrix

The following matrix describes the side effects that determine the next set of enabled controls. The right-hand column is an observable Worker state change; React must derive its button gates from that state on the next snapshot.

Action Must be true before the click State change after acceptance Controls affected next
ESTOP(active=true) Always accepted by the task state machine IO estop=1, ready=0; interpreter and TP aborted; task ESTOP; loaded source retained Disable power-on, home, jog, MDI, spindle, tool, probe and all program controls except E-stop reset/recovery-safe actions
ESTOP(active=false) Source toggle requires current status ESTOP IO E-stop clears; observed source task state becomes ESTOP_RESET; Web may display STOPPED only as an adapter label Power is enabled; motion/manual/MDI remain disabled until ON
POWER(active=true) Source ON edge is valid only from ESTOP_RESET; it does not require interpreter IDLE Trajectory enables and observed task state becomes ON Manual group requires ON+IDLE; program Run still meets UI/taskfile adapter and task-layer homing rules
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/joint) ON+IDLE, no concurrent homing or jog; ensure MANUAL FREE mode, original homing sequence and joint state machines; homing, homed, home_state, motor offset and position from WASM Home is not AUTO execution. INTERP_IDLE does not mean homing is inactive.
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 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; motion mode selects TELEOP when homed, FREE otherwise 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
PROGRAM_RESUME paused true, mode AUTO or MDI, and for combined toolbar resume_inhibit false Trajectory resumes and interpreter restores interpResumeState Combined toolbar returns to Pause; manual controls remain disabled until interpreter IDLE
PROGRAM_ABORT AXIS exposes Stop while machine ON and interpreter non-IDLE; the task abort command itself is immediate Interpreter aborts, TP queue is cleared, current taskfile/path remains selected, and the open task plan is closed Stop periodic run polling; direct Run is enabled again when the task is idle and reopens the same taskfile from line 0. Reload is not a prerequisite.
JOG_START manual_ok, Manual tab visible and a valid selected joint/axis. Homing selects coordinate TELEOP versus joint FREE behavior; it is not an unconditional GUI prerequisite. One incremental command or one continuous command is sent Continuous stays active until release/cancel sends JOG_STOP; incremental has no stop edge
JOG_STOP An active jog gesture exists Active jog is aborted/stopped; TP remains runnable Allows a subsequent jog, MDI or source-gated manual command
TOUCH_OFF Source widget/handler formula plus valid value; task MDI homing enforcement applies unless configured off MDI G10 L20 updates the selected coordinate system, reloads preview, then restores saved task/motion mode Position/DRO/offsets refresh; program execution state remains idle
SET_MAX_VELOCITY No native machine-state UI gate; numeric value follows source unit conversion/range Source TP maximum velocity changes Motion duration/velocity changes; jog/feed/rapid controls remain independent
Rapid Override control Not available in the selected WASM TP No state change Rapid Override remains disabled; Max Velocity must not be used as a substitute
Spindle/Coolant Native manual widgets require ON+IDLE; spindle direction/brake handlers additionally use manual_ok and ensure MANUAL where source does; coolant checkbox handlers do not switch mode Canonical device IO changes only after Worker verification Direction/checked states update from canonical status; spindle speed +/- also requires nonzero source direction
Feed/Spindle override No native machine-state UI gate; values follow INI ranges Send c.feedrate/c.spindleoverride equivalents Canonical override changes without inventing an ON-only restriction

Dependency Flow

ESTOP ──release──> ESTOP_RESET ──POWER ON──> ON
                                         │
                                         ├──HOME ALL──> homed ON
                                         │                 │
                                         │                 ├──RUN──> AUTO/RUNNING
                                         │                 │             ├──PAUSE──> PAUSED
                                         │                 │             │              └──RESUME──> RUNNING
                                         │                 │             └──STOP/ESTOP/POWER OFF──> idle loaded taskfile
                                         │                 └──MANUAL/MDI──> jog/home/offset/device gates
                                         └──PROGRAM LOAD──> loaded taskfile (idle only)

Any E-stop or power-off edge invalidates active motion immediately. It does not delete the selected taskfile, static preview or Live Plot, and only joints configured with volatile home are unhomed by the native source. The UI must not leave stale enabled controls while waiting for the next canonical snapshot; 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 仅改变可见性 不受影响
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 也没有暂停禁用公式。

合法、顺序正确的帧 -> 通信层接收 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 均保持原生保留语义,清图由用户显式触发。

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,单关节重复回零在确认后 ensure MANUAL;空白/非有限数字/非法 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 条,最后有一个空白输入位置。

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 使用同一目标选择规则:

一次 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

native priority: argv[0] -> AXIS_OPEN_FILE -> [DISPLAY] OPEN_FILE
browser applicable priority: selected INI [DISPLAY] OPEN_FILE
 -> keep preview/program controls in loading state
 -> LOAD_WORKSPACE
 -> exactly one PROGRAM_LOAD for the configured OPEN_FILE
 -> publish the configured program, source hash and preview atomically
 -> enable source-gated controls

The UI cache is read only for presentation preferences. A stale cached program from an earlier Web version is ignored and replaced on the next UI preference save. No default-preview-then-cached-preview transition is permitted.

File replacement

poll
 -> native Open/Reload gate: interpreter IDLE
 -> Web safety adapter also requires TP/MDI queue empty and no active parse
 -> save task mode
 -> ensure MANUAL
 -> optional source FILTER; on failure report and stop
 -> restore saved task mode before PLAN_OPEN-equivalent parsing
 -> set first line 0 and clear previous highlight
 -> task_plan_synch + wait_complete
 -> program_open / PROGRAM_LOAD
 -> replace editor text and AxisCanon/static preview owner
 -> construct preview init codes from canonical status and parse
 -> reapply selected tab mode after the UI refresh
 -> publish one canonical replacement snapshot

The replacement is rejected while the TP or an MDI move still has a queued or active segment by the Web safety adapter. Native open_file_guts builds preview initialization from polled machine position, units, tool, modal G/M codes and tool offsets. Therefore loading B after A moved the machine must not silently initialize relative preview at the origin. A failed filter does not open a new taskfile. A G-code preview parse error is reported for the newly selected file; native AXIS does not promise rollback to the old static preview.

E-stop recovery and startup

ESTOP(active=true)
 -> emcMotionAbort + spindle/task/IO abort + trajectory disable
 -> volatile-home joints may unhome; task-plan synch
 -> observed task_state ESTOP
ESTOP(active=false)
 -> send STATE_ESTOP_RESET; abort/synch; observed task_state ESTOP_RESET
POWER(active=true)
 -> valid from ESTOP_RESET; trajectory enable; observed task_state ON
HOME(all=true)
 -> manual_ok -> MANUAL -> joint/FREE -> c.home(-1) -> wait
PROGRAM_RUN
 -> if the previous plan is completed/aborted: reopen current taskfile at line 0
 -> AUTO mode, interpreter READING; browser scheduler drives WASM samples

Run after completion or Stop

RUNNING -> M2/end-of-file -> WAITING -> queue/IO drained -> IDLE/completed
                                                       -> press Run
                                                       -> reopen current taskfile
                                                       -> AUTO_RUN line 0

RUNNING -> Stop/abort -> abort interpreter + TP -> close task plan -> IDLE/aborted
                                                               -> press Run
                                                               -> reopen current taskfile
                                                               -> AUTO_RUN line 0

This is the source behavior in emctaskmain.cc: closing the task plan avoids resuming at an aborted line, and EMC_TASK_PLAN_RUN_TYPE opens the selected file again when no plan is open. The Web runtime may expose PROGRAM_RESTART internally for recovery/testing, but it is not a required user step and is not a second visible Run control. After the reopen/synchronization, a positioning block whose target already equals the current canonical pose is a zero-motion block: it advances the interpreter line but must not enqueue an invalid zero-length TP segment.

Native Homing and State Monitoring (2026-09-09)

This section supersedes the former axis-to-joint alias and coordinated-TP return-to-zero implementation. The reference is the pinned native source, including the selected INI, not the previous browser tests.

Requested status Correct native field / source Web snapshot
Task mode emcStatus->task.mode source_status.task.mode: MANUAL/MDI/AUTO
Machine state emcStatus->task.state source_status.task.state: STATE_ESTOP/STATE_ESTOP_RESET/STATE_ON
Interpreter state emcStatus->task.interpState source_status.task.interpState: INTERP_IDLE/READING/WAITING/PAUSED
Motion mode emcStatus->motion.traj.mode source_status.traj.mode: FREE/COORD/TELEOP
Motion type emcStatus->motion.traj.motion_type source_status.traj.motion_type: motion classification, not mode
Joint homing/homed get_homing(j) / get_homed(j), reflected in joint status source_status.joint[j].homing/homed
Joint home state homing.c::H[j].home_state, HAL joint.N.home-state source_status.joint[j].home_state/home_state_name

The native NML joint status does not directly expose a home_state member. WASM reads the original homemod state that drives the HAL pin. Task status remains the existing WASM task/interpreter adapter; this is not a claim that the entire NML task process has been ported. Motion mode is stored in WASM.

The visible monitor retains only Task mode, Task state, Interpreter, Motion mode, Motion type and Homing. Per-joint status, positions, motor offsets and bounded servo transition history remain in the Worker snapshot for logic and diagnostics, but their table and history list are not rendered on the panel. The detailed status strip is the single visible machine-state summary. The duplicate footer and header operation badge are removed. Connection status remains separately labelled. The case strip shows the current program basename with its full path in a tooltip, and renders the command result once. Existing machine/operation values remain on the main element as data attributes for browser diagnostics; no control command or gate changes with this layout. Home Joint, the numeric joint selector and Home All are rendered in the Manual tab's action area; the standalone Machine sequence heading is not rendered. The XYZAC position summary and preview HUD display only X/Y/Z/A/C; J0-J4 position readouts are omitted. Numeric homing target selection and canonical joint data remain available to the existing control logic.

HOME(all/joint)
 -> ON+IDLE and no active motion; ensure MANUAL
 -> native FREE, do_home_joint(-1 or numeric joint)
 -> ACK: accepted; does not mean homed
 -> servo cycle: original do_homing() then simple_tp_update()
 -> forward kinematics of resulting joint coordinates
 -> synchronize coordinated planner coordinate origin to native result
 -> publish homing/homed/home_state and motor position
 -> native all-homed edge: control.c switches to TELEOP; partial home remains FREE
 -> native homing inactive: synchronize interpreter

HOME_SEARCH_VEL=0 and default HOME_LATCH_VEL=0 select home-at-current- position (homing.c:805). HOME_SET_SWITCH_POSITION adjusts joint coordinates and motor offset without moving the motor. The final move targets INI HOME through the joint free_tp (homing.c:1248). In the selected XYZAC INI, HOME and HOME_OFFSET are zero; re-home after Jog can therefore reset displayed joint coordinates with unchanged physical motor position. This is native behavior. It must not be replaced by G0, G53 or a coordinated TP line to zero.

A nonzero HOME relative to HOME_OFFSET executes a real native free_tp final move. Search/index inputs use homemod inputs; the selected browser fixture has no physical switches. Unlock hardware is unsupported and configurations requiring it are rejected, rather than faking an unlocked device.

Home completion never follows coordinated queue depth. Abort, ESTOP and power off cancel homemod; aborted joints do not become homed. Power off additionally applies VOLATILE_HOME. During homing, conflicting Run/MDI/Jog/Load/mode/recovery requests reject, while status sampling and abort remain available. These extra Web UI exclusions are explicit adapters around the native button formulas.

visual.pose_epoch follows native home command sequence. It invalidates stale display playback without clearing Live Plot. Neither this sequence nor the displayed zero value is evidence of homing completion.

Validation boundary: the native homing state round-trip preserves joint/motor positions and offsets. The existing general recovery audit still reports endpoint_at_checkpoint_equal=false for planner/HAL fields; a roughly 7.7 MB full-history RECOVER envelope also exceeds the existing ccall-array stack boundary. These are not closed by this homing change and must not be included in claims of full recovery parity. Native homing checkpoint bytes are stored sparsely to avoid copying unused joint compensation tables into JSON.

Touch-off command encoding

AXIS's all_systems list is P0 Current, P1 G54 through P9 G59.3. touch_off_system passes the first token from the selected entry to G10 L20, so the source command is G10 L20 P1 X... for G54 (and P0 for the current system). The Worker accepts the same P-token or a G54..G59.3 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 等非源码系统标识必须拒绝。

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. The source sends one JOG_CONTINUOUS command and motion continues until a deferred JOG_STOP; it does not submit a fresh user command every GUI sample. The Web equivalent keeps one active {axis,direction} gesture, sends one JOG_START, advances its WASM servo simulation internally and sends JOG_STOP exactly once on release, cancel, leave, focus loss or E-stop.

Toolpath display lifecycle

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
Static program preview open_file_guts assigns a new AxisCanon; Gremlin renders that canon Remains visible during Run, Pause, Resume, Stop and normal completion Open/Reload replaces the previous canon with the newly selected program's canon and parses into it. A preview error is reported for the new file; native AXIS does not roll back to the old canon. Clear Live Plot does not affect it.
Machining Live Plot LivePlotter owns linuxcnc.positionlogger; show_live_plot controls visibility Accumulates independently of the selected static program. Pause freezes and retains it; Resume continues; Stop and normal completion retain it. Opening, reloading, running or rerunning a program also retains the existing trace. Hiding changes visibility only. Only clear_live_plot() calls LivePlotter.clear(), whose clear() calls positionlogger.clear(). No call exists in open_file_guts, reload_file, task_run, task_step, task_pause, task_resume or task_stop.

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. The epoch changes only for VISUAL_CLEAR (or restoration of a persisted epoch), never as an Open/Reload side effect.

PROGRAM_LOAD accepted -> live epoch unchanged -> replace static preview only
PROGRAM_RUN accepted  -> live epoch unchanged -> continue accumulating live trace
PROGRAM_PAUSE          -> epoch unchanged -> retain/freeze live trace
PROGRAM_RESUME         -> epoch unchanged -> continue live trace
PROGRAM_ABORT/STOP     -> epoch unchanged -> retain live trace
program completed      -> epoch unchanged -> retain live trace
VISUAL_CLEAR           -> increment live_path_epoch -> clear live trace only
PROGRAM_RESTART        -> epoch unchanged -> source has no visible Restart action

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.

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

  1. web/worker/sim-session.mjs is the command gate and transaction boundary.
  2. web/worker/wasm-linuxcnc-tp.mjs, wasm-program-runtime.mjs, wasm-native-homing.mjs, wasm-xyzac-io.mjs and wasm-xyzac-device-io.mjs are the only authorities for selected-case motion, interpreter and device state.
  3. web/react/src/App.tsx may render a button disabled from a gate snapshot, but a Worker gate is still mandatory because UI state can be stale.
  4. Every accepted operation produces a ledger entry and canonical snapshot; every rejected operation produces a stable source/gate reason.
  5. A feature that cannot be represented by the selected ABI is disabled with an explicit reason. It must not be approximated with G92, a fixed G43, local booleans or a second JavaScript state machine.
  6. New controls must be added to this matrix, the source symbol inventory and a focused Worker/browser regression test before deployment.
  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. 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.
  10. Exact axis.tcl::update_state formulas remain the reference UI gates. A browser asynchronous-load exclusion must be documented separately; no other stronger React gate may be attributed to AXIS.

Change Procedure

Focused evidence for this contract is kept in web/worker/test_xyzac_touch_off_source_flow.mjs, web/worker/test_native_homing.mjs, web/worker/test_xyzac_panel_buttons.mjs and scripts/test_xyzac_panel_buttons_browser.mjs. The completed/aborted direct rerun contract is covered by web/worker/test_xyzac_program_rerun.mjs. The direct visible Jog -> Home Joint/Home All sequence at scripts/test_xyzac_home_reset_browser.mjs records native homing transitions, joint coordinates and unchanged motor positions for no-search homing on the local :5175 panel. 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, KINEMATICS_BOTH world-axis 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 rendered actionable element without a documented ID or with a duplicate ID.

When LinuxCNC source behavior or the selected machine configuration changes:

  1. Update the source baseline and the affected matrix row/transaction above.
  2. Recompute the source-lock hashes and review every changed source symbol.
  3. Update the Worker gate/authority path.
  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 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 not claim that an unavailable LinuxCNC host or official native oracle has been silently reproduced in the browser.