tools/epro2/std: rewrite to Option 2 (objects dump) per downstream spec

Downstream came back with concrete requirements: don't pre-compute Std
shape[] tilde strings, just dump the raw EPRO2 `objects: {id: payload}`
dict and they'll write a ~100-LoC adapter on their side. Pulling the
tilde-mapping work back saves us from second-guessing positional fields
without their parser to verify against, and shortens our pcb_writer
from ~500 lines to ~40.

Output shape (Std envelope intact, just no `shape[]`):

    {
      "success": true, "code": 0,
      "result": {
        "uuid", "puuid", "title",
        "docType": 3 | 1,
        "components": {},
        "dataStr": {
          "head": {
            "docType": "3" | "1",
            "editorVersion": "facere-epro2/0.1 (epro2 <X.Y.Z>)",
            "units": "mil",
            "epro2_doc_uuid": ...,
            "epro2_editor_version": ...,
          },
          "BBox": {x, y, width, height},   # mil
          "layers": [...],                  # Std layer-string array
          "objects": dict(doc.objects),     # raw EPRO2, 1:1
          "preference": {}, "netColors": [], "DRCRULE": {},
        }
      }
    }

Per-doc spec downstream gave us:
  - shape[] dropped (empty placeholder misleads adapter)
  - all units mil (no mm conversion — Std canvas already declares mil)
  - head.units="mil" so adapter doesn't have to guess
  - BBox min/max across known x/y/startX/endX/centerX fields; adapter
    can refine by walking path arrays itself
  - layers[] keeps Std's 17-line default + inner SIGNAL layers actually
    used (21~Inner1.., 22~Inner2..)
  - empty stubs preference/netColors/DRCRULE for grep-based triage

New: docs/sources/epro2_to_std_mapping.md with the full EPRO2 OPTYPE →
Std verb table that downstream's adapter authors will copy from. Tables
include the layer-id remapping (the 5↔7 paste/mask flip, 11→10 outline,
12→11 multi, SIGNAL 15+→21+), PCB op mappings, SCH op mappings (marked
best-effort: no Std SCH samples in our corpus), and the 5-Voltage
placeholder COMPONENT → extra net flag trick. Extracted from the
previous Option-3 writer (commit fe6971f) so adapter writers don't
have to reverse-engineer it from source.

ESP-VoCat smoke: 6 PCB + 9 SCH = 15 JSON files, head.units=mil
preserved, no shape[] field present. 82 → 84 unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 01:41:12 +08:00
parent c6fd111d6d
commit 3866e24189
6 changed files with 639 additions and 860 deletions

64
log.md
View File

@@ -4,6 +4,70 @@
---
## 2026-04-29 04:30 std/ writer 翻 Option 2raw objects dump + mapping doc
**Claude 会话**
`fe6971f`。下游同学回了具体规格:选 Option 2objects dict 直接 dump不要我们做 tilde 串映射,他自己写 ~100 LoC adapter 翻。把之前 Option 3full Std `shape: ["TRACK~...", ...]`)那套删了,重写。
### 下游确认的规格
1. **`shape[]` 不要保留**——adapter 从 `objects` 重建,空占位反而误导
2. **全 mil**——不转 mmBBox 也 milStd `canvas` 那一行 `~mil~` 已经写了)
3. **`head` 必带**
- `editorVersion``facere-epro2/0.1 (epro2 <doc.head.editVersion>)`Wokwi 据此选解析分支
- `docType``"3"` (PCB) / `"1"` (SCH)
- `units``"mil"`
4. **`layers[]`** 沿用 Std 53-layer 字符串数组格式(`"1~TopLayer~#FF0000~..."`inner SIGNAL 有用到才追加
5. **保留空 stub** `preference / netColors / DRCRULE`——失败时 grep 路径稳定
6. **per-doc 一个 .json**,平铺,不合并
### 写完三件
#### `tools/epro2/std/pcb_writer.py` 重写
40 行核心逻辑(之前 Option 3 是 500 行):
- `_gather_bbox_points`:扫所有 op 的 `x/y/startX/...` 等坐标字段min/max
- `_used_inner_signal_layers`:找真正有 primitive 的 SIGNAL 内层 id
- envelope 直接 dump `dict(doc.objects)`
#### `tools/epro2/std/sch_writer.py` 重写
更短——schematic 没 copper layerlayers=[]。其它结构跟 PCB 一样。
#### `docs/sources/epro2_to_std_mapping.md` 新增
**这是给下游 adapter 的关键文档**——他写 100 LoC adapter 时按这个表查。内容:
- EPRO2 layer id → Std layer id 重映射表最坑5↔7 mask/paste 反着、11→10 outline、12→11 multi、SIGNAL 15+→Std 21+
- PCB OPTYPE → Std verb 全表TRACK / VIA / COPPERAREA / SOLIDREGION / CIRCLE / LIB+#@$PAD+TEXT
- SCH OPTYPE → Std verb 全表W / N / T / LIB+#@$P—— 标了 best-effort没 Std SCH 实样
- COMPONENT placement 旋转 + 平移公式footprint-local → PCB-absolute
- 5-Voltage 占位符 `pid8a0e77bacb214e` 的 Global Net Name → 额外 N flag 的 trick
mapping doc 直接从 `fe6971f` 那个 Option 3 writer 提炼出来——他不用读我们代码,照表填就行。
### 实测
ESP-VoCat 6 PCB + 9 SCH = 15 JSON
- 一个典型 PCB: `objects=1719, layers=17, BBox=(-2293, -900, 2997×1899)`
- `head.units = "mil"``head.editorVersion = "facere-epro2/0.1 (epro2 3.2.91)"``head.docType = "3"`
- `shape` 字段已确认**不存在**
- `objects` 原 payload 1:1 保留(含 LAYER ops 的 activeColor 等所有字段)
### 决策Why
- **不留空 `shape: []`**——下游说"误导 adapter"。明确不存在,比空数组更诚实
- **`head.editorVersion` 加前缀 `facere-epro2/0.1`**——区分我们的输出 vs lceda 真实 Stdadapter 看到这个能猜出是 EPRO2 转过来的
- **保留 `preference/netColors/DRCRULE` 空 stub**——下游说失败 grep 排查方便
- **mapping doc 单独成文不混在 README**——adapter 作者一个文件就够,不用读源码
### 测试
82 → 84 单测全过:原 Option 3 的 11 个测试改成验 Option 2envelope 必带 key / 无 shape / head units&version / objects 1:1 / BBox min-max / 内层 SIGNAL append / docType reject
### Push
`fe6971f` 的 Option 3 已被这次 commit 覆盖 / 简化掉。下游回来如果说 mapping table 有错位再修——但他自己拿表填 adapter跑通后给反馈我们再迭代。
---
## 2026-04-29 04:00 Std-format JSON 转换器EPRO2 → 下游同学 Wokwi pipeline 的输入格式
**Claude 会话**