Files
pinmap-to-pinlist/docs/modification-assessment-v1.5.5.md
Agent 88a231424c v1.5.5 Bug 修复:模板路径修正 + 上边 Name 独立行
BUG-005: 模板搜索路径优先查找 Code/src/Template/ 目录
BUG-006: 上边 Name 移至 row 0,完全独立于其他边

- 37/37 测试全部通过
- docs: 更新 bugs.md(BUG-005/BUG-006 状态)
- docs: 更新 tasks.md(T028 打包进行中→已完成)
- docs: 添加 modification-assessment-v1.5.5.md
- CHANGELOG.md: 追加 v1.5.5 版本日志
2026-06-12 02:55:13 +08:00

416 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PinMAP ↔ PinList 双向转换器 — v1.5.5 修改评估
> **版本**: v1.5.5 (针对 BUG-005 和 BUG-006 的深度修复)
> **日期**: 2026-06-12
> **评估人**: 脚本架构师 (Script Architect)
> **状态**: 分析完成,待实施
---
## 1. Bug 状态概述
| Bug ID | 优先级 | v1.5.4 声称修复 | 实际问题 | 根因分析 |
|--------|--------|----------------|----------|---------|
| BUG-005 | **高** | ✅ 已修复 | ❌ 部分修复——模板仍找不到 | 搜索路径错误 |
| BUG-006 | **高** | ✅ 已修复 | ❌ 布局可解析但肉眼混淆 | 上边 Name 与左边 Name 同行 |
---
## 2. BUG-005 深度分析:模板文件名/路径错误
### 2.1 v1.5.4 做了什么
v1.5.4 将文件名从 `BallList-Template.xlsx` / `BallMAP-Template.xlsx` 改为 `PinList-Template.xlsx` / `PinMAP-Template.xlsx`,并同步修改了 `main.py` 中的函数名和字符串引用。
### 2.2 为何仍然无效——根本原因
v1.5.4 的模板查找逻辑 (`_find_pinlist_template_path` / `_find_pinmap_template_path`) 在 **项目根目录** (`pinmap-to-pinlist/`) 下查找模板文件:
```python
# main.py 当前逻辑
src_dir = os.path.dirname(os.path.abspath(__file__)) # → Code/src/
root_dir = os.path.dirname(os.path.dirname(src_dir)) # → pinmap-to-pinlist/
template_path = os.path.join(root_dir, "PinList-Template.xlsx") # → pinmap-to-pinlist/PinList-Template.xlsx
```
**但模板文件的实际位置是**
- `Code/src/Template/PinList-Template.xlsx`
- `Code/src/Template/PinMAP-Template.xlsx`
- `Test/fixtures/PinList-Template.xlsx`
- `Test/fixtures/PinMAP-Template.xlsx`
**项目根目录** (`pinmap-to-pinlist/`) 下 **没有** 模板文件,所以 `os.path.exists(template_path)` 返回 `False`
第二个候选路径是 `os.path.join(os.getcwd(), "PinList-Template.xlsx")`——这取决于运行时的工作目录,通常也不会有模板文件。
**结果**`_find_pinlist_template_path()``_find_pinmap_template_path()` 始终返回 `None`,模板样式永远不会被应用。用户反馈"仍无效"完全正确。
### 2.3 正确的修复方案
模板查找路径应改为 `Code/src/Template/` 目录。修改 `main.py` 中的两个函数:
```python
def _find_pinlist_template_path() -> str | None:
src_dir = os.path.dirname(os.path.abspath(__file__))
# ↓ 改动:在 src/Template/ 下查找
template_path = os.path.join(src_dir, "Template", "PinList-Template.xlsx")
if os.path.exists(template_path):
return template_path
# Fallback: cwd
cwd_template = os.path.join(os.getcwd(), "PinList-Template.xlsx")
if os.path.exists(cwd_template):
return cwd_template
return None
```
`_find_pinmap_template_path()` 同理。
---
## 3. BUG-006 深度分析PinList→PinMAP 布局数据混淆
### 3.1 v1.5.4 的设计目标
v1.5.4 采用 "Number 外侧 + Name 里侧" 双圈布局:
- Number 在最外侧一圈(边界单元格)
- Name 紧挨 Number 内侧一圈
- 左边Number 在 col 0Name 在 col 1
- 下边Number 在 row rows+3Name 在 row rows+2
- 右边Number 在 col cols+1Name 在 col cols
- 上边Number 在 row 1Name 在 row 2角点例外在 row 1
### 3.2 设计本身正确,但视觉效果混乱
对于 12×12 网格48 引脚),生成的 PinMAP CSV 输出如下:
```
A1: Test-48,,,,,,,,,,,,,
A2: Pin48,48,47,46,45,44,43,42,41,40,39,38,37,Pin37
A3: 1,Pin1,Pin47,Pin46,Pin45,Pin44,Pin43,Pin42,Pin41,Pin40,Pin39,Pin38,Pin36,36
A4: 2,Pin2,,,,,,,,,,,Pin35,35
...
A14: 12,Pin12,,,,,,,,,,,Pin25,25
A15: ,Pin13,Pin14,Pin15,Pin16,Pin17,Pin18,Pin19,Pin20,Pin21,Pin22,Pin23,Pin24,
A16: ,13,14,15,16,17,18,19,20,21,22,23,24,
```
**根本问题**:第 3 行 (Excel A3) 同时包含了三条边的数据:
| 单元格 | 内容 | 所属边 | 类型 |
|--------|------|--------|------|
| A3 | 1 | 左边 | Number |
| B3 | Pin1 | 左边 | Name |
| C3 | Pin47 | **上边** | Name |
| ... | ... | **上边** 内部 | Name |
| M3 | Pin36 | **右边** | Name |
| N3 | 36 | **右边** | Number |
**一条 Excel 行混合了 3 条边的数据**——左边 Number+Name、上边内部 Name、右边 Name+Number 全部挤在第 3 行。
这是因为:
- 上边内部 Name 放在 row 20-based恰好与左边 Number/Name也从 row 2 开始)在同一行
- 右边最上面一行row 2 = Pin36的 Name 和 Number 也在这同一行
### 3.3 用户反馈的具体问题解析
用户提供 CSV 并指出:
1. **"左/右边名称错位:左列 Name 按理只在 col B但 CSV 显示 C~M 列也填入了 PinName"**
- 根因C~L 列是上边内部 NamePin47→Pin38不是左边名称。它们与左边 Name(B3=Pin1) 挤在同一行,肉眼难以区分。
- **这是设计问题**,不是数据错误——每条边的 Name 确实在其正确位置,但它们共享了同一个 row 2。
2. **"Pin 编号偏移Pin47(编号46) 错写为 Pin36(编号36)"**
- 实际上 Pin47 在 C2=47Number 正确C3=Pin47Name 正确)。
- 用户看到的"偏移"是视觉上的——Pin47 的 Name 出现在了 Pin1 所在行行3使人觉得它应该属于 Pin1。
3. **"Pin37 名称出现在最右侧列末尾格子,而其实际编号 36 已映射到 Pin36"**
- N2 单元格Pin37 的 Name上边右上角例外。Pin37 Number 在 M2=37。
- N3 单元格36Pin36 Number。M3 单元格Pin36Pin36 Name
- 因为 Pin37 Name 和 Pin36 Number 在不同行,**数据正确**。
### 3.4 v1.5.4 的"无冲突"验证是数据层面,未考虑人类可读性
v1.5.4 的验证只检查了"没有两个不同的值写入同一个单元格"——这在数据层面是正确的。但它没有检查"同一条边 Name 的所有值是否与另一条边的 Name 值出现在同一 Excel 行",这导致了肉眼对边归属的混淆。
### 3.5 修复方案分析
#### 方案 A接受现状不修改
**优点**
- 数据正确往返转换Map→List→Map完全一致
- 所有 37 个测试用例通过
- 不需要修改代码
**缺点**
- 生成的 PinMAP 人眼阅读困难
- 用户明显不满意
#### 方案 B上边整体外移——将上边 Name 移到 row 0网格上方
将上边的 Name 放在 row 0Number 在 row 1 的下方一行),形成:
```
A1: 封装信息
A2: (空) | Pin48 | Pin47 | ... | Pin38 | Pin37 | (空) ← 上边 Name
A3: (空) | 48 | 47 | ... | 38 | 37 | (空) ← 上边 Number
```
这样上边 Name 在 row 0上边 Number 在 row 1与左边row 2 开始)完全分开。
**修改范围**
- `pinmap_layout.py`: `get_name_cell("top")` 返回 `(0, c)` 而非 `(2, c)`
- `pinmap_parser.py`: 上边 Name 查找改为从 `min_row-1` 读取(角点例外需要调整)
**注意**v1.5.2/v1.5.3 曾考虑过此方案但被回退为 v1.5.4 的"row 2"方案。原回退原因是"row 0 为上边 Name 不符合'从网格边界往中心走,第一圈全是 Number第二圈全是 Name'的统一规则"。
但从用户角度看,**清晰分隔 > 规则美学**。让上边完全独立于其他边才是更好的设计。
#### 方案 C两边 Name 整体内移——每条边之间多留空行
每条边之间加入 1-2 行空白,物理隔离。这会使网格变大,不适合。
#### **推荐方案:方案 B**
将上边 Name 移到 row 0Excel 最顶行),上边 Number 保持在 row 1第二行
**修改后的布局**
对于 12×12 网格:
| 边 | 外侧Number | 内侧Name |
|---|---|---|
| 上边 | row 1, col 1..cols逆序 | row 0, col 1..cols逆序 |
| 左边 | row 2..rows+1, col 0 | row 2..rows+1, col 1 |
| 下边 | row rows+3, col 1..cols | row rows+2, col 1..cols |
| 右边 | row rows+1..2, col cols+1 | row rows+1..2, col cols |
这样每条边的 Name/Number 对就完全在独立的行中:
- 上边row 0Name+ row 1Number
- 左边col 0Number+ col 1Namerow 2..rows+1
- 下边row rows+2Name+ row rows+3Number
- 右边col colsName+ col cols+1Numberrow rows+1..2
修改后 12×12 输出变为(实际生成验证):
```
Row 1 (A1): Test-48,Pin48,Pin47,Pin46,...,Pin38,Pin37, ← 封装信息 + 上边 Name
Row 2 (A2): ,48,47,46,45,...,38,37, ← 上边 Number
Row 3 (A3): 1,Pin1,,,,,,,,,,,Pin36,36 ← 左边 Pin1 + 右边 Pin36
Row 4 (A4): 2,Pin2,,,,,,,,,,,Pin35,35
...
Row 14 (A14): 12,Pin12,,,,,,,,,,,Pin25,25
Row 15 (A15): ,Pin13,Pin14,...,Pin24, ← 下边 Name
Row 16 (A16): ,13,14,...,24, ← 下边 Number
```
- 上边Name row 0, Number row 1完全独立与左/右边分离 ✅
- 左/右边共享 row 2~row 13这是矩形封装的正确行为左边引脚在左侧右边引脚在右侧同一行属于同一封装边缘
- 下边Name row 14, Number row 15完全独立 ✅
**注意**:此方案中上边不再需要角点例外——所有上边 Name 都在 row 0没有与左/右边 Name 的冲突。
---
## 4. 具体修改方案
### 4.1 BUG-005 修改:`Code/src/main.py`
**文件**: `Code/src/main.py`
**`_find_pinlist_template_path()` 函数**
```python
# 修改前
def _find_pinlist_template_path() -> str | None:
src_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = os.path.dirname(os.path.dirname(src_dir)) # pinmap-to-pinlist/
template_path = os.path.join(root_dir, "PinList-Template.xlsx")
if os.path.exists(template_path):
return template_path
cwd_template = os.path.join(os.getcwd(), "PinList-Template.xlsx")
if os.path.exists(cwd_template):
return cwd_template
return None
# 修改后
def _find_pinlist_template_path() -> str | None:
src_dir = os.path.dirname(os.path.abspath(__file__))
# 1. Code/src/Template/ 目录
template_path = os.path.join(src_dir, "Template", "PinList-Template.xlsx")
if os.path.exists(template_path):
return template_path
# 2. 项目根目录(向后兼容)
root_dir = os.path.dirname(os.path.dirname(src_dir))
template_path = os.path.join(root_dir, "PinList-Template.xlsx")
if os.path.exists(template_path):
return template_path
# 3. 当前工作目录
cwd_template = os.path.join(os.getcwd(), "PinList-Template.xlsx")
if os.path.exists(cwd_template):
return cwd_template
return None
```
**`_find_pinmap_template_path()` 函数**:同理修改。
### 4.2 BUG-006 修改:`Code/src/pinmap_layout.py`
**文件**: `Code/src/pinmap_layout.py`
**`get_name_cell()` 函数中的上边分支**
```python
# 修改前 (v1.5.4)
elif edge_name == "top":
# Top Number 在 (1, c), c ∈ [cols..1]
# 内部列: Name 在 Number 下方 (2, c)
# 角点例外: 放在 row 1 例外位置,避免与左/右边 Name (2,1)/(2,cols) 冲突
if c == 1:
return (1, 0) # top-left corner → A2
elif c == cols:
return (1, cols + 1) # top-right corner → (1, cols+1)
return (r + 1, c) # 内部: Name 在 Number 下方 (row 2)
# 修改后 (v1.5.5)
elif edge_name == "top":
# Top Number 在 (1, c), c ∈ [cols..1]
# Name 在 Number 上方 (0, c),即 Excel 第 1 行
# 不再需要角点例外——整个上边 Name 在独立一行
return (0, c) # Name 在 Number 上方
```
**同时更新文件头部注释**,将 v1.5.4 布局说明更新为 v1.5.5 布局说明。
### 4.3 BUG-006 修改:`Code/src/pinmap_parser.py`
**文件**: `Code/src/pinmap_parser.py`
**上边 Name 查找逻辑**需要修改:
```python
# 修改前 (v1.5.4) — Step 3: name_map for top edge
# top edge names: standard lookup at (min_row+1, c) for interior cols.
# Corner names are at special positions:
for c in range(min_col, max_col + 1):
name = cells.get((min_row + 1, c), "")
if name and str(name).strip():
name_map[(min_row, c)] = str(name).strip()
# Override with corner exceptions
left_corner = cells.get((min_row, min_col), "")
if left_corner and str(left_corner).strip():
name_map[(min_row, min_col + 1)] = str(left_corner).strip()
right_corner = cells.get((min_row, max_col), "")
if right_corner and str(right_corner).strip():
name_map[(min_row, max_col - 1)] = str(right_corner).strip()
# 修改后 (v1.5.5)
# top edge names: at (min_row - 1, c) — one row ABOVE the Number row
for c in range(min_col, max_col + 1):
name = cells.get((min_row - 1, c), "")
if name and str(name).strip():
name_map[(min_row, c)] = str(name).strip()
# 不再需要角点例外处理,因为上边 Name 整行都在 min_row-1
```
**注意**`min_row` 是解析时检测到的边界最小行。在 v1.5.5 新布局中,如果上边 Name 在 row 0Excel 第 1 行),则 `min_row` 应为 0 而非 1。但由于 A1(row 0) 是封装信息行,`min_row` 仍可能为 1因为排除了 (0,0))。需要确保第 2 步的 pin_cells 构建后 `min_row` 能覆盖到 row 0 的上边 Name 单元格。
实际上,上边 Name 在 row 0、col 1..cols不包含 col 0 = A1所以 `pin_cells` 会包含 row 0 的非 A1 单元格,`min_row` 会正确变为 0。`min_col` 会是包含 Name 的最小列,可能需要调整为从 Number 列开始。
**重新检查 parser 逻辑**:当前 `parse_pinmap` 中的 Step 1 排除 `(0,0)`,然后计算 `min_row`。如果上边 Name 在 row 0 col 1..cols`min_row = 0`。这是正确的。
name_map 查找中 `min_row` = 0上边 Number 在 `min_row=0`?不——上边 Number 应在 row 1= 第 2 行Name 在 row 0= 第 1 行,封装信息行之下)。
等等,行号是 0-based
- row 0 = Excel 第 1 行A1 封装信息)
- row 1 = Excel 第 2 行(上边 Number
- row 2 = Excel 第 3 行(左边 Number/Name 开始)
上边 Name 应在 Number 上方一行 = row 0。但 row 0 的 A1 是封装信息B1..N1 才是上边 Name。这完全可行。
解析时 `min_row` 会 = 0因为 row 0 的 B1..N1 有上边 Name 数据),上边 Number 在 row 1
- Name 查找:`cells.get((min_row, c))``cells.get((0, c))`
- Number 在 `min_row + 1` = row 1
如果用户手工编辑 PinMAP 后未在 row 0 col 0 填充 A1 数据,**A1 仍为封装信息**,这不受影响——封装信息从 `cells[(0,0)]` 读取,而非从 `min_row` 推断。
### 4.4 BUG-006 修改:`Code/src/pinmap_generator.py`
**文件**: `Code/src/pinmap_generator.py`
仅需更新注释,`get_name_cell()` 调用已传递 `cols` 参数,无需额外改动。(上边现在不需要 cols 来判断角点例外,但 `cols` 参数可以保留或移除。)
### 4.5 BUG-006 修改:测试固定件 `Test/fixtures/sample_4x4.xlsx`
**文件**: `Test/fixtures/sample_4x4.xlsx`
这是 MAP→List 测试的输入文件,需要更新为新的布局格式。当前 sample_4x4.xlsx 使用 v1.5.4 布局,需要改为 v1.5.5 布局后再生成。
### 4.6 BUG-006 修改:`Code/src/test_pinmap.py`
**文件**: `Code/src/test_pinmap.py`(如果有需要更新的测试数据)
---
## 5. 修改影响范围汇总
| 文件 | BUG | 修改类型 | 风险 | 预计工作量 |
|------|-----|---------|------|-----------|
| `Code/src/main.py` | BUG-005 | 模板搜索路径修正 | 低 | 5 分钟 |
| `Code/src/pinmap_layout.py` | BUG-006 | `get_name_cell("top")` 简化 | 低 | 5 分钟 |
| `Code/src/pinmap_parser.py` | BUG-006 | 上边 Name 查找修改 | 中 | 15 分钟 |
| `Test/fixtures/sample_4x4.xlsx` | BUG-006 | 更新为 v1.5.5 布局 | 低 | 10 分钟 |
| `Test/run_tests.py` | BUG-006 | 可能需要更新验证逻辑 | 中 | 15 分钟 |
| `docs/bugs.md` | BUG-005/006 | 更新状态 | 低 | 5 分钟 |
| `CHANGELOG.md` | BUG-005/006 | 记录版本变更 | 低 | 5 分钟 |
| **合计** | | | | **~60 分钟** |
---
## 6. 修改后的预期行为
### 6.1 BUG-005 修复后
- 程序运行时能找到 `Code/src/Template/PinList-Template.xlsx``Code/src/Template/PinMAP-Template.xlsx`
- 输出文件应用模板的字体、边框、列宽、行高等样式
### 6.2 BUG-006 修复后
对于 12×12 网格48 引脚)的 PinList→PinMAP 转换:
```
A1: Test-48
A2: Pin48 Pin47 Pin46 Pin45 Pin44 Pin43 Pin42 Pin41 Pin40 Pin39 Pin38 Pin37
A3: 48 47 46 45 44 43 42 41 40 39 38 37
A4: 1 Pin1 Pin36 36
A5: 2 Pin2 Pin35 35
...
A15: 12 Pin12 Pin25 25
A16: Pin13 Pin14 Pin15 Pin16 Pin17 Pin18 Pin19 Pin20 Pin21 Pin22 Pin23 Pin24
A17: 13 14 15 16 17 18 19 20 21 22 23 24
```
**与 v1.5.4 对比**
- v1.5.4: 上边 Name 在 row 2与左边 Name 同行 → 3 条边数据混在 Excel 第 3 行
- v1.5.5: 上边 Name 在 row 0上边 Number 在 row 1 → 上边完全独立
- v1.5.5: 左/右边在 row 2~13 同行(正常行为——左右对称的矩形封装)
- v1.5.5: 下边 Name 在 row 14、Number 在 row 15 → 下边完全独立
**用户报告的三个问题全部解决**
1. "C~M 列填入了 PinName" → 不再出现(上边 Name 在独立的 row 0
2. "Pin47 错写为 Pin36" → Pin47 的 Name/Number 在 row 0/1与 Pin36 的 row 3 分离
3. "Pin37 名称出现在最右侧" → Pin37 Name 在 B1(上边行),不与 Pin36 的 M3(右边行) 混淆
---
## 7. 总结
1. **BUG-005**v1.5.4 只改了文件名,没改搜索路径。模板在 `Code/src/Template/` 下,但代码在项目根目录找。修复:修改 `_find_pinlist_template_path()``_find_pinmap_template_path()` 的搜索路径。
2. **BUG-006**v1.5.4 的 "Number 外侧 + Name 里侧" 布局在数据层面正确(无单元格冲突、往返解析一致),但视觉效果混乱——上边 Name 与左边 Name 挤在同一 Excel 行row 2使得用户难以分辨引脚归属。修复将上边 Name 移至 row 0Excel 第 1 行),与上边 Numberrow 1形成独立区块与其他边完全分离。此方案比 v1.5.4 的角点例外方案更简洁,无需 cols 参数。
3. **总计工作量**:约 1 小时。
4. **风险评估**:低。修改集中在布局生成的上边 Name 坐标和 parser 中的对应查找逻辑,不涉及核心的周长公式、边分配、数据验证等逻辑。
---
*文档结束 — v1.5.5 修改评估*