36 KiB
LinuxCNC Web/WASM 仿真前端与通信详细设计
文档状态:Draft v0.1
适用基线:LinuxCNC 2.10.0~pre0;浏览器运行时、WASM 核心、Pyodide、OPFS 和 Web UI 采用同一 context_id
上层路线:技术路线
源码关联:功能与源程序关联目录
覆盖验收:仿真全功能覆盖矩阵
执行治理:项目执行与交付策略
总控执行:移植总控执行手册
本文件把“浏览器前端”和“LinuxCNC 仿真核心之间的边界”定义为可实现、可测试、可版本化的契约。前端不直接调用 WASM 内部指针、不直接写 OPFS、不直接改变仿真状态;所有命令、状态、事件、日志、快照和恢复操作都通过 Worker 通信层完成。LinuxCNC 的解释器、TP、motion、HAL、IO、Python 和数字孪生是状态权威;前端是命令发起者、状态投影者和用户工作流编排者。
1. 设计目标与不可变原则
1.1 目标
- 覆盖 LinuxCNC 仿真用户的完整工作流:工程选择、配置加载、G-code 编辑/预览/运行、MDI、jog、home、override、主轴、换刀、冷却、探针、报警、HAL、ClassicLadder、工具表、参数、日志、快照、恢复、导入导出和离线运行。
- 前端在 Chromium、Firefox、Safari 支持范围内保持一致的命令语义和状态显示;不要求桌面 GUI 像素级复制,但必须保持工作流、状态、错误和结果等价。
- 主线程只处理输入、布局、可视化和状态投影;WASM、Python、HAL 周期、文件系统、journal、快照和恢复全部在 Worker 侧执行。
- 通信协议可录制、回放、限流、取消、重试和恢复;断网、标签页挂起、Worker 重启、回答中断或工具结果丢失不得产生重复副作用或跳过步骤。
- UI 功能条目与 LinuxCNC 源文件、配置入口、测试和 oracle 互相可追溯,纳入覆盖矩阵和发布门禁。
1.2 不可变原则
| 原则 | 约束 |
|---|---|
| Worker 权威 | 前端显示的运行状态必须来自 Worker 快照或事件;前端本地乐观状态只能标记为 pending,不能当作仿真已执行 |
| 单写者 | 一个仿真会话只允许一个 Worker 实例写入 WASM 状态和 OPFS journal;多标签页只能观察或显式取得租约 |
| 固定顺序 | 命令按 seq 排序,WASM step、HAL thread、事件和 OPFS commit 在会话内串行 |
| 版本化边界 | 命令、事件、快照、能力、工程格式和错误码均带 schema_version;未知版本拒绝而不猜测 |
| 可重放 | 任何影响仿真结果的命令都可记录输入、tick、结果和状态哈希;调试 UI 不得改变执行顺序 |
| 本地优先 | 网络不可用不影响已缓存资源和 OPFS 工程的本地仿真;远程同步是可选功能 |
| 安全排除 | 不把宿主路径、shell、真实设备、原始 socket、动态库或任意 Python 宿主能力暴露给前端插件 |
2. 技术路线与实现方法
2.1 前端技术栈
React + TypeScript
├─ AppShell / Router / PermissionBoundary
├─ Domain stores(session、program、motion、hal、io、workspace、recovery)
├─ WorkerClient(postMessage、序列、超时、重试和协议校验)
├─ Command UX(命令表单、pending、ack、result、错误和取消)
├─ Editors(G-code、INI、HAL、工具表、ClassicLadder、VCP schema)
├─ Machine UI(状态、MDI、jog、home、override、报警、工具和 I/O)
├─ Visualization(Three.js/WebGL/WebGPU 适配、轨迹、坐标、毛坯和模型)
└─ Diagnostics(HAL graph/scope、事件、日志、差异和能力报告)
│ structured clone / transferable ArrayBuffer
▼
Dedicated sim.worker.ts
├─ ProtocolGateway / TaskLedger
├─ SessionManager / CapabilityRegistry
├─ SimScheduler / LinuxCncCompat
├─ WasmRuntime / PythonRuntime
├─ WorkspaceRuntime / OpfsJournal
├─ SnapshotManager / RecoveryManager
└─ StatePublisher / BackpressureController
推荐采用 TypeScript 严格模式、运行时 schema 校验(例如手工生成的校验器或项目选定的 schema 工具)、结构化日志和可测试的纯 reducer。React 组件不能直接依赖 postMessage;所有消息经过 WorkerClient,所有 UI 状态经过 domain store。Three.js 只消费轨迹/姿态快照,不读取 WASM 内存。
2.2 分层责任
| 层 | 责任 | 允许访问 | 禁止访问 |
|---|---|---|---|
| View/UI | 用户输入、布局、无障碍、键盘/触摸、状态投影 | domain store、WorkerClient 的只读结果 | WASM、OPFS、网络写入、直接修改 authoritative state |
| Domain store | 规范化状态、reducer、派生选择器、pending/error 状态 | 已校验 Worker 消息 | 猜测未收到的状态、直接持久化 |
| WorkerClient | 握手、序列、超时、取消、重试、消息校验、传输统计 | postMessage、transferable buffer |
修改仿真语义、跳过 ack、吞掉错误 |
| ProtocolGateway | schema、权限、幂等键、命令路由和能力检查 | Worker 内模块 | 未版本化命令、跨会话写入 |
| SimRuntime | WASM、Python、HAL、IO、固定 tick、状态快照 | Worker 内 WASM/虚拟 FS | DOM、真实硬件、宿主命令 |
| WorkspaceRuntime | OPFS、journal、checkpoint、导入导出、配额 | Worker OPFS API | UI 线程同步句柄、未提交覆盖 |
| StatePublisher | 快照、事件、轨迹、采样、背压、丢弃策略 | Worker 状态 | 改变核心状态或重排事件 |
2.3 状态管理方法
Worker 发布三种数据:
- 权威快照:完整或增量的当前状态,带
simulation_tick和状态哈希;UI 刷新、重连和恢复以此为基准。 - 领域事件:报警、错误、模式变化、换刀、主轴到速、探针触发、HAL 变化、恢复进度和命令结果;事件不可替代缺失的权威快照。
- 高频流:轨迹点、HAL sample、位置/速度/电流等,可按订阅和背压丢弃中间样本;流末端必须有序号和时间范围,不能丢失最终状态。
前端 store 保存 authoritative、pending、last_error、last_verified_tick 和 stream_cursor。任何本地编辑先进入 draft;保存成功后才更新 committed 版本。仿真状态、工程文件状态和 UI 偏好分开存储。
3. 前端功能设计
3.1 应用壳和导航
| 功能 | 前端行为 | 通信依赖 |
|---|---|---|
| 启动/能力检查 | 显示浏览器、WASM、Pyodide、OPFS、WebGL/WebGPU、跨源隔离和缓存状态 | HELLO、CAPABILITIES |
| 会话选择 | 打开、创建、复制、重命名、导入、导出工程;显示租约和最近快照 | WORKSPACE_LIST、SESSION_CREATE、PROJECT_* |
| 运行态导航 | 控制台、程序、预览、HAL、PLC、工具、配置、日志和差异视图 | STATE_SNAPSHOT、领域订阅 |
| 多视图同步 | 一个主控视图,其他视图只读订阅;切换不重启仿真 | SUBSCRIBE、SNAPSHOT_REQUEST |
| 权限边界 | 插件和 Python VCP 只能访问 manifest 授权的 pins、命令和文件 | CAPABILITIES、PLUGIN_* |
3.2 程序、MDI 和编辑器
- G-code 编辑器:语法高亮、行号、错误定位、程序资源树、搜索、跳转行、保存草稿和版本比较。
- 程序预览:解析状态、轨迹、刀具、坐标系、毛坯、当前行和首个差异 tick;预览必须明确“仅解析”或“已运行”。
- 程序控制:加载、运行、暂停、单步、可选停止、恢复、从行运行、重启、清空队列和中止。
- MDI:输入、历史、状态、返回结果、队列破坏和错误行;提交后显示
pending → ack → result。 - 宏/remap:显示调用栈、参数、返回值、Python 异常、资源限制和
real_hardware_excluded错误。
3.3 手动操作和机床控制
- 模式:manual、mdi、auto、teleop、joint、world、emergency stop、off、on、homing。
- Jog:轴/关节选择、连续/增量、速度、方向、按下/释放、键盘/触控/手轮事件去抖和释放保护。
- Home/limit:单轴和全部回零、序列、共享开关、锁定转台、软限位/硬限位和跟随误差报警。
- Override:feed、spindle、rapid、adaptive、feed hold、feed inhibit、spindle brake 和急停;每个变更显示来源、范围和确认结果。
- 主轴/IO:方向、转速、定向、齿轮、冷却、润滑、换刀、探针、等离子 torch/arc-ok、激光功率和故障注入。
3.4 HAL、PLC 和诊断
- HAL browser:组件、pin、signal、param、类型、方向、线程、函数、连接和当前值。
- HAL command console:
loadrt、addf、setp、sets、net、unlinkp、show、save、source的受控子集。 - HAL scope/streamer:订阅通道、采样频率、触发、缩放、暂停、导出、回放和丢样本标记。
- ClassicLadder:工程加载/保存、section、symbol、变量、梯形图、顺序图、扫描周期、虚拟寄存器和调试状态。
- 诊断:事件时间线、报警确认、配置错误、Worker overrun、OPFS 错误、配额、能力报告和原生/WASM 差异。
3.5 工程、工具和配置
- 工程文件树:INI/HAL/TBL/VAR/NGC/PY/XML/TCL/CLP、模型和材料表;显示版本、哈希、未保存变更和冲突。
- 配置编辑器:section/键类型、include 图、HALFILE 顺序、资源引用、能力映射、警告和仿真排除项。
- 工具表:pocket/tool、几何、磨损、角度、方向、数据库查询、备份、迁移和换刀预览。
- 快照/恢复:运行态 checkpoint、事件增量、恢复点、差异和回滚;UI 不得把未提交 draft 当作已恢复。
- 导入/导出:工程包、manifest、golden、日志和兼容报告;导入先校验哈希、版本、路径和权限。
3.6 可视化与响应式设计
- 三栏工作布局:左侧工程/程序资源,中部程序与 3D 主视图,右侧状态/报警/工具面板;窄屏切换为标签页和底部控制栏。
- 关键状态使用稳定区域:模式、急停、运行/暂停、当前行、坐标、进给、主轴、报警和恢复状态不能因消息刷新跳动。
- 轨迹数据使用 transferable
ArrayBuffer;渲染线程只处理最新可绘制帧,中间帧可丢弃并显示采样间隙。 - 所有控制支持键盘和触摸;急停、停止、释放 jog 等高风险操作需要可见状态和二次确认策略。
- 前端不使用“看起来完成”的乐观动画;命令尚未
verified时显示 pending,错误必须保留来源和操作 ID。
4. 前端与 LinuxCNC 通信协议
4.1 通信通道
UI thread
└─ WorkerClient.postMessage()
├─ control channel: command/ack/result/error/recovery
├─ state channel: snapshot/event/alarm/capability
└─ stream channel: trajectory/HAL samples/log chunks (transferable buffers)
第一版使用 Dedicated Worker 的 postMessage 和结构化克隆;大块轨迹、日志和快照使用 transferable ArrayBuffer。SharedArrayBuffer 只作为后续性能优化,必须保持相同命令顺序和结果。通信不使用真实 NML socket;NML 消息类型和状态语义在 LinuxCncCompat 中映射到版本化 message bus。
4.2 通用消息信封
所有消息都包含以下字段;事件和流也必须带 seq 和 simulation_tick,不能使用裸 JSON:
type MessageEnvelope<T> = {
schema_version: 1;
kind: string;
context_id: string;
session_id: string;
seq: number;
operation_id?: string;
simulation_tick?: number;
sent_at_monotonic_ms?: number;
payload: T;
};
seq 在会话内单调递增;operation_id 对所有有副作用命令必填;sent_at_monotonic_ms 只用于诊断,不参与仿真结果。消息大小、数组长度、字符串长度、订阅数量和命令速率均由 capability/limits 下发并由 Worker 再次校验。
4.3 握手和能力协商
UI -> HELLO { client_version, supported_schema_versions, requested_features }
Worker -> CAPABILITIES { context_id, abi, snapshot, commands, streams, limits, exclusions }
UI -> SESSION_ATTACH { session_id?, project_id, lease_token? }
Worker -> SESSION_READY { session_id, state_hash, simulation_tick, subscriptions }
握手失败不得降级到未版本化协议。CAPABILITIES 必须列出支持的 G/M/O、HAL 组件、kinematics、Python 模块、UI 插件、最大程序大小、最大快照、可用流和 REAL-EXCLUDED 能力。前端按能力隐藏或禁用控制,并显示原因,不把缺失能力伪装成运行时错误。
4.4 命令分类
| 命令族 | 示例 | 副作用 | 处理要求 |
|---|---|---|---|
| 生命周期 | SESSION_CREATE、LOAD_PROJECT、START、STOP、RESET、DESTROY |
是 | prepare/verify/commit;租约和状态机检查 |
| 程序 | PROGRAM_LOAD、PROGRAM_RUN、PROGRAM_PAUSE、PROGRAM_RESUME、PROGRAM_ABORT、PROGRAM_STEP、PROGRAM_SEEK |
是 | 记录程序哈希、当前行和 tick;不允许跳过队列语义 |
| MDI/运动 | MDI_EXECUTE、JOG_START、JOG_STOP、HOME、SET_MODE |
是 | jog 必须有 stop/释放;重复 operation_id 返回原结果 |
| 倍率/IO | SET_OVERRIDE、SPINDLE_COMMAND、COOLANT_COMMAND、TOOL_PREPARE、TOOL_CHANGE、PROBE_CONFIG |
是 | 能力、范围、模式和虚拟设备状态校验 |
| HAL/PLC | HAL_COMMAND、HAL_SET、HAL_SUBSCRIBE、PLC_START/STOP |
是 | 受控白名单、pin 类型检查、周期和权限检查 |
| 工作区 | PROJECT_SAVE、IMPORT、EXPORT、CHECKPOINT、RECOVER |
是 | OPFS journal;结果带版本和哈希 |
| 查询/诊断 | SNAPSHOT_REQUEST、HAL_READ、LOG_QUERY、DIFF_QUERY、CAPABILITIES |
否/只读 | 可取消、可重复;不改变仿真状态 |
| 控制 | SUBSCRIBE、UNSUBSCRIBE、CANCEL、PING |
订阅有状态 | 明确 cursor、采样率、背压和取消结果 |
每条命令经过 validate → authorize → ledger_prepare → execute → verify → ledger_commit → ack/result。ACK 只表示 Worker 已接收和排队,不表示仿真完成;UI 只有收到 COMMAND_RESULT 或明确的 ERROR 才结束 pending。
4.5 状态、事件和流
| 消息 | 频率 | 内容 | 丢失策略 |
|---|---|---|---|
STATE_SNAPSHOT |
20-60 Hz 或请求 | task/motion/interpreter/IO/HAL/tool/Python/alarms/恢复状态 | 不可丢最终快照;按 cursor 重取 |
EVENT/ALARM |
事件驱动 | 状态变化、错误、换刀、探针、主轴、配置和恢复事件 | 不可静默丢;持久化 event log,缺号触发恢复 |
TRAJECTORY_CHUNK |
订阅 | tick 范围、坐标/姿态、刀具、采样和差异标记 | 可丢中间块;保留范围和 gap,最终位置来自快照 |
HAL_SAMPLE |
订阅 | 通道定义、采样值、tick、丢样本数 | 可按背压降采样;触发和边界样本不可丢 |
LOG_CHUNK |
请求/批量 | 级别、来源、操作 ID、错误码和文本 | 可滚动;错误和恢复日志不可丢 |
RECOVERY_STATUS |
恢复期间 | checkpoint、重放、对账、冲突、可继续状态 | 不可丢,UI 必须阻塞依赖状态的命令 |
4.6 典型消息
{
"schema_version": 1,
"kind": "MDI_EXECUTE",
"context_id": "linuxcnc-2.10.0-pre0-wasm-c1",
"session_id": "s-42",
"seq": 184,
"operation_id": "op-184-9fd1",
"payload": { "line": "G0 X10 Y5", "source": "mdi", "queue_policy": "queue_buster" }
}
{
"schema_version": 1,
"kind": "COMMAND_RESULT",
"context_id": "linuxcnc-2.10.0-pre0-wasm-c1",
"session_id": "s-42",
"seq": 184,
"operation_id": "op-184-9fd1",
"simulation_tick": 93210,
"payload": {
"status": "verified",
"state_hash": "sha256:...",
"canonical_events": 1,
"error": null,
"ledger_commit": "journal/184.commit"
}
}
4.7 错误、取消和超时
错误码分为 protocol_*、capability_*、config_*、interpreter_*、motion_*、hal_*、python_*、workspace_*、recovery_* 和 real_hardware_excluded。错误必须包含 code、message_key、参数、来源文件/行、operation_id、simulation_tick、可重试性和建议动作。
取消分为 requested、accepted、draining、cancelled、too_late;已经提交的有副作用命令不能回滚成“未执行”。超时只表示客户端尚未收到确认,WorkerClient 必须进入 awaiting_reconcile 并查询账本/状态,而不是直接重发。
4.8 背压、流控和性能预算
- 控制命令使用有界队列;超过上限返回
protocol_backpressure,不丢弃已经接收的副作用命令。 - 快照发布采用“最新优先”;事件、报警、恢复状态和命令结果采用“顺序优先”,不可用快照降采样替代。
- 轨迹和 HAL 流按订阅者独立限速;Worker 返回
cursor、sample_period、dropped_count和 tick 范围。 - 单次消息、单工程、单快照、单流和订阅数量上限在
CAPABILITIES.limits中声明,并在 Worker 和 UI 双重检查。 - 前端只渲染当前可见窗口和最近快照;日志、轨迹和波形使用虚拟列表/分块,不把无限历史放入 React state。
- M1 建立命令确认 P95/P99、快照延迟、流吞吐、UI 主线程阻塞和 Worker overrun 基线;超过预算必须阻止里程碑。
4.9 LinuxCNC 源程序和通信映射
ProtocolGateway 不能直接调用零散 LinuxCNC 函数;每个命令族先映射到 LinuxCncCompat 的稳定接口,再由兼容层访问对应源码状态机。以下关联必须写入 manifest 的 source_files、source_symbols、protocol_messages 和 state_fields:
| 前端通信域 | LinuxCNC 源程序 | Worker/WASM 适配接口 | 前端消息/状态 |
|---|---|---|---|
| 生命周期和 Task | linuxcnc/src/emc/task/emctaskmain.cc、taskclass.cc、taskintf.cc、emctask.cc |
SimTask.create/load/start/stop/reset/destroy |
SESSION_*、START/STOP/RESET、task.state |
| 程序和解释器 | linuxcnc/src/emc/rs274ngc/interp_read.cc、interp_execute.cc、interp_queue.cc、interp_convert.cc、modal_state.cc |
Interpreter.load/run/step/abort/seek |
PROGRAM_*、interpreter.*、canonical.* |
| MDI 和 canonical | linuxcnc/src/emc/task/emccanon.cc、linuxcnc/src/emc/nml_intf/interpl.cc、linuxcnc/src/emc/rs274ngc/interp_queue.cc |
SimTask.mdi、CanonicalQueue |
MDI_EXECUTE、command_result、当前行/队列 |
| Motion/jog/home/limit | linuxcnc/src/emc/motion/command.c、control.c、homing.c、motion.c、motion_struct.h |
SimMotion.command/jog/home/snapshot |
JOG_*、HOME、SET_MODE、motion.*、homing.* |
| TP 和轨迹 | linuxcnc/src/emc/tp/tp.c、tc.c、tcq.c、blendmath.c、spherical_arc.c |
TrajectoryPlanner.step/readChunk |
TRAJECTORY_CHUNK、位置/速度/加速度、queue depth |
| Kinematics/姿态 | linuxcnc/src/emc/kinematics/*kins.c、*kins.cc、kins_util.c、trtfuncs.c |
Kinematics.forward/inverse/select |
kinematics.*、joint/axis/pose、singularity error |
| IO/主轴/换刀/冷却 | linuxcnc/src/emc/iotask/ioControl.cc、ioControl_v2.cc;linuxcnc/src/emc/tooldata/** |
SimIo.command/snapshot、ToolService |
SPINDLE_*、TOOL_*、COOLANT_*、io.*、tool.* |
| HAL/halui | linuxcnc/src/hal/hal_lib.c、hal.h、hal_priv.h、halmodule.cc;linuxcnc/src/emc/usr_intf/halui.cc |
SimHal.execute/read/subscribe |
HAL_COMMAND/SET/SUBSCRIBE、hal.*、HAL_SAMPLE |
| ClassicLadder | linuxcnc/src/hal/classicladder/classicladder.c、calc.c、calc_sequential.c、files_project.c、module_hal.c |
SimPlc.load/start/stop/step |
PLC_*、PLC section/variable/scan state |
| NML 状态/命令/错误 | linuxcnc/src/emc/nml_intf/emc.cc、emc.hh、emc_nml.hh;linuxcnc/src/libnml/nml/stat_msg.cc、cmd_msg.cc |
LinuxCncCompat.translateCommand/publishStatus |
envelope、STATE_SNAPSHOT、EVENT、ALARM |
| Python/remap/VCP | linuxcnc/src/emc/pythonplugin/python_plugin.cc;linuxcnc/src/emc/rs274ngc/interp_python.cc、interp_remap.cc;linuxcnc/lib/python/hal.py、qtvcp/**、gladevcp/** |
PythonRuntime.invoke、虚拟 linuxcnc/hal 模块 |
PLUGIN_*、Python result/error、VCP pin/event |
| 工程和参数 | linuxcnc/src/libnml/inifile/**、linuxcnc/src/emc/ini/**、linuxcnc/src/emc/tooldata/** |
WorkspaceRuntime、ConfigService |
PROJECT_*、IMPORT/EXPORT、配置/工具/参数状态 |
前端所需但 LinuxCNC 原生状态中不存在的字段,例如 pending、operation_id、lease_token、recovery_progress 和 dropped_count,属于 Web 适配状态,必须明确标记为 SIM-ADAPTER,不能写回 LinuxCNC 核心结构。
4.10 快照状态契约
快照按领域分区,允许通过位图和版本号发送增量;增量必须基于已确认的 base_snapshot_id,基线缺失时前端请求完整快照:
type SimSnapshotV1 = {
snapshot_id: string;
base_snapshot_id?: string;
full: boolean;
simulation_tick: number;
state_hash: string;
task: { state: string; mode: string; interp_state: string; current_line: number };
motion: { axes: number[]; joints: number[]; velocity: number[]; homed: boolean[]; limits: number };
coordinates: { machine: number[]; relative: number[]; g5x: number; g92: number[]; tool_offset: number[] };
spindle: Array<{ enabled: boolean; direction: number; commanded: number; actual: number; at_speed: boolean }>;
io: { coolant: number; lube: boolean; digital: number[]; analog: number[]; probe: boolean };
tool: { current: number; prepared: number; pocket: number; change_state: string };
overrides: { feed: number; rapid: number; spindle: number[]; adaptive: number; feed_hold: boolean };
hal: { graph_revision: number; changed_values?: ArrayBuffer };
alarms: Array<{ id: string; severity: string; code: string; acknowledged: boolean }>;
recovery: { status: string; last_verified_seq: number; checkpoint_tick: number };
};
实际编码可以是生成的二进制 schema,但字段语义必须保持。浮点数组的轴顺序、单位、NaN/Infinity 策略、位掩码和空值语义必须在 schema 中固定;前端不能根据数组长度猜测机床配置。
4.11 Worker 到 WASM C ABI 调用规则
| Worker 操作 | C ABI | 调用规则 | 结果 |
|---|---|---|---|
| 创建核心 | sim_create(config, len) |
握手和配置校验后调用一次;失败则不创建会话 | 错误码、能力基础和初始状态 |
| 挂载工程 | sim_load_workspace(root_path) |
WorkspaceRuntime 完成 journal 恢复和 /workspace 挂载后调用 |
配置诊断、资源图和可启动状态 |
| 发送命令 | sim_command(cmd, len) |
ProtocolGateway 编码已校验命令;每个 operation_id 最多进入核心一次 |
入队/拒绝错误;最终结果从事件或快照确认 |
| 推进仿真 | sim_step(cycles) |
仅 SimScheduler 调用;UI 不得直接指定墙钟结果 | 新 tick、事件和可发布状态 |
| 读取快照 | sim_read_snapshot(out, capacity) |
先查询/预分配容量;复制到 Worker 所有的 buffer,再转移给 UI | STATE_SNAPSHOT 或容量错误 |
| 读取事件 | sim_read_event(out, capacity) |
循环读取到空或有界批次上限;保持核心顺序 | EVENT、ALARM、COMMAND_RESULT |
| 导出运行态 | sim_export_state(out, capacity) |
在安全 tick 暂停写入,生成带 schema/context 的 checkpoint | WorkspaceRuntime prepare/commit |
| 导入运行态 | sim_import_state(data, len) |
校验 context、schema、工程哈希后导入;导入失败保留旧实例 | RECOVERY_STATUS 和完整快照 |
| 销毁核心 | sim_destroy() |
关闭订阅、flush journal、关闭句柄后调用;幂等 | SESSION_DESTROYED |
WASM 内存由 WasmRuntime 独占。输入 buffer 在调用返回前有效;输出在下一次核心调用前复制到 Worker buffer;TypeScript 不长期保存 WASM heap view。C ABI 返回值统一映射为版本化错误码,禁止用异常字符串作为协议。sim_step、sim_command、状态导入导出和销毁不能并发执行。
5. 生命周期、恢复和离线通信
5.1 会话生命周期
new → handshaking → attaching → loading → ready
ready → running ↔ paused
running/paused → stopping → stopped
任何状态 → recovering → ready | blocked
任何状态 → destroying → destroyed
状态转换由 Worker 权威决定。UI 可以请求转换,但不能本地伪造转换。每次转换发送 STATE_TRANSITION 事件并写入任务账本;非法转换返回 state_transition_invalid。
5.2 刷新、Worker 重启和回答中断
Worker 启动后先发送 RECOVERY_REQUIRED 或 SESSION_READY。UI 在收到 RECOVERY_STATUS=verified 前禁用依赖运行态的运行、jog、MDI 和工具命令,只允许恢复、导出、查看日志和取消。WorkerClient 维护未完成操作表;重连时按 operation_id 查询结果,已 commit 的操作只关联原结果,未 commit 的操作从 checkpoint 继续。
5.3 断网和重新联网
前端的网络状态不能决定本地仿真状态。Service Worker 缓存应用壳、WASM、Pyodide、协议 schema 和样例;Worker 继续使用 OPFS journal、快照和本地命令。重新联网只执行缓存更新或可选同步,必须先比较 context_id、工程哈希、最后 seq、tick 和状态哈希;冲突时保留本地版本并显示差异。
5.4 多标签页与租约
默认一个工程只能有一个写入会话。主标签页持有 lease_token 和 heartbeat;其他标签页以只读模式订阅快照。主标签页崩溃后,候选标签页读取 lease expiry、校验 checkpoint 和 journal,再通过 SESSION_CLAIM 取得新租约。两个写者不允许同时运行;无法确定租约时进入 blocked 而不是并发写入。
6. 安全、权限和兼容性
- Worker 对命令、文件、组件、Python 模块和插件能力做白名单检查;UI 隐藏按钮不是安全边界。
- 工程路径统一规范化为
/workspace相对路径,拒绝..、绝对宿主路径、符号链接和未授权扩展名。 - Python remap、filter 和 VCP 只访问虚拟
linuxcnc、hal、INI、参数、时间和 I/O API;fork/exec/subprocess/threading/socket/设备返回明确错误。 - 所有外部输入(导入工程、G-code、HAL、XML/UI、插件)先解析、限制资源、记录来源哈希,再提交给 Worker。
- 前端显示
REAL-EXCLUDED时必须说明“不连接真实硬件、不提供安全保证”,禁止使用“在线”“已连接”等误导状态。 - schema/ABI/工程格式升级采用新版本号、双读迁移和 context lock;旧版本不能静默解释新字段。
7. 详细实施任务
任务标题统一使用 [WS-x][Mx],每个任务绑定 source map/coverage manifest 条目、固定上下文包和测试证据。
WS-5 前端与可视化任务
| 任务 ID | 任务 | 交付物 | 依赖 | 验收 |
|---|---|---|---|---|
| FE-01 | 应用壳、路由、响应式布局和主题变量 | AppShell、导航、布局断点、无障碍基础 | 无 | 首次启动、窄屏、键盘和只读模式通过 |
| FE-02 | WorkerClient 和协议校验 | 信封类型、schema 校验、序列、超时、取消、重试 | COM-01 | 非法/未知消息拒绝;重复操作不重复副作用 |
| FE-03 | 会话/工程工作流 | 工程选择、租约、导入导出、恢复向导 | WS-3、COM-02 | 刷新、Worker 重启、无网、冲突恢复通过 |
| FE-04 | 程序编辑器和预览 | G-code 编辑、诊断、轨迹预览、当前行和差异 | WS-1、COM-02 | 语法/运行错误定位,预览与 golden 一致 |
| FE-05 | 控制台和手动操作 | MDI、run/pause/step/abort、jog/home/override | WS-2、COM-02 | 模式状态机、释放保护、命令结果完整 |
| FE-06 | 工具、IO、报警和状态面板 | 工具表、主轴、冷却、换刀、探针、报警 | WS-2、WS-3 | 正常/故障/恢复工作流通过 |
| FE-07 | HAL/PLC/诊断界面 | pin 图、命令控制台、scope、Ladder、日志 | WS-2、WS-4 | 订阅、触发、回放和权限限制通过 |
| FE-08 | Three.js 机床和毛坯 | 模型、姿态、轨迹、坐标、切削层 | FE-04、COM-03 | 非空渲染、采样背压、最终姿态一致 |
| FE-09 | VCP/插件 schema 运行时 | XML/descriptor 转 Web Component、权限和生命周期 | WS-4、COM-02 | 样例面板、pin 回调、异常隔离通过 |
| FE-10 | 前端性能和可观测性 | 虚拟列表、帧预算、消息统计、录制回放 | FE-01..09 | P95/P99 和主线程预算满足门禁 |
WS-2/COM 通信与状态任务
| 任务 ID | 任务 | 交付物 | 依赖 | 验收 |
|---|---|---|---|---|
| COM-01 | schema 和 C/TS 类型生成 | protocol.v1、命令/事件/错误/能力定义 |
WS-0 | schema 版本锁定,未知字段策略明确 |
| COM-02 | ProtocolGateway/TaskLedger | 路由、权限、幂等、prepare/commit、恢复 | WS-1、WS-3 | 超时、确认丢失、重复响应和重启恢复通过 |
| COM-03 | SnapshotPublisher/流控 | 快照、事件、轨迹、HAL sample、cursor、背压 | WS-2 | 事件不丢、流可降采样、最终快照可重取 |
| COM-04 | LinuxCncCompat message bus | NML command/status/error 到 Worker 消息映射 | WS-1 | 消息类型、顺序、错误和状态 golden 对照 |
| COM-05 | 能力/限制注册表 | G/M/O、组件、kinematics、Python、UI、limits | WS-0、WS-1..4 | UI 正确启用/禁用,排除项可解释 |
| COM-06 | 浏览器和故障矩阵 | Playwright 通讯、离线、租约、刷新、背压测试 | COM-01..05 | Chromium/Firefox/Safari 目标矩阵通过 |
7.3 实施批次和依赖顺序
| 批次 | 并行任务 | 串行依赖/冻结点 | 可演示结果 |
|---|---|---|---|
| B0 规范和基线 | COM-01、COM-05、FE-01 | 冻结 protocol.v1、能力 ID、错误码和 UI 信息架构 |
mock Worker 可完成握手、能力显示和错误页 |
| B1 最小通信闭环 | COM-02、COM-04、FE-02 | 必须先有 sim_create/load/command/step/snapshot ABI |
加载最小工程、发送命令、显示权威快照 |
| B2 核心操作 | FE-04、FE-05、COM-03 | interpreter/motion 状态字段冻结 | G-code、MDI、jog、home、override、轨迹闭环 |
| B3 持久化恢复 | FE-03、COM-06 的恢复子集 | WorkspaceRuntime、TaskLedger、checkpoint schema 冻结 | 刷新、Worker 重启、断网和确认丢失自动接续 |
| B4 设备和工具 | FE-06、FE-07 | HAL/IO/tool/PLC schema 和虚拟设备能力冻结 | 主轴、换刀、探针、报警、HAL/PLC 工作流 |
| B5 可视化和插件 | FE-08、FE-09 | 轨迹/姿态流和插件权限冻结 | Three.js 机床、毛坯、VCP/QtVCP 等价工作流 |
| B6 稳定发布 | FE-10、COM-06 全矩阵 | 全量 manifest、浏览器和性能门禁 | 可离线发布候选、兼容/差异/性能报告 |
B0-B2 可以使用只读 mock/golden 驱动 UI 开发,但到对应里程碑退出时必须切换真实 Worker/WASM 状态;mock 不能作为功能完成证据。B3 以后不得修改 protocol.v1 语义,只能增加向后兼容字段;破坏性变更必须创建 protocol.v2 并回退到 FE-M0 评审。
8. 里程碑和退出条件
| 里程碑 | 目标 | 关键输出 | 必须通过的退出条件 |
|---|---|---|---|
| FE-M0 协议冻结 | 定义前端、Worker、WASM 和 OPFS 边界 | protocol.v1、能力模型、错误码、UI 信息架构、source/coverage 关联 |
所有消息有 envelope/schema;命令权限和状态转换无歧义 |
| FE-M1 可启动垂直切片 | 工程加载、状态显示、最小 G-code 运行 | AppShell、WorkerClient、create/load/step/snapshot、最小 3 轴页面 |
浏览器可加载缓存样例;命令 ack/result、快照和错误完整 |
| FE-M2 操作闭环 | 程序、MDI、jog、home、override、报警 | 控制台、程序编辑/预览、手动操作、状态面板 | 正常/边界/错误/暂停恢复与原生 golden 对照通过 |
| FE-M3 工程和恢复 | OPFS、快照、断网、重启、租约 | 工程树、导入导出、TaskLedger、恢复 UI、离线 PWA | 半提交、配额不足、确认丢失、标签页抢占和冲突均不丢数据 |
| FE-M4 全部仿真工作流 | HAL/PLC/工具/IO/Python/VCP/3D | scope、Ladder、工具、数字孪生、插件和机床视图 | 覆盖矩阵所有 Web/API 条目有 source/test/oracle,关键交互通过 |
| FE-M5 发布候选 | 稳定、离线、高效和可诊断 | 浏览器报告、性能报告、兼容报告、迁移/回滚和离线包 | G0-G5 全绿;无孤儿来源、无未覆盖 UI 工作流、无静默降级 |
每个里程碑都必须附固定 context lock、上下文包、源程序关联 diff、manifest diff、测试日志、golden/difference、性能指标和回滚步骤。发现协议、状态或源文件范围变化时,回退到最早受影响的 FE/COM 里程碑重新验证。
FE 里程碑与项目里程碑映射如下:FE-M0 属于 M0,FE-M1 属于 M1,FE-M2 属于 M2,FE-M3 属于 M3,FE-M4 属于 M4,FE-M5 属于 M5。项目里程碑未通过时,对应 FE 里程碑不能单独签署完成。
9. 前端验收与覆盖矩阵绑定
前端功能不得脱离 LinuxCNC 功能条目单独宣布完成。每个前端任务至少绑定:
feature_id → source_files/source_symbols
→ config_inputs
→ worker command/event schema
→ UI workflow test
→ native/WASM oracle or Web-equivalent oracle
→ recovery/offline test
→ context_id + evidence artifact
前端与 LinuxCNC 通信条目应使用 WEB-EQUIV 或 SIM-ADAPTER;Python/VCP 回调使用 PY-WASM;虚拟主轴、探针、换刀、等离子、激光、编码器和限位使用 SIM-DEVICE。真实设备、真实实时性和安全认证继续使用 REAL-EXCLUDED,但其仿真可观察行为必须由数字孪生条目覆盖。
只有以下条件全部满足,才能声明“前端仿真工作流完成”:
- 能力协商正确反映当前构建和配置,不显示未实现功能。
- 命令从 UI 到 Worker 到 LinuxCNC 兼容层的顺序、参数、错误和结果可追踪。
- 快照、事件、流和最终状态在正常、暂停、恢复、断网和 Worker 重启后保持一致。
- 每个操作具备 pending/ack/result/error 状态,超时先对账,重试不重复副作用。
- UI、API、Python、HAL 和 OPFS 证据都进入 manifest,且无孤儿来源或未覆盖交互。
10. 详细测试矩阵
| 测试层 | 重点用例 | 自动化方式 | 阻断条件 |
|---|---|---|---|
| Schema/类型 | 全部命令、事件、快照、未知版本、缺字段、超限数据 | TS 单测 + C/TS 编解码 round-trip | 任一消息无法双向解析或错误码不稳定 |
| WorkerClient | seq、operation_id、ack/result、timeout、cancel、重复/迟到响应 | 假 Worker、确定性时钟 | 重复副作用、pending 泄漏或错误被吞掉 |
| ProtocolGateway | 权限、状态机、能力、prepare/commit、幂等 | Worker 单测 + WASM stub | 未授权命令进入核心或账本不一致 |
| C ABI | create/load/command/step/snapshot/event/export/import/destroy | Node/WASM harness 和浏览器 Worker | 内存越界、顺序变化、错误映射不一致 |
| Domain store | 完整/增量快照、事件乱序、流 gap、恢复覆盖 | reducer/property tests | UI 状态无法由消息历史重建 |
| UI 工作流 | 工程、程序、MDI、jog、home、override、工具、HAL、PLC、报警 | Playwright + 页面对象 | 任一覆盖条目没有正常/错误/恢复路径 |
| 可视化 | 轨迹、姿态、坐标、毛坯、resize、隐藏/恢复 | Playwright screenshot + canvas pixel checks | 空画布、姿态错误、遮挡或动态内容改变布局 |
| 离线/恢复 | 首次缓存、启动无网、中途断网、刷新、Worker 崩溃、租约转移 | Playwright context/service-worker fault injection | 已提交数据丢失、重复命令、无法继续 |
| 性能/背压 | 大 G-code、多 HAL pin、高采样、长日志、快速模式 | browser benchmark + trace | P95/P99 超预算、无界队列、主线程长任务 |
| 浏览器兼容 | Chromium、Firefox、Safari 支持矩阵 | CI/browser farm 或锁定本地环境 | 必选工作流在任一目标浏览器失败 |
每个测试输出 context_id、浏览器、协议版本、工程哈希、最后 seq/tick、状态哈希和产物路径。视觉截图只验证布局和渲染,不能替代 LinuxCNC 状态和轨迹 golden。