# NX Open 跨版本兼容矩阵（本机实测，NX2212 / NX2412）

> 全部结论来自 2026-10-16 在**两个真实 NX 版本**上的批处理运行与反射（dir()），
> **不是来自文档**。凡未实测的都标"未证实"。
> 测试零件：`C:\Users\Administrator\Desktop\UG插件\后盖铝板-LQD80-镜像-175电机.prt`（5.9 MB，Brep 收敛体）

## 1. 一句话结论

> **几何量跨版本稳定；对象个数不稳定。判断几何特征必须用面/边/长度，不能用实体数/特征数。**

## 2. 稳定性对照（同一零件、同一脚本）

| 指标 | NX2212 | NX2412 | 是否稳定 |
|---|---|---|---|
| `list(part.Bodies)` 实体数 | **92** | **2** | ❌ **不稳定** |
| 特征数 `list(part.Features)` | 2 | 2 | ✅ 稳定 |
| 内嵌 Python | 3.10.6 | 3.11.9 | — |
| `body.GetFaces()` 面数 | 130 | 130 | ✅ 稳定 |
| `face.SolidFaceType` 分布 | `{2:68, 1:43, 3:19}` | 同 | ✅ 稳定 |
| 每面边数分布 | 完全一致 | 完全一致 | ✅ 稳定 |
| **孔径分布（32 个不同直径）** | 完全一致 | 完全一致 | ✅ **稳定** |
| 孔面计数 | 187 | 187 | ✅ 稳定 |

## 3. API 可用性矩阵

| API | NX2212 | NX2412 | 结论 |
|---|---|---|---|
| `body.GetFaces()` | ✅ | ✅ | 取面（首选） |
| `face.GetEdges()` | ✅ | ✅ | 取边 |
| `edge.GetLength()` | ✅ | ✅ | **取圆边长 → ÷π 得直径** |
| `edge.SolidEdgeType` | ✅ | ✅ | 边类型（返回 MemberType 包装） |
| `face.SolidFaceType` | ✅ | ✅ | 面类型；`str()` 给数字 id |
| `face.FaceType` | ✅ | ✅ | 返回**类** `NXOpen.FaceFaceType`（成员名可用：Cylindrical/Planar/Conical/Convergent/Blending/Spherical/Toroidal? 等） |
| `face.GetHoleData()` | ✅ | ✅ | 返回 `(ResizeHoleData, True)`，字段只有 count/index —— **不是孔径** |
| `face.GetBlendData()` / `GetChamferData()` | ✅ | ✅ | 可用（未深入验证字段） |
| `part.MeasureManager` | ✅ | ✅ | 有 `NewPolarRadius` / `RadialMeasureType` 等，但参数签名复杂（见 §5） |
| `len(collection)` | ❌ TypeError | ❌ | 集合无 `__len__` |
| `collection.ToArray()` | ❌ | ❌ | 不存在 |
| `part.Faces` | ❌ | ❌ | 不存在（只能从 body 取面） |
| `face.Radius` / `face.GetRadius()` | ❌ | ❌ | 不存在 |
| `edge.Radius` / `ArcRadius` / `GetCurveData()` | ❌ | ❌ | 不存在 |
| `body.GetBoundingBox()` | ❌ | ❌ | 不存在（包围盒需另找路径，未证实） |
| `ufs.Modl.AskFaceData` | ❌ | ❌ | UF Python 绑定里没有该名字 |

## 4. 圆柱直径的标准取法（已验证）

```python
d = edge.GetLength() / math.pi      # 完整圆边
# 实测：34.55751918948773 / π = 11.0000000000 → φ11
```
注意：**只对完整圆边成立**。部分圆弧需除以张角；本实现用"直径是否为 0.05 的整数倍"来筛选，
所以直方图里会混入少量非孔值（如 0.59 / 96.00），使用时按"计数≥2 且 1mm≤φ≤40mm"过滤。

## 5. 未解决 / 未证实（留给 P2-05）

1. **实体数 92 vs 2 的根因**未证实。已排除"特征历史不同"（特征完全一致）。
   最可能是 **Brep 收敛体在两个版本下的实体拆分语义不同**。验证方向：
   比较 `body.IsSolidBody`、`body.GetType()`、顶点/边数是否 92 份之和等于 2 份之和。
2. 包围盒的正确取法（`body.GetBoundingBox()` 不存在）——选刀需要工件尺寸。
   候选：`part.MeasureManager.NewRectangularExtreme`、或遍历顶点求 min/max。
3. `MeasureManager.NewPolarRadius` 的正确参数（实测传 Face 报 No overload；
   `InitializePolarRadius` 期望 `NXOpen.Measure`）。**若 ② 的顶点法可行，则无需走这条路。**
4. `face.FaceType` 返回的是枚举**类**，取实例值的方式未证实（可能需 `FaceFaceType.ValueOf(face)`）。

## 6. 探测方法论（重要，避免重复踩坑）

**不要凭文档猜 API 名字**。正确顺序：
1. `dir(obj)` 反射列出真实存在的名字（本轮的突破就是这样拿到的：`GetHoleData` 是反射出来的）
2. 每种候选写法都用 try/except 试一遍，**把成功与失败都记录下来**（写进 inspection 字段）
3. 关键数值必须做**数量级/整数性校验**（例如 34.55751918948773 ÷ π = 11.0000000000 才敢认定是 φ11）
4. 结论必须**在两个版本上各跑一次**再下

工具（均在 `D:\NX_AI\tools\`）：
`api_introspect.py`（反射 API 名）、`hole_probe.py`（面/半径路线）、
`hole_data_probe.py`（GetHoleData 结构）、`radius_route_probe.py`（半径路径穷举）、
`hole_diameter.py`（**正式孔径提取器**）、`model_facts.py`（模型事实提取）