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

18 KiB
Raw Permalink Blame History

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/) 下查找模板文件:

# 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 中的两个函数:

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() 函数

# 修改前
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() 函数中的上边分支

# 修改前 (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 查找逻辑需要修改:

# 修改前 (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..colsmin_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.xlsxCode/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-005v1.5.4 只改了文件名,没改搜索路径。模板在 Code/src/Template/ 下,但代码在项目根目录找。修复:修改 _find_pinlist_template_path()_find_pinmap_template_path() 的搜索路径。

  2. BUG-006v1.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 修改评估