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 · 源程序冲突时源程序优先", 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="源程序锁定契约\nSOURCE_EXACT · SOURCE_ADAPTER\nSOURCE_BLOCKED · WEB_EXTENSION", 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\noperation_id · ledger\nWeb 通道暂停仍接收合法连续 seq", 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 · ui_gates / 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\n急停已解除,机器未上电\nSTATE_OFF 是命令值,不是稳定状态", fillcolor="#f7f1df", color="#b08a2e"]; on_unhomed [label="ON · NOT HOMED\nOpen / Home / joint-mode manual\nAUTO/MDI 由 task homing 策略拒绝", 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 / MDI · PAUSED\n轨迹与恢复状态保留;通信连接持续\nClear / Override / Step 后仍可 Resume\n命令拒绝不产生 seq 断档", fillcolor="#fcebdc", color="#bd6a28", penwidth=2.1]; ended [label="INTERP_IDLE · completed / aborted\nWeb 状态标签不是 LinuxCNC 枚举\n保留 taskfile;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 (BOTH: world-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 (单击选择;双击 MDI_EXECUTE)\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 (SOURCE_ADAPTER)\noverride-feed / spindle / max-velocity\n (SOURCE_ADAPTER;原生无上电 UI gate)\noverride-rapid (SOURCE_BLOCKED)\npreview-layer-path / live / limits / velocity / offsets / extents\nworkspace-case-select / load-3d-chips (WEB_EXTENSION)\nmenu-file / menu-help (本地菜单)\nmenu-help-about / about-close (SOURCE_ADAPTER)\nmenu-file-edit-tool-data / quit (SOURCE_BLOCKED)\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 原生 UI gate:INTERP_IDLE\n2 Web 安全适配:TP/MDI queue=0\n3 save mode -> ensure MANUAL\n4 可选 FILTER -> restore saved mode\n5 first line 0 -> task_plan_synch\n6 program_open / PROGRAM_LOAD\n7 新 AxisCanon + 状态派生 preview init\n8 set_mode_from_tab;与原生一致:Live Plot 保留", fillcolor="#eef2fb", color="#526da8" ]; manual_tx [ label="手动 / MDI 事务\nmanual_ok = ON 且非运行\nHOME: ensure MANUAL -> home\nJOG: 一次 start -> WASM 限位目标 -> SIM_STEP\n -> release / blur / hidden 一次 stop\nTOUCH_OFF: save mode -> MDI -> G10 L20\n -> 保留位置、偏置 -> preview / relative -> restore mode\nMDI: queue 未满 -> ensure MDI -> c.mdi", fillcolor="#e9f5e8", color="#43844b" ]; run_tx [ label="程序执行事务\nRun UI gate:ON + INTERP_IDLE\nrun_warn (原生;Web 警告待补齐) -> ensure AUTO -> AUTO_RUN line 0\nStep UI gate:ON + taskfile\nStep: 首次暂停 / 读下一行 / TP 执行 ID 边界\nPause:READING/WAITING;Resume:PAUSED\nStop:ON + 非 IDLE -> abort/wait/plan close\nCompleted/Aborted:taskfile 保留;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 均保留\nOpen/Reload 分配新 canon;解析错误不回滚旧 canon", fillcolor="#eef2fb", color="#526da8"]; live_empty [label="显式清除后的 Live Plot\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="唯一源程序清除边界\nCtrl-K / Clear Live Plot\nVISUAL_CLEAR -> live_path_epoch + 1\n原生及 Web:Open / Reload / Run 均保留", 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_running [label="Run / 重跑 / 换程序后继续累积", color="#43844b"]; live_retained -> live_clear [label="仅 Clear Live Plot / Ctrl-K", 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• 源程序冲突时源程序优先\n• React 不是状态权威\n• exact update_state 公式是 UI gate 基线\n• 每条命令必须经过 Worker gate\n• 不可用 ABI 不得用本地布尔/G92/固定 G43 模拟\n• WEB_EXTENSION 不计入 AXIS 对标\n• 新控件必须先更新本文档、图和测试", fillcolor="#ffffff", color="#667078" ]; verification [ label="自动验收\n源码锁:axis.py + axis.tcl SHA-256\n7 条 exact update_state 公式\n5 条 handler 流程\n7 个非清除 Live Plot 生命周期函数\n82 个文档控制 ID\nSOURCE_EXACT / ADAPTER / BLOCKED / EXTENSION 分层\nWorker 与浏览器回归另行验证", fillcolor="#e9f5e8", color="#43844b" ]; legend [ label="对标图例\nSOURCE_EXACT:源代码公式/顺序原样\nSOURCE_ADAPTER:传输不同,语义相同\nSOURCE_BLOCKED:能力不足,禁用/缺席\nWEB_EXTENSION:非 AXIS 功能,不计入对标\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]; }