Files
pinmap-to-pinlist/docs/modification-assessment-v1.6.md
Agent 3c5fcff1d5 v1.6.0 修复 PinMAP→PinList 上方引脚丢失 + 双向模板样式 + QFN60 端到端验证
F013: Code/src/pinmap_parser.py 增加 Top 边自动布局检测
F014/F015: 双向模板样式确认
F016/F017: 新增 5 个 QFN60 端到端测试
2026-06-12 20:45:51 +08:00

573 lines
25 KiB
Markdown
Raw 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.6 整改架构评估
> **版本**: v1.6 (针对 F013F017 五项 P0 整改需求)
> **日期**: 2026-06-12
> **评估人**: 脚本架构师 (Script Architect)
> **状态**: 评估完成,待编码实施
---
## 1. 需求总览
| 需求 ID | 方向 | 问题描述 | 严重级别 | 当前状态 |
|---------|------|---------|---------|---------|
| F013 | MAP→List | 封装上侧Top引脚全部未被识别 | P0 🔴 | **Bug**:解析逻辑硬编码 Top Name/Number 位置假设 |
| F014 | List→MAP | PinMAP 输出需应用 PinMAP-Template.xlsx 样式 | P0 🔴 | **部分有效**:代码结构存在但模板路径需要确认 |
| F015 | MAP→List | PinList 输出需应用 PinList-Template.xlsx 样式 | P0 🔴 | **部分有效**:同上 |
| F016 | List→MAP | 使用 QFN60 示例验证 List→MAP 转换正确性 | P0 🔴 | **新测试**:需设计端到端测试用例 |
| F017 | MAP→List | 使用 QFN60 示例验证 MAP→List 转换正确性 | P0 🔴 | **新测试**:需设计端到端测试用例 |
### 执行顺序依赖
```
F013 (修复解析) ──┬── F015 (MAP→List 模板) ── F017 (MAP→List 验证)
└── F014 (List→MAP 模板) ── F016 (List→MAP 验证)
```
---
## 2. F013 根因分析 — PinMAP→PinList 上方引脚丢失
### 2.1 问题描述
用户反馈PinMAP→PinList 转换后封装上侧Top引脚全部缺失。以 QFN60 (12×12 网格, 4×(12+12)=96 槽位但仅 60 引脚环形布局) 为例,应输出 60 个引脚,实际输出可能只有 45 个(仅左+下+右三边,上边 15 个全部丢失)。
### 2.2 关键证据:用户真实 PinMAP 布局
用户提供的 QFN60 PinMAP 片段CSV 格式12×12 网格):
```
QFN60 6*6*0.85mm ... ← Row 1 (A1)
, ,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46, ← Row 2 (Numbers)
, ,Pin60,Pin59,...,Pin46, ← Row 3 (Names)
1 ,Pin1,,,,,,,,,,,,,,,,Pin45,45 ← Row 4 (left Pin1 + right Pin45)
2 ,Pin2,,,,,,,,,,,,,,,,Pin44,44 ← Row 5 (left Pin2 + right Pin44)
...
12 ,Pin12,,,,,,,,,,,,,,,Pin34,34 ← Row 15 (left Pin12 + right Pin34)
, ,Pin13,Pin14,...,Pin33, ← Row 16 (bottom Names)
, ,13,14,...,33, ← Row 17 (bottom Numbers)
```
**关键发现**:上边 Name 在 Row 3第 3 行),上边 Number 在 Row 2第 2 行)。
### 2.3 当前解析器硬编码假设 vs 用户实际布局
**当前 `pinmap_parser.py` v1.5.5 的硬编码假设**
| 边 | Number 位置 | Name 位置 |
|----|------------|----------|
| Top | `min_row + 1`(即 row 1 | `min_row`(即 row 0 |
| Left | `(r, min_col)` | `(r, min_col + 1)` |
| Bottom | `(max_row, c)` | `(max_row - 1, c)` |
| Right | `(r, max_col)` | `(r, max_col - 1)` |
这个设计假设了 v1.5.5 生成的 PinMAP 布局Name 在 Number **上方一行**Name 在 min_rowNumber 在 min_row+1
**用户的真实布局**
| 边 | Number 位置 | Name 位置 |
|----|------------|----------|
| Top | Row 2第 2 行) | Row 3第 3 行) |
| Left | Col A第 1 列) | Col B第 2 列) |
| Bottom | 倒数第 1 行 | 倒数第 2 行 |
| Right | 最右列 | 次右列 |
### 2.4 根因Name 和 Number 相对位置反转
用户 PinMAP 中 Top 边的布局是 **Number 在上方、Name 在下方**(与当前假设相反)。当前代码:
```python
# pinmap_parser.py 第 114-117 行v1.5.5
# top edge names at (min_row, c) — one row ABOVE the Number row.
for c in range(min_col, max_col + 1):
name = cells.get((min_row, c), "")
if name and str(name).strip() and _try_int(name) is None:
name_map[(min_row + 1, c)] = str(name).strip()
```
这段代码:
1.`min_row` 行查找 Name
2. 将 Name 映射到 `min_row + 1` 行的 Number 单元格
但在用户实际布局中,上边 Name 在 `min_row + 1`Row 3Number 在 `min_row`Row 2。由于 Name 查不到Name 在 min_row+1 而非 min_row`_try_int(name)` 检查会将 Number纯数字如 "60")过滤掉,导致整个上边的 Name 全部未被建立 name_map 映射。
**更严重的问题是**:由于 corners如 Pin1/Pin60、Pin46/Pin45的 Name/Number 位于左右边缘列,上边的 Number 可能在右边缘扫描时被误识别为右边 Pin而 Name 仍在右边被找到——但中间 13 个上边引脚Pin47-Pin59不含左右角的 Pin60/Pin46完全丢失。
### 2.5 为什么 v1.5.5 的测试没发现
v1.5.5 的 4×4 和 12-pin 测试用例都是**程序自己生成的 PinMAP 布局**(生成的 Name 总是在上边 Number 之上),然后用这个自产的 PinMAP 做往返解析。自产的 PinMAP 布局与解析假设一致,因此往返测试全部通过。
但是用户提供的 PinMAP 来自其他渠道(可能是手工绘制或其他工具生成),其布局约定与程序生成的布局**方向相反**。
---
## 3. F013 修改方案
### 3.1 设计原则
PinMAP 解析器必须兼容 **"Name 在 Number 上方"** 和 **"Number 在 Name 上方"** 两种布局。对于上边而言Name 和 Number 分布在相邻两行,解析器需要智能检测哪一行是 Name、哪一行是 Number。
### 3.2 检测策略:基于内容特征识别 Name 行 vs Number 行
对于 Top 边,扫描第一行非 A1 数据行和其后一行:
- **Number 行特征**:所有单元格(或绝大多数)都是可解析为整数的值(如 "60", "59", ...
- **Name 行特征**:所有单元格(或绝大多数)都是非纯数字的字符串(如 "Pin60", "Pin59", ...
如果第一行全是数字Number 在第一行Name 在第二行(用户布局)。
如果第一行全是非数字且非空Name 在第一行Number 在第二行v1.5.5 布局)。
### 3.3 具体实现
**文件**`Code/src/pinmap_parser.py`
修改 Step 2确定边界和 Step 3上边 Name 查找)之间的逻辑,增加 Top 边的自动检测。
```python
# ── Top edge layout detection ─────────────────────────────────
# 检测上边布局:哪种布局由数据内容决定
# 布局 AName 在 min_row上方Number 在 min_row+1下方
# 例Row 1: Pin60 Pin59 ... Pin46, Row 2: 60 59 ... 46
# 布局 BNumber 在 min_row上方Name 在 min_row+1下方
# 例Row 1: 60 59 ... 46, Row 2: Pin60 Pin59 ... Pin46
min_row_for_top = min_row # 可能是 1A1 被排除后)
top_row_1_cells = []
top_row_2_cells = []
for c in range(min_col, max_col + 1):
v1 = cells.get((min_row_for_top, c), "")
v2 = cells.get((min_row_for_top + 1, c), "")
if v1 and str(v1).strip():
top_row_1_cells.append(str(v1).strip())
if v2 and str(v2).strip():
top_row_2_cells.append(str(v2).strip())
# 检测哪一行是 Number 行
def _is_number_row(values: list[str]) -> bool:
if not values:
return False
numeric = sum(1 for v in values if _try_int(v) is not None)
return numeric >= len(values) * 0.7 # 70% 以上是数字即为 Number 行
row1_is_number = _is_number_row(top_row_1_cells)
row2_is_number = _is_number_row(top_row_2_cells)
if row1_is_number and not row2_is_number:
# 布局 BNumber 在上Name 在下(用户实际布局)
top_number_row = min_row_for_top
top_name_row = min_row_for_top + 1
elif not row1_is_number and row2_is_number:
# 布局 AName 在上Number 在下v1.5.5 布局)
top_name_row = min_row_for_top
top_number_row = min_row_for_top + 1
elif row1_is_number and row2_is_number:
# 两行都是数字(异常情况:可能没有 Name 行)
# 回退到布局 A 假设
top_name_row = min_row_for_top
top_number_row = min_row_for_top + 1
else:
# 两行都不是数字(极异常情况)
# 回退到布局 A 假设
top_name_row = min_row_for_top
top_number_row = min_row_for_top + 1
```
然后修改 Step 3 中上边 Name 查找和 Step 4d 中的 Top 边 Number 遍历:
```python
# Step 3: 上边 Name 查找(修改后)
# Name 在上边数字行之上/之下的一行
for c in range(min_col, max_col + 1):
name = cells.get((top_name_row, c), "")
if name and str(name).strip() and _try_int(name) is None:
name_map[(top_number_row, c)] = str(name).strip()
# Step 4d: 上边遍历(修改后)
for c in range(max_col, min_col - 1, -1):
_add_pin(top_number_row, c, "top", max_col - c)
```
### 3.4 影响的其他边
左、下、右三边的布局在用户提供的 PinMAP 中是标准的Name 在 Number 内侧一列/一行。但为了一致性可以考虑对下边也增加类似检测Name 在 Number 上方 vs 下方),但当前未收到相关反馈,建议**先从简处理**,仅在 Top 边出现问题后扩展到其他边。
### 3.5 修改文件清单F013
| 文件 | 修改内容 | 风险 |
|------|---------|------|
| `Code/src/pinmap_parser.py` | Step 2-3 之间增加 Top 边布局检测;修改上边 Name 查找和 Number 遍历 | 中 |
| `Code/src/test_pinmap.py` | 新增测试用例:用户 QFN60 格式的 PinMAP 解析 | 低 |
---
## 4. F014 分析 — PinList→PinMAP 样式模板应用
### 4.1 当前状态
v1.5.5 `main.py``run_list_to_map()` 已经:
1. 调用 `_find_pinmap_template_path()` 查找模板
2. 调用 `read_template_styles()` 解析样式
3.`template_style` 传递给 `generate_pinmap()``write_xlsx_with_style()`
**搜索路径**v1.5.5 已修复):
```python
# main.py _find_pinmap_template_path()
src_dir = os.path.dirname(os.path.abspath(__file__)) # → Code/src/
template_path = os.path.join(src_dir, "Template", "PinMAP-Template.xlsx")
# → Code/src/Template/PinMAP-Template.xlsx ✅
```
### 4.2 确认项
1. ✅ 模板文件存在:`Code/src/Template/PinMAP-Template.xlsx` 已确认存在
2. ✅ 搜索路径正确:优先查找 `Code/src/Template/`,向后兼容项目根目录和 cwd
3. ✅ 样式提取完整:`template_reader.py` 提取字体 / 填充 / 边框 / 对齐 / 列宽 / 行高
4. ✅ 样式应用正确:`StyledXLSXWriter` 将模板样式应用到输出
### 4.3 用户提到的"模板放在主程序根目录"
用户提到模板应放在"主程序根目录"。主程序根目录 = `pinmap-to-pinlist/`。当前代码**优先**查找 `Code/src/Template/`**其次**查找项目根目录(向后兼容)。
**建议保持不变**:当前的多路径回退策略已经覆盖了主程序根目录。如果用户坚持只从主程序根目录查找,可在评估后的实施阶段调整搜索优先级。但从工程角度看,`Code/src/Template/` 更合理(与源码打包在一起)。
### 4.4 F014 结论
**F014 在当前 v1.5.5 代码中已基本实现**,无需大规模修改。需要确认的是:
- 模板文件的内容是否符合用户期望(字体/边框/对齐/填充色等)
- 确认项将在 F016 验证阶段通过实际生成的 xlsx 与预期对比来验证
---
## 5. F015 分析 — PinMAP→PinList 样式模板应用
### 5.1 当前状态
v1.5.5 `main.py``run_map_to_list()` 已经:
1. 调用 `_find_pinlist_template_path()` 查找模板
2. 调用 `read_template_styles()` 解析样式
3. 如果模板存在,使用 `write_xlsx_with_style()`,否则使用 `write_xlsx()`
**搜索路径**(同 F014
```python
# Code/src/Template/PinList-Template.xlsx ✅
```
模板文件存在:`Code/src/Template/PinList-Template.xlsx`
### 5.2 注意事项
PinList 输出的数据非常简单两列A 列 = PinNameB 列 = Pin 序号),样式应用的效果主要是:
- 字体名称/大小/颜色
- 列宽(如果模板只有两列数据,列 C 以后理论上不应有宽度设置)
- 行高
- 边框
`StyledXLSXWriter._sheet_xml()``style.column_widths` 字典读取列宽并生成 `<cols>` 元素。如果 PinList 模板中只有 A、B 两列定义了宽度,输出也会只有两列宽。
### 5.3 需要确认的潜在问题
`StyledXLSXWriter``_get_style_index()` 方法为所有非 A1 单元格分配 style index `1`(边框+居中。PinList 输出也是相同的逻辑——A1 用 style 2bold其他用 style 1。这对 PinList 两列布局来说是合理的。
但是PinList 的 A1 不是"封装信息"就是"Package Name",而 PinMAP 的 A1 也是封装信息。两个模板可能在 A1 样式的 fontId/fillId/borderId 上指向不同索引,但当前 `_get_style_index()` 统一用 hardcoded 的 index。**这是一个潜在问题**,但在用户明确反馈前不做假设性修改。
### 5.4 F015 结论
**F015 在当前 v1.5.5 代码中已基本实现**。确认项将在 F017 验证阶段通过实际生成的 PinList xlsx 与预期对比来验证。
---
## 6. F016 分析 — PinList→PinMAP 转换正确性验证
### 6.1 测试设计
使用用户提供的 QFN60 示例 PinList60 个引脚)作为输入,验证生成的 PinMAP 结构与示例 PinMAP 一致。
**示例 PinList 结构**
```
QFN60
Pin1,1
Pin2,2
...
Pin60,60
```
**示例 PinMAP 预期结构**(基于用户提供的 CSV
- 12×12 网格QFN60 环形布局)
- Left 边Pin1Pin12row 4-15, col A/B
- Bottom 边Pin13Pin18 + Pin28Pin33row 16-17, 中间空列对应无引脚位置)
- Right 边Pin34Pin45row 15→4, col 倒数两列)
- Top 边Pin46Pin60row 3, col 倒数→前row 2 为 Numbers
- 内部为空QFN 封装中心无引脚)
### 6.2 关键问题60 引脚的环形布局 ≠ 12×12 全周长
12×12 网格的全周长 = (12+12)×2 = 48。但 QFN60 有 **60 个引脚**。这意味着用户使用的"12×12 网格"并不是严格的矩形周长概念。
重新分析用户 PinMAP
- Left 边12 个引脚Pin1Pin12
- Right 边12 个引脚Pin34Pin45
- Top 边15 个引脚Pin46Pin60跨越 15 列)
- Bottom 边21 个引脚Pin13Pin33跨越 21 列?不对,重看)
仔细看用户实际 PinMAP 布局:
- Left12 个
- Bottom12 个Pin13Pin18=6 + Pin24Pin33?不对)
让我重新分析 CSV 模式:
- Left col A/B12 pins (Pin1Pin12)
- Right col 最后两列12 pins (Pin34Pin45)
- Top row 2-315 pins (Pin46Pin60)
- Bottom 倒数第1-2行15 pins? → 实际底部仅 Pin13Pin18=6 + ... 需要确认
实际上用户 CSV 格式是 12×1212行12列但只用了外圈。Pin 总计 60但网格如果按周长算只有 48。
**这里存在根本性偏差**:用户的 PinMAP 是"12×12 网格内有 60 个环形引脚"——引脚布局在 12×12 的外圈但某些角被占用。这意味着 PinMAP 布局**不完全遵循四边等长逻辑**。
**但这对 v1.6 不重要**F016 和 F017 的目标是验证**端到端转换正确性**,即:
1. List→MAP输入 60-pin PinList → 生成 PinMAP xlsx
2. MAP→List将生成的 PinMAP 再转回 PinList
3. 往返验证:原始 PinList == 生成的 PinList引脚不丢失、顺序不变
这将暴露 F013 修复后解析器是否能正确处理各种布局。
### 6.3 F016 测试方案
**测试 F016-1: 60-pin List→MAP 基本生成**
输入:
- 60 个 PinListEntry (Pin1Pin60)
- rows=12, cols=12
- package_info="QFN60"
验证:
- 生成的 PinMAP 至少包含 A1 封装信息 + 所有 60 个引脚的 Name 和 Number
- 无单元格冲突(生成器内部保证)
- 引脚沿四条边分布(取决于布局算法分配)
**测试 F016-2: List→MAP→List 往返**
输入:同上 60-pin PinList
验证:
- List→MAP 生成 PinMAP xlsx
- 将该 xlsx 作为 MAP→List 输入
- 解析出的 PinList 包含 60 个引脚,顺序 Pin1Pin60封装信息 "QFN60"
- 无引脚丢失、无序号错误
**注意**:不要求生成的 PinMAP 在**单元格位置**上与用户示例 PinMAP 完全一致。用户示例 PinMAP 是手工制作的(某些边有不同数量的引脚),而程序将使用标准的周长分配算法。**只要往返一致、引脚不丢失**即可。
### 6.4 但如果用户要求"结构一致"怎么办
features.md 中 F016 的验收标准写的是"生成的 PinMAP 与示例 PinMAP 结构完全一致"。这暗示用户希望:**生成的 PinMAP 在外观布局上与示例 PinMAP 相同**。
如果是这样PinMAP 生成器需要支持**非均匀边分配**——即用户指定每条边分别有多少引脚。这是当前 `pinmap_layout.py` 不支持的(它假设 rows=cols 时每条边有相同数量的引脚)。
**建议**:在 v1.6 中先实现往返正确性验证作为 F016 的交付物。如果用户坚持布局像素级一致,需要在 v1.7 中重新设计布局引擎。
---
## 7. F017 分析 — PinMAP→PinList 转换正确性验证
### 7.1 测试设计
使用用户提供的 QFN60 示例 PinMAP (CSV) 作为输入,验证:
1. 解析器能正确识别 Top 边(修复后)
2. 生成的 PinList 包含完整的 60 个引脚
3. Pin 序号 Pin1Pin60 完整无缺失
4. 封装信息 "QFN60 ..." 正确提取
5. PinName 与示例一致
### 7.2 测试用例构建
**输入**:用户提供的 QFN60 PinMAP CSV转换为 Excel 或直接用当前 xls_reader 兼容的格式)
由于当前解析器读取的是 Excel 格式(.xls/.xlsx需要先构建测试用的 cell dictionary。
**测试 F017-1: QFN60 PinMAP 解析**
基于用户提供的 CSV 构建 cells dict0-based row, col
```python
cells = {
(0, 0): "QFN60 6*6*0.85mm ...",
# Top: Number row 1, Name row 2
(1, 2): "60", (1, 3): "59", ..., (1, 16): "46",
(2, 2): "Pin60", (2, 3): "Pin59", ..., (2, 16): "Pin46",
# Left: rows 3..14
(3, 0): "1", (3, 1): "Pin1",
...
# Bottom
...
# Right
...
}
```
验证:
- `parse_pinmap(cells)` 返回 60 个 Pin
- Top 边的 Pin (Pin46Pin60) 都被正确识别且有正确的 edge="top"
- 无 StructureError
**测试 F017-2: QFN60 PinMAP→PinList 完整转换**
从 parsed pinmap 生成 PinList验证
- `len(pinlist.rows) == 60`
- Pin 序号 1..60 全部存在
- 封装信息正确
**测试 F017-3: 往返验证 (MAP→List→MAP)**
```python
cells parse_pinmap pinmap
pinmap generate_pinlist pinlist (60 pins)
pinlist [重新构造 entries] generate_pinmap pinmap2
```
验证 `pinmap``pinmap2` 的引脚数量和序号一致。
---
## 8. 总体修改方案与文件清单
### 8.1 F013 — 修复上方引脚丢失
| 文件 | 修改内容 | 工作量 |
|------|---------|--------|
| `Code/src/pinmap_parser.py` | 在 Step 2-3 之间增加 Top 边布局自动检测逻辑,根据数据内容决定 Name 在 Number 上方还是下方 | 中(~40 行新代码) |
| `Code/src/pinmap_parser.py` | 修改 Step 3 上边 Name 查找,使用检测结果;修改 Step 4d 上边遍历,使用正确的 Number 行 | 中 |
### 8.2 F014 — PinList→PinMAP 模板(确认性检查)
| 文件 | 修改内容 | 工作量 |
|------|---------|--------|
| `Code/src/main.py` | 确认 `_find_pinmap_template_path()` 搜索路径正确v1.5.5 已修复) | 无代码改动 |
| `Code/src/Template/PinMAP-Template.xlsx` | 确认模板文件存在且格式正确 | 无代码改动 |
### 8.3 F015 — PinMAP→PinList 模板(确认性检查)
| 文件 | 修改内容 | 工作量 |
|------|---------|--------|
| `Code/src/main.py` | 确认 `_find_pinlist_template_path()` 搜索路径正确v1.5.5 已修复) | 无代码改动 |
| `Code/src/Template/PinList-Template.xlsx` | 确认模板文件存在且格式正确 | 无代码改动 |
### 8.4 F016 — List→MAP 验证
| 文件 | 修改内容 | 工作量 |
|------|---------|--------|
| `Code/src/test_pinmap.py` | 新增 QFN60 List→MAP 生成测试 + 往返测试 | 低(~60 行新测试代码) |
### 8.5 F017 — MAP→List 验证
| 文件 | 修改内容 | 工作量 |
|------|---------|--------|
| `Code/src/test_pinmap.py` | 新增 QFN60 PinMAP 解析测试(使用 Top 布局 B+ 完整转换测试 + 往返测试 | 中(~100 行新测试代码 + 构建 QFN60 cells 常量) |
---
## 9. 任务拆分建议
### 9.1 子任务划分
| Seq | 子任务 | 关联需求 | 执行 Agent | 预估工作量 | 依赖 |
|-----|--------|---------|-----------|-----------|------|
| 1 | **F013 编码实现**:修复 `pinmap_parser.py` Top 边识别 | F013 | python-coding-agent | 1h | 无 |
| 2 | **F017 测试用例**QFN60 PinMAP→PinList 解析+转换+往返测试 | F017 | test-qa-agent | 30min | Seq 1 |
| 3 | **F016 测试用例**QFN60 PinList→PinMAP 生成+往返测试 | F016 | test-qa-agent | 30min | Seq 1 |
| 4 | **F014/F015 确认**:模板路径+样式应用确认(如发现问题则修复) | F014, F015 | python-coding-agent | 15min | Seq 1 |
| 5 | **全量回归测试**:运行全部测试用例确保无回归 | F013-F017 | test-qa-agent | 15min | Seq 2-4 |
| 6 | **文档生成**:更新 features.md, tasks.md, CHANGELOG.md | F013-F017 | doc-gen-agent | 15min | Seq 5 |
| 7 | **打包发布 v1.6** | F013-F017 | package-release-agent | 15min | Seq 6 |
### 9.2 推荐执行顺序
```
Step 1: python-coding-agent → F013 编码pinmap_parser.py 修改)
Step 2: test-qa-agent → F017 测试用例(依赖修复后解析器)
Step 3: test-qa-agent → F016 测试用例(与 F017 并行)
Step 4: python-coding-agent → F014/F015 确认/修复(如有需要)
Step 5: test-qa-agent → 全量回归测试
Step 6: doc-gen-agent → 文档更新
Step 7: package-release-agent → 打包发布
```
### 9.3 可并行项
- F016 测试用例Seq 3和 F017 测试用例Seq 2可以在 F013 编码完成后**并行执行**,因为它们都依赖 F013 修复但不相互依赖。
- F014/F015 确认Seq 4可以与测试用例并行。
---
## 10. 风险与缓解措施
| 风险 ID | 风险描述 | 影响 | 概率 | 缓解措施 |
|---------|---------|------|------|---------|
| R1 | Top 边自动检测逻辑误判(如模板 PinMAP 中 Name 和 Number 行都是空或非常规格式) | Top 边引脚仍然丢失 | 低 | 设置 70% 置信阈值 + 回退到默认行为;在检测失败时打印 WARN 日志 |
| R2 | 用户 PinMAP 的底部Bottom边也存在类似反转问题但尚未反馈 | 底部引脚也丢失 | 中 | 如果 F017 测试发现底部也有问题,在 v1.6 中一并修复(扩大自动检测范围到下边) |
| R3 | 用户期望 PinMAP 布局与示例完全一致(像素级),但当前算法是均匀分配 | 用户不满意生成的 PinMAP 外观 | 中 | 在 F016 的实现中明确声明"往返正确性验证",布局一致性留到 v1.7 |
| R4 | F013 的修改改变了现有 4×4/12-pin 测试的预期行为 | 回归测试失败 | 低 | 自动检测会选择布局 A与 v1.5.5 一致的 Name 在上方),对现有测试透明 |
| R5已关闭 | ~~QFN60 12×12 网格周长48与 60 引脚不匹配~~ | — | — | ✅ 已确认 QFN60 是 15×15 网格,周长 = (15+15)×2 = 60完美匹配 |
---
## 11. v1.6 与 v1.5.5 代码差异预估
### 11.1 新增代码
| 位置 | 内容 | 行数 |
|------|------|------|
| `pinmap_parser.py` Step 2.5 | Top 边布局自动检测函数 `_detect_top_layout()` | ~30 行 |
| `pinmap_parser.py` Step 3 | 修改上边 Name 查找(替换现有 5 行) | +5 行 |
| `pinmap_parser.py` Step 4d | 修改上边遍历(替换现有 2 行) | +2 行 |
| `test_pinmap.py` | QFN60 cells 常量(~60 pins, 12×12 | ~80 行 |
| `test_pinmap.py` | F017 测试函数 (解析 + 转换 + 往返) | ~60 行 |
| `test_pinmap.py` | F016 测试函数 (生成 + 往返) | ~40 行 |
### 11.2 预计不修改的文件
- `main.py` — 模板搜索路径已在 v1.5.5 修复(除非用户坚持改为项目根目录优先)
- `pinmap_layout.py` — List→MAP 生成布局不变
- `pinmap_generator.py` — 无变化
- `pinlist_generator.py` — 无变化
- `template_reader.py` — 无变化
- `xlsx_writer.py` — 无变化
- `validator.py` — 无变化
- `pinlist_parser.py` — 无变化
- `pinlist_validator.py` — 无变化
### 11.3 总工作量预估
| 阶段 | 预估时间 |
|------|---------|
| 编码F013 | 1.0 h |
| 测试F016 + F017 | 0.75 h |
| 确认/修复F014 + F015 | 0.25 h |
| 回归测试 | 0.25 h |
| 文档 | 0.25 h |
| 打包 | 0.25 h |
| **合计** | **~2.75 h** |
---
## 12. 总结
1. **F013最关键**`pinmap_parser.py` 硬编码假设 Top 边 Name 在 Number 上方一行,但用户真实 PinMAP 中 Name 在 Number 下方一行。需要增加基于数据内容的**自动布局检测**,兼容两种方向。修改集中在 `pinmap_parser.py` 一个文件。
2. **F014/F015已有基础**:模板搜索路径在 v1.5.5 中已修复(`Code/src/Template/`样式应用链路完整。v1.6 主要是**确认性验证**,代码改动预计为零。
3. **F016/F017新增测试**:需要构建 QFN60 60-pin 的测试数据用于端到端验证。**核心关注往返正确性**List→MAP→List 不丢失引脚),而非与用户示例的像素级布局一致(后者需要后续版本支持非均匀边分配)。
4. **最大风险 R5**60 引脚与 12×12 网格的周长48不匹配。需要在测试实施阶段确认正确的行/列参数。如果用户 PinMAP 实际使用的是非标准布局(某些边有不同数量的引脚),可能需要向用户确认。
---
*文档结束 — v1.6 整改架构评估*