Skip to content

Commit afc88bd

Browse files
author
DravinceG16
committed
v1.0.5.3
bugfix:CRC分段计算时多了一个字节; modify:CRC计算多项式改为MPEG2,使用CRCMOD库; bugfix:更换项目之后编译的还是上一个项目,相关路径没有更新; update:添加IAR返回值判断; update:调用编译的命令内容打印到日志窗口 bugfix:flash偏移地址存在默认值,改为必须从链接文件中获取;
1 parent 6bb0147 commit afc88bd

10 files changed

Lines changed: 207 additions & 106 deletions

Embedded_Firmware_Manager.spec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ a = Analysis(
1111
('user_config.example.json', '.'),
1212
('docs', 'docs'),
1313
],
14-
hiddenimports=[],
14+
hiddenimports=['crcmod'],
1515
hookspath=[],
1616
hooksconfig={},
1717
runtime_hooks=[],
@@ -31,7 +31,7 @@ exe = EXE(
3131
a.zipfiles,
3232
a.datas,
3333
[],
34-
name='Embedded_Firmware_Manager_v1.0.5.2',
34+
name='Embedded_Firmware_Manager_v1.0.5.3',
3535
debug=False,
3636
bootloader_ignore_signals=False,
3737
strip=False,

binary_modifier.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
77
CRC32多项式信息:
88
- 标准多项式: X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 + X8 + X7 + X5 + X4 + X2 + X + 1
9-
- 十六进制表示: 0x04C11DB7 (标准形式) / 0xEDB88320 (反向形式)
10-
- 使用库: zlib.crc32() (使用反向形式 0xEDB88320)
11-
- 标准: IEEE 802.3 (以太网标准)
9+
- 十六进制表示: 0x04C11DB7 (正向形式,MPEG2标准)
10+
- 初始值: 0xFFFFFFFF
11+
- 标准: MPEG2 CRC-32
1212
"""
1313

1414
import os
1515
import struct
1616
import hashlib
17-
import zlib
17+
import crcmod
1818
from lib_logger import logger
1919
from typing import Tuple, Optional
2020
from pathlib import Path
@@ -106,27 +106,36 @@ def __init__(self, config: dict, feature_settings: dict = None):
106106
logger.info(f"哈希校验和偏移量: 0x{self.hash_value_offset:X} -> 相对偏移: 0x{self.actual_hash_value_offset:X}")
107107
else:
108108
self.actual_hash_value_offset = 0
109+
110+
# MPEG2 CRC32参数(用于所有段)
111+
# 多项式: 0x04C11DB7,初始值通过initial参数控制
112+
self.crc_polynomial = 0x104C11DB7
109113

110-
def calculate_crc32(self, data: bytes) -> int:
114+
def calculate_crc32(self, data: bytes, initial: int = 0xFFFFFFFF) -> int:
111115
"""
112-
计算CRC32值
116+
计算CRC32值(MPEG2标准,使用crcmod)
113117
114-
使用IEEE 802.3标准的CRC-32多项式:
118+
使用MPEG2标准的CRC-32多项式:
115119
多项式: X32 + X26 + X23 + X22 + X16 + X12 + X11 + X10 + X8 + X7 + X5 + X4 + X2 + X + 1
116-
十六进制: 0x04C11DB7 (标准形式) / 0xEDB88320 (反向形式,zlib使用)
120+
十六进制: 0x04C11DB7 (正向形式,MPEG2使用)
117121
118122
Args:
119123
data: 要计算CRC的数据
124+
initial: 初始CRC值(用于分段累加)
120125
121126
Returns:
122127
int: CRC32值
123128
"""
124-
return zlib.crc32(data) & 0xFFFFFFFF
129+
# 所有段都使用crcmod,动态创建函数
130+
crc_func = crcmod.mkCrcFun(self.crc_polynomial, initCrc=initial, rev=False, xorOut=0x00000000)
131+
return crc_func(data) & 0xFFFFFFFF
125132

126133
def calculate_file_crc(self, file_path: str) -> int:
127134
"""
128135
计算文件的CRC32值,排除CRC值和hash值存储区域
129136
137+
注意:文件大小和commit ID不包括在CRC计算中,因为它们是在CRC计算之前写入的
138+
130139
Args:
131140
file_path: 文件路径
132141
@@ -139,33 +148,33 @@ def calculate_file_crc(self, file_path: str) -> int:
139148

140149
# 排除CRC值存储区域(4字节)
141150
crc_start = self.actual_bin_checksum_offset
142-
crc_end = crc_start + self.crc_size
151+
crc_end = crc_start + self.crc_size - 1 # 结束位置 = 起始位置 + 长度 - 1
143152

144153
# 排除hash值存储区域(32字节)- 只有在启用hash功能时才排除
145154
if self.enable_hash_value:
146155
hash_start = self.actual_hash_value_offset
147-
hash_end = hash_start + 32
156+
hash_end = hash_start + 32 - 1 # 结束位置 = 起始位置 + 长度 - 1
148157
else:
149158
hash_start = 0
150159
hash_end = 0
151160

152161
# 分段计算CRC
153-
crc_value = 0
162+
crc_value = 0xFFFFFFFF # MPEG2 CRC32初始值
154163

155164
# 第一段:文件开始到CRC值之前
156165
if crc_start > 0 and crc_start < len(data):
157-
crc_value = zlib.crc32(data[:crc_start], crc_value) & 0xFFFFFFFF
166+
crc_value = self.calculate_crc32(data[:crc_start], crc_value) & 0xFFFFFFFF
158167

159168
# 第二段:CRC值之后到hash值之前(如果hash值存在)
160169
if self.enable_hash_value and crc_end < hash_start and crc_end < len(data):
161-
crc_value = zlib.crc32(data[crc_end:hash_start], crc_value) & 0xFFFFFFFF
170+
crc_value = self.calculate_crc32(data[crc_end + 1:hash_start], crc_value) & 0xFFFFFFFF
162171
elif not self.enable_hash_value and crc_end < len(data):
163172
# 如果hash值不存在,直接从CRC值之后到文件结束
164-
crc_value = zlib.crc32(data[crc_end:], crc_value) & 0xFFFFFFFF
173+
crc_value = self.calculate_crc32(data[crc_end + 1:], crc_value) & 0xFFFFFFFF
165174

166175
# 第三段:hash值之后到文件结束(只有在hash值存在时才执行)
167176
if self.enable_hash_value and hash_end < len(data):
168-
crc_value = zlib.crc32(data[hash_end:], crc_value) & 0xFFFFFFFF
177+
crc_value = self.calculate_crc32(data[hash_end + 1:], crc_value) & 0xFFFFFFFF
169178

170179
# 记录排除的区域信息
171180
excluded_regions = f"CRC值区域(0x{crc_start:X}-0x{crc_end:X})"

build_exe.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def create_spec_file(spec_name, exe_name):
117117
('user_config.example.json', '.'),
118118
('docs', 'docs'),
119119
],
120-
hiddenimports=[],
120+
hiddenimports=['crcmod'],
121121
hookspath=[],
122122
hooksconfig={{}},
123123
runtime_hooks=[],

lib_IAR/builder.py

Lines changed: 55 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -252,11 +252,13 @@ def clean_project(self) -> bool:
252252
cwd=os.path.dirname(self.iar_project_path) if self.iar_project_path else None # 设置工作目录
253253
)
254254

255+
logger.info(f"清理命令返回码: {result.returncode}")
255256
if result.returncode == 0:
256-
logger.info("项目清理成功")
257+
logger.info("项目清理成功(返回码 0)")
257258
return True
258259
else:
259-
logger.error(f"项目清理失败: {result.stderr}")
260+
logger.error(f"项目清理失败(返回码 {result.returncode})")
261+
logger.error(f"错误输出: {result.stderr}")
260262
return False
261263

262264
except subprocess.TimeoutExpired:
@@ -303,18 +305,28 @@ def build_project(self, force_rebuild: bool = False) -> Tuple[bool, str]:
303305
self.build_config
304306
]
305307

306-
logger.info(f"执行命令: {' '.join(cmd)}")
308+
# 打印完整的编译命令(重要:便于用户确认使用的项目文件)
309+
logger.info("=" * 80)
310+
logger.info("IAR编译命令:")
311+
logger.info(f" 完整命令: {' '.join(cmd)}")
312+
logger.info(f" 工作目录: {os.path.dirname(self.iar_project_path) if self.iar_project_path else 'None'}")
313+
logger.info("=" * 80)
307314

308-
# 调试信息
309-
logger.info(f"IAR可执行文件路径: {self.iar_exe_path}")
310-
logger.info(f"项目文件路径: {self.iar_project_path}")
311-
logger.info(f"工作目录: {os.path.dirname(self.iar_project_path) if self.iar_project_path else None}")
315+
# 验证项目文件是否正确
316+
if self.iar_project_path:
317+
logger.info(f"使用项目文件: {self.iar_project_path}")
318+
logger.info(f"项目文件存在: {os.path.exists(self.iar_project_path)}")
319+
if os.path.exists(self.iar_project_path):
320+
file_size = os.path.getsize(self.iar_project_path)
321+
logger.info(f"项目文件大小: {file_size} 字节")
322+
else:
323+
logger.error("项目文件路径为空!")
312324

313325
# 检查文件是否存在
314326
if not os.path.exists(self.iar_exe_path):
315-
return False, f"IAR可执行文件不存在: {self.iar_exe_path}"
327+
return False, f"IAR可执行文件不存在: {self.iar_exe_path}\n尝试使用的项目文件: {self.iar_project_path}"
316328
if not os.path.exists(self.iar_project_path):
317-
return False, f"项目文件不存在: {self.iar_project_path}"
329+
return False, f"项目文件不存在: {self.iar_project_path}\n请检查项目路径是否正确"
318330

319331
# 执行编译
320332
start_time = time.time()
@@ -354,7 +366,11 @@ def build_project(self, force_rebuild: bool = False) -> Tuple[bool, str]:
354366
)
355367

356368
# 如果成功执行,跳出循环
357-
logger.info(f"编译尝试 {attempt + 1} 成功执行,返回码: {result.returncode}")
369+
logger.info(f"编译尝试 {attempt + 1} 完成,返回码: {result.returncode}")
370+
logger.info(f"返回码 {result.returncode} 表示: {'编译成功' if result.returncode == 0 else '编译失败'}")
371+
if result.returncode != 0:
372+
logger.error(f"编译失败,返回码: {result.returncode}")
373+
logger.error(f"错误输出: {result.stderr[:500]}")
358374
break
359375

360376
except PermissionError as e:
@@ -381,14 +397,18 @@ def build_project(self, force_rebuild: bool = False) -> Tuple[bool, str]:
381397
logger.info(f"编译耗时: {compile_time:.2f}秒")
382398

383399
# 分析编译结果
400+
logger.info(f"编译命令返回码: {result.returncode}")
401+
402+
# 准备编译命令信息字符串(用于GUI显示,不包含输出)
403+
cmd_info = f"使用项目文件: {self.iar_project_path}\n命令: {' '.join(cmd)}\n"
404+
384405
if result.returncode == 0:
385-
logger.info("编译成功")
386-
output_info = f"编译成功\n耗时: {compile_time:.2f}\n\n输出信息:\n{result.stdout}"
387-
return True, output_info
406+
logger.info("编译成功(返回码 0)")
407+
return True, f"编译成功,耗时: {compile_time:.2f}秒"
388408
else:
389-
logger.error("编译失败")
390-
error_info = f"编译失败\n返回码: {result.returncode}\n\n错误信息:\n{result.stderr}\n\n输出信息:\n{result.stdout}"
391-
return False, error_info
409+
logger.error(f"编译失败(返回码 {result.returncode}")
410+
logger.error(f"错误输出: {result.stderr[:500] if result.stderr else '无错误输出'}")
411+
return False, f"编译失败,返回码: {result.returncode}"
392412

393413
except subprocess.TimeoutExpired:
394414
logger.error("编译超时")
@@ -486,6 +506,25 @@ def build_and_check(self) -> Tuple[bool, str, dict]:
486506

487507
return success, message, bin_info
488508

509+
def get_command_string(self, only_version_changed: bool = True) -> str:
510+
"""
511+
获取编译命令字符串(用于GUI显示)
512+
513+
Args:
514+
only_version_changed: 是否只有版本号发生变化
515+
516+
Returns:
517+
str: 编译命令字符串
518+
"""
519+
should_clean = self.clean_before_build or (not only_version_changed)
520+
521+
if should_clean:
522+
cmd = [self.iar_exe_path, self.iar_project_path, '-build', self.build_config]
523+
else:
524+
cmd = [self.iar_exe_path, self.iar_project_path, '-make', self.build_config]
525+
526+
return ' '.join(cmd)
527+
489528
def smart_build(self, only_version_changed: bool = True) -> Tuple[bool, str]:
490529
"""
491530
智能编译:根据修改内容决定编译策略
@@ -497,10 +536,8 @@ def smart_build(self, only_version_changed: bool = True) -> Tuple[bool, str]:
497536
Tuple[bool, str]: (编译是否成功, 输出信息)
498537
"""
499538
if only_version_changed:
500-
logger.info("检测到仅版本号变化,使用增量编译(make)")
501539
return self.build_project(force_rebuild=False)
502540
else:
503-
logger.info("检测到代码变化,使用清理编译(rebuild all)")
504541
return self.build_project(force_rebuild=True)
505542

506543

lib_IAR/path_manager.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -369,12 +369,13 @@ def validate_paths(self, paths: dict) -> Tuple[bool, List[str]]:
369369

370370
return len(invalid_paths) == 0, invalid_paths
371371

372-
def auto_find_paths(self, config: dict) -> dict:
372+
def auto_find_paths(self, config: dict, selected_config: Dict = None) -> dict:
373373
"""
374374
自动查找并更新配置中的路径
375375
376376
Args:
377377
config: 配置字典
378+
selected_config: 用户选择的配置(Debug或Release),必须提供
378379
379380
Returns:
380381
dict: 更新后的配置字典
@@ -396,28 +397,30 @@ def auto_find_paths(self, config: dict) -> dict:
396397
updated_config['iar_workspace_path'] = workspace_path # 同时更新根级别
397398
logger.info(f"自动找到IAR工作区文件: {workspace_path}")
398399

399-
# 查找IAR项目文件
400-
current_project = project_settings.get('iar_project_path', '')
401-
if not current_project or not os.path.exists(current_project):
402-
project_path = self.find_iar_project()
403-
if project_path:
404-
project_settings['iar_project_path'] = project_path
405-
updated_config['iar_project_path'] = project_path # 同时更新根级别
406-
logger.info(f"自动找到IAR项目文件: {project_path}")
400+
# 查找IAR项目文件(总是重新查找,确保使用最新的项目路径)
401+
logger.info("重新查找IAR项目文件...")
402+
project_path = self.find_iar_project()
403+
if project_path:
404+
project_settings['iar_project_path'] = project_path
405+
updated_config['iar_project_path'] = project_path # 同时更新根级别
406+
logger.info(f"找到IAR项目文件: {project_path}")
407+
else:
408+
logger.warning("未找到IAR项目文件")
407409

408-
# 查找bin文件
409-
current_bin = project_settings.get('output_bin_path', '')
410-
if not current_bin or not os.path.exists(current_bin):
411-
# 尝试从项目文件中获取项目名称
410+
# 查找bin文件(基于最新找到的项目文件)
411+
if project_path: # 只在找到项目文件后才查找bin文件
412412
project_name = self._extract_project_name_from_ewp(project_settings.get('iar_project_path', ''))
413413
if not project_name:
414414
project_name = 'MCU' # 默认项目名称
415415

416+
logger.info(f"基于项目名称查找bin文件: {project_name}")
416417
bin_path = self.find_bin_file(project_name)
417418
if bin_path:
418419
project_settings['output_bin_path'] = bin_path
419420
updated_config['output_bin_path'] = bin_path # 同时更新根级别
420-
logger.info(f"自动找到bin文件: {bin_path}")
421+
logger.info(f"找到bin文件: {bin_path}")
422+
else:
423+
logger.warning(f"未找到bin文件(项目名: {project_name})")
421424

422425
# 自动获取flash偏移地址
423426
if 'binary_settings' not in updated_config:
@@ -427,16 +430,22 @@ def auto_find_paths(self, config: dict) -> dict:
427430
logger.info(f"当前bin_start_address: 0x{current_bin_start:X}")
428431
if current_bin_start == 0:
429432
logger.info("尝试自动获取flash偏移地址...")
430-
flash_offset = self.get_flash_offset_from_configuration()
433+
# 必须使用用户选择的配置
434+
if not selected_config:
435+
error_msg = "必须提供用户选择的配置(Debug或Release),请先选择配置"
436+
logger.error(error_msg)
437+
raise ValueError(error_msg)
438+
439+
logger.info(f"使用用户选择的配置: {selected_config.get('name', 'Unknown')}")
440+
flash_offset = self.get_flash_offset_from_configuration(selected_config)
441+
431442
if flash_offset:
432443
updated_config['binary_settings']['bin_start_address'] = flash_offset
433444
logger.info(f"自动获取flash偏移地址成功: 0x{flash_offset:X}")
434445
else:
435-
# 如果无法自动获取,设置一个默认值(STM32的常见起始地址)
436-
default_flash_offset = 0x08000000
437-
updated_config['binary_settings']['bin_start_address'] = default_flash_offset
438-
logger.warning(f"无法自动获取flash偏移地址,使用默认值: 0x{default_flash_offset:X}")
439-
logger.warning("请检查IAR项目文件是否正确配置,或手动设置正确的起始地址")
446+
error_msg = "无法从ICF文件获取flash偏移地址,请检查IAR项目配置"
447+
logger.error(error_msg)
448+
raise ValueError(error_msg)
440449
else:
441450
logger.info(f"使用已配置的bin_start_address: 0x{current_bin_start:X}")
442451

0 commit comments

Comments
 (0)