R2 底板改 4 层(D15):规则 / 几何 / 背面摆位 / 4 层布线与铺铜,已下发
- 规则:铜层 2→4,差分 0.28/0.18,间距 0.15,过孔 0.3/0.2(rules_base) - 几何:98×98 板框 + 4×M3,槽阵居中,L1 12V 大铜 + 指头(pcb_base) - 摆位:五插槽留 L1,其余 133 件翻到 L4;背面镜像规则实测;Hub 放走廊 2、 东移 2.5 mm;扇出圈 / 小件间距 1.5(去耦例外)/ 9 A 过孔阵禁区(place_base) - 叠层按用户决定改为 L1 槽面 / L2 整层 +3V3 / L3 整层 GND / L4 器件面 - 布线:D± 七对按构造布通(USB3 掉头、USB1/2 北去掉头、USB4/5 东绕、 J23 桥),+3V3 / GND / +12V 平面网每脚一孔,0.125 网格 + PathFinder 开关(route_base、remote_route.ps1);剩 37 个网待手工补 - 铺铜:4 层重写,入口链自南向北切段 + 断面校核(pour_base); 已下发布线 / 缝合孔 / 铜面轮廓,填铜未成功(待查) - 工具:plot_base / plot_routes / shot_base,board_check 通孔两层都算 Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_0168Bu6SEx3cXXCJLkdnE6zB
这个提交包含在:
@@ -0,0 +1,382 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
u"""R2 刀片 PCB 摆位(M4c-2)。
|
||||
|
||||
/c/Python314/python .claude/r2/pcb_place.py # 只算 + 自检(不下发)
|
||||
/c/Python314/python .claude/r2/pcb_place.py --push # 下发摆位
|
||||
/c/Python314/python .claude/r2/pcb_place.py --verify # 读回校验
|
||||
|
||||
## 三类件,三种定位方式
|
||||
|
||||
1. **机械锚定件**(位置由小米8 主板决定,不许挪):`K1~K4` 侧键触点盘、
|
||||
`J5` 电池排线焊盘、`J4` BM24 尾插座。坐标从 **`.claude/mi8_mech.json`** 换算,
|
||||
不手抄。手机主板在刀片上的落点是 `PHONE_ORG`(方案 §3.1:x 3~70.09 / y 29~73.66,
|
||||
长边水平、**接口边朝下**)。
|
||||
2. **手工钉死件**:`J1` 金手指(`pcb_blade.J1_X`)、`C60` Ø10.3 的 2200 µF、
|
||||
`J2` D± 退路座、`J3` EDL 座 —— 各有各的理由,见 `ANCHORS` 的注释。
|
||||
3. **其余 70 件**:按功能分组、按「电流流向」排序,塞进四个区(`REGIONS`),
|
||||
用一个简单的货架式排布器摆。**顺序是设计意图,坐标不是** ——
|
||||
要改布局改 `GROUPS` 里的顺序和区,不要去改具体坐标。
|
||||
|
||||
## 分区(方案 §3.1,坐标见 `pcb_blade.py`)
|
||||
|
||||
| 区 | 范围 | 放什么 |
|
||||
|---|---|---|
|
||||
| 金手指区 | y 0~11 | **只有 J1,其余一件不放**(插槽体 ~9 mm 要让开)|
|
||||
| 服务带 | y 12~28 | 电池/尾插/EDL/侧键 MOS/阻尼组 —— 全部接口都在这一带 |
|
||||
| 手机投影 | x 3~70.1 / y 29~73.7 | **只有 K1~K4 四个裸铜盘**(螺柱间隙只有 1.0 mm,放不下别的)|
|
||||
| 功率列 | x 72~98 | Buck / eFuse / 检流 / PCA9555。**按「离手机越远越吵」内外分两条** |
|
||||
|
||||
功率列内部:外条(x 86~98,靠板边)放 SW 节点、电感、Cin —— 最吵的;
|
||||
内条(x 72~85,贴手机)放 FB 分压、DAC 低通、INA226、PCA9555 —— 最怕吵的。
|
||||
这是方案 §3.1 的「按离手机越远越吵排」和风险 R-12。
|
||||
|
||||
## B 面(底层)留空
|
||||
|
||||
方案 §3.2 的高度表把 `L1`(SPM6530T,3.0 mm)算作「B 面最高件」,但同一张表里
|
||||
**A 面功率列可用 11.7~13.7 mm** —— 3.0 的电感放 A 面绰绰有余,而且能贴着 `U1` 摆,
|
||||
**SW 节点不用打过孔**(§7.2 第二条:SW 面积最小化)。所以这一版 **83 件全在 A 面**,
|
||||
B 面空着。这让 `h_B` 从 3.0 变成 0,槽距只会更宽裕 —— 是放松,不是违反。
|
||||
"""
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.stdout.reconfigure(encoding='utf-8')
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
# ⚠ 顺序不能反:`.claude` 下有同名的 R1 脚本(pcb_pour / pcb_rules / pcb_place),
|
||||
# 先插 .claude 再插 HERE 的话,`import pcb_pour` 会拿到 R1 那一份,
|
||||
# 而且**不报错**,只在调到 R2 专有的函数时才炸。
|
||||
sys.path.insert(0, os.path.dirname(HERE))
|
||||
sys.path.insert(0, HERE)
|
||||
from eda import run # noqa: E402
|
||||
import pcb_blade as B # noqa: E402
|
||||
|
||||
MIL = B.MIL
|
||||
PCB = B.PCB
|
||||
BBOX = os.path.join(HERE, '_blade_bbox.json')
|
||||
MECH = os.path.join(os.path.dirname(HERE), 'mi8_mech.json')
|
||||
|
||||
GAP = 0.6 # 件间净距(mm)
|
||||
|
||||
# 手机主板在刀片上的落点:本体 (u,v) -> 板 (x,y) = (PHONE_ORG + (u,v))
|
||||
# 方案 §3.1:主板 67.09 × 44.66,长边水平、**接口边(v=0 那条,J600/J1401/侧键都在上面)朝下**。
|
||||
# ⚠ 事实来源在 pcb_blade(板级),这里只做别名,不要抄第二份
|
||||
PHONE_ORG = B.PHONE_ORG
|
||||
PHONE_WH = B.PHONE_WH
|
||||
|
||||
# 焊盘外框量不到本体的几个件:给真实本体尺寸(mm)
|
||||
BODY = {
|
||||
'C60': (11.0, 11.0), # Ø10.3 铝电解,2200 µF(T2 = 高 10.5)
|
||||
'L1': (7.6, 7.0), # SPM6530T-4R7M
|
||||
'J4': (13.2, 3.6), # BM24-50DS
|
||||
'U4': (8.0, 8.0),
|
||||
}
|
||||
# ⚠ **全部放 A 面,B 面留空。** 方案 §3.2 的高度表把 `L1`(SPM6530T 3.0 mm)算成
|
||||
# 「B 面最高件」,但那张表同时写着 **A 面功率列可用 11.7~13.7 mm** —— 3.0 的电感
|
||||
# 放 A 面绰绰有余,而且能贴着 U1 摆、SW 节点不用打过孔(§7.2 第二条:SW 面积最小化)。
|
||||
# B 面空着只会让槽距更宽裕(h_B 从 3.0 变 0),是**放松**不是违反。
|
||||
BOTTOM = []
|
||||
|
||||
|
||||
def phone(u, v):
|
||||
return (round(PHONE_ORG[0] + u, 3), round(PHONE_ORG[1] + v, 3))
|
||||
|
||||
|
||||
def anchors():
|
||||
u"""机械锚定 + 手工钉死的件 -> (x, y, rot)。"""
|
||||
m = json.load(io.open(MECH, encoding='utf-8'))
|
||||
sk = m['side_key_contacts']
|
||||
a = {}
|
||||
# ── 侧键触点盘:主板自己的弹片直接压在这四个裸铜盘上(R1 §22.3e 中框式)──
|
||||
# 位置**完全由主板决定**,Ø1.5 的盘只容许 0.50 mm 位置误差(pcb2_place 的算账)。
|
||||
for ref, key in (('K1', 'PWR'), ('K2', 'VUP'), ('K3', 'VDN'), ('K4', 'GND')):
|
||||
a[ref] = phone(*sk[key]) + (0,)
|
||||
# ── 电池排线焊盘:排线从 J600 翻过接口边折下来,落在接口边正下方 ──
|
||||
a['J5'] = (phone(m['contacts_up']['J600'][0], 0)[0], 25.6, 0)
|
||||
# ── BM24 尾插座:同理,落在 J1401 正下方 ──
|
||||
a['J4'] = (phone(m['contacts_up']['J1401'][0], 0)[0], 21.0, 0)
|
||||
# ── 手工钉死的四个 ──
|
||||
# C60:Ø10.3 / 高 10.5,**只能放没有手机压着的地方**(§21.2),
|
||||
# 且要紧挨 J5(它是 VO_P 上的阻尼组主电容,回路越短越好)。
|
||||
a['C60'] = (37.5, 19.0, 0)
|
||||
# J2:D± 退路座。要靠近金手指的 D± 针(B14/B15,x≈62/63),
|
||||
# **金手指到 J2 之间要留割断位**(D12 / §17.4),所以摆在指区正上方偏右。
|
||||
a['J2'] = (62.0, 15.0, 0)
|
||||
# J3:EDL 座。跟着 U6(TS5A3157 模拟开关)走 —— 它是手机侧信号,
|
||||
# 放到功率列去的话 EDL 线要横穿 40 mm。摆在服务带右段、U6 上方。
|
||||
a['J3'] = (66.5, 25.0, 0)
|
||||
return a
|
||||
|
||||
|
||||
# ── 分组:**顺序 = 电流/信号流向**,排布器按这个顺序摆 ──────────────────
|
||||
GROUPS = [
|
||||
# (区, 组名, [位号...])
|
||||
# ⚠ 功率列**自下而上 = 输出端 → 输入端**(和电流方向相反)。理由:
|
||||
# `VO_P` 是 4 A,出口必须离服务带的 `C60`/`J5` 越近越好;`+12V` 只有 1.45 A,
|
||||
# 而且走 L3 灌铜,从金手指爬到板子上半段无所谓。反过来排就是拿 4 A 换 1.45 A。
|
||||
# ⚠ `U3` INA226 和 `Rs1` **必须同组且相邻**(§7.2 第四条:取样脚落在 Rs 的
|
||||
# Kelvin 焊盘上,不许就近扎孔)。放到内条去就隔了整整一列。
|
||||
('POWER_OUT', u'eFuse + 检流 + INA226', ['U2', 'C14', 'Rs1', 'U3', 'C13',
|
||||
'R19', 'Q2']),
|
||||
('POWER_OUT', u'Buck 输出', ['C6', 'C7', 'C8']),
|
||||
('POWER_OUT', u'Buck 本体 + SW + L', ['U1', 'C5', 'L1', 'C9']),
|
||||
('POWER_OUT', u'12V 入口 + Buck 输入', ['F1', 'D1', 'C1', 'C2', 'C3', 'C4']),
|
||||
# 内条按同样的自下而上顺序,让每一级和外条大致齐平
|
||||
('POWER_IN', u'PCA9555 + I2C 上拉', ['U4', 'C40', 'R40', 'R41']),
|
||||
('POWER_IN', u'eFuse 外围', ['R9', 'R10', 'R11', 'R12', 'R16', 'R17',
|
||||
'R13', 'R15', 'R14', 'Q1', 'R18']),
|
||||
('POWER_IN', u'FB 分压 + DAC 低通', ['R1', 'R2', 'R7', 'R8', 'C12']),
|
||||
('POWER_IN', u'Buck 外围', ['R3', 'R4', 'R5', 'R6', 'C10', 'C11']),
|
||||
('SERVICE_L', u'侧键 MOS(贴着 K1~K3)', ['Q3', 'R42', 'R43', 'Q4', 'R44', 'R45',
|
||||
'Q5', 'R46', 'R47']),
|
||||
('SERVICE_M', u'VO_P 阻尼组 + 电池外围', ['C61', 'C62', 'C63', 'C64', 'C65',
|
||||
'R60', 'R61']),
|
||||
('SERVICE_R', u'尾插 USB + VBUS 开关', ['U7', 'U5', 'C66', 'C67', 'C68',
|
||||
'R62', 'R64', 'R65']),
|
||||
('SERVICE_R', u'EDL 模拟开关', ['U6', 'R63', 'C69']),
|
||||
]
|
||||
|
||||
# ── 区(x0, y0, x1, y1),排布器在里面自下而上、自左而右铺 ────────────────
|
||||
REGIONS = {
|
||||
'POWER_OUT': (85.8, 12.0, 97.5, 74.0),
|
||||
'POWER_IN': (72.3, 12.0, 85.0, 74.0),
|
||||
'SERVICE_L': (2.5, 12.0, 19.5, 28.0),
|
||||
'SERVICE_M': (20.0, 12.0, 41.5, 28.0),
|
||||
'SERVICE_R': (42.0, 12.0, 71.0, 28.0),
|
||||
}
|
||||
|
||||
|
||||
def sizes():
|
||||
d = json.load(io.open(BBOX, encoding='utf-8'))
|
||||
out = {}
|
||||
for ref, v in d.items():
|
||||
w, h = BODY.get(ref, (v['w'], v['h']))
|
||||
out[ref] = (max(w, 0.6), max(h, 0.6))
|
||||
return out
|
||||
|
||||
|
||||
def overlaps(r, rects):
|
||||
for q in rects:
|
||||
if r[0] < q[2] and q[0] < r[2] and r[1] < q[3] and q[1] < r[3]:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def plan():
|
||||
u"""返回 {位号: (x, y, rot)} 和占位矩形表。"""
|
||||
sz = sizes()
|
||||
pos = dict(anchors())
|
||||
pos['J1'] = (B.J1_X, B.J1_Y, B.J1_ROT)
|
||||
rects = []
|
||||
for ref, (x, y, _r) in pos.items():
|
||||
w, h = sz[ref]
|
||||
rects.append((x - w / 2 - GAP / 2, y - h / 2 - GAP / 2,
|
||||
x + w / 2 + GAP / 2, y + h / 2 + GAP / 2))
|
||||
|
||||
# 每个区一个 y 游标:**一个组占一条完整的横带**,下一组从带子上沿重新开始。
|
||||
# 不这么做的话小件会回填到前面组的空隙里,功能级在 y 上互相咬 ——
|
||||
# 后面 L3 要按「哪一段是 12V、哪一段是 VO」切铜面就切不出来了。
|
||||
cursor = {k: v[1] for k, v in REGIONS.items()}
|
||||
unplaced = []
|
||||
for region, _name, refs in GROUPS:
|
||||
x0, _y0, x1, y1 = REGIONS[region]
|
||||
y0 = cursor[region]
|
||||
band_top = y0
|
||||
for ref in refs:
|
||||
if ref in pos:
|
||||
continue
|
||||
w, h = sz[ref]
|
||||
w += GAP
|
||||
h += GAP
|
||||
placed = False
|
||||
# 自下而上扫,步长 0.5;每一行自左而右扫
|
||||
yy = y0
|
||||
while yy + h <= y1 + 1e-9 and not placed:
|
||||
xx = x0
|
||||
while xx + w <= x1 + 1e-9:
|
||||
r = (xx, yy, xx + w, yy + h)
|
||||
if not overlaps(r, rects):
|
||||
rects.append(r)
|
||||
pos[ref] = (round(xx + w / 2, 3), round(yy + h / 2, 3), 0)
|
||||
band_top = max(band_top, yy + h)
|
||||
placed = True
|
||||
break
|
||||
xx += 0.5
|
||||
yy += 0.5
|
||||
if not placed:
|
||||
unplaced.append((ref, region))
|
||||
cursor[region] = band_top + 0.4
|
||||
return pos, rects, unplaced, sz
|
||||
|
||||
|
||||
def bands():
|
||||
u"""每个组占的 y 带 —— L3 切铜面按这个来,别手抄坐标。"""
|
||||
pos, _r, _u, sz = plan()
|
||||
out = []
|
||||
for region, name, refs in GROUPS:
|
||||
ys = [(pos[r][1] - sz[r][1] / 2, pos[r][1] + sz[r][1] / 2) for r in refs if r in pos]
|
||||
if ys:
|
||||
out.append((region, name, round(min(a for a, _ in ys), 2),
|
||||
round(max(b for _, b in ys), 2)))
|
||||
return out
|
||||
|
||||
|
||||
def selfcheck(pos, rects, unplaced, sz):
|
||||
bad = []
|
||||
if unplaced:
|
||||
bad.append(u'放不下: %s' % ', '.join(u'%s(%s)' % t for t in unplaced))
|
||||
d = json.load(io.open(BBOX, encoding='utf-8'))
|
||||
missing = sorted(set(d) - set(pos))
|
||||
if missing:
|
||||
bad.append(u'没安排位置: %s' % ' '.join(missing))
|
||||
# 重叠。⚠ K1~K4 例外:四个盘的位置是**主板定的**(实测最小间距 1.78 mm、
|
||||
# 盘 Ø1.5 ⇒ 净距 0.28),通用的 GAP=0.6 会把它们互判成重叠。
|
||||
# 它们之间改按「铜净距 ≥ 0.2」单独校核。
|
||||
import math
|
||||
keys = ['K1', 'K2', 'K3', 'K4']
|
||||
rmap = {}
|
||||
for ref in pos:
|
||||
if ref in keys:
|
||||
continue
|
||||
x, y, _ = pos[ref]
|
||||
w, h = sz[ref]
|
||||
rmap[ref] = (x - w / 2 - GAP / 2, y - h / 2 - GAP / 2,
|
||||
x + w / 2 + GAP / 2, y + h / 2 + GAP / 2)
|
||||
rl = sorted(rmap.items())
|
||||
for i in range(len(rl)):
|
||||
for j in range(i + 1, len(rl)):
|
||||
a, b = rl[i][1], rl[j][1]
|
||||
if a[0] < b[2] - 1e-6 and b[0] < a[2] - 1e-6 and \
|
||||
a[1] < b[3] - 1e-6 and b[1] < a[3] - 1e-6:
|
||||
bad.append(u'%s 和 %s 重叠' % (rl[i][0], rl[j][0]))
|
||||
PAD_D = 1.5
|
||||
for i in range(len(keys)):
|
||||
for j in range(i + 1, len(keys)):
|
||||
ax, ay, _ = pos[keys[i]]
|
||||
bx, by, _ = pos[keys[j]]
|
||||
gap = math.hypot(ax - bx, ay - by) - PAD_D
|
||||
if gap < 0.2:
|
||||
bad.append(u'%s/%s 铜净距 %.2f < 0.2' % (keys[i], keys[j], gap))
|
||||
# 全部在板内
|
||||
for ref, (x, y, _r) in pos.items():
|
||||
w, h = sz[ref]
|
||||
if ref == 'J1':
|
||||
continue
|
||||
if x - w / 2 < 0.5 or x + w / 2 > B.BW - 0.5 or \
|
||||
y - h / 2 < 0.5 or y + h / 2 > B.BH - 0.5:
|
||||
bad.append(u'%s 出板框: (%.2f,%.2f) %.1fx%.1f' % (ref, x, y, w, h))
|
||||
# 金手指区(y<11)除 J1 外一件不许有
|
||||
for ref, (x, y, _r) in pos.items():
|
||||
if ref == 'J1':
|
||||
continue
|
||||
if y - sz[ref][1] / 2 < 11.0:
|
||||
bad.append(u'%s 侵入金手指区(y=%.2f,插槽体要 ~9 mm)' % (ref, y))
|
||||
# 手机投影里只许 K1~K4
|
||||
px0, py0 = PHONE_ORG
|
||||
px1, py1 = px0 + PHONE_WH[0], py0 + PHONE_WH[1]
|
||||
for ref, (x, y, _r) in pos.items():
|
||||
if ref.startswith('K'):
|
||||
continue
|
||||
w, h = sz[ref]
|
||||
if x + w / 2 > px0 and x - w / 2 < px1 and y + h / 2 > py0 and y - h / 2 < py1:
|
||||
bad.append(u'%s 落进手机投影(螺柱间隙只有 1.0 mm)' % ref)
|
||||
# ⛔ 螺钉头避让:任何元件离主板定位孔中心都要 >= HOLE_KEEP。
|
||||
# M4c 第一版把这七个孔整个忘了 —— 高度预算和 K1~K4 的接触压力全靠它们。
|
||||
for hname, (hx, hy) in B.phone_holes().items():
|
||||
for ref, (x, y, _r) in pos.items():
|
||||
w, h = sz[ref]
|
||||
dx = max(abs(hx - x) - w / 2.0, 0.0)
|
||||
dy = max(abs(hy - y) - h / 2.0, 0.0)
|
||||
if math.hypot(dx, dy) < B.HOLE_KEEP:
|
||||
bad.append(u'%s 离定位孔 %s 只有 %.2f < %.1f(螺钉头压上去)'
|
||||
% (ref, hname, math.hypot(dx, dy), B.HOLE_KEEP))
|
||||
# C60 不许在手机底下(已由上一条覆盖),另外校核它确实在服务带/功率列
|
||||
cx, cy, _ = pos['C60']
|
||||
if not (12.0 <= cy - BODY['C60'][1] / 2 and cy + BODY['C60'][1] / 2 <= 28.0):
|
||||
bad.append(u'C60 不在服务带里(可用高度 11.7~13.7 只有这一带和功率列有)')
|
||||
return bad
|
||||
|
||||
|
||||
PUSH_JS = u"""
|
||||
const MIL = %s, P = %s, BOT = %s;
|
||||
await eda.dmt_EditorControl.openDocument('%s');
|
||||
await new Promise(r=>setTimeout(r,2500));
|
||||
const bot = new Set(BOT);
|
||||
let n = 0, miss = [];
|
||||
for (const c of await eda.pcb_PrimitiveComponent.getAll()) {
|
||||
const d = c.designator;
|
||||
if (!P[d]) { miss.push(d); continue; }
|
||||
const id = c.getState_PrimitiveId();
|
||||
await eda.pcb_PrimitiveComponent.modify(id, {layer: bot.has(d) ? 2 : 1});
|
||||
await eda.pcb_PrimitiveComponent.modify(id,
|
||||
{x: P[d][0]*MIL, y: P[d][1]*MIL, rotation: P[d][2]});
|
||||
n++;
|
||||
}
|
||||
await eda.pcb_Document.save();
|
||||
return {n, miss};
|
||||
"""
|
||||
|
||||
|
||||
def push(pos):
|
||||
r = run(PUSH_JS % (MIL, json.dumps(pos), json.dumps(BOTTOM), PCB), timeout=600)
|
||||
if not r.get('success'):
|
||||
sys.exit(json.dumps(r, ensure_ascii=False)[:600])
|
||||
print(u'已摆 %d 件;没找到位置的: %s' % (r['result']['n'], r['result']['miss'] or u'无'))
|
||||
|
||||
|
||||
def verify(pos, sz):
|
||||
r = run("await eda.dmt_EditorControl.openDocument('%s');"
|
||||
"await new Promise(r=>setTimeout(r,2000));"
|
||||
"return (await eda.pcb_PrimitiveComponent.getAll()).map("
|
||||
" c=>[c.designator, c.x, c.y, c.rotation, c.layer]);" % PCB, timeout=300)
|
||||
if not r.get('success'):
|
||||
sys.exit(json.dumps(r, ensure_ascii=False)[:600])
|
||||
got = {a[0]: (a[1] / MIL, a[2] / MIL, a[3], a[4]) for a in r['result']}
|
||||
bad = []
|
||||
for ref, (x, y, rot) in pos.items():
|
||||
if ref not in got:
|
||||
bad.append(u'板上没有 %s' % ref)
|
||||
continue
|
||||
gx, gy, grot, glay = got[ref]
|
||||
if abs(gx - x) > 0.02 or abs(gy - y) > 0.02:
|
||||
bad.append(u'%s 在 (%.2f,%.2f),应为 (%.2f,%.2f)' % (ref, gx, gy, x, y))
|
||||
want_lay = 2 if ref in BOTTOM else 1
|
||||
if glay != want_lay:
|
||||
bad.append(u'%s 在 layer %s,应为 %s' % (ref, glay, want_lay))
|
||||
print(u'读回 %d 件' % len(got))
|
||||
if bad:
|
||||
print(u'[X] %d 条不过:' % len(bad))
|
||||
for b in bad[:20]:
|
||||
print(u' ' + b)
|
||||
else:
|
||||
print(u'[OK] %d 件位置 / 层 全部对上' % len(got))
|
||||
return bad
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pos, rects, unplaced, sz = plan()
|
||||
bad = selfcheck(pos, rects, unplaced, sz)
|
||||
print(u'安排了 %d 件(含 J1)' % len(pos))
|
||||
for region, name, refs in GROUPS:
|
||||
xs = [pos[r] for r in refs if r in pos]
|
||||
if xs:
|
||||
print(u' %-11s %-22s x %5.1f~%5.1f y %5.1f~%5.1f %d 件'
|
||||
% (region, name, min(p[0] for p in xs), max(p[0] for p in xs),
|
||||
min(p[1] for p in xs), max(p[1] for p in xs), len(xs)))
|
||||
print(u' 锚定件: ' + ' '.join(u'%s(%.1f,%.1f)' % (k, pos[k][0], pos[k][1])
|
||||
for k in ('K1', 'K2', 'K3', 'K4', 'J5', 'J4',
|
||||
'C60', 'J2', 'J3')))
|
||||
if bad:
|
||||
print(u'[X] 自检不过 (%d):' % len(bad))
|
||||
for b in bad[:20]:
|
||||
print(u' ' + b)
|
||||
if '--push' in sys.argv:
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(u'[OK] 自检通过:无重叠 / 全在板内 / 金手指区干净 / 手机投影只有 K1~K4')
|
||||
if '--push' in sys.argv:
|
||||
push(pos)
|
||||
verify(pos, sz)
|
||||
elif '--verify' in sys.argv:
|
||||
verify(pos, sz)
|
||||
在新工单中引用
屏蔽一个用户