# 02-项目程序开发详细步骤 ## 开发原则 1. 以 LinuxCNC 源码语义为准,不在 JavaScript 层发明新的 CNC 暂停语义。 2. `wasm-port` 的 wrapper 只能作为 native/WASM 边界适配,不能扩大为独立 CNC 语义实现。 3. 先补 task/motion 可观测状态,再补 STEP,再推进 TP 真实接入。 4. 每一步必须有 Node WASM 测试和 Web 集成测试证据。 ## 步骤 1:补齐 task 层暂停恢复状态 目标文件: - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp` - `wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs` - `wasm-port/runtime/sdk/src/linuxcnc-task-hal.js` - `web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/runtime/linuxcnc-task-hal-runtime.js` 开发动作: 1. 在 `TaskRuntime` 增加字段: - `std::string interp_resume_state = "IDLE";` 2. 初始化、reset、abort、home、program complete 时同步维护 `interp_resume_state`。 3. 在 `EMC_TASK_PLAN_PAUSE` 分支中按 LinuxCNC 语义处理: ```cpp if (state.interp_state != "PAUSED") { state.interp_resume_state = state.interp_state.empty() ? "READING" : state.interp_state; } state.interp_state = "PAUSED"; state.exec_state = "PAUSED"; state.task_paused = true; forward_motion_command("{\"type\":\"EMCMOT_PAUSE\"}"); ``` 4. 在 `EMC_TASK_PLAN_RESUME` 分支中按 LinuxCNC 语义处理: ```cpp const std::string resume = state.interp_resume_state == "PAUSED" || state.interp_resume_state.empty() ? "READING" : state.interp_resume_state; state.interp_state = resume; state.interp_resume_state = resume; state.exec_state = resume == "IDLE" ? "DONE" : "WAITING_FOR_MOTION"; state.task_paused = false; state.single_stepping = false; forward_motion_command("{\"type\":\"EMCMOT_RESUME\"}"); ``` 5. `status_json()` 输出 `interpResumeState`。 6. SDK 和 Web runtime wrapper 把 `interpResumeState` 转成小写并映射到 UI 状态。 验收: - pause 前为 `READING`,pause 后 `interpResumeState=READING`。 - resume 后恢复 `READING`,不是固定靠前端猜测。 - pause 前为 `WAITING` 时 resume 能恢复 `WAITING`。 ## 步骤 2:补齐 motion 层 immediate pause 语义 目标文件: - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_motion_runtime.c` - `wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs` 当前风险: `lcmot_write_command_json()` 把 `EMC_TRAJ_PAUSE` 作为普通命令入队。如果队列中已有多个 move,pause 可能排在 move 后执行。AXIS 用户点击暂停属于 GUI immediate command,应尽快阻止继续消费运动队列。 开发动作: 1. 对 `EMCMOT_PAUSE` 或 `EMC_TRAJ_PAUSE` 做即时状态切换: - `lcmot_state.paused = 1` - `lcmot_state.current_vel = 0` - `lcmot_state.motion_type = 0` - `lcmot_state.in_position = 0` 2. 不清空运动队列。 3. 暂停期间 `lcmot_step_servo()` 不消费普通 move,只允许: - `STEP` - `RESUME` - `ABORT` 4. `EMCMOT_RESUME` 即时清除 paused,后续 servo cycle 继续消费保留队列。 5. 状态 JSON 增加: - `motion.stepping` - `motion.idForStep` 或同等 step 追踪字段。 验收: - 先入队多个 line move,运行一部分后 pause。 - pause 后连续运行 20 个 cycle,`programLine` 不变、axis 不变、queueDepth 不减少。 - resume 后 queueDepth 继续减少,programLine 继续推进。 ## 步骤 3:实现 STEP 自动回暂停 目标文件: - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_motion_runtime.c` - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_task_hal_wasm.cpp` - `wasm-port/tests/wasm/node/verify_task_hal_wasm.mjs` - `web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_feedback_loop.mjs` LinuxCNC 对标: - `EMCMOT_STEP` 在 paused 时记录当前 motion id,短暂恢复 TP; - motion id 改变后自动 `tpPause()`; - 外部仍把程序视为 paused。 开发动作: 1. 在 motion runtime 增加: - `int stepping;` - `int id_for_step;` - `int motion_id;` 2. 每执行一个普通 move 时递增 `motion_id`。 3. `EMCMOT_STEP`: - 如果当前 paused,设置 `stepping=1`、`id_for_step=motion_id`; - 允许消费一个普通 move; - 保持对外 `paused=true` 或在状态中明确 `paused=true, stepping=true`。 4. `lcmot_step_servo()` 在 step 消费一个 move 后: - 自动恢复 `paused=1`; - 清 `stepping=0`。 5. task 层 `EMC_TASK_PLAN_STEP`: - 不把解释器永久改成 running; - step 完成后恢复 `interpState=PAUSED`、`taskPaused=true`。 验收: - paused 状态下 STEP 后只推进一个 line 或一个 motion id。 - STEP 完成后 `task.interpState=PAUSED`、`taskPaused=true`、`motion.paused=true`。 - 再次 Resume 可从 step 后位置继续运行。 ## 步骤 4:把 motion paused 字段接入 Web task policy 目标文件: - `web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/state/linuxcnc-task-policy.js` - `web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/state/store.js` - `web-rtcp-5axis-xyzbc-trt-sim-plan/app/src/ui/axis-shell.js` - `web-rtcp-5axis-xyzbc-trt-sim-plan/tests/node/verify_run_feedback_loop.mjs` 开发动作: 1. 从 `taskHalStatus.ui` 或 `taskHalStatus.motionStatus.motion.paused` 映射 `machine.motionPaused`。 2. `isProgramPaused()` 判断改为包含: - `state.machine.motionPaused === true` - `state.programRuntimeFeedback.paused === true` 3. `canResume` 和 `RESUME` gate 改为: ```text taskState == on taskMode in auto/mdi motionPaused 或 interpState paused 或 taskPaused 未被 resumeInhibit 阻止 ``` 4. `PAUSE_RESUME` toggle 优先看 motion paused,贴近 AXIS `s.paused`。 5. 保留菜单 Pause 的严格条件:AUTO 且 `interpState` 为 `reading/waiting`。 验收: - motion paused 已 true 但 interpState 同步稍晚时,工具栏按钮显示 Resume。 - Resume 不再只依赖 `interpState === paused`。 - 菜单 Pause/Resume 与工具栏 toggle 逻辑仍分离。 ## 步骤 5:补充 TP 层暂停验证 目标文件: - `wasm-port/runtime/core/linuxcnc_wrap/linuxcnc_tp_wasm.c` - `wasm-port/tests/wasm/node/verify_tp_wasm.mjs` 开发动作: 1. 在 TP probe 中增加 pause/resume 场景: - 创建 TP; - 加入至少两段 line; - 运行若干 cycle; - 调 `tpPause()`; - 继续运行若干 cycle; - 验证位置不继续跨段推进或速度趋向 0; - 调 `tpResume()`; - 验证可继续完成队列。 2. 输出可测试字段: - `tp_pause_rc=0` - `tp_resume_rc=0` - `tp_paused_velocity_zero=1` - `tp_resume_done=1` 验收: - `node wasm-port/tests/wasm/node/verify_tp_wasm.mjs` 通过。 - 证据显示 TP pause/resume 使用 vendored LinuxCNC TP API,而不是 JS 模拟。 ## 步骤 6:构建与回归 推荐命令: ```bash cd /home/mes123456/cnc_wams/wasm-port ./tools/build_task_hal_wasm.sh ./tools/build_tp_wasm.sh node tests/wasm/node/verify_task_hal_wasm.mjs node tests/wasm/node/verify_tp_wasm.mjs ``` Web 集成命令: ```bash cd /home/mes123456/cnc_wams/web-rtcp-5axis-xyzbc-trt-sim-plan npm --prefix app run build node tests/node/verify_run_feedback_loop.mjs APP_URL_PATH=/web-rtcp-5axis-xyzbc-trt-sim-plan/app/dist/index.html node tools/trace-pause-position-json.mjs RUN_STABLE_BEFORE_FIRST_PAUSE_MS=3000 node tools/verify-estop-power-home-run-pause-50ms.mjs ``` 验收输出至少包括: - `linuxcnc_task_runtime_smoke=ok` - `tp_wasm_node_smoke=ok` - `run_feedback_status_loop_smoke=ok` - `pause_position_status=passed-position-frozen` - `verification_status=passed` ## 步骤 7:记录证据与关闭任务 每完成一个编号任务: 1. 更新 `03-推进台账.md`。 2. 更新 `04-任务矩阵.md` 状态。 3. 把命令、输出、trace 路径、截图目录写入 `05-验收证据.md`。 4. 如果出现新取舍,追加 `06-决策记录.md`。