F013: Code/src/pinmap_parser.py 增加 Top 边自动布局检测 F014/F015: 双向模板样式确认 F016/F017: 新增 5 个 QFN60 端到端测试
573 lines
25 KiB
Markdown
573 lines
25 KiB
Markdown
# PinMAP ↔ PinList 双向转换器 — v1.6 整改架构评估
|
||
|
||
> **版本**: v1.6 (针对 F013–F017 五项 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_row,Number 在 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 3),Number 在 `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 ─────────────────────────────────
|
||
# 检测上边布局:哪种布局由数据内容决定
|
||
# 布局 A:Name 在 min_row(上方),Number 在 min_row+1(下方)
|
||
# 例:Row 1: Pin60 Pin59 ... Pin46, Row 2: 60 59 ... 46
|
||
# 布局 B:Number 在 min_row(上方),Name 在 min_row+1(下方)
|
||
# 例:Row 1: 60 59 ... 46, Row 2: Pin60 Pin59 ... Pin46
|
||
|
||
min_row_for_top = min_row # 可能是 1(A1 被排除后)
|
||
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:
|
||
# 布局 B:Number 在上,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:
|
||
# 布局 A:Name 在上,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 列 = PinName,B 列 = 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 2(bold),其他用 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 示例 PinList(60 个引脚)作为输入,验证生成的 PinMAP 结构与示例 PinMAP 一致。
|
||
|
||
**示例 PinList 结构**:
|
||
```
|
||
QFN60
|
||
Pin1,1
|
||
Pin2,2
|
||
...
|
||
Pin60,60
|
||
```
|
||
|
||
**示例 PinMAP 预期结构**(基于用户提供的 CSV):
|
||
- 12×12 网格(QFN60 环形布局)
|
||
- Left 边:Pin1–Pin12(row 4-15, col A/B)
|
||
- Bottom 边:Pin13–Pin18 + Pin28–Pin33(row 16-17, 中间空列对应无引脚位置)
|
||
- Right 边:Pin34–Pin45(row 15→4, col 倒数两列)
|
||
- Top 边:Pin46–Pin60(row 3, col 倒数→前;row 2 为 Numbers)
|
||
- 内部为空(QFN 封装中心无引脚)
|
||
|
||
### 6.2 关键问题:60 引脚的环形布局 ≠ 12×12 全周长
|
||
|
||
12×12 网格的全周长 = (12+12)×2 = 48。但 QFN60 有 **60 个引脚**。这意味着用户使用的"12×12 网格"并不是严格的矩形周长概念。
|
||
|
||
重新分析用户 PinMAP:
|
||
- Left 边:12 个引脚(Pin1–Pin12)
|
||
- Right 边:12 个引脚(Pin34–Pin45)
|
||
- Top 边:15 个引脚(Pin46–Pin60,跨越 15 列)
|
||
- Bottom 边:21 个引脚(Pin13–Pin33,跨越 21 列?不对,重看)
|
||
|
||
仔细看用户实际 PinMAP 布局:
|
||
- Left:12 个
|
||
- Bottom:12 个(Pin13–Pin18=6 + Pin24–Pin33?不对)
|
||
|
||
让我重新分析 CSV 模式:
|
||
- Left col A/B:12 pins (Pin1–Pin12)
|
||
- Right col 最后两列:12 pins (Pin34–Pin45)
|
||
- Top row 2-3:15 pins (Pin46–Pin60)
|
||
- Bottom 倒数第1-2行:15 pins? → 实际底部仅 Pin13–Pin18=6 + ... 需要确认
|
||
|
||
实际上用户 CSV 格式是 12×12(12行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 (Pin1–Pin60)
|
||
- 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 个引脚,顺序 Pin1–Pin60,封装信息 "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 序号 Pin1–Pin60 完整无缺失
|
||
4. 封装信息 "QFN60 ..." 正确提取
|
||
5. PinName 与示例一致
|
||
|
||
### 7.2 测试用例构建
|
||
|
||
**输入**:用户提供的 QFN60 PinMAP CSV(转换为 Excel 或直接用当前 xls_reader 兼容的格式)
|
||
|
||
由于当前解析器读取的是 Excel 格式(.xls/.xlsx),需要先构建测试用的 cell dictionary。
|
||
|
||
**测试 F017-1: QFN60 PinMAP 解析**
|
||
|
||
基于用户提供的 CSV 构建 cells dict(0-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 (Pin46–Pin60) 都被正确识别且有正确的 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 整改架构评估*
|