- name
- easyeda-draw-checking
- description
- Schematic verification and quality checking for EasyEDA Pro. Invoke when verifying layout, checking for overlaps, validating designators, or running design rule checks.
# 原理图检查
布局验证、重叠检测、位号检查、DRC 的完整指南。
> **⚠️ 重要**:所有检查必须通过 CLI 执行,禁止使用 curl 直接调用 API。
---
## 0. 快捷入口:CLI 一键检查
按绘制阶段使用不同的检查命令:
```bash
# 摆放一个模块后(每个模块摆放完必须执行)
./scripts/draw_cli.py verify-placement
# 连线一个模块后(每个模块连线完必须执行)
./scripts/draw_cli.py verify-wiring
# 全部模块完成后(最终提交前必须执行)
./scripts/draw_cli.py check-all
# DRC 不通过时查看逐项详情
./scripts/draw_cli.py drc --show-ui
```
---
## 1. DRC 检查(Design Rule Check)
### 1.0 DRC API 限制(重要)
> **⚠️ EasyEDA SCH_Drc API 只返回聚合统计**:
> ```javascript
> // check(true, false, true) 返回的是:
> [{ type: "fatalError", count: 1 }, { type: "warn", count: 11 }]
> ```
> **不返回每个错误的具体信息**(器件名、引脚号、错误描述等)。
>
> **查看逐项详细错误的唯一方式**:
> ```bash
> ./scripts/draw_cli.py drc --show-ui
> ```
> 这会呼出 EasyEDA 底部的 DRC 面板,面板中可看到每个错误的器件、引脚、具体描述。
### 1.1 基础 DRC
```javascript
// 仅知道是否通过
const passed = await eda.sch_Drc.check(true, false, false);
// passed: true = 通过, false = 有错误
// 获取详细错误统计
const errors = await eda.sch_Drc.check(true, false, true);
// 返回数组:[{ type: "fatalError"|"warn", count: N }]
```
### 1.2 DRC 问题排查流程
```
DRC 不通过
│
├─ fatalError(致命错误)
│ ├─ 未连接引脚 → 补全连线 或 添加 NC 标记
│ ├─ 网络短路 → 检查同一网络是否有不该连接的引脚
│ └─ 缺失网络标签 → 添加 NetFlag / NetPort
│
└─ warn(警告)
├─ 空网络导线 → 为导线设置网络名
├─ 未使用引脚 → 确认是否需要 NC 标记
└─ 去耦电容缺失 → 在 IC 电源引脚附近补充
```
### 1.3 使用 easyeda-draw 函数
```javascript
import utils from './scripts/draw-utils.js';
// 运行 DRC 详细检查
const result = await utils.runDrc();
// { passed: boolean, fatalErrors: number, warnings: number, details: Array }
```
---
## 2. 重复标签检测
### 2.1 检测原理
遍历所有 NetFlag/NetPort 图元,按坐标分组,找出同一位置放置多次的图元。
### 2.2 使用函数
```javascript
import utils from './scripts/draw-utils.js';
// 检测重复标签
const result = await utils.detectDuplicateLabels();
// { duplicates: [{ position, items: [{id, net, type}] }] }
// 自动删除重复标签(每组保留第一个)
const cleanup = await utils.cleanupDuplicateLabels();
// { deleted: N, details: [...] }
```
### 2.3 手动检测
```javascript
const allComps = await eda.sch_PrimitiveComponent.getAll();
const posMap = new Map();
for (const c of allComps || []) {
// 只检查 NetFlag/NetPort (designator 为空或 "?")
if (c.designator && c.designator !== '?') continue;
const key = `${c.x},${c.y}`;
if (!posMap.has(key)) posMap.set(key, []);
posMap.get(key).push({
id: c.primitiveId,
type: c.net ? 'NetFlag' : 'NetPort',
net: c.net,
rotation: c.rotation,
});
}
const duplicates = [];
for (const [pos, items] of posMap) {
if (items.length > 1) duplicates.push({ position: pos, items });
}
```
---
## 3. 导线网络名完整性检查
### 3.1 检测原理
遍历所有导线,检查网络名是否为空。
### 3.2 使用函数
```javascript
import utils from './scripts/draw-utils.js';
// 检测无网络名的导线
const result = await utils.checkWireNets();
// { total: N, noNet: M, wiresNoNet: [{id, line}] }
```
### 3.3 修复导线网络名
```javascript
// 逐个修复:用 getPrimitiveByPrimitiveId 获取导线 → toAsync → setState_Net → done
const w = await eda.sch_Primitive.getPrimitiveByPrimitiveId(wireId);
const aw = w.toAsync();
aw.setState_Net("NET_NAME");
aw.done();
```
---
## 4. 未连接引脚检测
### 4.1 检测原理
对每个真实器件(有设计位号的),检查各引脚是否被导线可达或连接了网络标签。
**改进点(与旧版本差异):**
| 特性 | 旧版本 | 新版本 |
|------|--------|--------|
| 4PIN 轻触开关 | 所有引脚都检查 | PIN1↔PIN2 内部短接,PIN3↔PIN4 内部短接,只接一对即可 |
| NC 标记 | 不识别 | 有 NC 标记的引脚不算未连接 |
| 分级 | 全部报 error | 区分 error (应连接) 和 warning (4PIN 冗余引脚,正常) |
### 4.2 使用 CLI
```bash
./scripts/draw_cli.py check-unconnected
# 返回: { errorCount, warningCount, errors: [...], warnings: [...] }
# errors: 应连接但未连接的引脚
# warnings: 4PIN 开关内部短接的冗余引脚 (正常)
```
### 4.3 输出解读
- `errorCount > 0` → 有引脚应连接但未连接,必须修复
- `warningCount > 0` 且 `errorCount = 0` → 4PIN 开关冗余引脚,正常无需修复
- 4PIN 开关判断: 器件有 4 个引脚且符号名包含 SWITCH/轻触/4PIN/SMD_4
---
## 4.5 短路检测(NEW)
### 原理
用并查集 (union-find) 合并物理上相连的连接点,如果同一个连通分量中有 2+ 个不同网络名 → 短路。
### 使用 CLI
```bash
./scripts/draw_cli.py check-shorts
# 返回: { shortCount, shorts: [{nets, points, wireCount, wireIds}], passed }
```
### 典型短路场景
- VCC 和 GND 通过导线直接连通
- ROW0 和 ROW1 通过同一连接点误连
- 两个不同信号通过 NetPort 标签放在同一坐标
## 4.7 冗余导线检测(NEW)
### 原理
检查同两个物理点之间是否有多条导线。冗余导线不会引起电气错误,但让原理图凌乱。
### 使用 CLI
```bash
./scripts/draw_cli.py check-redundant-wires
# 返回: { redundantCount, redundant: [{endpoints, count, wires}], passed }
```
---
## 5. 布局验证(重叠检测)
### 检测所有器件重叠
```javascript
import utils from './scripts/draw-utils.js';
const { overlaps } = await utils.checkOverlaps();
// overlaps: [{comp1, comp2}]
```
### 手动检测
```javascript
const compIds = await eda.sch_PrimitiveComponent.getAllPrimitiveId();
const bboxes = [];
for (const id of compIds) {
const comps = await eda.sch_PrimitiveComponent.get([id]);
if (!comps || comps.length === 0) continue;
const c = comps[0];
const pins = await eda.sch_PrimitiveComponent.getAllPinsByPrimitiveId(id);
if (pins && pins.length > 0) {
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
for (const p of pins) {
minX = Math.min(minX, p.x);
maxX = Math.max(maxX, p.x);
minY = Math.min(minY, p.y);
maxY = Math.max(maxY, p.y);
}
bboxes.push({
id,
designator: c.designator,
name: c.name,
bbox: [minX, minY, maxX, maxY],
area: (maxX - minX) * (maxY - minY),
});
}
}
// 检查重叠
const overlaps = [];
for (let i = 0; i < bboxes.length; i++) {
for (let j = i + 1; j < bboxes.length; j++) {
const a = bboxes[i].bbox;
const b = bboxes[j].bbox;
if (a[0] < b[2] && a[2] > b[0] && a[1] < b[3] && a[3] > b[1]) {
overlaps.push({
comp1: bboxes[i].designator,
comp2: bboxes[j].designator,
});
}
}
}
```
### 间距检查
```javascript
const minSpacing = 40; // 最小间距 40 单位 (0.4 inch)
const tooClose = [];
for (let i = 0; i < bboxes.length; i++) {
for (let j = i + 1; j < bboxes.length; j++) {
const a = bboxes[i].bbox;
const b = bboxes[j].bbox;
const ax = (a[0] + a[2]) / 2;
const ay = (a[1] + a[3]) / 2;
const bx = (b[0] + b[2]) / 2;
const by = (b[1] + b[3]) / 2;
const dist = Math.sqrt((ax - bx) ** 2 + (ay - by) ** 2);
if (dist < minSpacing) {
tooClose.push({
comp1: bboxes[i].designator,
comp2: bboxes[j].designator,
distance: dist,
minRequired: minSpacing,
});
}
}
}
```
---
## 6. 位号检查
### 检查缺失或异常位号
```javascript
const compIds = await eda.sch_PrimitiveComponent.getAllPrimitiveId();
const issues = [];
for (const id of compIds) {
const comps = await eda.sch_PrimitiveComponent.get([id]);
if (!comps || comps.length === 0) continue;
const c = comps[0];
// 检查位号是否存在
if (!c.designator) {
issues.push({ id, type: "missing_designator", name: c.name });
continue;
}
// 检查位号是否包含 ?(未分配)
if (c.designator.includes("?")) {
issues.push({ id, type: "unassigned_designator", designator: c.designator, name: c.name });
}
// 检查位号格式(字母+数字)
if (!/^[A-Z]+\d+$/.test(c.designator)) {
issues.push({ id, type: "invalid_format", designator: c.designator, name: c.name });
}
}
```
### 位号重复检查
```javascript
const designatorMap = new Map();
const duplicates = [];
for (const id of compIds) {
const comps = await eda.sch_PrimitiveComponent.get([id]);
const c = comps[0];
if (c.designator) {
if (designatorMap.has(c.designator)) {
duplicates.push({
designator: c.designator,
ids: [designatorMap.get(c.designator), id],
});
} else {
designatorMap.set(c.designator, id);
}
}
}
```
---
## 7. 文本标注检查
### 检测重复文本
```javascript
const textIds = await eda.sch_PrimitiveText.getAllPrimitiveId();
const textMap = new Map();
for (const tid of textIds) {
const t = await eda.sch_PrimitiveText.get([tid]);
if (t && t[0]) {
const key = `${t[0].x},${t[0].y}`;
if (!textMap.has(key)) textMap.set(key, []);
textMap.get(key).push({ id: tid, content: t[0].content, fontSize: t[0].fontSize });
}
}
const duplicateTexts = [];
for (const [pos, items] of textMap) {
if (items.length > 1) duplicateTexts.push({ position: pos, items });
}
```
### 检查文本位置是否合理
```javascript
// 检查文本是否与器件重叠
const textOverlaps = [];
for (const t of texts) {
for (const b of bboxes) {
if (t.x > b.bbox[0] && t.x < b.bbox[2] && t.y > b.bbox[1] && t.y < b.bbox[3]) {
textOverlaps.push({
text: t.content,
component: b.designator,
});
}
}
}
```
---
## 8. 导线穿过器件检测(NEW)
### 8.1 检测原理
遍历所有导线的每个线段,与所有真实器件的 BBox(边界框)做交叉检测:
- 若导线端点在器件 BBox 内部(排除 ±3 容差的引脚边缘)→ 报出
- 若线段与器件 BBox 相交,且交叉长度 > 5 单位 → 报出
这能发现像 R7、R8 被 ROW 网络导线直接穿过的问题。
### 8.2 使用 CLI
```bash
./scripts/draw_cli.py check-wire-through
```
### 8.3 使用函数
```javascript
import utils from './scripts/draw-utils.js';
const result = await utils.checkWireThroughComponent();
Auf GitHub ansehen