首次提交: OnebotCatalog 项目代码与文档(含 NX 按需生成服务二期、后台、一键启动)
This commit is contained in:
51
sample-data/README.md
Normal file
51
sample-data/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# 欧霓博目录软件 —— 示例数据包(自编,非真实产品数据)
|
||||
|
||||
> 用途:供目录软件(OnebotCatalog)开发与测试。SC 系列为**虚构系列**,几何为简化模型。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
sample-data/
|
||||
├── onb-sc/ # 示例系列主体
|
||||
│ ├── step/ # 72 个 STEP 变体文件(AP203 B-rep,4 圆柱组合)
|
||||
│ ├── params.csv # 参数表(Excel 可直接打开;model_code/bore/stroke/magnet/mount/step_file)
|
||||
│ ├── coding-rules.md # 型号编码规则 + 选型约束 R1~R3 + 几何约定
|
||||
│ └── docs/
|
||||
│ ├── dim-drawing/ # 2D 尺寸图 PNG(中英双语标注)×2
|
||||
│ └── datasheet/ # 数据表 PDF(中文/英文)×2 + 源 JPEG
|
||||
└── tools/ # 生成脚本(全流程可复现)
|
||||
├── gen-step.ps1 # 生成 STEP 变体 + params.csv
|
||||
├── verify-step.ps1 # STEP 结构自检(引用/括号/ASCII/实体数)
|
||||
├── gen-images.ps1 # 生成尺寸图 PNG + 数据表 JPEG
|
||||
├── gen-pdf.ps1 # JPEG → 极简 PDF(含 xref 自检)
|
||||
├── fix-bom.ps1 # .ps1 加 UTF-8 BOM(PS 5.1 中文编码必需)
|
||||
└── parse-check.ps1 # PowerShell 语法错误定位
|
||||
```
|
||||
|
||||
## 快速复现
|
||||
|
||||
```powershell
|
||||
# 1. 生成 72 个 STEP + params.csv
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-step.ps1
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File tools\verify-step.ps1
|
||||
|
||||
# 2. 生成尺寸图与数据表
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-images.ps1
|
||||
```
|
||||
|
||||
## 数据规模
|
||||
|
||||
| 项 | 值 |
|
||||
|---|---|
|
||||
| 系列 | SC 标准气缸(1 个系列) |
|
||||
| 缸径 | 16 / 25 / 32 / 40 mm |
|
||||
| 行程 | 25 / 50 / 75 / 100 mm(缸径 16 仅 25/50) |
|
||||
| 磁石 / 安装 | 无,S × FA,LB,CB(受规则约束) |
|
||||
| 合法变体 | 72 个 |
|
||||
| STEP 格式 | ISO 10303-21 AP203 (CONFIG_CONTROL_DESIGN),毫米单位 |
|
||||
|
||||
## 注意事项
|
||||
|
||||
- STEP 为手写极简 B-rep(每文件 126 实体),已通过结构自检;**几何有效性需在真实 CAD 中抽查**(SolidWorks/NX 打开 `step\SC32x50S-LB.step` 确认 4 个圆柱实体与尺寸)
|
||||
- `params.csv` 为 UTF-8 带 BOM,Excel 直接打开不乱码
|
||||
- 修改型号/规则后重跑 `gen-step.ps1` 即可整体重新生成
|
||||
48
sample-data/onb-sc/coding-rules.md
Normal file
48
sample-data/onb-sc/coding-rules.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# 欧霓博 SC 系列标准气缸 —— 型号编码规则(示例数据)
|
||||
|
||||
> 本文件为**自编示例数据**(虚构系列),用于目录软件开发测试,非真实产品数据。
|
||||
|
||||
## 1. 型号编码格式
|
||||
|
||||
```
|
||||
SC{Bore}x{Stroke}{Magnet}-{Mount}
|
||||
```
|
||||
|
||||
| 段 | 含义 | 取值 | 示例 |
|
||||
|---|---|---|---|
|
||||
| `SC` | 系列代码(标准气缸) | 固定 | SC |
|
||||
| `{Bore}` | 缸径 (mm) | 16 / 25 / 32 / 40 | 32 |
|
||||
| `x` | 分隔符 | 固定 | x |
|
||||
| `{Stroke}` | 行程 (mm) | 25 / 50 / 75 / 100 | 50 |
|
||||
| `{Magnet}` | 磁石 | 空 = 无磁石;`S` = 带磁石 | S |
|
||||
| `-` | 分隔符 | 固定 | - |
|
||||
| `{Mount}` | 安装方式 | `FA` = 前法兰 / `LB` = 脚座 / `CB` = 中摆 | LB |
|
||||
|
||||
**完整示例:`SC32x50S-LB`** = SC 系列,缸径 Ø32,行程 50mm,带磁石,脚座安装。
|
||||
|
||||
## 2. 选型约束规则
|
||||
|
||||
| # | 规则 | 说明 |
|
||||
|---|---|---|
|
||||
| R1 | 缸径 16 → 行程仅 25 / 50 | 小缸径不支持长行程 |
|
||||
| R2 | 磁石 `S` 要求缸径 ≥ 25 | 缸径 16 无磁石选项 |
|
||||
| R3 | 中摆 `CB` 要求行程 ≤ 75 | 长行程不提供中摆安装 |
|
||||
|
||||
对应 [../params.csv](../params.csv) 中 72 个合法型号组合。
|
||||
|
||||
## 3. STEP 文件命名
|
||||
|
||||
STEP 文件名 = 型号 + `.step`,如 `SC32x50S-LB.step`,与 `params.csv` 的 `step_file` 列一一对应。
|
||||
|
||||
## 4. 简化几何约定(示例用)
|
||||
|
||||
模型为 4 个圆柱体组合(后盖 + 缸体 + 前盖 + 活塞杆),单位 mm,沿 Z 轴:
|
||||
|
||||
| 零件 | 外径 | 长度/位置 |
|
||||
|---|---|---|
|
||||
| 缸体 | bore + 4 | 长 = stroke + 60,z: 0 ~ stroke+60 |
|
||||
| 后盖 | bore + 10 | 厚 8,z: -8 ~ 0 |
|
||||
| 前盖 | bore + 10 | 厚 10,z: stroke+60 ~ stroke+70 |
|
||||
| 活塞杆 | max(bore/5, 3) × 2 | 自 z = stroke+46 伸出至前盖上方 stroke+10 |
|
||||
|
||||
> 真实产品数据接入时,用 Builder 导入真实 STEP 模型替换即可,编码规则与参数表格式不变。
|
||||
73
sample-data/onb-sc/params.csv
Normal file
73
sample-data/onb-sc/params.csv
Normal file
@@ -0,0 +1,73 @@
|
||||
model_code,bore,stroke,magnet,mount,step_file
|
||||
SC16x25-FA,16,25,,FA,SC16x25-FA.step
|
||||
SC16x25-LB,16,25,,LB,SC16x25-LB.step
|
||||
SC16x25-CB,16,25,,CB,SC16x25-CB.step
|
||||
SC16x50-FA,16,50,,FA,SC16x50-FA.step
|
||||
SC16x50-LB,16,50,,LB,SC16x50-LB.step
|
||||
SC16x50-CB,16,50,,CB,SC16x50-CB.step
|
||||
SC25x25-FA,25,25,,FA,SC25x25-FA.step
|
||||
SC25x25-LB,25,25,,LB,SC25x25-LB.step
|
||||
SC25x25-CB,25,25,,CB,SC25x25-CB.step
|
||||
SC25x25S-FA,25,25,S,FA,SC25x25S-FA.step
|
||||
SC25x25S-LB,25,25,S,LB,SC25x25S-LB.step
|
||||
SC25x25S-CB,25,25,S,CB,SC25x25S-CB.step
|
||||
SC25x50-FA,25,50,,FA,SC25x50-FA.step
|
||||
SC25x50-LB,25,50,,LB,SC25x50-LB.step
|
||||
SC25x50-CB,25,50,,CB,SC25x50-CB.step
|
||||
SC25x50S-FA,25,50,S,FA,SC25x50S-FA.step
|
||||
SC25x50S-LB,25,50,S,LB,SC25x50S-LB.step
|
||||
SC25x50S-CB,25,50,S,CB,SC25x50S-CB.step
|
||||
SC25x75-FA,25,75,,FA,SC25x75-FA.step
|
||||
SC25x75-LB,25,75,,LB,SC25x75-LB.step
|
||||
SC25x75-CB,25,75,,CB,SC25x75-CB.step
|
||||
SC25x75S-FA,25,75,S,FA,SC25x75S-FA.step
|
||||
SC25x75S-LB,25,75,S,LB,SC25x75S-LB.step
|
||||
SC25x75S-CB,25,75,S,CB,SC25x75S-CB.step
|
||||
SC25x100-FA,25,100,,FA,SC25x100-FA.step
|
||||
SC25x100-LB,25,100,,LB,SC25x100-LB.step
|
||||
SC25x100S-FA,25,100,S,FA,SC25x100S-FA.step
|
||||
SC25x100S-LB,25,100,S,LB,SC25x100S-LB.step
|
||||
SC32x25-FA,32,25,,FA,SC32x25-FA.step
|
||||
SC32x25-LB,32,25,,LB,SC32x25-LB.step
|
||||
SC32x25-CB,32,25,,CB,SC32x25-CB.step
|
||||
SC32x25S-FA,32,25,S,FA,SC32x25S-FA.step
|
||||
SC32x25S-LB,32,25,S,LB,SC32x25S-LB.step
|
||||
SC32x25S-CB,32,25,S,CB,SC32x25S-CB.step
|
||||
SC32x50-FA,32,50,,FA,SC32x50-FA.step
|
||||
SC32x50-LB,32,50,,LB,SC32x50-LB.step
|
||||
SC32x50-CB,32,50,,CB,SC32x50-CB.step
|
||||
SC32x50S-FA,32,50,S,FA,SC32x50S-FA.step
|
||||
SC32x50S-LB,32,50,S,LB,SC32x50S-LB.step
|
||||
SC32x50S-CB,32,50,S,CB,SC32x50S-CB.step
|
||||
SC32x75-FA,32,75,,FA,SC32x75-FA.step
|
||||
SC32x75-LB,32,75,,LB,SC32x75-LB.step
|
||||
SC32x75-CB,32,75,,CB,SC32x75-CB.step
|
||||
SC32x75S-FA,32,75,S,FA,SC32x75S-FA.step
|
||||
SC32x75S-LB,32,75,S,LB,SC32x75S-LB.step
|
||||
SC32x75S-CB,32,75,S,CB,SC32x75S-CB.step
|
||||
SC32x100-FA,32,100,,FA,SC32x100-FA.step
|
||||
SC32x100-LB,32,100,,LB,SC32x100-LB.step
|
||||
SC32x100S-FA,32,100,S,FA,SC32x100S-FA.step
|
||||
SC32x100S-LB,32,100,S,LB,SC32x100S-LB.step
|
||||
SC40x25-FA,40,25,,FA,SC40x25-FA.step
|
||||
SC40x25-LB,40,25,,LB,SC40x25-LB.step
|
||||
SC40x25-CB,40,25,,CB,SC40x25-CB.step
|
||||
SC40x25S-FA,40,25,S,FA,SC40x25S-FA.step
|
||||
SC40x25S-LB,40,25,S,LB,SC40x25S-LB.step
|
||||
SC40x25S-CB,40,25,S,CB,SC40x25S-CB.step
|
||||
SC40x50-FA,40,50,,FA,SC40x50-FA.step
|
||||
SC40x50-LB,40,50,,LB,SC40x50-LB.step
|
||||
SC40x50-CB,40,50,,CB,SC40x50-CB.step
|
||||
SC40x50S-FA,40,50,S,FA,SC40x50S-FA.step
|
||||
SC40x50S-LB,40,50,S,LB,SC40x50S-LB.step
|
||||
SC40x50S-CB,40,50,S,CB,SC40x50S-CB.step
|
||||
SC40x75-FA,40,75,,FA,SC40x75-FA.step
|
||||
SC40x75-LB,40,75,,LB,SC40x75-LB.step
|
||||
SC40x75-CB,40,75,,CB,SC40x75-CB.step
|
||||
SC40x75S-FA,40,75,S,FA,SC40x75S-FA.step
|
||||
SC40x75S-LB,40,75,S,LB,SC40x75S-LB.step
|
||||
SC40x75S-CB,40,75,S,CB,SC40x75S-CB.step
|
||||
SC40x100-FA,40,100,,FA,SC40x100-FA.step
|
||||
SC40x100-LB,40,100,,LB,SC40x100-LB.step
|
||||
SC40x100S-FA,40,100,S,FA,SC40x100S-FA.step
|
||||
SC40x100S-LB,40,100,S,LB,SC40x100S-LB.step
|
||||
|
37
sample-data/onb-sc/params_maq.csv
Normal file
37
sample-data/onb-sc/params_maq.csv
Normal file
@@ -0,0 +1,37 @@
|
||||
model_code,bore,stroke,magnet,mount,step_file
|
||||
MAQ6x10-FA,6,10,,FA,MAQ6x10-FA.step
|
||||
MAQ6x10-LB,6,10,,LB,MAQ6x10-LB.step
|
||||
MAQ6x20-FA,6,20,,FA,MAQ6x20-FA.step
|
||||
MAQ6x20-LB,6,20,,LB,MAQ6x20-LB.step
|
||||
MAQ10x10-FA,10,10,,FA,MAQ10x10-FA.step
|
||||
MAQ10x10-LB,10,10,,LB,MAQ10x10-LB.step
|
||||
MAQ10x10S-FA,10,10,S,FA,MAQ10x10S-FA.step
|
||||
MAQ10x10S-LB,10,10,S,LB,MAQ10x10S-LB.step
|
||||
MAQ10x20-FA,10,20,,FA,MAQ10x20-FA.step
|
||||
MAQ10x20-LB,10,20,,LB,MAQ10x20-LB.step
|
||||
MAQ10x20S-FA,10,20,S,FA,MAQ10x20S-FA.step
|
||||
MAQ10x20S-LB,10,20,S,LB,MAQ10x20S-LB.step
|
||||
MAQ10x30-FA,10,30,,FA,MAQ10x30-FA.step
|
||||
MAQ10x30-LB,10,30,,LB,MAQ10x30-LB.step
|
||||
MAQ10x30S-FA,10,30,S,FA,MAQ10x30S-FA.step
|
||||
MAQ10x30S-LB,10,30,S,LB,MAQ10x30S-LB.step
|
||||
MAQ10x40-FA,10,40,,FA,MAQ10x40-FA.step
|
||||
MAQ10x40-LB,10,40,,LB,MAQ10x40-LB.step
|
||||
MAQ10x40S-FA,10,40,S,FA,MAQ10x40S-FA.step
|
||||
MAQ10x40S-LB,10,40,S,LB,MAQ10x40S-LB.step
|
||||
MAQ16x10-FA,16,10,,FA,MAQ16x10-FA.step
|
||||
MAQ16x10-LB,16,10,,LB,MAQ16x10-LB.step
|
||||
MAQ16x10S-FA,16,10,S,FA,MAQ16x10S-FA.step
|
||||
MAQ16x10S-LB,16,10,S,LB,MAQ16x10S-LB.step
|
||||
MAQ16x20-FA,16,20,,FA,MAQ16x20-FA.step
|
||||
MAQ16x20-LB,16,20,,LB,MAQ16x20-LB.step
|
||||
MAQ16x20S-FA,16,20,S,FA,MAQ16x20S-FA.step
|
||||
MAQ16x20S-LB,16,20,S,LB,MAQ16x20S-LB.step
|
||||
MAQ16x30-FA,16,30,,FA,MAQ16x30-FA.step
|
||||
MAQ16x30-LB,16,30,,LB,MAQ16x30-LB.step
|
||||
MAQ16x30S-FA,16,30,S,FA,MAQ16x30S-FA.step
|
||||
MAQ16x30S-LB,16,30,S,LB,MAQ16x30S-LB.step
|
||||
MAQ16x40-FA,16,40,,FA,MAQ16x40-FA.step
|
||||
MAQ16x40-LB,16,40,,LB,MAQ16x40-LB.step
|
||||
MAQ16x40S-FA,16,40,S,FA,MAQ16x40S-FA.step
|
||||
MAQ16x40S-LB,16,40,S,LB,MAQ16x40S-LB.step
|
||||
|
163
sample-data/tools/NX母模建模操作说明书.md
Normal file
163
sample-data/tools/NX母模建模操作说明书.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# NX 2506 参数化母模建模操作说明书
|
||||
|
||||
> 目的:为欧霓博目录软件建立"一个母模覆盖全系列所有型号组合"的参数化零件,
|
||||
> 配套 `nx-batch-export.vb` 批量导出 STEP。
|
||||
> 读者:NX 建模工程师。**建模前先通读一遍,严格按命名规范执行。**
|
||||
|
||||
---
|
||||
|
||||
## 0. 核心概念(先想清楚再动手)
|
||||
|
||||
```
|
||||
CSV 参数表 (数值源头) ←── 列名必须与母模表达式名一致 ──→ NX 母模 (公式/特征)
|
||||
↓ ↓
|
||||
nx-batch-export.vb (翻译官) ── 改表达式 → 更新模型 ──→ 导出 STEP
|
||||
```
|
||||
|
||||
- **表达式名 = CSV 列名**(一字不差,区分大小写):`bore`、`stroke`、`magnet`、`mount`
|
||||
- 开关类表达式另起名字并在 journal CONFIG 区映射:`magOn`、`mountIdx`
|
||||
- 所有"会变"的尺寸必须表达式化;所有"有无"的特征必须按表达式抑制
|
||||
- 建模完成后验收标准:**手动改 3 组不同参数组合,模型能正确重建且无错误**
|
||||
|
||||
---
|
||||
|
||||
## 1. 建模前规划
|
||||
|
||||
### 1.1 确定本系列的几何键(决定哪些组合要分别导出)
|
||||
|
||||
几何键 = 影响外形的一切参数组合。以 KC 系列为例:
|
||||
|
||||
| 参数 | 是否影响外形 | 母模处理 |
|
||||
|---|---|---|
|
||||
| type 型号段 (00/02/03 单杆/双轴/双轴可调) | ✅ | 双轴 = 第二根活塞杆特征组按表达式抑制 |
|
||||
| bore 缸径 | ✅ | 尺寸表达式 |
|
||||
| stroke 行程 | ✅ | 尺寸表达式 |
|
||||
| magnet 磁石 | ✅(磁石槽+开关导轨) | 特征组按表达式抑制 (magOn=0/1) |
|
||||
| mount 固定形式 | ✅(端盖耳座/支座/法兰特征) | 特征组按表达式抑制 (mountIdx=0~6) |
|
||||
|
||||
### 1.2 表达式命名清单(先建全,再开始画)
|
||||
|
||||
在母模里**先建立全部表达式**(工具→表达式 Ctrl+E),带注释:
|
||||
|
||||
| 名称 | 类型/初值 | 公式 | 注释 |
|
||||
|---|---|---|---|
|
||||
| bore | Number / 32 | 32 | 缸径 mm(基准值,journal 会覆盖) |
|
||||
| stroke | Number / 50 | 50 | 行程 mm |
|
||||
| rodLen | Number | stroke + 60 | 活塞杆长(联动示例,按实际结构改) |
|
||||
| bodyLen | Number | stroke + 缸体常数 | 缸筒长(联动) |
|
||||
| magOn | Number / 1 | 1 | 磁石开关:1=有磁石 0=无 |
|
||||
| magPos | Number | stroke + 40 | 磁石中心位置(联动;若独立规格则写常数+journal 映射) |
|
||||
| guideLen | Number | stroke + 30 | 开关导轨长(联动) |
|
||||
| mountIdx | Number / 0 | 0 | 安装形式:0=基本 1=FA 2=FB 3=CA 4=CB 5=LB 6=YB |
|
||||
| typeTwin | Number / 0 | 0 | 双轴开关:1=双轴(第二根杆特征组抑制式引用它) |
|
||||
|
||||
> 命名建议:全小写、驼峰(bore/stroke/magOn),与 CSV 列名一致;开关类加 On/Idx 后缀。
|
||||
|
||||
---
|
||||
|
||||
## 2. 建模步骤(KC 母模示例)
|
||||
|
||||
### 步骤 1:建缸体主体
|
||||
|
||||
1. 新建零件 → 存为 `KC_master.prt`
|
||||
2. 草图:缸筒截面(**全约束**!)——外径引用 `bodyR = bore/2 + 壁厚` 等派生表达式,不要写死数
|
||||
3. 拉伸长度引用 `bodyLen` 表达式
|
||||
|
||||
### 步骤 2:端盖与活塞杆
|
||||
|
||||
- 前盖/后盖草图定位引用**基准坐标系或已有表达式**(严禁引用会被抑制的特征面)
|
||||
- 活塞杆:圆柱特征,直径/长度引用表达式,定位在轴心
|
||||
- **双轴型号(02/03)**:把第二根活塞杆+对应孔做成**特征组**,按表达式抑制:
|
||||
```
|
||||
右键特征 → 特征组 → 命名为 group_twin
|
||||
右键 group_twin → 抑制 → 按表达式抑制
|
||||
→ 表达式列表里新建 twinOn = 1,抑制表达式写 if(twinOn==1)(1)else(0)
|
||||
```
|
||||
|
||||
### 步骤 3:磁石槽(有/无 切换)
|
||||
|
||||
1. 在缸筒上画磁石槽:草图 → 拉伸求差(布尔 Subtract)
|
||||
2. 槽位置引用 `magPos`(中心),长度引用 `guideLen`
|
||||
3. 磁石槽+导轨特征全选 → 特征组 `group_magnet`
|
||||
4. 按表达式抑制:`if(magOn==1)(1)else(0)`
|
||||
5. 开关导轨(如有独立件外形)同样入组
|
||||
|
||||
> ⚠️ 若倒角/圆角引用磁石槽的边,把倒角也放进 group_magnet,否则抑制后更新报错。
|
||||
|
||||
### 步骤 4:安装形式特征组(7 种切换)
|
||||
|
||||
1. 以"基本型"端盖为底
|
||||
2. 每种形式单独建模(FA 法兰、FB 后法兰、CA 单耳、CB 双耳、LB 脚架孔、YB 支座孔),每组做完立即:
|
||||
- 全选该组特征 → 特征组 `group_mount_FA` 等 6 个组
|
||||
- 按表达式抑制,条件式:`if(mountIdx==1)(1)else(0)`(FA 对应 1,依此类推)
|
||||
3. 建模次序建议:先建完**基本型全部结构**,再逐个叠加形式特征,最后统一分组抑制
|
||||
|
||||
> 特征跨实体布尔(求和/求差)时,目标体选基本体,别选会被抑制的特征体。
|
||||
|
||||
### 步骤 5:验证(必须做!)
|
||||
|
||||
| 验证项 | 操作 | 通过标准 |
|
||||
|---|---|---|
|
||||
| 尺寸联动 | 改 bore=125 → Ctrl+F7 更新 | 无报错,模型整体放大 |
|
||||
| 行程联动 | 改 stroke=1000 | 缸筒/活塞杆/导轨跟着变 |
|
||||
| 磁石切换 | magOn=0/1 | 槽消失/出现,无悬空特征 |
|
||||
| 形式切换 | mountIdx=0~6 逐个试 | 每值对应正确外形 |
|
||||
| 双轴切换 | typeTwin=0/1 | 第二杆出现/消失 |
|
||||
| 极限组合 | bore=320 + stroke=1000 + magOn=1 + mountIdx=6 | 极端组合不破面 |
|
||||
|
||||
验收后再交给 journal 跑批量。
|
||||
|
||||
---
|
||||
|
||||
## 3. Journal 配置(nx-batch-export.vb)
|
||||
|
||||
文件头有完整注释。**建模工程师只需改 CONFIG 区**:
|
||||
|
||||
```vb
|
||||
' ============================ CONFIG 区 ============================
|
||||
Dim csvPath As String = "D:\catalog\params.csv" ' ← 改成 catalog\csv\KC.csv
|
||||
Dim outDir As String = "D:\catalog\step_out\" ' ← 改成 catalog\step\
|
||||
|
||||
' 1) 尺寸映射: NX 表达式名 → CSV 列名
|
||||
exprMap.Add("bore", "bore")
|
||||
exprMap.Add("stroke", "stroke")
|
||||
' 有独立表达式就加: exprMap.Add("magPos", "magpos") ← CSV 里也要有这一列
|
||||
|
||||
' 2) 枚举开关映射: {CSV列名, 列值, NX表达式名, 表达式值}
|
||||
switchRules.Add({"magnet", "", "magOn", "0"}) ' 无磁石
|
||||
switchRules.Add({"magnet", "M", "magOn", "1"}) ' 有磁石
|
||||
switchRules.Add({"mount", "", "mountIdx", "0"}) ' 基本型
|
||||
switchRules.Add({"mount", "FA", "mountIdx", "1"}) ' 前盖固定
|
||||
...(按你们母模实际的表达式名和取值改)
|
||||
' ================================================================
|
||||
```
|
||||
|
||||
**运行**:NX 里 工具 → 日记 → 播放 → 选 `nx-batch-export.vb`(不是 Ctrl+U,Ctrl+U 只认 .dll)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 全流程走查(建模 → 目录上线)
|
||||
|
||||
```
|
||||
1. NX 母模验收 (步骤 5 的验证表)
|
||||
2. journal CONFIG 改好 → 播放 → 挂机批量导出 (已去重, 同文件只导一次)
|
||||
3. STEP 落盘 catalog\step\ → 双击 OnebotCatalog\bin\维护入口.bat
|
||||
→ 目录制作器 → 【重建全量目录】
|
||||
4. 自测 (发布门禁自动跑 25 项, 缺文件会拦)
|
||||
5. 停预览服务器 → publish.ps1 → 三份产物
|
||||
6. 网页 localhost:8080 验证 3D + 下载
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见问题排查
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|---|---|---|
|
||||
| 导出后模型没变化 | journal 里表达式名与母模对不上 | 对照 Ctrl+E 列表逐名核对 |
|
||||
| 更新报"更新失败/内部错误" | 被抑制特征被其它特征引用(边/面) | 把引用它的特征也入同一个抑制组 |
|
||||
| 改 mountIdx 后外形不对 | 抑制条件式写错(== 写成 =) | 条件式格式: `if(mountIdx==1)(1)else(0)` |
|
||||
| 抑制后草图悬空 | 草图定位引用了会被抑制的边 | 定位改引用基准轴/面 |
|
||||
| journal 报"参数表缺少列" | CSV 表头与 exprMap/switchRules 不一致 | 表头名与 CONFIG 统一 |
|
||||
| 导出的 STEP 在网页渲染异常 | 用了特殊特征(钣金/焊接件) | 导出前抽空合并实体,尽量常规特征 |
|
||||
| 中文乱码 | 编辑 .vb 后没跑 fix-bom.ps1 | 每次改完 journal 跑一次 fix-bom |
|
||||
45
sample-data/tools/add-expressions.vb
Normal file
45
sample-data/tools/add-expressions.vb
Normal file
@@ -0,0 +1,45 @@
|
||||
' 给现有部件注入参数化表达式 (真数模做母模的管线验证用; 不改几何, 只加 bore/stroke/magOn/mountIdx)
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module AddExpressions
|
||||
Sub Main()
|
||||
Dim lw As ListingWindow = Session.GetSession().ListingWindow
|
||||
lw.Open()
|
||||
Dim outPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master.prt")
|
||||
If Not File.Exists(outPath) Then
|
||||
lw.WriteLine("部件缺失: " & outPath)
|
||||
Return
|
||||
End If
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
If w Is Nothing Then
|
||||
lw.WriteLine("打开失败")
|
||||
Return
|
||||
End If
|
||||
Dim mm As NXOpen.Unit = w.UnitCollection.FindObject("MilliMeter")
|
||||
Try
|
||||
If mm IsNot Nothing Then
|
||||
w.Expressions.CreateExpressionWithUnit("Number", "bore=32", mm)
|
||||
w.Expressions.CreateExpressionWithUnit("Number", "stroke=10", mm)
|
||||
Else
|
||||
w.Expressions.CreateExpression("Number", "bore=32")
|
||||
w.Expressions.CreateExpression("Number", "stroke=10")
|
||||
End If
|
||||
w.Expressions.CreateExpression("Number", "magOn=0")
|
||||
w.Expressions.CreateExpression("Number", "mountIdx=0")
|
||||
lw.WriteLine("表达式已注入")
|
||||
Catch ex As Exception
|
||||
lw.WriteLine("表达式注入失败: " & ex.Message)
|
||||
Return
|
||||
End Try
|
||||
Dim pss As NXOpen.PartSaveStatus = w.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
lw.WriteLine("已保存: " & outPath)
|
||||
End Sub
|
||||
End Module
|
||||
69
sample-data/tools/check-master.vb
Normal file
69
sample-data/tools/check-master.vb
Normal file
@@ -0,0 +1,69 @@
|
||||
' 检查母模: 特征数 / 实体数 / 表达式值
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module CheckMaster
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim outPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master.prt")
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
If w Is Nothing Then
|
||||
sb.AppendLine("Work=NULL")
|
||||
Else
|
||||
Dim featCount As Integer = 0
|
||||
Try
|
||||
For Each f As NXOpen.Features.Feature In w.Features.GetFeatures()
|
||||
featCount += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
sb.AppendLine("特征数=" & featCount)
|
||||
Dim bodyCount As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
bodyCount += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
sb.AppendLine("实体数=" & bodyCount)
|
||||
Dim bi As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
Dim fc As Integer = 0
|
||||
Try
|
||||
For Each f As NXOpen.Face In b.GetFaces()
|
||||
fc += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
bi += 1
|
||||
Try
|
||||
For Each f As NXOpen.Face In b.GetFaces()
|
||||
fc += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
sb.AppendLine("实体" & bi & " 面数=" & fc)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("实体检查异常: " & ex.Message)
|
||||
End Try
|
||||
For Each nm As String In New String() {"bore", "stroke", "magOn", "mountIdx"}
|
||||
Try
|
||||
Dim e As NXOpen.Expression = w.Expressions.FindObject(nm)
|
||||
sb.AppendLine("表达式 " & nm & "=" & (If(e Is Nothing, "(未找到)", e.Value)))
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("表达式 " & nm & " 异常: " & ex.Message)
|
||||
End Try
|
||||
Next
|
||||
sb.AppendLine("IsModified=" & w.IsModified)
|
||||
End If
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "check-master.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
100
sample-data/tools/create-test-master.vb
Normal file
100
sample-data/tools/create-test-master.vb
Normal file
@@ -0,0 +1,100 @@
|
||||
' =============================================================================
|
||||
' NX Journal: 冒烟测试母模创建 (按需生成服务联调用, 2026-08-26; NX 2506 实测修正版)
|
||||
'
|
||||
' ▍干什么: 打开"当前工作目录\KC_master.prt"(启动前由脚本从 NX 模板复制) →
|
||||
' 建 bore/stroke/magOn/mountIdx 四个表达式 (UF 底层 API, 跨版本稳定) → 建方块实体 → 保存
|
||||
' 用途: 验证 worker_journal.vb 全链路 (打开→设表达式→更新→导出 4 格式) 在无真实母模时跑通。
|
||||
' 真母模建好后同名替换 catalog-masters\KC\KC_master.prt, 本脚本可弃。
|
||||
'
|
||||
' ▍运行 (run_journal 只接受一个参数, 用 WorkingDirectory 定位):
|
||||
' WorkingDirectory = catalog-masters\KC
|
||||
' run_journal.exe <本文件完整路径>
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module CreateTestMaster
|
||||
|
||||
Sub Main()
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim lw As ListingWindow = theSession.ListingWindow
|
||||
lw.Open()
|
||||
|
||||
Dim outPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master.prt")
|
||||
If Not File.Exists(outPath) Then
|
||||
lw.WriteLine("部件文件缺失: " & outPath & " (请先从 NX 模板复制到当前目录)")
|
||||
Return
|
||||
End If
|
||||
|
||||
' 打开模板部件 (批处理必须用 OpenBaseDisplay 才能成为工作部件, OpenBase 打开后 Work=NULL)
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Dim basePart1 As NXOpen.BasePart = theSession.Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
If partLoadStatus1 IsNot Nothing Then partLoadStatus1.Dispose()
|
||||
Dim part1 As Part = theSession.Parts.Work
|
||||
If part1 Is Nothing Then
|
||||
lw.WriteLine("打开失败: " & outPath)
|
||||
Return
|
||||
End If
|
||||
lw.WriteLine("部件已打开: " & part1.FullPath)
|
||||
|
||||
' 表达式 (NX 2506: 用 ExpressionCollection.CreateExpression(值, 名) 创建无单位表达式;
|
||||
' 特征尺寸按部件单位解释; EditExp 只编辑已存在的表达式)
|
||||
Dim mm As NXOpen.Unit = part1.UnitCollection.FindObject("MilliMeter")
|
||||
If mm Is Nothing Then
|
||||
lw.WriteLine("警告: 未找到 MilliMeter 单位对象, 尝试 Millimetre")
|
||||
mm = part1.UnitCollection.FindObject("Millimetre")
|
||||
End If
|
||||
' 缸径/行程带长度单位 (特征尺寸表达式需要); 开关无单位
|
||||
If mm IsNot Nothing Then
|
||||
part1.Expressions.CreateExpressionWithUnit("Number", "bore=32", mm)
|
||||
part1.Expressions.CreateExpressionWithUnit("Number", "stroke=10", mm)
|
||||
Else
|
||||
part1.Expressions.CreateExpression("Number", "bore=32")
|
||||
part1.Expressions.CreateExpression("Number", "stroke=10")
|
||||
End If
|
||||
part1.Expressions.CreateExpression("Number", "magOn=0")
|
||||
part1.Expressions.CreateExpression("Number", "mountIdx=0")
|
||||
lw.WriteLine("表达式已建: bore=32 stroke=10 magOn=0 mountIdx=0")
|
||||
|
||||
' 仿真气缸母模 (测试级真实感): 缸筒 + 活塞杆, 尺寸全部由表达式驱动
|
||||
' 缸筒: 外径 bore+8 (壁厚4), 长度 stroke; 活塞杆: 直径 bore*0.33, 长度 stroke+30 (前伸 30)
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
Dim origin(2) As Double
|
||||
Dim dir(2) As Double
|
||||
dir(2) = 1.0 ' +Z 轴向
|
||||
' NX 2506 签名: CreateCyl1(sign, origin, height, diam, direction, ByRef tag)
|
||||
Dim tagTube As Tag
|
||||
ufs.Modl.CreateCyl1(NXOpen.UF.FeatureSigns.Nullsign, origin, "stroke", "bore+8", dir, tagTube)
|
||||
Dim tagRod As Tag
|
||||
ufs.Modl.CreateCyl1(NXOpen.UF.FeatureSigns.Nullsign, origin, "stroke+30", "bore*0.33", dir, tagRod)
|
||||
lw.WriteLine("仿真气缸已建: 缸筒(bore+8 x stroke) + 活塞杆(bore*0.33 x stroke+30), 表达式驱动")
|
||||
|
||||
' 更新模型 (特征刚建, 必须 Update 后才成为实体几何)
|
||||
ufs.Modl.Update()
|
||||
lw.WriteLine("模型已更新")
|
||||
|
||||
' 把实体加进 Model 引用集 (UF 建的实体默认不进引用集 → 导出器按引用集过滤会导出空几何)
|
||||
Dim bodyList As New System.Collections.Generic.List(Of NXOpen.NXObject)()
|
||||
For Each b As NXOpen.Body In part1.Bodies
|
||||
bodyList.Add(b)
|
||||
Next
|
||||
If bodyList.Count > 0 Then
|
||||
For Each rs As NXOpen.ReferenceSet In part1.GetAllReferenceSets()
|
||||
If rs.Name.ToUpperInvariant().Contains("MODEL") Then
|
||||
rs.AddObjectsToReferenceSet(bodyList.ToArray())
|
||||
lw.WriteLine("实体已加入引用集 " & rs.Name & " (" & bodyList.Count & " 个)")
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
' 保存 (NX 2506: Save 两参数, 返回 PartSaveStatus)
|
||||
Dim pss As NXOpen.PartSaveStatus = part1.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
lw.WriteLine("已保存: " & outPath)
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
91
sample-data/tools/debug-export-all.vb
Normal file
91
sample-data/tools/debug-export-all.vb
Normal file
@@ -0,0 +1,91 @@
|
||||
' 调试: 四格式导出交叉测试 (STEP/Parasolid/IGES/STL) — 判断是几何问题还是翻译器配置问题
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module DebugExportAll
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(masterPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
Dim outDir As String = Path.Combine(cur, "dbg_out")
|
||||
Directory.CreateDirectory(outDir)
|
||||
For Each f As String In Directory.GetFiles(outDir)
|
||||
File.Delete(f)
|
||||
Next
|
||||
|
||||
' 保存工作部件 (导出器可能读文件; 也验证 Save 后实体仍在)
|
||||
Try
|
||||
Dim pss As NXOpen.PartSaveStatus = w.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
sb.AppendLine("Save OK")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("Save 失败: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 1) STL (显示几何三角化; 若此也不出网格 → 几何本身有问题)
|
||||
Try
|
||||
Dim stc As NXOpen.STLCreator = Session.GetSession().DexManager.CreateStlCreator()
|
||||
stc.OutputType = NXOpen.STLCreator.OutputTypeEnum.Binary
|
||||
stc.ChordalTol = 0.5
|
||||
stc.AdjacencyTol = 0.1
|
||||
stc.AutoNormalGen = True
|
||||
stc.OutputFile = Path.Combine(outDir, "T.stl")
|
||||
stc.Commit()
|
||||
stc.Destroy()
|
||||
sb.AppendLine("STL: " & (New FileInfo(Path.Combine(outDir, "T.stl"))).Length & " 字节")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("STL 失败: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 2) Parasolid (文件方式)
|
||||
Try
|
||||
Dim pe As NXOpen.ParasolidExporter = Session.GetSession().DexManager.CreateParasolidExporter()
|
||||
pe.ExportFrom = NXOpen.ParasolidExporter.ExportFromOption.DisplayedPart
|
||||
pe.ParasolidVersion = NXOpen.ParasolidExporter.ParasolidVersionOption.Current
|
||||
pe.OutputFile = Path.Combine(outDir, "T.x_t")
|
||||
pe.Commit()
|
||||
pe.Destroy()
|
||||
sb.AppendLine("Parasolid: " & (New FileInfo(Path.Combine(outDir, "T.x_t"))).Length & " 字节")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("Parasolid 失败: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 3) IGES (文件方式 ExistingPart)
|
||||
Try
|
||||
Dim ic As NXOpen.IgesCreator = Session.GetSession().DexManager.CreateIgesCreator()
|
||||
ic.ExportFrom = NXOpen.IgesCreator.ExportFromOption.ExistingPart
|
||||
ic.InputFile = masterPath
|
||||
ic.ExportModelData = True
|
||||
ic.OutputFile = Path.Combine(outDir, "T.igs")
|
||||
ic.Commit()
|
||||
ic.Destroy()
|
||||
sb.AppendLine("IGES: " & (New FileInfo(Path.Combine(outDir, "T.igs"))).Length & " 字节")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("IGES 失败: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 4) STEP (文件方式 ExistingPart)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = Session.GetSession().DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.ExistingPart
|
||||
sc.InputFile = masterPath
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = Path.Combine(outDir, "T.stp")
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
sb.AppendLine("STEP: " & (New FileInfo(Path.Combine(outDir, "T.stp"))).Length & " 字节")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("STEP 失败: " & ex.Message)
|
||||
End Try
|
||||
|
||||
File.WriteAllText(Path.Combine(cur, "debug-export-all.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
69
sample-data/tools/debug-export.vb
Normal file
69
sample-data/tools/debug-export.vb
Normal file
@@ -0,0 +1,69 @@
|
||||
' 调试: 单步 STEP 导出定位 "File already exists" 来源
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module DebugExport
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
sb.AppendLine("CWD=" & cur & " masterExists=" & File.Exists(masterPath))
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(masterPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
sb.AppendLine("Work=" & (If(w Is Nothing, "NULL", w.FullPath)))
|
||||
If w IsNot Nothing Then
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
Try
|
||||
ufs.Modl.EditExp("stroke=87")
|
||||
ufs.Modl.Update()
|
||||
sb.AppendLine("EditExp+Update OK")
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("EditExp/Update 失败: " & ex.Message)
|
||||
End Try
|
||||
' 诊断: 实体数与引用集
|
||||
Dim bodyCount As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
bodyCount += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
sb.AppendLine("实体数=" & bodyCount)
|
||||
Try
|
||||
For Each rs As NXOpen.ReferenceSet In w.GetAllReferenceSets()
|
||||
sb.AppendLine("引用集: " & rs.Name)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("引用集枚举失败: " & ex.Message)
|
||||
End Try
|
||||
Dim outDir As String = Path.Combine(cur, "dbg_out")
|
||||
Directory.CreateDirectory(outDir)
|
||||
Dim stpPath As String = Path.Combine(outDir, "DBG.stp")
|
||||
If File.Exists(stpPath) Then File.Delete(stpPath)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = Session.GetSession().DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.ExistingPart ' 批处理无显示部件 → 直接处理部件文件
|
||||
sc.InputFile = masterPath
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = stpPath
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
sb.AppendLine("STEP 导出成功: " & stpPath & " size=" & (New FileInfo(stpPath)).Length)
|
||||
Catch ex As Exception
|
||||
sb.AppendLine("STEP 导出失败: " & ex.GetType().Name & " / " & ex.Message)
|
||||
Dim inner As Exception = ex.InnerException
|
||||
While inner IsNot Nothing
|
||||
sb.AppendLine(" inner: " & inner.Message)
|
||||
inner = inner.InnerException
|
||||
End While
|
||||
End Try
|
||||
End If
|
||||
File.WriteAllText(Path.Combine(cur, "debug-export.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
46
sample-data/tools/debug-save.vb
Normal file
46
sample-data/tools/debug-save.vb
Normal file
@@ -0,0 +1,46 @@
|
||||
' 诊断 2: 批处理下 Work=NULL — 验证 (a) BasePart 直接转 Part 操作 (b) SetDisplay 签名
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module DebugSave
|
||||
Sub Main()
|
||||
Dim lw As ListingWindow = Session.GetSession().ListingWindow
|
||||
lw.Open()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim outPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master.prt")
|
||||
sb.AppendLine("CWD=" & Directory.GetCurrentDirectory())
|
||||
|
||||
' (a) SetDisplay 签名反射
|
||||
Dim pc As NXOpen.PartCollection = Session.GetSession().Parts
|
||||
For Each mi As System.Reflection.MethodInfo In pc.GetType().GetMethods()
|
||||
If mi.Name = "SetDisplay" OrElse mi.Name = "Open" Then sb.AppendLine("SIG: " & mi.ToString())
|
||||
Next
|
||||
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Dim basePart1 As NXOpen.BasePart = Session.GetSession().Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
sb.AppendLine("basePart1=" & (If(basePart1 Is Nothing, "NULL", basePart1.FullPath)))
|
||||
sb.AppendLine("Work=" & (If(Session.GetSession().Parts.Work Is Nothing, "NULL", "非空")))
|
||||
|
||||
' (b) 直接转换 BasePart → Part 并操作
|
||||
Dim p1 As Part = CType(basePart1, Part)
|
||||
sb.AppendLine("p1 cast ok, FullPath=" & p1.FullPath & " IsModified(before)=" & p1.IsModified)
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
ufs.Modl.EditExp("bore=50")
|
||||
ufs.Modl.Update()
|
||||
sb.AppendLine("p1.IsModified(after EditExp+Update)=" & p1.IsModified)
|
||||
' 对比 Save 与 SaveAs 在批处理下的落盘行为
|
||||
Dim pss As NXOpen.PartSaveStatus = p1.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
sb.AppendLine("after Save: size=" & (New FileInfo(outPath)).Length & " mtime=" & File.GetLastWriteTime(outPath))
|
||||
Dim asPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master_as.prt")
|
||||
Dim pss2 As NXOpen.PartSaveStatus = p1.SaveAs(asPath)
|
||||
If pss2 IsNot Nothing Then pss2.Dispose()
|
||||
sb.AppendLine("after SaveAs: exists=" & File.Exists(asPath) & " size=" & (If(File.Exists(asPath), (New FileInfo(asPath)).Length, 0)))
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "debug-save.txt"), sb.ToString(), Encoding.UTF8)
|
||||
lw.WriteLine(sb.ToString())
|
||||
End Sub
|
||||
End Module
|
||||
18
sample-data/tools/fix-bom.ps1
Normal file
18
sample-data/tools/fix-bom.ps1
Normal file
@@ -0,0 +1,18 @@
|
||||
# Ensure each .ps1 has exactly ONE UTF-8 BOM so PowerShell 5.1 reads Chinese correctly.
|
||||
# Idempotent: strips any leading BOMs first, then prepends a single one.
|
||||
# (PS 5.1 treats BOM-less files as ANSI/GBK; this script is intentionally ASCII-only.)
|
||||
param([string[]]$Files)
|
||||
if (-not $Files) {
|
||||
$Files = Get-ChildItem (Join-Path $PSScriptRoot '*.ps1') | ForEach-Object { $_.FullName }
|
||||
}
|
||||
foreach ($f in $Files) {
|
||||
$bytes = [System.IO.File]::ReadAllBytes($f)
|
||||
$i = 0
|
||||
while (($i + 2) -lt $bytes.Length -and $bytes[$i] -eq 0xEF -and $bytes[$i + 1] -eq 0xBB -and $bytes[$i + 2] -eq 0xBF) { $i += 3 }
|
||||
if ($i -gt 0) { $bytes = $bytes[$i..($bytes.Length - 1)] }
|
||||
$out = New-Object byte[] ($bytes.Length + 3)
|
||||
$out[0] = 0xEF; $out[1] = 0xBB; $out[2] = 0xBF
|
||||
[Array]::Copy($bytes, 0, $out, 3, $bytes.Length)
|
||||
[System.IO.File]::WriteAllBytes($f, $out)
|
||||
Write-Output ("BOM fixed: " + $f)
|
||||
}
|
||||
235
sample-data/tools/gen-images.ps1
Normal file
235
sample-data/tools/gen-images.ps1
Normal file
@@ -0,0 +1,235 @@
|
||||
# 生成示例 2D 尺寸图 PNG 与数据表 JPEG (中/英)
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-images.ps1
|
||||
# 依赖: 仅 Windows 自带 System.Drawing (PowerShell 5.1)
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
|
||||
$docsDir = Join-Path (Join-Path $PSScriptRoot '..\onb-sc') 'docs'
|
||||
$dimDir = Join-Path $docsDir 'dim-drawing'
|
||||
$dsDir = Join-Path $docsDir 'datasheet'
|
||||
New-Item -ItemType Directory -Force -Path $dimDir | Out-Null
|
||||
New-Item -ItemType Directory -Force -Path $dsDir | Out-Null
|
||||
|
||||
$fontTitle = New-Object System.Drawing.Font('Microsoft YaHei', 26, [System.Drawing.FontStyle]::Bold)
|
||||
$fontH2 = New-Object System.Drawing.Font('Microsoft YaHei', 17, [System.Drawing.FontStyle]::Bold)
|
||||
$fontText = New-Object System.Drawing.Font('Microsoft YaHei', 13)
|
||||
$fontSmall = New-Object System.Drawing.Font('Microsoft YaHei', 11)
|
||||
$penThin = New-Object System.Drawing.Pen([System.Drawing.Color]::Black, 1)
|
||||
$penLine = New-Object System.Drawing.Pen([System.Drawing.Color]::Black, 2)
|
||||
$penDash = New-Object System.Drawing.Pen([System.Drawing.Color]::Black, 1)
|
||||
$penDash.DashStyle = [System.Drawing.Drawing2D.DashStyle]::Dash
|
||||
$brush = [System.Drawing.Brushes]::Black
|
||||
$fillGray = New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb(235, 235, 235))
|
||||
|
||||
function New-Canvas([int]$w, [int]$h) {
|
||||
$bmp = New-Object System.Drawing.Bitmap($w, $h)
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
|
||||
$g.TextRenderingHint = [System.Drawing.Text.TextRenderingHint]::AntiAlias
|
||||
$g.Clear([System.Drawing.Color]::White)
|
||||
return ,@($bmp, $g)
|
||||
}
|
||||
|
||||
# 尺寸线箭头 (dx,dy 为箭头指向)
|
||||
function Draw-Arrow($g, [float]$x, [float]$y, [float]$dx, [float]$dy, [float]$size) {
|
||||
$px = -$dy; $py = $dx
|
||||
$p1 = New-Object System.Drawing.PointF(($x + $dx * $size), ($y + $dy * $size))
|
||||
$p2 = New-Object System.Drawing.PointF(($x + $px * $size * 0.45), ($y + $py * $size * 0.45))
|
||||
$p3 = New-Object System.Drawing.PointF(($x - $px * $size * 0.45), ($y - $py * $size * 0.45))
|
||||
$g.FillPolygon($brush, [System.Drawing.PointF[]]@($p1, $p2, $p3))
|
||||
}
|
||||
|
||||
# 表格: rows 为 string[] 数组, cols 为每列宽度
|
||||
function Draw-Table($g, [float]$x, [float]$y, $rows, [float[]]$cols, [float]$rowH) {
|
||||
$top = $y
|
||||
foreach ($r in $rows) {
|
||||
$xx = $x
|
||||
for ($i = 0; $i -lt $cols.Count; $i++) {
|
||||
$g.DrawRectangle($penThin, $xx, $top, $cols[$i], $rowH)
|
||||
$g.DrawString($r[$i], $fontText, $brush, ($xx + 8), ($top + 10))
|
||||
$xx += $cols[$i]
|
||||
}
|
||||
$top += $rowH
|
||||
}
|
||||
}
|
||||
|
||||
# ---- 2D 尺寸图 (中英双语标注) ----
|
||||
function Draw-DimDrawing([string]$outPath, [int]$bore, [int]$stroke, [string]$magnet, [string]$mount) {
|
||||
$c = New-Canvas 1000 1500
|
||||
$bmp = $c[0]; $g = $c[1]
|
||||
$s = 3.0 # px/mm
|
||||
$cx = 500.0
|
||||
$yTop = 320.0 # 前盖顶边
|
||||
$capW = ($bore + 10) * $s
|
||||
$bodyW = ($bore + 4) * $s
|
||||
$rodD = [Math]::Max($bore / 5.0, 3.0) * 2
|
||||
$rodW = $rodD * $s
|
||||
$yFrontCap = $yTop + 10 * $s
|
||||
$yBody = $yFrontCap + ($stroke + 60) * $s
|
||||
$yRearCap = $yBody + 8 * $s
|
||||
$yRodTop = $yTop - ($stroke + 10) * $s
|
||||
$yRodBot = $yTop + 14 * $s
|
||||
$model = "SC$bore`x$stroke$magnet-$mount"
|
||||
$total = 2 * $stroke + 88
|
||||
|
||||
$g.DrawString('欧霓博 OUNIBO · SC 系列标准气缸 尺寸图', $fontTitle, $brush, 80, 40)
|
||||
$g.DrawString('SC Series Standard Cylinder · Dimension Drawing', $fontText, $brush, 82, 92)
|
||||
$g.DrawString("型号 Model: $model", $fontH2, $brush, 82, 128)
|
||||
|
||||
# 外形 (前盖/缸体/后盖/活塞杆)
|
||||
$g.FillRectangle($fillGray, ($cx - $capW / 2), $yTop, $capW, (10 * $s))
|
||||
$g.DrawRectangle($penLine, ($cx - $capW / 2), $yTop, $capW, (10 * $s))
|
||||
$g.FillRectangle($fillGray, ($cx - $bodyW / 2), $yFrontCap, $bodyW, (($stroke + 60) * $s))
|
||||
$g.DrawRectangle($penLine, ($cx - $bodyW / 2), $yFrontCap, $bodyW, (($stroke + 60) * $s))
|
||||
$g.FillRectangle($fillGray, ($cx - $capW / 2), $yBody, $capW, (8 * $s))
|
||||
$g.DrawRectangle($penLine, ($cx - $capW / 2), $yBody, $capW, (8 * $s))
|
||||
$g.FillRectangle($fillGray, ($cx - $rodW / 2), $yRodTop, $rodW, ($yRodBot - $yRodTop))
|
||||
$g.DrawRectangle($penLine, ($cx - $rodW / 2), $yRodTop, $rodW, ($yRodBot - $yRodTop))
|
||||
|
||||
# 中心线
|
||||
$g.DrawLine($penDash, $cx, ($yRodTop - 20), $cx, ($yRearCap + 30))
|
||||
|
||||
# 缸径尺寸线 (跨前盖)
|
||||
$dimY = $yFrontCap + 40
|
||||
$g.DrawLine($penDash, ($cx - $capW / 2), ($yTop + 4), ($cx - $capW / 2), $dimY)
|
||||
$g.DrawLine($penDash, ($cx + $capW / 2), ($yTop + 4), ($cx + $capW / 2), $dimY)
|
||||
$g.DrawLine($penThin, ($cx - $capW / 2), $dimY, ($cx + $capW / 2), $dimY)
|
||||
Draw-Arrow $g ($cx - $capW / 2) $dimY -1 0 9
|
||||
Draw-Arrow $g ($cx + $capW / 2) $dimY 1 0 9
|
||||
$g.DrawString("Ø$bore mm (缸径 Bore)", $fontH2, $brush, ($cx - 70), ($dimY + 10))
|
||||
|
||||
# 行程尺寸线 (活塞杆伸出段, 右侧)
|
||||
$dimX = $cx + $capW / 2 + 80
|
||||
$g.DrawLine($penDash, ($cx + $capW / 2 + 4), $yTop, $dimX, $yTop)
|
||||
$g.DrawLine($penDash, ($cx + $rodW / 2 + 4), $yRodTop, $dimX, $yRodTop)
|
||||
$g.DrawLine($penThin, $dimX, $yRodTop, $dimX, $yTop)
|
||||
Draw-Arrow $g $dimX $yRodTop 0 1 9
|
||||
Draw-Arrow $g $dimX $yTop 0 -1 9
|
||||
$g.DrawString("$stroke mm (行程 Stroke)", $fontH2, $brush, ($dimX + 12), (($yTop + $yRodTop) / 2 - 8))
|
||||
|
||||
# 总长尺寸线 (左侧)
|
||||
$dimX2 = $cx - $capW / 2 - 80
|
||||
$g.DrawLine($penDash, ($cx - $capW / 2 - 4), $yRodTop, $dimX2, $yRodTop)
|
||||
$g.DrawLine($penDash, ($cx - $capW / 2 - 4), $yRearCap, $dimX2, $yRearCap)
|
||||
$g.DrawLine($penThin, $dimX2, $yRodTop, $dimX2, $yRearCap)
|
||||
Draw-Arrow $g $dimX2 $yRodTop 0 1 9
|
||||
Draw-Arrow $g $dimX2 $yRearCap 0 -1 9
|
||||
$g.DrawString("总长 Total: $total mm", $fontH2, $brush, ($dimX2 - 180), (($yRodTop + $yRearCap) / 2 - 8))
|
||||
|
||||
# 磁石标注
|
||||
if ($magnet -eq 'S') {
|
||||
$g.DrawString('磁石 S / Magnet', $fontH2, $brush, ($cx + $bodyW / 2 + 20), ($yFrontCap + 60))
|
||||
$g.DrawLine($penThin, ($cx + $bodyW / 2 + 6), ($yFrontCap + 66), ($cx + $bodyW / 2 + 18), ($yFrontCap + 66))
|
||||
}
|
||||
|
||||
# 信息表
|
||||
$mountDesc = 'FA=前法兰 Front flange / LB=脚座 Foot / CB=中摆 Clevis'
|
||||
$tblRows = @()
|
||||
$tblRows += ,@('项目 Item', '值 Value')
|
||||
$tblRows += ,@('型号 Model', $model)
|
||||
$tblRows += ,@('缸径 Bore', "Ø$bore mm")
|
||||
$tblRows += ,@('行程 Stroke', "$stroke mm")
|
||||
$tblRows += ,@('磁石 Magnet', $(if ($magnet -eq 'S') { 'S 带磁石' } else { '无 None' }))
|
||||
$tblRows += ,@('安装 Mount', $mountDesc)
|
||||
Draw-Table $g 150 1120 $tblRows @(200.0, 500.0) 40
|
||||
|
||||
$g.DrawString('示意图 · 自编示例数据,仅用于目录软件开发 / Sample drawing for development only', $fontSmall, $brush, 150, 1420)
|
||||
|
||||
$bmp.Save($outPath, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$g.Dispose(); $bmp.Dispose()
|
||||
Write-Output ("尺寸图: " + $outPath)
|
||||
}
|
||||
|
||||
# ---- 数据表 JPEG (zh/en) ----
|
||||
function Draw-Datasheet([string]$outPath, [string]$lang) {
|
||||
$c = New-Canvas 1190 1684
|
||||
$bmp = $c[0]; $g = $c[1]
|
||||
$zh = ($lang -eq 'zh')
|
||||
|
||||
if ($zh) {
|
||||
$g.DrawString('欧霓博 OUNIBO 气动元件', $fontTitle, $brush, 70, 50)
|
||||
$g.DrawString('SC 系列标准气缸 · 数据表 Datasheet', $fontH2, $brush, 70, 100)
|
||||
$g.DrawString('型号编码 Order Code: SC{Bore}x{Stroke}{Magnet}-{Mount}', $fontH2, $brush, 70, 150)
|
||||
$encRows = @()
|
||||
$encRows += ,@('段 Segment', '含义 Meaning', '取值 Values')
|
||||
$encRows += ,@('SC', '系列 Series', '固定 Fixed')
|
||||
$encRows += ,@('{Bore}', '缸径 Bore (mm)', '16 / 25 / 32 / 40')
|
||||
$encRows += ,@('{Stroke}', '行程 Stroke (mm)', '25 / 50 / 75 / 100')
|
||||
$encRows += ,@('{Magnet}', '磁石 Magnet', '空=无 None / S=带磁石')
|
||||
$encRows += ,@('{Mount}', '安装 Mount', 'FA=前法兰 / LB=脚座 / CB=中摆')
|
||||
Draw-Table $g 70 200 $encRows @(150.0, 240.0, 360.0) 44
|
||||
|
||||
$g.DrawString('规格 Specifications', $fontH2, $brush, 70, 500)
|
||||
$specRows = @()
|
||||
$specRows += ,@('参数 Parameter', '取值 Values')
|
||||
$specRows += ,@('缸径 Bore (mm)', '16 / 25 / 32 / 40')
|
||||
$specRows += ,@('行程 Stroke (mm)', '25 / 50 / 75 / 100')
|
||||
$specRows += ,@('磁石 Magnet', '无 None / S')
|
||||
$specRows += ,@('安装 Mount', 'FA / LB / CB')
|
||||
Draw-Table $g 70 550 $specRows @(260.0, 400.0) 44
|
||||
|
||||
$g.DrawString('选型约束 Selection Rules', $fontH2, $brush, 70, 780)
|
||||
$g.DrawString('R1 缸径 16 时,行程仅可选 25 / 50', $fontText, $brush, 90, 830)
|
||||
$g.DrawString('R2 磁石 S 仅缸径 >= 25 可选', $fontText, $brush, 90, 870)
|
||||
$g.DrawString('R3 中摆 CB 仅行程 <= 75 可选', $fontText, $brush, 90, 910)
|
||||
|
||||
$g.DrawString('参考尺寸 Reference Dimensions', $fontH2, $brush, 70, 980)
|
||||
$dimRows = @()
|
||||
$dimRows += ,@('型号 Model', '缸径 Bore', '行程 Stroke', '总长 Overall (mm)', '缸体长 Body (mm)')
|
||||
$dimRows += ,@('SC16x25-FA', '16', '25', '138', '85')
|
||||
$dimRows += ,@('SC25x50S-FA', '25', '50', '188', '110')
|
||||
$dimRows += ,@('SC32x50S-LB', '32', '50', '188', '110')
|
||||
$dimRows += ,@('SC40x100S-LB', '40', '100', '288', '160')
|
||||
Draw-Table $g 70 1030 $dimRows @(210.0, 120.0, 130.0, 200.0, 190.0) 44
|
||||
|
||||
$g.DrawString('注 Note: 本数据表为自编示例数据(虚构系列),仅用于目录软件开发测试,非真实产品数据。', $fontSmall, $brush, 70, 1290)
|
||||
$g.DrawString('生成工具: sample-data/tools/gen-images.ps1', $fontSmall, $brush, 70, 1320)
|
||||
} else {
|
||||
$g.DrawString('OUNIBO Pneumatic Components', $fontTitle, $brush, 70, 50)
|
||||
$g.DrawString('SC Series Standard Cylinder · Datasheet', $fontH2, $brush, 70, 100)
|
||||
$g.DrawString('Order Code: SC{Bore}x{Stroke}{Magnet}-{Mount}', $fontH2, $brush, 70, 150)
|
||||
$encRows = @()
|
||||
$encRows += ,@('Segment', 'Meaning', 'Values')
|
||||
$encRows += ,@('SC', 'Series', 'Fixed')
|
||||
$encRows += ,@('{Bore}', 'Bore (mm)', '16 / 25 / 32 / 40')
|
||||
$encRows += ,@('{Stroke}', 'Stroke (mm)', '25 / 50 / 75 / 100')
|
||||
$encRows += ,@('{Magnet}', 'Magnet', 'blank=None / S=Magnet')
|
||||
$encRows += ,@('{Mount}', 'Mounting', 'FA=Front flange / LB=Foot / CB=Clevis')
|
||||
Draw-Table $g 70 200 $encRows @(150.0, 240.0, 360.0) 44
|
||||
|
||||
$g.DrawString('Specifications', $fontH2, $brush, 70, 500)
|
||||
$specRows = @()
|
||||
$specRows += ,@('Parameter', 'Values')
|
||||
$specRows += ,@('Bore (mm)', '16 / 25 / 32 / 40')
|
||||
$specRows += ,@('Stroke (mm)', '25 / 50 / 75 / 100')
|
||||
$specRows += ,@('Magnet', 'None / S')
|
||||
$specRows += ,@('Mounting', 'FA / LB / CB')
|
||||
Draw-Table $g 70 550 $specRows @(260.0, 400.0) 44
|
||||
|
||||
$g.DrawString('Selection Rules', $fontH2, $brush, 70, 780)
|
||||
$g.DrawString('R1 Bore 16: stroke limited to 25 / 50', $fontText, $brush, 90, 830)
|
||||
$g.DrawString('R2 Magnet S requires bore >= 25', $fontText, $brush, 90, 870)
|
||||
$g.DrawString('R3 Clevis CB requires stroke <= 75', $fontText, $brush, 90, 910)
|
||||
|
||||
$g.DrawString('Reference Dimensions', $fontH2, $brush, 70, 980)
|
||||
$dimRows = @()
|
||||
$dimRows += ,@('Model', 'Bore', 'Stroke', 'Overall (mm)', 'Body (mm)')
|
||||
$dimRows += ,@('SC16x25-FA', '16', '25', '138', '85')
|
||||
$dimRows += ,@('SC25x50S-FA', '25', '50', '188', '110')
|
||||
$dimRows += ,@('SC32x50S-LB', '32', '50', '188', '110')
|
||||
$dimRows += ,@('SC40x100S-LB', '40', '100', '288', '160')
|
||||
Draw-Table $g 70 1030 $dimRows @(210.0, 120.0, 130.0, 200.0, 190.0) 44
|
||||
|
||||
$g.DrawString('Note: This datasheet contains sample data (fictional series) for catalog software development only, not real product data.', $fontSmall, $brush, 70, 1290)
|
||||
$g.DrawString('Generated by: sample-data/tools/gen-images.ps1', $fontSmall, $brush, 70, 1320)
|
||||
}
|
||||
|
||||
$bmp.Save($outPath, [System.Drawing.Imaging.ImageFormat]::Jpeg)
|
||||
$g.Dispose(); $bmp.Dispose()
|
||||
Write-Output ("数据表: " + $outPath)
|
||||
}
|
||||
|
||||
# ---- 生成 ----
|
||||
Draw-DimDrawing (Join-Path $dimDir 'SC32x50S-LB.png') 32 50 'S' 'LB'
|
||||
Draw-DimDrawing (Join-Path $dimDir 'SC16x25-FA.png') 16 25 '' 'FA'
|
||||
Draw-Datasheet (Join-Path $dsDir 'SC_datasheet_zh.jpg') 'zh'
|
||||
Draw-Datasheet (Join-Path $dsDir 'SC_datasheet_en.jpg') 'en'
|
||||
72
sample-data/tools/gen-pdf.ps1
Normal file
72
sample-data/tools/gen-pdf.ps1
Normal file
@@ -0,0 +1,72 @@
|
||||
# 将数据表 JPEG 包装成极简单页 A4 PDF (DCTDecode 直嵌, 字节级 xref 偏移)
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-pdf.ps1
|
||||
# 说明: 中文内容以图片形式嵌入, 规避 PDF 中文字体嵌入问题; 结构最小化, 主流阅读器可打开。
|
||||
param(
|
||||
[string]$Jpg,
|
||||
[string]$Pdf
|
||||
)
|
||||
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
$img = [System.Drawing.Image]::FromFile($Jpg)
|
||||
$w = $img.Width
|
||||
$h = $img.Height
|
||||
$img.Dispose()
|
||||
$jpgBytes = [System.IO.File]::ReadAllBytes($Jpg)
|
||||
|
||||
$chunks = New-Object System.Collections.Generic.List[byte[]]
|
||||
$offsets = New-Object System.Collections.Generic.List[int]
|
||||
$pos = 0
|
||||
|
||||
function Add-Ascii([string]$t) {
|
||||
$b = [System.Text.Encoding]::ASCII.GetBytes($t)
|
||||
$chunks.Add($b)
|
||||
$script:pos += $b.Length
|
||||
}
|
||||
function Mark-Obj() { $offsets.Add($script:pos) }
|
||||
|
||||
$imgLen = $jpgBytes.Length + 1
|
||||
$content = 'q 595.28 0 0 841.89 0 0 cm /Im0 Do Q'
|
||||
$contentLen = $content.Length + 1
|
||||
|
||||
Add-Ascii "%PDF-1.4`n"
|
||||
Mark-Obj; Add-Ascii "1 0 obj<< /Type /Catalog /Pages 2 0 R >>endobj`n"
|
||||
Mark-Obj; Add-Ascii "2 0 obj<< /Type /Pages /Kids [3 0 R] /Count 1 >>endobj`n"
|
||||
Mark-Obj; Add-Ascii "3 0 obj<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595.28 841.89] /Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>endobj`n"
|
||||
Mark-Obj; Add-Ascii "4 0 obj<< /Type /XObject /Subtype /Image /Width $w /Height $h /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length $imgLen >>stream`n"
|
||||
$chunks.Add($jpgBytes); $pos += $jpgBytes.Length
|
||||
Add-Ascii "`nendstream`nendobj`n"
|
||||
Mark-Obj; Add-Ascii "5 0 obj<< /Length $contentLen >>stream`n"
|
||||
Add-Ascii "$content`nendstream`nendobj`n"
|
||||
|
||||
$xrefOffset = $pos
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
[void]$sb.Append("xref`n0 6`n0000000000 65535 f `n")
|
||||
foreach ($off in $offsets) {
|
||||
[void]$sb.Append(("{0:0000000000} {1:00000} n `n" -f $off, 0))
|
||||
}
|
||||
Add-Ascii $sb.ToString()
|
||||
Add-Ascii ("trailer`n<< /Size 6 /Root 1 0 R >>`nstartxref`n" + $xrefOffset + "`n%%EOF`n")
|
||||
|
||||
$fs = [System.IO.File]::Create($Pdf)
|
||||
foreach ($b in $chunks) { $fs.Write($b, 0, $b.Length) }
|
||||
$fs.Close()
|
||||
|
||||
# ---- 自检: startxref 指向 xref, 且每个对象偏移处是 "N 0 obj" ----
|
||||
$check = [System.IO.File]::ReadAllBytes($Pdf)
|
||||
$text = [System.Text.Encoding]::ASCII.GetString($check)
|
||||
$m = [regex]::Match($text, 'startxref\s+(\d+)')
|
||||
if (-not $m.Success) { Write-Output ("FAIL: 无 startxref - " + $Pdf); return }
|
||||
$xref = [int64]$m.Groups[1].Value
|
||||
$ok = ($text.Substring($xref, 4) -eq 'xref')
|
||||
$lines = $text.Substring($xref + 4).Split("`n")
|
||||
# xref 段: 行0='xref', 行1='0 6', 行2=第0号自由条目, 行3..7=对象1..5
|
||||
for ($i = 3; $i -lt 8; $i++) {
|
||||
$entry = $lines[$i]
|
||||
$objNum = $i - 2
|
||||
if ($entry -match '^(\d{10}) 00000 n') {
|
||||
$off2 = [int64]$entry.Substring(0, 10)
|
||||
if ($text.Substring($off2, 10) -notmatch ('^' + $objNum + ' 0 obj')) { $ok = $false }
|
||||
} else { $ok = $false }
|
||||
}
|
||||
if ($ok) { Write-Output ("OK: " + $Pdf + " (" + (($check.Length / 1KB).ToString('0') + " KB)")) }
|
||||
else { Write-Output ("FAIL: xref 校验失败 - " + $Pdf) }
|
||||
230
sample-data/tools/gen-step.ps1
Normal file
230
sample-data/tools/gen-step.ps1
Normal file
@@ -0,0 +1,230 @@
|
||||
# 欧霓博 SC 系列示例数据生成器:STEP 变体文件 + params.csv
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-step.ps1
|
||||
# 说明: 以极简封闭 B-rep 圆柱(MANIFOLD_SOLID_BREP)组合出简化气缸模型
|
||||
# (后盖 + 缸体 + 前盖 + 活塞杆 4 个实体), 尺寸随缸径/行程参数变化。
|
||||
|
||||
param(
|
||||
[string]$OutDir = (Join-Path $PSScriptRoot '..\onb-sc')
|
||||
)
|
||||
|
||||
$inv = [System.Globalization.CultureInfo]::InvariantCulture
|
||||
$stepDir = Join-Path $OutDir 'step'
|
||||
$csvPath = Join-Path $OutDir 'params.csv'
|
||||
New-Item -ItemType Directory -Force -Path $stepDir | Out-Null
|
||||
|
||||
function Format-Num([double]$v) { return $v.ToString('0.###', $inv) }
|
||||
|
||||
# 单个圆柱实体的实体行 (OCCT 兼容拓扑: 顶点在圆周上+全圆闭边+接缝边, 共 36 个实体)
|
||||
# 约定: 圆心点 = (x, 0, z) 是仅有的 y=0 点 (供解析器识别); 顶点 = (x, r, z) 在 +Y 方向
|
||||
function New-CylinderEntities([int]$id, [double]$x, [double]$y, [double]$z, [double]$r, [double]$len) {
|
||||
$X = Format-Num $x
|
||||
$Y = Format-Num $y
|
||||
$Z = Format-Num $z
|
||||
$ZT = Format-Num ($z + $len)
|
||||
$R = Format-Num $r
|
||||
$VY = Format-Num ($y + $r) # 顶点在圆周 +Y 处 (y=0 留给圆心供解析)
|
||||
$e = @(
|
||||
"#$($id)=DIRECTION('',(0.,0.,1.));", # 轴
|
||||
"#$($id+1)=CARTESIAN_POINT('',($X,$Y,$Z));", # 底圆心
|
||||
"#$($id+2)=CARTESIAN_POINT('',($X,$Y,$ZT));", # 顶圆心
|
||||
"#$($id+3)=DIRECTION('',(1.,0.,0.));", # 参考方向
|
||||
"#$($id+4)=AXIS2_PLACEMENT_3D('',#$($id+1),#$($id),#$($id+3));", # 底放置
|
||||
"#$($id+5)=CARTESIAN_POINT('',($X,$VY,$Z));", # 底顶点(圆周上)
|
||||
"#$($id+6)=CARTESIAN_POINT('',($X,$VY,$ZT));", # 顶顶点(圆周上)
|
||||
"#$($id+7)=VERTEX_POINT('',#$($id+5));",
|
||||
"#$($id+8)=VERTEX_POINT('',#$($id+6));",
|
||||
"#$($id+9)=CIRCLE('',#$($id+4),$R);", # 底圆
|
||||
"#$($id+10)=EDGE_CURVE('',#$($id+7),#$($id+7),#$($id+9),.T.);", # 底圆闭边
|
||||
"#$($id+11)=CYLINDRICAL_SURFACE('',#$($id+4),$R);",
|
||||
"#$($id+12)=PLANE('',#$($id+4));", # 底面
|
||||
"#$($id+13)=DIRECTION('',(0.,0.,1.));",
|
||||
"#$($id+14)=AXIS2_PLACEMENT_3D('',#$($id+2),#$($id+13),#$($id+3));", # 顶放置
|
||||
"#$($id+15)=PLANE('',#$($id+14));", # 顶面
|
||||
"#$($id+16)=CIRCLE('',#$($id+14),$R);", # 顶圆
|
||||
"#$($id+17)=EDGE_CURVE('',#$($id+8),#$($id+8),#$($id+16),.T.);", # 顶圆闭边
|
||||
"#$($id+18)=DIRECTION('',(0.,0.,1.));",
|
||||
"#$($id+19)=VECTOR('',#$($id+18),1.);",
|
||||
"#$($id+20)=LINE('',#$($id+5),#$($id+19));", # 接缝线
|
||||
"#$($id+21)=EDGE_CURVE('',#$($id+7),#$($id+8),#$($id+20),.T.);", # 接缝边
|
||||
"#$($id+22)=ORIENTED_EDGE('',*,*,#$($id+10),.T.);", # 底环
|
||||
"#$($id+23)=ORIENTED_EDGE('',*,*,#$($id+17),.T.);", # 顶环
|
||||
"#$($id+24)=ORIENTED_EDGE('',*,*,#$($id+21),.T.);", # 壁环: 缝上
|
||||
"#$($id+25)=ORIENTED_EDGE('',*,*,#$($id+17),.T.);",
|
||||
"#$($id+26)=ORIENTED_EDGE('',*,*,#$($id+21),.F.);", # 缝下
|
||||
"#$($id+27)=ORIENTED_EDGE('',*,*,#$($id+10),.F.);",
|
||||
"#$($id+28)=EDGE_LOOP('',(#$($id+22)));",
|
||||
"#$($id+29)=EDGE_LOOP('',(#$($id+23)));",
|
||||
"#$($id+30)=EDGE_LOOP('',(#$($id+24),#$($id+25),#$($id+26),#$($id+27)));",
|
||||
"#$($id+31)=ADVANCED_FACE('',(#$($id+30)),#$($id+11),.T.);", # 柱壁
|
||||
"#$($id+32)=ADVANCED_FACE('',(#$($id+28)),#$($id+12),.F.);", # 底面(法线朝下)
|
||||
"#$($id+33)=ADVANCED_FACE('',(#$($id+29)),#$($id+15),.T.);", # 顶面(法线朝上)
|
||||
"#$($id+34)=CLOSED_SHELL('',(#$($id+31),#$($id+32),#$($id+33)));",
|
||||
"#$($id+35)=MANIFOLD_SOLID_BREP('',#$($id+34));"
|
||||
)
|
||||
return $e
|
||||
}
|
||||
|
||||
# 尾部共享实体: 每个实体注入 STYLED_ITEM 颜色 (occt-import-js 对无颜色模型会崩)
|
||||
# 从 $id 开始: 每实体 7 个颜色实体 + 14 个尾部实体
|
||||
function New-TailEntities([int]$id, [int[]]$solidIds) {
|
||||
$t = New-Object System.Collections.Generic.List[string]
|
||||
$styled = New-Object System.Collections.Generic.List[string]
|
||||
$c = $id
|
||||
foreach ($s in $solidIds) {
|
||||
$t.Add("#$c=COLOUR_RGB('',0.65,0.72,0.85);")
|
||||
$t.Add("#$($c+1)=FILL_AREA_STYLE_COLOUR('',#$c);")
|
||||
$t.Add("#$($c+2)=SURFACE_STYLE_FILL_AREA(#$($c+1));")
|
||||
$t.Add("#$($c+3)=SURFACE_SIDE_STYLE('',(#$($c+2)));")
|
||||
$t.Add("#$($c+4)=SURFACE_STYLE_USAGE(.BOTH.,#$($c+3));")
|
||||
$t.Add("#$($c+5)=PRESENTATION_STYLE_ASSIGNMENT((#$($c+4)));")
|
||||
$t.Add("#$($c+6)=STYLED_ITEM('',(#$($c+5)),#$s);")
|
||||
$styled.Add("#$($c+6)")
|
||||
$c += 7
|
||||
}
|
||||
$items = (($solidIds | ForEach-Object { "#$_" }) + $styled) -join ','
|
||||
$t.Add("#$c=SHAPE_REPRESENTATION('',($items),#$($c+1));")
|
||||
$t.Add("#$($c+1)=(GEOMETRIC_REPRESENTATION_CONTEXT(3)GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#$($c+2)))GLOBAL_UNIT_ASSIGNED_CONTEXT((#$($c+3),#$($c+4),#$($c+5)))REPRESENTATION_CONTEXT('',''));")
|
||||
$t.Add("#$($c+2)=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-06),#$($c+3),'');")
|
||||
$t.Add("#$($c+3)=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));")
|
||||
$t.Add("#$($c+4)=(NAMED_UNIT(*)PLANE_ANGLE_UNIT()SI_UNIT($,.RADIAN.));")
|
||||
$t.Add("#$($c+5)=(NAMED_UNIT(*)SI_UNIT($,.STERADIAN.)SOLID_ANGLE_UNIT());")
|
||||
$t.Add("#$($c+6)=PRODUCT('OUNIBO_SC','OUNIBO_SC','',(#$($c+7)));")
|
||||
$t.Add("#$($c+7)=PRODUCT_CONTEXT('',#$($c+8),'mechanical');")
|
||||
$t.Add("#$($c+8)=APPLICATION_CONTEXT('configuration controlled 3d designs of mechanical parts and assemblies');")
|
||||
$t.Add("#$($c+9)=PRODUCT_DEFINITION_FORMATION('','',#$($c+6));")
|
||||
$t.Add("#$($c+10)=PRODUCT_DEFINITION('design','',#$($c+9),#$($c+11));")
|
||||
$t.Add("#$($c+11)=PRODUCT_DEFINITION_CONTEXT('part definition',#$($c+8),'design');")
|
||||
$t.Add("#$($c+12)=PRODUCT_DEFINITION_SHAPE('','',#$($c+10));")
|
||||
$t.Add("#$($c+13)=SHAPE_DEFINITION_REPRESENTATION(#$($c+12),#$c);")
|
||||
return $t
|
||||
}
|
||||
|
||||
# ---- 型号与规则定义 (见 coding-rules.md) ----
|
||||
# 缸径 16: 行程仅 25/50, 无磁石; 磁石 S 要求缸径>=25; 中摆 CB 要求行程<=75
|
||||
$bores = @(16, 25, 32, 40)
|
||||
$strokesAll = @(25, 50, 75, 100)
|
||||
$mountsAll = @('FA', 'LB', 'CB')
|
||||
|
||||
$encNoBom = New-Object System.Text.UTF8Encoding($false)
|
||||
$encBom = New-Object System.Text.UTF8Encoding($true)
|
||||
$csvLines = New-Object System.Collections.Generic.List[string]
|
||||
$csvLines.Add('model_code,bore,stroke,magnet,mount,step_file')
|
||||
|
||||
$count = 0
|
||||
foreach ($bore in $bores) {
|
||||
$strokes = if ($bore -eq 16) { @(25, 50) } else { $strokesAll }
|
||||
$magnets = if ($bore -eq 16) { @('') } else { @('', 'S') }
|
||||
foreach ($stroke in $strokes) {
|
||||
$mounts = if ($stroke -gt 75) { @('FA', 'LB') } else { $mountsAll }
|
||||
foreach ($magnet in $magnets) {
|
||||
foreach ($mount in $mounts) {
|
||||
$model = "SC$bore`x$stroke$magnet-$mount"
|
||||
$stepFile = "$model.step"
|
||||
|
||||
# ---- 简化气缸尺寸 (mm): 后盖 + 缸体 + 前盖 + 活塞杆, 沿 Z 轴 ----
|
||||
$bodyR = $bore / 2.0 + 2.0 # 缸体外径
|
||||
$bodyLen = $stroke + 60.0 # 缸体长度(随行程)
|
||||
$capR = $bore / 2.0 + 5.0 # 端盖外径
|
||||
$capLen = 8.0 # 后盖厚度
|
||||
$fcapLen = 10.0 # 前盖厚度
|
||||
$rodR = [Math]::Max($bore / 5.0, 3.0)
|
||||
$rodBot = $bodyLen - 14.0 # 活塞杆埋入缸体
|
||||
$rodLen = $stroke + 34.0
|
||||
|
||||
$lines = New-Object System.Collections.Generic.List[string]
|
||||
$lines.Add('ISO-10303-21;')
|
||||
$lines.Add('HEADER;')
|
||||
$lines.Add("FILE_DESCRIPTION(('OUNIBO SC series sample part $model'),'2;1');")
|
||||
$lines.Add("FILE_NAME('$stepFile','2026-06-02T00:00:00',('OUNIBO'),('OUNIBO'),'','','');")
|
||||
$lines.Add("FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));")
|
||||
$lines.Add('ENDSEC;')
|
||||
$lines.Add('DATA;')
|
||||
|
||||
$ids = New-Object System.Collections.Generic.List[int]
|
||||
$nextId = 1
|
||||
$cyls = New-Object System.Collections.Generic.List[object]
|
||||
$cyls.Add(@{ z = -$capLen; r = $capR; len = $capLen }) # 后盖
|
||||
$cyls.Add(@{ z = 0.0; r = $bodyR; len = $bodyLen }) # 缸体
|
||||
$cyls.Add(@{ z = $bodyLen; r = $capR; len = $fcapLen }) # 前盖
|
||||
$cyls.Add(@{ z = $rodBot; r = $rodR; len = $rodLen }) # 活塞杆
|
||||
foreach ($cyl in $cyls) {
|
||||
[string[]]$entities = New-CylinderEntities -id $nextId -x 0 -y 0 -z $cyl['z'] -r $cyl['r'] -len $cyl['len']
|
||||
$lines.AddRange($entities)
|
||||
$ids.Add($nextId + 35) # MANIFOLD_SOLID_BREP 实体号
|
||||
$nextId += 36
|
||||
}
|
||||
[string[]]$tail = New-TailEntities -id $nextId -solidIds $ids.ToArray()
|
||||
$lines.AddRange($tail)
|
||||
$lines.Add('ENDSEC;')
|
||||
$lines.Add('END-ISO-10303-21;')
|
||||
|
||||
[System.IO.File]::WriteAllLines((Join-Path $stepDir $stepFile), $lines.ToArray(), $encNoBom)
|
||||
$csvLines.Add("$model,$bore,$stroke,$magnet,$mount,$stepFile")
|
||||
$count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ---- MAQ 迷你气缸系列 (多系列演示): 缸径 6/10/16, 行程 10/20/30/40 ----
|
||||
# 规则: 缸径 6 仅行程 10/20 且无磁石; 安装仅 FA/LB
|
||||
$csvLinesM = New-Object System.Collections.Generic.List[string]
|
||||
$csvLinesM.Add('model_code,bore,stroke,magnet,mount,step_file')
|
||||
$countM = 0
|
||||
foreach ($bore in @(6, 10, 16)) {
|
||||
$strokes = if ($bore -eq 6) { @(10, 20) } else { @(10, 20, 30, 40) }
|
||||
$magnets = if ($bore -eq 6) { @('') } else { @('', 'S') }
|
||||
foreach ($stroke in $strokes) {
|
||||
foreach ($magnet in $magnets) {
|
||||
foreach ($mount in @('FA', 'LB')) {
|
||||
$model = "MAQ$bore`x$stroke$magnet-$mount"
|
||||
$stepFile = "$model.step"
|
||||
|
||||
$bodyR = $bore / 2.0 + 1.5
|
||||
$bodyLen = $stroke + 40.0
|
||||
$capR = $bore / 2.0 + 4.0
|
||||
$capLen = 6.0
|
||||
$fcapLen = 8.0
|
||||
$rodR = [Math]::Max($bore / 5.0, 2.0)
|
||||
$rodBot = $bodyLen - 10.0
|
||||
$rodLen = $stroke + 24.0
|
||||
|
||||
$lines = New-Object System.Collections.Generic.List[string]
|
||||
$lines.Add('ISO-10303-21;')
|
||||
$lines.Add('HEADER;')
|
||||
$lines.Add("FILE_DESCRIPTION(('OUNIBO MAQ series sample part $model'),'2;1');")
|
||||
$lines.Add("FILE_NAME('$stepFile','2026-06-02T00:00:00',('OUNIBO'),('OUNIBO'),'','','');")
|
||||
$lines.Add("FILE_SCHEMA(('CONFIG_CONTROL_DESIGN'));")
|
||||
$lines.Add('ENDSEC;')
|
||||
$lines.Add('DATA;')
|
||||
|
||||
$ids = New-Object System.Collections.Generic.List[int]
|
||||
$nextId = 1
|
||||
$cyls = New-Object System.Collections.Generic.List[object]
|
||||
$cyls.Add(@{ z = -$capLen; r = $capR; len = $capLen })
|
||||
$cyls.Add(@{ z = 0.0; r = $bodyR; len = $bodyLen })
|
||||
$cyls.Add(@{ z = $bodyLen; r = $capR; len = $fcapLen })
|
||||
$cyls.Add(@{ z = $rodBot; r = $rodR; len = $rodLen })
|
||||
foreach ($cyl in $cyls) {
|
||||
[string[]]$entities = New-CylinderEntities -id $nextId -x 0 -y 0 -z $cyl['z'] -r $cyl['r'] -len $cyl['len']
|
||||
$lines.AddRange($entities)
|
||||
$ids.Add($nextId + 35)
|
||||
$nextId += 36
|
||||
}
|
||||
[string[]]$tail = New-TailEntities -id $nextId -solidIds $ids.ToArray()
|
||||
$lines.AddRange($tail)
|
||||
$lines.Add('ENDSEC;')
|
||||
$lines.Add('END-ISO-10303-21;')
|
||||
|
||||
[System.IO.File]::WriteAllLines((Join-Path $stepDir $stepFile), $lines.ToArray(), $encNoBom)
|
||||
$csvLinesM.Add("$model,$bore,$stroke,$magnet,$mount,$stepFile")
|
||||
$countM++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[System.IO.File]::WriteAllLines($csvPath, $csvLines.ToArray(), $encBom)
|
||||
[System.IO.File]::WriteAllLines((Join-Path $OutDir 'params_maq.csv'), $csvLinesM.ToArray(), $encBom)
|
||||
Write-Output "生成完成: SC $count 个 + MAQ $countM 个 STEP 文件 -> $stepDir"
|
||||
Write-Output "参数表: $csvPath / params_maq.csv"
|
||||
103
sample-data/tools/gen-xlsx.ps1
Normal file
103
sample-data/tools/gen-xlsx.ps1
Normal file
@@ -0,0 +1,103 @@
|
||||
# 把 params.csv 生成一份真正的 .xlsx (最小 OOXML 结构, 免依赖 Excel)
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\gen-xlsx.ps1
|
||||
# 说明: 仅供查看格式参考; Builder 导入用 CSV (Excel 里另存为 CSV 即可)。
|
||||
param(
|
||||
[string]$Csv = (Join-Path $PSScriptRoot '..\onb-sc\params.csv'),
|
||||
[string]$Out = (Join-Path $PSScriptRoot '..\onb-sc\params.xlsx')
|
||||
)
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
|
||||
function Xml-Esc([string]$s) {
|
||||
return $s.Replace('&', '&').Replace('<', '<').Replace('>', '>').Replace('"', '"')
|
||||
}
|
||||
|
||||
# 列号 → 列字母 (A..Z, AA..)
|
||||
function Col-Name([int]$i) {
|
||||
$name = ''
|
||||
$n = $i + 1
|
||||
while ($n -gt 0) {
|
||||
$r = ($n - 1) % 26
|
||||
$name = [char](65 + $r) + $name
|
||||
$n = [int](($n - 1) / 26)
|
||||
}
|
||||
return $name
|
||||
}
|
||||
|
||||
$rows = Get-Content $Csv | Where-Object { -not [string]::IsNullOrWhiteSpace($_) }
|
||||
if ($rows.Count -lt 1) { Write-Output "CSV 为空"; exit 1 }
|
||||
|
||||
# ---- sheet1.xml ----
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
[void]$sb.Append('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>')
|
||||
[void]$sb.Append('<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>')
|
||||
for ($r = 0; $r -lt $rows.Count; $r++) {
|
||||
$cells = $rows[$r].Split(',')
|
||||
[void]$sb.Append('<row r="' + ($r + 1) + '">')
|
||||
for ($c = 0; $c -lt $cells.Count; $c++) {
|
||||
$val = $cells[$c]
|
||||
$ref = (Col-Name $c) + ($r + 1)
|
||||
$num = 0.0
|
||||
if ($r -gt 0 -and [double]::TryParse($val, [System.Globalization.NumberStyles]::Float, [System.Globalization.CultureInfo]::InvariantCulture, [ref]$num)) {
|
||||
[void]$sb.Append('<c r="' + $ref + '"><v>' + $val + '</v></c>')
|
||||
} else {
|
||||
[void]$sb.Append('<c r="' + $ref + '" t="inlineStr"><is><t xml:space="preserve">' + (Xml-Esc $val) + '</t></is></c>')
|
||||
}
|
||||
}
|
||||
[void]$sb.Append('</row>')
|
||||
}
|
||||
[void]$sb.Append('</sheetData></worksheet>')
|
||||
$sheetXml = $sb.ToString()
|
||||
|
||||
# ---- 其余固定部件 ----
|
||||
$contentTypes = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
||||
'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
|
||||
'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>' +
|
||||
'<Default Extension="xml" ContentType="application/xml"/>' +
|
||||
'<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>' +
|
||||
'<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>' +
|
||||
'</Types>'
|
||||
$rels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
||||
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
||||
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
|
||||
'</Relationships>'
|
||||
$workbook = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
||||
'<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
|
||||
'<sheets><sheet name="SC系列" sheetId="1" r:id="rId1"/></sheets></workbook>'
|
||||
$workbookRels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
|
||||
'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
|
||||
'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
|
||||
'</Relationships>'
|
||||
|
||||
$enc = New-Object System.Text.UTF8Encoding($false)
|
||||
|
||||
# 手动 CreateEntry 控制条目名 (OOXML 规范要求正斜杠, CreateFromDirectory 会生成反斜杠)
|
||||
if (Test-Path $Out) { Remove-Item $Out -Force }
|
||||
$zip = [System.IO.Compression.ZipFile]::Open($Out, [System.IO.Compression.ZipArchiveMode]::Create)
|
||||
function Add-Entry([string]$name, [string]$content) {
|
||||
$e = $zip.CreateEntry($name)
|
||||
$sw = New-Object System.IO.StreamWriter($e.Open(), $enc)
|
||||
$sw.Write($content)
|
||||
$sw.Close()
|
||||
}
|
||||
Add-Entry '[Content_Types].xml' $contentTypes
|
||||
Add-Entry '_rels/.rels' $rels
|
||||
Add-Entry 'xl/workbook.xml' $workbook
|
||||
Add-Entry 'xl/_rels/workbook.xml.rels' $workbookRels
|
||||
Add-Entry 'xl/worksheets/sheet1.xml' $sheetXml
|
||||
$zip.Dispose()
|
||||
|
||||
# ---- 自检: 重新打开并校验 XML 结构 ----
|
||||
$z = [System.IO.Compression.ZipFile]::OpenRead($Out)
|
||||
$ok = $true
|
||||
foreach ($part in @('[Content_Types].xml', '_rels/.rels', 'xl/workbook.xml', 'xl/_rels/workbook.xml.rels', 'xl/worksheets/sheet1.xml')) {
|
||||
try {
|
||||
$e = $z.GetEntry($part)
|
||||
$sr = New-Object System.IO.StreamReader($e.Open())
|
||||
$null = [xml]$sr.ReadToEnd()
|
||||
$sr.Close()
|
||||
} catch { Write-Output ("XML 无效: " + $part); $ok = $false }
|
||||
}
|
||||
$z.Dispose()
|
||||
if ($ok) { Write-Output ("xlsx 生成并通过自检: " + $Out + " (" + $rows.Count + " 行)") }
|
||||
58
sample-data/tools/import-master.vb
Normal file
58
sample-data/tools/import-master.vb
Normal file
@@ -0,0 +1,58 @@
|
||||
' 母模构建 (管线验证版): 模板部件 + STEP 导入真实几何 + 注入参数化表达式
|
||||
' 用法: WorkingDirectory=母模目录, 目录内放 src.step (真实 STEP) 与 KC_master.prt (模板副本)
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ImportMaster
|
||||
Sub Main()
|
||||
Dim lw As ListingWindow = Session.GetSession().ListingWindow
|
||||
lw.Open()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim outPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Dim srcStep As String = Path.Combine(cur, "src.step")
|
||||
If Not File.Exists(outPath) OrElse Not File.Exists(srcStep) Then
|
||||
lw.WriteLine("缺文件: " & outPath & " / " & srcStep)
|
||||
Return
|
||||
End If
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
If w Is Nothing Then
|
||||
lw.WriteLine("打开失败")
|
||||
Return
|
||||
End If
|
||||
|
||||
' 导入真实 STEP 到当前工作部件 (WorkPart; 前提: 模板副本必须可写, FileOpenFlag 打开源文件)
|
||||
Dim imp As NXOpen.Step214Importer = Session.GetSession().DexManager.CreateStep214Importer()
|
||||
imp.InputFile = srcStep
|
||||
imp.ImportTo = NXOpen.Step214Importer.ImportToOption.WorkPart
|
||||
imp.FileOpenFlag = True
|
||||
imp.Commit()
|
||||
imp.Destroy()
|
||||
lw.WriteLine("STEP 已导入到工作部件")
|
||||
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
ufs.Modl.Update()
|
||||
|
||||
' 注入参数化表达式
|
||||
Dim mm As NXOpen.Unit = w.UnitCollection.FindObject("MilliMeter")
|
||||
If mm IsNot Nothing Then
|
||||
w.Expressions.CreateExpressionWithUnit("Number", "bore=32", mm)
|
||||
w.Expressions.CreateExpressionWithUnit("Number", "stroke=10", mm)
|
||||
Else
|
||||
w.Expressions.CreateExpression("Number", "bore=32")
|
||||
w.Expressions.CreateExpression("Number", "stroke=10")
|
||||
End If
|
||||
w.Expressions.CreateExpression("Number", "magOn=0")
|
||||
w.Expressions.CreateExpression("Number", "mountIdx=0")
|
||||
lw.WriteLine("表达式已注入")
|
||||
|
||||
Dim pss As NXOpen.PartSaveStatus = w.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
lw.WriteLine("已保存: " & outPath)
|
||||
End Sub
|
||||
End Module
|
||||
38
sample-data/tools/inject-colors.ps1
Normal file
38
sample-data/tools/inject-colors.ps1
Normal file
@@ -0,0 +1,38 @@
|
||||
# 实验: 给极简 STEP 注入 STYLED_ITEM 颜色实体 (测试 occt-import-js 对无颜色模型的兼容性)
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\inject-colors.ps1 -In x.step -Out y.step
|
||||
param([string]$In, [string]$Out)
|
||||
$text = [System.IO.File]::ReadAllText($In)
|
||||
$maxId = 0
|
||||
foreach ($m in [regex]::Matches($text, '#(\d+)\s*=')) { $id = [int]$m.Groups[1].Value; if ($id -gt $maxId) { $maxId = $id } }
|
||||
$next = $maxId + 1
|
||||
|
||||
# 收集实体 (保持文件顺序: 插入到 ENDSEC 前)
|
||||
$solids = @()
|
||||
foreach ($m in [regex]::Matches($text, '#(\d+)=MANIFOLD_SOLID_BREP')) { $solids += [int]$m.Groups[1].Value }
|
||||
|
||||
$newEntities = @()
|
||||
$styledIds = @()
|
||||
foreach ($s in $solids) {
|
||||
$newEntities += "#$next=COLOUR_RGB('',0.65,0.72,0.85);"
|
||||
$newEntities += "#$($next+1)=FILL_AREA_STYLE_COLOUR('',#$next);"
|
||||
$newEntities += "#$($next+2)=SURFACE_STYLE_FILL_AREA(#$($next+1));"
|
||||
$newEntities += "#$($next+3)=SURFACE_SIDE_STYLE('',(#$($next+2)));"
|
||||
$newEntities += "#$($next+4)=SURFACE_STYLE_USAGE(.BOTH.,#$($next+3));"
|
||||
$newEntities += "#$($next+5)=PRESENTATION_STYLE_ASSIGNMENT((#$($next+4)));"
|
||||
$newEntities += "#$($next+6)=STYLED_ITEM('',(#$($next+5)),#$s);"
|
||||
$styledIds += "#$($next+6)"
|
||||
$next += 7
|
||||
}
|
||||
|
||||
# 把 STYLED_ITEM 追加进 SHAPE_REPRESENTATION 的 items 列表
|
||||
$shapeRe = [regex]::Match($text, '#\d+=SHAPE_REPRESENTATION\('''',\(([^)]*)\),#\d+\);')
|
||||
if (-not $shapeRe.Success) { Write-Output "未找到 SHAPE_REPRESENTATION"; exit 1 }
|
||||
$oldItems = $shapeRe.Groups[2].Value
|
||||
$newItems = $oldItems + ',' + ($styledIds -join ',')
|
||||
$text = $text.Remove($shapeRe.Groups[2].Index, $shapeRe.Groups[2].Length).Insert($shapeRe.Groups[2].Index, $newItems)
|
||||
|
||||
$insert = ($newEntities -join "`r`n") + "`r`n"
|
||||
$ends = $text.IndexOf('ENDSEC;', $text.IndexOf('ENDSEC;') + 7) # 第二个 ENDSEC (DATA 结束)
|
||||
$text = $text.Insert($ends, $insert)
|
||||
[System.IO.File]::WriteAllText($Out, $text, (New-Object System.Text.UTF8Encoding($false)))
|
||||
Write-Output ("已注入颜色: " + $solids.Count + " 个实体 → " + $Out)
|
||||
122
sample-data/tools/nx-batch-export-glb.vb
Normal file
122
sample-data/tools/nx-batch-export-glb.vb
Normal file
@@ -0,0 +1,122 @@
|
||||
' =============================================================================
|
||||
' NX Journal: 参数化母模 + 参数表 → 批量导出 GLB 轻量化网格 (供网页 3D 预览)
|
||||
'
|
||||
' 背景: 网页 3D 走"轻量网格"路线 (参照 CADENAS 服务器端轻量化思路, 但用 NX
|
||||
' 离线预转换, 零服务器成本): 网页优先加载 GLB (快/稳/手机流畅),
|
||||
' STEP 照旧提供下载。见 项目总览.md 待办 9。
|
||||
'
|
||||
' 使用方法:
|
||||
' 1) 与 nx-batch-export.vb 相同: 母模用表达式驱动, 表达式名与 CSV 列名一致
|
||||
' 2) 关键一步 (NX 2506): 菜单 工具→日记→录制 → 手动执行
|
||||
' 文件→导出→Extended Reality (导出类型选 GLB, 单个二进制文件)
|
||||
' → 停止录制 → 把录出的代码替换下方 ExportGlb 函数体
|
||||
' (XR 导出的 API 与 DexBuilder 不同, 录出的代码就是你这台 NX 的真实接口,
|
||||
' 这是 Siemens 官方推荐的获取方式; 不同小版本录出的代码可能不同,
|
||||
' 所以模板不硬写该 API)
|
||||
' 3) 工具→日记→播放 选本文件运行
|
||||
'
|
||||
' 输出: 每个变体一个 .glb 文件, 放到网页站点的 mesh/ 目录,
|
||||
' app.js 优先用 three.js GLTFLoader 加载, 失败回退 STEP 查看器/Canvas。
|
||||
'
|
||||
' 注意: 中文注释需文件保存为 UTF-8 带 BOM (fix-bom.ps1 已处理)。
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module BatchGlbExport
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim workPart As Part = theSession.Parts.Work
|
||||
Dim lw As ListingWindow = theSession.ListingWindow
|
||||
|
||||
Sub Main()
|
||||
|
||||
' ============================ CONFIG 区 ============================
|
||||
Dim csvPath As String = "D:\catalog\params.csv" ' 参数表 (与 STEP 导出同一张表)
|
||||
Dim outDir As String = "D:\catalog\glb_out\" ' GLB 输出目录
|
||||
' 表达式名 → CSV 列名 映射 (母模表达式名 : 参数表列名)
|
||||
Dim exprMap As New System.Collections.Generic.Dictionary(Of String, String)()
|
||||
exprMap.Add("bore", "bore")
|
||||
exprMap.Add("stroke", "stroke")
|
||||
' ================================================================
|
||||
|
||||
lw.Open()
|
||||
lw.WriteLine("GLB 批量导出开始: " & csvPath)
|
||||
Directory.CreateDirectory(outDir)
|
||||
|
||||
Dim lines() As String = File.ReadAllLines(csvPath)
|
||||
If lines.Length < 2 Then
|
||||
lw.WriteLine("参数表为空!")
|
||||
Return
|
||||
End If
|
||||
|
||||
Dim header() As String = lines(0).Split(","c)
|
||||
Dim colStep As Integer = Array.IndexOf(header, "step_file")
|
||||
If colStep < 0 Then
|
||||
lw.WriteLine("参数表缺少 step_file 列 (用于推导 GLB 文件名)!")
|
||||
Return
|
||||
End If
|
||||
' 预解析列号
|
||||
Dim colOf As New System.Collections.Generic.Dictionary(Of String, Integer)()
|
||||
For Each kv As KeyValuePair(Of String, String) In exprMap
|
||||
colOf(kv.Key) = Array.IndexOf(header, kv.Value)
|
||||
If colOf(kv.Key) < 0 Then
|
||||
lw.WriteLine("参数表缺少列: " & kv.Value)
|
||||
Return
|
||||
End If
|
||||
Next
|
||||
|
||||
Dim okCount As Integer = 0
|
||||
Dim failCount As Integer = 0
|
||||
|
||||
For i As Integer = 1 To lines.Length - 1
|
||||
If lines(i).Trim().Length = 0 Then Continue For
|
||||
Dim c() As String = lines(i).Split(","c)
|
||||
Dim model As String = c(0).Trim()
|
||||
' GLB 文件名与 STEP 同名不同后缀 (网页端按型号拼 glb 路径)
|
||||
Dim glbFile As String = Path.ChangeExtension(c(colStep).Trim(), ".glb")
|
||||
lw.Write("[" & i & "/" & (lines.Length - 1) & "] " & model & " ... ")
|
||||
|
||||
Try
|
||||
' ---- 1) 设置表达式 ----
|
||||
For Each kv As KeyValuePair(Of String, String) In exprMap
|
||||
workPart.Expressions.Edit(kv.Key, c(colOf(kv.Key)).Trim())
|
||||
Next
|
||||
|
||||
' ---- 2) 更新模型 ----
|
||||
Dim update As NXOpen.Update = theSession.CreateUpdate()
|
||||
Dim nErrs As Integer = 0
|
||||
update.InterpartDelay = False
|
||||
update.ModelingInterpartUpdate = True
|
||||
update.DoUpdate(nErrs)
|
||||
update.Dispose()
|
||||
If nErrs > 0 Then
|
||||
lw.WriteLine("警告: 更新有 " & nErrs & " 个错误")
|
||||
End If
|
||||
|
||||
' ---- 3) 导出 GLB (用录制日记替换此函数体) ----
|
||||
ExportGlb(Path.Combine(outDir, glbFile))
|
||||
okCount += 1
|
||||
lw.WriteLine("OK")
|
||||
|
||||
Catch ex As Exception
|
||||
failCount += 1
|
||||
lw.WriteLine("FAIL: " & ex.Message)
|
||||
End Try
|
||||
Next
|
||||
|
||||
lw.WriteLine("")
|
||||
lw.WriteLine("完成: " & okCount & " 成功, " & failCount & " 失败, 输出目录: " & outDir)
|
||||
End Sub
|
||||
|
||||
' GLB 导出: 用【工具→日记→录制】录一次 文件→导出→Extended Reality 后,
|
||||
' 把录出的代码粘进这里, 并把其中写死的输出路径改成参数 fileName。
|
||||
Sub ExportGlb(fileName As String)
|
||||
lw.WriteLine("ExportGlb 未实现: 请按文件头说明录制导出操作并替换本函数")
|
||||
Throw New Exception("ExportGlb 未实现")
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
215
sample-data/tools/nx-batch-export.vb
Normal file
215
sample-data/tools/nx-batch-export.vb
Normal file
@@ -0,0 +1,215 @@
|
||||
' =============================================================================
|
||||
' NX Journal: 参数化母模 + 参数表 → 批量导出 STEP (欧霓博目录软件专用)
|
||||
'
|
||||
' ▍这个脚本干什么
|
||||
' 逐行读取 CSV 参数表 (每行 = 一个型号: 型号编码 + bore/stroke/magnet/mount... + step_file),
|
||||
' 对每个型号: 修改母模表达式 → 更新模型 → 导出 STEP214 文件。
|
||||
' ★ 已按 step_file 去重: 多个型号行共用同一文件时只导出一次 (52,154 行型号
|
||||
' 只会导出唯一几何文件, 例如 KC 系列 2,772 个)。断点续跑: 输出目录已存在的文件会跳过。
|
||||
'
|
||||
' ▍使用前置 (母模要求, 详见《NX母模建模操作说明书.md》)
|
||||
' 1) 母模中所有"会变"尺寸必须用表达式驱动, 表达式名与 CSV 列名一字不差
|
||||
' 2) 磁石槽/安装形式/双轴等"有无"特征 → 按表达式抑制 (Suppression by Expression)
|
||||
' 3) 建模验收: 手动改 bore/stroke/magOn/mountIdx 各验证过重建无误
|
||||
'
|
||||
' ▍运行方法
|
||||
' NX 打开母模 → 菜单 工具 → 日记 → 播放 → 选本文件
|
||||
' (注意: Ctrl+U 只认 .dll, 本 .vb 必须走"日记→播放")
|
||||
'
|
||||
' ▍CONFIG 区修改清单 (建模工程师每次只改这里)
|
||||
' [1] csvPath / outDir 两个路径
|
||||
' [2] exprMap — 尺寸映射: NX 表达式名 → CSV 列名 (新增独立尺寸表达式就加一行)
|
||||
' [3] switchRules — 枚举开关: {CSV列名, 列值, NX表达式名, 表达式值}
|
||||
' 例: 磁石列 "M" → magOn=1; 固定形式 "FA" → mountIdx=1 (取值按母模约定)
|
||||
' [4] exportStl / stlOutDir — 同步导出 STL 网格 (桌面目录软件窗口内 3D 预览用, 开源 Helix 渲染)
|
||||
' STL 文件名与 STEP 同名 (.stl), 放在 catalog\mesh\ 目录; 不需要窗口内预览就 exportStl = False
|
||||
'
|
||||
' ▍注意
|
||||
' - DexBuilder 导出接口 NX 10 ~ NX 2506 兼容; 若你的 NX 版本 API 有差异,
|
||||
' 用【工具→日记→录制】录一次 文件→导出→STEP214, 替换 ExportStep 函数体。
|
||||
' - 中文注释需 UTF-8 带 BOM (改完跑 fix-bom.ps1), 否则 NX 读乱。
|
||||
' - 导出失败的行不会中断整体, 最后汇总统计写进 NX 信息窗口 (ListingWindow)。
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module BatchStepExport
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim workPart As Part = theSession.Parts.Work
|
||||
Dim lw As ListingWindow = theSession.ListingWindow
|
||||
|
||||
Sub Main()
|
||||
|
||||
' ╔════════════════════════════ CONFIG 区 (只改这里) ═══════════════════════════════╗
|
||||
' ║ [1] 两个路径 ║
|
||||
' ╚═════════════════════════════════════════════════════════════════════════════════╝
|
||||
Dim csvPath As String = "D:\catalog\params.csv" ' 参数表路径 → 改为 catalog\csv\<系列>.csv
|
||||
Dim outDir As String = "D:\catalog\step_out\" ' STEP 输出目录 → 改为 catalog\step\
|
||||
' (目录不存在会自动创建)
|
||||
|
||||
' ╔═════════════════════════════════════════════════════════════════════════════════╗
|
||||
' ║ [4] STL 网格同步导出 (窗口内 3D 预览用; 不开就设 False) ║
|
||||
' ╚═════════════════════════════════════════════════════════════════════════════════╝
|
||||
Dim exportStl As Boolean = True ' True = STEP 导出时同步导出同名 STL
|
||||
Dim stlOutDir As String = "D:\catalog\mesh\" ' STL 输出目录 → 改为 catalog\mesh\
|
||||
|
||||
' ╔═════════════════════════════════════════════════════════════════════════════════╗
|
||||
' ║ [2] 尺寸映射 exprMap: "NX母模表达式名" → "CSV列名" ║
|
||||
' ║ 母模里每个独立尺寸表达式加一行; 联动尺寸(公式)不用写 ║
|
||||
' ╚═════════════════════════════════════════════════════════════════════════════════╝
|
||||
Dim exprMap As New System.Collections.Generic.Dictionary(Of String, String)()
|
||||
exprMap.Add("bore", "bore") ' 缸径: 母模表达式 "bore" ← CSV 列 "bore"
|
||||
exprMap.Add("stroke", "stroke") ' 行程: 母模表达式 "stroke" ← CSV 列 "stroke"
|
||||
' 示例(按需取消注释):
|
||||
' exprMap.Add("magPos", "magpos") ' 磁石位置是独立规格时: CSV 要有 magpos 列
|
||||
|
||||
' ╔═════════════════════════════════════════════════════════════════════════════════╗
|
||||
' ║ [3] 枚举开关映射 switchRules: 每行 = {CSV列名, 列值, NX表达式名, 表达式值} ║
|
||||
' ║ 作用: 该型号在这一列的值 = 列值 时, 把 NX 表达式设为 表达式值 ║
|
||||
' ║ (驱动母模里"按表达式抑制"的开关, 空串列值写作 "") ║
|
||||
' ╚═════════════════════════════════════════════════════════════════════════════════╝
|
||||
Dim switchRules As New System.Collections.Generic.List(Of String())()
|
||||
switchRules.Add({"magnet", "", "magOn", "0"})
|
||||
switchRules.Add({"magnet", "M", "magOn", "1"})
|
||||
switchRules.Add({"mount", "", "mountIdx", "0"})
|
||||
switchRules.Add({"mount", "FA", "mountIdx", "1"})
|
||||
switchRules.Add({"mount", "FB", "mountIdx", "2"})
|
||||
switchRules.Add({"mount", "CA", "mountIdx", "3"})
|
||||
switchRules.Add({"mount", "CB", "mountIdx", "4"})
|
||||
switchRules.Add({"mount", "LB", "mountIdx", "5"})
|
||||
switchRules.Add({"mount", "YB", "mountIdx", "6"})
|
||||
' ================================================================
|
||||
|
||||
lw.Open()
|
||||
lw.WriteLine("批量导出开始: " & csvPath)
|
||||
Directory.CreateDirectory(outDir)
|
||||
If exportStl Then
|
||||
Directory.CreateDirectory(stlOutDir)
|
||||
lw.WriteLine("同步导出 STL 网格: " & stlOutDir)
|
||||
End If
|
||||
|
||||
Dim lines() As String = File.ReadAllLines(csvPath)
|
||||
If lines.Length < 2 Then
|
||||
lw.WriteLine("参数表为空!")
|
||||
Return
|
||||
End If
|
||||
|
||||
Dim header() As String = lines(0).Split(","c)
|
||||
Dim colStep As Integer = Array.IndexOf(header, "step_file")
|
||||
If colStep < 0 Then
|
||||
lw.WriteLine("参数表缺少 step_file 列!")
|
||||
Return
|
||||
End If
|
||||
' 预解析列号
|
||||
Dim colOf As New System.Collections.Generic.Dictionary(Of String, Integer)()
|
||||
For Each kv As KeyValuePair(Of String, String) In exprMap
|
||||
colOf(kv.Key) = Array.IndexOf(header, kv.Value)
|
||||
If colOf(kv.Key) < 0 Then
|
||||
lw.WriteLine("参数表缺少列: " & kv.Value)
|
||||
Return
|
||||
End If
|
||||
Next
|
||||
|
||||
Dim okCount As Integer = 0
|
||||
Dim failCount As Integer = 0
|
||||
Dim skipCount As Integer = 0
|
||||
' 去重: 多个型号行共用同一 step_file, 每个文件只导出一次 (本批内)
|
||||
Dim exported As New System.Collections.Generic.HashSet(Of String)()
|
||||
|
||||
For i As Integer = 1 To lines.Length - 1
|
||||
If lines(i).Trim().Length = 0 Then Continue For
|
||||
Dim c() As String = lines(i).Split(","c)
|
||||
Dim model As String = c(0).Trim()
|
||||
Dim stepFile As String = c(colStep).Trim()
|
||||
If exported.Contains(stepFile) Then
|
||||
skipCount += 1
|
||||
Continue For
|
||||
End If
|
||||
' 断点续跑: 输出目录已存在同名文件 → 跳过 (想强制重导, 先删该文件再跑)
|
||||
If File.Exists(Path.Combine(outDir, stepFile)) Then
|
||||
exported.Add(stepFile)
|
||||
skipCount += 1
|
||||
Continue For
|
||||
End If
|
||||
lw.Write("[" & i & "/" & (lines.Length - 1) & "] " & stepFile & " ... ")
|
||||
|
||||
Try
|
||||
' ---- 1) 设置表达式 (尺寸) ----
|
||||
For Each kv As KeyValuePair(Of String, String) In exprMap
|
||||
workPart.Expressions.Edit(kv.Key, c(colOf(kv.Key)).Trim())
|
||||
Next
|
||||
|
||||
' ---- 1.5) 设置枚举开关 (抑制/特征组切换) ----
|
||||
For Each rule As String() In switchRules
|
||||
Dim colIdx As Integer = Array.IndexOf(header, rule(0))
|
||||
If colIdx >= 0 AndAlso c(colIdx).Trim() = rule(1) Then
|
||||
workPart.Expressions.Edit(rule(2), rule(3))
|
||||
End If
|
||||
Next
|
||||
|
||||
' ---- 2) 更新模型 ----
|
||||
Dim update As NXOpen.Update = theSession.CreateUpdate()
|
||||
Dim nErrs As Integer = 0
|
||||
update.InterpartDelay = False
|
||||
update.ModelingInterpartUpdate = True
|
||||
update.DoUpdate(nErrs)
|
||||
update.Dispose()
|
||||
If nErrs > 0 Then
|
||||
lw.WriteLine("警告: 更新有 " & nErrs & " 个错误")
|
||||
End If
|
||||
|
||||
' ---- 3) 导出 STEP ----
|
||||
ExportStep(Path.Combine(outDir, stepFile))
|
||||
|
||||
' ---- 3.5) 同步导出 STL 网格 (窗口内 3D 预览; 同名 .stl) ----
|
||||
If exportStl Then
|
||||
Dim stlFile As String = Path.ChangeExtension(stepFile, ".stl")
|
||||
If Not File.Exists(Path.Combine(stlOutDir, stlFile)) Then
|
||||
ExportStl(Path.Combine(stlOutDir, stlFile))
|
||||
End If
|
||||
End If
|
||||
|
||||
exported.Add(stepFile)
|
||||
okCount += 1
|
||||
lw.WriteLine("OK")
|
||||
|
||||
Catch ex As Exception
|
||||
failCount += 1
|
||||
lw.WriteLine("FAIL: " & ex.Message)
|
||||
End Try
|
||||
Next
|
||||
|
||||
lw.WriteLine("")
|
||||
lw.WriteLine("完成: " & okCount & " 个文件导出成功, " & failCount & " 失败, " & skipCount & " 行共用文件已跳过 (共扫描 " & (lines.Length - 1) & " 行型号), 输出目录: " & outDir)
|
||||
End Sub
|
||||
|
||||
' STEP214 导出 (NX 10+ DexBuilder; 旧版本请用录制日记替换此函数)
|
||||
Sub ExportStep(fileName As String)
|
||||
Dim dexBuilder As NXOpen.DexBuilder = workPart.DexManager.CreateBuilder()
|
||||
dexBuilder.PartType = NXOpen.DexBuilder.PartTypeOption.Part
|
||||
dexBuilder.OutputFile = fileName
|
||||
dexBuilder.Translator = NXOpen.DexBuilder.TranslatorType.Step214
|
||||
dexBuilder.ColorAndLayers = True
|
||||
dexBuilder.Commit()
|
||||
dexBuilder.Destroy()
|
||||
End Sub
|
||||
|
||||
' STL 网格导出 (窗口内 3D 预览用, 二进制格式体积小; 若 NX 版本 API 有差异,
|
||||
' 用【工具→日记→录制】录一次 文件→导出→STL 替换本函数体)
|
||||
Sub ExportStl(fileName As String)
|
||||
Dim stlBuilder As NXOpen.StlBuilder = workPart.StlManager.CreateStlBuilder()
|
||||
stlBuilder.OutputFile = fileName
|
||||
stlBuilder.BinaryFormat = True ' 二进制 STL
|
||||
stlBuilder.TriangleTolerance = 0.5 ' 三角网格公差 (mm), 越小越精细文件越大 (0.5 屏幕预览足够)
|
||||
stlBuilder.AdjacentTolerance = 0.1
|
||||
stlBuilder.AutoNormalGen = True
|
||||
stlBuilder.NormalDirection = 1 ' 1 = 朝外
|
||||
stlBuilder.Commit()
|
||||
stlBuilder.Destroy()
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
20
sample-data/tools/parse-check.ps1
Normal file
20
sample-data/tools/parse-check.ps1
Normal file
@@ -0,0 +1,20 @@
|
||||
# 用真实解析器定位 .ps1 语法错误, 并打印出错行上下文
|
||||
param([string]$File = (Join-Path $PSScriptRoot 'gen-step.ps1'))
|
||||
$tokens = $null
|
||||
$errors = $null
|
||||
[System.Management.Automation.Language.Parser]::ParseFile($File, [ref]$tokens, [ref]$errors) | Out-Null
|
||||
$lines = [System.IO.File]::ReadAllLines($File)
|
||||
Write-Output ("总行数: " + $lines.Count)
|
||||
if ($errors.Count -eq 0) {
|
||||
Write-Output "语法 OK"
|
||||
} else {
|
||||
foreach ($e in $errors) {
|
||||
$ln = $e.Extent.StartLineNumber
|
||||
Write-Output ("--- 错误 行{0} 列{1}: {2}" -f $ln, $e.Extent.StartColumnNumber, $e.Message)
|
||||
for ($i = $ln - 3; $i -le ($ln + 1); $i++) {
|
||||
if ($i -ge 1 -and $i -le $lines.Count) {
|
||||
Write-Output ("{0,4} | {1}" -f $i, $lines[$i - 1])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
sample-data/tools/probe-api.vb
Normal file
52
sample-data/tools/probe-api.vb
Normal file
@@ -0,0 +1,52 @@
|
||||
' =============================================================================
|
||||
' NX Journal: NX 2506 API 反射探针 (一次性诊断, 结果写当前工作目录 probe.txt)
|
||||
' 摸清: Dex/Stl/Update/Translator 相关类型与成员、CloseAll 签名、Save 签名
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi
|
||||
Sub Main()
|
||||
Dim out As New StringBuilder()
|
||||
Dim asm As System.Reflection.Assembly = GetType(Session).Assembly
|
||||
out.AppendLine("=== 类型 (Dex/Stl/Update/Translator/Step/Iges) ===")
|
||||
For Each t As System.Type In asm.GetTypes()
|
||||
Dim n As String = t.FullName
|
||||
If n.IndexOf("Dex", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
n.IndexOf("Stl", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
n.IndexOf("Update", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
n.IndexOf("Translator", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("T: " & n)
|
||||
End If
|
||||
Next
|
||||
out.AppendLine("=== Session 成员 (Dex/Update/Export/Create) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(Session).GetMembers()
|
||||
If mi.Name.IndexOf("Dex", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Update", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Export", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("S: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
out.AppendLine("=== Part 成员 (Dex/Stl/Export) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(Part).GetMembers()
|
||||
If mi.Name.IndexOf("Dex", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Stl", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Export", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("P: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
out.AppendLine("=== PartCollection.CloseAll 重载 ===")
|
||||
For Each mi As System.Reflection.MethodInfo In GetType(PartCollection).GetMethods()
|
||||
If mi.Name = "CloseAll" Then out.AppendLine("CA: " & mi.ToString())
|
||||
Next
|
||||
out.AppendLine("=== Part.Save 重载 ===")
|
||||
For Each mi As System.Reflection.MethodInfo In GetType(Part).GetMethods()
|
||||
If mi.Name = "Save" OrElse mi.Name = "SaveAs" Then out.AppendLine("SV: " & mi.ToString())
|
||||
Next
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe.txt"), out.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
59
sample-data/tools/probe-api2.vb
Normal file
59
sample-data/tools/probe-api2.vb
Normal file
@@ -0,0 +1,59 @@
|
||||
' NX 2506 API 反射探针 2 (稳健版): 跨全部已加载程序集查找类型成员与枚举
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi2
|
||||
|
||||
Function FindType(n As String) As System.Type
|
||||
' 探针1已验证: NXOpen 类型都在 GetType(Session).Assembly 里 (运行时程序集名不含 NXOpen)
|
||||
Dim t As System.Type = GetType(Session).Assembly.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
For Each a As System.Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
|
||||
t = a.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
Next
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
Sub Dump(out As StringBuilder, prefix As String, typeName As String)
|
||||
Dim t As System.Type = FindType(typeName)
|
||||
If t Is Nothing Then
|
||||
out.AppendLine(prefix & " [类型未找到: " & typeName & "]")
|
||||
Return
|
||||
End If
|
||||
out.AppendLine("=== " & typeName & " 成员 ===")
|
||||
For Each mi As System.Reflection.MemberInfo In t.GetMembers()
|
||||
If mi.MemberType = System.Reflection.MemberTypes.Method OrElse
|
||||
mi.MemberType = System.Reflection.MemberTypes.Property Then
|
||||
out.AppendLine(prefix & ": " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
For Each nt As System.Type In t.GetNestedTypes()
|
||||
out.AppendLine(prefix & " NESTED: " & nt.Name)
|
||||
For Each f As System.Reflection.FieldInfo In nt.GetFields()
|
||||
If f.IsStatic Then out.AppendLine(prefix & " = " & f.Name)
|
||||
Next
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Dim out As New StringBuilder()
|
||||
Dump(out, "DM", "NXOpen.DexManager")
|
||||
Dump(out, "DB", "NXOpen.DexBuilder")
|
||||
Dump(out, "ST", "NXOpen.STLCreator")
|
||||
Dump(out, "PS", "NXOpen.ParasolidExporter")
|
||||
Dump(out, "UM", "NXOpen.UpdateManager")
|
||||
Dump(out, "UP", "NXOpen.Update")
|
||||
out.AppendLine("=== 全部已加载程序集 ===")
|
||||
For Each a As System.Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
|
||||
out.AppendLine("ASM: " & a.FullName)
|
||||
Next
|
||||
out.AppendLine("=== Session 所在程序集 ===")
|
||||
out.AppendLine("SES: " & GetType(Session).Assembly.FullName)
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe2.txt"), out.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
70
sample-data/tools/probe-api3.vb
Normal file
70
sample-data/tools/probe-api3.vb
Normal file
@@ -0,0 +1,70 @@
|
||||
' NX 2506 API 反射探针 3: StepCreator / IgesCreator / Part.CreateUpdate 成员
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi3
|
||||
|
||||
Function FindType(n As String) As System.Type
|
||||
Dim t As System.Type = GetType(Session).Assembly.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
For Each a As System.Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
|
||||
t = a.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
Next
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
Sub Dump(out As StringBuilder, prefix As String, typeName As String)
|
||||
Dim t As System.Type = FindType(typeName)
|
||||
If t Is Nothing Then
|
||||
out.AppendLine(prefix & " [类型未找到: " & typeName & "]")
|
||||
Return
|
||||
End If
|
||||
out.AppendLine("=== " & typeName & " ===")
|
||||
For Each mi As System.Reflection.MemberInfo In t.GetMembers()
|
||||
If mi.MemberType = System.Reflection.MemberTypes.Method OrElse
|
||||
mi.MemberType = System.Reflection.MemberTypes.Property Then
|
||||
out.AppendLine(prefix & ": " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
For Each nt As System.Type In t.GetNestedTypes()
|
||||
out.AppendLine(prefix & " NESTED: " & nt.Name)
|
||||
For Each f As System.Reflection.FieldInfo In nt.GetFields()
|
||||
If f.IsStatic Then out.AppendLine(prefix & " = " & f.Name)
|
||||
Next
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Dim out As New StringBuilder()
|
||||
Dump(out, "SC", "NXOpen.StepCreator")
|
||||
Dump(out, "IC", "NXOpen.IgesCreator")
|
||||
Dump(out, "EX", "NXOpen.UpdateSession")
|
||||
out.AppendLine("=== Part 成员 (Update/Create) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(Part).GetMembers()
|
||||
If mi.Name.IndexOf("Update", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Create", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("PT: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
out.AppendLine("=== PartCollection 成员 (Update/Create/New) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(PartCollection).GetMembers()
|
||||
If mi.Name.IndexOf("Update", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("New", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("PC: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
out.AppendLine("=== UFModl 成员 (Update/Cyl) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(UFModl).GetMembers()
|
||||
If mi.Name.IndexOf("Update", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Cyl", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
out.AppendLine("UF: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe3.txt"), out.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
35
sample-data/tools/probe-api4.vb
Normal file
35
sample-data/tools/probe-api4.vb
Normal file
@@ -0,0 +1,35 @@
|
||||
' 探针 4: ExpressionCollection 创建/编辑方法签名
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi4
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim outPath As String = Path.Combine(Directory.GetCurrentDirectory(), "KC_master.prt")
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Session.GetSession().Parts.OpenBaseDisplay(outPath, partLoadStatus1)
|
||||
Dim w As Part = Session.GetSession().Parts.Work
|
||||
sb.AppendLine("Work=" & (If(w Is Nothing, "NULL", w.FullPath)))
|
||||
If w IsNot Nothing Then
|
||||
Dim ec As System.Type = w.Expressions.GetType()
|
||||
sb.AppendLine("ExpressionCollection type: " & ec.FullName)
|
||||
For Each mi As System.Reflection.MethodInfo In ec.GetMethods()
|
||||
If mi.Name.IndexOf("Create", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
mi.Name.IndexOf("Edit", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
sb.AppendLine("EC: " & mi.ToString())
|
||||
End If
|
||||
Next
|
||||
Dim uc As System.Type = w.UnitCollection.GetType()
|
||||
For Each mi As System.Reflection.MethodInfo In uc.GetMethods()
|
||||
If mi.Name.IndexOf("Find", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
sb.AppendLine("UC: " & mi.ToString())
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe4.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
51
sample-data/tools/probe-api5.vb
Normal file
51
sample-data/tools/probe-api5.vb
Normal file
@@ -0,0 +1,51 @@
|
||||
' 探针 5: ReferenceSet 相关类型/成员
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi5
|
||||
Function FindType(n As String) As System.Type
|
||||
Dim t As System.Type = GetType(Session).Assembly.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
For Each a As System.Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
|
||||
t = a.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
Next
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim asm As System.Reflection.Assembly = GetType(Session).Assembly
|
||||
sb.AppendLine("=== 含 ReferenceSet 的类型 ===")
|
||||
For Each t As System.Type In asm.GetTypes()
|
||||
If t.FullName.IndexOf("ReferenceSet", StringComparison.OrdinalIgnoreCase) >= 0 Then sb.AppendLine("T: " & t.FullName)
|
||||
Next
|
||||
sb.AppendLine("=== Part 成员 (Reference) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(Part).GetMembers()
|
||||
If mi.Name.IndexOf("Reference", StringComparison.OrdinalIgnoreCase) >= 0 Then sb.AppendLine("P: " & mi.MemberType & " " & mi.Name)
|
||||
Next
|
||||
sb.AppendLine("=== BasePart 成员 (Reference) ===")
|
||||
For Each mi As System.Reflection.MemberInfo In GetType(BasePart).GetMembers()
|
||||
If mi.Name.IndexOf("Reference", StringComparison.OrdinalIgnoreCase) >= 0 Then sb.AppendLine("B: " & mi.MemberType & " " & mi.Name)
|
||||
Next
|
||||
Dim rsT As System.Type = FindType("NXOpen.ReferenceSet")
|
||||
If rsT IsNot Nothing Then
|
||||
sb.AppendLine("=== NXOpen.ReferenceSet 方法 ===")
|
||||
For Each mi As System.Reflection.MethodInfo In rsT.GetMethods()
|
||||
If mi.DeclaringType Is rsT Then sb.AppendLine("RS: " & mi.Name)
|
||||
Next
|
||||
End If
|
||||
Dim rscT As System.Type = FindType("NXOpen.Assemblies.ReferenceSetCollection")
|
||||
If rscT IsNot Nothing Then
|
||||
sb.AppendLine("=== Assemblies.ReferenceSetCollection 方法 ===")
|
||||
For Each mi As System.Reflection.MethodInfo In rscT.GetMethods()
|
||||
If mi.DeclaringType Is rscT Then sb.AppendLine("RSC: " & mi.Name)
|
||||
Next
|
||||
End If
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe5.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
33
sample-data/tools/probe-api6.vb
Normal file
33
sample-data/tools/probe-api6.vb
Normal file
@@ -0,0 +1,33 @@
|
||||
' 探针 6: Step214Importer 成员
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeApi6
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
Dim t As System.Type = GetType(Session).Assembly.GetType("NXOpen.Step214Importer", False)
|
||||
If t Is Nothing Then
|
||||
sb.AppendLine("Step214Importer 未找到; 列出 Step 相关类型:")
|
||||
For Each tt As System.Type In GetType(Session).Assembly.GetTypes()
|
||||
If tt.FullName.IndexOf("Step", StringComparison.OrdinalIgnoreCase) >= 0 Then sb.AppendLine("T: " & tt.FullName)
|
||||
Next
|
||||
Else
|
||||
For Each mi As System.Reflection.MemberInfo In t.GetMembers()
|
||||
If mi.MemberType = System.Reflection.MemberTypes.Method OrElse mi.MemberType = System.Reflection.MemberTypes.Property Then
|
||||
sb.AppendLine("SI: " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
For Each nt As System.Type In t.GetNestedTypes()
|
||||
sb.AppendLine("SI NESTED: " & nt.Name)
|
||||
For Each f As System.Reflection.FieldInfo In nt.GetFields()
|
||||
If f.IsStatic Then sb.AppendLine(" = " & f.Name)
|
||||
Next
|
||||
Next
|
||||
End If
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe6.txt"), sb.ToString(), Encoding.UTF8)
|
||||
End Sub
|
||||
End Module
|
||||
72
sample-data/tools/probe-display-api.vb
Normal file
72
sample-data/tools/probe-display-api.vb
Normal file
@@ -0,0 +1,72 @@
|
||||
' =============================================================================
|
||||
' NX Journal: probe how to SET the display part + locate DexManager (NX 2506)
|
||||
'
|
||||
' Last test failed: Parts.Display is ReadOnly. Need to find the correct API to
|
||||
' set the display part (so the STEP translator's DisplayPart mode can see the
|
||||
' solids in batch mode). Also re-confirm where DexManager lives (Session vs
|
||||
' Part vs BasePart) to know which export API to use.
|
||||
'
|
||||
' Dumps (reflection only, no risky calls):
|
||||
' Session members containing "Display" / "Dex"
|
||||
' PartCollection members containing "Display" / "Set" / "Work"
|
||||
' Part members containing "Display" / "Dex"
|
||||
' BasePart members containing "Display" / "Dex"
|
||||
'
|
||||
' Run: WorkingDirectory = catalog-masters\KC; run_journal.exe <this file>
|
||||
' Result: <cwd>\probe-display-api.txt
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeDisplayApi
|
||||
|
||||
Sub DumpMembers(sb As StringBuilder, prefix As String, t As System.Type, filter As String)
|
||||
For Each mi As System.Reflection.MemberInfo In t.GetMembers()
|
||||
If filter.Length = 0 OrElse mi.Name.IndexOf(filter, StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
If mi.MemberType = System.Reflection.MemberTypes.Method OrElse
|
||||
mi.MemberType = System.Reflection.MemberTypes.Property Then
|
||||
sb.AppendLine(prefix & ": " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
sb.AppendLine("=== Session members (Display) ===")
|
||||
DumpMembers(sb, "S", GetType(Session), "Display")
|
||||
sb.AppendLine("=== Session members (Dex) ===")
|
||||
DumpMembers(sb, "S", GetType(Session), "Dex")
|
||||
|
||||
sb.AppendLine("=== PartCollection members (Display) ===")
|
||||
DumpMembers(sb, "PC", GetType(PartCollection), "Display")
|
||||
sb.AppendLine("=== PartCollection members (Set) ===")
|
||||
DumpMembers(sb, "PC", GetType(PartCollection), "Set")
|
||||
sb.AppendLine("=== PartCollection members (Work) ===")
|
||||
DumpMembers(sb, "PC", GetType(PartCollection), "Work")
|
||||
|
||||
sb.AppendLine("=== Part members (Display) ===")
|
||||
DumpMembers(sb, "P", GetType(Part), "Display")
|
||||
sb.AppendLine("=== Part members (Dex) ===")
|
||||
DumpMembers(sb, "P", GetType(Part), "Dex")
|
||||
|
||||
sb.AppendLine("=== BasePart members (Display) ===")
|
||||
DumpMembers(sb, "B", GetType(BasePart), "Display")
|
||||
sb.AppendLine("=== BasePart members (Dex) ===")
|
||||
DumpMembers(sb, "B", GetType(BasePart), "Dex")
|
||||
|
||||
' Also: does BasePart have a SetDisplay-style method?
|
||||
sb.AppendLine("=== BasePart members (Set) ===")
|
||||
DumpMembers(sb, "B", GetType(BasePart), "Set")
|
||||
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe-display-api.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
84
sample-data/tools/probe-selection.vb
Normal file
84
sample-data/tools/probe-selection.vb
Normal file
@@ -0,0 +1,84 @@
|
||||
' =============================================================================
|
||||
' NX Journal: probe SelectionBlock / ScCollector API (NX 2506)
|
||||
'
|
||||
' Purpose: StepCreator has an ExportSelectionBlock property. If batch mode has
|
||||
' no "DisplayPart", we may be able to export by EXPLICITLY selecting the
|
||||
' bodies into the ExportSelectionBlock. This probe dumps the exact API so the
|
||||
' next script can use it correctly (compile-safe).
|
||||
'
|
||||
' Dumps:
|
||||
' 1. Type of StepCreator.ExportSelectionBlock
|
||||
' 2. Members of that type
|
||||
' 3. Members of NXOpen.SelectionBlock, NXOpen.ScCollector (if found)
|
||||
' 4. All types whose name contains "Selection" or "Collector"
|
||||
'
|
||||
' Run: WorkingDirectory = catalog-masters\KC; run_journal.exe <this file>
|
||||
' Result: <cwd>\probe-selection.txt
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module ProbeSelection
|
||||
|
||||
Function FindType(n As String) As System.Type
|
||||
Dim t As System.Type = GetType(Session).Assembly.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
For Each a As System.Reflection.Assembly In AppDomain.CurrentDomain.GetAssemblies()
|
||||
t = a.GetType(n, False)
|
||||
If t IsNot Nothing Then Return t
|
||||
Next
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
Sub DumpMembers(sb As StringBuilder, prefix As String, t As System.Type)
|
||||
If t Is Nothing Then
|
||||
sb.AppendLine(prefix & " [NULL]")
|
||||
Return
|
||||
End If
|
||||
sb.AppendLine("=== " & t.FullName & " ===")
|
||||
For Each mi As System.Reflection.MemberInfo In t.GetMembers()
|
||||
If mi.MemberType = System.Reflection.MemberTypes.Method OrElse
|
||||
mi.MemberType = System.Reflection.MemberTypes.Property Then
|
||||
sb.AppendLine(prefix & ": " & mi.MemberType & " " & mi.Name)
|
||||
End If
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
' 1. Type of StepCreator.ExportSelectionBlock
|
||||
Dim scT As System.Type = GetType(NXOpen.StepCreator)
|
||||
Dim esb As System.Reflection.PropertyInfo = scT.GetProperty("ExportSelectionBlock")
|
||||
If esb IsNot Nothing Then
|
||||
sb.AppendLine("StepCreator.ExportSelectionBlock type = " & esb.PropertyType.FullName)
|
||||
DumpMembers(sb, "ESB", esb.PropertyType)
|
||||
Else
|
||||
sb.AppendLine("StepCreator.ExportSelectionBlock NOT FOUND")
|
||||
End If
|
||||
|
||||
' 2. SelectionBlock / ScCollector
|
||||
DumpMembers(sb, "SELB", FindType("NXOpen.SelectionBlock"))
|
||||
DumpMembers(sb, "SCCO", FindType("NXOpen.ScCollector"))
|
||||
DumpMembers(sb, "SEL", FindType("NXOpen.Selection"))
|
||||
|
||||
' 3. All types containing Selection / Collector
|
||||
sb.AppendLine("=== types containing Selection/Collector ===")
|
||||
For Each t As System.Type In GetType(Session).Assembly.GetTypes()
|
||||
Dim n As String = t.FullName
|
||||
If n.IndexOf("Selection", StringComparison.OrdinalIgnoreCase) >= 0 OrElse
|
||||
n.IndexOf("Collector", StringComparison.OrdinalIgnoreCase) >= 0 Then
|
||||
sb.AppendLine("T: " & n)
|
||||
End If
|
||||
Next
|
||||
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "probe-selection.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
276
sample-data/tools/rebuild-verify-master.vb
Normal file
276
sample-data/tools/rebuild-verify-master.vb
Normal file
@@ -0,0 +1,276 @@
|
||||
' =============================================================================
|
||||
' NX Journal: rebuild test master + diagnose why translator sees 0 solids (NX 2506)
|
||||
'
|
||||
' Hypothesis to test: UF CreateCyl1 built bodies that Part.Bodies counts (=2)
|
||||
' but the STEP translator reports "0 solids". Suspect: unitless expressions
|
||||
' ("bore=32" with no mm unit) produce degenerate/empty-shell bodies.
|
||||
'
|
||||
' This script:
|
||||
' Phase 1 (single session, no reopen):
|
||||
' OpenBaseDisplay -> ensure expressions WITH units (bore/stroke = mm) ->
|
||||
' CreateCyl1 -> Update -> report body count AND face count per body ->
|
||||
' add to MODEL refset -> export STEP (DisplayPart) -> Save ->
|
||||
' export STEP again via ExistingPart (reads the saved file from disk).
|
||||
' The face count tells us if bodies are real solids (cylinder ~3 faces)
|
||||
' or empty shells (0 faces). The ExistingPart export tells us if the solids
|
||||
' actually persisted to the .prt on disk.
|
||||
'
|
||||
' Run (run_journal takes one arg; use WorkingDirectory):
|
||||
' WorkingDirectory = catalog-masters\KC (the .bat does "cd /d %~dp0")
|
||||
' run_journal.exe <this file full path>
|
||||
' Result log: <cwd>\rebuild-verify-master.txt Products: <cwd>\dbg_out\
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module RebuildVerifyMaster
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
Sub Log(s As String)
|
||||
sb.AppendLine(s)
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Try
|
||||
Run()
|
||||
Catch ex As Exception
|
||||
Log("[FATAL] " & ex.GetType().Name & " / " & ex.Message)
|
||||
End Try
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "rebuild-verify-master.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
Sub Run()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Log("CWD=" & cur)
|
||||
Log("masterExists=" & File.Exists(masterPath))
|
||||
If Not File.Exists(masterPath) Then
|
||||
Log("[FAIL] master file missing: " & masterPath)
|
||||
Return
|
||||
End If
|
||||
Try
|
||||
File.SetAttributes(masterPath, File.GetAttributes(masterPath) And Not FileAttributes.ReadOnly)
|
||||
Catch ex As Exception
|
||||
Log("clear readonly: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Dim outDir As String = Path.Combine(cur, "dbg_out")
|
||||
Directory.CreateDirectory(outDir)
|
||||
|
||||
Dim pls As NXOpen.PartLoadStatus = Nothing
|
||||
Dim bp As NXOpen.BasePart = theSession.Parts.OpenBaseDisplay(masterPath, pls)
|
||||
If pls IsNot Nothing Then pls.Dispose()
|
||||
Dim w As Part = theSession.Parts.Work
|
||||
If w Is Nothing Then
|
||||
Log("[FAIL] OpenBaseDisplay -> Work=NULL")
|
||||
Return
|
||||
End If
|
||||
Log("Work=" & w.FullPath)
|
||||
Log("bodiesBefore=" & CountBodies(w))
|
||||
|
||||
' mm unit object (for length-typed expressions). Fall back to no unit.
|
||||
Dim mm As NXOpen.Unit = w.UnitCollection.FindObject("MilliMeter")
|
||||
If mm Is Nothing Then mm = w.UnitCollection.FindObject("Millimetre")
|
||||
Log("unit mm=" & (If(mm Is Nothing, "NULL", mm.ToString())))
|
||||
|
||||
EnsureExprWithUnit(w, "bore", "32", mm)
|
||||
EnsureExprWithUnit(w, "stroke", "10", mm)
|
||||
EnsureExprNoUnit(w, "magOn", "0")
|
||||
EnsureExprNoUnit(w, "mountIdx", "0")
|
||||
Log("expr bore=" & ExprVal(w, "bore") & " stroke=" & ExprVal(w, "stroke") & " magOn=" & ExprVal(w, "magOn") & " mountIdx=" & ExprVal(w, "mountIdx"))
|
||||
|
||||
If CountBodies(w) = 0 Then
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
Dim origin(2) As Double
|
||||
Dim dir(2) As Double
|
||||
dir(2) = 1.0
|
||||
Dim t1 As Tag
|
||||
Dim t2 As Tag
|
||||
ufs.Modl.CreateCyl1(NXOpen.UF.FeatureSigns.Nullsign, origin, "stroke", "bore+8", dir, t1)
|
||||
ufs.Modl.CreateCyl1(NXOpen.UF.FeatureSigns.Nullsign, origin, "stroke+30", "bore*0.33", dir, t2)
|
||||
Log("created 2 cylinders (tags " & t1.ToString() & ", " & t2.ToString() & ")")
|
||||
Else
|
||||
Log("solids already exist, skip creation")
|
||||
End If
|
||||
|
||||
Dim u2 As UFSession = UFSession.GetUFSession()
|
||||
u2.Modl.Update()
|
||||
Log("afterUpdate bodies=" & CountBodies(w))
|
||||
DumpBodyFaces(w)
|
||||
AddToModelRefset(w)
|
||||
Log("afterRefset bodies=" & CountBodies(w))
|
||||
|
||||
' ---- TEST A: DisplayPart export of in-memory bodies ----
|
||||
ExportStep(Path.Combine(outDir, "A_display.stp"))
|
||||
Log("A_display.stp size=" & FileSize(Path.Combine(outDir, "A_display.stp")))
|
||||
|
||||
' ---- Save so the bodies (hopefully) persist to the .prt ----
|
||||
Try
|
||||
Dim pss As NXOpen.PartSaveStatus = w.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
Log("save OK")
|
||||
Catch ex As Exception
|
||||
Log("save FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- TEST B: ExistingPart export reads the SAVED file from disk ----
|
||||
ExportStepExisting(masterPath, Path.Combine(outDir, "B_existing.stp"))
|
||||
Log("B_existing.stp size=" & FileSize(Path.Combine(outDir, "B_existing.stp")))
|
||||
|
||||
Log("DONE")
|
||||
End Sub
|
||||
|
||||
' ---- helpers ----
|
||||
Function CountBodies(w As Part) As Integer
|
||||
Dim n As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
n += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
Return n
|
||||
End Function
|
||||
|
||||
' Count faces per body. A real cylinder ~3 faces; 0 faces = empty shell.
|
||||
Sub DumpBodyFaces(w As Part)
|
||||
Dim i As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
i += 1
|
||||
Dim fc As Integer = 0
|
||||
Try
|
||||
For Each f As NXOpen.Face In b.GetFaces()
|
||||
fc += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
Log("body" & i & " faces=" & fc)
|
||||
Next
|
||||
Catch ex As Exception
|
||||
Log("DumpBodyFaces FAIL: " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Sub EnsureExprWithUnit(w As Part, name As String, val As String, mm As NXOpen.Unit)
|
||||
Try
|
||||
If w.Expressions.FindObject(name) IsNot Nothing Then Return
|
||||
Catch
|
||||
End Try
|
||||
Try
|
||||
If mm IsNot Nothing Then
|
||||
w.Expressions.CreateExpressionWithUnit("Number", name & "=" & val, mm)
|
||||
Else
|
||||
w.Expressions.CreateExpression("Number", name & "=" & val)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log("EnsureExprWithUnit " & name & ": " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Sub EnsureExprNoUnit(w As Part, name As String, val As String)
|
||||
Try
|
||||
If w.Expressions.FindObject(name) IsNot Nothing Then Return
|
||||
Catch
|
||||
End Try
|
||||
Try
|
||||
w.Expressions.CreateExpression("Number", name & "=" & val)
|
||||
Catch ex As Exception
|
||||
Log("EnsureExprNoUnit " & name & ": " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Function ExprVal(w As Part, name As String) As String
|
||||
Try
|
||||
Dim e As NXOpen.Expression = w.Expressions.FindObject(name)
|
||||
If e IsNot Nothing Then Return e.Value
|
||||
Catch
|
||||
End Try
|
||||
Return "(missing)"
|
||||
End Function
|
||||
|
||||
Sub AddToModelRefset(w As Part)
|
||||
Try
|
||||
Dim bodyList As New System.Collections.Generic.List(Of NXOpen.NXObject)()
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
bodyList.Add(b)
|
||||
Next
|
||||
If bodyList.Count = 0 Then
|
||||
Log("AddToModelRefset: no bodies")
|
||||
Return
|
||||
End If
|
||||
Dim added As Boolean = False
|
||||
For Each rs As NXOpen.ReferenceSet In w.GetAllReferenceSets()
|
||||
If rs.Name.ToUpperInvariant().Contains("MODEL") Then
|
||||
rs.AddObjectsToReferenceSet(bodyList.ToArray())
|
||||
Log("added " & bodyList.Count & " bodies to refset '" & rs.Name & "'")
|
||||
added = True
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
If Not added Then
|
||||
Log("AddToModelRefset: no MODEL refset (refsets: " & RefsetNames(w) & ")")
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Log("AddToModelRefset FAIL: " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Function RefsetNames(w As Part) As String
|
||||
Dim s As String = ""
|
||||
Try
|
||||
For Each rs As NXOpen.ReferenceSet In w.GetAllReferenceSets()
|
||||
If s.Length > 0 Then s &= ", "
|
||||
s &= rs.Name
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
Return s
|
||||
End Function
|
||||
|
||||
Function FileSize(p As String) As String
|
||||
If Not File.Exists(p) Then Return "MISSING"
|
||||
Return (New FileInfo(p)).Length & " bytes"
|
||||
End Function
|
||||
|
||||
' ---- export functions ----
|
||||
Sub ExportStep(fileName As String)
|
||||
If File.Exists(fileName) Then File.Delete(fileName)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.DisplayPart
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = fileName
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Catch ex As Exception
|
||||
Log("ExportStep FAIL: " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
Sub ExportStepExisting(inputFile As String, fileName As String)
|
||||
If File.Exists(fileName) Then File.Delete(fileName)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.ExistingPart
|
||||
sc.InputFile = inputFile
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = fileName
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Catch ex As Exception
|
||||
Log("ExportStepExisting FAIL: " & ex.Message)
|
||||
End Try
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
40
sample-data/tools/reorder-colors.ps1
Normal file
40
sample-data/tools/reorder-colors.ps1
Normal file
@@ -0,0 +1,40 @@
|
||||
# 实验: 把颜色实体块移到 DATA 段末尾 (复刻 inject-colors 的成功布局), 并全量重编号
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\reorder-colors.ps1 -In a.step -Out b.step
|
||||
param([string]$In, [string]$Out)
|
||||
$lines = Get-Content $In
|
||||
|
||||
# 切分: 头部(至 DATA;) / 实体区 / 尾部(ENDSEC; 起)
|
||||
$dataIdx = 0
|
||||
for ($i = 0; $i -lt $lines.Count; $i++) { if ($lines[$i] -eq 'DATA;') { $dataIdx = $i; break } }
|
||||
$endIdx = $lines.Count - 1
|
||||
for ($i = $dataIdx; $i -lt $lines.Count; $i++) { if ($lines[$i] -eq 'ENDSEC;') { $endIdx = $i; break } }
|
||||
$head = $lines[0..$dataIdx]
|
||||
$ents = $lines[($dataIdx + 1)..($endIdx - 1)]
|
||||
$foot = $lines[$endIdx..($lines.Count - 1)]
|
||||
|
||||
# 实体区再分: 几何区 / 颜色区 / 尾部区
|
||||
$colorStart = -1; $tailStart = -1
|
||||
for ($i = 0; $i -lt $ents.Count; $i++) {
|
||||
if ($colorStart -lt 0 -and $ents[$i] -match '=COLOUR_RGB\(') { $colorStart = $i }
|
||||
if ($tailStart -lt 0 -and $ents[$i] -match '=SHAPE_REPRESENTATION\(') { $tailStart = $i }
|
||||
}
|
||||
if ($colorStart -lt 0 -or $tailStart -lt 0) { Write-Output "结构不识别"; exit 1 }
|
||||
$geo = $ents[0..($colorStart - 1)]
|
||||
$colors = $ents[$colorStart..($tailStart - 1)]
|
||||
$tail = $ents[$tailStart..($ents.Count - 1)]
|
||||
$newEnts = @($geo) + @($tail) + @($colors) # 新顺序: 几何 → 尾部 → 颜色
|
||||
|
||||
# 全量重编号
|
||||
$map = @{}
|
||||
$next = 1
|
||||
foreach ($l in $newEnts) {
|
||||
$m = [regex]::Match($l, '^#(\d+)=')
|
||||
if ($m.Success) { $map[$m.Groups[1].Value] = $next; $next++ }
|
||||
}
|
||||
$renum = $newEnts | ForEach-Object {
|
||||
[regex]::Replace($_, '#\d+', { param($mm) $id = $mm.Value.Substring(1); if ($map.ContainsKey($id)) { return '#' + $map[$id] } else { return $mm.Value } })
|
||||
}
|
||||
|
||||
$content = @($head) + @($renum) + @($foot)
|
||||
[System.IO.File]::WriteAllLines($Out, [string[]]$content, (New-Object System.Text.UTF8Encoding($false)))
|
||||
Write-Output ("重排完成: 几何 " + $geo.Count + " 行 + 尾部 " + $tail.Count + " 行 + 颜色 " + $colors.Count + " 行 → " + $Out)
|
||||
34
sample-data/tools/run-rebuild-verify.ps1
Normal file
34
sample-data/tools/run-rebuild-verify.ps1
Normal file
@@ -0,0 +1,34 @@
|
||||
# 运行 rebuild-verify-master.vb (NX 2506 批处理): 重建含实体母模 + 验证导出
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File run-rebuild-verify.ps1
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
$nx = 'C:\Program Files\Siemens\NX2506\NXBIN\run_journal.exe'
|
||||
$journal = 'd:\开发\OnebotCatalog\sample-data\tools\rebuild-verify-master.vb'
|
||||
$workDir = 'd:\开发\OnebotCatalog\catalog-masters\KC'
|
||||
|
||||
if (-not (Test-Path $nx)) { Write-Output "[FAIL] NX run_journal 不存在: $nx"; exit 1 }
|
||||
if (-not (Test-Path $journal)) { Write-Output "[FAIL] journal 不存在: $journal"; exit 1 }
|
||||
if (-not (Test-Path $workDir)) { Write-Output "[FAIL] 工作目录不存在: $workDir"; exit 1 }
|
||||
|
||||
# 清理旧结果
|
||||
Remove-Item (Join-Path $workDir 'rebuild-verify-master.txt') -ErrorAction SilentlyContinue
|
||||
Remove-Item (Join-Path $workDir 'dbg_out\*') -ErrorAction SilentlyContinue
|
||||
|
||||
Write-Output "[RUN] run_journal 启动 (预计 30-90 秒, NX 会话冷启动)..."
|
||||
$sw = [System.Diagnostics.Stopwatch]::StartNew()
|
||||
$p = Start-Process -FilePath $nx -ArgumentList $journal -WorkingDirectory $workDir -PassThru -Wait
|
||||
$sw.Stop()
|
||||
|
||||
Write-Output "[RUN] run_journal 退出, 用时 $([int]$sw.Elapsed.TotalSeconds) 秒"
|
||||
|
||||
$log = Join-Path $workDir 'rebuild-verify-master.txt'
|
||||
if (Test-Path $log) {
|
||||
Write-Output "========== rebuild-verify-master.txt =========="
|
||||
Get-Content $log -Encoding UTF8
|
||||
Write-Output "==============================================="
|
||||
} else {
|
||||
Write-Output "[FAIL] 未生成结果日志: $log (NX 可能启动失败或被 license 占用)"
|
||||
}
|
||||
|
||||
Write-Output "========== dbg_out 产物 =========="
|
||||
Get-ChildItem (Join-Path $workDir 'dbg_out') -ErrorAction SilentlyContinue | Select-Object Name, Length | Format-Table -AutoSize
|
||||
139
sample-data/tools/test-display-part.vb
Normal file
139
sample-data/tools/test-display-part.vb
Normal file
@@ -0,0 +1,139 @@
|
||||
' =============================================================================
|
||||
' NX Journal: test whether batch-mode DisplayPart is empty (NX 2506)
|
||||
'
|
||||
' Hypothesis: ExportFrom=DisplayPart exports Session.Parts.Display, but in
|
||||
' batch (run_journal) OpenBaseDisplay sets Work part WITHOUT setting the
|
||||
' Display part -> Display is null -> translator sees 0 solids.
|
||||
'
|
||||
' This script:
|
||||
' 1. OpenBaseDisplay -> print Work AND Display state
|
||||
' 2. Try setting Display = workPart
|
||||
' 3. Update + add to MODEL refset
|
||||
' 4. DisplayPart STEP export (should now produce a real solid)
|
||||
'
|
||||
' Run: WorkingDirectory = catalog-masters\KC; run_journal.exe <this file>
|
||||
' Result: <cwd>\test-display-part.txt Products: <cwd>\dbg_out\
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module TestDisplayPart
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
Sub Log(s As String)
|
||||
sb.AppendLine(s)
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Try
|
||||
Run()
|
||||
Catch ex As Exception
|
||||
Log("[FATAL] " & ex.GetType().Name & " / " & ex.Message)
|
||||
End Try
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "test-display-part.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
Function CountBodies(w As Part) As Integer
|
||||
Dim n As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
n += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
Return n
|
||||
End Function
|
||||
|
||||
Function FileSize(p As String) As String
|
||||
If Not File.Exists(p) Then Return "MISSING"
|
||||
Return (New FileInfo(p)).Length & " bytes"
|
||||
End Function
|
||||
|
||||
Sub Run()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Log("masterExists=" & File.Exists(masterPath))
|
||||
If Not File.Exists(masterPath) Then Return
|
||||
|
||||
Dim pls As NXOpen.PartLoadStatus = Nothing
|
||||
theSession.Parts.OpenBaseDisplay(masterPath, pls)
|
||||
If pls IsNot Nothing Then pls.Dispose()
|
||||
Dim w As Part = theSession.Parts.Work
|
||||
If w Is Nothing Then
|
||||
Log("[FAIL] Work=NULL")
|
||||
Return
|
||||
End If
|
||||
Log("Work=" & w.FullPath)
|
||||
|
||||
' ---- KEY: state of the DISPLAY part ----
|
||||
Try
|
||||
Dim disp As BasePart = theSession.Parts.Display
|
||||
Log("Display=" & (If(disp Is Nothing, "NULL", disp.FullPath)))
|
||||
Catch ex As Exception
|
||||
Log("Parts.Display get throws: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- try to SET Display = work part ----
|
||||
Try
|
||||
theSession.Parts.Display = w
|
||||
Log("set Display=work OK")
|
||||
Catch ex As Exception
|
||||
Log("set Display=work FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Try
|
||||
Dim disp2 As BasePart = theSession.Parts.Display
|
||||
Log("Display(after set)=" & (If(disp2 Is Nothing, "NULL", disp2.FullPath)))
|
||||
Catch ex As Exception
|
||||
Log("Parts.Display get2 throws: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- Update + refset (align with the flow that produced a file) ----
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
ufs.Modl.Update()
|
||||
Log("afterUpdate bodies=" & CountBodies(w))
|
||||
Try
|
||||
Dim bodyList As New System.Collections.Generic.List(Of NXOpen.NXObject)()
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
bodyList.Add(b)
|
||||
Next
|
||||
For Each rs As NXOpen.ReferenceSet In w.GetAllReferenceSets()
|
||||
If rs.Name.ToUpperInvariant().Contains("MODEL") Then
|
||||
rs.AddObjectsToReferenceSet(bodyList.ToArray())
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Log("refset bodies added")
|
||||
Catch ex As Exception
|
||||
Log("refset add FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- DisplayPart STEP export ----
|
||||
Dim f As String = Path.Combine(cur, "dbg_out", "D_display.stp")
|
||||
Directory.CreateDirectory(Path.Combine(cur, "dbg_out"))
|
||||
If File.Exists(f) Then File.Delete(f)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.DisplayPart
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = f
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Log("D_display.stp size=" & FileSize(f))
|
||||
Catch ex As Exception
|
||||
Log("D_display FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Log("DONE")
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
134
sample-data/tools/test-display-state.vb
Normal file
134
sample-data/tools/test-display-state.vb
Normal file
@@ -0,0 +1,134 @@
|
||||
' =============================================================================
|
||||
' NX Journal: inspect Display vs Work state in batch mode (NX 2506)
|
||||
'
|
||||
' Last attempt failed to compile: Parts.Display is ReadOnly. This version only
|
||||
' READS and prints state (no assignment), so it compiles and tells us whether
|
||||
' the batch-mode Display part is NULL / same as Work / different.
|
||||
'
|
||||
' Prints:
|
||||
' Work.FullPath, Work bodies
|
||||
' Display (NULL or FullPath), Display bodies
|
||||
' Display Is Work?
|
||||
' then a DisplayPart STEP export to see solids count.
|
||||
'
|
||||
' Run: WorkingDirectory = catalog-masters\KC; run_journal.exe <this file>
|
||||
' Result: <cwd>\test-display-state.txt
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module TestDisplayState
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
Sub Log(s As String)
|
||||
sb.AppendLine(s)
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Try
|
||||
Run()
|
||||
Catch ex As Exception
|
||||
Log("[FATAL] " & ex.GetType().Name & " / " & ex.Message)
|
||||
End Try
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "test-display-state.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
Function CountBodies(p As BasePart) As Integer
|
||||
Dim n As Integer = 0
|
||||
Try
|
||||
Dim pp As Part = CType(p, Part)
|
||||
If pp IsNot Nothing Then
|
||||
For Each b As NXOpen.Body In pp.Bodies
|
||||
n += 1
|
||||
Next
|
||||
End If
|
||||
Catch
|
||||
End Try
|
||||
Return n
|
||||
End Function
|
||||
|
||||
Function FileSize(p As String) As String
|
||||
If Not File.Exists(p) Then Return "MISSING"
|
||||
Return (New FileInfo(p)).Length & " bytes"
|
||||
End Function
|
||||
|
||||
Sub Run()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Log("masterExists=" & File.Exists(masterPath))
|
||||
If Not File.Exists(masterPath) Then Return
|
||||
|
||||
Dim pls As NXOpen.PartLoadStatus = Nothing
|
||||
theSession.Parts.OpenBaseDisplay(masterPath, pls)
|
||||
If pls IsNot Nothing Then pls.Dispose()
|
||||
Dim w As Part = theSession.Parts.Work
|
||||
If w Is Nothing Then
|
||||
Log("[FAIL] Work=NULL")
|
||||
Return
|
||||
End If
|
||||
Log("Work=" & w.FullPath)
|
||||
Log("Work.bodies=" & CountBodies(w))
|
||||
|
||||
' ---- READ Display state (no assignment) ----
|
||||
Dim disp As BasePart = Nothing
|
||||
Try
|
||||
disp = theSession.Parts.Display
|
||||
Catch ex As Exception
|
||||
Log("Parts.Display get throws: " & ex.Message)
|
||||
End Try
|
||||
If disp Is Nothing Then
|
||||
Log("Display=NULL")
|
||||
Else
|
||||
Log("Display=" & disp.FullPath)
|
||||
Log("Display.bodies=" & CountBodies(disp))
|
||||
Log("Display Is Work? " & (disp Is w))
|
||||
End If
|
||||
|
||||
' ---- Update work + add bodies to MODEL refset (so the file is exportable) ----
|
||||
Try
|
||||
Dim ufs As UFSession = UFSession.GetUFSession()
|
||||
ufs.Modl.Update()
|
||||
Dim bodyList As New System.Collections.Generic.List(Of NXOpen.NXObject)()
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
bodyList.Add(b)
|
||||
Next
|
||||
For Each rs As NXOpen.ReferenceSet In w.GetAllReferenceSets()
|
||||
If rs.Name.ToUpperInvariant().Contains("MODEL") Then
|
||||
rs.AddObjectsToReferenceSet(bodyList.ToArray())
|
||||
Exit For
|
||||
End If
|
||||
Next
|
||||
Log("refset added")
|
||||
Catch ex As Exception
|
||||
Log("refset FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- DisplayPart export ----
|
||||
Dim f As String = Path.Combine(cur, "dbg_out", "S_display.stp")
|
||||
Directory.CreateDirectory(Path.Combine(cur, "dbg_out"))
|
||||
If File.Exists(f) Then File.Delete(f)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.DisplayPart
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = f
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Log("S_display.stp size=" & FileSize(f))
|
||||
Catch ex As Exception
|
||||
Log("S_display FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Log("DONE")
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
102
sample-data/tools/test-existingpart.vb
Normal file
102
sample-data/tools/test-existingpart.vb
Normal file
@@ -0,0 +1,102 @@
|
||||
' =============================================================================
|
||||
' NX Journal: test ExistingPart (read-from-disk) STEP export, no open part.
|
||||
'
|
||||
' The last test proved the master is healthy (2 bodies, faces=3) but:
|
||||
' - DisplayPart export -> "0 solids" (batch has no display part)
|
||||
' - ExistingPart export -> MISSING (because KC_master.prt was locked by the
|
||||
' open Work part when we tried to read it)
|
||||
'
|
||||
' Hypothesis: In batch (run_journal), the ONLY reliable way to export is
|
||||
' ExistingPart + InputFile reading the SAVED .prt file, with NO part open.
|
||||
'
|
||||
' This script does NOT call OpenBaseDisplay. It only:
|
||||
' 1. ExistingPart STEP export -> E1_existing.stp
|
||||
' 2. ExistingPart IGES export -> E1_existing.igs
|
||||
'
|
||||
' Run (run_journal takes one arg; use WorkingDirectory):
|
||||
' WorkingDirectory = catalog-masters\KC
|
||||
' run_journal.exe <this file full path>
|
||||
' Result log: <cwd>\test-existingpart.txt
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module TestExistingPart
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
Sub Log(s As String)
|
||||
sb.AppendLine(s)
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Try
|
||||
Run()
|
||||
Catch ex As Exception
|
||||
Log("[FATAL] " & ex.GetType().Name & " / " & ex.Message)
|
||||
End Try
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "test-existingpart.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
Sub Run()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Log("CWD=" & cur)
|
||||
Log("masterExists=" & File.Exists(masterPath))
|
||||
If Not File.Exists(masterPath) Then
|
||||
Log("[FAIL] master missing: " & masterPath)
|
||||
Return
|
||||
End If
|
||||
|
||||
Dim outDir As String = Path.Combine(cur, "dbg_out")
|
||||
Directory.CreateDirectory(outDir)
|
||||
|
||||
' 1) STEP via ExistingPart (reads saved file from disk, no open part)
|
||||
Dim stp As String = Path.Combine(outDir, "E1_existing.stp")
|
||||
If File.Exists(stp) Then File.Delete(stp)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.ExistingPart
|
||||
sc.InputFile = masterPath
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = stp
|
||||
sc.ColorAndLayers = True
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Log("E1_existing.stp size=" & FileSize(stp))
|
||||
Catch ex As Exception
|
||||
Log("STEP ExistingPart FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 2) IGES via ExistingPart
|
||||
Dim igs As String = Path.Combine(outDir, "E1_existing.igs")
|
||||
If File.Exists(igs) Then File.Delete(igs)
|
||||
Try
|
||||
Dim ic As NXOpen.IgesCreator = theSession.DexManager.CreateIgesCreator()
|
||||
ic.ExportFrom = NXOpen.IgesCreator.ExportFromOption.ExistingPart
|
||||
ic.InputFile = masterPath
|
||||
ic.ExportModelData = True
|
||||
ic.OutputFile = igs
|
||||
ic.Commit()
|
||||
ic.Destroy()
|
||||
Log("E1_existing.igs size=" & FileSize(igs))
|
||||
Catch ex As Exception
|
||||
Log("IGES ExistingPart FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Log("DONE")
|
||||
End Sub
|
||||
|
||||
Function FileSize(p As String) As String
|
||||
If Not File.Exists(p) Then Return "MISSING"
|
||||
Return (New FileInfo(p)).Length & " bytes"
|
||||
End Function
|
||||
|
||||
End Module
|
||||
130
sample-data/tools/test-fix-flags.vb
Normal file
130
sample-data/tools/test-fix-flags.vb
Normal file
@@ -0,0 +1,130 @@
|
||||
' =============================================================================
|
||||
' NX Journal: test StepCreator flags that likely cause "0 solids" in batch
|
||||
'
|
||||
' The master is healthy (2 bodies, faces=3) but DisplayPart export gives 0
|
||||
' solids. Prime suspects are StepCreator properties whose batch-mode defaults
|
||||
' differ from interactive mode:
|
||||
'
|
||||
' 1. ExportSolidsAndSurfacesAs (Precise vs Tessellated) <-- TOP suspect
|
||||
' Tessellated needs display facets; batch has no rendering -> 0 solids.
|
||||
' 2. ProcessHoldFlag (if True, translator may hold and not produce)
|
||||
' 3. FileSaveFlag
|
||||
'
|
||||
' This script dumps the defaults of these properties, then tries exporting with
|
||||
' ExportSolidsAndSurfacesAs = Precise (and, for contrast, Tessellated).
|
||||
'
|
||||
' Run: WorkingDirectory = catalog-masters\KC; run_journal.exe <this file>
|
||||
' Result: <cwd>\test-fix-flags.txt Products: <cwd>\dbg_out\
|
||||
'
|
||||
' Note: English comments only (avoids GBK/BOM mojibake on no-BOM .vb files).
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module TestFixFlags
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim sb As New StringBuilder()
|
||||
|
||||
Sub Log(s As String)
|
||||
sb.AppendLine(s)
|
||||
End Sub
|
||||
|
||||
Sub Main()
|
||||
Try
|
||||
Run()
|
||||
Catch ex As Exception
|
||||
Log("[FATAL] " & ex.GetType().Name & " / " & ex.Message)
|
||||
End Try
|
||||
File.WriteAllText(Path.Combine(Directory.GetCurrentDirectory(), "test-fix-flags.txt"), sb.ToString(), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
Function CountBodies(w As Part) As Integer
|
||||
Dim n As Integer = 0
|
||||
Try
|
||||
For Each b As NXOpen.Body In w.Bodies
|
||||
n += 1
|
||||
Next
|
||||
Catch
|
||||
End Try
|
||||
Return n
|
||||
End Function
|
||||
|
||||
Function FileSize(p As String) As String
|
||||
If Not File.Exists(p) Then Return "MISSING"
|
||||
Return (New FileInfo(p)).Length & " bytes"
|
||||
End Function
|
||||
|
||||
Sub Run()
|
||||
Dim cur As String = Directory.GetCurrentDirectory()
|
||||
Dim masterPath As String = Path.Combine(cur, "KC_master.prt")
|
||||
Log("masterExists=" & File.Exists(masterPath))
|
||||
If Not File.Exists(masterPath) Then Return
|
||||
|
||||
Dim pls As NXOpen.PartLoadStatus = Nothing
|
||||
theSession.Parts.OpenBaseDisplay(masterPath, pls)
|
||||
If pls IsNot Nothing Then pls.Dispose()
|
||||
Dim w As Part = theSession.Parts.Work
|
||||
If w Is Nothing Then
|
||||
Log("[FAIL] Work=NULL")
|
||||
Return
|
||||
End If
|
||||
Log("bodies=" & CountBodies(w))
|
||||
|
||||
Dim outDir As String = Path.Combine(cur, "dbg_out")
|
||||
Directory.CreateDirectory(outDir)
|
||||
|
||||
' ---- dump defaults of suspect properties ----
|
||||
Dim sc0 As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
Log("--- StepCreator defaults ---")
|
||||
Try : Log("ProcessHoldFlag=" & sc0.ProcessHoldFlag) : Catch ex As Exception : Log("ProcessHoldFlag throws: " & ex.Message) : End Try
|
||||
Try : Log("FileSaveFlag=" & sc0.FileSaveFlag) : Catch ex As Exception : Log("FileSaveFlag throws: " & ex.Message) : End Try
|
||||
Try : Log("ColorAndLayers=" & sc0.ColorAndLayers) : Catch ex As Exception : Log("ColorAndLayers throws: " & ex.Message) : End Try
|
||||
Try : Log("ExportFrom=" & sc0.ExportFrom.ToString()) : Catch ex As Exception : Log("ExportFrom throws: " & ex.Message) : End Try
|
||||
Try : Log("ExportAs=" & sc0.ExportAs.ToString()) : Catch ex As Exception : Log("ExportAs throws: " & ex.Message) : End Try
|
||||
Try : Log("ExportSolidsAndSurfacesAs=" & sc0.ExportSolidsAndSurfacesAs.ToString()) : Catch ex As Exception : Log("ESSAs throws: " & ex.Message) : End Try
|
||||
Try : Log("ReferenceType=" & sc0.ReferenceType.ToString()) : Catch ex As Exception : Log("ReferenceType throws: " & ex.Message) : End Try
|
||||
sc0.Destroy()
|
||||
|
||||
' ---- Attempt 1: DisplayPart + ESSAs=Precise (TOP hypothesis) ----
|
||||
Dim f1 As String = Path.Combine(outDir, "F1_display_precise.stp")
|
||||
If File.Exists(f1) Then File.Delete(f1)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.DisplayPart
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = f1
|
||||
sc.ColorAndLayers = True
|
||||
Try : sc.ExportSolidsAndSurfacesAs = NXOpen.StepCreator.ExportSolidsAndSurfacesAsOption.Precise : Catch ex As Exception : Log("set ESSAs=Precise: " & ex.Message) : End Try
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Log("F1_display_precise.stp size=" & FileSize(f1))
|
||||
Catch ex As Exception
|
||||
Log("F1 FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' ---- Attempt 2: DisplayPart + ESSAs=Tessellated (contrast) ----
|
||||
Dim f2 As String = Path.Combine(outDir, "F2_display_tess.stp")
|
||||
If File.Exists(f2) Then File.Delete(f2)
|
||||
Try
|
||||
Dim sc As NXOpen.StepCreator = theSession.DexManager.CreateStepCreator()
|
||||
sc.ExportFrom = NXOpen.StepCreator.ExportFromOption.DisplayPart
|
||||
sc.ExportAs = NXOpen.StepCreator.ExportAsOption.Ap214
|
||||
sc.OutputFile = f2
|
||||
sc.ColorAndLayers = True
|
||||
Try : sc.ExportSolidsAndSurfacesAs = NXOpen.StepCreator.ExportSolidsAndSurfacesAsOption.Tessellated : Catch ex As Exception : Log("set ESSAs=Tessellated: " & ex.Message) : End Try
|
||||
sc.Commit()
|
||||
sc.Destroy()
|
||||
Log("F2_display_tess.stp size=" & FileSize(f2))
|
||||
Catch ex As Exception
|
||||
Log("F2 FAIL: " & ex.Message)
|
||||
End Try
|
||||
|
||||
Log("DONE")
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
50
sample-data/tools/verify-step.ps1
Normal file
50
sample-data/tools/verify-step.ps1
Normal file
@@ -0,0 +1,50 @@
|
||||
# STEP 结构自检:引用完整性 / 括号配平 / 纯 ASCII / 头尾结构
|
||||
# 用法: powershell -NoProfile -ExecutionPolicy Bypass -File tools\verify-step.ps1
|
||||
param(
|
||||
[string]$StepDir = (Join-Path $PSScriptRoot '..\onb-sc\step')
|
||||
)
|
||||
|
||||
$files = Get-ChildItem $StepDir -Filter '*.step' | Sort-Object Name
|
||||
$errors = @()
|
||||
$checked = 0
|
||||
|
||||
foreach ($f in $files) {
|
||||
$text = [System.IO.File]::ReadAllText($f.FullName)
|
||||
$checked++
|
||||
|
||||
# 1. 头尾结构
|
||||
if (-not $text.StartsWith('ISO-10303-21;')) { $errors += "$($f.Name): 缺少 ISO-10303-21 头" }
|
||||
if (-not $text.TrimEnd().EndsWith('END-ISO-10303-21;')) { $errors += "$($f.Name): 缺少 END-ISO-10303-21 尾" }
|
||||
if ($text -notmatch '(?s)HEADER;.*?ENDSEC;.*?DATA;.*?ENDSEC;') { $errors += "$($f.Name): HEADER/DATA 段结构异常" }
|
||||
|
||||
# 2. 引用完整性: 所有被引用的 #id 都已定义
|
||||
$defined = @{}
|
||||
foreach ($m in [regex]::Matches($text, '#(\d+)\s*=')) { $defined[[int]$m.Groups[1].Value] = $true }
|
||||
foreach ($m in [regex]::Matches($text, '#(\d+)')) {
|
||||
$id = [int]$m.Groups[1].Value
|
||||
if (-not $defined.ContainsKey($id)) { $errors += "$($f.Name): 引用了未定义的 #$id"; break }
|
||||
}
|
||||
|
||||
# 3. 括号配平 (排除文本单引号内的内容后逐字符统计)
|
||||
$noStr = [regex]::Replace($text, "'[^']*'", "''")
|
||||
$open = ([regex]::Matches($noStr, '\(')).Count
|
||||
$close = ([regex]::Matches($noStr, '\)')).Count
|
||||
if ($open -ne $close) { $errors += "$($f.Name): 括号不配平 ($open/$close)" }
|
||||
|
||||
# 4. 纯 ASCII (STEP 交换格式要求)
|
||||
foreach ($ch in $text.ToCharArray()) {
|
||||
if ([int]$ch -gt 127) { $errors += "$($f.Name): 存在非 ASCII 字符"; break }
|
||||
}
|
||||
|
||||
# 5. 实体计数: 单杆 4x(36+7色) + 尾部14 = 186; 双杆 5x(36+7色) + 14 = 229
|
||||
$entityCount = ([regex]::Matches($text, '#\d+\s*=')).Count
|
||||
if ($entityCount -ne 186 -and $entityCount -ne 229) { $errors += "$($f.Name): 实体数异常 ($entityCount, 期望 186/229)" }
|
||||
}
|
||||
|
||||
Write-Output "检查文件数: $checked"
|
||||
if ($errors.Count -eq 0) {
|
||||
Write-Output "全部通过: 引用完整 / 括号配平 / 纯 ASCII / 结构正常"
|
||||
} else {
|
||||
Write-Output "发现问题 $($errors.Count) 条:"
|
||||
$errors | ForEach-Object { Write-Output " - $_" }
|
||||
}
|
||||
441
sample-data/tools/worker_journal.vb
Normal file
441
sample-data/tools/worker_journal.vb
Normal file
@@ -0,0 +1,441 @@
|
||||
' =============================================================================
|
||||
' NX Journal: 按需生成服务 worker (二期 A 版, NX 2506 实测适配版, 2026-08-27)
|
||||
'
|
||||
' ▍干什么: 常驻轮询 GenServer 任务队列, 认领任务后在**同一 NX 会话内**逐项生成:
|
||||
' 开母模副本 → 设表达式 → 更新 → Save 副本 → PRT SaveAs →
|
||||
' CloseAll 释放句柄 → 外部独立翻译器导出 STEP/IGES → 关闭不保存。
|
||||
' 结果写 jobs\job_<id>.done (行式键值格式, 无 JSON — NX journal 编译环境没有 JSON 库)。
|
||||
'
|
||||
' ▍关键改造 (2026-08-27): 批处理内 DexManager 导出判 0 solid (ST-DEVELOPER 翻译器
|
||||
' 在 run_journal 无 GUI 下读不到几何), 改为 Save 副本后 spawn 独立命令行翻译器
|
||||
' step214ug.exe (.prt→.step) / iges.exe (.prt→.igs), 实测成功。
|
||||
'
|
||||
' ▍任务文件格式 (GenServer 写入, 本脚本解析; 母模/映射/开关/导出配置全部烘进任务, 不读 masters.json):
|
||||
' ITEM
|
||||
' SERIES=KC
|
||||
' CODE=KC0032-87
|
||||
' PARAM type=00 (每参数一行, 值可为空)
|
||||
' PARAM bore=32
|
||||
' MASTER=KC_master.prt
|
||||
' MAP bore=bore (目录参数代码=NX表达式名)
|
||||
' SW magnet,=magOn,0 (参数代码,参数值=NX表达式名,表达式值)
|
||||
' EXP step=1 (导出格式开关; step 恒 1)
|
||||
' END
|
||||
'
|
||||
' ▍运行 (run_journal 只接受一个参数; 用 WorkingDirectory 定位配置):
|
||||
' WorkingDirectory = GenServer 运行目录 (含 worker-config.txt + worker-no.txt)
|
||||
' run_journal.exe <本文件完整路径>
|
||||
'
|
||||
' ▍注意
|
||||
' - 中文注释需 UTF-8 带 BOM (改完手动恢复 BOM)
|
||||
' - 母模副本现在必须 Save (独立翻译器从磁盘读); 但副本用完即删, 绝不保留脏副本
|
||||
' - 导出格式 (masters.json exports): step 恒导出, iges/prt 可选, parasolid/stl 已按用户要求砍掉
|
||||
' - NX 2506 关键 API (反射探针核对): OpenBaseDisplay(批处理必须, OpenBase 后 Work=NULL) /
|
||||
' ExpressionCollection.CreateExpression("Number","名=值") / UF EditExp(仅编辑已存在) / CloseAll 两参数
|
||||
' =============================================================================
|
||||
Option Strict Off
|
||||
Imports System
|
||||
Imports System.IO
|
||||
Imports System.Text
|
||||
Imports System.Diagnostics
|
||||
Imports System.Collections.Generic
|
||||
Imports NXOpen
|
||||
Imports NXOpen.UF
|
||||
|
||||
Module WorkerGen
|
||||
|
||||
Dim theSession As Session = Session.GetSession()
|
||||
Dim lw As ListingWindow = theSession.ListingWindow
|
||||
|
||||
Dim jobsDir, outputDir, mastersDir, workDir, nxBin As String
|
||||
Dim workerNo As String = "0"
|
||||
Dim pollMs As Integer = 1500
|
||||
|
||||
Sub Main()
|
||||
lw.Open()
|
||||
' 配置 = 当前工作目录 worker-config.txt (key=value 行式)
|
||||
Dim cfgPath As String = Path.Combine(Directory.GetCurrentDirectory(), "worker-config.txt")
|
||||
If Not File.Exists(cfgPath) Then
|
||||
lw.WriteLine("[worker] 未找到配置: " & cfgPath & " (请以 GenServer 运行目录为工作目录启动)")
|
||||
Return
|
||||
End If
|
||||
For Each line As String In File.ReadAllLines(cfgPath, Encoding.UTF8)
|
||||
Dim i As Integer = line.IndexOf("="c)
|
||||
If i <= 0 Then Continue For
|
||||
Dim k As String = line.Substring(0, i).Trim()
|
||||
Dim v As String = line.Substring(i + 1).Trim()
|
||||
If k = "jobsDir" Then jobsDir = v
|
||||
If k = "outputDir" Then outputDir = v
|
||||
If k = "mastersDir" Then mastersDir = v
|
||||
If k = "workDir" Then workDir = v
|
||||
If k = "nxBin" Then nxBin = v
|
||||
Next
|
||||
If jobsDir.Length = 0 OrElse outputDir.Length = 0 Then
|
||||
lw.WriteLine("[worker] 配置缺少 jobsDir/outputDir")
|
||||
Return
|
||||
End If
|
||||
If nxBin.Length = 0 Then
|
||||
' 回退: 自动探测 NX 安装目录 (部署到新机器无需手填)
|
||||
For Each root As String In New String() {
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Siemens"),
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Siemens"),
|
||||
"C:\Program Files\Siemens"}
|
||||
If Not Directory.Exists(root) Then Continue For
|
||||
For Each ver As String In Directory.GetDirectories(root, "NX*")
|
||||
Dim b As String = Path.Combine(ver, "NXBIN")
|
||||
If File.Exists(Path.Combine(b, "run_journal.exe")) Then nxBin = b : Exit For
|
||||
Next
|
||||
If nxBin.Length > 0 Then Exit For
|
||||
Next
|
||||
End If
|
||||
If nxBin.Length = 0 Then
|
||||
lw.WriteLine("[worker] 警告: 未找到 NX NXBIN 目录 (nxBin 未配置且自动探测失败), STEP/IGES 导出将失败")
|
||||
Else
|
||||
lw.WriteLine("[worker] nxBin=" & nxBin)
|
||||
End If
|
||||
Dim noPath As String = Path.Combine(Directory.GetCurrentDirectory(), "worker-no.txt")
|
||||
If File.Exists(noPath) Then
|
||||
Dim noTxt As String = File.ReadAllText(noPath, Encoding.UTF8).Trim()
|
||||
If noTxt.Length > 0 Then workerNo = noTxt
|
||||
End If
|
||||
For Each d As String In {jobsDir, outputDir, workDir}
|
||||
If d.Length > 0 AndAlso Not Directory.Exists(d) Then Directory.CreateDirectory(d)
|
||||
Next
|
||||
lw.WriteLine("[worker" & workerNo & "] 启动: jobs=" & jobsDir & " 输出=" & outputDir)
|
||||
|
||||
While True
|
||||
Try
|
||||
TouchHeartbeat()
|
||||
ProcessNextJob()
|
||||
Catch ex As Exception
|
||||
lw.WriteLine("[worker" & workerNo & "] 循环异常: " & ex.Message)
|
||||
End Try
|
||||
Threading.Thread.Sleep(pollMs)
|
||||
End While
|
||||
End Sub
|
||||
|
||||
' ---- 认领一个任务并处理 (原子改名防双 worker 抢同一任务) ----
|
||||
Sub ProcessNextJob()
|
||||
Dim jobFile As String = Nothing
|
||||
Dim files() As String = Directory.GetFiles(jobsDir, "job_*.json")
|
||||
If files.Length = 0 Then Return
|
||||
Array.Sort(files)
|
||||
For Each f As String In files
|
||||
Dim running As String = f.Substring(0, f.Length - 5) & ".running"
|
||||
Try
|
||||
File.Move(f, running)
|
||||
jobFile = running
|
||||
Exit For
|
||||
Catch
|
||||
' 被其他 worker 抢走, 试下一个
|
||||
End Try
|
||||
Next
|
||||
If jobFile Is Nothing Then Return
|
||||
|
||||
Dim jobId As String = Path.GetFileName(jobFile).Replace("job_", "").Replace(".running", "")
|
||||
Dim workJobDir As String = Path.Combine(workDir, "job_" & jobId)
|
||||
Directory.CreateDirectory(workJobDir)
|
||||
lw.WriteLine("[worker" & workerNo & "] 认领任务 " & jobId & " " & DateTime.Now.ToString("HH:mm:ss"))
|
||||
|
||||
Dim okOverall As Boolean = True
|
||||
Dim errOverall As String = ""
|
||||
Dim doneSb As New StringBuilder()
|
||||
Dim items As New List(Of Dictionary(Of String, Object))()
|
||||
Dim masterCopies As New Dictionary(Of String, String)() ' 同系列共用一份母模副本 (副本从不保存)
|
||||
|
||||
Try
|
||||
items = ParseJob(File.ReadAllText(jobFile, Encoding.UTF8))
|
||||
doneSb.AppendLine("OK=1")
|
||||
doneSb.AppendLine("ERROR=")
|
||||
For idx As Integer = 0 To items.Count - 1
|
||||
Dim it As Dictionary(Of String, Object) = items(idx)
|
||||
Dim series As String = GetStr(it, "series")
|
||||
Dim code As String = GetStr(it, "code")
|
||||
Dim pars As Dictionary(Of String, Object) = GetDict(it, "params")
|
||||
Dim outDir As String = Path.Combine(outputDir, series, code)
|
||||
Directory.CreateDirectory(outDir)
|
||||
Dim itemOk As Boolean = True
|
||||
Dim itemErr As String = ""
|
||||
Dim itemHit As Boolean = False
|
||||
Dim filesList As New List(Of String)()
|
||||
Try
|
||||
' 检索先行 (认领时再查一次, 防并发窗口重复生成)
|
||||
If OutputHit(series, code, it) Then
|
||||
itemHit = True
|
||||
filesList = ListOutputFiles(code, it)
|
||||
lw.WriteLine(" [" & code & "] 已存在, 命中复用")
|
||||
Else
|
||||
' 拷母模副本 (同系列共用; 绝不保存)
|
||||
If Not masterCopies.ContainsKey(series) Then
|
||||
Dim src As String = Path.Combine(mastersDir, series, GetStr(it, "master"))
|
||||
If Not File.Exists(src) Then Throw New Exception("母模缺失: " & src)
|
||||
Dim dst As String = Path.Combine(workJobDir, series & "_master.prt")
|
||||
File.Copy(src, dst, True)
|
||||
' 清只读属性 (源母模可能带只读, NX 只读打开会拒绝保存/导出)
|
||||
File.SetAttributes(dst, File.GetAttributes(dst) And Not FileAttributes.ReadOnly)
|
||||
masterCopies(series) = dst
|
||||
End If
|
||||
GenerateOne(series, code, pars, it, masterCopies(series), outDir)
|
||||
filesList = ListOutputFiles(code, it)
|
||||
lw.WriteLine(" [" & code & "] 生成完成 → " & outDir)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
okOverall = False
|
||||
itemOk = False
|
||||
itemErr = ex.Message
|
||||
lw.WriteLine(" [" & code & "] 失败: " & ex.Message)
|
||||
End Try
|
||||
doneSb.AppendLine("ITEM")
|
||||
doneSb.AppendLine("SERIES=" & series)
|
||||
doneSb.AppendLine("CODE=" & code)
|
||||
doneSb.AppendLine("OK=" & (If(itemOk, "1", "0")))
|
||||
doneSb.AppendLine("HIT=" & (If(itemHit, "1", "0")))
|
||||
doneSb.AppendLine("ERROR=" & itemErr)
|
||||
For Each f As String In filesList
|
||||
doneSb.AppendLine("FILE=" & f)
|
||||
Next
|
||||
doneSb.AppendLine("END")
|
||||
' 逐项进度 (批量打包 UI 显示 done/total)
|
||||
File.WriteAllText(Path.Combine(jobsDir, "job_" & jobId & ".prog"),
|
||||
"DONE=" & (idx + 1) & vbCrLf & "TOTAL=" & items.Count & vbCrLf, Encoding.UTF8)
|
||||
' 关闭所有部件 (不保存), 会话内循环 (NX 2506: CloseAll 两参数)
|
||||
Try
|
||||
theSession.Parts.CloseAll(NXOpen.BasePart.CloseModified.CloseModified, Nothing)
|
||||
Catch
|
||||
End Try
|
||||
Next
|
||||
Catch ex As Exception
|
||||
okOverall = False
|
||||
errOverall = ex.Message
|
||||
lw.WriteLine("[worker" & workerNo & "] 任务异常: " & ex.Message)
|
||||
End Try
|
||||
|
||||
' 写结果 (原子: 先写临时再改名; 错误信息去换行防格式破坏)
|
||||
Dim doneText As String = doneSb.ToString()
|
||||
If Not okOverall Then
|
||||
Dim errClean As String = errOverall.Replace(vbCr, " ").Replace(vbLf, " ")
|
||||
If doneText.IndexOf("ITEM", StringComparison.OrdinalIgnoreCase) < 0 Then
|
||||
doneText = "OK=0" & vbCrLf & "ERROR=" & errClean & vbCrLf
|
||||
Else
|
||||
doneText = doneText.Replace("OK=1" & vbCrLf & "ERROR=", "OK=0" & vbCrLf & "ERROR=" & errClean)
|
||||
End If
|
||||
End If
|
||||
Dim donePath As String = Path.Combine(jobsDir, "job_" & jobId & ".done")
|
||||
Dim tmpPath As String = donePath & ".tmp"
|
||||
File.WriteAllText(tmpPath, doneText, New UTF8Encoding(True))
|
||||
If File.Exists(donePath) Then File.Delete(donePath)
|
||||
File.Move(tmpPath, donePath)
|
||||
Try
|
||||
File.Delete(jobFile) ' 删 .running
|
||||
Directory.Delete(workJobDir, True) ' 任务私有工作副本即清 (输出留存不删)
|
||||
Catch
|
||||
End Try
|
||||
lw.WriteLine("[worker" & workerNo & "] 任务 " & jobId & " 结束 " & (If(okOverall, "OK", "FAIL")))
|
||||
End Sub
|
||||
|
||||
' ---- 任务文件解析 (行式键值; 返回每项一个字典: series/code/params/master/map/sw/exp) ----
|
||||
Function ParseJob(text As String) As List(Of Dictionary(Of String, Object))
|
||||
Dim result As New List(Of Dictionary(Of String, Object))()
|
||||
Dim cur As Dictionary(Of String, Object) = Nothing
|
||||
For Each raw As String In text.Split(New String() {vbCrLf, vbLf, vbCr}, StringSplitOptions.None)
|
||||
Dim l As String = raw.Trim()
|
||||
If l.Length = 0 Then Continue For
|
||||
If l = "ITEM" Then
|
||||
cur = New Dictionary(Of String, Object)()
|
||||
cur("params") = New Dictionary(Of String, Object)()
|
||||
cur("map") = New Dictionary(Of String, Object)()
|
||||
cur("sw") = New List(Of Object)()
|
||||
cur("exp") = New Dictionary(Of String, Object)()
|
||||
result.Add(cur)
|
||||
Continue For
|
||||
End If
|
||||
If l = "END" Then cur = Nothing : Continue For
|
||||
If cur Is Nothing Then Continue For
|
||||
If l.StartsWith("SERIES=") Then cur("series") = l.Substring(7).Trim() : Continue For
|
||||
If l.StartsWith("CODE=") Then cur("code") = l.Substring(5).Trim() : Continue For
|
||||
If l.StartsWith("MASTER=") Then cur("master") = l.Substring(7).Trim() : Continue For
|
||||
If l.StartsWith("PARAM ") Then
|
||||
Dim rest As String = l.Substring(6)
|
||||
Dim j As Integer = rest.IndexOf("="c)
|
||||
If j > 0 Then CType(cur("params"), Dictionary(Of String, Object))(rest.Substring(0, j).Trim()) = rest.Substring(j + 1).Trim()
|
||||
Continue For
|
||||
End If
|
||||
If l.StartsWith("MAP ") Then
|
||||
Dim rest As String = l.Substring(4)
|
||||
Dim j As Integer = rest.IndexOf("="c)
|
||||
If j > 0 Then CType(cur("map"), Dictionary(Of String, Object))(rest.Substring(0, j).Trim()) = rest.Substring(j + 1).Trim()
|
||||
Continue For
|
||||
End If
|
||||
If l.StartsWith("SW ") Then
|
||||
' SW 参数代码,参数值=表达式名,表达式值
|
||||
Dim rest As String = l.Substring(3)
|
||||
Dim j As Integer = rest.IndexOf("="c)
|
||||
If j > 0 Then
|
||||
Dim left() As String = rest.Substring(0, j).Split(","c)
|
||||
Dim right() As String = rest.Substring(j + 1).Split(","c)
|
||||
If left.Length >= 2 AndAlso right.Length >= 2 Then
|
||||
Dim sw As New Dictionary(Of String, Object)()
|
||||
sw("pc") = left(0).Trim() : sw("pv") = left(1).Trim()
|
||||
sw("ex") = right(0).Trim() : sw("ev") = right(1).Trim()
|
||||
CType(cur("sw"), List(Of Object)).Add(sw)
|
||||
End If
|
||||
End If
|
||||
Continue For
|
||||
End If
|
||||
If l.StartsWith("EXP ") Then
|
||||
Dim rest As String = l.Substring(4)
|
||||
Dim j As Integer = rest.IndexOf("="c)
|
||||
If j > 0 AndAlso rest.Substring(j + 1).Trim() = "1" Then
|
||||
CType(cur("exp"), Dictionary(Of String, Object))(rest.Substring(0, j).Trim()) = "1"
|
||||
End If
|
||||
Continue For
|
||||
End If
|
||||
Next
|
||||
Return result
|
||||
End Function
|
||||
|
||||
Function GetStr(d As Dictionary(Of String, Object), key As String) As String
|
||||
If d Is Nothing OrElse Not d.ContainsKey(key) OrElse d(key) Is Nothing Then Return ""
|
||||
Return Convert.ToString(d(key))
|
||||
End Function
|
||||
|
||||
Function GetDict(d As Dictionary(Of String, Object), key As String) As Dictionary(Of String, Object)
|
||||
If d Is Nothing OrElse Not d.ContainsKey(key) OrElse d(key) Is Nothing Then Return New Dictionary(Of String, Object)()
|
||||
Return CType(d(key), Dictionary(Of String, Object))
|
||||
End Function
|
||||
|
||||
' 导出格式开关: job 里 EXP 行 (EXP parasolid=1); step 恒导出
|
||||
Function ExpOn(it As Dictionary(Of String, Object), fmt As String) As Boolean
|
||||
If fmt = "step" Then Return True
|
||||
Dim exp As Dictionary(Of String, Object) = GetDict(it, "exp")
|
||||
Return exp.ContainsKey(fmt)
|
||||
End Function
|
||||
|
||||
' ---- 检索先行: 输出目录存在且全部配置格式文件齐全 ----
|
||||
Function OutputHit(series As String, code As String, it As Dictionary(Of String, Object)) As Boolean
|
||||
Dim dir As String = Path.Combine(outputDir, series, code)
|
||||
If Not Directory.Exists(dir) Then Return False
|
||||
For Each f As String In New String() {code & ".stp", code & ".x_t", code & ".igs", code & ".stl", code & ".prt"}
|
||||
If f.EndsWith(".stp") AndAlso Not File.Exists(Path.Combine(dir, f)) Then Return False
|
||||
If f.EndsWith(".x_t") AndAlso ExpOn(it, "parasolid") AndAlso Not File.Exists(Path.Combine(dir, f)) Then Return False
|
||||
If f.EndsWith(".igs") AndAlso ExpOn(it, "iges") AndAlso Not File.Exists(Path.Combine(dir, f)) Then Return False
|
||||
If f.EndsWith(".stl") AndAlso ExpOn(it, "stl") AndAlso Not File.Exists(Path.Combine(dir, f)) Then Return False
|
||||
If f.EndsWith(".prt") AndAlso ExpOn(it, "prt") AndAlso Not File.Exists(Path.Combine(dir, f)) Then Return False
|
||||
Next
|
||||
Return True
|
||||
End Function
|
||||
|
||||
Function ListOutputFiles(code As String, it As Dictionary(Of String, Object)) As List(Of String)
|
||||
Dim list As New List(Of String)()
|
||||
list.Add(code & ".stp")
|
||||
If ExpOn(it, "parasolid") Then list.Add(code & ".x_t")
|
||||
If ExpOn(it, "iges") Then list.Add(code & ".igs")
|
||||
If ExpOn(it, "stl") Then list.Add(code & ".stl")
|
||||
If ExpOn(it, "prt") Then list.Add(code & ".prt")
|
||||
Return list
|
||||
End Function
|
||||
|
||||
' ---- 单型号生成: 开副本 → 设表达式 → 更新 → 多格式导出 → 关闭 ----
|
||||
Sub GenerateOne(series As String, code As String, pars As Dictionary(Of String, Object),
|
||||
it As Dictionary(Of String, Object), masterCopy As String, outDir As String)
|
||||
' 1) 打开母模副本 (OpenBaseDisplay 使其成为工作部件; 副本会 Save 供外部翻译器读盘, 用完即删)
|
||||
Dim partLoadStatus1 As NXOpen.PartLoadStatus = Nothing
|
||||
Dim basePart1 As NXOpen.BasePart = theSession.Parts.OpenBaseDisplay(masterCopy, partLoadStatus1)
|
||||
If partLoadStatus1 IsNot Nothing Then partLoadStatus1.Dispose()
|
||||
Dim workPart As NXOpen.Part = theSession.Parts.Work
|
||||
If workPart Is Nothing Then Throw New Exception("打开母模失败: " & masterCopy)
|
||||
|
||||
' 2) 设表达式 (UF EditExp 只编辑已存在的表达式; 母模必须已建好这些表达式)
|
||||
Dim ufsW As UFSession = UFSession.GetUFSession()
|
||||
Dim exprMap As Dictionary(Of String, Object) = GetDict(it, "map")
|
||||
For Each kv As KeyValuePair(Of String, Object) In exprMap
|
||||
If pars.ContainsKey(kv.Key) Then
|
||||
ufsW.Modl.EditExp(Convert.ToString(kv.Value) & "=" & ValueStr(pars(kv.Key)))
|
||||
End If
|
||||
Next
|
||||
Dim swList As Object = Nothing
|
||||
If it.ContainsKey("sw") Then swList = it("sw")
|
||||
If swList IsNot Nothing Then
|
||||
For Each swObj As Object In CType(swList, List(Of Object))
|
||||
Dim sw As Dictionary(Of String, Object) = CType(swObj, Dictionary(Of String, Object))
|
||||
If pars.ContainsKey(GetStr(sw, "pc")) AndAlso ValueStr(pars(GetStr(sw, "pc"))) = GetStr(sw, "pv") Then
|
||||
ufsW.Modl.EditExp(GetStr(sw, "ex") & "=" & GetStr(sw, "ev"))
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
' 3) Update model
|
||||
ufsW.Modl.Update()
|
||||
|
||||
' 4) Save the copy to disk so independent translators can read it.
|
||||
' (in-session DexManager export yields 0 solids in batch - verified; see dev log 4.5)
|
||||
Dim pssSave As NXOpen.PartSaveStatus = workPart.Save(NXOpen.BasePart.SaveComponents.True, NXOpen.BasePart.CloseAfterSave.False)
|
||||
If pssSave IsNot Nothing Then pssSave.Dispose()
|
||||
|
||||
' 5) PRT save-as (NX native parametric part; session SaveAs)
|
||||
If ExpOn(it, "prt") Then ExportPrt(Path.Combine(outDir, code & ".prt"))
|
||||
|
||||
' 6) Close all parts to release the file handle so external translators can read
|
||||
Try
|
||||
theSession.Parts.CloseAll(NXOpen.BasePart.CloseModified.CloseModified, Nothing)
|
||||
Catch
|
||||
End Try
|
||||
|
||||
' 7) Export via independent CLI translators (bypass batch 0-solid dead end)
|
||||
ExportStepCli(masterCopy, Path.Combine(outDir, code & ".stp"))
|
||||
If ExpOn(it, "iges") Then ExportIgesCli(masterCopy, Path.Combine(outDir, code & ".igs"))
|
||||
End Sub
|
||||
|
||||
' ---- Independent CLI translators (bypass batch 0-solid dead end) ----
|
||||
' step214ug.exe: bidirectional. input .prt -> export STEP. (verified 2 solids, 7KB)
|
||||
' Syntax: step214ug.exe <input> o=<output> d=<def> l=<log>
|
||||
Sub ExportStepCli(inputPrt As String, fileName As String)
|
||||
If File.Exists(fileName) Then File.Delete(fileName)
|
||||
Dim psi As New ProcessStartInfo()
|
||||
psi.FileName = Path.Combine(nxBin, "step214ug.exe")
|
||||
psi.Arguments = """" & inputPrt & """ o=""" & fileName & """ l=""" & fileName & ".log"""
|
||||
psi.WorkingDirectory = Path.GetDirectoryName(inputPrt)
|
||||
psi.UseShellExecute = False
|
||||
psi.CreateNoWindow = True
|
||||
Dim p As Process = Process.Start(psi)
|
||||
p.WaitForExit()
|
||||
If Not File.Exists(fileName) OrElse (New FileInfo(fileName)).Length < 1000 Then
|
||||
Throw New Exception("STEP export failed (empty/missing): " & fileName)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' iges.exe: input .prt -> export IGES. (verified Face=6)
|
||||
' Syntax: iges.exe <input> o=<output> l=<log>
|
||||
Sub ExportIgesCli(inputPrt As String, fileName As String)
|
||||
If File.Exists(fileName) Then File.Delete(fileName)
|
||||
Dim psi As New ProcessStartInfo()
|
||||
psi.FileName = Path.Combine(nxBin, "iges.exe")
|
||||
psi.Arguments = """" & inputPrt & """ o=""" & fileName & """ l=""" & fileName & ".log"""
|
||||
psi.WorkingDirectory = Path.GetDirectoryName(inputPrt)
|
||||
psi.UseShellExecute = False
|
||||
psi.CreateNoWindow = True
|
||||
Dim p As Process = Process.Start(psi)
|
||||
p.WaitForExit()
|
||||
If Not File.Exists(fileName) Then
|
||||
Throw New Exception("IGES export failed (missing): " & fileName)
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' NX native part export: save-as current work part (parametric exprs + feature tree; 2026-08-26 user request)
|
||||
Sub ExportPrt(fileName As String)
|
||||
Dim pss As NXOpen.PartSaveStatus = theSession.Parts.Work.SaveAs(fileName)
|
||||
If pss IsNot Nothing Then pss.Dispose()
|
||||
End Sub
|
||||
|
||||
' ---- 工具 ----
|
||||
Function ValueStr(v As Object) As String
|
||||
If v Is Nothing Then Return ""
|
||||
Return Convert.ToString(v, System.Globalization.CultureInfo.InvariantCulture).Replace(",", ".")
|
||||
End Function
|
||||
|
||||
Sub TouchHeartbeat()
|
||||
Dim hb As String = Path.Combine(workDir, "heartbeat_" & workerNo & ".txt")
|
||||
File.WriteAllText(hb, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), New UTF8Encoding(True))
|
||||
End Sub
|
||||
|
||||
End Module
|
||||
Reference in New Issue
Block a user