diff --git a/docs/linuxcnc-axis-web-control-panel-source-logic.dot b/docs/linuxcnc-axis-web-control-panel-source-logic.dot new file mode 100644 index 0000000..d695cc9 --- /dev/null +++ b/docs/linuxcnc-axis-web-control-panel-source-logic.dot @@ -0,0 +1,255 @@ +digraph LinuxCncAxisWebControlLogic { + graph [ + charset="UTF-8", + rankdir=TB, + bgcolor="#f3f4f1", + pad="0.35", + nodesep="0.28", + ranksep="0.55", + splines=polyline, + compound=true, + newrank=true, + fontname="Noto Sans CJK SC", + fontsize=22, + label="LinuxCNC AXIS Web/WASM 控制面板完整逻辑关系图\n基线 239dcf4284762ad7e9c65d390bc5524d855c2f8e · 文档是 Web 面板唯一实现契约", + labelloc=t, + labeljust=l + ]; + + node [ + shape=box, + style="rounded,filled", + color="#667078", + fillcolor="#ffffff", + fontcolor="#172027", + fontname="Noto Sans CJK SC", + fontsize=13, + margin="0.16,0.10", + penwidth=1.4 + ]; + edge [ + color="#59656d", + fontcolor="#303b42", + fontname="Noto Sans CJK SC", + fontsize=11, + arrowsize=0.72, + penwidth=1.4 + ]; + + subgraph cluster_authority_chain { + label="A. 权威链:按钮状态只能来自 Worker/WASM 的规范快照"; + color="#aeb5b9"; + fillcolor="#e9ecea"; + style="rounded,filled"; + penwidth=1.5; + + source [label="LinuxCNC 源程序\naxis.py · axis.tcl\nemctaskmain.cc · HAL/INI", fillcolor="#f7f1df", color="#b08a2e"]; + contract [label="源程序对标文档\nlinuxcnc-axis-web-control-panel-\nsource-logic.md", fillcolor="#fff5c9", color="#b08a2e", penwidth=2.2]; + gesture [label="浏览器控件手势\ndata-axis-control\n82 个文档控制 ID", fillcolor="#edf6f8", color="#3c8091"]; + react [label="React handler\n只发意图,不是状态权威", fillcolor="#edf6f8", color="#3c8091"]; + operation [label="AxisOperationController\n串行操作 · operation_id · ledger", fillcolor="#eef2fb", color="#526da8"]; + gate [label="SimSession command gate\n最终门控与稳定拒绝原因", fillcolor="#fcebdc", color="#bd6a28", penwidth=2.2]; + wasm [label="LinuxCNC/WASM 权威\nTask · Interpreter · TP\nIO · Home/Limit · Kinematics", fillcolor="#e9f5e8", color="#43844b", penwidth=2.2]; + snapshot [label="Canonical snapshot\naxis_state · gates · motion\nprogram · devices · visual", fillcolor="#e9f5e8", color="#43844b"]; + projection [label="React projection\n按钮 enabled/pressed\nDRO · Preview · Diagnostics", fillcolor="#edf6f8", color="#3c8091"]; + + { rank=same; source; contract; gesture; react; operation; } + { rank=same; gate; wasm; snapshot; projection; } + source -> contract [label="校准"]; + contract -> gesture [label="允许实现"]; + gesture -> react; + react -> operation; + operation -> gate; + gate -> wasm [label="accepted"]; + wasm -> snapshot; + snapshot -> projection; + projection -> gesture [label="下一帧门控", constraint=false, color="#3c8091"]; + gate -> projection [label="blocked: reason", constraint=false, color="#b94f45", style=dashed]; + } + + subgraph cluster_gate_inputs { + label="B. Worker 门控输入"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#ffffff"; + + task_input [label="Task\nstate · mode · interp_state\nprogram_status · current_line", fillcolor="#eef2fb", color="#526da8"]; + io_input [label="IO\nready · estop · fault\nspindle · coolant", fillcolor="#e9f5e8", color="#43844b"]; + motion_input [label="Motion / TP\nhomed mask · queue depth\nactive id · velocity · limits", fillcolor="#e9f5e8", color="#43844b"]; + file_input [label="Taskfile / Program\npath · source_hash · loaded\npreview · subroutines", fillcolor="#f7f1df", color="#b08a2e"]; + abi_input [label="能力边界\nselected WASM ABI\nhost boundary", fillcolor="#f8e7e5", color="#b94f45"]; + + { rank=same; task_input; io_input; motion_input; file_input; abi_input; } + task_input -> gate [label="状态", constraint=false]; + io_input -> gate [constraint=false]; + motion_input -> gate [constraint=false]; + file_input -> gate [constraint=false]; + abi_input -> gate [label="可用/禁用", constraint=false]; + } + + subgraph cluster_state_machine { + label="C. 机器与程序状态机(主操作顺序)"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#ffffff"; + + estop [label="ESTOP\n仅允许解除急停/安全诊断", fillcolor="#f8e7e5", color="#b94f45", penwidth=2.1]; + off [label="ESTOP_RESET / OFF\n急停已解除,机器未上电", fillcolor="#f7f1df", color="#b08a2e"]; + on_unhomed [label="ON · NOT HOMED\n可加载文件,可 HOME\n禁止 AUTO 运行", fillcolor="#fff5c9", color="#b08a2e"]; + idle [label="ON · HOMED · IDLE\nRun / Step / Manual / MDI\n按各自源门控启用", fillcolor="#e9f5e8", color="#43844b", penwidth=2.1]; + running [label="AUTO · RUNNING\n解释器读取 + TP 执行\nPause / Stop / Power Off / ESTOP", fillcolor="#e5effa", color="#526da8", penwidth=2.1]; + paused [label="AUTO · PAUSED\n轨迹冻结但保留\nResume / Stop / Power Off / ESTOP", fillcolor="#fcebdc", color="#bd6a28", penwidth=2.1]; + ended [label="IDLE · completed / aborted\n保留当前 taskfile\n直接 Run 从第 0 行重新打开", fillcolor="#e9f5e8", color="#43844b"]; + + { rank=same; estop; off; on_unhomed; idle; } + { rank=same; running; paused; ended; } + + estop -> off [label="ESTOP active=false"]; + off -> on_unhomed [label="POWER active=true"]; + on_unhomed -> idle [label="HOME all=true"]; + idle -> running [label="PROGRAM_RUN"]; + running -> paused [label="PROGRAM_PAUSE"]; + paused -> running [label="PROGRAM_RESUME"]; + running -> ended [label="M2 / EOF 或 PROGRAM_ABORT"]; + paused -> ended [label="PROGRAM_ABORT"]; + ended -> running [label="PROGRAM_RUN\nreopen line 0"]; + running -> off [label="POWER active=false", color="#b94f45", constraint=false]; + paused -> off [label="POWER active=false", color="#b94f45", constraint=false]; + on_unhomed -> estop [label="ESTOP active=true", color="#b94f45", constraint=false]; + idle -> estop [label="ESTOP active=true", color="#b94f45", constraint=false]; + running -> estop [label="ESTOP active=true", color="#b94f45", constraint=false]; + paused -> estop [label="ESTOP active=true", color="#b94f45", constraint=false]; + } + + subgraph cluster_controls { + label="D. 控制面板全量分组(括号内为 operation / 本地投影 / 明确禁用)"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#e9ecea"; + + machine_controls [ + label="机器 / 程序工具栏\n\ntoolbar-estop (ESTOP)\ntoolbar-power (POWER)\ntoolbar-open + program-file-input (PROGRAM_LOAD)\ntoolbar-load-case (LOAD_WORKSPACE + PROGRAM_LOAD)\ntoolbar-reload (PROGRAM_LOAD current source)\ntoolbar-run (PROGRAM_RUN)\ntoolbar-step (PROGRAM_STEP)\ntoolbar-pause-resume (PAUSE / RESUME)\ntoolbar-stop (PROGRAM_ABORT)\ntoolbar-block-delete (blocked_by_abi)\ntoolbar-optional-pause (blocked_by_abi)\nmanual-home-all (HOME all)", + fontname="Noto Sans Mono CJK SC", + fillcolor="#eef2fb", + color="#526da8" + ]; + + manual_controls [ + label="手动控制\n\nmanual-axis-x / y / z / a / c (本地选择)\nmanual-jog-increment (本地选择)\nmanual-jog-negative / positive (JOG_START -> release JOG_STOP)\nmanual-home-axis (HOME axis)\nmanual-touch-off (TOUCH_OFF / G10 L20)\nmanual-tool-touch-off (blocked_by_abi_tool_offsets)\nmanual-override-limits (blocked_by_abi)\nmanual-spindle-ccw / stop / cw (SPINDLE_COMMAND)\nmanual-spindle-decrease / increase\n (SPINDLE_COMMAND -> release SPINDLE_CONSTANT)\nmanual-spindle-brake (SPINDLE_BRAKE)\nmanual-coolant-mist / flood (COOLANT_COMMAND)", + fontname="Noto Sans Mono CJK SC", + fillcolor="#e9f5e8", + color="#43844b" + ]; + + program_view_controls [ + label="MDI / 视图 / DRO / 编辑\n\nmdi-command / mdi-submit (MDI_EXECUTE)\nmdi-history-select (本地选择,不自动执行)\ntab-manual / tab-mdi (SET_MODE)\ntab-diagnostics / tab-preview / tab-dro (本地投影)\ntoolbar-zoom-in / zoom-out (本地相机)\ntoolbar-view-z / z2 / x / y / y2 / p (本地视图)\ntoolbar-rotate (本地交互模式)\ntoolbar-clear-live-plot (VISUAL_CLEAR)\npreview-panLeft / panRight / panUp / panDown / reset\ndro-coordinate / dro-display / dro-units\nprogram-editor (待提交文本;toolbar-reload 才生效)", + fontname="Noto Sans Mono CJK SC", + fillcolor="#edf6f8", + color="#3c8091" + ]; + + secondary_controls [ + label="覆盖率 / 图层 / 菜单 / 边界\n\noverride-jog-speed (SET_JOG_SCALE)\noverride-feed (SET_OVERRIDE)\noverride-spindle (SET_SPINDLE_OVERRIDE)\noverride-max-velocity (SET_MAX_VELOCITY)\noverride-rapid (blocked_by_abi_rapidrate)\npreview-layer-path / live / limits / velocity / offsets / extents\nworkspace-case-select (本地配置选择)\nworkspace-load-3d-chips (PROGRAM_LOAD)\nmenu-file / menu-help (本地菜单)\nmenu-help-about / about-close (本地对话框)\nmenu-file-edit-tool-data (host_boundary disabled)\nmenu-file-quit (host_boundary disabled)\nTool / Probe:只读;无额外主面板命令按钮", + fontname="Noto Sans Mono CJK SC", + fillcolor="#f7f1df", + color="#b08a2e" + ]; + + d_anchor [shape=point, width=0, height=0, label="", style=invis]; + { rank=same; machine_controls; d_anchor; manual_controls; } + { rank=same; program_view_controls; secondary_controls; } + machine_controls -> d_anchor -> manual_controls [style=invis, weight=20]; + program_view_controls -> secondary_controls [style=invis, weight=20]; + d_anchor -> program_view_controls [style=invis, weight=100, minlen=1]; + machine_controls -> operation [label="Worker 命令", color="#526da8", constraint=false]; + program_view_controls -> projection [label="本地投影", style=dashed, color="#3c8091", constraint=false]; + } + + subgraph cluster_transactions { + label="E. 四个关键事务"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#ffffff"; + + startup_tx [ + label="启动事务\n1 读取 selected INI\n2 解析 [DISPLAY] OPEN_FILE\n3 控件保持 loading/disabled\n4 LOAD_WORKSPACE\n5 仅一次 PROGRAM_LOAD\n6 原子发布 path/hash/preview\n7 OPFS 只恢复视图偏好,不恢复 G-code", + fillcolor="#f7f1df", + color="#b08a2e" + ]; + load_tx [ + label="文件替换事务\n1 仅 IDLE 且 TP/MDI queue=0\n2 save mode -> ensure MANUAL\n3 清旧 preview 与待处理边\n4 恢复 saved mode\n5 捕获当前 canonical TP pose\n6 PROGRAM_LOAD + task_plan_synch\n7 subroutine + preview\n8 重算 gates 并发布单一快照", + fillcolor="#eef2fb", + color="#526da8" + ]; + manual_tx [ + label="手动 / MDI 事务\nmanual_ok = ON 且非运行\nHOME: ensure MANUAL -> home\nJOG: press start -> release stop\nTOUCH_OFF: save mode -> MDI -> G10 L20\n -> wait/sync/reload -> restore mode\nMDI: queue 未满 -> ensure MDI -> c.mdi", + fillcolor="#e9f5e8", + color="#43844b" + ]; + run_tx [ + label="程序执行事务\nRun: run_warn -> ensure AUTO -> AUTO_RUN line 0\nPause: 仅 READING/WAITING\nResume: 仅 PAUSED\nStop: abort -> wait -> TP queue clear\nCompleted/Aborted: taskfile 保留\n再次 Run: reopen 当前文件 line 0", + fillcolor="#fcebdc", + color="#bd6a28" + ]; + + e_anchor [shape=point, width=0, height=0, label="", style=invis]; + { rank=same; startup_tx; e_anchor; load_tx; } + { rank=same; manual_tx; run_tx; } + startup_tx -> e_anchor -> load_tx [style=invis, weight=20]; + e_anchor -> manual_tx [style=invis, weight=100, minlen=1]; + startup_tx -> load_tx [label="configured OPEN_FILE"]; + } + + subgraph cluster_live_path { + label="F. 刀具路径显示生命周期:静态 Preview 与加工 Live Plot 严格分离"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#ffffff"; + + static_preview [label="静态程序 Preview\n来源:open_file_guts / AxisCanon / WASM preview\nRun · Pause · Resume · Stop · Completed · Clear Plot 均保留\n仅新 PROGRAM_LOAD 隐藏旧图并原子替换", fillcolor="#eef2fb", color="#526da8"]; + live_empty [label="Live Plot 新 epoch\n轨迹点 = 0", fillcolor="#edf6f8", color="#3c8091"]; + live_running [label="RUNNING\npositionlogger / motion_trace 累积", fillcolor="#e9f5e8", color="#43844b"]; + live_paused [label="PAUSED\n冻结且保留同一轨迹", fillcolor="#fcebdc", color="#bd6a28"]; + live_retained [label="STOPPED / COMPLETED\n保留完整加工轨迹", fillcolor="#e9f5e8", color="#43844b"]; + live_clear [label="清除边界\nPROGRAM_LOAD · 新 PROGRAM_RUN\nPROGRAM_RESTART · VISUAL_CLEAR\nWorker: live_path_epoch + 1", fillcolor="#f8e7e5", color="#b94f45", penwidth=2.1]; + + f_anchor [shape=point, width=0, height=0, label="", style=invis]; + { rank=same; static_preview; f_anchor; live_clear; } + { rank=same; live_empty; live_running; live_paused; live_retained; } + static_preview -> f_anchor -> live_clear [style=invis, weight=20]; + live_empty -> live_running [label="motion samples"]; + live_running -> live_paused [label="Pause"]; + live_paused -> live_running [label="Resume"]; + live_running -> live_retained [label="Stop / Complete"]; + live_clear -> live_empty [label="epoch change"]; + live_retained -> live_clear [label="重新加工 / 换程序 / Clear", color="#b94f45"]; + static_preview -> live_running [label="相互独立", style=dashed, dir=both, arrowtail=none, color="#526da8"]; + } + + subgraph cluster_invariants { + label="G. 不变量与验收"; + color="#aeb5b9"; + style="rounded,filled"; + fillcolor="#e9ecea"; + + invariants [ + label="实现不变量\n• React 不是状态权威\n• 每条命令必须经过 Worker gate\n• 每个控件必须有文档 ID\n• 拒绝必须返回稳定 reason\n• 不可用 ABI 不得用本地布尔/G92/固定 G43 模拟\n• 重复入口删除,不改变保留命令语义\n• 新控件必须先更新本文档与测试", + fillcolor="#ffffff", + color="#667078" + ]; + verification [ + label="自动验收\n82 个文档控制 ID\n80 个静态 DOM 控件 + 2 个动态控制族\n51 个实际按钮 / 18 组浏览器流程\nWorker: 98 verified / 8 rejected / 6 ABI boundaries\nNative: 22 / 22\n多程序 A -> B -> C -> A\n桌面/移动 WebGL 像素检查", + fillcolor="#e9f5e8", + color="#43844b" + ]; + legend [ + label="颜色图例\n绿色:WASM 规范状态/已验证操作\n蓝色:Task/程序控制\n青色:本地视图投影\n黄色:INI/文件/启动事务\n橙色:模式转换/暂停\n红色:急停/清除边界/明确禁用", + fillcolor="#ffffff", + color="#667078" + ]; + + { rank=same; invariants; verification; legend; } + } + + gesture -> motion_input -> on_unhomed -> d_anchor -> e_anchor -> f_anchor -> verification [style=invis, weight=100, minlen=2]; +} diff --git a/docs/linuxcnc-axis-web-control-panel-source-logic.md b/docs/linuxcnc-axis-web-control-panel-source-logic.md index 13241b8..ea1d94c 100644 --- a/docs/linuxcnc-axis-web-control-panel-source-logic.md +++ b/docs/linuxcnc-axis-web-control-panel-source-logic.md @@ -5,6 +5,10 @@ Scope: the selected `XYZAC TRT` Web/WASM panel and its LinuxCNC-compatible Worker command path Source baseline: LinuxCNC submodule `239dcf4284762ad7e9c65d390bc5524d855c2f8e` +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) + This document is the control-flow authority for the Web panel. A button is implemented only when its complete path is defined here: diff --git a/docs/linuxcnc-axis-web-control-panel-source-logic.png b/docs/linuxcnc-axis-web-control-panel-source-logic.png new file mode 100644 index 0000000..cf020e1 Binary files /dev/null and b/docs/linuxcnc-axis-web-control-panel-source-logic.png differ diff --git a/scripts/render_axis_control_logic_png.sh b/scripts/render_axis_control_logic_png.sh new file mode 100755 index 0000000..656bebc --- /dev/null +++ b/scripts/render_axis_control_logic_png.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +source_file="${repo_root}/docs/linuxcnc-axis-web-control-panel-source-logic.dot" +output_file="${repo_root}/docs/linuxcnc-axis-web-control-panel-source-logic.png" + +dot -Tpng -Gdpi=155 "${source_file}" -o "${output_file}" +identify "${output_file}"