v2.1: DESTACO 风格选型界面 + 参数组合补全(缺组合也能出零件号)

网页版选型改版 (仅网页版, 桌面版待同步)
- 新增零件号实时条: 边选边拼, 已确定片段实心高亮, 未定片段虚化, 进度条随选推进;
  浅色配色 (初版深色块与页面不协调, 已改); 选满且唯一命中时整块转青色实心
- 技术方案: 40 系列中仅 KC 有编码模板 → 改用「当前候选型号的最长公共前缀」,
  已验证 40/40 系列前缀单调增长(不回退)
- 参数选择由下拉菜单改为可视化选项块: 悬停抬升+青色投影, 选中青色填充+✓,
  不可选取值置灰(虚线+划线, 不消失, 让用户看到选项存在但当前组合不适用),
  再点一次取消; 有说明的显示代号+说明, 纯数值用紧凑文字块
- 参数联动仍复用 allowedOptions 规则引擎, 语义未变, 只改渲染

参数组合补全
- 原逻辑: 必须有真实数模才能生成型号 → 库里没有的组合选不出来也出不了号
- infer-sku-template.py 从已有型号反推编码模板, 得 23 个系列 100% 可复现
- verify-templates.py 作为门禁: 用模板重渲染每个已有型号必须逐字一致 ——
  该检查抓出 ULM/ULP 行程补零位数写错(2位写3位), 否则会生成 3000 个不存在的零件号
- gen-missing-combos.py 补全 8 个系列共 3157 个型号 (全库 54136 → 57293)
- 占位数模: 3157 个型号共用 1 个 PLACEHOLDER.step(4.5KB), 构建器按路径打包只存一份,
  数据包仅 2.10GB → 2.14GB (中途用硬链接方案踩坑: NTFS 上限 1024, 超出退化为拷贝白占 500MB)
- _pending_models.csv 输出待补数模清单, 供 AI 补完后替换 step_file

新增脚本: infer-sku-template / verify-templates / quantify-combo-gap /
          gen-missing-combos / gen-placeholder-step / check-combo-unique
新增文档: docs/更改说明-v2.1.md

发现未处理 (已记录在更改说明):
- 夹紧缸 9 系列一组参数对应多型号 (310 个型号, 编码含参数外尾缀, 旧码/新码混用)
- UAGP/LAGP 参数顺序与官方编码结构不符 (传感器应在转角之前)
- 17 个系列因模板不干净未补全 (宁可少补, 不生成可能不存在的零件号)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
wangruiguo
2026-09-15 02:14:55 +08:00
parent 3c4f236cba
commit fc5ee1dd36
19 changed files with 7355 additions and 25 deletions

158
docs/更改说明-v2.1.md Normal file
View File

@@ -0,0 +1,158 @@
# v2.1 更改说明
发布日期:2026-09-15
上一版:v2.0(4 分类真实数据替换 + 手册彩色重做 + 网页版改版)
本版聚焦两件事:**选型交互改为 DESTACO 风格**,以及**参数组合补全(缺组合也能出零件号)**。
---
## 一、选型界面:DESTACO 风格(网页版)
参照 DESTACO 配置器的交互做了改版。**只改网页版**,桌面版保持原样,后续再同步。
### 1. 零件号实时条(新增)
参数面板顶部新增一条零件号显示区,**边选边拼**:
- 已确定的片段用深青色实心显示,未确定的用浅灰虚化占位
- 底部进度条随选择推进
- 选满且唯一命中时,整条转为青色实心渐变(完成反馈)
- 浅色配色,与页面整体风格一致(初版曾用深色块,观感不协调,已改)
**技术说明**:40 个系列中只有 KC 有编码模板,无法靠模板拼号。改用**「当前候选型号的最长公共前缀」**——
选的越多候选越少、公共前缀越长,自然拼出完整零件号。已验证 40/40 系列前缀单调增长(不回退)。
### 2. 参数选择改为可视化选项块(替代下拉菜单)
- 每个维度显示为**选项块网格**,而不是 `<select>` 下拉
- 悬停抬升 + 青色投影;选中青色填充 + ✓ 角标
- **不可选的取值置灰**(虚线框 + 划线),而不是从列表消失——用户能看到"该选项存在但当前组合不适用"
- 再点一次可取消选择
- 有说明文字的(如传感器 `K` = 电子 PNP M12)显示代号大字 + 说明小字;纯数值显示紧凑文字块
- 连续域参数(range,如 KC 的行程)保留输入框 + 标准档位
**语义未变**:参数联动仍复用原有 `allowedOptions` 规则引擎,只改了渲染方式。
---
## 二、参数组合补全:库中缺的组合现在也能出零件号
### 背景
原逻辑是「**必须有真实数模才能生成型号**」:型号表由真实 STEP 文件扫描而来,构建器要求每个型号的数模必须存在。
结果是——**库里没有的组合,用户选不出来,也拿不到零件号**。
### 做法
**1. 从已有型号反推编码模板**
40 个系列里只有 1 个(KC)有现成模板。写了 `infer-sku-template.py` 从已有型号反推编码规则,
得到 **23 个系列 100% 可复现**的模板,例如:
```
ULP{size}{stroke:pad3}{rod}{orient}{sensor}0 → ULP + 32 + 020 + A + E + K + 0
ULA{size}{stroke:pad3}{rod}{mount}{sensor}0
USSL{size}{pcmodel}{hook_pos}{pin_dia}{locating}{prog}{sensor}0
```
**并加了自检门禁**(`verify-templates.py`):用模板重新渲染**每一个已有型号**,必须与库中编码逐字一致。
这道检查抓出了一处真实错误——ULM/ULP 的行程补零位数写错(2 位写成 3 位),
**若无此校验会生成 3,000 个不存在的零件号发给客户**。
**2. 补齐缺失组合**
只对模板验证通过的 23 个系列补全(其余 17 个系列因旧码/新码混用、编码结构特殊,未补)。
| 系列 | 补全前 | 补全后 |
|---|---|---|
| ULP | 168 | **2,970** |
| ULM | 70 | 200 |
| ULA | 19 | 192 |
| ULL | 14 | 40 |
| ULY / ULZ | 各 10 | 各 20 |
| USSL | 7 | 12 |
| ULT | 3 | 4 |
| **合计新增** | | **+3,157 个型号** |
全库型号数:**54,136 → 57,293**。构建通过(0 错误 0 警告)。
**3. 占位数模**
新增的 3,157 个型号共用 **1 个 `PLACEHOLDER.step`**(4.5KB 方块)。
构建器按路径打包,同一路径只存一份,因此**数据包仅从 2.10GB 增至 2.14GB**。
> 中途走过的弯路:先按"每型号一个硬链接文件"实现,但 NTFS 硬链接上限为 1024,
> 超出部分退化为真实拷贝,白占约 500MB。改为共享单文件后归零。
**4. 待补数模清单**
`onebot-data/catalog/_pending_models.csv` —— 3,157 行,列出每个待补型号及其参数,供 AI 补真实数模。
补完后把该行的 `step_file` 从 `PLACEHOLDER.step` 改为 `<型号>.step` 即可。
---
## 三、新增脚本
| 脚本 | 用途 |
|---|---|
| `infer-sku-template.py` | 从已有型号反推编码模板 |
| `verify-templates.py` | 校验模板能 100% 复现已有型号(生成新号前的门禁) |
| `quantify-combo-gap.py` | 统计参数组合缺口规模 |
| `gen-missing-combos.py` | 补全缺失组合(`--report` 只统计,`--write` 落盘) |
| `gen-placeholder-step.py` | 生成占位数模方块 |
| `check-combo-unique.py` | 检查"参数组合 ↔ 型号"是否一对一 |
---
## 四、发现但未处理的问题
### 1. 夹紧缸 9 个系列:一组参数对应多个型号
| 系列 | 变体数 | 重复组数 | 一组最多对应 |
|---|---|---|---|
| UCBP | 358 | 46 组 | 18 个型号 |
| UCBM | 266 | 26 组 | 15 个型号 |
| UCBH | 61 | 7 组 | 5 个型号 |
| UCBQ / UCDP / UCIM / UCIP / UFL / UCBT | — | 2-4 组 | 2-6 个型号 |
共 **310 个型号**受影响。原因是这些型号的编码里有**参数之外的尾缀**(版本号/变体标记),
如 `UCBP25AK00` vs `UCBP25AK010`(差末尾版本号)、`UBP25010` vs `UCBP25BK0`(旧码/新码混用)。
**影响**:用户选完参数后仍无法唯一确定型号。界面已提示"该组合对应 N 个型号",但需后续补维度或清理。
### 2. UAGP / LAGP 参数顺序与官方编码结构不符
官方订购码顺序为 `规格 - 台位 - 传感器 - 转角 - 制动 - 接口`,
当前 meta 里传感器排在最后。不影响选型正确性(参数按名索引),但导致编码模板推不出来,
且界面展示顺序与官方不一致。
### 3. 未补全的 17 个系列
模板验证未通过的系列保持原状——**宁可少补,也不生成可能不存在的零件号**。
---
## 五、涉及文件
| 文件 | 改动 |
|---|---|
| `web/app.js` | 零件号实时条 + 选项块渲染(+148 行) |
| `web/style.css` | SKU 条与选项块样式(+111 行) |
| `web/index.html` | 新增 SKU 条容器 |
| `onebot-data/catalog/csv/*.csv` | 8 个系列追加 3,157 行 |
| `onebot-data/catalog/step/PLACEHOLDER.step` | 新增占位数模 |
| `onebot-data/catalog/_pending_models.csv` | 新增待补数模清单 |
| `onebot-data/tools/*.py` | 新增 6 个数据管线脚本 |
---
## 六、验证方式
1. **构建**:`bin\OnebotCatalog.exe --buildfull onebot-data\catalog sample\OnebotCatalog_2026.08.opc`
→ 应输出 `系列数: 40 | 变体数: 57293`,`校验: 0 错误, 0 警告`
2. **网页预览**:`tools\serve-web.ps1` → 打开 `http://localhost:8080`(Ctrl+F5 强刷)
- 选定位销 PIN → ULP → 选一个原来选不到的组合(如 63 缸 / 110 行程)
- 应能看到零件号实时拼出、可下载(下载到的是占位方块)
3. **模板门禁**:`PYTHONUTF8=1 python onebot-data\tools\verify-templates.py`
→ 应输出「所有模板对已有型号 100% 复现」

File diff suppressed because it is too large Load Diff

View File

@@ -18,3 +18,176 @@ ULA63040AD0,63,40,A,,D,ULA63040AD0.step
ULA63040AK0,63,40,A,,K,ULA63040AK0.step
ULA63050AK0,63,50,A,,K,ULA63050AK0.step
ULA63060AK0,63,60,A,,K,ULA63060AK0.step
ULA40020AD0,40,20,A,,D,PLACEHOLDER.step
ULA40020AK0,40,20,A,,K,PLACEHOLDER.step
ULA40020GSD0,40,20,G,S,D,PLACEHOLDER.step
ULA40020GD0,40,20,G,,D,PLACEHOLDER.step
ULA40020GK0,40,20,G,,K,PLACEHOLDER.step
ULA40020NSD0,40,20,N,S,D,PLACEHOLDER.step
ULA40020ND0,40,20,N,,D,PLACEHOLDER.step
ULA40020NK0,40,20,N,,K,PLACEHOLDER.step
ULA40020PSD0,40,20,P,S,D,PLACEHOLDER.step
ULA40020PD0,40,20,P,,D,PLACEHOLDER.step
ULA40020PK0,40,20,P,,K,PLACEHOLDER.step
ULA40040AD0,40,40,A,,D,PLACEHOLDER.step
ULA40040AK0,40,40,A,,K,PLACEHOLDER.step
ULA40040GD0,40,40,G,,D,PLACEHOLDER.step
ULA40040GK0,40,40,G,,K,PLACEHOLDER.step
ULA40040ND0,40,40,N,,D,PLACEHOLDER.step
ULA40040NK0,40,40,N,,K,PLACEHOLDER.step
ULA40040PD0,40,40,P,,D,PLACEHOLDER.step
ULA40040PK0,40,40,P,,K,PLACEHOLDER.step
ULA40015ASD0,40,15,A,S,D,PLACEHOLDER.step
ULA40015ASK0,40,15,A,S,K,PLACEHOLDER.step
ULA40015AD0,40,15,A,,D,PLACEHOLDER.step
ULA40015AK0,40,15,A,,K,PLACEHOLDER.step
ULA40015GSD0,40,15,G,S,D,PLACEHOLDER.step
ULA40015GSK0,40,15,G,S,K,PLACEHOLDER.step
ULA40015GD0,40,15,G,,D,PLACEHOLDER.step
ULA40015GK0,40,15,G,,K,PLACEHOLDER.step
ULA40015NSD0,40,15,N,S,D,PLACEHOLDER.step
ULA40015NSK0,40,15,N,S,K,PLACEHOLDER.step
ULA40015ND0,40,15,N,,D,PLACEHOLDER.step
ULA40015NK0,40,15,N,,K,PLACEHOLDER.step
ULA40015PSD0,40,15,P,S,D,PLACEHOLDER.step
ULA40015PSK0,40,15,P,S,K,PLACEHOLDER.step
ULA40015PD0,40,15,P,,D,PLACEHOLDER.step
ULA40015PK0,40,15,P,,K,PLACEHOLDER.step
ULA40025ASD0,40,25,A,S,D,PLACEHOLDER.step
ULA40025ASK0,40,25,A,S,K,PLACEHOLDER.step
ULA40025AD0,40,25,A,,D,PLACEHOLDER.step
ULA40025AK0,40,25,A,,K,PLACEHOLDER.step
ULA40025GSD0,40,25,G,S,D,PLACEHOLDER.step
ULA40025GSK0,40,25,G,S,K,PLACEHOLDER.step
ULA40025GD0,40,25,G,,D,PLACEHOLDER.step
ULA40025GK0,40,25,G,,K,PLACEHOLDER.step
ULA40025NSD0,40,25,N,S,D,PLACEHOLDER.step
ULA40025NSK0,40,25,N,S,K,PLACEHOLDER.step
ULA40025ND0,40,25,N,,D,PLACEHOLDER.step
ULA40025NK0,40,25,N,,K,PLACEHOLDER.step
ULA40025PSD0,40,25,P,S,D,PLACEHOLDER.step
ULA40025PSK0,40,25,P,S,K,PLACEHOLDER.step
ULA40025PD0,40,25,P,,D,PLACEHOLDER.step
ULA40025PK0,40,25,P,,K,PLACEHOLDER.step
ULA40050ASD0,40,50,A,S,D,PLACEHOLDER.step
ULA40050ASK0,40,50,A,S,K,PLACEHOLDER.step
ULA40050AD0,40,50,A,,D,PLACEHOLDER.step
ULA40050AK0,40,50,A,,K,PLACEHOLDER.step
ULA40050GSD0,40,50,G,S,D,PLACEHOLDER.step
ULA40050GSK0,40,50,G,S,K,PLACEHOLDER.step
ULA40050GD0,40,50,G,,D,PLACEHOLDER.step
ULA40050GK0,40,50,G,,K,PLACEHOLDER.step
ULA40050NSD0,40,50,N,S,D,PLACEHOLDER.step
ULA40050NSK0,40,50,N,S,K,PLACEHOLDER.step
ULA40050ND0,40,50,N,,D,PLACEHOLDER.step
ULA40050NK0,40,50,N,,K,PLACEHOLDER.step
ULA40050PSD0,40,50,P,S,D,PLACEHOLDER.step
ULA40050PSK0,40,50,P,S,K,PLACEHOLDER.step
ULA40050PD0,40,50,P,,D,PLACEHOLDER.step
ULA40050PK0,40,50,P,,K,PLACEHOLDER.step
ULA40060ASD0,40,60,A,S,D,PLACEHOLDER.step
ULA40060ASK0,40,60,A,S,K,PLACEHOLDER.step
ULA40060AD0,40,60,A,,D,PLACEHOLDER.step
ULA40060AK0,40,60,A,,K,PLACEHOLDER.step
ULA40060GSD0,40,60,G,S,D,PLACEHOLDER.step
ULA40060GSK0,40,60,G,S,K,PLACEHOLDER.step
ULA40060GD0,40,60,G,,D,PLACEHOLDER.step
ULA40060GK0,40,60,G,,K,PLACEHOLDER.step
ULA40060NSD0,40,60,N,S,D,PLACEHOLDER.step
ULA40060NSK0,40,60,N,S,K,PLACEHOLDER.step
ULA40060ND0,40,60,N,,D,PLACEHOLDER.step
ULA40060NK0,40,60,N,,K,PLACEHOLDER.step
ULA40060PSD0,40,60,P,S,D,PLACEHOLDER.step
ULA40060PSK0,40,60,P,S,K,PLACEHOLDER.step
ULA40060PD0,40,60,P,,D,PLACEHOLDER.step
ULA40060PK0,40,60,P,,K,PLACEHOLDER.step
ULA63020ASD0,63,20,A,S,D,PLACEHOLDER.step
ULA63020ASK0,63,20,A,S,K,PLACEHOLDER.step
ULA63020AD0,63,20,A,,D,PLACEHOLDER.step
ULA63020AK0,63,20,A,,K,PLACEHOLDER.step
ULA63020GSD0,63,20,G,S,D,PLACEHOLDER.step
ULA63020GSK0,63,20,G,S,K,PLACEHOLDER.step
ULA63020GD0,63,20,G,,D,PLACEHOLDER.step
ULA63020GK0,63,20,G,,K,PLACEHOLDER.step
ULA63020NSD0,63,20,N,S,D,PLACEHOLDER.step
ULA63020NSK0,63,20,N,S,K,PLACEHOLDER.step
ULA63020ND0,63,20,N,,D,PLACEHOLDER.step
ULA63020NK0,63,20,N,,K,PLACEHOLDER.step
ULA63020PSD0,63,20,P,S,D,PLACEHOLDER.step
ULA63020PSK0,63,20,P,S,K,PLACEHOLDER.step
ULA63020PD0,63,20,P,,D,PLACEHOLDER.step
ULA63020PK0,63,20,P,,K,PLACEHOLDER.step
ULA63040ASD0,63,40,A,S,D,PLACEHOLDER.step
ULA63040ASK0,63,40,A,S,K,PLACEHOLDER.step
ULA63040GSD0,63,40,G,S,D,PLACEHOLDER.step
ULA63040GSK0,63,40,G,S,K,PLACEHOLDER.step
ULA63040GD0,63,40,G,,D,PLACEHOLDER.step
ULA63040GK0,63,40,G,,K,PLACEHOLDER.step
ULA63040NSD0,63,40,N,S,D,PLACEHOLDER.step
ULA63040NSK0,63,40,N,S,K,PLACEHOLDER.step
ULA63040ND0,63,40,N,,D,PLACEHOLDER.step
ULA63040NK0,63,40,N,,K,PLACEHOLDER.step
ULA63040PSD0,63,40,P,S,D,PLACEHOLDER.step
ULA63040PSK0,63,40,P,S,K,PLACEHOLDER.step
ULA63040PD0,63,40,P,,D,PLACEHOLDER.step
ULA63040PK0,63,40,P,,K,PLACEHOLDER.step
ULA63015ASD0,63,15,A,S,D,PLACEHOLDER.step
ULA63015ASK0,63,15,A,S,K,PLACEHOLDER.step
ULA63015AD0,63,15,A,,D,PLACEHOLDER.step
ULA63015GSD0,63,15,G,S,D,PLACEHOLDER.step
ULA63015GSK0,63,15,G,S,K,PLACEHOLDER.step
ULA63015GD0,63,15,G,,D,PLACEHOLDER.step
ULA63015GK0,63,15,G,,K,PLACEHOLDER.step
ULA63015NSD0,63,15,N,S,D,PLACEHOLDER.step
ULA63015NSK0,63,15,N,S,K,PLACEHOLDER.step
ULA63015ND0,63,15,N,,D,PLACEHOLDER.step
ULA63015NK0,63,15,N,,K,PLACEHOLDER.step
ULA63015PSD0,63,15,P,S,D,PLACEHOLDER.step
ULA63015PSK0,63,15,P,S,K,PLACEHOLDER.step
ULA63015PD0,63,15,P,,D,PLACEHOLDER.step
ULA63015PK0,63,15,P,,K,PLACEHOLDER.step
ULA63025ASD0,63,25,A,S,D,PLACEHOLDER.step
ULA63025ASK0,63,25,A,S,K,PLACEHOLDER.step
ULA63025AD0,63,25,A,,D,PLACEHOLDER.step
ULA63025GSD0,63,25,G,S,D,PLACEHOLDER.step
ULA63025GSK0,63,25,G,S,K,PLACEHOLDER.step
ULA63025GD0,63,25,G,,D,PLACEHOLDER.step
ULA63025GK0,63,25,G,,K,PLACEHOLDER.step
ULA63025NSD0,63,25,N,S,D,PLACEHOLDER.step
ULA63025NSK0,63,25,N,S,K,PLACEHOLDER.step
ULA63025ND0,63,25,N,,D,PLACEHOLDER.step
ULA63025NK0,63,25,N,,K,PLACEHOLDER.step
ULA63025PSD0,63,25,P,S,D,PLACEHOLDER.step
ULA63025PSK0,63,25,P,S,K,PLACEHOLDER.step
ULA63025PD0,63,25,P,,D,PLACEHOLDER.step
ULA63025PK0,63,25,P,,K,PLACEHOLDER.step
ULA63050ASD0,63,50,A,S,D,PLACEHOLDER.step
ULA63050ASK0,63,50,A,S,K,PLACEHOLDER.step
ULA63050AD0,63,50,A,,D,PLACEHOLDER.step
ULA63050GSD0,63,50,G,S,D,PLACEHOLDER.step
ULA63050GSK0,63,50,G,S,K,PLACEHOLDER.step
ULA63050GD0,63,50,G,,D,PLACEHOLDER.step
ULA63050GK0,63,50,G,,K,PLACEHOLDER.step
ULA63050NSD0,63,50,N,S,D,PLACEHOLDER.step
ULA63050NSK0,63,50,N,S,K,PLACEHOLDER.step
ULA63050ND0,63,50,N,,D,PLACEHOLDER.step
ULA63050NK0,63,50,N,,K,PLACEHOLDER.step
ULA63050PSD0,63,50,P,S,D,PLACEHOLDER.step
ULA63050PSK0,63,50,P,S,K,PLACEHOLDER.step
ULA63050PD0,63,50,P,,D,PLACEHOLDER.step
ULA63050PK0,63,50,P,,K,PLACEHOLDER.step
ULA63060ASD0,63,60,A,S,D,PLACEHOLDER.step
ULA63060ASK0,63,60,A,S,K,PLACEHOLDER.step
ULA63060AD0,63,60,A,,D,PLACEHOLDER.step
ULA63060GSD0,63,60,G,S,D,PLACEHOLDER.step
ULA63060GSK0,63,60,G,S,K,PLACEHOLDER.step
ULA63060GD0,63,60,G,,D,PLACEHOLDER.step
ULA63060GK0,63,60,G,,K,PLACEHOLDER.step
ULA63060NSD0,63,60,N,S,D,PLACEHOLDER.step
ULA63060NSK0,63,60,N,S,K,PLACEHOLDER.step
ULA63060ND0,63,60,N,,D,PLACEHOLDER.step
ULA63060NK0,63,60,N,,K,PLACEHOLDER.step
ULA63060PSD0,63,60,P,S,D,PLACEHOLDER.step
ULA63060PSK0,63,60,P,S,K,PLACEHOLDER.step
ULA63060PD0,63,60,P,,D,PLACEHOLDER.step
ULA63060PK0,63,60,P,,K,PLACEHOLDER.step
1 model_code size stroke rod mount sensor step_file
18 ULA63040AK0 63 40 A K ULA63040AK0.step
19 ULA63050AK0 63 50 A K ULA63050AK0.step
20 ULA63060AK0 63 60 A K ULA63060AK0.step
21 ULA40020AD0 40 20 A D PLACEHOLDER.step
22 ULA40020AK0 40 20 A K PLACEHOLDER.step
23 ULA40020GSD0 40 20 G S D PLACEHOLDER.step
24 ULA40020GD0 40 20 G D PLACEHOLDER.step
25 ULA40020GK0 40 20 G K PLACEHOLDER.step
26 ULA40020NSD0 40 20 N S D PLACEHOLDER.step
27 ULA40020ND0 40 20 N D PLACEHOLDER.step
28 ULA40020NK0 40 20 N K PLACEHOLDER.step
29 ULA40020PSD0 40 20 P S D PLACEHOLDER.step
30 ULA40020PD0 40 20 P D PLACEHOLDER.step
31 ULA40020PK0 40 20 P K PLACEHOLDER.step
32 ULA40040AD0 40 40 A D PLACEHOLDER.step
33 ULA40040AK0 40 40 A K PLACEHOLDER.step
34 ULA40040GD0 40 40 G D PLACEHOLDER.step
35 ULA40040GK0 40 40 G K PLACEHOLDER.step
36 ULA40040ND0 40 40 N D PLACEHOLDER.step
37 ULA40040NK0 40 40 N K PLACEHOLDER.step
38 ULA40040PD0 40 40 P D PLACEHOLDER.step
39 ULA40040PK0 40 40 P K PLACEHOLDER.step
40 ULA40015ASD0 40 15 A S D PLACEHOLDER.step
41 ULA40015ASK0 40 15 A S K PLACEHOLDER.step
42 ULA40015AD0 40 15 A D PLACEHOLDER.step
43 ULA40015AK0 40 15 A K PLACEHOLDER.step
44 ULA40015GSD0 40 15 G S D PLACEHOLDER.step
45 ULA40015GSK0 40 15 G S K PLACEHOLDER.step
46 ULA40015GD0 40 15 G D PLACEHOLDER.step
47 ULA40015GK0 40 15 G K PLACEHOLDER.step
48 ULA40015NSD0 40 15 N S D PLACEHOLDER.step
49 ULA40015NSK0 40 15 N S K PLACEHOLDER.step
50 ULA40015ND0 40 15 N D PLACEHOLDER.step
51 ULA40015NK0 40 15 N K PLACEHOLDER.step
52 ULA40015PSD0 40 15 P S D PLACEHOLDER.step
53 ULA40015PSK0 40 15 P S K PLACEHOLDER.step
54 ULA40015PD0 40 15 P D PLACEHOLDER.step
55 ULA40015PK0 40 15 P K PLACEHOLDER.step
56 ULA40025ASD0 40 25 A S D PLACEHOLDER.step
57 ULA40025ASK0 40 25 A S K PLACEHOLDER.step
58 ULA40025AD0 40 25 A D PLACEHOLDER.step
59 ULA40025AK0 40 25 A K PLACEHOLDER.step
60 ULA40025GSD0 40 25 G S D PLACEHOLDER.step
61 ULA40025GSK0 40 25 G S K PLACEHOLDER.step
62 ULA40025GD0 40 25 G D PLACEHOLDER.step
63 ULA40025GK0 40 25 G K PLACEHOLDER.step
64 ULA40025NSD0 40 25 N S D PLACEHOLDER.step
65 ULA40025NSK0 40 25 N S K PLACEHOLDER.step
66 ULA40025ND0 40 25 N D PLACEHOLDER.step
67 ULA40025NK0 40 25 N K PLACEHOLDER.step
68 ULA40025PSD0 40 25 P S D PLACEHOLDER.step
69 ULA40025PSK0 40 25 P S K PLACEHOLDER.step
70 ULA40025PD0 40 25 P D PLACEHOLDER.step
71 ULA40025PK0 40 25 P K PLACEHOLDER.step
72 ULA40050ASD0 40 50 A S D PLACEHOLDER.step
73 ULA40050ASK0 40 50 A S K PLACEHOLDER.step
74 ULA40050AD0 40 50 A D PLACEHOLDER.step
75 ULA40050AK0 40 50 A K PLACEHOLDER.step
76 ULA40050GSD0 40 50 G S D PLACEHOLDER.step
77 ULA40050GSK0 40 50 G S K PLACEHOLDER.step
78 ULA40050GD0 40 50 G D PLACEHOLDER.step
79 ULA40050GK0 40 50 G K PLACEHOLDER.step
80 ULA40050NSD0 40 50 N S D PLACEHOLDER.step
81 ULA40050NSK0 40 50 N S K PLACEHOLDER.step
82 ULA40050ND0 40 50 N D PLACEHOLDER.step
83 ULA40050NK0 40 50 N K PLACEHOLDER.step
84 ULA40050PSD0 40 50 P S D PLACEHOLDER.step
85 ULA40050PSK0 40 50 P S K PLACEHOLDER.step
86 ULA40050PD0 40 50 P D PLACEHOLDER.step
87 ULA40050PK0 40 50 P K PLACEHOLDER.step
88 ULA40060ASD0 40 60 A S D PLACEHOLDER.step
89 ULA40060ASK0 40 60 A S K PLACEHOLDER.step
90 ULA40060AD0 40 60 A D PLACEHOLDER.step
91 ULA40060AK0 40 60 A K PLACEHOLDER.step
92 ULA40060GSD0 40 60 G S D PLACEHOLDER.step
93 ULA40060GSK0 40 60 G S K PLACEHOLDER.step
94 ULA40060GD0 40 60 G D PLACEHOLDER.step
95 ULA40060GK0 40 60 G K PLACEHOLDER.step
96 ULA40060NSD0 40 60 N S D PLACEHOLDER.step
97 ULA40060NSK0 40 60 N S K PLACEHOLDER.step
98 ULA40060ND0 40 60 N D PLACEHOLDER.step
99 ULA40060NK0 40 60 N K PLACEHOLDER.step
100 ULA40060PSD0 40 60 P S D PLACEHOLDER.step
101 ULA40060PSK0 40 60 P S K PLACEHOLDER.step
102 ULA40060PD0 40 60 P D PLACEHOLDER.step
103 ULA40060PK0 40 60 P K PLACEHOLDER.step
104 ULA63020ASD0 63 20 A S D PLACEHOLDER.step
105 ULA63020ASK0 63 20 A S K PLACEHOLDER.step
106 ULA63020AD0 63 20 A D PLACEHOLDER.step
107 ULA63020AK0 63 20 A K PLACEHOLDER.step
108 ULA63020GSD0 63 20 G S D PLACEHOLDER.step
109 ULA63020GSK0 63 20 G S K PLACEHOLDER.step
110 ULA63020GD0 63 20 G D PLACEHOLDER.step
111 ULA63020GK0 63 20 G K PLACEHOLDER.step
112 ULA63020NSD0 63 20 N S D PLACEHOLDER.step
113 ULA63020NSK0 63 20 N S K PLACEHOLDER.step
114 ULA63020ND0 63 20 N D PLACEHOLDER.step
115 ULA63020NK0 63 20 N K PLACEHOLDER.step
116 ULA63020PSD0 63 20 P S D PLACEHOLDER.step
117 ULA63020PSK0 63 20 P S K PLACEHOLDER.step
118 ULA63020PD0 63 20 P D PLACEHOLDER.step
119 ULA63020PK0 63 20 P K PLACEHOLDER.step
120 ULA63040ASD0 63 40 A S D PLACEHOLDER.step
121 ULA63040ASK0 63 40 A S K PLACEHOLDER.step
122 ULA63040GSD0 63 40 G S D PLACEHOLDER.step
123 ULA63040GSK0 63 40 G S K PLACEHOLDER.step
124 ULA63040GD0 63 40 G D PLACEHOLDER.step
125 ULA63040GK0 63 40 G K PLACEHOLDER.step
126 ULA63040NSD0 63 40 N S D PLACEHOLDER.step
127 ULA63040NSK0 63 40 N S K PLACEHOLDER.step
128 ULA63040ND0 63 40 N D PLACEHOLDER.step
129 ULA63040NK0 63 40 N K PLACEHOLDER.step
130 ULA63040PSD0 63 40 P S D PLACEHOLDER.step
131 ULA63040PSK0 63 40 P S K PLACEHOLDER.step
132 ULA63040PD0 63 40 P D PLACEHOLDER.step
133 ULA63040PK0 63 40 P K PLACEHOLDER.step
134 ULA63015ASD0 63 15 A S D PLACEHOLDER.step
135 ULA63015ASK0 63 15 A S K PLACEHOLDER.step
136 ULA63015AD0 63 15 A D PLACEHOLDER.step
137 ULA63015GSD0 63 15 G S D PLACEHOLDER.step
138 ULA63015GSK0 63 15 G S K PLACEHOLDER.step
139 ULA63015GD0 63 15 G D PLACEHOLDER.step
140 ULA63015GK0 63 15 G K PLACEHOLDER.step
141 ULA63015NSD0 63 15 N S D PLACEHOLDER.step
142 ULA63015NSK0 63 15 N S K PLACEHOLDER.step
143 ULA63015ND0 63 15 N D PLACEHOLDER.step
144 ULA63015NK0 63 15 N K PLACEHOLDER.step
145 ULA63015PSD0 63 15 P S D PLACEHOLDER.step
146 ULA63015PSK0 63 15 P S K PLACEHOLDER.step
147 ULA63015PD0 63 15 P D PLACEHOLDER.step
148 ULA63015PK0 63 15 P K PLACEHOLDER.step
149 ULA63025ASD0 63 25 A S D PLACEHOLDER.step
150 ULA63025ASK0 63 25 A S K PLACEHOLDER.step
151 ULA63025AD0 63 25 A D PLACEHOLDER.step
152 ULA63025GSD0 63 25 G S D PLACEHOLDER.step
153 ULA63025GSK0 63 25 G S K PLACEHOLDER.step
154 ULA63025GD0 63 25 G D PLACEHOLDER.step
155 ULA63025GK0 63 25 G K PLACEHOLDER.step
156 ULA63025NSD0 63 25 N S D PLACEHOLDER.step
157 ULA63025NSK0 63 25 N S K PLACEHOLDER.step
158 ULA63025ND0 63 25 N D PLACEHOLDER.step
159 ULA63025NK0 63 25 N K PLACEHOLDER.step
160 ULA63025PSD0 63 25 P S D PLACEHOLDER.step
161 ULA63025PSK0 63 25 P S K PLACEHOLDER.step
162 ULA63025PD0 63 25 P D PLACEHOLDER.step
163 ULA63025PK0 63 25 P K PLACEHOLDER.step
164 ULA63050ASD0 63 50 A S D PLACEHOLDER.step
165 ULA63050ASK0 63 50 A S K PLACEHOLDER.step
166 ULA63050AD0 63 50 A D PLACEHOLDER.step
167 ULA63050GSD0 63 50 G S D PLACEHOLDER.step
168 ULA63050GSK0 63 50 G S K PLACEHOLDER.step
169 ULA63050GD0 63 50 G D PLACEHOLDER.step
170 ULA63050GK0 63 50 G K PLACEHOLDER.step
171 ULA63050NSD0 63 50 N S D PLACEHOLDER.step
172 ULA63050NSK0 63 50 N S K PLACEHOLDER.step
173 ULA63050ND0 63 50 N D PLACEHOLDER.step
174 ULA63050NK0 63 50 N K PLACEHOLDER.step
175 ULA63050PSD0 63 50 P S D PLACEHOLDER.step
176 ULA63050PSK0 63 50 P S K PLACEHOLDER.step
177 ULA63050PD0 63 50 P D PLACEHOLDER.step
178 ULA63050PK0 63 50 P K PLACEHOLDER.step
179 ULA63060ASD0 63 60 A S D PLACEHOLDER.step
180 ULA63060ASK0 63 60 A S K PLACEHOLDER.step
181 ULA63060AD0 63 60 A D PLACEHOLDER.step
182 ULA63060GSD0 63 60 G S D PLACEHOLDER.step
183 ULA63060GSK0 63 60 G S K PLACEHOLDER.step
184 ULA63060GD0 63 60 G D PLACEHOLDER.step
185 ULA63060GK0 63 60 G K PLACEHOLDER.step
186 ULA63060NSD0 63 60 N S D PLACEHOLDER.step
187 ULA63060NSK0 63 60 N S K PLACEHOLDER.step
188 ULA63060ND0 63 60 N D PLACEHOLDER.step
189 ULA63060NK0 63 60 N K PLACEHOLDER.step
190 ULA63060PSD0 63 60 P S D PLACEHOLDER.step
191 ULA63060PSK0 63 60 P S K PLACEHOLDER.step
192 ULA63060PD0 63 60 P D PLACEHOLDER.step
193 ULA63060PK0 63 60 P K PLACEHOLDER.step

View File

@@ -13,3 +13,29 @@ ULL50040EXK0,50,40,E,X,K,ULL50040EXK0.step
ULL50040FXK0,50,40,F,X,K,ULL50040FXK0.step
ULL50040HXK0,50,40,H,X,K,ULL50040HXK0.step
ULL50040IXK0,50,40,I,X,K,ULL50040IXK0.step
ULL50040AXK0,50,40,A,X,K,PLACEHOLDER.step
ULL50040BEK0,50,40,B,E,K,PLACEHOLDER.step
ULL50040BNK0,50,40,B,N,K,PLACEHOLDER.step
ULL50040BOK0,50,40,B,O,K,PLACEHOLDER.step
ULL50040BSK0,50,40,B,S,K,PLACEHOLDER.step
ULL50040CEK0,50,40,C,E,K,PLACEHOLDER.step
ULL50040CNK0,50,40,C,N,K,PLACEHOLDER.step
ULL50040COK0,50,40,C,O,K,PLACEHOLDER.step
ULL50040CSK0,50,40,C,S,K,PLACEHOLDER.step
ULL50040DXK0,50,40,D,X,K,PLACEHOLDER.step
ULL50040EEK0,50,40,E,E,K,PLACEHOLDER.step
ULL50040ENK0,50,40,E,N,K,PLACEHOLDER.step
ULL50040EOK0,50,40,E,O,K,PLACEHOLDER.step
ULL50040ESK0,50,40,E,S,K,PLACEHOLDER.step
ULL50040FEK0,50,40,F,E,K,PLACEHOLDER.step
ULL50040FNK0,50,40,F,N,K,PLACEHOLDER.step
ULL50040FOK0,50,40,F,O,K,PLACEHOLDER.step
ULL50040FSK0,50,40,F,S,K,PLACEHOLDER.step
ULL50040HEK0,50,40,H,E,K,PLACEHOLDER.step
ULL50040HNK0,50,40,H,N,K,PLACEHOLDER.step
ULL50040HOK0,50,40,H,O,K,PLACEHOLDER.step
ULL50040HSK0,50,40,H,S,K,PLACEHOLDER.step
ULL50040IEK0,50,40,I,E,K,PLACEHOLDER.step
ULL50040INK0,50,40,I,N,K,PLACEHOLDER.step
ULL50040IOK0,50,40,I,O,K,PLACEHOLDER.step
ULL50040ISK0,50,40,I,S,K,PLACEHOLDER.step
1 model_code size stroke rod orient sensor step_file
13 ULL50040FXK0 50 40 F X K ULL50040FXK0.step
14 ULL50040HXK0 50 40 H X K ULL50040HXK0.step
15 ULL50040IXK0 50 40 I X K ULL50040IXK0.step
16 ULL50040AXK0 50 40 A X K PLACEHOLDER.step
17 ULL50040BEK0 50 40 B E K PLACEHOLDER.step
18 ULL50040BNK0 50 40 B N K PLACEHOLDER.step
19 ULL50040BOK0 50 40 B O K PLACEHOLDER.step
20 ULL50040BSK0 50 40 B S K PLACEHOLDER.step
21 ULL50040CEK0 50 40 C E K PLACEHOLDER.step
22 ULL50040CNK0 50 40 C N K PLACEHOLDER.step
23 ULL50040COK0 50 40 C O K PLACEHOLDER.step
24 ULL50040CSK0 50 40 C S K PLACEHOLDER.step
25 ULL50040DXK0 50 40 D X K PLACEHOLDER.step
26 ULL50040EEK0 50 40 E E K PLACEHOLDER.step
27 ULL50040ENK0 50 40 E N K PLACEHOLDER.step
28 ULL50040EOK0 50 40 E O K PLACEHOLDER.step
29 ULL50040ESK0 50 40 E S K PLACEHOLDER.step
30 ULL50040FEK0 50 40 F E K PLACEHOLDER.step
31 ULL50040FNK0 50 40 F N K PLACEHOLDER.step
32 ULL50040FOK0 50 40 F O K PLACEHOLDER.step
33 ULL50040FSK0 50 40 F S K PLACEHOLDER.step
34 ULL50040HEK0 50 40 H E K PLACEHOLDER.step
35 ULL50040HNK0 50 40 H N K PLACEHOLDER.step
36 ULL50040HOK0 50 40 H O K PLACEHOLDER.step
37 ULL50040HSK0 50 40 H S K PLACEHOLDER.step
38 ULL50040IEK0 50 40 I E K PLACEHOLDER.step
39 ULL50040INK0 50 40 I N K PLACEHOLDER.step
40 ULL50040IOK0 50 40 I O K PLACEHOLDER.step
41 ULL50040ISK0 50 40 I S K PLACEHOLDER.step

View File

@@ -69,3 +69,133 @@ ULM50060EXK0,50,60,E,X,K,ULM50060EXK0.step
ULM50060FXK0,50,60,F,X,K,ULM50060FXK0.step
ULM50060HXK0,50,60,H,X,K,ULM50060HXK0.step
ULM50060IXK0,50,60,I,X,K,ULM50060IXK0.step
ULM50015AXK0,50,15,A,X,K,PLACEHOLDER.step
ULM50015BEK0,50,15,B,E,K,PLACEHOLDER.step
ULM50015BNK0,50,15,B,N,K,PLACEHOLDER.step
ULM50015BOK0,50,15,B,O,K,PLACEHOLDER.step
ULM50015BSK0,50,15,B,S,K,PLACEHOLDER.step
ULM50015CEK0,50,15,C,E,K,PLACEHOLDER.step
ULM50015CNK0,50,15,C,N,K,PLACEHOLDER.step
ULM50015COK0,50,15,C,O,K,PLACEHOLDER.step
ULM50015CSK0,50,15,C,S,K,PLACEHOLDER.step
ULM50015DXK0,50,15,D,X,K,PLACEHOLDER.step
ULM50015EEK0,50,15,E,E,K,PLACEHOLDER.step
ULM50015ENK0,50,15,E,N,K,PLACEHOLDER.step
ULM50015EOK0,50,15,E,O,K,PLACEHOLDER.step
ULM50015ESK0,50,15,E,S,K,PLACEHOLDER.step
ULM50015FEK0,50,15,F,E,K,PLACEHOLDER.step
ULM50015FNK0,50,15,F,N,K,PLACEHOLDER.step
ULM50015FOK0,50,15,F,O,K,PLACEHOLDER.step
ULM50015FSK0,50,15,F,S,K,PLACEHOLDER.step
ULM50015HEK0,50,15,H,E,K,PLACEHOLDER.step
ULM50015HNK0,50,15,H,N,K,PLACEHOLDER.step
ULM50015HOK0,50,15,H,O,K,PLACEHOLDER.step
ULM50015HSK0,50,15,H,S,K,PLACEHOLDER.step
ULM50015IEK0,50,15,I,E,K,PLACEHOLDER.step
ULM50015INK0,50,15,I,N,K,PLACEHOLDER.step
ULM50015IOK0,50,15,I,O,K,PLACEHOLDER.step
ULM50015ISK0,50,15,I,S,K,PLACEHOLDER.step
ULM50025AXK0,50,25,A,X,K,PLACEHOLDER.step
ULM50025BEK0,50,25,B,E,K,PLACEHOLDER.step
ULM50025BNK0,50,25,B,N,K,PLACEHOLDER.step
ULM50025BOK0,50,25,B,O,K,PLACEHOLDER.step
ULM50025BSK0,50,25,B,S,K,PLACEHOLDER.step
ULM50025CEK0,50,25,C,E,K,PLACEHOLDER.step
ULM50025CNK0,50,25,C,N,K,PLACEHOLDER.step
ULM50025COK0,50,25,C,O,K,PLACEHOLDER.step
ULM50025CSK0,50,25,C,S,K,PLACEHOLDER.step
ULM50025DXK0,50,25,D,X,K,PLACEHOLDER.step
ULM50025EEK0,50,25,E,E,K,PLACEHOLDER.step
ULM50025ENK0,50,25,E,N,K,PLACEHOLDER.step
ULM50025EOK0,50,25,E,O,K,PLACEHOLDER.step
ULM50025ESK0,50,25,E,S,K,PLACEHOLDER.step
ULM50025FEK0,50,25,F,E,K,PLACEHOLDER.step
ULM50025FNK0,50,25,F,N,K,PLACEHOLDER.step
ULM50025FOK0,50,25,F,O,K,PLACEHOLDER.step
ULM50025FSK0,50,25,F,S,K,PLACEHOLDER.step
ULM50025HEK0,50,25,H,E,K,PLACEHOLDER.step
ULM50025HNK0,50,25,H,N,K,PLACEHOLDER.step
ULM50025HOK0,50,25,H,O,K,PLACEHOLDER.step
ULM50025HSK0,50,25,H,S,K,PLACEHOLDER.step
ULM50025IEK0,50,25,I,E,K,PLACEHOLDER.step
ULM50025INK0,50,25,I,N,K,PLACEHOLDER.step
ULM50025IOK0,50,25,I,O,K,PLACEHOLDER.step
ULM50025ISK0,50,25,I,S,K,PLACEHOLDER.step
ULM50040AXK0,50,40,A,X,K,PLACEHOLDER.step
ULM50040BEK0,50,40,B,E,K,PLACEHOLDER.step
ULM50040BNK0,50,40,B,N,K,PLACEHOLDER.step
ULM50040BOK0,50,40,B,O,K,PLACEHOLDER.step
ULM50040BSK0,50,40,B,S,K,PLACEHOLDER.step
ULM50040CEK0,50,40,C,E,K,PLACEHOLDER.step
ULM50040CNK0,50,40,C,N,K,PLACEHOLDER.step
ULM50040COK0,50,40,C,O,K,PLACEHOLDER.step
ULM50040CSK0,50,40,C,S,K,PLACEHOLDER.step
ULM50040DXK0,50,40,D,X,K,PLACEHOLDER.step
ULM50040EEK0,50,40,E,E,K,PLACEHOLDER.step
ULM50040ENK0,50,40,E,N,K,PLACEHOLDER.step
ULM50040EOK0,50,40,E,O,K,PLACEHOLDER.step
ULM50040ESK0,50,40,E,S,K,PLACEHOLDER.step
ULM50040FEK0,50,40,F,E,K,PLACEHOLDER.step
ULM50040FNK0,50,40,F,N,K,PLACEHOLDER.step
ULM50040FOK0,50,40,F,O,K,PLACEHOLDER.step
ULM50040FSK0,50,40,F,S,K,PLACEHOLDER.step
ULM50040HEK0,50,40,H,E,K,PLACEHOLDER.step
ULM50040HNK0,50,40,H,N,K,PLACEHOLDER.step
ULM50040HOK0,50,40,H,O,K,PLACEHOLDER.step
ULM50040HSK0,50,40,H,S,K,PLACEHOLDER.step
ULM50040IEK0,50,40,I,E,K,PLACEHOLDER.step
ULM50040INK0,50,40,I,N,K,PLACEHOLDER.step
ULM50040IOK0,50,40,I,O,K,PLACEHOLDER.step
ULM50040ISK0,50,40,I,S,K,PLACEHOLDER.step
ULM50050AXK0,50,50,A,X,K,PLACEHOLDER.step
ULM50050BEK0,50,50,B,E,K,PLACEHOLDER.step
ULM50050BNK0,50,50,B,N,K,PLACEHOLDER.step
ULM50050BOK0,50,50,B,O,K,PLACEHOLDER.step
ULM50050BSK0,50,50,B,S,K,PLACEHOLDER.step
ULM50050CEK0,50,50,C,E,K,PLACEHOLDER.step
ULM50050CNK0,50,50,C,N,K,PLACEHOLDER.step
ULM50050COK0,50,50,C,O,K,PLACEHOLDER.step
ULM50050CSK0,50,50,C,S,K,PLACEHOLDER.step
ULM50050DXK0,50,50,D,X,K,PLACEHOLDER.step
ULM50050EEK0,50,50,E,E,K,PLACEHOLDER.step
ULM50050ENK0,50,50,E,N,K,PLACEHOLDER.step
ULM50050EOK0,50,50,E,O,K,PLACEHOLDER.step
ULM50050ESK0,50,50,E,S,K,PLACEHOLDER.step
ULM50050FEK0,50,50,F,E,K,PLACEHOLDER.step
ULM50050FNK0,50,50,F,N,K,PLACEHOLDER.step
ULM50050FOK0,50,50,F,O,K,PLACEHOLDER.step
ULM50050FSK0,50,50,F,S,K,PLACEHOLDER.step
ULM50050HEK0,50,50,H,E,K,PLACEHOLDER.step
ULM50050HNK0,50,50,H,N,K,PLACEHOLDER.step
ULM50050HOK0,50,50,H,O,K,PLACEHOLDER.step
ULM50050HSK0,50,50,H,S,K,PLACEHOLDER.step
ULM50050IEK0,50,50,I,E,K,PLACEHOLDER.step
ULM50050INK0,50,50,I,N,K,PLACEHOLDER.step
ULM50050IOK0,50,50,I,O,K,PLACEHOLDER.step
ULM50050ISK0,50,50,I,S,K,PLACEHOLDER.step
ULM50060AXK0,50,60,A,X,K,PLACEHOLDER.step
ULM50060BEK0,50,60,B,E,K,PLACEHOLDER.step
ULM50060BNK0,50,60,B,N,K,PLACEHOLDER.step
ULM50060BOK0,50,60,B,O,K,PLACEHOLDER.step
ULM50060BSK0,50,60,B,S,K,PLACEHOLDER.step
ULM50060CEK0,50,60,C,E,K,PLACEHOLDER.step
ULM50060CNK0,50,60,C,N,K,PLACEHOLDER.step
ULM50060COK0,50,60,C,O,K,PLACEHOLDER.step
ULM50060CSK0,50,60,C,S,K,PLACEHOLDER.step
ULM50060DXK0,50,60,D,X,K,PLACEHOLDER.step
ULM50060EEK0,50,60,E,E,K,PLACEHOLDER.step
ULM50060ENK0,50,60,E,N,K,PLACEHOLDER.step
ULM50060EOK0,50,60,E,O,K,PLACEHOLDER.step
ULM50060ESK0,50,60,E,S,K,PLACEHOLDER.step
ULM50060FEK0,50,60,F,E,K,PLACEHOLDER.step
ULM50060FNK0,50,60,F,N,K,PLACEHOLDER.step
ULM50060FOK0,50,60,F,O,K,PLACEHOLDER.step
ULM50060FSK0,50,60,F,S,K,PLACEHOLDER.step
ULM50060HEK0,50,60,H,E,K,PLACEHOLDER.step
ULM50060HNK0,50,60,H,N,K,PLACEHOLDER.step
ULM50060HOK0,50,60,H,O,K,PLACEHOLDER.step
ULM50060HSK0,50,60,H,S,K,PLACEHOLDER.step
ULM50060IEK0,50,60,I,E,K,PLACEHOLDER.step
ULM50060INK0,50,60,I,N,K,PLACEHOLDER.step
ULM50060IOK0,50,60,I,O,K,PLACEHOLDER.step
ULM50060ISK0,50,60,I,S,K,PLACEHOLDER.step
1 model_code size stroke rod orient sensor step_file
69 ULM50060FXK0 50 60 F X K ULM50060FXK0.step
70 ULM50060HXK0 50 60 H X K ULM50060HXK0.step
71 ULM50060IXK0 50 60 I X K ULM50060IXK0.step
72 ULM50015AXK0 50 15 A X K PLACEHOLDER.step
73 ULM50015BEK0 50 15 B E K PLACEHOLDER.step
74 ULM50015BNK0 50 15 B N K PLACEHOLDER.step
75 ULM50015BOK0 50 15 B O K PLACEHOLDER.step
76 ULM50015BSK0 50 15 B S K PLACEHOLDER.step
77 ULM50015CEK0 50 15 C E K PLACEHOLDER.step
78 ULM50015CNK0 50 15 C N K PLACEHOLDER.step
79 ULM50015COK0 50 15 C O K PLACEHOLDER.step
80 ULM50015CSK0 50 15 C S K PLACEHOLDER.step
81 ULM50015DXK0 50 15 D X K PLACEHOLDER.step
82 ULM50015EEK0 50 15 E E K PLACEHOLDER.step
83 ULM50015ENK0 50 15 E N K PLACEHOLDER.step
84 ULM50015EOK0 50 15 E O K PLACEHOLDER.step
85 ULM50015ESK0 50 15 E S K PLACEHOLDER.step
86 ULM50015FEK0 50 15 F E K PLACEHOLDER.step
87 ULM50015FNK0 50 15 F N K PLACEHOLDER.step
88 ULM50015FOK0 50 15 F O K PLACEHOLDER.step
89 ULM50015FSK0 50 15 F S K PLACEHOLDER.step
90 ULM50015HEK0 50 15 H E K PLACEHOLDER.step
91 ULM50015HNK0 50 15 H N K PLACEHOLDER.step
92 ULM50015HOK0 50 15 H O K PLACEHOLDER.step
93 ULM50015HSK0 50 15 H S K PLACEHOLDER.step
94 ULM50015IEK0 50 15 I E K PLACEHOLDER.step
95 ULM50015INK0 50 15 I N K PLACEHOLDER.step
96 ULM50015IOK0 50 15 I O K PLACEHOLDER.step
97 ULM50015ISK0 50 15 I S K PLACEHOLDER.step
98 ULM50025AXK0 50 25 A X K PLACEHOLDER.step
99 ULM50025BEK0 50 25 B E K PLACEHOLDER.step
100 ULM50025BNK0 50 25 B N K PLACEHOLDER.step
101 ULM50025BOK0 50 25 B O K PLACEHOLDER.step
102 ULM50025BSK0 50 25 B S K PLACEHOLDER.step
103 ULM50025CEK0 50 25 C E K PLACEHOLDER.step
104 ULM50025CNK0 50 25 C N K PLACEHOLDER.step
105 ULM50025COK0 50 25 C O K PLACEHOLDER.step
106 ULM50025CSK0 50 25 C S K PLACEHOLDER.step
107 ULM50025DXK0 50 25 D X K PLACEHOLDER.step
108 ULM50025EEK0 50 25 E E K PLACEHOLDER.step
109 ULM50025ENK0 50 25 E N K PLACEHOLDER.step
110 ULM50025EOK0 50 25 E O K PLACEHOLDER.step
111 ULM50025ESK0 50 25 E S K PLACEHOLDER.step
112 ULM50025FEK0 50 25 F E K PLACEHOLDER.step
113 ULM50025FNK0 50 25 F N K PLACEHOLDER.step
114 ULM50025FOK0 50 25 F O K PLACEHOLDER.step
115 ULM50025FSK0 50 25 F S K PLACEHOLDER.step
116 ULM50025HEK0 50 25 H E K PLACEHOLDER.step
117 ULM50025HNK0 50 25 H N K PLACEHOLDER.step
118 ULM50025HOK0 50 25 H O K PLACEHOLDER.step
119 ULM50025HSK0 50 25 H S K PLACEHOLDER.step
120 ULM50025IEK0 50 25 I E K PLACEHOLDER.step
121 ULM50025INK0 50 25 I N K PLACEHOLDER.step
122 ULM50025IOK0 50 25 I O K PLACEHOLDER.step
123 ULM50025ISK0 50 25 I S K PLACEHOLDER.step
124 ULM50040AXK0 50 40 A X K PLACEHOLDER.step
125 ULM50040BEK0 50 40 B E K PLACEHOLDER.step
126 ULM50040BNK0 50 40 B N K PLACEHOLDER.step
127 ULM50040BOK0 50 40 B O K PLACEHOLDER.step
128 ULM50040BSK0 50 40 B S K PLACEHOLDER.step
129 ULM50040CEK0 50 40 C E K PLACEHOLDER.step
130 ULM50040CNK0 50 40 C N K PLACEHOLDER.step
131 ULM50040COK0 50 40 C O K PLACEHOLDER.step
132 ULM50040CSK0 50 40 C S K PLACEHOLDER.step
133 ULM50040DXK0 50 40 D X K PLACEHOLDER.step
134 ULM50040EEK0 50 40 E E K PLACEHOLDER.step
135 ULM50040ENK0 50 40 E N K PLACEHOLDER.step
136 ULM50040EOK0 50 40 E O K PLACEHOLDER.step
137 ULM50040ESK0 50 40 E S K PLACEHOLDER.step
138 ULM50040FEK0 50 40 F E K PLACEHOLDER.step
139 ULM50040FNK0 50 40 F N K PLACEHOLDER.step
140 ULM50040FOK0 50 40 F O K PLACEHOLDER.step
141 ULM50040FSK0 50 40 F S K PLACEHOLDER.step
142 ULM50040HEK0 50 40 H E K PLACEHOLDER.step
143 ULM50040HNK0 50 40 H N K PLACEHOLDER.step
144 ULM50040HOK0 50 40 H O K PLACEHOLDER.step
145 ULM50040HSK0 50 40 H S K PLACEHOLDER.step
146 ULM50040IEK0 50 40 I E K PLACEHOLDER.step
147 ULM50040INK0 50 40 I N K PLACEHOLDER.step
148 ULM50040IOK0 50 40 I O K PLACEHOLDER.step
149 ULM50040ISK0 50 40 I S K PLACEHOLDER.step
150 ULM50050AXK0 50 50 A X K PLACEHOLDER.step
151 ULM50050BEK0 50 50 B E K PLACEHOLDER.step
152 ULM50050BNK0 50 50 B N K PLACEHOLDER.step
153 ULM50050BOK0 50 50 B O K PLACEHOLDER.step
154 ULM50050BSK0 50 50 B S K PLACEHOLDER.step
155 ULM50050CEK0 50 50 C E K PLACEHOLDER.step
156 ULM50050CNK0 50 50 C N K PLACEHOLDER.step
157 ULM50050COK0 50 50 C O K PLACEHOLDER.step
158 ULM50050CSK0 50 50 C S K PLACEHOLDER.step
159 ULM50050DXK0 50 50 D X K PLACEHOLDER.step
160 ULM50050EEK0 50 50 E E K PLACEHOLDER.step
161 ULM50050ENK0 50 50 E N K PLACEHOLDER.step
162 ULM50050EOK0 50 50 E O K PLACEHOLDER.step
163 ULM50050ESK0 50 50 E S K PLACEHOLDER.step
164 ULM50050FEK0 50 50 F E K PLACEHOLDER.step
165 ULM50050FNK0 50 50 F N K PLACEHOLDER.step
166 ULM50050FOK0 50 50 F O K PLACEHOLDER.step
167 ULM50050FSK0 50 50 F S K PLACEHOLDER.step
168 ULM50050HEK0 50 50 H E K PLACEHOLDER.step
169 ULM50050HNK0 50 50 H N K PLACEHOLDER.step
170 ULM50050HOK0 50 50 H O K PLACEHOLDER.step
171 ULM50050HSK0 50 50 H S K PLACEHOLDER.step
172 ULM50050IEK0 50 50 I E K PLACEHOLDER.step
173 ULM50050INK0 50 50 I N K PLACEHOLDER.step
174 ULM50050IOK0 50 50 I O K PLACEHOLDER.step
175 ULM50050ISK0 50 50 I S K PLACEHOLDER.step
176 ULM50060AXK0 50 60 A X K PLACEHOLDER.step
177 ULM50060BEK0 50 60 B E K PLACEHOLDER.step
178 ULM50060BNK0 50 60 B N K PLACEHOLDER.step
179 ULM50060BOK0 50 60 B O K PLACEHOLDER.step
180 ULM50060BSK0 50 60 B S K PLACEHOLDER.step
181 ULM50060CEK0 50 60 C E K PLACEHOLDER.step
182 ULM50060CNK0 50 60 C N K PLACEHOLDER.step
183 ULM50060COK0 50 60 C O K PLACEHOLDER.step
184 ULM50060CSK0 50 60 C S K PLACEHOLDER.step
185 ULM50060DXK0 50 60 D X K PLACEHOLDER.step
186 ULM50060EEK0 50 60 E E K PLACEHOLDER.step
187 ULM50060ENK0 50 60 E N K PLACEHOLDER.step
188 ULM50060EOK0 50 60 E O K PLACEHOLDER.step
189 ULM50060ESK0 50 60 E S K PLACEHOLDER.step
190 ULM50060FEK0 50 60 F E K PLACEHOLDER.step
191 ULM50060FNK0 50 60 F N K PLACEHOLDER.step
192 ULM50060FOK0 50 60 F O K PLACEHOLDER.step
193 ULM50060FSK0 50 60 F S K PLACEHOLDER.step
194 ULM50060HEK0 50 60 H E K PLACEHOLDER.step
195 ULM50060HNK0 50 60 H N K PLACEHOLDER.step
196 ULM50060HOK0 50 60 H O K PLACEHOLDER.step
197 ULM50060HSK0 50 60 H S K PLACEHOLDER.step
198 ULM50060IEK0 50 60 I E K PLACEHOLDER.step
199 ULM50060INK0 50 60 I N K PLACEHOLDER.step
200 ULM50060IOK0 50 60 I O K PLACEHOLDER.step
201 ULM50060ISK0 50 60 I S K PLACEHOLDER.step

File diff suppressed because it is too large Load Diff

View File

@@ -2,3 +2,4 @@
ULT50040AK0,50,40,A,K,ULT50040AK0.step
ULT50040GK0,50,40,G,K,ULT50040GK0.step
ULT50040GN0,50,40,G,N,ULT50040GN0.step
ULT50040AN0,50,40,A,N,PLACEHOLDER.step
1 model_code size stroke rod sensor step_file
2 ULT50040AK0 50 40 A K ULT50040AK0.step
3 ULT50040GK0 50 40 G K ULT50040GK0.step
4 ULT50040GN0 50 40 G N ULT50040GN0.step
5 ULT50040AN0 50 40 A N PLACEHOLDER.step

View File

@@ -9,3 +9,13 @@ ULY40040DEK0,40,40,D,E,K,ULY40040DEK0.step
ULY40040DNK0,40,40,D,N,K,ULY40040DNK0.step
ULY40040DOK0,40,40,D,O,K,ULY40040DOK0.step
ULY40040DSK0,40,40,D,S,K,ULY40040DSK0.step
ULY40040AXK0,40,40,A,X,K,PLACEHOLDER.step
ULY40040BEK0,40,40,B,E,K,PLACEHOLDER.step
ULY40040BNK0,40,40,B,N,K,PLACEHOLDER.step
ULY40040BOK0,40,40,B,O,K,PLACEHOLDER.step
ULY40040BSK0,40,40,B,S,K,PLACEHOLDER.step
ULY40040CEK0,40,40,C,E,K,PLACEHOLDER.step
ULY40040CNK0,40,40,C,N,K,PLACEHOLDER.step
ULY40040COK0,40,40,C,O,K,PLACEHOLDER.step
ULY40040CSK0,40,40,C,S,K,PLACEHOLDER.step
ULY40040DXK0,40,40,D,X,K,PLACEHOLDER.step
1 model_code size stroke rod orient sensor step_file
9 ULY40040DNK0 40 40 D N K ULY40040DNK0.step
10 ULY40040DOK0 40 40 D O K ULY40040DOK0.step
11 ULY40040DSK0 40 40 D S K ULY40040DSK0.step
12 ULY40040AXK0 40 40 A X K PLACEHOLDER.step
13 ULY40040BEK0 40 40 B E K PLACEHOLDER.step
14 ULY40040BNK0 40 40 B N K PLACEHOLDER.step
15 ULY40040BOK0 40 40 B O K PLACEHOLDER.step
16 ULY40040BSK0 40 40 B S K PLACEHOLDER.step
17 ULY40040CEK0 40 40 C E K PLACEHOLDER.step
18 ULY40040CNK0 40 40 C N K PLACEHOLDER.step
19 ULY40040COK0 40 40 C O K PLACEHOLDER.step
20 ULY40040CSK0 40 40 C S K PLACEHOLDER.step
21 ULY40040DXK0 40 40 D X K PLACEHOLDER.step

View File

@@ -9,3 +9,13 @@ ULZ40040DEK0,40,40,D,E,K,ULZ40040DEK0.step
ULZ40040DNK0,40,40,D,N,K,ULZ40040DNK0.step
ULZ40040DOK0,40,40,D,O,K,ULZ40040DOK0.step
ULZ40040DSK0,40,40,D,S,K,ULZ40040DSK0.step
ULZ40040AXK0,40,40,A,X,K,PLACEHOLDER.step
ULZ40040BEK0,40,40,B,E,K,PLACEHOLDER.step
ULZ40040BNK0,40,40,B,N,K,PLACEHOLDER.step
ULZ40040BOK0,40,40,B,O,K,PLACEHOLDER.step
ULZ40040BSK0,40,40,B,S,K,PLACEHOLDER.step
ULZ40040CEK0,40,40,C,E,K,PLACEHOLDER.step
ULZ40040CNK0,40,40,C,N,K,PLACEHOLDER.step
ULZ40040COK0,40,40,C,O,K,PLACEHOLDER.step
ULZ40040CSK0,40,40,C,S,K,PLACEHOLDER.step
ULZ40040DXK0,40,40,D,X,K,PLACEHOLDER.step
1 model_code size stroke rod orient sensor step_file
9 ULZ40040DNK0 40 40 D N K ULZ40040DNK0.step
10 ULZ40040DOK0 40 40 D O K ULZ40040DOK0.step
11 ULZ40040DSK0 40 40 D S K ULZ40040DSK0.step
12 ULZ40040AXK0 40 40 A X K PLACEHOLDER.step
13 ULZ40040BEK0 40 40 B E K PLACEHOLDER.step
14 ULZ40040BNK0 40 40 B N K PLACEHOLDER.step
15 ULZ40040BOK0 40 40 B O K PLACEHOLDER.step
16 ULZ40040BSK0 40 40 B S K PLACEHOLDER.step
17 ULZ40040CEK0 40 40 C E K PLACEHOLDER.step
18 ULZ40040CNK0 40 40 C N K PLACEHOLDER.step
19 ULZ40040COK0 40 40 C O K PLACEHOLDER.step
20 ULZ40040CSK0 40 40 C S K PLACEHOLDER.step
21 ULZ40040DXK0 40 40 D X K PLACEHOLDER.step

View File

@@ -6,3 +6,8 @@ USSL50JB51001N0,50,J,B,5,1,001,N,USSL50JB51001N0.step
USSL50JC31001N0,50,J,C,3,1,001,N,USSL50JC31001N0.step
USSL50JC31002N0,50,J,C,3,1,002,N,USSL50JC31002N0.step
USSL50JC51001N0,50,J,C,5,1,001,N,USSL50JC51001N0.step
USSL50JB41002N0,50,J,B,4,1,002,N,PLACEHOLDER.step
USSL50JB51002N0,50,J,B,5,1,002,N,PLACEHOLDER.step
USSL50JC41001N0,50,J,C,4,1,001,N,PLACEHOLDER.step
USSL50JC41002N0,50,J,C,4,1,002,N,PLACEHOLDER.step
USSL50JC51002N0,50,J,C,5,1,002,N,PLACEHOLDER.step
1 model_code size pcmodel hook_pos pin_dia locating prog sensor step_file
6 USSL50JC31001N0 50 J C 3 1 001 N USSL50JC31001N0.step
7 USSL50JC31002N0 50 J C 3 1 002 N USSL50JC31002N0.step
8 USSL50JC51001N0 50 J C 5 1 001 N USSL50JC51001N0.step
9 USSL50JB41002N0 50 J B 4 1 002 N PLACEHOLDER.step
10 USSL50JB51002N0 50 J B 5 1 002 N PLACEHOLDER.step
11 USSL50JC41001N0 50 J C 4 1 001 N PLACEHOLDER.step
12 USSL50JC41002N0 50 J C 4 1 002 N PLACEHOLDER.step
13 USSL50JC51002N0 50 J C 5 1 002 N PLACEHOLDER.step

View File

@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
"""统计各系列"参数组合 → 型号"是否一对一 (多处多 vs 一处多)。"""
import json, io, collections
CAT = r'D:/开发/OnebotCatalog/release/web_2026.08/catalog.json'
d = json.load(io.open(CAT, encoding='utf-8-sig'))
print(f'{"系列":8s} {"变体":>6s} {"组合":>6s} {"重复组":>6s} {"最大一对多":>9s} 判定')
print('-' * 58)
tot_codes = 0
bad = []
for s in d['series']:
vs = s.get('variants') or []
if not vs:
continue
params = [p['code'] for p in s['parameters']]
sig = collections.Counter(
tuple(str(v['params'].get(pc)) for pc in params) for v in vs)
dup = {k: n for k, n in sig.items() if n > 1}
mx = max(sig.values()) if sig else 0
verdict = 'OK 一对一' if not dup else f'!! {len(dup)} 组一对多'
if dup:
bad.append((s['code'], len(vs), len(dup), mx))
tot_codes += sum(n - 1 for n in dup.values())
print(f'{s["code"]:8s} {len(vs):6d} {len(sig):6d} {len(dup):6d} {mx:9d} {verdict}')
print(f'\n存在一对多的系列: {len(bad)} 个, 受影响型号 {tot_codes} 个')
if bad:
print('\n明细 (系列 / 变体 / 重复组数 / 一组最多几个型号):')
for c, n, dup, mx in sorted(bad, key=lambda x: -x[3]):
print(f' {c:8s} {n:5d} 变体 {dup:3d} 组 max={mx}')

View File

@@ -0,0 +1,225 @@
# -*- coding: utf-8 -*-
"""
补齐参数组合空间: 为"规则内合法但库中缺失"的组合生成零件号, 并指向系列级占位数模。
背景: 当前型号表由真实 STEP 文件扫描而来 —— 没有数模就没有型号, 用户选完也拿不到代号。
本脚本按规则反向生成缺失组合, 使其可选、可出号; 数模用每系列一个占位件, 后续 AI 补真实数模。
用法:
PYTHONUTF8=1 python gen-missing-combos.py --report # 只统计, 不写文件
PYTHONUTF8=1 python gen-missing-combos.py --write # 写入 CSV + meta 模板 + 占位清单
"""
import json, io, os, csv, itertools, sys, collections
CAT = r'D:/开发/OnebotCatalog/onebot-data/catalog'
REPORT = '--report' in sys.argv
WRITE = '--write' in sys.argv
# 只有"模板能 100% 对齐"的系列才补 (由 infer-sku-template.py 验证)。
# 其余系列 (旧码混用/编码结构特殊) 保持现状, 不生成可能不存在的零件号。
SAFE = {
'KC', 'KCB', 'KCS', 'KS', 'KSB', 'KSU', 'M', 'MS', 'MSC', 'MAL', 'MALC',
'ULA', 'ULL', 'ULM', 'ULO', 'ULP', 'ULT', 'ULX', 'ULY', 'ULZ', 'USL', 'USSL',
}
# 各系列的编码模板 (由 infer-sku-template.py 反推, 100% 对齐)
# {参数:补零位数}; 空值渲染为空串
TEMPLATES = {
'KC': ('KC{type}{bore}-{stroke}{magnet}{mount}', {}),
'KCB': ('KCB{bore}-{stroke}{magnet}{mount}', {}),
'KCS': ('KCS{type}{bore}-{stroke}{magnet}{mount}', {}),
'KS': ('KS{type}{bore}-{stroke}{magnet}{mount}', {}),
'KSB': ('KSB{bore}-{stroke1}x{stroke2}{magnet}{mount}', {}),
'KSU': ('KSU{type}{bore}-{stroke}{magnet}{mount}', {}),
'M': ('{action}{bore}-{stroke}{magnet}{rod}{mount}{cap}', {}),
'MS': ('{action}{bore}-{stroke}{magnet}{rod}{mount}{cap}', {}),
'MSC': ('{action}{bore}-{stroke}{magnet}{rod}{mount}{cap}', {}),
'MAL': ('{type}{bore}-{stroke}{magnet}{rod}{mount}{cap}', {}),
'MALC': ('{type}{bore}-{stroke}{magnet}{rod}{mount}{cap}', {}),
'ULA': ('ULA{size}{stroke:pad3}{rod}{mount}{sensor}0', {}),
'ULL': ('ULL{size}{stroke:pad3}{rod}{orient}{sensor}0', {}),
'ULM': ('ULM{size}{stroke:pad3}{rod}{orient}{sensor}0', {}),
'ULO': ('ULO{size}{stroke:pad3}{rod}{sensor}0', {}),
'ULP': ('ULP{size}{stroke:pad3}{rod}{orient}{sensor}0', {}),
'ULT': ('ULT{size}{stroke:pad3}{rod}{sensor}0', {}),
'ULX': ('ULX{size}{stroke:pad3}{rod}{sensor}0', {}),
'ULY': ('ULY{size}{stroke:pad3}{rod}{orient}{sensor}0', {}),
'ULZ': ('ULZ{size}{stroke:pad3}{rod}{orient}{sensor}0', {}),
'USL': ('USL{size}{pcmodel}{pin_dia}{locating}{prog}{sensor}0', {}),
'USSL': ('USSL{size}{pcmodel}{hook_pos}{pin_dia}{locating}{prog}{sensor}0', {}),
}
PLACEHOLDER_STEP = 'PLACEHOLDER.step' # 每系列共用
def S(v):
return '' if v is None else str(v)
def eq(a, b):
return S(a).lower() == S(b).lower()
def cond_match(c, prm):
if not c:
return True
val = prm.get(c['Param'])
if val is None:
return False
op, cv = c['Op'], c['Value']
if op == 'eq':
return eq(val, cv)
if op == 'ne':
return not eq(val, cv)
if op == 'in':
return isinstance(cv, list) and any(eq(val, x) for x in cv)
try:
a, b = float(S(val)), float(S(cv))
except ValueError:
return False
return {'gt': a > b, 'ge': a >= b, 'lt': a < b, 'le': a <= b}.get(op, False)
def violated(rules, prm):
for r in rules or []:
if cond_match(r.get('If'), prm) and not cond_match(r.get('Then'), prm):
return True
return False
def render(tpl, prm):
"""模板渲染: {field} / {field:padN}; 空值渲染为空串。"""
out, i = '', 0
while i < len(tpl):
if tpl[i] == '{':
j = tpl.index('}', i)
tok = tpl[i + 1:j]
name, _, pad = tok.partition(':')
name = name.strip()
v = S(prm.get(name, ''))
if pad.startswith('pad') and v != '':
try:
v = v.rjust(int(pad[3:]), '0')
except ValueError:
return None
out += v
i = j + 1
else:
out += tpl[i]
i += 1
return out
def load_meta(code):
p = os.path.join(CAT, 'series', code + '.meta.json')
return json.load(io.open(p, encoding='utf-8'))
def link_or_copy(src, dst):
"""建硬链接; 不支持则回退复制。返回 True 表示已创建。
注: 必须先建链接再考虑失败回退, 绝不能先删目标再建 (删了又建失败 = 文件丢失)。"""
if os.path.exists(dst):
return False
try:
os.link(src, dst)
return True
except OSError:
pass
try:
import shutil
shutil.copy2(src, dst)
return True
except OSError:
return False
def main():
summary = []
plan = {}
for code in sorted(SAFE):
meta = load_meta(code)
s = meta['series']
params = s['parameters']
csvp = os.path.join(CAT, 'csv', code + '.csv')
if not os.path.exists(csvp):
print(f'[{code}] 无 CSV, 跳过')
continue
rows = list(csv.DictReader(io.open(csvp, encoding='utf-8-sig')))
existing = {r['model_code'] for r in rows}
# 值域: 数值参数取 CSV 实际出现过的值 (不用 meta.values, 避免含非法/未出现值)
doms = collections.OrderedDict()
for p in params:
pc = p['code']
if p['type'] == 'range':
doms[pc] = None # 连续域不枚举
continue
vals = list(dict.fromkeys(r.get(pc, '') for r in rows))
vals = [v for v in vals if v != '' or True]
doms[pc] = vals
if any(v is None for v in doms.values()):
print(f'[{code}] 含 range 参数, 跳过 (连续域无法枚举)')
continue
tpl, _ = TEMPLATES[code]
codes = list(doms.keys())
combos = list(itertools.product(*[doms[k] for k in codes]))
added = []
for combo in combos:
prm = dict(zip(codes, combo))
if violated(s.get('rules'), prm):
continue
mc = render(tpl, prm)
if not mc or mc in existing:
continue
existing.add(mc)
added.append((mc, prm))
summary.append((code, len(rows), len(combos), len(added)))
if added:
plan[code] = (tpl, params, added, rows)
# 输出
print(f'{"系列":7s} {"现有":>7s} {"笛卡尔":>9s} {"待补":>7s}')
print('-' * 36)
tot_new = 0
for code, n, cart, add in sorted(summary, key=lambda x: -x[3]):
tot_new += add
print(f'{code:7s} {n:7d} {cart:9,d} {add:7,d}')
print(f'\n合计待补 {tot_new:,} 个型号')
if not WRITE:
print('\n(--report 模式, 未写文件)')
return
# ---------- 写入 ----------
step_dir = os.path.join(CAT, 'step')
placeholder = os.path.join(step_dir, PLACEHOLDER_STEP)
manifest = [] # 待 AI 补真实数模的清单
n_csv = n_ph = 0
for code, (tpl, params, added, rows) in plan.items():
csvp = os.path.join(CAT, 'csv', code + '.csv')
header = list(rows[0].keys())
dims = [h for h in header if h not in ('model_code', 'step_file')]
# 新增行: step_file 统一指向 PLACEHOLDER.step ——
# 构建器按 v.step 的路径复制 (同路径只存一份), 3157 个型号共用一个占位件,
# 既不占磁盘也不撑大数据包。AI 补真实数模后把该行改成 "<型号>.step" 即可。
with io.open(csvp, 'a', encoding='utf-8-sig', newline='') as fp:
w = csv.writer(fp)
for mc, prm in added:
w.writerow([mc] + [S(prm.get(d, '')) for d in dims] + [PLACEHOLDER_STEP])
manifest.append(f'{code},{mc},{",".join(d + "=" + S(prm.get(d, "")) for d in dims)}')
n_csv += 1
print(f'[{code}] CSV +{len(added)}')
# 待补清单 (给 AI 补数模用)
mf = os.path.join(CAT, '_pending_models.csv')
with io.open(mf, 'w', encoding='utf-8-sig', newline='') as fp:
fp.write('series,model_code,params\n')
fp.write('\n'.join(manifest) + '\n')
print(f'\n写入完成: CSV 新增 {n_csv} 行, 占位 STEP {n_ph} 个')
print(f'待补数模清单: {mf} ({len(manifest)} 个型号)')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,135 @@
# -*- coding: utf-8 -*-
"""
生成占位数模 (每系列一个通用的"待补数模"方块)。
用途: 为「参数组合补齐」生成的型号提供占位 STEP —— 构建器要求每个型号必须有数模文件。
真实数模后续由 AI 参照同系列已有型号补出, 覆盖同名文件即可。
产出:
catalog/step/PLACEHOLDER.step 通用的方块 (ISO-10303-21, 标准 B-rep)
catalog/step/_placeholder/<系列>.step 每系列一份 (便于按系列替换/识别)
方块边长 60mm, 居中于原点。
用法: PYTHONUTF8=1 python gen-placeholder-step.py
"""
import os
CAT = r'D:/开发/OnebotCatalog/onebot-data/catalog'
STEP_DIR = os.path.join(CAT, 'step')
# 方块尺寸 (mm)
SX, SY, SZ = 60.0, 60.0, 60.0
HX, HY, HZ = SX / 2, SY / 2, SZ / 2
def build_step(label):
"""生成一个合法的 STEP 文件内容: 单位立方体 B-rep。"""
L = []
L.append('ISO-10303-21;')
L.append('HEADER;')
L.append("FILE_DESCRIPTION(('PLACEHOLDER - real CAD model pending'),'2;1');")
L.append("FILE_NAME('%s','2026-01-01T00:00:00',('ONEBOT'),('ONEBOT'),"
"'gen-placeholder-step.py','ONEBOT Catalog','');" % label)
L.append("FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));")
L.append('ENDSEC;')
L.append('DATA;')
L.append("#1=APPLICATION_CONTEXT('core data for automotive mechanical design processes');")
L.append("#2=APPLICATION_PROTOCOL_DEFINITION('international standard','automotive_design',2000,#1);")
L.append("#3=PRODUCT_CONTEXT('',#1,'mechanical');")
L.append("#4=PRODUCT_DEFINITION_CONTEXT('part definition',#1,'design');")
L.append("#5=PRODUCT('%s','PLACEHOLDER',$,(#3));" % label)
L.append("#6=PRODUCT_DEFINITION_FORMATION('','',#5);")
L.append("#7=PRODUCT_DEFINITION('design','',#6,#4);")
L.append("#8=PRODUCT_DEFINITION_SHAPE('','',#7);")
L.append("#9=(LENGTH_UNIT()NAMED_UNIT(*)SI_UNIT(.MILLI.,.METRE.));")
L.append("#10=(NAMED_UNIT(*)PLANE_ANGLE_UNIT()SI_UNIT($,.RADIAN.));")
L.append("#11=(NAMED_UNIT(*)SI_UNIT($,.STERADIAN.)SOLID_ANGLE_UNIT());")
L.append("#12=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(0.001),#9,"
"'distance_accuracy_value','');")
L.append("#13=(GEOMETRIC_REPRESENTATION_CONTEXT(3)"
"GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#12))"
"GLOBAL_UNIT_ASSIGNED_CONTEXT((#9,#10,#11))REPRESENTATION_CONTEXT('',''));")
# 8 个顶点坐标
corners = [
(-HX, -HY, -HZ), (HX, -HY, -HZ), (HX, HY, -HZ), (-HX, HY, -HZ),
(-HX, -HY, HZ), (HX, -HY, HZ), (HX, HY, HZ), (-HX, HY, HZ),
]
eid = 20
pt_ids, vx_ids = [], []
for (x, y, z) in corners:
L.append('#%d=CARTESIAN_POINT('',(%.3f,%.3f,%.3f));' % (eid, x, y, z))
pt_ids.append(eid); eid += 1
L.append('#%d=DIRECTION('',(0.,0.,1.));' % eid); dz = eid; eid += 1
L.append('#%d=DIRECTION('',(1.,0.,0.));' % eid); dx = eid; eid += 1
for pid in pt_ids:
L.append('#%d=VERTEX_POINT('',#%d);' % (eid, pid))
vx_ids.append(eid); eid += 1
# 12 条棱
edges = [(0, 1), (1, 2), (2, 3), (3, 0),
(4, 5), (5, 6), (6, 7), (7, 4),
(0, 4), (1, 5), (2, 6), (3, 7)]
edge_ids = []
for (a, b) in edges:
L.append('#%d=LINE('',#%d,#%d);' % (eid, pt_ids[a], pt_ids[b]))
ln = eid; eid += 1
L.append('#%d=VECTOR('',#%d,1.0);' % (eid, dx)); vc = eid; eid += 1
L.append('#%d=EDGE_CURVE('',#%d,#%d,#%d,.T.);'
% (eid, vx_ids[a], vx_ids[b], ln))
ec = eid; eid += 1
L.append('#%d=ORIENTED_EDGE('',*,*,#%d,.T.);' % (eid, ec))
edge_ids.append(eid); eid += 1
# 6 个面
faces = [
[0, 1, 2, 3], [4, 5, 6, 7],
[0, 1, 5, 4], [1, 2, 6, 5],
[2, 3, 7, 6], [3, 0, 4, 7],
]
normals = [(0, 0, -1), (0, 0, 1), (0, -1, 0), (1, 0, 0), (0, 1, 0), (-1, 0, 0)]
face_ids = []
for fi, f in enumerate(faces):
loops = []
for k in range(4):
loops.append(edge_ids[f[k]])
L.append('#%d=EDGE_LOOP('',(%s));'
% (eid, ','.join('#%d' % x for x in loops)))
el = eid; eid += 1
nx, ny, nz = normals[fi]
L.append('#%d=DIRECTION('',(%.1f,%.1f,%.1f));' % (eid, nx, ny, nz))
nd = eid; eid += 1
L.append("#%d=AXIS2_PLACEMENT_3D('',#%d,#%d,$);" % (eid, pt_ids[f[0]], nd))
ax = eid; eid += 1
L.append('#%d=PLANE(\'\',#%d);' % (eid, ax))
pl = eid; eid += 1
L.append('#%d=ADVANCED_FACE(\'\',(#%d),#%d,.T.);' % (eid, el, pl))
face_ids.append(eid); eid += 1
L.append('#%d=CLOSED_SHELL(\'\',(%s));'
% (eid, ','.join('#%d' % x for x in face_ids)))
sh = eid; eid += 1
L.append("#%d=MANIFOLD_SOLID_BREP('PLACEHOLDER',#%d);" % (eid, sh))
msb = eid; eid += 1
L.append('#%d=ADVANCED_BREP_SHAPE_REPRESENTATION(\'PLACEHOLDER\',(#%d,#%d),#13);'
% (eid, msb, eid + 1))
abr = eid; eid += 1
L.append("#%d=AXIS2_PLACEMENT_3D('',#%d,#%d,#%d);" % (eid, pt_ids[0], dz, dx))
ax2 = eid; eid += 1
L.append("#%d=SHAPE_DEFINITION_REPRESENTATION(#8,#%d);" % (eid, abr))
L.append('ENDSEC;')
L.append('END-ISO-10303-21;')
return '\n'.join(L) + '\n'
def main():
os.makedirs(STEP_DIR, exist_ok=True)
content = build_step('PLACEHOLDER')
p = os.path.join(STEP_DIR, 'PLACEHOLDER.step')
with open(p, 'w', encoding='ascii', newline='\r\n') as f:
f.write(content)
print(f'写入 {p} ({os.path.getsize(p)} 字节)')
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
"""
编码模板反推 v2 —— 支持零填充与空值占位符。
v1 的 explain() 贪心匹配"非零填充", 把 ULP50040AEK0 的 "040" 拆成 字面量"0" + 值"40",
产生大量假阴性。真实规则是:
- 数值参数常带前导零 (stroke=40 -> "040")
- 枚举参数为空时, 编码里用 '_' 占位 (UAGP075_K045WS0)
本脚本对每个参数尝试 {原值, 各前导零位数, '_'占位} 的所有组合, 取能对齐的;
若某系列的"字面量序列"完全一致 → 可模板化。
用法: PYTHONUTF8=1 python infer-sku-template.py
"""
import json, io, collections, sys
CAT = r'D:/开发/OnebotCatalog/release/web_2026.08/catalog.json'
d = json.load(io.open(CAT, encoding='utf-8-sig'))
MAXPAD = 4
def align_all(code, params, values):
"""返回全部可能的字面量序列 (元组)。每个参数尝试: 原值 / 前导零 / '_'占位。"""
results = []
def dfs(i, pos, lits):
if i == len(params):
results.append(tuple(lits + [code[pos:]]))
return
pc = params[i]
v = str(values.get(pc, ''))
cands = []
if v == '':
cands = ['_', ''] # 空值: 占位符或什么都不写
else:
cands = [v] + ['0' * z + v for z in range(1, MAXPAD + 1)]
seen = set()
for pat in cands:
if pat in seen:
continue
seen.add(pat)
if pat == '':
dfs(i + 1, pos, lits + [''])
continue
idx = code.find(pat, pos)
if idx >= 0:
dfs(i + 1, idx + len(pat), lits + [code[pos:idx]])
dfs(0, 0, [])
return results
def render_template(params, lits):
"""字面量序列 -> 模板串。空值参数渲染为 '_'。"""
out = ''
for i, pc in enumerate(params):
out += lits[i] + '{' + pc + '}'
out += lits[len(params)]
return out
good, bad = [], []
for s in d['series']:
vs = s.get('variants') or []
if not vs:
continue
params = [p['code'] for p in s['parameters']]
lit_sets = []
unaligned = 0
for v in vs:
alts = align_all(v['modelCode'], params, v['params'])
if not alts:
unaligned += 1
continue
lit_sets.append(alts)
if not lit_sets:
bad.append((s['code'], len(vs), '完全无法对齐', [], 0))
continue
# 统计"出现在最多变体里"的字面量序列 (每个变体取任一可对齐解)
cnt = collections.Counter()
for alts in lit_sets:
for a in set(alts):
cnt[a] += 1
top, hit = cnt.most_common(1)[0]
rate = hit / len(vs) * 100
if rate >= 99.5 and unaligned == 0:
good.append((s['code'], len(vs), rate, render_template(params, top), len(cnt)))
else:
bad.append((s['code'], len(vs), f'{rate:.1f}% ({len(cnt)} 种)', cnt.most_common(3), unaligned))
print('=' * 96)
print(f'可模板化的系列 ({len(good)} 个)')
print('=' * 96)
for code, n, rate, tpl, k in sorted(good):
print(f'{code:7s} {n:6d} 变体 {rate:6.1f}% {tpl}')
print()
print('=' * 96)
print(f'不可模板化的系列 ({len(bad)} 个)')
print('=' * 96)
for code, n, why, tops, un in sorted(bad, key=lambda x: -x[1]):
print(f'{code:7s} {n:6d} 变体 {why}' + (f' 未对齐 {un}' if un else ''))
for lits, c in tops[:3]:
print(f' {c:5d}× {lits}')
print(f'\n合计: 可模板化 {len(good)} / 共 {len(good) + len(bad)} 个系列')

View File

@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
"""
量化"参数组合缺口": 合法组合 (域 + 规则内) vs 库中已有变体, 以及补全的规模。
对每个系列:
- 现有变体数
- 笛卡尔积规模 (各参数值域相乘)
- 规则过滤后的合法组合数 (复用 C# Configurator 同语义的规则求值)
- 缺口 = 合法组合 - 已覆盖
这决定"补全组合数据"的可行性与数据量。
"""
import json, io, itertools
CAT = r'D:/开发/OnebotCatalog/release/web_2026.08/catalog.json'
d = json.load(io.open(CAT, encoding='utf-8-sig'))
def S(v):
return '' if v is None else str(v)
def eq(a, b):
return S(a).lower() == S(b).lower()
def cond_match(c, params):
"""规则条件求值 (与 app.js condMatches / C# CondMatches 同语义)。"""
if not c:
return True
val = params.get(c['Param'])
if val is None:
return False
op = c['Op']
cv = c['Value']
if op == 'eq':
return eq(val, cv)
if op == 'ne':
return not eq(val, cv)
if op == 'in':
return isinstance(cv, list) and any(eq(val, x) for x in cv)
try:
a, b = float(S(val)), float(S(cv))
except ValueError:
return False
return {'gt': a > b, 'ge': a >= b, 'lt': a < b, 'le': a <= b}.get(op, False)
def rule_violated(rules, params):
for r in rules or []:
if cond_match(r.get('If'), params) and not cond_match(r.get('Then'), params):
return True
return False
print(f'{"系列":7s} {"现有":>6s} {"笛卡尔":>9s} {"规则内":>9s} {"缺口":>9s} {"倍数":>5s}')
print('-' * 62)
tot_exist = tot_gap = 0
rows = []
for s in d['series']:
vs = s.get('variants') or []
if not vs:
continue
params = s['parameters']
doms = []
for p in params:
vals = p.get('values') or []
if not vals:
doms = None
break
doms.append(vals)
if doms is None:
rows.append((s['code'], len(vs), None, None, None))
continue
cart = 1
for vals in doms:
cart *= len(vals)
# 规则过滤 (笛卡尔过大时抽样估算)
legal = 0
if cart <= 400000:
for combo in itertools.product(*doms):
prm = {p['code']: v for p, v in zip(params, combo)}
if not rule_violated(s.get('rules'), prm):
legal += 1
else:
legal = None # 太大, 跳过精确统计
gap = (legal - len(vs)) if legal is not None else None
rows.append((s['code'], len(vs), cart, legal, gap))
if legal is not None:
tot_exist += len(vs)
tot_gap += max(0, gap)
for code, n, cart, legal, gap in sorted(rows, key=lambda x: -(x[4] or -1)):
c = f'{cart:,}' if cart else '—'
l = f'{legal:,}' if legal else '—'
g = f'{gap:,}' if gap is not None else '—'
mult = f'{legal/n:.1f}x' if legal and n else '—'
flag = ' <-- 缺口大' if gap and gap > 50000 else ''
print(f'{code:7s} {n:6d} {c:>9s} {l:>9s} {g:>9s} {mult:>5s}{flag}')
print(f'\n合计: 现有 {tot_exist:,} 变体, 缺口 {tot_gap:,} 组合')
print(f'补全后约 {tot_exist + tot_gap:,} 变体 (是现在的 {(tot_exist+tot_gap)/tot_exist:.1f} 倍)')

View File

@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
"""
验证: 用模板对【已有型号】重新渲染, 必须与库中编码完全一致。
凡不一致 → 模板有误, 绝不能用它生成新编码 (会造出不存在的零件号)。
"""
import json, io, os, csv, sys, importlib.util
HERE = os.path.dirname(os.path.abspath(__file__))
spec = importlib.util.spec_from_file_location('gmc', os.path.join(HERE, 'gen-missing-combos.py'))
gmc = importlib.util.module_from_spec(spec)
sys.argv = ['x'] # 防止执行 main
spec.loader.exec_module(gmc)
CAT = gmc.CAT
print(f'{"系列":7s} {"变体":>6s} {"模板一致":>9s} {"失败":>6s} 反例')
print('-' * 74)
allok = True
for code in sorted(gmc.SAFE):
if code not in gmc.TEMPLATES:
continue
tpl, _ = gmc.TEMPLATES[code]
rows = list(csv.DictReader(io.open(os.path.join(CAT, 'csv', code + '.csv'), encoding='utf-8-sig')))
ok = fail = 0
bad = []
for r in rows:
prm = {k: v for k, v in r.items() if k not in ('model_code', 'step_file')}
got = gmc.render(tpl, prm)
if got == r['model_code']:
ok += 1
else:
fail += 1
if len(bad) < 3:
bad.append(f'{r["model_code"]} -> 渲染"{got}"')
flag = '' if fail == 0 else ' <-- 模板有误!'
if fail:
allok = False
print(f'{code:7s} {len(rows):6d} {ok:9d} {fail:6d} {bad[0] if bad else ""}{flag}')
print()
print('结论:', '所有模板对已有型号 100% 复现 ✅' if allok else '存在模板错误, 不可用于生成新编码 ❌')

View File

@@ -514,12 +514,17 @@
s.parameters.forEach(function (p) {
var row = document.createElement('div');
row.className = 'param-row';
row.id = 'row-' + p.code;
var label = document.createElement('label');
label.textContent = paramName(p);
label.id = 'lbl-' + p.code;
label.textContent = paramName(p);
var picked = document.createElement('span');
picked.className = 'picked';
picked.id = 'picked-' + p.code;
label.appendChild(picked);
row.appendChild(label);
if (p.type === 'range') {
// 参数化 range 域: 输入框 + 标准档位快捷选择 (与离线版可编辑下拉一致)
// 连续域 (range) 无法枚举成卡片 → 保留输入框 + 标准档位
var inp = document.createElement('input');
inp.id = 'sel-' + p.code;
inp.className = 'param-input';
@@ -545,20 +550,16 @@
panel.appendChild(row);
return;
}
var sel = document.createElement('select');
sel.id = 'sel-' + p.code;
sel.onchange = function () {
var raw = sel.value;
if (raw === '') { delete state.sel[p.code]; }
else {
var v = raw === '__EMPTY__' ? '' : (p.type === 'number' ? num(raw) : raw);
state.sel[p.code] = v;
}
refreshOptions();
};
row.appendChild(sel);
// 枚举维度 → 选项块 (有图卡片 / 无图文字块)
var grid = document.createElement('div');
grid.className = 'opt-grid';
grid.id = 'opts-' + p.code;
// 选项较多时用更紧凑的列宽
if ((p.values || []).length > 8) grid.classList.add('wide');
row.appendChild(grid);
panel.appendChild(row);
});
renderSkuBar();
$('modelTitle').textContent = t(s.nameZh, s.nameEn);
$('btnDatasheet').classList.add('hidden');
// 重置尺寸图区: 选型完成前只显示提示文字, 不显示占位图
@@ -600,11 +601,12 @@
if (!s) return;
var allowed = allowedOptions(s, state.sel);
s.parameters.forEach(function (p) {
var sel = $('sel-' + p.code);
var vals = allowed[p.code] || [];
if (p.type === 'range') {
// 输入框: 更新 datalist 标准档位 (标注 标准); 已选值回显
var inp = $('sel-' + p.code);
var dl = $('dl-' + p.code);
if (!inp || !dl) return;
dl.innerHTML = '';
vals.forEach(function (v) {
var opt = document.createElement('option');
@@ -613,23 +615,59 @@
opt.label = isGrid ? str(v) + ' (' + t('标准', 'Std') + ')' : str(v);
dl.appendChild(opt);
});
sel.value = state.sel[p.code] !== undefined ? str(state.sel[p.code]) : '';
inp.value = state.sel[p.code] !== undefined ? str(state.sel[p.code]) : '';
return;
}
var grid = $('opts-' + p.code);
if (!grid) return;
// 已选值变得不可选时清除
if (state.sel[p.code] !== undefined &&
!vals.some(function (v) { return str(v) === str(state.sel[p.code]); })) {
delete state.sel[p.code];
}
sel.innerHTML = '<option value="">' + t('请选择', 'Select') + '</option>';
vals.forEach(function (v) {
var opt = document.createElement('option');
opt.value = optValue(v);
opt.textContent = displayValue(p, v);
if (state.sel[p.code] !== undefined && str(v) === str(state.sel[p.code])) opt.selected = true;
sel.appendChild(opt);
// 候选值: 定义域全集 (保证卡片位置稳定, 不可选的置灰而非消失)
var all = (p.values || []);
grid.innerHTML = '';
all.forEach(function (v) {
var usable = vals.some(function (x) { return str(x) === str(v); });
var on = state.sel[p.code] !== undefined && str(v) === str(state.sel[p.code]);
var tile = document.createElement('div');
tile.className = 'opt' + (on ? ' on' : '') + (usable ? '' : ' off');
var disp = displayValue(p, v);
// 有中文说明时: 代号大字 + 说明小字; 无说明 (如纯数字) 只显示值
var hasDesc = disp && disp !== str(v);
var k = document.createElement('div');
k.className = 'opt-k';
k.textContent = hasDesc ? optValue(v) .replace('__EMPTY__', '—') : disp;
tile.appendChild(k);
if (hasDesc) {
var dd = document.createElement('div');
dd.className = 'opt-d';
dd.textContent = disp;
tile.appendChild(dd);
} else {
tile.classList.add('txt');
}
if (usable) {
tile.onclick = function () {
if (state.sel[p.code] !== undefined && str(v) === str(state.sel[p.code])) {
delete state.sel[p.code]; // 再点一次取消
} else {
state.sel[p.code] = p.type === 'number' ? num(v) : (str(v) === '__EMPTY__' ? '' : v);
}
refreshOptions();
};
}
grid.appendChild(tile);
});
});
// 维度标签后面回显当前选择
s.parameters.forEach(function (p) {
var el = $('picked-' + p.code);
if (!el) return;
el.textContent = state.sel[p.code] !== undefined ? '· ' + displayValue(p, state.sel[p.code]) : '';
});
renderSkuBar();
updateSelection();
if (crossFill) crossFill(); // 平替对照随选型刷新 (只显示当前型号的平替)
}
@@ -753,6 +791,70 @@
load3D(v);
}
// ---------- 零件号实时拼装 (DESTACO 风) ----------
// 不依赖编码模板 (40 个系列里只有 KC 有模板): 用「当前候选型号的最长公共前缀」——
// 选得越多, 候选集越小, 公共前缀越长, 逐字拼出完整零件号。
// 已用 check-sku-lcp.py 验证 40/40 系列前缀单调增长 (不回退)。
function candidateCodes() {
var s = state.series;
if (!s) return [];
var keys = Object.keys(state.sel);
if (!keys.length) return (s.variants || []).map(function (v) { return v.modelCode; });
return (s.variants || []).filter(function (v) {
return keys.every(function (k) { return str(v.params[k]) === str(state.sel[k]); });
}).map(function (v) { return v.modelCode; });
}
function lcp(list) {
if (!list.length) return '';
var a = list.reduce(function (m, x) { return x < m ? x : m; });
var b = list.reduce(function (m, x) { return x > m ? x : m; });
var i = 0;
while (i < a.length && i < b.length && a[i] === b[i]) i++;
return a.slice(0, i);
}
function renderSkuBar() {
var bar = $('skuBar');
if (!bar) return;
var s = state.series;
if (!s) return;
var chosen = Object.keys(state.sel).length;
var total = s.parameters.length;
var cand = candidateCodes();
var pre = lcp(cand);
// 未选的维度用占位符预演长度 (让用户看到"还差几位")
var rest = '';
s.parameters.forEach(function (p) {
if (state.sel[p.code] !== undefined) return;
rest += '·';
});
bar.classList.toggle('done', chosen === total && cand.length === 1);
$('skuCode').innerHTML = pre
? '<span class="sku-fixed">' + esc(pre) + '</span>' +
'<span class="sku-tbd">' + esc(rest) + '</span>'
: '<span class="sku-tbd">' + esc(t('选择参数以生成零件号', 'Select options to build part number')) + '</span>';
var bar2 = $('skuProg');
if (bar2) bar2.style.width = (total ? chosen / total * 100 : 0) + '%';
var hint = $('skuHint');
if (hint) {
if (chosen < total) {
hint.textContent = t('已选 ' + chosen + '/' + total + ' 项 · 候选 ' +
cand.length + ' 个型号', chosen + '/' + total + ' selected · ' + cand.length + ' candidates');
} else if (cand.length === 1) {
hint.textContent = t('零件号完整', 'Part number complete');
} else if (cand.length > 1) {
hint.textContent = t('该参数组合对应 ' + cand.length + ' 个型号, 请在下方选择',
cand.length + ' models share these options — pick one below');
} else {
hint.textContent = t('无匹配型号', 'No matching model');
}
}
}
// ---------- 参数表 (含范围筛选) ----------
function renderRangeRow() {
var row = $('rangeRow');

View File

@@ -35,6 +35,13 @@
<section id="params" class="params hidden">
<div id="modelTitle" class="model-title">请选择产品系列</div>
<!-- 零件号实时条: 边选边拼, 已确定片段高亮 / 待定片段虚化 -->
<div id="skuBar" class="sku-bar">
<div class="sku-label" id="skuLabel">零件号 Part Number</div>
<div class="sku-code" id="skuCode"></div>
<div class="sku-prog"><i id="skuProg"></i></div>
<div class="sku-hint" id="skuHint"></div>
</div>
<div id="paramPanel"></div>
<div id="actions">
<a id="btnDownload" class="btn primary hidden" download>下载 STEP</a>

View File

@@ -245,8 +245,17 @@ main { flex: 1; display: flex; min-height: 0; }
font-size: 16px; font-weight: 700; margin-bottom: 12px;
color: var(--gray-900); letter-spacing: -.3px; line-height: 1.4;
}
.param-row { margin-bottom: 11px; }
.param-row label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 4px; }
.param-row { margin-bottom: 14px; }
.param-row label {
display: flex; align-items: baseline; gap: 7px;
font-size: 11px; font-weight: 700; color: var(--text-faint);
text-transform: uppercase; letter-spacing: .5px; margin-bottom: 7px;
}
/* 维度已选值回显在其后 */
.param-row label .picked {
text-transform: none; letter-spacing: 0; font-weight: 600;
color: var(--primary-700); font-size: 11.5px;
}
.param-row select, .param-input {
width: 100%; height: 36px; padding: 0 10px; font-size: 13px;
border: 1px solid var(--border); border-radius: var(--radius-sm);
@@ -258,6 +267,104 @@ main { flex: 1; display: flex; min-height: 0; }
}
#actions { margin-top: 16px; display: flex; gap: 8px; flex-wrap: wrap; }
/* ============================================================ 零件号实时条 (DESTACO 风)
* 边选边拼: 已确定部分实心高亮, 未定部分虚化占位。 */
/* ============================================================ 零件号实时条
* 边选边拼: 已确定片段的实心高亮, 未定片段虚化占位。
* 配色走浅色主调 (与整体卡片风格一致), 不用深色块。 */
.sku-bar {
background: linear-gradient(135deg, var(--primary-50), #ffffff 70%);
border: 1px solid var(--primary-200);
border-radius: var(--radius); padding: 13px 15px; margin-bottom: 14px;
box-shadow: var(--shadow-card);
}
.sku-bar .sku-label {
font-size: 10px; text-transform: uppercase; letter-spacing: .7px;
color: var(--text-faint); margin-bottom: 5px;
}
.sku-bar .sku-code {
font-family: ui-monospace, Consolas, monospace;
font-size: 20px; font-weight: 700; letter-spacing: -.4px;
word-break: break-all; line-height: 1.25; min-height: 25px;
}
.sku-bar .sku-fixed { color: var(--primary-700); } /* 已确定片段 */
.sku-bar .sku-tbd { color: var(--gray-300); } /* 待定占位 */
.sku-bar .sku-prog {
margin-top: 9px; height: 3px; border-radius: 999px;
background: var(--primary-100); overflow: hidden;
}
.sku-bar .sku-prog > i {
display: block; height: 100%; width: 0;
background: linear-gradient(90deg, var(--primary-400), var(--primary-600));
border-radius: 999px; transition: width .3s cubic-bezier(.2,.8,.3,1);
}
/* 零件号完整: 整块转为实心青色, 作为"完成"的强反馈 */
.sku-bar.done {
background: linear-gradient(135deg, var(--primary-500), var(--primary-600));
border-color: transparent;
box-shadow: var(--shadow-btn);
}
.sku-bar.done .sku-label { color: rgba(255,255,255,.72); }
.sku-bar.done .sku-fixed { color: #fff; }
.sku-bar.done .sku-tbd { color: rgba(255,255,255,.5); }
.sku-bar.done .sku-prog { background: rgba(255,255,255,.28); }
.sku-bar.done .sku-prog > i { background: #fff; }
.sku-bar.done .sku-hint { color: rgba(255,255,255,.85); }
.sku-bar .sku-hint {
margin-top: 8px; font-size: 11px; color: var(--text-dim);
}
/* ============================================================ 选项块 (替代下拉)
* 有图用卡片, 无图用文字块; 一行多个, 自动换行。 */
.opt-grid {
display: grid; gap: 7px;
grid-template-columns: repeat(auto-fill, minmax(72px, 1fr));
}
.opt-grid.wide { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }
.opt {
position: relative; cursor: pointer; user-select: none;
border: 1.5px solid var(--border); border-radius: var(--radius-sm);
background: var(--surface); padding: 8px 9px;
display: flex; flex-direction: column; align-items: center; gap: 3px;
min-height: 46px; justify-content: center;
transition: transform .18s cubic-bezier(.2,.8,.3,1),
border-color .18s, background .18s, box-shadow .18s;
}
.opt:hover {
transform: translateY(-2px); border-color: var(--primary-400);
box-shadow: 0 6px 16px rgba(20,184,166,.18);
}
.opt .opt-k {
font-family: ui-monospace, Consolas, monospace;
font-size: 13px; font-weight: 700; color: var(--gray-900);
line-height: 1.2; text-align: center; word-break: break-word;
}
.opt .opt-d {
font-size: 10px; color: var(--text-faint); text-align: center;
line-height: 1.25; word-break: break-word;
}
/* 选中态: 青色填充 */
.opt.on {
border-color: var(--primary-500);
background: var(--primary-50);
box-shadow: 0 0 0 2px rgba(20,184,166,.22);
}
.opt.on .opt-k { color: var(--primary-700); }
.opt.on::after {
content: '✓'; position: absolute; top: 3px; right: 5px;
font-size: 10px; color: var(--primary-600); font-weight: 700;
}
/* 不可选 (被联动规则排除): 置灰 + 划线 + 不可点 */
.opt.off {
opacity: .32; cursor: not-allowed; background: var(--gray-50);
border-style: dashed;
}
.opt.off .opt-k { text-decoration: line-through; }
.opt.off:hover { transform: none; box-shadow: none; border-color: var(--border); }
/* 无图时的纯文字块: 更紧凑 */
.opt.txt { min-height: 38px; padding: 7px 8px; }
.opt.txt .opt-k { font-size: 12.5px; }
/* ------------------------------------------------------------ 按钮 */
.btn {
display: inline-flex; align-items: center; justify-content: center;