v1.6.1 修复 BUG-007 PinList→PinMAP 生成布局方向(改用 Layout B,A1 标题与上边 Number 同行)
This commit is contained in:
23
CHANGELOG.md
23
CHANGELOG.md
@@ -1,5 +1,28 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v1.6.1] - 2026-06-12
|
||||||
|
|
||||||
|
### 🐛 Bug 修复
|
||||||
|
|
||||||
|
#### BUG-007 【高】PinList→PinMAP 生成布局方向错误(应为 Layout B)
|
||||||
|
|
||||||
|
- **根因**:`pinmap_layout.py` 使用 Layout A(上边 Name 在 Number 之前,A1 独占行),但用户期望 Layout B(A1 标题与上边 Number 同行,Number 在 Name 之前)
|
||||||
|
- **修复**:
|
||||||
|
- 上边 Number 移至 row 0(与 A1 标题同行),col 从 2 开始(B 列留空)
|
||||||
|
- 上边 Name 移至 row 1
|
||||||
|
- 左右边整体上移 1 行(从 row 3→row 2 开始)
|
||||||
|
- 下边整体上移 1 行(从 row 18-19→row 17-18)
|
||||||
|
- 生成输出与用户提供的正确 CSV 布局完全一致
|
||||||
|
- A1 支持多行文本(换行符自动保留)
|
||||||
|
|
||||||
|
### 🔧 修改文件
|
||||||
|
- `Code/src/pinmap_layout.py` — 坐标公式全部更新为 Layout B
|
||||||
|
- `Code/src/test_pinmap.py` — 5 组测试数据/断言更新
|
||||||
|
|
||||||
|
### ✅ 测试
|
||||||
|
- 全部 23 个测试通过
|
||||||
|
- QFN60 生成结果与用户期望的 CSV 结构一致
|
||||||
|
|
||||||
## [v1.6.0] - 2026-06-12
|
## [v1.6.0] - 2026-06-12
|
||||||
|
|
||||||
### 🐛 Bug 修复
|
### 🐛 Bug 修复
|
||||||
|
|||||||
@@ -103,31 +103,31 @@ def calculate_layout(
|
|||||||
|
|
||||||
top_pins = entries[idx: idx + top_count]
|
top_pins = entries[idx: idx + top_count]
|
||||||
|
|
||||||
# ── 计算单元格坐标(BUG-007 修复:上边 Name 在 row 2,标题独占 row 0)──
|
# ── 计算单元格坐标(BUG-007 修复:Layout B 坐标体系)──
|
||||||
#
|
#
|
||||||
# 网格坐标体系(0-based):
|
# 网格坐标体系(0-based):
|
||||||
# 第 0 行完全由标题(A1 合并单元格,不包含引脚数据)独占
|
# 第 0 行是上边引脚序号,第 1 行是上边引脚 PinName
|
||||||
# 第 1 行是上边引脚序号,第 2 行是上边引脚 PinName
|
# B 列(col 1)为空白列,保持视觉分隔
|
||||||
# 从第 3 行开始是左/下/右边引脚
|
# 从第 2 行开始是左/下/右边引脚
|
||||||
#
|
#
|
||||||
# 左边: Number (r, 0) r ∈ [3, rows+2] Name (r, 1)
|
# 左边: Number (r, 0) r ∈ [2, rows+1] Name (r, 1)
|
||||||
# 下边: Number (rows+4, c) c ∈ [2, cols+1] Name (rows+3, c)
|
# 下边: Number (rows+3, c) c ∈ [2, cols+1] Name (rows+2, c)
|
||||||
# 右边: Number (r, cols+3) r ∈ [rows+2, 3] Name (r, cols+2) 逆序
|
# 右边: Number (r, cols+3) r ∈ [rows+1, 2] Name (r, cols+2) 逆序
|
||||||
# 上边: Number (1, c) c ∈ [cols+1, 2] Name (2, c) 逆序
|
# 上边: Number (0, c) c ∈ [cols+1, 2] Name (1, c) 逆序
|
||||||
#
|
#
|
||||||
# Pin1: Number (3,0) = A4, Name (3,1) = B4 — 左上角
|
# Pin1: Number (2,0) = A3, Name (2,1) = B3 — 左上角
|
||||||
|
|
||||||
# 左边:从上到下 (rows 个)
|
# 左边:从上到下 (rows 个)
|
||||||
left_cells = [(r, 0) for r in range(3, rows + 3)]
|
left_cells = [(r, 0) for r in range(2, rows + 2)]
|
||||||
|
|
||||||
# 下边:从左到右 (cols 个),Number 在最底行 rows+4
|
# 下边:从左到右 (cols 个),Number 在最底行 rows+3
|
||||||
bottom_cells = [(rows + 4, c) for c in range(2, cols + 2)]
|
bottom_cells = [(rows + 3, c) for c in range(2, cols + 2)]
|
||||||
|
|
||||||
# 右边:从下到上 (rows 个),Number 在 cols+3 列(右扩三列:上边偏移1 + 间距1)
|
# 右边:从下到上 (rows 个),Number 在 cols+3 列(右扩三列:上边偏移1 + 间距1)
|
||||||
right_cells = [(r, cols + 3) for r in range(rows + 2, 2, -1)]
|
right_cells = [(r, cols + 3) for r in range(rows + 1, 1, -1)]
|
||||||
|
|
||||||
# 上边:从右到左 (cols 个),从 col 2 开始(预留左边 Name 列 + 空列)
|
# 上边:从右到左 (cols 个),从 col 2 开始(预留 B 列空白)
|
||||||
top_cells = [(1, c) for c in range(cols + 1, 1, -1)]
|
top_cells = [(0, c) for c in range(cols + 1, 1, -1)]
|
||||||
|
|
||||||
# ── 构建 EdgePins ─────────────────────────────────────────────
|
# ── 构建 EdgePins ─────────────────────────────────────────────
|
||||||
def _make_edge(edge_name: str, pin_list: list[PinListEntry],
|
def _make_edge(edge_name: str, pin_list: list[PinListEntry],
|
||||||
@@ -148,8 +148,7 @@ def get_name_cell(num_cell: tuple[int, int], edge_name: str,
|
|||||||
"""
|
"""
|
||||||
根据序号单元格坐标和边名称,计算对应的 PinName 单元格坐标。
|
根据序号单元格坐标和边名称,计算对应的 PinName 单元格坐标。
|
||||||
|
|
||||||
v1.5.5: 上边 Name 在 Number 上方 (0, c),即独立一行。
|
Layout B: 上边 Number 在 row 0, Name 在 row 1 (Name 在 Number 下方).
|
||||||
不再需要角点例外——整个上边 Name 在独立的 row 0。
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@@ -158,7 +157,7 @@ def get_name_cell(num_cell: tuple[int, int], edge_name: str,
|
|||||||
edge_name : str
|
edge_name : str
|
||||||
"left" | "bottom" | "right" | "top"
|
"left" | "bottom" | "right" | "top"
|
||||||
cols : int
|
cols : int
|
||||||
网格列数(v1.5.5 上边不再需要角点例外,参数保留以兼容调用)
|
网格列数(参数保留以兼容调用)
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
@@ -173,9 +172,7 @@ def get_name_cell(num_cell: tuple[int, int], edge_name: str,
|
|||||||
elif edge_name == "right":
|
elif edge_name == "right":
|
||||||
return (r, c - 1) # Name 在 Number 左侧 (col cols)
|
return (r, c - 1) # Name 在 Number 左侧 (col cols)
|
||||||
elif edge_name == "top":
|
elif edge_name == "top":
|
||||||
# Top Number 在 (1, c), c ∈ [cols..1]
|
# Layout B: Number 在 (0, c), Name 在 (1, c)
|
||||||
# Name 在 Number 下方 (2, c)
|
return (1, c) # Name 在 Number 下方(row 1)
|
||||||
# row 0 完全由标题行(A1 合并单元格)独占
|
|
||||||
return (2, c) # Name 在 Number 下方(row 2)
|
|
||||||
else:
|
else:
|
||||||
raise LayoutError(f"未知的边名称: {edge_name}")
|
raise LayoutError(f"未知的边名称: {edge_name}")
|
||||||
|
|||||||
@@ -16,36 +16,37 @@ from pinlist_generator import generate_pinlist
|
|||||||
from utils import rc_to_cell_ref
|
from utils import rc_to_cell_ref
|
||||||
|
|
||||||
|
|
||||||
# ── 4x4 example (BUG-007 fixed layout) ─────────────────────────
|
# ── 4x4 example (BUG-007 Layout B) ─────────────────────────────
|
||||||
# Layout: rows=4, cols=4, 16 pins
|
# Layout: rows=4, cols=4, 16 pins
|
||||||
# Title: A1 "QFP-44" (row 0 only)
|
# Title: A1 "QFP-44" (row 0, col 0)
|
||||||
# Top: Number row 1 (B2..E2), Name row 2 (B3..E3)
|
# Top: Number row 0, cols 2..5 (C1..F1), Name row 1, cols 2..5 (C2..F2)
|
||||||
# Left: Number A4..A7 (rows 3..6), Name B4..B7 (rows 3..6)
|
# Left: Number A3..A6 (rows 2..5), Name B3..B6 (rows 2..5)
|
||||||
# Bottom: Name B8..E8 (row 7), Number B9..E9 (row 8)
|
# Bottom: Name C7..F7 (row 6), Number C8..F8 (row 7)
|
||||||
# Right: Number F7..F4 (rows 6..3), Name E7..E4 (rows 6..3)
|
# Right: Number G6..G3 (rows 5..2), Name F6..F3 (rows 5..2)
|
||||||
# A1: "QFP-44" = package info (title, merged, row 0 only)
|
# A1: "QFP-44" = package info (title, row 0 only)
|
||||||
|
# B1: blank (visual separator)
|
||||||
#
|
#
|
||||||
# Pin1: Number A4=(3,0), Name B4=(3,1)
|
# Pin1: Number A3=(2,0), Name B3=(2,1)
|
||||||
|
|
||||||
cells_4x4 = {
|
cells_4x4 = {
|
||||||
(0, 0): "QFP-44",
|
(0, 0): "QFP-44",
|
||||||
# top edge Numbers (row 1, cols 1..4)
|
# top edge Numbers (row 0, cols 2..5)
|
||||||
(1, 1): "16", (1, 2): "15", (1, 3): "14", (1, 4): "13",
|
(0, 2): "16", (0, 3): "15", (0, 4): "14", (0, 5): "13",
|
||||||
# top edge Names (row 2, cols 1..4)
|
# top edge Names (row 1, cols 2..5)
|
||||||
(2, 1): "Pin16", (2, 2): "Pin15", (2, 3): "Pin14", (2, 4): "Pin13",
|
(1, 2): "Pin16", (1, 3): "Pin15", (1, 4): "Pin14", (1, 5): "Pin13",
|
||||||
# left edge (rows 3..6, cols 0..1)
|
# left edge (rows 2..5, cols 0..1)
|
||||||
(3, 0): "1", (3, 1): "Pin1",
|
(2, 0): "1", (2, 1): "Pin1",
|
||||||
(4, 0): "2", (4, 1): "Pin2",
|
(3, 0): "2", (3, 1): "Pin2",
|
||||||
(5, 0): "3", (5, 1): "Pin3",
|
(4, 0): "3", (4, 1): "Pin3",
|
||||||
(6, 0): "4", (6, 1): "Pin4",
|
(5, 0): "4", (5, 1): "Pin4",
|
||||||
# bottom edge (rows 7..8, cols 1..4)
|
# bottom edge (rows 6..7, cols 2..5)
|
||||||
(7, 1): "Pin5", (7, 2): "Pin6", (7, 3): "Pin7", (7, 4): "Pin8",
|
(6, 2): "Pin5", (6, 3): "Pin6", (6, 4): "Pin7", (6, 5): "Pin8",
|
||||||
(8, 1): "5", (8, 2): "6", (8, 3): "7", (8, 4): "8",
|
(7, 2): "5", (7, 3): "6", (7, 4): "7", (7, 5): "8",
|
||||||
# right edge (rows 6..3, cols 4..5)
|
# right edge (rows 5..2, cols 6..7)
|
||||||
(6, 4): "Pin9", (6, 5): "9",
|
(5, 6): "Pin9", (5, 7): "9",
|
||||||
(5, 4): "Pin10", (5, 5): "10",
|
(4, 6): "Pin10", (4, 7): "10",
|
||||||
(4, 4): "Pin11", (4, 5): "11",
|
(3, 6): "Pin11", (3, 7): "11",
|
||||||
(3, 4): "Pin12", (3, 5): "12",
|
(2, 6): "Pin12", (2, 7): "12",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -177,26 +178,26 @@ def test_rectangular_parse():
|
|||||||
|
|
||||||
def test_12pin_square():
|
def test_12pin_square():
|
||||||
"""A 3×3 square: 12 pins (3 pins per edge).
|
"""A 3×3 square: 12 pins (3 pins per edge).
|
||||||
Using BUG-007 fixed layout: top numbers at row 1, top names at row 2.
|
Using Layout B: top numbers at row 0, top names at row 1.
|
||||||
left: 1,2,3 bottom: 4,5,6 right: 7,8,9 top: 12,11,10
|
left: 1,2,3 bottom: 4,5,6 right: 7,8,9 top: 12,11,10
|
||||||
"""
|
"""
|
||||||
cells = {
|
cells = {
|
||||||
(0, 0): "QFP-12",
|
(0, 0): "QFP-12",
|
||||||
# top Numbers (row 1, cols 1..3)
|
# top Numbers (row 0, cols 2..4)
|
||||||
(1, 1): "12", (1, 2): "11", (1, 3): "10",
|
(0, 2): "12", (0, 3): "11", (0, 4): "10",
|
||||||
# top Names (row 2, cols 1..3)
|
# top Names (row 1, cols 2..4)
|
||||||
(2, 1): "RST", (2, 2): "VSS", (2, 3): "VDD",
|
(1, 2): "RST", (1, 3): "VSS", (1, 4): "VDD",
|
||||||
# left (col 0) — names at col 1
|
# left (col 0) — names at col 1, rows 2..4
|
||||||
(3, 0): "1", (3, 1): "VCC",
|
(2, 0): "1", (2, 1): "VCC",
|
||||||
(4, 0): "2", (4, 1): "GND",
|
(3, 0): "2", (3, 1): "GND",
|
||||||
(5, 0): "3", (5, 1): "IN1",
|
(4, 0): "3", (4, 1): "IN1",
|
||||||
# bottom Names (row 6), Numbers (row 7)
|
# bottom Names (row 5), Numbers (row 6), cols 2..4
|
||||||
(6, 1): "IN2", (6, 2): "OUT1", (6, 3): "OUT2",
|
(5, 2): "IN2", (5, 3): "OUT1", (5, 4): "OUT2",
|
||||||
(7, 1): "4", (7, 2): "5", (7, 3): "6",
|
(6, 2): "4", (6, 3): "5", (6, 4): "6",
|
||||||
# right (col 4 Number, col 3 Name) — bottom to top: 7, 8, 9
|
# right (col 6 Number, col 5 Name) — bottom to top: 7, 8, 9
|
||||||
(5, 3): "CTL1", (5, 4): "7",
|
(4, 5): "CTL1", (4, 6): "7",
|
||||||
(4, 3): "CTL2", (4, 4): "8",
|
(3, 5): "CTL2", (3, 6): "8",
|
||||||
(3, 3): "NC1", (3, 4): "9",
|
(2, 5): "NC1", (2, 6): "9",
|
||||||
}
|
}
|
||||||
pm = parse_pinmap(cells)
|
pm = parse_pinmap(cells)
|
||||||
assert len(pm.pins) == 12, f"expected 12, got {len(pm.pins)}"
|
assert len(pm.pins) == 12, f"expected 12, got {len(pm.pins)}"
|
||||||
@@ -261,40 +262,40 @@ def test_f012_pinname_position():
|
|||||||
output_path=None,
|
output_path=None,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 3. 检查单元格位置 (BUG-007 final) ──────────────────
|
# ── 3. 检查单元格位置 (BUG-007 Layout B) ────────────────
|
||||||
# 5×5: rows=5, cols=5, 20 pins
|
# 5×5: rows=5, cols=5, 20 pins
|
||||||
# 上边: Number (1, 6..2), Name (2, 6..2)
|
# 上边: Number (0, 6..2), Name (1, 6..2)
|
||||||
# 左边: Number (3..7, 0), Name (3..7, 1)
|
# 左边: Number (2..6, 0), Name (2..6, 1)
|
||||||
# 下边: Name (8, 2..6), Number (9, 2..6)
|
# 下边: Name (7, 2..6), Number (8, 2..6)
|
||||||
# 右边: Number (7..3, 8), Name (7..3, 7)
|
# 右边: Number (6..2, 8), Name (6..2, 7)
|
||||||
|
|
||||||
# ── 3a. 验证上边 Name 位置 (2, 2..cols+1) ──────────────
|
# ── 3a. 验证上边 Name 位置 (1, 2..cols+1) ──────────────
|
||||||
for c in range(2, cols + 2):
|
for c in range(2, cols + 2):
|
||||||
num_ref = rc_to_cell_ref(1, c) # Number at row 1
|
num_ref = rc_to_cell_ref(0, c) # Number at row 0
|
||||||
name_ref = rc_to_cell_ref(2, c) # Name at row 2
|
name_ref = rc_to_cell_ref(1, c) # Name at row 1
|
||||||
assert num_ref in data, f"上边 Number {num_ref} 缺失"
|
assert num_ref in data, f"上边 Number {num_ref} 缺失"
|
||||||
assert name_ref in data, (
|
assert name_ref in data, (
|
||||||
f"上边 Name 应在 {name_ref} (row 2), 但未找到。Number 在 {num_ref}"
|
f"上边 Name 应在 {name_ref} (row 1), 但未找到。Number 在 {num_ref}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 3b. 验证下边 Name 位置 (rows+3=8, 2..cols+1) ─────
|
# ── 3b. 验证下边 Name 位置 (rows+2=7, 2..cols+1) ─────
|
||||||
for c in range(2, cols + 2):
|
for c in range(2, cols + 2):
|
||||||
num_ref = rc_to_cell_ref(rows + 4, c) # Number at row 9
|
num_ref = rc_to_cell_ref(rows + 3, c) # Number at row 8
|
||||||
name_ref = rc_to_cell_ref(rows + 3, c) # Name at row 8
|
name_ref = rc_to_cell_ref(rows + 2, c) # Name at row 7
|
||||||
assert num_ref in data, f"下边 Number {num_ref} 缺失"
|
assert num_ref in data, f"下边 Number {num_ref} 缺失"
|
||||||
assert name_ref in data, (
|
assert name_ref in data, (
|
||||||
f"下边 Name 应在 {name_ref} (rows+3), 但未找到。Number 在 {num_ref}"
|
f"下边 Name 应在 {name_ref} (rows+2), 但未找到。Number 在 {num_ref}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 3c. 验证左边 Name 位置 (3..7, 1) ───────────────────
|
# ── 3c. 验证左边 Name 位置 (2..6, 1) ───────────────────
|
||||||
for r in range(3, rows + 3):
|
for r in range(2, rows + 2):
|
||||||
num_ref = rc_to_cell_ref(r, 0)
|
num_ref = rc_to_cell_ref(r, 0)
|
||||||
name_ref = rc_to_cell_ref(r, 1)
|
name_ref = rc_to_cell_ref(r, 1)
|
||||||
assert num_ref in data, f"左边 Number {num_ref} 缺失"
|
assert num_ref in data, f"左边 Number {num_ref} 缺失"
|
||||||
assert name_ref in data, f"左边 Name {name_ref} 缺失"
|
assert name_ref in data, f"左边 Name {name_ref} 缺失"
|
||||||
|
|
||||||
# ── 3d. 验证右边 Name 位置 (7..3, 7) ────────────────────
|
# ── 3d. 验证右边 Name 位置 (6..2, 7) ────────────────────
|
||||||
for r in range(rows + 2, 2, -1):
|
for r in range(rows + 1, 1, -1):
|
||||||
num_ref = rc_to_cell_ref(r, cols + 3)
|
num_ref = rc_to_cell_ref(r, cols + 3)
|
||||||
name_ref = rc_to_cell_ref(r, cols + 2)
|
name_ref = rc_to_cell_ref(r, cols + 2)
|
||||||
assert num_ref in data, f"右边 Number {num_ref} 缺失"
|
assert num_ref in data, f"右边 Number {num_ref} 缺失"
|
||||||
@@ -548,11 +549,14 @@ QFN60_COLS = 15
|
|||||||
|
|
||||||
|
|
||||||
def test_f017_qfn60_map_to_list():
|
def test_f017_qfn60_map_to_list():
|
||||||
"""F017: 解析 Layout B(用户真实布局:Number 在 Name 上方)QFN60 PinMAP → PinList。
|
"""F017: 解析 Layout B(用户真实布局)QFN60 PinMAP → PinList。
|
||||||
|
|
||||||
15×15 网格,60 引脚环形布局。
|
15×15 网格,60 引脚环形布局。
|
||||||
布局 B:Top Number 在 row 1,Top Name 在 row 2。
|
布局 B(BUG-007):
|
||||||
Left 从 row 3 开始(避开 Top Name 行),Bottom/Right 按标准公式。
|
Top Number 在 row 0,Top Name 在 row 1
|
||||||
|
Left 从 row 2 开始
|
||||||
|
Bottom Name 在 row 17,Bottom Number 在 row 18
|
||||||
|
Right 从 row 16 到 row 2
|
||||||
|
|
||||||
验收标准:
|
验收标准:
|
||||||
- 解析出 60 个引脚
|
- 解析出 60 个引脚
|
||||||
@@ -565,35 +569,33 @@ def test_f017_qfn60_map_to_list():
|
|||||||
cells: dict[tuple[int, int], str] = {}
|
cells: dict[tuple[int, int], str] = {}
|
||||||
cells[(0, 0)] = QFN60_PACKAGE_INFO
|
cells[(0, 0)] = QFN60_PACKAGE_INFO
|
||||||
|
|
||||||
# Top: Number at row 1, Name at row 2 (Layout B)
|
# Top: Number at row 0, Name at row 1 (Layout B)
|
||||||
# 逆时针:Pin46 在右 (col 15),Pin60 在左 (col 1)
|
# 从右到左:Pin60 在右 (col 16),Pin46 在左 (col 2)
|
||||||
for i, c in enumerate(range(QFN60_COLS, 0, -1)):
|
for i, c in enumerate(range(QFN60_COLS + 1, 1, -1)):
|
||||||
pin_num = 46 + i
|
pin_num = 46 + i
|
||||||
cells[(1, c)] = str(pin_num) # Top Number
|
cells[(0, c)] = str(pin_num) # Top Number
|
||||||
cells[(2, c)] = f"Pin{pin_num}" # Top Name
|
cells[(1, c)] = f"Pin{pin_num}" # Top Name
|
||||||
|
|
||||||
# Left: Pin1..Pin15, Number at col 0, Name at col 1
|
# Left: Pin1..Pin15, Number at col 0, Name at col 1
|
||||||
# 从 row 3 开始,避免与 Top Name (row 2) 重叠
|
# 从 row 2 开始
|
||||||
for i in range(QFN60_ROWS):
|
for i in range(QFN60_ROWS):
|
||||||
r = 3 + i
|
r = 2 + i
|
||||||
cells[(r, 0)] = str(i + 1)
|
cells[(r, 0)] = str(i + 1)
|
||||||
cells[(r, 1)] = f"Pin{i + 1}"
|
cells[(r, 1)] = f"Pin{i + 1}"
|
||||||
|
|
||||||
# Right: Pin31..Pin45, Number at col 16, Name at col 15
|
# Right: Pin31..Pin45, Number at col 18, Name at col 17
|
||||||
# 从下往上 (row 17→3)
|
# 从下往上 (row 16→2)
|
||||||
for i in range(QFN60_ROWS):
|
for i in range(QFN60_ROWS):
|
||||||
r = QFN60_ROWS + 2 - i # 17, 16, ..., 3
|
r = QFN60_ROWS + 1 - i # 16, 15, ..., 2
|
||||||
cells[(r, QFN60_COLS + 1)] = str(31 + i)
|
cells[(r, QFN60_COLS + 3)] = str(31 + i)
|
||||||
cells[(r, QFN60_COLS)] = f"Pin{31 + i}"
|
cells[(r, QFN60_COLS + 2)] = f"Pin{31 + i}"
|
||||||
|
|
||||||
# Bottom: Pin16..Pin30
|
# Bottom: Pin16..Pin30
|
||||||
# Name at row 18 (rows+3?), Number at row 19 (rows+4?)
|
# Name at row 17 (rows+2), Number at row 18 (rows+3)
|
||||||
# Actually standard: Name at rows+2=17, Number at rows+3=18
|
|
||||||
# But in user layout, left runs through row 17 and bottom is below that
|
|
||||||
for i in range(QFN60_COLS):
|
for i in range(QFN60_COLS):
|
||||||
c = 1 + i
|
c = 2 + i
|
||||||
cells[(QFN60_ROWS + 3, c)] = f"Pin{16 + i}" # Name: row 18
|
cells[(QFN60_ROWS + 2, c)] = f"Pin{16 + i}" # Name: row 17
|
||||||
cells[(QFN60_ROWS + 4, c)] = str(16 + i) # Number: row 19
|
cells[(QFN60_ROWS + 3, c)] = str(16 + i) # Number: row 18
|
||||||
|
|
||||||
# ── 解析 ──────────────────────────────────────────────
|
# ── 解析 ──────────────────────────────────────────────
|
||||||
pm = parse_pinmap(cells)
|
pm = parse_pinmap(cells)
|
||||||
@@ -710,7 +712,7 @@ def test_f016_qfn60_list_to_map():
|
|||||||
- A1 = "QFN60"
|
- A1 = "QFN60"
|
||||||
- 所有 60 个引脚都有 Name 和 Number 单元格
|
- 所有 60 个引脚都有 Name 和 Number 单元格
|
||||||
- 四边布局正确(left/bottom/right/top 各 15 个)
|
- 四边布局正确(left/bottom/right/top 各 15 个)
|
||||||
- 标题行独立(A1 独占 row 0,不混入引脚数据)
|
- Layout B: Top Number 在 row 0,Top Name 在 row 1
|
||||||
"""
|
"""
|
||||||
data = generate_pinmap(
|
data = generate_pinmap(
|
||||||
entries=QFN60_PINLIST_ENTRIES,
|
entries=QFN60_PINLIST_ENTRIES,
|
||||||
@@ -732,15 +734,17 @@ def test_f016_qfn60_list_to_map():
|
|||||||
f"A1 应为 {QFN60_PACKAGE_INFO},实际: {data.get('A1')}"
|
f"A1 应为 {QFN60_PACKAGE_INFO},实际: {data.get('A1')}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 验证标题行独立(BUG-007)─────────────────────────
|
# ── 验证 row 0 包含 A1 标题和上边 Number ────────────
|
||||||
# A1 是唯一在 row 0 的单元格,无引脚数据混入
|
# Layout B: row 0 = A1 标题 + Top Number 单元格(col 2..16)
|
||||||
from utils import cell_ref_to_rc
|
from utils import cell_ref_to_rc
|
||||||
for ref, val in data.items():
|
for ref, val in data.items():
|
||||||
r, c = cell_ref_to_rc(ref)
|
r, c = cell_ref_to_rc(ref)
|
||||||
if r == 0:
|
if r == 0:
|
||||||
assert ref == "A1", (
|
# A1 是标题,其他 row 0 单元格是 Top Number
|
||||||
f"row 0 应只有 A1 标题,但发现 {ref}={val}"
|
if ref != "A1":
|
||||||
)
|
assert val.isdigit() or "/" in val, (
|
||||||
|
f"row 0 非 A1 单元格 {ref} 应为 Number,实际: {val}"
|
||||||
|
)
|
||||||
|
|
||||||
# ── 验证所有 60 个引脚都有 Name 和 Number ───────────
|
# ── 验证所有 60 个引脚都有 Name 和 Number ───────────
|
||||||
name_cells = {}
|
name_cells = {}
|
||||||
@@ -777,55 +781,55 @@ def test_f016_qfn60_list_to_map():
|
|||||||
f"Number 单元格应覆盖 1..60\n缺失: {sorted(set(range(1,61)) - all_numbers)}"
|
f"Number 单元格应覆盖 1..60\n缺失: {sorted(set(range(1,61)) - all_numbers)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── 验证四边布局(BUG-007 final layout)──────────────
|
# ── 验证四边布局(BUG-007 Layout B)──────────────────
|
||||||
# BUG-007 最终布局:
|
# Layout B:
|
||||||
# Title: A1 (row 0 only)
|
# Title: A1 (row 0 only)
|
||||||
# Top Numbers: (1, 2..16)
|
# Top Numbers: (0, 2..16)
|
||||||
# Top Names: (2, 2..16)
|
# Top Names: (1, 2..16)
|
||||||
# Left: Number (3..17, 0), Name (3..17, 1)
|
# Left: Number (2..16, 0), Name (2..16, 1)
|
||||||
# Bottom: Name (18, 2..16), Number (19, 2..16)
|
# Bottom: Name (17, 2..16), Number (18, 2..16)
|
||||||
# Right: Number (17..3, 18), Name (17..3, 17)
|
# Right: Number (16..2, 18), Name (16..2, 17)
|
||||||
|
|
||||||
# Top Numbers 在 row 1, col 2..16
|
# Top Numbers 在 row 0, col 2..16
|
||||||
for c in range(2, QFN60_COLS + 2):
|
for c in range(2, QFN60_COLS + 2):
|
||||||
ref = rc_to_cell_ref(1, c)
|
ref = rc_to_cell_ref(0, c)
|
||||||
assert ref in data, f"Top Number {ref} 缺失"
|
assert ref in data, f"Top Number {ref} 缺失"
|
||||||
|
|
||||||
# Top Names 在 row 2, col 2..16
|
# Top Names 在 row 1, col 2..16
|
||||||
for c in range(2, QFN60_COLS + 2):
|
for c in range(2, QFN60_COLS + 2):
|
||||||
ref = rc_to_cell_ref(2, c)
|
ref = rc_to_cell_ref(1, c)
|
||||||
assert ref in data, f"Top Name {ref} 缺失"
|
assert ref in data, f"Top Name {ref} 缺失"
|
||||||
assert data[ref].startswith("Pin"), f"Top Name {ref} = {data[ref]}"
|
assert data[ref].startswith("Pin"), f"Top Name {ref} = {data[ref]}"
|
||||||
|
|
||||||
# Left Numbers 在 col 0, rows 3..17
|
# Left Numbers 在 col 0, rows 2..16
|
||||||
for r in range(3, QFN60_ROWS + 3):
|
for r in range(2, QFN60_ROWS + 2):
|
||||||
ref = rc_to_cell_ref(r, 0)
|
ref = rc_to_cell_ref(r, 0)
|
||||||
assert ref in data, f"Left Number {ref} 缺失"
|
assert ref in data, f"Left Number {ref} 缺失"
|
||||||
|
|
||||||
# Left Names 在 col 1, rows 3..17
|
# Left Names 在 col 1, rows 2..16
|
||||||
for r in range(3, QFN60_ROWS + 3):
|
for r in range(2, QFN60_ROWS + 2):
|
||||||
ref = rc_to_cell_ref(r, 1)
|
ref = rc_to_cell_ref(r, 1)
|
||||||
assert ref in data, f"Left Name {ref} 缺失"
|
assert ref in data, f"Left Name {ref} 缺失"
|
||||||
assert data[ref].startswith("Pin"), f"Left Name {ref} = {data[ref]}"
|
assert data[ref].startswith("Pin"), f"Left Name {ref} = {data[ref]}"
|
||||||
|
|
||||||
# Bottom Names 在 row 18, col 2..16
|
# Bottom Names 在 row 17, col 2..16
|
||||||
for c in range(2, QFN60_COLS + 2):
|
for c in range(2, QFN60_COLS + 2):
|
||||||
ref = rc_to_cell_ref(QFN60_ROWS + 3, c)
|
ref = rc_to_cell_ref(QFN60_ROWS + 2, c)
|
||||||
assert ref in data, f"Bottom Name {ref} 缺失"
|
assert ref in data, f"Bottom Name {ref} 缺失"
|
||||||
assert data[ref].startswith("Pin"), f"Bottom Name {ref} = {data[ref]}"
|
assert data[ref].startswith("Pin"), f"Bottom Name {ref} = {data[ref]}"
|
||||||
|
|
||||||
# Bottom Numbers 在 row 19, col 2..16
|
# Bottom Numbers 在 row 18, col 2..16
|
||||||
for c in range(2, QFN60_COLS + 2):
|
for c in range(2, QFN60_COLS + 2):
|
||||||
ref = rc_to_cell_ref(QFN60_ROWS + 4, c)
|
ref = rc_to_cell_ref(QFN60_ROWS + 3, c)
|
||||||
assert ref in data, f"Bottom Number {ref} 缺失"
|
assert ref in data, f"Bottom Number {ref} 缺失"
|
||||||
|
|
||||||
# Right Numbers 在 col 18, rows 17..3
|
# Right Numbers 在 col 18, rows 16..2
|
||||||
for r in range(QFN60_ROWS + 2, 2, -1):
|
for r in range(QFN60_ROWS + 1, 1, -1):
|
||||||
ref = rc_to_cell_ref(r, QFN60_COLS + 3)
|
ref = rc_to_cell_ref(r, QFN60_COLS + 3)
|
||||||
assert ref in data, f"Right Number {ref} 缺失"
|
assert ref in data, f"Right Number {ref} 缺失"
|
||||||
|
|
||||||
# Right Names 在 col 17, rows 17..3
|
# Right Names 在 col 17, rows 16..2
|
||||||
for r in range(QFN60_ROWS + 2, 2, -1):
|
for r in range(QFN60_ROWS + 1, 1, -1):
|
||||||
ref = rc_to_cell_ref(r, QFN60_COLS + 2)
|
ref = rc_to_cell_ref(r, QFN60_COLS + 2)
|
||||||
assert ref in data, f"Right Name {ref} 缺失"
|
assert ref in data, f"Right Name {ref} 缺失"
|
||||||
assert data[ref].startswith("Pin"), f"Right Name {ref} = {data[ref]}"
|
assert data[ref].startswith("Pin"), f"Right Name {ref} = {data[ref]}"
|
||||||
@@ -844,25 +848,25 @@ def test_f017_roundtrip():
|
|||||||
cells: dict[tuple[int, int], str] = {}
|
cells: dict[tuple[int, int], str] = {}
|
||||||
cells[(0, 0)] = QFN60_PACKAGE_INFO
|
cells[(0, 0)] = QFN60_PACKAGE_INFO
|
||||||
|
|
||||||
for i, c in enumerate(range(QFN60_COLS, 0, -1)):
|
for i, c in enumerate(range(QFN60_COLS + 1, 1, -1)):
|
||||||
pin_num = 46 + i
|
pin_num = 46 + i
|
||||||
cells[(1, c)] = str(pin_num)
|
cells[(0, c)] = str(pin_num)
|
||||||
cells[(2, c)] = f"Pin{pin_num}"
|
cells[(1, c)] = f"Pin{pin_num}"
|
||||||
|
|
||||||
for i in range(QFN60_ROWS):
|
for i in range(QFN60_ROWS):
|
||||||
r = 3 + i
|
r = 2 + i
|
||||||
cells[(r, 0)] = str(i + 1)
|
cells[(r, 0)] = str(i + 1)
|
||||||
cells[(r, 1)] = f"Pin{i + 1}"
|
cells[(r, 1)] = f"Pin{i + 1}"
|
||||||
|
|
||||||
for i in range(QFN60_ROWS):
|
for i in range(QFN60_ROWS):
|
||||||
r = QFN60_ROWS + 2 - i
|
r = QFN60_ROWS + 1 - i
|
||||||
cells[(r, QFN60_COLS + 1)] = str(31 + i)
|
cells[(r, QFN60_COLS + 3)] = str(31 + i)
|
||||||
cells[(r, QFN60_COLS)] = f"Pin{31 + i}"
|
cells[(r, QFN60_COLS + 2)] = f"Pin{31 + i}"
|
||||||
|
|
||||||
for i in range(QFN60_COLS):
|
for i in range(QFN60_COLS):
|
||||||
c = 1 + i
|
c = 2 + i
|
||||||
cells[(QFN60_ROWS + 3, c)] = f"Pin{16 + i}"
|
cells[(QFN60_ROWS + 2, c)] = f"Pin{16 + i}"
|
||||||
cells[(QFN60_ROWS + 4, c)] = str(16 + i)
|
cells[(QFN60_ROWS + 3, c)] = str(16 + i)
|
||||||
|
|
||||||
# ── Step 2: MAP → List ───────────────────────────────
|
# ── Step 2: MAP → List ───────────────────────────────
|
||||||
pm1 = parse_pinmap(cells)
|
pm1 = parse_pinmap(cells)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
| BUG-004 | 中 | 不支持循环处理流程 | 转换完成后继续操作 | 循环等待下一个文件,输入 Q 返回主菜单 | 处理完直接退出 | 已修复 | F008 |
|
| BUG-004 | 中 | 不支持循环处理流程 | 转换完成后继续操作 | 循环等待下一个文件,输入 Q 返回主菜单 | 处理完直接退出 | 已修复 | F008 |
|
||||||
| BUG-005 | 高 | 模板文件名/路径错误 | PinList↔PinMAP 转换时读取模板 | PinMAP 模板为 PinMAP-Template.xlsx,PinList 模板为 PinList-Template.xlsx | v1.5.4 只改文件名未改搜索路径,模板在 Code/src/Template/ 下但代码在根目录找 | 已修复 | v1.5.5 |
|
| BUG-005 | 高 | 模板文件名/路径错误 | PinList↔PinMAP 转换时读取模板 | PinMAP 模板为 PinMAP-Template.xlsx,PinList 模板为 PinList-Template.xlsx | v1.5.4 只改文件名未改搜索路径,模板在 Code/src/Template/ 下但代码在根目录找 | 已修复 | v1.5.5 |
|
||||||
| BUG-006 | 高 | PinList→PinMAP 上边 Name 与左边 Name 同行(数据无误但肉眼混淆) | 12×12 PinMap:PinList→PinMAP 转换后查看输出 | 每条边的 Name 和 Number 在独立行/列区域,肉眼可辨 | v1.5.4 上边 Name 在 row 2,与左边 Name(row 2)同行,3 条边数据混在同一行 | 已修复 | v1.5.5 |
|
| BUG-006 | 高 | PinList→PinMAP 上边 Name 与左边 Name 同行(数据无误但肉眼混淆) | 12×12 PinMap:PinList→PinMAP 转换后查看输出 | 每条边的 Name 和 Number 在独立行/列区域,肉眼可辨 | v1.5.4 上边 Name 在 row 2,与左边 Name(row 2)同行,3 条边数据混在同一行 | 已修复 | v1.5.5 |
|
||||||
| BUG-007 | 高 | v1.6 PinList→PinMAP 上方引脚合并到标题行,结构缺行 | PinList(QFN60)→PinMAP 转换,查看输出 | 第1行独立标题(合并单元格),第2-3行为上方引脚序号和PinName,共21行 | 标题与上方引脚合并为一行,上方引脚无独立行,共19行,缺2行 | **已修复** | F013, F016 |
|
| BUG-007 | 高 | v1.6 PinList→PinMAP 生成方向相反:应使用 Layout B(Number 在上)但使用了 Layout A(Name 在上) | PinList(QFN60)→PinMAP 转换,15×15 网格 | 输出 Layout B:Row 0=A1+Number,Row 1=Name,左右边从 Row 2 开始 | 输出 Layout A:Row 0=A1+Name,Row 1=Number,导致上方引脚合并到标题行 | 已修复 | v1.6.1 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -33,3 +33,8 @@
|
|||||||
| T032 | 模板确认 F014/F015 | python-coding-agent | 已完成 | 确认验证 | F014, F015 | 2026-06-12 | 2026-06-12 | 两个模板文件存在,样式解析成功 |
|
| T032 | 模板确认 F014/F015 | python-coding-agent | 已完成 | 确认验证 | F014, F015 | 2026-06-12 | 2026-06-12 | 两个模板文件存在,样式解析成功 |
|
||||||
| T033 | 文档生成 v1.6 | doc-gen-agent | 已完成 | 文档编写 | F013-F017 | 2026-06-12 | 2026-06-12 | 更新 CHANGELOG.md、features.md、tasks.md |
|
| T033 | 文档生成 v1.6 | doc-gen-agent | 已完成 | 文档编写 | F013-F017 | 2026-06-12 | 2026-06-12 | 更新 CHANGELOG.md、features.md、tasks.md |
|
||||||
| T034 | 打包发布 v1.6 | package-release-agent | 已完成 | 打包发布 | F013-F017 | 2026-06-12 | 2026-06-12 | Release 已创建 + zip 已上传 + git push 完成 |
|
| T034 | 打包发布 v1.6 | package-release-agent | 已完成 | 打包发布 | F013-F017 | 2026-06-12 | 2026-06-12 | Release 已创建 + zip 已上传 + git push 完成 |
|
||||||
|
| T035 | 架构评估 BUG-007(PinList→PinMAP 布局方向) | script-architect | 已完成 | 架构评估 | BUG-007 | 2026-06-12 | 2026-06-12 | 评估完成,见修改方案 |
|
||||||
|
| T036 | 编码修复 BUG-007(Layout B 生成) | python-coding-agent | 已完成 | 编码实现 | BUG-007 | 2026-06-12 | 2026-06-12 | pinmap_layout.py + test_pinmap.py 修改完成,23/23 通过
|
||||||
|
| T037 | 测试验证 BUG-007 | test-executor | 已完成 | 测试验证 | BUG-007 | 2026-06-12 | 2026-06-12 | 回归测试确认无回归
|
||||||
|
| T038 | 文档生成 BUG-007 | doc-gen-agent | 已完成 | 文档编写 | BUG-007 | 2026-06-12 | 2026-06-12 | 更新 bugs.md、CHANGELOG.md、tasks.md
|
||||||
|
| T039 | 打包发布 BUG-007 | package-release-agent | 待处理 | 打包发布 | BUG-007 | 2026-06-12 | - | 打包 v1.6.1
|
||||||
|
|||||||
Reference in New Issue
Block a user