Files
pinmap-to-pinlist/Code/src/main.py

299 lines
12 KiB
Python
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 bidirectional converter
Usage:
python main.py # Interactive — choose direction + file
python main.py input.xls # MAP→List mode (legacy, specify file directly)
"""
import sys
import os
# ── Banner ──────────────────────────────────────────────────────────
def show_banner():
"""显示程序启动说明"""
print("=" * 60)
print(" PinMAP ↔ PinList 双向转换器")
print(" 支持 PinMAP→PinList 与 PinList→PinMAP 互转")
print(" 支持.xls和.xlsx格式输出.xlsx格式")
print("=" * 60)
print()
def wait_for_exit():
"""等待用户按键后退出Windows任意键其他平台Enter键"""
try:
import msvcrt
print("按任意键退出...")
msvcrt.getch()
except ImportError:
input("按Enter键退出...")
# ── Path helpers ────────────────────────────────────────────────────
def _build_output_path_map_to_list(input_path: str) -> str:
"""Generate output path: {original_filename}_PinList.xlsx"""
base, _ = os.path.splitext(input_path)
return f"{base}_PinList.xlsx"
def _build_output_path_list_to_map(input_path: str) -> str:
"""Generate output path: {original_filename}_PinMAP.xlsx"""
base, _ = os.path.splitext(input_path)
return f"{base}_PinMAP.xlsx"
# ── Direction 1: MAP → List ────────────────────────────────────────
def run_map_to_list(filepath: str):
"""执行 PinMAP → PinList 转换流程。"""
from file_selector import select_file
from xls_reader import read_excel_cells
from xlsx_reader import read_excel_cells as read_xlsx_cells
from pinmap_parser import parse_pinmap
from validator import validate_pinmap
from pinlist_generator import generate_pinlist
from xlsx_writer import write_xlsx
from models import FileFormatError, StructureError
# ── 1. File selection ───────────────────────────────────────────
if not filepath:
filepath = select_file(mode="map_to_list")
if not filepath:
print("未选择文件,退出。")
wait_for_exit()
return
# ── 2. Read Excel ───────────────────────────────────────────────
print(f"[INFO] 正在读取文件: {filepath}")
try:
if filepath.lower().endswith('.xlsx'):
cells = read_xlsx_cells(filepath)
else:
cells = read_excel_cells(filepath)
except Exception as e:
print(f"[FATAL] 文件读取失败: {e}")
wait_for_exit()
return
print(f"[INFO] 文件读取完成,共 {len(cells)} 个非空单元格")
# ── 3. Parse PinMAP ─────────────────────────────────────────────
print("[INFO] 正在解析 PinMAP 结构...")
try:
pinmap = parse_pinmap(cells)
print(f"[INFO] 解析完成: {pinmap.width}x{pinmap.height} 方形,共 {len(pinmap.pins)} 个Pin")
print(f"[INFO] 封装信息: {pinmap.package_info}")
except (FileFormatError, StructureError) as e:
print(f"[FATAL] 结构错误: {e}")
wait_for_exit()
return
# ── 4. Validate ─────────────────────────────────────────────────
print("[INFO] 正在验证数据...")
validation = validate_pinmap(pinmap)
if validation.errors:
print(f"[ERROR] 验证未通过,发现 {len(validation.errors)} 个错误:")
for err in validation.errors:
print(f" - {err.message}: {err.details}")
print("\n转换终止请修正PinMAP文件后重试。")
wait_for_exit()
return
if validation.warnings:
print(f"[WARN] 发现 {len(validation.warnings)} 个警告:")
for warn in validation.warnings:
print(f" - {warn.message}: {warn.details}")
else:
print("[INFO] 验证通过")
# ── 5. Generate PinList ─────────────────────────────────────────
print("[INFO] 正在生成 PinList...")
pinlist = generate_pinlist(pinmap, validation)
# ── 6. Write XLSX ───────────────────────────────────────────────
output_path = _build_output_path_map_to_list(filepath)
print(f"[INFO] 正在写入输出文件: {output_path}")
try:
data = {}
data['A1'] = pinlist.package_info
for i, (pin_name, pin_num) in enumerate(pinlist.rows):
row = i + 2
data[f'A{row}'] = pin_name
data[f'B{row}'] = str(pin_num)
write_xlsx(data, output_path)
except Exception as e:
print(f"[FATAL] 输出失败: {e}")
wait_for_exit()
return
# ── 7. Result summary ───────────────────────────────────────────
print()
print("[SUCCESS] 转换完成!")
print(f" 输出文件: {output_path}")
print(f" 封装信息: {pinlist.package_info}")
print(f" Pin数量: {len(pinlist.rows)}")
wait_for_exit()
# ── Direction 2: List → MAP ────────────────────────────────────────
def run_list_to_map(filepath: str):
"""执行 PinList → PinMAP 转换流程。"""
from file_selector import select_file
from pinlist_parser import parse_pinlist
from pinlist_validator import validate_pinlist
from pinmap_generator import generate_pinmap, generate_output_path
from template_reader import read_template_styles
from models import StructureError, LayoutError
# ── 1. File selection ───────────────────────────────────────────
if not filepath:
filepath = select_file(mode="list_to_map")
if not filepath:
print("未选择文件,退出。")
wait_for_exit()
return
# ── 2. Input PinMAP dimensions ──────────────────────────────────
while True:
try:
rows_input = input("请输入 PinMAP 行数: ").strip()
rows = int(rows_input)
if rows < 2:
print("[ERROR] 行数至少为 2")
continue
break
except ValueError:
print("[ERROR] 请输入有效的整数")
while True:
try:
cols_input = input("请输入 PinMAP 列数: ").strip()
cols = int(cols_input)
if cols < 2:
print("[ERROR] 列数至少为 2")
continue
break
except ValueError:
print("[ERROR] 请输入有效的整数")
print(f"[INFO] PinMAP 尺寸: {rows}× {cols}")
# ── 3. Parse PinList ────────────────────────────────────────────
print(f"[INFO] 正在解析 PinList 文件: {filepath}")
try:
package_info, entries = parse_pinlist(filepath)
print(f"[INFO] 解析完成: 封装信息 '{package_info}', 共 {len(entries)} 个引脚")
except StructureError as e:
print(f"[FATAL] 解析失败: {e}")
wait_for_exit()
return
# ── 4. Validate ─────────────────────────────────────────────────
print("[INFO] 正在验证数据...")
validation = validate_pinlist(entries, rows, cols)
if validation.errors:
print(f"[ERROR] 验证未通过,发现 {len(validation.errors)} 个错误:")
for err in validation.errors:
print(f" - {err.message}: {err.details}")
print("\n转换终止请修正PinList文件或网格尺寸后重试。")
wait_for_exit()
return
if validation.warnings:
print(f"[WARN] 发现 {len(validation.warnings)} 个警告:")
for warn in validation.warnings:
print(f" - {warn.message}: {warn.details}")
else:
print("[INFO] 验证通过")
# ── 5. Generate PinMAP ──────────────────────────────────────────
output_path = generate_output_path(filepath)
print(f"[INFO] 正在生成 PinMAP 并写入: {output_path}")
try:
# 尝试读取模板样式(优雅降级)
template_style = read_template_styles(filepath)
generate_pinmap(
entries=entries,
rows=rows,
cols=cols,
package_info=package_info,
template_style=template_style,
output_path=output_path,
)
except LayoutError as e:
print(f"[FATAL] 布局计算失败: {e}")
wait_for_exit()
return
except Exception as e:
print(f"[FATAL] 输出失败: {e}")
wait_for_exit()
return
# ── 6. Result summary ───────────────────────────────────────────
print()
print("[SUCCESS] 转换完成!")
print(f" 输出文件: {output_path}")
print(f" 封装信息: {package_info}")
print(f" PinMAP 尺寸: {rows}×{cols}")
print(f" Pin数量: {len(entries)}")
wait_for_exit()
# ── Main entry ──────────────────────────────────────────────────────
def main():
show_banner()
# ── Direction selection ─────────────────────────────────────────
if len(sys.argv) > 1:
# Legacy mode: direct file argument → MAP→List
direction = 1
filepath = sys.argv[1]
else:
print("请选择转换方向:")
print(" 1 — PinMAP → PinList")
print(" 2 — PinList → PinMAP")
print()
while True:
choice = input("请输入选项 (1/2): ").strip()
if choice in ('1', '2'):
direction = int(choice)
break
print("[ERROR] 无效选项,请输入 1 或 2")
filepath = None # will be selected inside the flow
# ── Dispatch ────────────────────────────────────────────────────
if direction == 1:
print()
print("" * 40)
print(" 方向: PinMAP → PinList")
print("" * 40)
print()
run_map_to_list(filepath)
else:
print()
print("" * 40)
print(" 方向: PinList → PinMAP")
print("" * 40)
print()
run_list_to_map(filepath)
if __name__ == '__main__':
main()