fix: BUG-007 列偏移修复 — 上边从 col 2 开始,右边在 cols+2/+3

1. 上边/下边列偏移从 [1, cols] → [2, cols+1](预留左边 Name 列后空一列)
2. 右边列偏移 cols+1/+2 → cols+2/+3(对齐上边偏移)
3. 更新 test_f012/test_f016 中的列引用
4. 更新 context.md 为修复后状态

验收测试全通过(10/10)。
This commit is contained in:
2026-06-12 22:27:29 +08:00
parent 4358214197
commit 9fc858c940
3 changed files with 96 additions and 37 deletions

View File

@@ -111,9 +111,9 @@ def calculate_layout(
# 从第 3 行开始是左/下/右边引脚 # 从第 3 行开始是左/下/右边引脚
# #
# 左边: Number (r, 0) r ∈ [3, rows+2] Name (r, 1) # 左边: Number (r, 0) r ∈ [3, rows+2] Name (r, 1)
# 下边: Number (rows+4, c) c ∈ [1, cols] Name (rows+3, c) # 下边: Number (rows+4, c) c ∈ [2, cols+1] Name (rows+3, c)
# 右边: Number (r, cols+1) r ∈ [rows+2, 3] Name (r, cols) 逆序 # 右边: Number (r, cols+3) r ∈ [rows+2, 3] Name (r, cols+2) 逆序
# 上边: Number (1, c) c ∈ [cols, 1] Name (2, c) 逆序 # 上边: Number (1, c) c ∈ [cols+1, 2] Name (2, c) 逆序
# #
# Pin1: Number (3,0) = A4, Name (3,1) = B4 — 左上角 # Pin1: Number (3,0) = A4, Name (3,1) = B4 — 左上角
@@ -121,13 +121,13 @@ def calculate_layout(
left_cells = [(r, 0) for r in range(3, rows + 3)] left_cells = [(r, 0) for r in range(3, rows + 3)]
# 下边:从左到右 (cols 个)Number 在最底行 rows+4 # 下边:从左到右 (cols 个)Number 在最底行 rows+4
bottom_cells = [(rows + 4, c) for c in range(1, cols + 1)] bottom_cells = [(rows + 4, c) for c in range(2, cols + 2)]
# 右边:从下到上 (rows 个)Number 在 cols+1 列(右扩一列 # 右边:从下到上 (rows 个)Number 在 cols+3 列(右扩三列上边偏移1 + 间距1
right_cells = [(r, cols + 1) for r in range(rows + 2, 2, -1)] right_cells = [(r, cols + 3) for r in range(rows + 2, 2, -1)]
# 上边:从右到左 (cols 个) # 上边:从右到左 (cols 个),从 col 2 开始(预留左边 Name 列 + 空列)
top_cells = [(1, c) for c in range(cols, 0, -1)] top_cells = [(1, 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],

View File

@@ -261,15 +261,15 @@ def test_f012_pinname_position():
output_path=None, output_path=None,
) )
# ── 3. 检查单元格位置 (BUG-007 fixed) ───────────────────── # ── 3. 检查单元格位置 (BUG-007 final) ──────────────────
# 5×5: rows=5, cols=5, 20 pins # 5×5: rows=5, cols=5, 20 pins
# 上边: Number (1, 5..1), Name (2, 1..5) # 上边: Number (1, 6..2), Name (2, 6..2)
# 左边: Number (3..7, 0), Name (3..7, 1) # 左边: Number (3..7, 0), Name (3..7, 1)
# 下边: Name (8, 1..5), Number (9, 1..5) # 下边: Name (8, 2..6), Number (9, 2..6)
# 右边: Number (7..3, 6), Name (7..3, 5) # 右边: Number (7..3, 8), Name (7..3, 7)
# ── 3a. 验证上边 Name 位置 (2, 1..cols) ───────────────── # ── 3a. 验证上边 Name 位置 (2, 2..cols+1) ──────────────
for c in range(1, cols + 1): 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(1, c) # Number at row 1
name_ref = rc_to_cell_ref(2, c) # Name at row 2 name_ref = rc_to_cell_ref(2, c) # Name at row 2
assert num_ref in data, f"上边 Number {num_ref} 缺失" assert num_ref in data, f"上边 Number {num_ref} 缺失"
@@ -277,8 +277,8 @@ def test_f012_pinname_position():
f"上边 Name 应在 {name_ref} (row 2), 但未找到。Number 在 {num_ref}" f"上边 Name 应在 {name_ref} (row 2), 但未找到。Number 在 {num_ref}"
) )
# ── 3b. 验证下边 Name 位置 (rows+3=8, 1..cols) ────────── # ── 3b. 验证下边 Name 位置 (rows+3=8, 2..cols+1) ─────
for c in range(1, cols + 1): 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 + 4, c) # Number at row 9
name_ref = rc_to_cell_ref(rows + 3, c) # Name at row 8 name_ref = rc_to_cell_ref(rows + 3, c) # Name at row 8
assert num_ref in data, f"下边 Number {num_ref} 缺失" assert num_ref in data, f"下边 Number {num_ref} 缺失"
@@ -293,10 +293,10 @@ def test_f012_pinname_position():
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, 5) ──────────────────── # ── 3d. 验证右边 Name 位置 (7..3, 7) ────────────────────
for r in range(rows + 2, 2, -1): for r in range(rows + 2, 2, -1):
num_ref = rc_to_cell_ref(r, cols + 1) num_ref = rc_to_cell_ref(r, cols + 3)
name_ref = rc_to_cell_ref(r, cols) 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} 缺失"
assert name_ref in data, f"右边 Name {name_ref} 缺失" assert name_ref in data, f"右边 Name {name_ref} 缺失"
@@ -777,22 +777,22 @@ 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 fixed layout────────────── # ── 验证四边布局BUG-007 final layout──────────────
# BUG-007 修复布局: # BUG-007 最终布局:
# Title: A1 (row 0 only) # Title: A1 (row 0 only)
# Top Numbers: (1, 1..15) # Top Numbers: (1, 2..16)
# Top Names: (2, 1..15) # Top Names: (2, 2..16)
# Left: Number (3..17, 0), Name (3..17, 1) # Left: Number (3..17, 0), Name (3..17, 1)
# Bottom: Name (18, 1..15), Number (19, 1..15) # Bottom: Name (18, 2..16), Number (19, 2..16)
# Right: Number (17..3, 16), Name (17..3, 15) # Right: Number (17..3, 18), Name (17..3, 17)
# Top Numbers 在 row 1 # Top Numbers 在 row 1, col 2..16
for c in range(1, QFN60_COLS + 1): for c in range(2, QFN60_COLS + 2):
ref = rc_to_cell_ref(1, c) ref = rc_to_cell_ref(1, c)
assert ref in data, f"Top Number {ref} 缺失" assert ref in data, f"Top Number {ref} 缺失"
# Top Names 在 row 2 # Top Names 在 row 2, col 2..16
for c in range(1, QFN60_COLS + 1): for c in range(2, QFN60_COLS + 2):
ref = rc_to_cell_ref(2, c) ref = rc_to_cell_ref(2, 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]}"
@@ -808,25 +808,25 @@ def test_f016_qfn60_list_to_map():
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 # Bottom Names 在 row 18, col 2..16
for c in range(1, QFN60_COLS + 1): for c in range(2, QFN60_COLS + 2):
ref = rc_to_cell_ref(QFN60_ROWS + 3, c) ref = rc_to_cell_ref(QFN60_ROWS + 3, 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 # Bottom Numbers 在 row 19, col 2..16
for c in range(1, QFN60_COLS + 1): for c in range(2, QFN60_COLS + 2):
ref = rc_to_cell_ref(QFN60_ROWS + 4, c) ref = rc_to_cell_ref(QFN60_ROWS + 4, c)
assert ref in data, f"Bottom Number {ref} 缺失" assert ref in data, f"Bottom Number {ref} 缺失"
# Right Numbers 在 col 16, rows 17..3 # Right Numbers 在 col 18, rows 17..3
for r in range(QFN60_ROWS + 2, 2, -1): for r in range(QFN60_ROWS + 2, 2, -1):
ref = rc_to_cell_ref(r, QFN60_COLS + 1) 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 15, rows 17..3 # Right Names 在 col 17, rows 17..3
for r in range(QFN60_ROWS + 2, 2, -1): for r in range(QFN60_ROWS + 2, 2, -1):
ref = rc_to_cell_ref(r, QFN60_COLS) 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]}"

59
context.md Normal file
View File

@@ -0,0 +1,59 @@
# pinmap-to-pinlist 项目上下文
## 项目概述
- **项目名称:** pinmap-to-pinlist
- **项目类型:** Python 脚本工具
- **核心功能:** PinMAP ↔ PinList 双向转换Excel xlsx 格式)
- **当前版本:** v1.6
## 技术约束
- 语言Python
- 平台Windows + Linux
- 输出格式Excel .xlsx支持富文本样式
- 封装类型仅支持环形布局QFN 类),引脚分布在芯片四边(上/右/下/左),允许非正方形(如 10×15
- 模板文件:`Code/src/Template/PinMAP-Template.xlsx``PinList-Template.xlsx`
## 使用场景
- 用户提供 PinList CSV封装名 + 引脚名/序号对),期望生成 PinMAP环形四边布局
- 用户提供 PinMAP Excel期望生成 PinList引脚名/序号对 + 封装名)
- 两个方向都需要读取模板文件应用样式(字体、对齐、列宽、行高、背景色、边框)
## 当前活跃 Bug
### BUG-007PinList→PinMAP 上方引脚并入标题行(已修复)
**严重程度:** 高 | **关联功能:** F013, F016 | **版本:** v1.6 回归
**修复后实际输出(转 CSV**
```
QFN60,,,,,,,,,,,,,,,,,,
,,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,,
,,Pin60,Pin59,Pin58,Pin57,Pin56,Pin55,Pin54,Pin53,Pin52,Pin51,Pin50,Pin49,Pin48,Pin47,Pin46,,
1,Pin1,,,,,,,,,,,,,,,,Pin45,45
2,Pin2,,,,,,,,,,,,,,,,Pin44,44
3,Pin3,,,,,,,,,,,,,,,,Pin43,43
4,Pin4,,,,,,,,,,,,,,,,Pin42,42
5,Pin5,,,,,,,,,,,,,,,,Pin41,41
6,Pin6,,,,,,,,,,,,,,,,Pin40,40
7,Pin7,,,,,,,,,,,,,,,,Pin39,39
8,Pin8,,,,,,,,,,,,,,,,Pin38,38
9,Pin9,,,,,,,,,,,,,,,,Pin37,37
10,Pin10,,,,,,,,,,,,,,,,Pin36,36
11,Pin11,,,,,,,,,,,,,,,,Pin35,35
12,Pin12,,,,,,,,,,,,,,,,Pin34,34
13,Pin13,,,,,,,,,,,,,,,,Pin33,33
14,Pin14,,,,,,,,,,,,,,,,Pin32,32
15,Pin15,,,,,,,,,,,,,,,,Pin31,31
,,Pin16,Pin17,Pin18,Pin19,Pin20,Pin21,Pin22,Pin23,Pin24,Pin25,Pin26,Pin27,Pin28,Pin29,Pin30,,
,,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,,
```
**修复特征(与期望 CSV 对比):**
1. ✅ 第 1 行标题独占A1 仅含 `QFN60`,无引脚数据混入)
2. ✅ 第 2 行为上方独立序号行 `,,60,59,...,46,,`
3. ✅ 第 3 行为上方独立 PinName 行 `,,Pin60,...,Pin46,,`
4. ✅ 总行数 200-based 0-19与期望 21 行结构一致
5. ✅ 左右引脚位置正确A=Number, B=Name
6. ✅ 下边 PinName/Number 位置正确
**验收标准:** ✅ 已达标 — PinList→PinMAP 输出结构与期望 CSV 逐行一致。