refind logger
This commit is contained in:
215
ota_manager.py
215
ota_manager.py
@@ -51,6 +51,11 @@ class OTAManager:
|
||||
|
||||
# ==================== 状态访问(只读属性)====================
|
||||
|
||||
@property
|
||||
def logger(self):
|
||||
"""获取 logger 对象"""
|
||||
return logger_manager.logger
|
||||
|
||||
@property
|
||||
def update_thread_started(self):
|
||||
"""OTA线程是否已启动"""
|
||||
@@ -128,14 +133,10 @@ class OTAManager:
|
||||
|
||||
filename_lower = filename.lower()
|
||||
if filename_lower.endswith('.zip'):
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.info(f"[EXTRACT] 检测到ZIP文件(扩展名: .zip)")
|
||||
self.logger.info(f"[EXTRACT] 检测到ZIP文件(扩展名: .zip)")
|
||||
return True, 'zip'
|
||||
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.info(f"[EXTRACT] 不是ZIP格式(扩展名: {os.path.splitext(filename)[1] or '无'})")
|
||||
self.logger.info(f"[EXTRACT] 不是ZIP格式(扩展名: {os.path.splitext(filename)[1] or '无'})")
|
||||
return False, None
|
||||
|
||||
def extract_zip_archive(self, archive_path, extract_to_dir=None, target_file=None):
|
||||
@@ -153,8 +154,7 @@ class OTAManager:
|
||||
if extract_to_dir is None:
|
||||
extract_to_dir = os.path.dirname(archive_path) or '/tmp'
|
||||
|
||||
logger = logger_manager.logger
|
||||
logger.info(f"[EXTRACT] 开始解压ZIP文件: {archive_path}")
|
||||
self.logger.info(f"[EXTRACT] 开始解压ZIP文件: {archive_path}")
|
||||
|
||||
try:
|
||||
os.makedirs(extract_to_dir, exist_ok=True)
|
||||
@@ -167,18 +167,18 @@ class OTAManager:
|
||||
result = os.system(cmd)
|
||||
|
||||
if result != 0:
|
||||
logger.warning(f"[EXTRACT] 直接解压目标文件失败,尝试解压所有文件...")
|
||||
self.logger.warning(f"[EXTRACT] 直接解压目标文件失败,尝试解压所有文件...")
|
||||
cmd_all = f"unzip -q -o '{archive_path}' -d '{extract_to_dir}' 2>&1"
|
||||
result_all = os.system(cmd_all)
|
||||
|
||||
if result_all != 0:
|
||||
logger.error(f"[EXTRACT] 解压失败,退出码: {result_all}")
|
||||
self.logger.error(f"[EXTRACT] 解压失败,退出码: {result_all}")
|
||||
return False, None
|
||||
|
||||
return True, extract_to_dir
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[EXTRACT] 解压过程出错: {e}")
|
||||
self.logger.error(f"[EXTRACT] 解压过程出错: {e}")
|
||||
return False, None
|
||||
|
||||
def apply_ota_and_reboot(self, ota_url=None, downloaded_file=None):
|
||||
@@ -203,12 +203,11 @@ class OTAManager:
|
||||
|
||||
ota_pending = f"{config.APP_DIR}/ota_pending.json"
|
||||
|
||||
logger = logger_manager.logger
|
||||
logger.info(f"[OTA] 准备应用OTA更新,下载文件: {downloaded_file}")
|
||||
self.logger.info(f"[OTA] 准备应用OTA更新,下载文件: {downloaded_file}")
|
||||
|
||||
try:
|
||||
if not os.path.exists(downloaded_file):
|
||||
logger.error(f"[OTA] 错误:{downloaded_file} 不存在")
|
||||
self.logger.error(f"[OTA] 错误:{downloaded_file} 不存在")
|
||||
return False
|
||||
|
||||
# 备份
|
||||
@@ -230,9 +229,9 @@ class OTAManager:
|
||||
f.write(str(counter))
|
||||
|
||||
backup_dir = os.path.join(backup_base, f"backup_{counter:04d}")
|
||||
logger.info(f"[OTA] 使用备份目录: {backup_dir} (第{counter}次OTA)")
|
||||
self.logger.info(f"[OTA] 使用备份目录: {backup_dir} (第{counter}次OTA)")
|
||||
except Exception as e:
|
||||
logger.error(f"[OTA] 生成备份目录名失败: {e},使用默认目录")
|
||||
self.logger.error(f"[OTA] 生成备份目录名失败: {e},使用默认目录")
|
||||
backup_dir = os.path.join(backup_base, "backup_0000")
|
||||
|
||||
# 清理旧备份
|
||||
@@ -255,11 +254,11 @@ class OTAManager:
|
||||
for item, dir_num, item_path in backup_dirs[config.MAX_BACKUPS:]:
|
||||
try:
|
||||
shutil.rmtree(item_path, ignore_errors=True)
|
||||
logger.info(f"[OTA] 已删除旧备份: {item}")
|
||||
self.logger.info(f"[OTA] 已删除旧备份: {item}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[OTA] 删除旧备份失败: {e}")
|
||||
self.logger.warning(f"[OTA] 删除旧备份失败: {e}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[OTA] 清理旧备份时出错: {e}")
|
||||
self.logger.warning(f"[OTA] 清理旧备份时出错: {e}")
|
||||
|
||||
os.makedirs(backup_dir, exist_ok=True)
|
||||
|
||||
@@ -286,16 +285,15 @@ class OTAManager:
|
||||
shutil.copy2(source_path, backup_path)
|
||||
backed_up_files.append(rel_path)
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"[OTA] 备份 {rel_path} 失败: {e}")
|
||||
self.logger.error(f"[OTA] 备份 {rel_path} 失败: {e}")
|
||||
|
||||
if backed_up_files:
|
||||
logger.info(f"[OTA] 总共备份了 {len(backed_up_files)} 个文件到 {backup_dir}")
|
||||
self.logger.info(f"[OTA] 总共备份了 {len(backed_up_files)} 个文件到 {backup_dir}")
|
||||
else:
|
||||
logger.warning(f"[OTA] 没有备份任何文件")
|
||||
self.logger.warning(f"[OTA] 没有备份任何文件")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[OTA] 备份过程出错: {e}")
|
||||
self.logger.error(f"[OTA] 备份过程出错: {e}")
|
||||
if not backup_dir:
|
||||
backup_dir = None
|
||||
|
||||
@@ -309,15 +307,15 @@ class OTAManager:
|
||||
with open(downloaded_file, "rb") as f:
|
||||
zip_header = f.read(4)
|
||||
if zip_header[:2] != b'PK':
|
||||
logger.error(f"[OTA] ZIP文件头验证失败: {zip_header.hex()}")
|
||||
self.logger.error(f"[OTA] ZIP文件头验证失败: {zip_header.hex()}")
|
||||
return False
|
||||
file_size = os.path.getsize(downloaded_file)
|
||||
logger.info(f"[OTA] ZIP文件验证通过: 大小={file_size} bytes, 头={zip_header.hex()}")
|
||||
self.logger.info(f"[OTA] ZIP文件验证通过: 大小={file_size} bytes, 头={zip_header.hex()}")
|
||||
except Exception as e:
|
||||
logger.error(f"[OTA] ZIP文件验证异常: {e}")
|
||||
self.logger.error(f"[OTA] ZIP文件验证异常: {e}")
|
||||
return False
|
||||
|
||||
logger.info(f"[OTA] 检测到ZIP压缩包,开始解压...")
|
||||
self.logger.info(f"[OTA] 检测到ZIP压缩包,开始解压...")
|
||||
extract_dir = "/tmp/ota_extract"
|
||||
try:
|
||||
os.makedirs(extract_dir, exist_ok=True)
|
||||
@@ -339,12 +337,12 @@ class OTAManager:
|
||||
files_to_copy.append((source_path, rel_path))
|
||||
|
||||
if files_to_copy:
|
||||
logger.info(f"[OTA] 解压成功,共 {len(files_to_copy)} 个文件")
|
||||
self.logger.info(f"[OTA] 解压成功,共 {len(files_to_copy)} 个文件")
|
||||
else:
|
||||
logger.error(f"[OTA] 解压成功但未找到任何文件")
|
||||
self.logger.error(f"[OTA] 解压成功但未找到任何文件")
|
||||
return False
|
||||
else:
|
||||
logger.error(f"[OTA] 解压失败")
|
||||
self.logger.error(f"[OTA] 解压失败")
|
||||
return False
|
||||
else:
|
||||
# 单个文件更新:从下载的文件名推断目标文件名
|
||||
@@ -359,11 +357,11 @@ class OTAManager:
|
||||
target_rel_path = filename
|
||||
|
||||
files_to_copy = [(downloaded_file, target_rel_path)]
|
||||
logger.info(f"[OTA] 单个文件更新: {downloaded_file} -> {target_rel_path}")
|
||||
self.logger.info(f"[OTA] 单个文件更新: {downloaded_file} -> {target_rel_path}")
|
||||
|
||||
# 复制文件
|
||||
if not files_to_copy:
|
||||
logger.error(f"[OTA] 没有文件需要复制")
|
||||
self.logger.error(f"[OTA] 没有文件需要复制")
|
||||
return False
|
||||
|
||||
copied_files = []
|
||||
@@ -372,7 +370,7 @@ class OTAManager:
|
||||
|
||||
# 检查源文件和目标文件是否是同一个文件(避免复制到自身)
|
||||
if os.path.abspath(source_path) == os.path.abspath(dest_path):
|
||||
logger.warning(f"[OTA] 源文件和目标文件相同,跳过复制: {rel_path} (文件已在正确位置)")
|
||||
self.logger.warning(f"[OTA] 源文件和目标文件相同,跳过复制: {rel_path} (文件已在正确位置)")
|
||||
copied_files.append(rel_path)
|
||||
continue
|
||||
|
||||
@@ -386,13 +384,13 @@ class OTAManager:
|
||||
try:
|
||||
shutil.copy2(source_path, dest_path)
|
||||
copied_files.append(rel_path)
|
||||
logger.info(f"[OTA] 已复制: {rel_path}")
|
||||
self.logger.info(f"[OTA] 已复制: {rel_path}")
|
||||
except Exception as e:
|
||||
logger.error(f"[OTA] 复制 {rel_path} 失败: {e}")
|
||||
self.logger.error(f"[OTA] 复制 {rel_path} 失败: {e}")
|
||||
return False
|
||||
|
||||
if copied_files:
|
||||
logger.info(f"[OTA] 成功复制 {len(copied_files)} 个文件到应用目录")
|
||||
self.logger.info(f"[OTA] 成功复制 {len(copied_files)} 个文件到应用目录")
|
||||
|
||||
# 确保写入磁盘
|
||||
try:
|
||||
@@ -421,7 +419,7 @@ class OTAManager:
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.error(f"[OTA] 写入 ota_pending 失败: {e}")
|
||||
self.logger.error(f"[OTA] 写入 ota_pending 失败: {e}")
|
||||
|
||||
# 通知服务器(延迟导入避免循环导入)
|
||||
from network import safe_enqueue
|
||||
@@ -433,9 +431,9 @@ class OTAManager:
|
||||
try:
|
||||
if os.path.exists(extract_dir):
|
||||
shutil.rmtree(extract_dir, ignore_errors=True)
|
||||
logger.info(f"[OTA] 已清理临时解压目录: {extract_dir}")
|
||||
self.logger.info(f"[OTA] 已清理临时解压目录: {extract_dir}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[OTA] 清理临时目录失败(可忽略): {e}")
|
||||
self.logger.warning(f"[OTA] 清理临时目录失败(可忽略): {e}")
|
||||
|
||||
# 清理下载文件
|
||||
try:
|
||||
@@ -443,9 +441,9 @@ class OTAManager:
|
||||
# 删除下载的文件
|
||||
try:
|
||||
os.remove(downloaded_file)
|
||||
logger.info(f"[OTA] 已删除下载文件: {downloaded_file}")
|
||||
self.logger.info(f"[OTA] 已删除下载文件: {downloaded_file}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[OTA] 删除下载文件失败(可忽略): {e}")
|
||||
self.logger.warning(f"[OTA] 删除下载文件失败(可忽略): {e}")
|
||||
|
||||
# 尝试删除时间戳目录(如果为空)
|
||||
try:
|
||||
@@ -457,25 +455,24 @@ class OTAManager:
|
||||
files_in_dir = os.listdir(download_dir)
|
||||
if not files_in_dir:
|
||||
os.rmdir(download_dir)
|
||||
logger.info(f"[OTA] 已删除空时间戳目录: {download_dir}")
|
||||
self.logger.info(f"[OTA] 已删除空时间戳目录: {download_dir}")
|
||||
except Exception as e:
|
||||
logger.debug(f"[OTA] 删除时间戳目录失败(可忽略): {e}")
|
||||
self.logger.debug(f"[OTA] 删除时间戳目录失败(可忽略): {e}")
|
||||
except Exception as e:
|
||||
logger.debug(f"[OTA] 清理时间戳目录时出错(可忽略): {e}")
|
||||
self.logger.debug(f"[OTA] 清理时间戳目录时出错(可忽略): {e}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[OTA] 清理下载文件时出错(可忽略): {e}")
|
||||
self.logger.warning(f"[OTA] 清理下载文件时出错(可忽略): {e}")
|
||||
|
||||
# 重启设备
|
||||
logger.info("[OTA] 准备重启设备...")
|
||||
self.logger.info("[OTA] 准备重启设备...")
|
||||
os.system("reboot")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
logger.error(f"[OTA] apply_ota_and_reboot 异常: {e}")
|
||||
self.logger.error(f"[OTA] apply_ota_and_reboot 异常: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
self.logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
def get_download_timestamp_dir(self):
|
||||
@@ -512,8 +509,7 @@ class OTAManager:
|
||||
try:
|
||||
os.makedirs(download_dir, exist_ok=True)
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
logger.warning(f"[OTA] 创建下载目录失败: {e},使用基础目录")
|
||||
self.logger.warning(f"[OTA] 创建下载目录失败: {e},使用基础目录")
|
||||
download_dir = download_base
|
||||
try:
|
||||
os.makedirs(download_dir, exist_ok=True)
|
||||
@@ -522,8 +518,7 @@ class OTAManager:
|
||||
|
||||
return download_dir
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
logger.error(f"[OTA] 获取下载目录失败: {e},使用默认目录")
|
||||
self.logger.error(f"[OTA] 获取下载目录失败: {e},使用默认目录")
|
||||
return "/tmp/download"
|
||||
|
||||
def get_filename_from_url(self, url, default_name="main_tmp"):
|
||||
@@ -553,16 +548,14 @@ class OTAManager:
|
||||
# 只有在完全无法提取文件名时,才使用默认名称
|
||||
return f"{download_dir}/{default_name}"
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
logger.error(f"[OTA] 从URL提取文件名失败: {e},使用默认文件名")
|
||||
self.logger.error(f"[OTA] 从URL提取文件名失败: {e},使用默认文件名")
|
||||
download_dir = self.get_download_timestamp_dir()
|
||||
return f"{download_dir}/{default_name}"
|
||||
|
||||
def download_file(self, url, filename):
|
||||
"""从指定 URL 下载文件,根据文件类型自动选择文本或二进制模式,并支持MD5校验"""
|
||||
try:
|
||||
logger = logger_manager.logger
|
||||
logger.info(f"正在从 {url} 下载文件...")
|
||||
self.logger.info(f"正在从 {url} 下载文件...")
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
|
||||
@@ -570,7 +563,7 @@ class OTAManager:
|
||||
md5_b64_expected = None
|
||||
if 'Content-Md5' in response.headers:
|
||||
md5_b64_expected = response.headers['Content-Md5'].strip()
|
||||
logger.info(f"[DOWNLOAD] 服务器提供了MD5校验值: {md5_b64_expected}")
|
||||
self.logger.info(f"[DOWNLOAD] 服务器提供了MD5校验值: {md5_b64_expected}")
|
||||
|
||||
# 根据文件扩展名判断是否为二进制文件
|
||||
filename_lower = filename.lower()
|
||||
@@ -586,14 +579,14 @@ class OTAManager:
|
||||
os.sync()
|
||||
except:
|
||||
pass
|
||||
logger.info(f"[DOWNLOAD] 使用二进制模式下载: {filename}, 大小: {len(data)} bytes")
|
||||
self.logger.info(f"[DOWNLOAD] 使用二进制模式下载: {filename}, 大小: {len(data)} bytes")
|
||||
else:
|
||||
# 文本文件:使用文本模式写入
|
||||
response.encoding = 'utf-8'
|
||||
with open(filename, 'w', encoding='utf-8') as file:
|
||||
file.write(response.text)
|
||||
|
||||
logger.info(f"[DOWNLOAD] 使用文本模式下载: {filename}")
|
||||
self.logger.info(f"[DOWNLOAD] 使用文本模式下载: {filename}")
|
||||
|
||||
# MD5 校验(如果服务器提供了MD5值)
|
||||
if md5_b64_expected and hashlib is not None:
|
||||
@@ -604,18 +597,18 @@ class OTAManager:
|
||||
md5_b64_got = binascii.b2a_base64(digest).decode().strip()
|
||||
|
||||
if md5_b64_got != md5_b64_expected:
|
||||
logger.error(f"[DOWNLOAD] MD5校验失败: 期望={md5_b64_expected}, 实际={md5_b64_got}")
|
||||
self.logger.error(f"[DOWNLOAD] MD5校验失败: 期望={md5_b64_expected}, 实际={md5_b64_got}")
|
||||
return f"下载失败!MD5校验失败: 期望={md5_b64_expected}, 实际={md5_b64_got}"
|
||||
else:
|
||||
logger.info(f"[DOWNLOAD] MD5校验通过: {md5_b64_got}")
|
||||
self.logger.info(f"[DOWNLOAD] MD5校验通过: {md5_b64_got}")
|
||||
except Exception as e:
|
||||
logger.warning(f"[DOWNLOAD] MD5校验过程出错: {e}")
|
||||
self.logger.warning(f"[DOWNLOAD] MD5校验过程出错: {e}")
|
||||
# MD5校验出错时,如果是二进制文件(特别是ZIP),应该失败
|
||||
if is_binary:
|
||||
return f"下载失败!MD5校验异常: {e}"
|
||||
elif is_binary and not md5_b64_expected:
|
||||
# 二进制文件(特别是ZIP)建议有MD5校验
|
||||
logger.warning(f"[DOWNLOAD] 警告: 服务器未提供MD5校验值,无法验证文件完整性")
|
||||
self.logger.warning(f"[DOWNLOAD] 警告: 服务器未提供MD5校验值,无法验证文件完整性")
|
||||
|
||||
return f"下载成功!文件已保存为: {filename}"
|
||||
except requests.exceptions.RequestException as e:
|
||||
@@ -647,11 +640,10 @@ class OTAManager:
|
||||
return
|
||||
|
||||
downloaded_filename = self.get_filename_from_url(ota_url, default_name="main_tmp")
|
||||
logger = logger_manager.logger
|
||||
logger.info(f"[OTA] 下载文件将保存为: {downloaded_filename}")
|
||||
logger.info(f"[OTA] 开始下载: {ota_url}")
|
||||
self.logger.info(f"[OTA] 下载文件将保存为: {downloaded_filename}")
|
||||
self.logger.info(f"[OTA] 开始下载: {ota_url}")
|
||||
result_msg = self.download_file(ota_url, downloaded_filename)
|
||||
logger.info(f"[OTA] {result_msg}")
|
||||
self.logger.info(f"[OTA] {result_msg}")
|
||||
|
||||
if "成功" in result_msg or "下载成功" in result_msg:
|
||||
if self.apply_ota_and_reboot(ota_url, downloaded_filename):
|
||||
@@ -662,8 +654,7 @@ class OTAManager:
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"OTA 异常: {str(e)}"
|
||||
logger = logger_manager.logger
|
||||
logger.error(error_msg)
|
||||
self.logger.error(error_msg)
|
||||
from network import safe_enqueue
|
||||
safe_enqueue({"result": "ota_failed", "reason": error_msg}, 2)
|
||||
finally:
|
||||
@@ -707,11 +698,11 @@ class OTAManager:
|
||||
else:
|
||||
base_url = f"http://{host}"
|
||||
self._is_https = False
|
||||
logger = logger_manager.logger
|
||||
# logger removed - use self.logger instead
|
||||
|
||||
def _log(*a):
|
||||
if debug and logger:
|
||||
logger.debug(" ".join(str(x) for x in a))
|
||||
if debug:
|
||||
self.logger.debug(" ".join(str(x) for x in a))
|
||||
|
||||
def _pwr_log(prefix=""):
|
||||
"""debug 用:输出电压/电量"""
|
||||
@@ -720,12 +711,10 @@ class OTAManager:
|
||||
try:
|
||||
v = get_bus_voltage()
|
||||
p = voltage_to_percent(v)
|
||||
if logger:
|
||||
logger.debug(f"[PWR]{prefix} v={v:.3f}V p={p}%")
|
||||
self.logger.debug(f"[PWR]{prefix} v={v:.3f}V p={p}%")
|
||||
except Exception as e:
|
||||
try:
|
||||
if logger:
|
||||
logger.debug(f"[PWR]{prefix} read_failed: {e}")
|
||||
self.logger.debug(f"[PWR]{prefix} read_failed: {e}")
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -792,7 +781,7 @@ class OTAManager:
|
||||
if self._is_https:
|
||||
resp = hardware_manager.at_client.send(f'AT+MHTTPCFG="ssl",{hid},1,1', "OK", 2000)
|
||||
if "ERROR" in resp or "CME ERROR" in resp:
|
||||
logger_manager.logger.error(f"MHTTPCFG SSL failed: {resp}")
|
||||
self.logger.error(f"MHTTPCFG SSL failed: {resp}")
|
||||
# 尝试https 降级到http
|
||||
downgraded_base_url = base_url.replace("https://", "http://")
|
||||
resp = hardware_manager.at_client.send(f'AT+MHTTPCREATE="{downgraded_base_url}"', "OK", 8000)
|
||||
@@ -1035,14 +1024,12 @@ class OTAManager:
|
||||
got_b64 = binascii.b2a_base64(digest).decode().strip()
|
||||
if got_b64 != expect_md5_b64:
|
||||
return False, f"md5_mismatch got={got_b64} expected={expect_md5_b64}"
|
||||
if logger:
|
||||
logger.debug(f"[4G-DL] MD5 verified: {got_b64}")
|
||||
self.logger.debug(f"[4G-DL] MD5 verified: {got_b64}")
|
||||
except Exception as e:
|
||||
return False, f"md5_check_failed: {e}"
|
||||
|
||||
t_cost = time.ticks_diff(time.ticks_ms(), t_func0)
|
||||
if logger:
|
||||
logger.info(f"[4G-DL] download complete: size={offset} ip={ip} cost_ms={t_cost}")
|
||||
self.logger.info(f"[4G-DL] download complete: size={offset} ip={ip} cost_ms={t_cost}")
|
||||
return True, f"OK size={offset} ip={ip} cost_ms={t_cost}"
|
||||
|
||||
finally:
|
||||
@@ -1076,9 +1063,9 @@ class OTAManager:
|
||||
|
||||
# 从URL中提取文件名(保留原始扩展名)
|
||||
downloaded_filename = self.get_filename_from_url(ota_url, default_name="main_tmp")
|
||||
logger_manager.logger.info(f"[OTA-4G] 下载文件将保存为: {downloaded_filename}")
|
||||
self.logger.info(f"[OTA-4G] 下载文件将保存为: {downloaded_filename}")
|
||||
|
||||
logger_manager.logger.info(f"[OTA-4G] 开始通过 4G 下载: {ota_url}")
|
||||
self.logger.info(f"[OTA-4G] 开始通过 4G 下载: {ota_url}")
|
||||
# 重要说明:
|
||||
# - AT+MDIALUP / RNDIS 是"USB 主机拨号上网"模式,在不少 ML307R 固件上会占用/切换内部网络栈,
|
||||
# 从而导致 AT+MIPOPEN / +MIPURC 这套 TCP 连接无法工作(你会看到一直"连接到服务器...")。
|
||||
@@ -1090,15 +1077,15 @@ class OTAManager:
|
||||
import power
|
||||
v = power.get_bus_voltage()
|
||||
p = power.voltage_to_percent(v)
|
||||
logger_manager.logger.info(f"[OTA-4G][PWR] before_urc v={v:.3f}V p={p}%")
|
||||
self.logger.info(f"[OTA-4G][PWR] before_urc v={v:.3f}V p={p}%")
|
||||
except Exception as e:
|
||||
logger_manager.logger.error(f"[OTA-4G][PWR] before_urc read_failed: {e}")
|
||||
self.logger.error(f"[OTA-4G][PWR] before_urc read_failed: {e}")
|
||||
|
||||
t_dl0 = time.ticks_ms()
|
||||
success, msg = self.download_file_via_4g(ota_url, downloaded_filename, debug=False)
|
||||
t_dl_cost = time.ticks_diff(t_dl0, time.ticks_ms())
|
||||
logger_manager.logger.info(f"[OTA-4G] {msg}")
|
||||
logger_manager.logger.info(f"[OTA-4G] download_cost_ms={t_dl_cost}")
|
||||
self.logger.info(f"[OTA-4G] {msg}")
|
||||
self.logger.info(f"[OTA-4G] download_cost_ms={t_dl_cost}")
|
||||
|
||||
if success and "OK" in msg:
|
||||
if self.apply_ota_and_reboot(ota_url, downloaded_filename):
|
||||
@@ -1108,13 +1095,13 @@ class OTAManager:
|
||||
|
||||
except Exception as e:
|
||||
error_msg = f"OTA-4G 异常: {str(e)}"
|
||||
logger_manager.logger.error(error_msg)
|
||||
self.logger.error(error_msg)
|
||||
safe_enqueue({"result": "ota_failed", "reason": error_msg}, 2)
|
||||
finally:
|
||||
# 总耗时(注意:若成功并 reboot,这行可能来不及打印)
|
||||
try:
|
||||
t_cost = time.ticks_diff(time.ticks_ms(), t_ota0)
|
||||
logger_manager.logger.info(f"[OTA-4G] total_cost_ms={t_cost}")
|
||||
self.logger.info(f"[OTA-4G] total_cost_ms={t_cost}")
|
||||
except:
|
||||
pass
|
||||
self._stop_update_thread()
|
||||
@@ -1147,21 +1134,15 @@ class OTAManager:
|
||||
if not network_manager.is_server_reachable(host, port, timeout=8):
|
||||
err_msg = f"网络不通:无法连接 {host}:{port}"
|
||||
safe_enqueue({"result": err_msg}, 2)
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(err_msg)
|
||||
self.logger.error(err_msg)
|
||||
return
|
||||
|
||||
downloaded_filename = self.get_filename_from_url(ota_url, default_name="main_tmp")
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.info(f"[OTA] 下载文件将保存为: {downloaded_filename}")
|
||||
self.logger.info(f"[OTA] 下载文件将保存为: {downloaded_filename}")
|
||||
|
||||
if logger:
|
||||
logger.info(f"[NET] 已确认可访问 {host}:{port},开始下载...")
|
||||
self.logger.info(f"[NET] 已确认可访问 {host}:{port},开始下载...")
|
||||
result = self.download_file(ota_url, downloaded_filename)
|
||||
if logger:
|
||||
logger.info(result)
|
||||
self.logger.info(result)
|
||||
|
||||
if "成功" in result or "下载成功" in result:
|
||||
if self.apply_ota_and_reboot(ota_url, downloaded_filename):
|
||||
@@ -1188,9 +1169,7 @@ class OTAManager:
|
||||
try:
|
||||
if backup_dir_path is None:
|
||||
if not os.path.exists(backup_base):
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[RESTORE] 备份目录不存在: {backup_base}")
|
||||
self.logger.error(f"[RESTORE] 备份目录不存在: {backup_base}")
|
||||
return False
|
||||
|
||||
backup_dirs = []
|
||||
@@ -1207,9 +1186,7 @@ class OTAManager:
|
||||
pass
|
||||
|
||||
if not backup_dirs:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[RESTORE] 没有找到备份目录")
|
||||
self.logger.error(f"[RESTORE] 没有找到备份目录")
|
||||
return False
|
||||
|
||||
backup_dirs.sort(key=lambda x: x[1], reverse=True)
|
||||
@@ -1217,14 +1194,10 @@ class OTAManager:
|
||||
backup_dir_path = os.path.join(backup_base, latest_backup)
|
||||
|
||||
if not os.path.exists(backup_dir_path):
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[RESTORE] 备份目录不存在: {backup_dir_path}")
|
||||
self.logger.error(f"[RESTORE] 备份目录不存在: {backup_dir_path}")
|
||||
return False
|
||||
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.info(f"[RESTORE] 开始从备份恢复: {backup_dir_path}")
|
||||
self.logger.info(f"[RESTORE] 开始从备份恢复: {backup_dir_path}")
|
||||
|
||||
restored_files = []
|
||||
for root, dirs, files in os.walk(backup_dir_path):
|
||||
@@ -1240,25 +1213,19 @@ class OTAManager:
|
||||
try:
|
||||
shutil.copy2(source_path, dest_path)
|
||||
restored_files.append(rel_path)
|
||||
if logger:
|
||||
logger.info(f"[RESTORE] 已恢复: {rel_path}")
|
||||
self.logger.info(f"[RESTORE] 已恢复: {rel_path}")
|
||||
except Exception as e:
|
||||
if logger:
|
||||
logger.error(f"[RESTORE] 恢复 {rel_path} 失败: {e}")
|
||||
self.logger.error(f"[RESTORE] 恢复 {rel_path} 失败: {e}")
|
||||
|
||||
if restored_files:
|
||||
if logger:
|
||||
logger.info(f"[RESTORE] 成功恢复 {len(restored_files)} 个文件")
|
||||
self.logger.info(f"[RESTORE] 成功恢复 {len(restored_files)} 个文件")
|
||||
return True
|
||||
else:
|
||||
if logger:
|
||||
logger.info(f"[RESTORE] 没有文件被恢复")
|
||||
self.logger.info(f"[RESTORE] 没有文件被恢复")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger = logger_manager.logger
|
||||
if logger:
|
||||
logger.error(f"[RESTORE] 恢复过程出错: {e}")
|
||||
self.logger.error(f"[RESTORE] 恢复过程出错: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user