# -*- coding: utf-8 -*- u"""P1-INPUT 两处换件(2026-09-06 用户拍板): 1. **F1**:`F1206HC10A0TM`(C310989, 10 A) → **`F1206HC12A0TM`(C310990, 12 A)**。 同是 1206,但**封装对象不是同一个**(`FUSE-SMD_L3.2-W1.6-1` → `F1206`), 焊盘尺寸可能不一样 ⇒ 导入变更之后必须重抓 `pcb1-parts.json`, 并且重跑 `pcb1_pour.py` —— 顶层那两块 8 A 宽铜的边界就是照 F1 的焊盘定的。 为什么升到 12 A:8 A 连续 / 10 A 额定 = 80%,超过保险丝常规的 75% 降额线; 而且 10 A 那颗 I²t 只有 15 A²s,C2/C3 两颗 470 µF 的上电浪涌有误熔断风险 (12 A 那颗是 20 A²s)。**不是因为 1206 做不到 10 A** —— 那是一次判断错误, C310989 确实是 1206/10 A,BOM 和封装本来就对得上。 2. **R9**:`5W水泥电阻 5W10RJ`(立式,P5.80 脚距)→ **5 × 2 Ω 2512 串联** (`RHC2512FT2R00` C2570006,2 Ω/2 W)= 10 Ω/10 W,**高度 9.5+ mm → 0.8 mm**。 起因:`pcb1_place.H_FP` 把那颗立式水泥电阻当成 9.5 mm,而它是 P5.80 脚距的 **立式**件(同族 C9900012721 的 3D 模型标 H=52.0),那一片 hmax 只有 12.0 —— **高度检查一直在拿一个错的数放行**。库里的卧式 5W 脚距 26 mm、占位 28×11 mm, 北左角塞不下;2512 串联既矮又放得下,功率还从 5 W 变成 10 W。 ## 版面 R9 原来横在 y=300、x 700~740,右端经 (780,300)→(780,270) 接 GND 旗。 五颗按 70 间距排在同一条线上,右端顺延到 (1060,270) 的 GND 旗: Q3.D ──(700,300)── R9A ── R9B ── R9C ── R9D ── R9E ──(1060,300) 720 790 860 930 1000 │ (1060,270) GND x 1060 离 A4 右边界 1145 还有 85;y=300 在标题栏(x>455 且 y<190)之上。 ## 这个脚本按 skill 的四条硬规矩写 * **先 create 后 delete**(§9b):create 参数写错时删除已经生效,页面会凭空少件。 * **旗只能删掉重建**(§3b):移动 netflag 会把网络名重置成符号默认值(`+5V`), 而且 `setState_Net()` 修不回来 —— 几何验证完全抓不到。 * **所有坐标是 5 的倍数**(§8a-2b):旗/端口会被吸附到 5 的倍数,导线不会; 差 2 个单位就是断的,四道检查全过,只有 `port_audit.py` 抓得到。 * **引脚跨距读回来,不要按 ±20 推**(§8d):0402 是 ±20、1206 是 ±15、33nF 不对称。 这里创建完立刻 `getAllPins()` 断言,对不上就停手(导线是照断言的坐标算的)。 用法: /c/Python314/python .claude/p1_r9f1.py [--push] 下发完必跑:verify_sch.py --nets / port_audit.py / xpage.py """ import io import json import os import sys HERE = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, HERE) from eda import run PAGE = '9a6096762e036e23' # P1-INPUT LIB = '0819f05c4eef4c71ace90d822a990e87' # ── 新器件 ──────────────────────────────────────────────────────────── DEV_FUSE12 = '507cd700b60b451caa0f3237e41baf6d' # F1206HC12A0TM C310990 fp=F1206 DEV_R2R = '3c25870a08a64dc7b809655f380d8ce9' # RHC2512FT2R00 C2570006 fp=R2512 # ── 要删掉的旧图元(id 是 2026-09-06 从页上读回来的,下发前会逐个核对存在)── OLD_F1 = 'f597ddb161844709' # F1 (270,710) OLD_R9 = 'f031e2bfbab8d6bf' # R9 (720,300) OLD_WIRE = '454a8c93074353e1' # (780,300)-(740,300) + (780,300)-(780,270) OLD_GND = 'f73d47707aa44277' # GND 旗 (780,270) F1_XY = (270, 710) # F1 原位,新件放同一处(换件不动版面) F1_PINS = ((250, 710), (290, 710)) # 断言用:旧件的两脚 # ── R9A~R9E ─────────────────────────────────────────────────────────── R9_Y = 300 R9_X = [720, 790, 860, 930, 1000] # 间距 70,本体 ±20 ⇒ 缝 30 R9_DES = ['R9A', 'R9B', 'R9C', 'R9D', 'R9E'] R9_SPAN = 20 # 断言用;对不上就停 GND_XY = (1060, 270) # 新 GND 旗 # 链间连线 + 右端到 GND(按 R9_X / R9_SPAN 算,见 wires()) TAIL_X = 1060 def wires(): u"""五颗串起来 + 右端拐下去接地。全部横平竖直、非零长、5 的倍数。""" w = [] for a, b in zip(R9_X, R9_X[1:]): w.append((a + R9_SPAN, R9_Y, b - R9_SPAN, R9_Y)) w.append((R9_X[-1] + R9_SPAN, R9_Y, TAIL_X, R9_Y)) w.append((TAIL_X, R9_Y, GND_XY[0], GND_XY[1])) return w def selfcheck(): u"""下发前的本地防呆(§6d / §8a-2b)。""" bad = [] for x1, y1, x2, y2 in wires(): if not ((x1 == x2) != (y1 == y2)): bad.append(u'不是横平竖直或零长度: %s' % ((x1, y1, x2, y2),)) for v in (x1, y1, x2, y2): if v % 5: bad.append(u'坐标不是 5 的倍数: %s' % ((x1, y1, x2, y2),)) for v in list(GND_XY) + R9_X + [R9_Y, TAIL_X] + list(F1_XY): if v % 5: bad.append(u'坐标不是 5 的倍数: %s' % v) # 相邻两颗之间要留得下位号/型号文字 for a, b in zip(R9_X, R9_X[1:]): gap = (b - R9_SPAN) - (a + R9_SPAN) if gap < 20: bad.append(u'%d 和 %d 之间只剩 %d 个单位,文字会压上' % (a, b, gap)) # A4 可用区 x 20~1145,标题栏 x>455 且 y<190(§8c / §12) if TAIL_X > 1100: bad.append(u'右端 %d 太靠边,型号文字会出图框' % TAIL_X) if R9_Y < 190 and R9_X[0] > 455: bad.append(u'y=%d 落进标题栏了' % R9_Y) return bad OPEN = ("await eda.dmt_EditorControl.openDocument('%s');" "await new Promise(r=>setTimeout(r,1500));" % PAGE) def call(body, timeout=180): r = run(OPEN + body, timeout=timeout) if not r.get('success'): print(u'失败:', json.dumps(r, ensure_ascii=False)[:600]) sys.exit(1) return r['result'] # ── ① 建新件并读回引脚(**还没删任何东西**)───────────────────────────── JS_CREATE = u""" const LIB='%(lib)s'; const cur = (await eda.dmt_Schematic.getCurrentSchematicPageInfo()).name; if (cur !== 'P1-INPUT') return {err: '打错页了: ' + cur}; // 先确认四个要删的图元都还在 —— 不在就说明这个脚本已经跑过一次了 const have = {}; for (const c of await eda.sch_PrimitiveComponent.getAll()) have[c.getState_PrimitiveId()] = String(c.getState_Designator ? c.getState_Designator() : ''); for (const w of await eda.sch_PrimitiveWire.getAll()) have[w.getState_PrimitiveId()] = 'wire'; const missing = %(need)s.filter(id => !(id in have)); if (missing.length) return {err: '要删的图元不在页上(已经跑过一次?): ' + missing.join(' ')}; const made = []; // F1:同一位置建新件,暂不改位号(旧 F1 还在,避免重名) { const c = await eda.sch_PrimitiveComponent.create({libraryUuid:LIB, uuid:'%(fuse)s'}, %(fx)d, %(fy)d, undefined, 0, false); const pins = []; for (const p of await c.getAllPins()) pins.push([String(p.pinName||''), +p.x, +p.y]); made.push({tag:'F1', id: c.getState_PrimitiveId(), pins}); } // R9A~R9E for (const [i, x] of %(rx)s.entries()) { const c = await eda.sch_PrimitiveComponent.create({libraryUuid:LIB, uuid:'%(r2r)s'}, x, %(ry)d, undefined, 0, false); const pins = []; for (const p of await c.getAllPins()) pins.push([String(p.pinName||''), +p.x, +p.y]); made.push({tag: %(des)s[i], id: c.getState_PrimitiveId(), pins}); } return {made}; """ # ── ② 删旧件 / 旧线 / 旧旗,设位号,画线建旗 ─────────────────────────── JS_FINISH = u""" const cur = (await eda.dmt_Schematic.getCurrentSchematicPageInfo()).name; if (cur !== 'P1-INPUT') return {err: '打错页了: ' + cur}; const out = {}; // 删旧:先删元件和旗,再删导线 for (const id of ['%(oldF1)s', '%(oldR9)s', '%(oldGnd)s']) await eda.sch_PrimitiveComponent.delete(id); await eda.sch_PrimitiveWire.delete('%(oldWire)s'); out.deleted = 4; // 设位号(旧件已删,不会重名) const byId = {}; for (const c of await eda.sch_PrimitiveComponent.getAll()) byId[c.getState_PrimitiveId()] = c; out.named = []; for (const [id, des] of %(names)s) { const c = byId[id]; if (!c) { out.err = '新建的元件不见了: ' + des; return out; } await c.toAsync().setState_Designator(des).done(); out.named.push(des); } // 导线 out.wires = 0; for (const s of %(wires)s) { await eda.sch_PrimitiveWire.create([s[0],s[1],s[2],s[3]]); out.wires++; } // GND 旗(rot 0 = 引脚在上,导线从上方接进来) await eda.sch_PrimitiveComponent.createNetFlag('Ground', 'GND', %(gx)d, %(gy)d, 0, false); out.flag = 'GND@%(gx)d,%(gy)d'; return out; """ def main(): bad = selfcheck() print(u'P1-INPUT 换件:F1 → F1206HC12A0TM(12A) / R9 → 5×2Ω 2512 串联') print(u' R9A~R9E 位置: %s @ y=%d,引脚跨距按 ±%d 算' % (' '.join(str(x) for x in R9_X), R9_Y, R9_SPAN)) print(u' 导线 %d 段 + GND 旗 1 个 @ %s' % (len(wires()), GND_XY)) for w in wires(): print(u' %s' % (w,)) if bad: print(u'❌ 本地自检没过:') for b in bad: print(u' · ' + b) sys.exit(1) print(u' [OK] 本地自检:横平竖直 / 非零长 / 全是 5 的倍数 / 文字有缝 / 不进标题栏') if '--push' not in sys.argv: print(u'\n加 --push 才真的改原理图。') return r = call(JS_CREATE % dict( lib=LIB, fuse=DEV_FUSE12, r2r=DEV_R2R, fx=F1_XY[0], fy=F1_XY[1], ry=R9_Y, rx=json.dumps(R9_X), des=json.dumps(R9_DES), need=json.dumps([OLD_F1, OLD_R9, OLD_WIRE, OLD_GND]))) if r.get('err'): sys.exit(u'❌ ' + r['err']) # 引脚断言:导线是照这些坐标算的,对不上就停手(§8d) made = r['made'] ids, errs = [], [] for e in made: xs = sorted(p[1] for p in e['pins']) ys = set(p[2] for p in e['pins']) if e['tag'] == 'F1': want = sorted(F1_PINS) got = sorted((p[1], p[2]) for p in e['pins']) if got != want: errs.append(u'F1 新件引脚 %s,期望 %s' % (got, want)) else: i = R9_DES.index(e['tag']) want = [R9_X[i] - R9_SPAN, R9_X[i] + R9_SPAN] if len(e['pins']) != 2 or xs != want or ys != {R9_Y}: errs.append(u'%s 引脚 %s,期望 x=%s y=%d' % (e['tag'], e['pins'], want, R9_Y)) ids.append((e['id'], 'F1' if e['tag'] == 'F1' else e['tag'])) for e in made: print(u' 建出 %-4s %s' % (e['tag'], ' '.join('%s(%d,%d)' % tuple(p) for p in e['pins']))) if errs: print(u'❌ 引脚坐标对不上,**旧件一个都没删**,页面还是完整的:') for x in errs: print(u' · ' + x) print(u' 把新建的这几个删掉(id 见上),改好 R9_SPAN / F1_PINS 再来。') sys.exit(1) print(u' [OK] 六个新件的引脚坐标全部落在算好的位置上') d = call(JS_FINISH % dict( oldF1=OLD_F1, oldR9=OLD_R9, oldGnd=OLD_GND, oldWire=OLD_WIRE, names=json.dumps(ids), wires=json.dumps([list(w) for w in wires()]), gx=GND_XY[0], gy=GND_XY[1])) if d.get('err'): sys.exit(u'❌ ' + d['err']) print(u' 删旧 %d / 设位号 %s / 导线 %d 段 / %s' % (d['deleted'], ' '.join(d['named']), d['wires'], d['flag'])) print(u'\n下一步(一个都不能省):') print(u' 1. verify_sch.py --nets 看 R9A~R9E 是不是串成一条、GND 只有一个名字') print(u' 2. port_audit.py 旗/端口必须落在导线**端点**') print(u' 3. xpage.py 跨页对账') print(u' 4. 截图人眼看一次(几何证明不了「看着对」)') print(u' 5. pcb1_import.py --probe/--apply/--verify → pcb1_parts_dump.py') print(u' 6. pcb1_place.py(加 R9A~R9E,删 R9)→ --push → 扇出三步 → pcb1_pour.py --push') if __name__ == '__main__': main()