# UG/NX AI 外挂（NX Copilot）使用手册

> 本手册每条命令都在本机（2026-10-18）**实际跑过**，输出是真实的。
> 环境：Windows + NX2212 + NX2412 双版本，Python 3.12（`C:\Users\Administrator\AppData\Local\Programs\Python\Python312\python.exe`）。

---

## 1. 这是什么

一个**在 NX 外部**运行的 AI 辅助编程工具。NX 里只放极薄的桥接，业务逻辑全在外部程序里，因此：

- 同一套逻辑**同时支持 NX2212 和 NX2412**（自动识别、可切换）
- 不修改 NX 安装目录、不写系统/用户级环境变量（只用进程级）
- 断网可用（云端能力是可选的）

**能做什么**：
- 自动探测本机所有 NX 版本并任选其一执行任务
- 用 NX 打开真实零件，提取几何事实（实体数/面数/孔直径等）
- 按规则引擎 + 人工修正经验，输出**工序单**（刀具、转速、进给、切深等）
- 把人工修正沉淀成经验，下次遇到同类上下文自动复用（L2 精确 / L3 归并）
- 云端长期记忆：与云端 Agent 记忆双向同步、一键备份到本地

**明确不能做什么**（重要）：
- **输出是"建议态"**，上机前必须人工审核，并过 NX 仿真 / 过切检查
- CAM 工序：已能在 headless 模式**创建钻孔工序并生成刀路**（见第 11 章），但属"部分可用"——刀具真实几何、孔选择全覆盖、切深映射待补；铣削类工序尚未实现
- 不做碰撞检查、不替代工艺员

---

## 2. 快速开始（三条命令）

```powershell
# ① 看本机有哪些 NX 版本
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\nxai.ps1 list
```
真实输出：
```
Version Root                            RunJournalOk
------- ----                            ------------
2212    E:\Program Files\Siemens\NX2212         True
2412    E:\Program Files\Siemens\NX2412         True
```

```powershell
# ② 让指定版本跑一次自检任务（会真的启动 NX，约 40~50 秒）
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\nxai.ps1 run -Version 2412 -Task hello
```
真实输出（尾部）：
```
[nxai] 启动器退出码=0  耗时=44.6s
[nxai] 成功 ✓ 证据：PY_JOURNAL OK  NX=2412  Python=3.11.9  host_pid=3236
[nxai] 产物目录：D:\NX_AI\状态\artifacts\75c6d99e-...（manifest.json 已写）
```

```powershell
# ③ 校验产物完整性（按 manifest 里的 SHA256 逐个比对）
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\nxai.ps1 verify -TaskId <上一步的 id>
```
真实输出：
```
  [OK]   journal_result.txt  sha256 一致（594 字节）
  [OK]   run_journal_20261017_211405.log  sha256 一致（1017 字节）
校验通过：全部产物完整
```

> `run` 的实现链：提交任务到 `状态\tasks.db` → 进程级环境注入 → 启动 NX 批处理 → 抓成功标记 → 归档产物并写 manifest → 回写任务状态。

```powershell
# ④ 一条命令跑 7 个阶段（提取→读参→对账→出单→建工序→语法门禁→评测门禁，详见第 15 章）
powershell -File D:\NX_AI\nxai.ps1 pipeline -WithNx -CreateOp
```
日常体检可以先只跑纯 Python 段（不启动 NX，约 5 秒）：
```powershell
powershell -File D:\NX_AI\nxai.ps1 pipeline
```
真实输出：
```
  [SKIP] [1] 提取真实工序         （需要 --with-nx）
  [SKIP] [2] 读真实转速进给       （需要 --with-nx）
  [OK  ] [3] 真实 vs 引擎对账     CAM_PARAM_DIFF samples=18 compared=15 skipped=3
  [OK  ] [4] 出工序单             产物: D:\NX_AI\状态\artifacts\plan_20261018_155933
  [SKIP] [5] 建工序+生成刀路      （需要 --create-op）
  [OK  ] [6a] 语法门禁            扫描 108 个 .py  0 错误 0 预警
  [OK  ] [6b] 评测门禁            通过 21/21  漂移 0 处
  4 个阶段：4 通过 / 0 失败   报告：D:\NX_AI\状态\pipeline_report.md
```

---

## 3. 真实零件全链路演示

```powershell
python D:\NX_AI\tools\demo_end_to_end.py
```
会启动 NX 两次（提几何事实 + 提孔直径），全程约 **65 秒**，报告写到 `D:\NX_AI\状态\demo_report.md`。

真实输出（关键行）：
```
几何事实: {"probe": "model_facts", ..., "nx_version": "NX2412", "python": "3.11.9",
          "leaf": "后盖铝板-LQD80-镜像-175电机", "body_count": 2, "face_count": 130}
步骤 3 规则引擎出工序单（L1）  退出码=0
步骤 4 模拟一次人工修正        已记录修正 / 回查命中人工修正
步骤 6 前后对比                计划 A/B 不同 —— 学习已生效，人工修正被复用
```

不想启动 NX 时加 `--skip-nx`（用已有产物或合成事实，秒级）。

---

## 4. 出工序单

```powershell
python D:\NX_AI\core\propose_plan.py --facts D:\NX_AI\状态\_demo_facts.json --material N --json
```

**材料组对照**（不指定 `--material-name` 时按组自动取该组第一个材料，已实测）：

| 组 | 默认材料 | 说明 |
|---|---|---|
| **P** | 45钢 | 钢 |
| **M** | 304不锈钢 | 不锈钢 |
| **K** | HT250灰铸铁 | 铸铁 |
| **N** | 6061铝合金 | 铝/非铁 |
| **S** | TC4钛合金 | 耐热合金（钛/镍基） |
| **H** | SKD11淬硬钢 | 淬硬钢 |

> ⚠ 一定要选对组：**铝件用 P（钢）会套上钢的切削参数**。
> 可用 `--material-name "6061铝合金"` 精确指定，或 `--hardness 20` 指定硬度。

**学习开关**：
- `--no-learn` 关闭 L2 精确修正
- `--no-flywheel` 关闭 L3 归并经验
- 都不加 = 规则 + 精确修正 + 归并经验（优先级：精确 > 归并 > 规则）

### 4.1 材料分族与参数来源（P10-06）

把切削特性差别不大的材料归为一族（共 7 族），存于 `data\std\material_families.json`，
按族给出 Vc / fz / ap / ae 的**参考区间**。数值来源：
**刀具厂商样本参考区间（Tungaloy 立铣刀样本 / OSG、Ingersoll 切削数据手册）**。

| 族 | 名称 | ISO | 包含材料（别名） | 硬度 | Vc (m/min) | fz (mm/齿) | ap (×D) | ae (×D) | 备注 |
|---|---|---|---|---|---|---|---|---|---|
| AL | 铝及铝合金 | N | 6061/6063/7075/5052/5083/LY12/ADC12 | HB 60-120 | 200-500 | 0.05-0.12 | 1.0-2.0 | 0.3-0.6 | 易粘刀，务必大流量冷却；7075 取区间中上 |
| ST | 普通碳钢/软态模具钢 | P | 45#/Q235/S50C/P20/718/738/2738/1045/S136(退火) | HB 150-250 | 100-180 | 0.05-0.10 | 0.8-1.0 | 0.3-0.5 | |
| STH | 预硬模具钢 | P | NAK80/718H/738H/2316/2738H/P20H/S136H | HB 260-330 | 70-120 | 0.03-0.08 | 0.5-1.0 | 0.2-0.4 | 预硬状态，ap 不要照搬软钢 |
| SS | 不锈钢 | M | 304/316/316L/321/201/17-4PH(固溶) | HB 150-220 | 40-120 | 0.03-0.08 | 0.5-1.0 | 0.2-0.4 | 加工硬化，禁止小进给停留刮擦；304 常用 vc 60-90 |
| HRC | 淬硬钢 | H | SKD11/DC53/CR12MoV/SKD61(淬火)/HRC45-62 | HRC 45-62 | 30-80 | 0.02-0.05 | 0.1-0.3 | 0.05-0.15 | 硬铣，小 ap 小 ae，多用球刀/圆鼻刀 |
| CI | 铸铁 | K | HT250/HT300/QT600/GG25 | HB 180-260 | 100-200 | 0.05-0.12 | 1.0-1.5 | 0.3-0.5 | 干切或微量冷却，注意粉尘 |
| TI | 钛/高温合金 | S | TC4/Ti-6Al-4V/GH4169/Inconel718 | HB 300-400 | 30-60 | 0.02-0.05 | 0.3-0.8 | 0.1-0.25 | 低转速大进给，严禁高速轻载 |

> ⚠ **区间是参考值，不替代试切**；首件必须空跑或小切深验证。

**计算规则**（族通道，`rule_engine.recommend_by_family`，独立于 SpeedFeedTable 旧通道）：
- `rpm = 1000 × Vc ÷ (π × D)`，Vc 按材料硬度在族区间内**线性插值**（硬度越低取上限）
- `F = rpm × fz × z × 有效齿系数`（z 取刀具库刃数，无则按 4 齿估算并标注；有效齿系数默认 1.0）
- ap/ae 按加工阶段：粗加工取族区间下限×D；半精取 0.5 倍（0.4~0.6 取中值）；精加工 ap 按 RL-009 clamp 到 0.1~0.3mm、ae 取 0.05D（保光洁度）
- 手动改 Vc/fz 超出族区间 → 明确标注 `[超出族区间]` 并给建议，**绝不静默采用**

**怎么用**：

```powershell
# ① CLI 按族算一组（同 D12 刀，6061 粗加工档）
python D:\NX_AI\data\rules\rule_engine.py --family-calc --material-name 6061 --tool-name D12 --stage rough
# 实测输出：S=8620.9  F=2047.5  ap=12.0  ae=3.6（Vc=325 由硬度 95HB 在 60-120 插值）
# 换 304 不锈钢（同刀同阶段）：S=1667.3  F=221.5  ap=6.0  ae=2.4 —— 不锈钢显著低于铝

# ② 出工序单：顶部自动标注 材料 → 族 → Vc 区间，并附「材料分族参考」节
python D:\NX_AI\core\propose_plan.py --facts D:\NX_AI\状态\_demo_facts.json --material N

# ③ 手动改 Vc/fz（超区间会标 [超出族区间]）
python D:\NX_AI\data\rules\rule_engine.py --family-calc --material-name 6061 --tool-name D12 --vc 600
```

- **图形工作台**：「材料」下拉按族分组（如"铝件 / 6061"），选中即带出 Vc 区间；出工序单与工序单编辑器重算都使用该选择，选材结果持久化到 `config\ui.json`。
- **手动改值沉淀**：在工序单编辑器里改 S/F/ap/ae 并勾选"永久优化+原因"→ 走既有 overrides 机制写入 `状态\overrides\<零件>.json` 并记入长期记忆，下次同类零件自动复用。
- 出工序单的正式参数仍走 SpeedFeedTable 通道（评测基线 21/21 不变）；族区间用于对照核对与缺档参考。

---

## 5. 记录一次人工修正（让系统学）

```powershell
python D:\NX_AI\core\feedback.py record --op drilling --material N --tool D11 --diameter 11 `
    --set rpm=1800 --set fz_mm_per_tooth=0.07 --why "薄壁让刀，现场降速"
```

**`--set` 只接受这 7 个键**（实测 `OVERRIDABLE`）：
```
ae_mm, ap_mm, coolant, feed_mm_min, fz_mm_per_tooth, rpm, vc_m_min
```
写别的键会直接报错 `ValueError: 不可覆盖字段` —— 这是设计如此（防止拼错键名静默失效）。

查询与冲突检查：
```powershell
python D:\NX_AI\core\feedback.py lookup --op drilling --material N --tool D11 --diameter 11
python D:\NX_AI\core\feedback.py list
python D:\NX_AI\core\feedback.py conflicts
```

---

## 6. 让经验沉淀下来（飞轮：归并 → 审批 → 发布）

人工修正先变成**事件**，需要归并成**候选**，审批发布后才生效。

```powershell
# 看现状（只读，不会建库）
python D:\NX_AI\tools\flywheel_merge.py stats
```
真实输出（库不存在时）：
```
飞轮库不存在：D:\NX_AI\状态\feedback.db
  （还没有任何经验；先用 feedback.py record 采集修正，再跑 run --write）
```

```powershell
# 预演归并（默认只读！）
python D:\NX_AI\tools\flywheel_merge.py run

# 真正落库
python D:\NX_AI\tools\flywheel_merge.py run --write
```
真实输出：
```
  扫描事件 4 条 → 分组 1 个 → 候选 1 个
  新候选 8d796f2e  支持=4 置信=1.0  建议={'rpm': 1800, 'fz_mm_per_tooth': 0.07}
  已写入 1 个候选（状态 pending，需 approve 后才能 publish）
```

```powershell
python D:\NX_AI\tools\flywheel_merge.py list
python D:\NX_AI\tools\flywheel_merge.py approve --id 8d796f2e
python D:\NX_AI\tools\flywheel_merge.py publish --id 8d796f2e
python D:\NX_AI\tools\flywheel_merge.py rollback --id 8d796f2e   # 回滚，事件释放回未归并
```

> **状态机是硬的**：对 `pending` 直接 `publish` 会被拒绝（退出码 1）。
> 必须 `approve` → `publish`。

---

## 7. 云端长期记忆

```powershell
# 查看云端 Agent 记忆现状（只读）
python D:\NX_AI\core\agent_memory.py show
```
真实输出：
```
Agent 记忆现状  https://ldeh.asia/api/v1
  blocks=32  CORE=1370 字符  USER=811 字符  合计=2181 字符（预算上限 2700）
  我们的块: 1 条（★标记）
```

```powershell
# 双向同步（幂等）
python D:\NX_AI\core\agent_memory.py pull
python D:\NX_AI\core\agent_memory.py push
```

```powershell
# 一键备份到本地
python D:\NX_AI\tools\cloud_backup.py --health    # 只探云端健康
python D:\NX_AI\tools\cloud_backup.py --list      # 列本地快照
python D:\NX_AI\tools\cloud_backup.py --no-db     # 执行备份（PostgreSQL 没跑时必须带 --no-db）
```
真实输出：
```
[云端健康] ✓  HTTP 200  {"status":"ok","version":"0.1.0"}  ←  https://ldeh.asia/api/v1/health
本地备份快照 2 份（D:\NX_AI\backup\cloud）:
  20261017_214215      60.5 KB  云端=https://ldeh.asia/api  DB=无
```

**改云端地址**（唯一入口，代码里无硬编码）：
```powershell
python D:\NX_AI\tools\cloud_backup.py --set-url https://新地址/api
```

**每天自动同步 + 备份**：双击 `D:\NX_AI\tools\install_daily_task.bat`（弹 UAC 点"是"），
注册每天 03:10 的 `NXAI_CloudSync_Daily` 任务，日志在 `C:\Users\Administrator\.nxai\sync.log`。

---

## 8. 自建云服务（nxai-cloud）

部署在服务器上，**只监听 127.0.0.1:8790**，对外经 nginx 的 `/nxai/` 反代。

```powershell
# 外网探活（返回 JSON 才算通，只看 200 会被静态首页骗）
Invoke-WebRequest https://ldeh.asia/nxai/v1/healthz -UseBasicParsing | Select-Object -ExpandProperty Content
```
应得到：
```
{"ok": true, "service": "nxai-cloud", "pending_jobs": 0}
```

服务器侧：
- 代码 `C:\nxai-cloud\app\{server,auth,store}.py`
- 任务库 `C:\nxai-cloud\data\nxai_cloud.db`
- 管理员密钥 `C:\nxai-cloud\app\data\admin_key.txt`（**勿外传**）
- 任务白名单：`report_versions`, `export_drawings`

安全设计：HMAC-SHA256 请求签名 + nonce 防重放 + 每设备独立密钥 + TLS 指纹固定 + **无 shell 通道**。

本地自测（离线）：
```powershell
python D:\NX_AI\tests\test_p1_06_cloud.py        # 40 项集成测试（真实端口）
python D:\NX_AI\cloud\server\server.py --self-test
```

---

## 9. 提取零件现有 CAM 工序（cam_extract）

零件里已有的真实工序（型腔铣/钻孔/攻丝…）是规则引擎最好的对照样本。提取是**只读**的：不 Commit、不改零件、不保存。

**必须在 NX 里跑**（经 nx_launch 启动 headless journal，约 18~20 秒）：

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\core\nx_launch.ps1 `
    -Version 2412 -Mode journal `
    -Journal D:\NX_AI\tools\cam_extract.py `
    -JournalArgs "C:\Users\Administrator\Desktop\UG插件\后盖铝板-LQD80-镜像-175电机.prt" `
    -LogDir D:\NX_AI\状态\artifacts\_extract -TimeoutSec 300
```
> 参数就是**零件 .prt 的裸路径**（不带 `--part`）。成功标记：stdout 出现 `CAM_EXTRACT OK ops=20`。

产物：
- `D:\NX_AI\状态\cam_ops.jsonl` —— 每道工序一条：名称/类型/刀路长度/切削时间
- `D:\NX_AI\状态\cam_ops_report.md` —— 人看的汇总表（按类型分组）

> ⚠ 报告里的"推算平均进给"是 `切削长度 ÷ 切削时间` 的**推算值**，不是机床设定值，只能参考。
> 要读**真实转速/进给**，用下一条。

### 9.1 读真实转速与进给（cam_read_params）

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\core\nx_launch.ps1 `
    -Version 2412 -Mode journal `
    -Journal D:\NX_AI\tools\cam_read_params.py `
    -JournalArgs "--part C:\Users\Administrator\Desktop\UG插件\后盖铝板-LQD80-镜像-175电机.prt" `
    -LogDir D:\NX_AI\状态\artifacts\_params -TimeoutSec 300
```

产物：
- `D:\NX_AI\状态\cam_ops_params.jsonl` —— 每工序：rpm / feed_mm_min / feed_unit / source_method / status
- `D:\NX_AI\状态\cam_ops_params_report.md` —— 按工序类型分组的汇总（含攻丝螺距自校验）

本零件实测：ok=18 / partial=2 / unavailable=0。partial 的 2 道是 `SpindleRpmToggle=0`
（转速输出未启用），转速如实留空，不拿占位 Value 冒充实测值。

> 读取入口是 **builder 只读通路**：`Create*Builder(op)` → `FeedsBuilder.SpindleRpmBuilder.Value / FeedCutBuilder.Value`，
> 且必须先 `CreateCamSession()`。这套入口是 P5-02 探明的，细节见台账 §40。

---

## 10. 真实工序 vs AI 工序单对账（cam_reconcile）

提取出真实工序后，最有价值的用法是**拿真实工艺员的做法校验 AI 工序单**。

**纯 Python，不需要 NX**（前置：先有第 9 章的 `cam_ops.jsonl` 和第 4 章的 `plan_*` 目录）：

```powershell
python D:\NX_AI\tools\cam_reconcile.py
# 或指定工序单目录（缺省自动取最新的 plan_*）
python D:\NX_AI\tools\cam_reconcile.py --plan D:\NX_AI\状态\artifacts\plan_20261018_105512
```

真实输出（2026-10-18 本机实测）：
```
真实钻的直径 : φ5.00、φ6.80、φ7.00、φ8.50、φ9.80、φ10.00、φ11.00
我们建议钻的 : φ1.71、φ5.00、φ5.64、φ6.00、φ6.80、φ7.00、φ8.50、φ10.00、φ11.00、φ11.50
命中         : φ5.00、φ6.80、φ7.00、φ8.50、φ10.00、φ11.00
真实有我们没 : φ9.80  <-- 补刀清单
我们有他没钻 : φ1.71、φ5.64、φ6.00、φ11.50
真实攻丝     : M8×1、M8×1.25
AI 攻丝      : 0 道
```

产物：`D:\NX_AI\状态\cam_reconcile.md`；退出码：0 正常 / 2 缺输入（会先提示去跑 cam_extract / propose_plan）。

**三个结论区怎么读**：
- **命中** = 真实程序钻的 ∩ AI 单建议钻的 —— 越大说明规则引擎越贴近实战
- **真实有我们没（missed）** = 真实要钻、我们库里没有吻合刀具 —— **直接就是补刀清单**，交给采购/工艺
- **我们有他没钻（extra）** = 我们建议、他没钻 —— 可能是把非孔特征当了孔，值得回看 P4-07 的孔分类结果

> 报告只读对账：不做任何工艺决策，也不修改数据。真实程序是参照，不是唯一正确答案。

---

## 11. 创建 CAM 工序（cam_create_op）

在 headless 模式里建钻孔工序、选孔、写转速进给、生成刀路、另存副本。**配方全部来自实测**（台账 §38）。

**必须在 NX 里跑**（约 18~20 秒）：

```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\core\nx_launch.ps1 `
    -Version 2412 -Mode journal `
    -Journal D:\NX_AI\tools\cam_create_op.py `
    -JournalArgs "--part C:\Users\Administrator\Desktop\UG插件\后盖铝板-LQD80-镜像-175电机.prt --name P501_DRILL10 --plan D:\NX_AI\状态\artifacts\plan_20261018_104705\process_plan.json --hole-dia 10 --generate --save-as D:\NX_AI\状态\artifacts\_p501\后盖铝板_p501f.prt" `
    -LogDir D:\NX_AI\状态\artifacts\_p501 -TimeoutSec 300
```
真实输出：
```
CAM_CREATE_OP OK ops=20->21 delta=1 op=HoleDrilling
```

**反直觉的关键点（20 种组合里唯一可行的配方）**：
- `type = "hole_making"`（**小写**）、`subtype = "DRILLING"`（**大写**）—— 其它 19 种一律报
  `NXException: The desired template does not exist`
- 必须先 `Session.CreateCamSession()`，否则 `A CAM session does not exist`
- 第 3 个参数 tool 不能是 None（本零件无刀具组，用 `CreateDrillStdToolBuilder(...).Commit()` 造占位）
- `CAMOperationCollection.Create(...)` 返回的是**工序对象本身**，不是 builder，没有 `.Commit()`

**参数**：

| 参数 | 说明 |
|---|---|
| `--part` | 零件 .prt（必填） |
| `--name` | 新工序名，默认 `P4-01_TEST` |
| `--plan` | propose_plan 工序单 JSON —— **孔径与切削参数的唯一来源，绝不自创工艺数值** |
| `--hole-dia` | 目标孔径；缺省自动选工序单里第一个带 params 的钻孔 |
| `--generate` | 建完后 `GenerateToolPath` |
| `--save-as` | 把结果另存为副本（**绝不覆盖原文件**） |
| `--verify-only` | 只读重开零件数 ops 做持久化验证，零副作用 |
| `--expect-ops` | verify-only 期望的 ops 数 |

**退出码**：0 成功 / 2 参数错误 / 1 NX 侧失败。
> 已知行为：run_journal 会把 Python 的 `sys.exit(0)` 报成 `SystemExit: 0`，使子进程 exitcode=1。
> **成功判定只看 stdout 标记**：`CAM_CREATE_OP OK` 或 `VERIFY_ONLY ... persist_ok=True`（台账 §39 已记录，待修）。

**安全约定：默认不保存零件。** 只有显式给 `--save-as` 才写副本，且会自动对比原零件的
mtime/大小，证明未动原文件。持久化验证用独立进程 `--verify-only`（session 内 close→reopen
已判定不可用：`CloseAll` 传枚举会触发 NX 进程级 fatal，代码里已禁用）。

持久化验证（只读，2026-10-18 本机实测）：
```powershell
powershell -NoProfile -ExecutionPolicy Bypass -File D:\NX_AI\core\nx_launch.ps1 `
    -Version 2412 -Mode journal `
    -Journal D:\NX_AI\tools\cam_create_op.py `
    -JournalArgs "--part D:\NX_AI\状态\artifacts\_p501\后盖铝板_p501f.prt --verify-only --expect-ops 21" `
    -LogDir D:\NX_AI\状态\artifacts\_p503 -TimeoutSec 300
```
真实输出：
```
CAM_CREATE_OP VERIFY_ONLY ops=21 expect=21 persist_ok=True
```

**当前缺口（如实）**：
- 刀具**真实几何** unavailable：占位 Commit 产物是 NCGroup 不是真 Tool；DrillStdToolBuilder 是纯提交壳，
  三层（直属/子 builder/customizable item）都无直径写入口。下一步：用 grpcoll 上 38 个刀具工厂建真刀具
- 孔选择按直径匹配（P4-07 口径：圆柱面 + 闭合整圆 + 边长/π ≈ 目标 ±0.03mm），φ10 命中 8/12 个面
- `ap` 对钻孔是 CycleTable 每啄深度语义，CutParameters 无 Depth 成员，未做映射（如实 unavailable）
- 铣削类工序创建未实现

---

## 12. 改任何代码之前：两道门禁

```powershell
python D:\NX_AI\tools\check_py_syntax.py
```
真实输出：
```
扫描 108 个 .py   语法错误 0 个文件/0 处   预警 0 个文件/0 处
全部通过：无语法错误、无非 raw 路径写法、无多余 BOM
```
> 它专治三类历史事故：**非 raw 字符串里的 `\N`/`\U`**、多余 BOM、缺 import。改完必须过。

```powershell
python D:\NX_AI\tests\run_eval.py
python D:\NX_AI\tests\run_eval.py --strict-drift    # 任何漂移即非零退出
```
真实输出：
```
防退化评测  通过 21/21   已知未修 0   意外退化 0
特征化基线：36 个上下文，漂移 0 处
```

---

## 13. 故障排查（都真实遇到过）

| 现象 | 原因与处理 |
|---|---|
| `nxai run` 秒退、`tasks.db` 里任务卡在 `pending` | 回写状态失败。历史根因是 `python -c` 拼接路径导致 SyntaxError，打断脚本。现已改为 `core\task_update.py` 参数传值。看 `状态\<id>\task.log` |
| 控制台中文乱码 / 打印崩 | 所有入口已内置编码护栏；仍有问题设 `PYTHONIOENCODING=utf-8` |
| `feedback record` 报"不可覆盖字段" | `--set` 键名不在 7 个允许键内（见第 5 节） |
| 记忆同步报 URLError | 先 `cloud_backup.py --health`；云端地址用 `--set-url` 改 |
| 改动后测试失败在"入口含护栏" | 新增的 `.py` 入口缺少字面量 `P2-02 控制台编码护栏`，补上即可 |
| 跑完测试/演示后多出 `状态\feedback.db` | 说明有脚本没隔离飞轮库；用 `NXAI_FLYWHEEL_DB` 指到临时库，或 `NXAI_NO_FLYWHEEL=1` 关闭 |
| `.bat` 双击后 cmd 一闪而过、一条命令都不执行 | 文件被写成了 **LF 换行**，cmd 解析 `.bat` 需要 CRLF。判据——看前几字节有没有 `13 10`（即 0D 0A）：`(Get-Content D:\NX_AI\tools\install_daily_task.bat -Encoding Byte -TotalCount 16) -join ' '`；只见 `10` 不见 `13` 就是它。修法：编辑器另存为 **CRLF** 换行 |
| 用 edit 类工具改完 `.ps1` 报"< 运算符是为将来使用而保留的"/中文乱码 | 这类工具保存时**弄掉了 BOM**，PS 5.1 按 ANSI 读、中文吞掉引号。判据——前 3 字节必须是 `EF BB BF`：`(Get-Content D:\NX_AI\nxai.ps1 -Encoding Byte -TotalCount 3) -join ' '` 应输出 `239 187 191`；不是就按 UTF-8 with BOM 重写（本工程所有 `.ps1` 都带 BOM） |
| journal 里 `--dry-run` 这类开关"传了跟没传一样"（静默失效） | `run_journal` 会把 `-args` 后面的所有参数**压成一个字符串**传给 journal；journal 入口解析必须先合并、剥引号、再按空白拆，否则整串被当成一个参数（P4-01 修过这个 bug，新 journal 脚本照此处理） |
| 建工序报 `NXException: The desired template does not exist` | type/subtype 大小写是**硬**的：必须 `type="hole_making"`（小写）+ `subtype="DRILLING"`（大写）。台账 §38 实测其它组合一律这个错——先对大小写，别试排列组合 |
| 建工序报 `Input parameter is out of range`，怎么改模板名都没用 | 这个错会**掩盖**真正的模板错误：`Create(type, subtype, tool, …)` 的 tool 传 None 时先炸参数校验。先解决 tool（零件无刀具组时用 `CreateDrillStdToolBuilder(...).Commit()` 造占位），再看剩下的报错 |
| `flywheel_merge` 把不同直径的修正并成一组（如 φ5 与 φ10 钻头同组） | 相似度 = 0.5×键集合 Jaccard + 0.5×键值均相似，默认 threshold=0.8 时键集合全同白送 0.5 分，直径差被稀释（实测 0.85≥0.8 照并）。修法：调用侧显式传 `--threshold 0.9`；**不要改 `memory\flywheel.py` 的默认值**——评测基线依赖它 |
| 服务器上"2 个 uvicorn 进程" | **正常**，是 `main.py` 的设计（venv 父进程派生系统 Python 子进程）。不要去杀 |

### 红线（不要做）

- 不要写系统/用户级 NX 环境变量（只用进程级）；不要设 `UGII_VENDOR_DIR` 或 `UGII_LOCAL_USER_DEFAULTS`
- 不要改 `E:\Program Files\Siemens\NX*` 下的任何文件
- 不要用带 `-Apply / -Force / -Kill` 之类开关的脚本去清理，除非明确知道后果
- 不要在没跑两道门禁的情况下提交改动

---

## 14. 目录速查

```
D:\NX_AI\
├─ nxai.ps1                 单入口（list / run / verify）
├─ core\                    nx_versions / nx_launch / task_engine / task_update
│  ├─ feedback.py           L2 人工修正采集与精确复用
│  ├─ propose_plan.py       L1 规则 + L3 归并经验 → 工序单
│  └─ agent_memory.py       云端 Agent 记忆双向同步
├─ data\rules\              rule_engine.py + ai_programming_rules_v2.json（含 RL-001~009 红线）
├─ data\std\                tools.json / materials.json / parameters.json
├─ memory\                  store.py（本地记忆）/ flywheel.py（经验飞轮）
├─ tools\                   demo_end_to_end / flywheel_merge / cloud_backup / model_facts / hole_diameter
│                           cam_extract / cam_read_params / cam_reconcile / cam_create_op …
├─ cloud\{server,client}\   自建云服务与 worker
├─ tests\                   16 个测试文件（含金标防退化）
├─ 状态\                    tasks.db / artifacts / *_report.json / *.done.json
└─ docs\                    台账（10_状态台账.md）/ 本手册 / 能力清单.md / 各阶段落地文档
```

**遇到不确定的事，先看 `D:\NX_AI\docs\10_状态台账.md`** —— 里面按时间记了每个决策、每个踩过的坑和"不要再重复做"的清单。


## 15. 一条命令跑完整流水线（nxai pipeline）

> 前面各章是"单个环节"的用法；这一章把它们串成一条命令，适合日常跑一遍看全局。

```powershell
# 只跑纯 Python 段（不启动 NX，约 5 秒）
powershell -File D:\NX_AI\nxai.ps1 pipeline

# 加跑 NX 提取与参数读取（约 45 秒）
powershell -File D:\NX_AI\nxai.ps1 pipeline -WithNx

# 再加建工序 + 生成刀路（约 65 秒；结果写副本，不动原零件）
powershell -File D:\NX_AI\nxai.ps1 pipeline -WithNx -CreateOp

# 指定材料组与工序序列
powershell -File D:\NX_AI\nxai.ps1 pipeline -Material N -Operations "face_milling,roughing,finishing"
```

阶段说明：

| 阶段 | 内容 | 需要 NX | 开关 |
|---|---|---|---|
| [1] | 提取零件现有 CAM 工序 | 是 | `-WithNx` |
| [2] | 读真实 rpm/feed | 是 | `-WithNx` |
| [3] | 真实 vs 引擎对账 | 否 | 总是 |
| [4] | 出工序单 | 否 | 总是 |
| [5] | 建工序 + 生成刀路（写副本） | 是 | `-CreateOp` |
| [6a] | 语法门禁 | 否 | 总是 |
| [6b] | 评测门禁 | 否 | 总是 |

> 门禁拆 6a/6b 两步执行，所以全量跑时执行记录共 **7 条**（第 2 章说的"7 个阶段"即此口径）。

产物：`状态\pipeline_report.md`（每个阶段的通过/失败与关键数字）。
退出码：0 全通过；1 有阶段失败（可直接挂到定时任务里做体检）。

**安全约定**：不加 `-WithNx` / `-CreateOp` 就完全不启动 NX；建工序一律写到
`状态\artifacts\pipeline_<时间戳>\part_with_op.prt` 副本，**绝不覆盖原零件**。

---

## 16. 授权（序列号 / 一号一机 / 按月）

### 16.1 规则

| 项目 | 说明 |
|---|---|
| 一个序列号 | 授权**一台**电脑（首次激活绑定机器指纹） |
| 有效期 | 30 天，从**首次激活**起算；到期续费换新号 |
| 联网时 | **全部功能**可用 |
| 离线时 | **只能**用：建刀具、建工序、程序组（纯本地计算） |
| 需要联网的功能 | 出工序单、真实工序对账、云端记忆、学习提升 —— 这些依赖云端 |
| 授权到期后 | **离线功能也一并失效**（无宽限期）：到期后即使断网前能用的三个功能，也会要求联网续期一次才能继续（`core\license.py` 中 `GRACE_DAYS=0`，实测断网时报"授权已到期或票据无效，需要联网续期"） |
| 换机 | 需管理员在服务端重置绑定，否则新机激活被拒 |

> 为什么离线受限：出工序单要读云端积累的工艺记忆、对账要拿云端基准、学习要把修正回传。
> 这些能力离线无法提供，所以只在联网时开放。纯本地的"建刀/建工序/程序组"离线照常可用。

### 16.2 激活

图形界面（推荐）：双击 `NXAI.exe` → 点「授权」→ 粘贴序列号 → 激活。

命令行：

```powershell
powershell -File D:\NX_AI\nxai.ps1 license activate NXAI-XXXXX-XXXXX-XXXXX
powershell -File D:\NX_AI\nxai.ps1 license status
```

`license` 动词把参数原样透传给 `core\license.py`（支持 status / activate / verify / hwid）。真跑 `license status` 输出（2026-10-18）：

```
============================================================
  NXAI 授权状态
============================================================
  状态    ：active
  序列号  ：NXAI-FJDGD-DBAPA-D62FA
  到期    ：2026-11-17T11:21:49+00:00
  剩余    ：29 天
  机器指纹：3b89be8760e4b719cf69009a20145dca
  强制拦截：是
  状态文件：C:\Users\Administrator\AppData\Local\NXAI\license.json
```

### 16.3 票据与防篡改

授权台账在 `%LOCALAPPDATA%\NXAI\license.json`，其中的到期时间由**服务端 RSA 私钥签名**，
客户端用公钥离线验签：

- 改本地 JSON 的到期时间 → 验签失败 → 视为无效，必须联网重新激活
- 客户端没有私钥 → 伪造不出新票据
- 系统时间回拨 → 检测到即要求联网

### 16.4 常见问题

**Q：断网后还能做什么？**
A：授权有效期内，建刀具、建工序、程序组可用（示例见 16.5）；出工序单/对账/学习会提示"该功能需要联网使用"。**授权到期后离线功能同样失效**，需联网续期一次。

**Q：提示"授权已到期或票据无效"？**
A：联网一次即可续期；如果已经续费，重新激活新序列号。到期后连离线的三个功能也会被拦，属正常。

**Q：换电脑/重装系统后提示"已绑定其它电脑"？**
A：机器指纹变了。联系管理员在服务端把该号的 `hwid` 清空，再在新机激活。

**Q：把系统时间改回去能延长使用吗？**
A：不能。判定以签名票据为准，且时钟回拨会被检测。

### 16.5 离线能做什么（真跑示例，断网模拟）

模拟断网的方法：把 `config\cloud.json` 里 `cloud.base_url` 改成一个连不上的地址
（如 `http://127.0.0.1:1/api`），授权与云端功能立即走"断网"分支；跑完改回来。
本节两条命令均在 2026-10-18 用此方法真跑过（改前备份 + 改后 SHA256 比对恢复一致）。

**① 需要联网的功能：pipeline 会被授权门禁拦下（退出码 3）**

```powershell
powershell -File D:\NX_AI\nxai.ps1 pipeline
```

断网时输出：

```
  ✗ 【pipeline】该功能需要联网使用（依赖云端数据记忆与能力提升）。
  离线时只能用：建刀具、建工序、程序组。
  机器指纹：3b89be8760e4b719cf69009a20145dca
  详情：连不上授权服务器：
    http://127.0.0.1:1/nxai/v1/license/verify -> URLError
    http://127.0.0.1:1/api/v1/license/verify -> URLError
```

**② 离线功能：建程序组照常可用（真跑 NX2412，19.5 秒）**

```powershell
powershell -File D:\NX_AI\tools\run_journal_probe.ps1 -NxRoot "E:\Program Files\Siemens\NX2412" -Journal "D:\NX_AI\tools\cam_build_local.py" -JournalArgs "--part <你的零件.prt> --what group --name NXAI_OFFLINE_TEST" -LogDir "D:\NX_AI\状态\artifacts\_build_local"
```

断网时输出：

```
  [license] 已授权（离线可用功能：program_group；授权剩 29 天）
CAM_BUILD_LOCAL group OK NXAI_OFFLINE_TEST
CAM_BUILD_LOCAL OK groups=7->8
```

建刀具同通路，把 `-JournalArgs` 换成 `--part <零件.prt> --what tool --name NXAI_D12 --diameter 12 --kind mill`（P7-02 真跑：Tool 0→1、读回直径 12.0）；建工序走 `tools\cam_create_op.py`，同为离线白名单。

> 注意两点：`cam_build_local.py` 是 NX journal 脚本（依赖 NXOpen），**不能直接用桌面
> Python 跑**，必须走 `run_journal_probe.ps1` 通路；`--part` 是必填参数。

### 16.6 管理员操作（发号 / 查号 / 吊销 / 重置绑定）

> 这一节只给**软件所有者**看，客户不需要。

管理员口令在服务器：`C:\nxai-cloud\app\data\admin_key.txt`（64 字符，不进安装包）。

#### 方法一：图形发号工具（推荐）

双击 `<安装目录>\tools\发号工具.bat`，或者：

```powershell
python D:\NX_AI\tools\nxai_admin.py
```

界面上：

1. 「管理员口令」粘贴服务器的 `admin_key.txt` 内容（填一次会自动记住在本机
   `%LOCALAPPDATA%\NXAI\admin_key.txt`）
2. 填数量 / 天数（默认 30）/ 备注
3. 点「批量发号」→ 下方列表出现新序列号，直接复制给客户
4. 点「导出 CSV」→ Excel 双击打开，含序列号/签发/激活/到期/机器指纹/是否吊销

换客户电脑时：在列表里选中旧号 → 「吊销选中」→ 再发一个新号给他。

#### 方法二：命令行

```powershell
# 第一次：把管理员口令存到本机
python D:\NX_AI\tools\nxai_admin.py --cli setadmin --key <64位口令>

# 发 10 个号（30 天），并导出全部号的 CSV
python D:\NX_AI\tools\nxai_admin.py --cli issue --count 10 --days 30 --note 第一批 --csv D:\序列号.csv

# 查看全部
python D:\NX_AI\tools\nxai_admin.py --cli list

# 吊销
python D:\NX_AI\tools\nxai_admin.py --cli revoke --serial NXAI-XXXXX-XXXXX-XXXXX
```

#### 方法三：直接调云接口

```bash
POST https://ldeh.asia/nxai/v1/license/issue
{"count":10, "days":30, "note":"第一批", "admin":"<admin_key>"}

POST /v1/license/list   {"admin":"<admin_key>"}
POST /v1/license/revoke {"serial":"NXAI-...", "admin":"<admin_key>"}
```

#### 换机 / 重置绑定

- 最省事：**吊销旧号 + 发新号**（用户在新机激活即可）
- 或直接把该序列号的 `hwid` 置空，用户可在新机重新激活同一号

## 17. 自动派卡（开发者自用）

> 给软件作者本人用：定时把 `queue\*.txt` 里还没写的卡自动喂给 Trae。
> 客户用不到这一节。

### 17.1 它做什么

每轮扫描 `D:\NX_AI\queue\*.txt`，凡是没有对应 `D:\NX_AI\状态\<卡名>.done.json`
的就算"待派卡"，调用 `tools\trae_feeder.ps1` 派出去；连续失败 3 次自动停止；
日志写在 `D:\NX_AI\logs\auto_dispatch.log`。

### 17.2 怎么开

双击 `tools\install_auto_dispatch.bat`（右键→以管理员身份运行，确认 UAC）。
它注册计划任务 `NXAI_AutoDispatch`：每 30 分钟跑一次
`auto_dispatch.ps1 -MaxRounds 5 -IntervalSec 300`（每次最多 5 轮、轮间隔 5 分钟，
一个进程最多存在 25 分钟，不会叠层）。

先手动验一遍再注册（DryRun 只打印不派卡）：

```powershell
powershell -File D:\NX_AI\tools\auto_dispatch.ps1 -MaxRounds 1 -DryRun
```

2026-10-18 验证输出（真实队列，当时只有 P8-03 一张卡没写完）：

```
--- auto dispatch start ---
params: MaxRounds=1 Interval=120 DryRun=True
round 1: pending 1 : P8-03
DryRun: would call feeder
--- auto dispatch end ---        （退出码 0）
```

队列空的分支同日验证：`round 1: queue empty`，退出码 0；
feeder 脚本缺失的分支：`FAIL feeder not found ...`，退出码 2（防呆）。

### 17.3 怎么停

```bat
schtasks /delete /tn NXAI_AutoDispatch /f
```

（或卸载时直接删除计划任务；`install_auto_dispatch.bat` 每次运行都会先删旧任务再注册。）

### 17.4 日志

`D:\NX_AI\logs\auto_dispatch.log`——每轮的待派清单、feeder 返回码、失败连击数
都在里面；`feeder rc=0` 表示该轮派卡成功。排查顺序：先看这个文件，再去看
Trae 那边有没有收到卡。

---

## 17. 远程派活（云 -> 目标电脑 -> Trae）

### 17.1 它能做什么

在任何地方往指定电脑派一张任务卡，那台电脑上的 Trae 自动接收并执行，结果回传云端可查。

实测：从入队到 Trae 完成共 **96 秒**。

### 17.2 链路

```
POST /v1/agent/enqueue  {card_id, card, admin}
   -> 目标电脑运行 tools\trae_agent.py（常驻或 --once）
   -> 代理 POST /v1/agent/next（设备令牌）领活
   -> 写 queue\<card_id>.txt -> 调 tools\trae_feeder.ps1 投给 Trae
   -> Trae 执行后写 状态\<card_id>.done.json
   -> 代理 POST /v1/agent/result 回传
   -> 任意地方 POST /v1/agent/list 查看
```

### 17.3 硬性前提（必须满足，否则任务会超时）

- 目标电脑**开机且桌面未锁屏**（Trae 无 API，只能模拟点击）
- **Trae 已启动**且窗口可聚焦
- 一次只跑一个任务
- 能访问 https://ldeh.asia

### 17.4 卡片的写法要点

任务卡必须**要求 Trae 写出 done.json**，否则代理会一直等：

```text
【任务 AGENT-02】闭环测试
完成后必须创建文件 D:\NX_AI\状态\AGENT-02.done.json，内容为：
{"status":"done","verify":"...","notes":["..."]}
不要修改任何源代码。
```

### 17.5 命令

```powershell
# 目标电脑：常驻代理
python D:\NX_AI\tools\trae_agent.py --transport cloud

# 只跑一次（便于验证）
python D:\NX_AI\tools\trae_agent.py --transport cloud --once

# 本地闭环自测（不调用 Trae）
python D:\NX_AI\tools\trae_agent.py --selftest
```

日志：`状态\trae_agent.jsonl`（每次 job_start / job_done 都有记录）

### 17.6 鉴权

| 用途 | 密钥 | 位置 |
|---|---|---|
| 派活 / 查看 / 吊销 | admin_key | 服务器 `data\admin_key.txt` |
| 代理领活 / 回传 | 设备令牌 | 服务器 `data\agent_token.txt`，本机 `config\device_token.json` |
---

## 18. 图形工作台（NXAI工作台.exe）

日常操作的图形界面，安装包自带（与 NXAI.exe 并列在安装目录根下），双击即用。
NXAI.exe 仍只做安装/升级/授权；工作台只做日常操作，两者互不影响。

### 18.1 按钮说明

| 按钮 | 干什么 | 注意事项 |
|---|---|---|
| ① 出工序单 | 调 core\propose_plan.py 生成工序单，自动用记事本打开，并把每道工序的 刀具/S/F/ap/ae 填进下方「参数摘要」区 | 摘要区只读、带滚动条 |
| ② 建刀具 | 按上方 刀具名/直径 在零件里建一把铣刀（cam_build_local.py --what tool） | 会启动 NX，先关掉已打开的 NX |
| ③ 建程序组 | 建 NC 程序组（--what group） | 同上 |
| ④ 建工序+刀路 | cam_create_op.py 建工序并尝试生成刀路，结果写副本 | 不动原零件 |
| ⑤ 检查环境 | nxai.ps1 list + 授权状态 | 只读 |
| ⑥ 检查更新 | nxai.ps1 check-update | 需联网 |
| ⑦ 参数微调 | 按 S×/F× 倍率（如 0.8 / 1.2）缩放最新工序单每行 S/F，写**临时覆盖**（sheet_feedback.py mul-ov -> 状态\overrides\<零件>.json） | 不改全局预设；重新点 ① 看效果；清除用 clear-ov |
| ⑧ 批量建钻孔 | 读 状态\hole_diameter.jsonl 最新分类记录，取前 3 个孔径（沉台/凸台不算孔），逐个调 cam_create_op.py 各建一道钻孔工序，每道一个副本 | 会启动 NX；执行前有确认框；jsonl 不存在会提示先生成 |
| ⑨ 批量常用工序 | 按 7-17.prt 实测可用通路批量建常用工序（WORKPIECE 几何幂等补写、孔径反算、面铣不写 op 层几何），每道一个副本 | 会启动 NX；如实报告每道 cut 长度与失败原文 |
| ⑩ 一键准备 | 读当前工序单 → 一次建齐组/刀/工序并生成刀路 → 写桌面「NXAI产物\准备_<零件>_<时间>.prt」副本并自动打开 | 会启动 NX；先做 facts 过期检测；无工序单会提示先点 ①|
| ⑪ 三阶段出程序 | 轮廓粗/半精/精各出一个**独立 NC**（A01_粗/A02_半精/A03_精），每段末强制 M01，半精段末附测量提示 | 会启动 NX；产物进桌面「NXAI产物」；用当前工序单 |
| ⑫ 刀路自检 | **纯离线**（不启 NX）：取桌面「NXAI产物」里最新的 A01/A02/A03 → 跑 tools\nc_check.py 做 7 项自检（行程越界/空程占比+时间估算/刀号合法性/冷却主轴/进给异常/重复刀路/抬刀次数）→ 日志区打摘要表 → 自动打开三联预览 PNG（XY 俯视 + XZ 侧视 + Z 深度直方图） | 有致命告警（越界 / T0 / 缺冷却）会弹框**逐条列出**；结果 JSON 在 状态\artifacts\check_*.json，摘要在 check_summary.txt |
| 机床设置 | 编辑 config\machine.json 的行程（X/Y/Z 上下限）与刀号范围 T1~T20（自检的比对基准） | 保存前自动生成带时间戳的 `.bak`；850 型常用 X800/Y500/Z500 |
| 打开产物目录 | 资源管理器打开 状态\artifacts | |

顶部可切换 NX 版本（2412/2212）、零件路径（浏览...）。

### 18.2 注意事项

- ②③④⑧⑨⑩⑪ 会启动 NX（headless journal）：**先关闭已打开的 NX**，避免抢许可；**⑫ 刀路自检是纯离线的，不启 NX**
- ④⑧⑨ 一律写副本（gui_op_*.prt / gui_drill_*.prt），原零件不动
- ⑦ 的覆盖只影响当前零件；换零件自动失效
- ⑫ 自检的机床基准来自 config\machine.json（默认 VMC850）：X[-420,420] Y[-260,260] Z[-500,100]；对不上就先用「机床设置」改。**自检只做数字核对与肉眼预览，不替代实机空跑**
- ⑫ 的两处口径（如实说明，勿当缺陷）：①「重复刀路」用 5mm 网格，精加工步距/段长常 < 5mm，占比会接近 100% 属**网格饱和**，报告里会打印「切削段 XY 投影中位值 + 短于网格边长的比例」供判断；②「抬刀次数」按 **抬刀/加工格点** 比判定，次数多但相对面积正常时只给提示、不判"刀路碎"
- 无界面自检：`NXAI工作台.exe --selftest`（验证孔径解析与摘要解析，写 状态\artifacts\gui_selftest.log）；离线自检也可直接跑 `python tools\nc_check.py <nc文件...>`

### 18.3 后处理三套选型（P10-10，FANUC 双转台厂家后处理）

三套厂家后处理已随包放进 `nx_post_lib\fanuc_dual_table\`（3A/4A/5A 各含 .cdl/.def/.pui/.tcl）：

| 模板名 | 用途 | 关键差异（tcl 实测原文） |
|---|---|---|
| NXAI_3AX_FANUC | 纯三轴 | 4th 轴行程 [-120,0]，程序头无四/五轴标签 |
| NXAI_4AX_FANUC | 四轴（A 轴） | 4th 轴行程 [-360,360]，程序头输出「( 本程序是用*四轴*后处理 : ...)」 |
| NXAI_5AX_FANUC | 五轴 | 4th [-120,0] / 5th [-360,360]，程序头输出「( 本程序是用*五轴*后处理 : ...)」 |

三套共同参数：4th 轴 leader=A、绕 X（vector=1,0,0）、plane=**YZ**、Table；5th 轴 leader=C、plane=XY、Table；machine_type=5_axis_dual_table。

- **选型**：`python tools\post_selector.py --pick --part <零件.prt>` —— 按该零件工序单是否含旋转轴工序自动建议 3A/4A/5A，结果写 `状态\post_select.json`；可用 `--ops-json` 显式给工序数据，`--force-4a` 等强制指定。三轴零件被选成 4A/5A（或反之）会在日志警告。
- **校验**：`python tools\post_selector.py --check <某套.tcl>`（plane↔vector/leader/limits/type 自洽，exit 0=自洽）；`--list` 列三套参数摘要并比对 MANIFEST 的 12 个 SHA256。
- **出程序选用**：三阶段出程序自动按 `post_select.json` 的选型结果选用后处理（匹配零件名），也可 `--post-template NXAI_4AX_FANUC` 显式指定；缺省仍为 NXAI_3AX。
- **注册（实测结论，重要）**：NX 解析后处理名字**只认 NX 安装目录** `MACH\resource\postprocessor\template_post.dat`（`UGII_CAM_POST_DIR` 仅用于展开条目里的文件路径；往 `D:\NX_AI\nx_post\template_post.dat` 加新名字用 run_journal 验证不被解析）。因此要在 NX 里用这三套，必须**以管理员运行 `tools\post_register.bat`**（备份→复制 12 文件→幂等追加三条→回读校验）——按项目硬约束该步走**人工执行清单**，不由自动化脚本代跑。`tools\post_register_local.py` 维护的 `D:\NX_AI\nx_post\` 下 12 个文件 + 本地 dat 是预注册副本（journal 流程 `${UGII_CAM_POST_DIR}` 的文件落点，注册后条目里的路径即指向这些文件）。**未注册期间**出程序若选了 NXAI_*_FANUC，会自动回退 NXAI_3AX 并在日志警告（不让工序单中断）。
- **A 轴验证**：探针 `tools\probe_post_rotary.py` 把 MCS 绕 X 转 90°（仅会话内、不保存零件）→ 重算刀路 → 后处理 → 在 NC 中搜 A 角度值与程序头标签，结果写 `状态\post_rotary.json`。已实测：MCS 旋转生效（cut 40137→32541mm）、后处理管线可用；`NXAI_4AX`（4Zdbzq，machine_type=4_axis_table、4th plane=ZX——正是卡文铁律指出的错误形态）对绕 X 旋转的刀路**输出不了 A 角**，A 角证据需在 post_register.bat 注册后用 `--template NXAI_4AX_FANUC` 复验取得。
