|
|
3052e42991
|
tools/epro2: add ProjectRelations for cross-document resolution
per-doc Relations 在大量 cross-doc 引用前是不够的:PCB 的 PAD_NET 复合
id [PAD_NET, comp, pin, pad] 里的 pad 实际是 FOOTPRINT 文档里的 pad
实例;SCH_PAGE 的 COMPONENT.partId 指向某个 SYMBOL 文档的 PART.id。
ProjectRelations 在 per-doc Relations 之上做项目级聚合,把这些跨文档
引用拼起来。
Probe 阶段(ESP-VoCat)发现的映射规则(已写入 docstring):
1. SCH_PAGE COMPONENT.partId === PART.id in some SYMBOL doc
- 命名两种风格:'pid<hex>' (anonymous/系统 part) + '<name>.<n>' (具
名 SKU),但都直接相等 PART.id,**不**是不同 namespace
- 同一 PART.id 可能出现在多个 SYMBOL 文档里(库快照),
parts_by_id 保留全部,consumer 通常取第一个
2. PCB COMPONENT.id → FOOTPRINT 文档 UUID via 单独 ATTR op:
ATTR(parentId=<comp>, key="Footprint", value=<fp_doc_uuid>)
COMPONENT.attrs 子 dict 只有内务字段(Unique ID / Channel ID / ...),
**不**含 footprint 引用。这跟 schematic 的 partId 在 COMPONENT 上的
做法不一样,是 EPRO2 流的一处不对称
3. PCB PAD_NET[comp,pin,pad] 里的 pad 是 FOOTPRINT 文档内部的 pad id;
解析链: comp → ATTR Footprint → FOOTPRINT relations.pads[pad]
API:
ProjectRelations.build(project) — 单遍构建
resolve_symbol_docs(sch_uuid, comp_id) → [SYMBOL doc uuids]
resolve_footprint_doc(pcb_uuid, comp_id) → FOOTPRINT doc uuid | None
pad_in_footprint(fp_uuid, pad_id) → PAD payload | None
resolve_pcb_pad_net(pcb_uuid, comp, pin, pad) → {footprint, pad} | None
attrs_for_pcb_component(pcb_uuid, comp_id) → {key: value} 折叠
CLI 加 --project-relations,跑 ESP-VoCat:
documents 278
distinct_parts 87
duplicated_parts 9
pcb_components_with_footprint 206
pcb_components_unresolved_footprint 0
sch_components_with_partid 572
sch_components_unresolved_part 0
PCB 样本验证:comp=e0 → fp=1069352d81c6 Designator='U8',
PAD_NET pin=1 pad=e7 net=GND 跨文档解到坐标 (-37.4,-45.24)。
测试:6 个新单测覆盖 partId→symbol、comp→footprint、PAD_NET 跨文档、
attrs 折叠、unresolved 计数。parser + relations + project_relations
共 21/21 通过。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-28 22:22:39 +08:00 |
|
|
|
7f9e2fad73
|
tools/epro2: add Relations layer for cross-object navigation
在 replay 的扁平 objects[id] -> payload 之上盖一层 Relations,建索引和
反向引用,把孤立对象拼成可遍历的图,是后续 EPRO2 → KiCad 转换器的
中间表示前置。
Relations.build(doc) 单遍扫所有对象,得到:
主集合(按类型分桶):
parts / components / pins / pads / wires / nets / layers / rules
复合 ID 解析(关键):
'["LAYER",1]' → layers[1]
'["NET","GND"]' → nets["GND"]
'["PAD_NET","e0","1","e7"]' → pad_nets_by_pad/by_net
'["RULE","SAFE","copperThickness1oz"]' → rules[("RULE","SAFE",...)]
反向引用:
obj_ids_by_part partId → 引用对象 ids(lib 内 RECT/TEXT/PIN 都带 partId)
components_by_part partId → component ids
attrs_by_parent parentId → ATTR ids
lines_by_wire WIRE.id → LINE ids(wire 由若干 LINE 段组成)
pad_nets_by_pad PAD.id → PAD_NET 记录
pad_nets_by_net net name → PAD_NET 记录
objects_on_layer / objects_in_net 字段反查
便捷 accessor:
attrs_dict(parent_id) 折叠所有 ATTR ops 到 {key: value} dict(last
write wins),KiCad 转换时按 component 拿
Designator/Value/Footprint 的常用入口
ATTR.parentId 解析(实测发现的两种坑):
1. 不仅指向 COMPONENT/PART —— 也大量指向 WIRE(schematic 上的网络
标签 / 网络属性)。原查重函数漏算,636 个 false positive
unresolved;改为"任意 doc.objects[parentId] 命中即算 resolved"
2. 复合形式 `<comp_id>-<pin_id>` 用于把 ATTR 挂在某 component 的某个
pin 上(如 PinName)。`_resolve_parent()` 用 split("-",1) 兜底
CLI 加 --relations,按 docType 聚合 stats:
uv run python -m tools.epro2 data/raw/oshwhub/<uuid> --relations
ESP-VoCat 验证:
SCH_PAGE 9 docs : 572 components, 563 wires, 934 lines_grouped,
4111 attrs_attached, 0 unresolved_parents
PCB 6 docs : 206 components, 807 pad_nets, 173 nets, 544 layers
SYMBOL 105 docs : 106 parts, 560 pins, 1680 attrs_attached
FOOTPRINT 55 docs: 496 pads, 9 nets, 1771 layers, 140 rules
注:PCB 内 pads=6 vs pad_nets=807 不矛盾 —— PAD 实例存在 FOOTPRINT
文档里,PCB stream 用 ["PAD_NET",comp,pin,pad] 复合 id 跨文档引用;
解析"comp 的某 pin 通过哪个 footprint 的哪个 pad"需要 project-级
Relations 聚合(下个 task)。
测试:tools/epro2/tests/test_relations.py 9 个单测覆盖复合 id 解析、
lineGroup 链接、parentId 直/复合解析、partId 反查、attrs 折叠。
parser + relations 共 15/15 通过。
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
2026-04-28 22:17:28 +08:00 |
|