pref: 版本说明

This commit is contained in:
2026-06-03 16:02:39 +08:00
parent 583748fda3
commit 0d69a01a1f
5 changed files with 213 additions and 170 deletions

View File

@@ -1,6 +1,6 @@
id: t11 id: t11
name: t11 name: t11
version: 2.15.3 version: 2.15.6
author: t11 author: t11
icon: '' icon: ''
desc: t11 desc: t11

View File

@@ -309,7 +309,7 @@ LASER_THICKNESS = 1
LASER_LENGTH = 2 LASER_LENGTH = 2
# ==================== 图像保存配置 ==================== # ==================== 图像保存配置 ====================
SAVE_IMAGE_ENABLED = False # 是否保存图像True=保存False=不保存) SAVE_IMAGE_ENABLED = True # 是否保存图像True=保存False=不保存)
PHOTO_DIR = "/root/phot" # 照片存储目录 PHOTO_DIR = "/root/phot" # 照片存储目录
MAX_IMAGES = 1000 MAX_IMAGES = 1000
# Stage2 调试目录(默认 PHOTO_DIR/stage2_roi内 JPEG 最多保留张数None 表示与 MAX_IMAGES 相同 # Stage2 调试目录(默认 PHOTO_DIR/stage2_roi内 JPEG 最多保留张数None 表示与 MAX_IMAGES 相同

View File

@@ -24,7 +24,6 @@ from wifi import wifi_manager
import subprocess import subprocess
def _wifi_tls_would_block(exc): def _wifi_tls_would_block(exc):
""" """
非阻塞 TLS 下 recv/send 常抛出 WANT_READ / WANT_WRITE或等价文案 非阻塞 TLS 下 recv/send 常抛出 WANT_READ / WANT_WRITE或等价文案
@@ -84,7 +83,8 @@ class NetworkManager:
try: try:
import archery_netcore as _netcore import archery_netcore as _netcore
self._netcore = _netcore self._netcore = _netcore
if hasattr(self._netcore, "parse_packet") and hasattr(self._netcore, "make_packet") and hasattr(self._netcore, "actions_for_inner_cmd"): if hasattr(self._netcore, "parse_packet") and hasattr(self._netcore, "make_packet") and hasattr(
self._netcore, "actions_for_inner_cmd"):
print("[NET] archery_netcore found") print("[NET] archery_netcore found")
else: else:
print("[NET] archery_netcore not found parse_packet or make_packet") print("[NET] archery_netcore not found parse_packet or make_packet")
@@ -147,7 +147,6 @@ class NetworkManager:
# ==================== 内部状态管理方法 ==================== # ==================== 内部状态管理方法 ====================
def set_manual_trigger(self, value=True): def set_manual_trigger(self, value=True):
"""设置手动触发标志(公共方法)""" """设置手动触发标志(公共方法)"""
self._manual_trigger_flag = value self._manual_trigger_flag = value
@@ -203,6 +202,7 @@ class NetworkManager:
def read_device_id(self): def read_device_id(self):
"""从 /device_key 文件读取设备唯一 ID失败则使用默认值""" """从 /device_key 文件读取设备唯一 ID失败则使用默认值"""
def _set_password_for_device_id(device_id): def _set_password_for_device_id(device_id):
if getattr(config, "USE_TCP_SSL", False): if getattr(config, "USE_TCP_SSL", False):
iccid = self.get_4g_mccid() iccid = self.get_4g_mccid()
@@ -242,6 +242,7 @@ class NetworkManager:
连接 Wi-Fi委托 ``wifi_manager.connect_wifi``。 连接 Wi-Fi委托 ``wifi_manager.connect_wifi``。
未指定 ``verify_host``/``verify_port`` 时,可达性校验使用本管理器配置的 ``_server_ip``/``_server_port``。 未指定 ``verify_host``/``verify_port`` 时,可达性校验使用本管理器配置的 ``_server_ip``/``_server_port``。
""" """
def _verify(ip: str): def _verify(ip: str):
v_host = verify_host if verify_host is not None else self._server_ip v_host = verify_host if verify_host is not None else self._server_ip
v_port = verify_port if verify_port is not None else self._server_port v_port = verify_port if verify_port is not None else self._server_port
@@ -552,7 +553,6 @@ class NetworkManager:
self._session_force_4g = False self._session_force_4g = False
return False return False
def _cmd200_detect_laser(self): def _cmd200_detect_laser(self):
"""后台线程执行 cmd200 激光检测,避免阻塞主循环""" """后台线程执行 cmd200 激光检测,避免阻塞主循环"""
from laser_manager import laser_manager from laser_manager import laser_manager
@@ -598,8 +598,24 @@ class NetworkManager:
err.check_raise(e, "connect wifi failed") err.check_raise(e, "connect wifi failed")
if self.logger: if self.logger:
self.logger.info(f"[ota] Connect success, got ip{w.get_ip()}") self.logger.info(f"[ota] Connect success, got ip{w.get_ip()}")
self.safe_enqueue(
{
"cmd": 300,
"result": "ota start...",
"wifi": w.get_ip(),
},
2,
)
subprocess.run( subprocess.run(
["sh", "/maixapp/apps/t11/ota_curl.sh", ota_res_url]) ["sh", "/maixapp/apps/t11/ota_curl.sh", ota_res_url])
self.safe_enqueue(
{
"cmd": 300,
"result": "success",
"wifi": w.get_ip(),
},
2,
)
except Exception as e: except Exception as e:
self.logger.error(f"[ota] cmd300 失败: {e}") self.logger.error(f"[ota] cmd300 失败: {e}")
self.safe_enqueue( self.safe_enqueue(
@@ -615,9 +631,6 @@ class NetworkManager:
"""线程安全地将消息加入队列(公共方法)""" """线程安全地将消息加入队列(公共方法)"""
self._enqueue((msg_type, data_dict), high) self._enqueue((msg_type, data_dict), high)
def connect_server(self): def connect_server(self):
""" """
连接到服务器自动选择WiFi或4G 连接到服务器自动选择WiFi或4G
@@ -894,7 +907,6 @@ class NetworkManager:
finally: finally:
self._uart4g_lock.release() self._uart4g_lock.release()
def tcp_send_raw(self, data: bytes, max_retries=2) -> bool: def tcp_send_raw(self, data: bytes, max_retries=2) -> bool:
""" """
统一的TCP发送接口自动选择WiFi或4G 统一的TCP发送接口自动选择WiFi或4G
@@ -1054,7 +1066,6 @@ class NetworkManager:
r = hardware_manager.at_client.send(f'AT+MSSLCERTRD="{cert_filename}"', "OK", 3000) r = hardware_manager.at_client.send(f'AT+MSSLCERTRD="{cert_filename}"', "OK", 3000)
self.logger.info(f"[4G-TCP] AT+MSSLCERTRD=\"{cert_filename}\" response: {r}") self.logger.info(f"[4G-TCP] AT+MSSLCERTRD=\"{cert_filename}\" response: {r}")
# 3) 引用根证书 # 3) 引用根证书
r = hardware_manager.at_client.send(f'AT+MSSLCFG="cert",{ssl_id},"{cert_filename}"', "OK", 3000) r = hardware_manager.at_client.send(f'AT+MSSLCFG="cert",{ssl_id},"{cert_filename}"', "OK", 3000)
if "OK" not in r: if "OK" not in r:
@@ -1112,7 +1123,8 @@ class NetworkManager:
self.logger.error(f"[WIFI-TCP] 接收数据异常: {e}") self.logger.error(f"[WIFI-TCP] 接收数据异常: {e}")
return b"" return b""
def _upload_log_file(self, upload_url, wifi_ssid=None, wifi_password=None, include_rotated=True, max_files=None, archive_format="tgz"): def _upload_log_file(self, upload_url, wifi_ssid=None, wifi_password=None, include_rotated=True, max_files=None,
archive_format="tgz"):
"""上传日志文件到指定URL """上传日志文件到指定URL
Args: Args:
@@ -1245,7 +1257,8 @@ class NetworkManager:
staged_paths.append(dst) staged_paths.append(dst)
except Exception as e: except Exception as e:
self.logger.error(f"[LOG_UPLOAD] 复制日志快照失败: {e}") self.logger.error(f"[LOG_UPLOAD] 复制日志快照失败: {e}")
self.safe_enqueue({"result": "log_upload_failed", "reason": "snapshot_failed", "detail": str(e)[:100]}, 2) self.safe_enqueue({"result": "log_upload_failed", "reason": "snapshot_failed", "detail": str(e)[:100]},
2)
try: try:
shutil.rmtree(staging_dir) shutil.rmtree(staging_dir)
except: except:
@@ -1273,7 +1286,8 @@ class NetworkManager:
self.logger.info(f"[LOG_UPLOAD] 日志压缩包已生成: {archive_path}") self.logger.info(f"[LOG_UPLOAD] 日志压缩包已生成: {archive_path}")
except Exception as e: except Exception as e:
self.logger.error(f"[LOG_UPLOAD] 打包压缩失败: {e}") self.logger.error(f"[LOG_UPLOAD] 打包压缩失败: {e}")
self.safe_enqueue({"result": "log_upload_failed", "reason": "archive_failed", "detail": str(e)[:100]}, 2) self.safe_enqueue({"result": "log_upload_failed", "reason": "archive_failed", "detail": str(e)[:100]},
2)
try: try:
shutil.rmtree(staging_dir) shutil.rmtree(staging_dir)
except: except:
@@ -1322,7 +1336,8 @@ class NetworkManager:
"status_code": response.status_code "status_code": response.status_code
}, 2) }, 2)
else: else:
self.logger.error(f"[LOG_UPLOAD] 上传失败! 状态码: {response.status_code}, 响应: {response.text[:200]}") self.logger.error(
f"[LOG_UPLOAD] 上传失败! 状态码: {response.status_code}, 响应: {response.text[:200]}")
self.safe_enqueue({ self.safe_enqueue({
"result": "log_upload_failed", "result": "log_upload_failed",
"reason": f"http_{response.status_code}", "reason": f"http_{response.status_code}",
@@ -1458,7 +1473,8 @@ class NetworkManager:
except Exception as e: except Exception as e:
return None, f"prepare_exception: {e}" return None, f"prepare_exception: {e}"
def _upload_log_file_v2(self, upload_url, upload_token, key, outlink="", include_rotated=True, max_files=None, archive_format="tgz"): def _upload_log_file_v2(self, upload_url, upload_token, key, outlink="", include_rotated=True, max_files=None,
archive_format="tgz"):
"""上传日志到 Qiniu支持 WiFi 和 4G 双路径) """上传日志到 Qiniu支持 WiFi 和 4G 双路径)
流程:准备日志归档 -> 自动检测网络 -> WiFi(requests) 或 4G(AT命令) 上传 流程:准备日志归档 -> 自动检测网络 -> WiFi(requests) 或 4G(AT命令) 上传
@@ -1793,7 +1809,8 @@ class NetworkManager:
if not logged_in: if not logged_in:
try: try:
self.logger.debug(f"[TCP] rx link={link_id} len={len(payload)} head={payload[:12].hex()}") self.logger.debug(
f"[TCP] rx link={link_id} len={len(payload)} head={payload[:12].hex()}")
except: except:
pass pass
@@ -1819,7 +1836,8 @@ class NetworkManager:
pending_obj = json.load(f) pending_obj = json.load(f)
except: except:
pending_obj = {} pending_obj = {}
self.safe_enqueue({"result": "ota_ok", "url": pending_obj.get("url", "")}, 2) self.safe_enqueue({"result": "ota_ok", "url": pending_obj.get("url", "")},
2)
self.logger.info("[OTA] 已上报 ota_ok等待心跳确认后删除 pending") self.logger.info("[OTA] 已上报 ota_ok等待心跳确认后删除 pending")
except Exception as e: except Exception as e:
self.logger.error(f"[OTA] ota_ok 上报失败: {e}") self.logger.error(f"[OTA] ota_ok 上报失败: {e}")
@@ -1838,7 +1856,8 @@ class NetworkManager:
t = body.get('t', 0) t = body.get('t', 0)
v = body.get('v') v = body.get('v')
# 如果是第一个分片,清空之前的缓存 # 如果是第一个分片,清空之前的缓存
if len(self._raw_line_data) == 0 or (len(self._raw_line_data) > 0 and self._raw_line_data[0].get('v') != v): if len(self._raw_line_data) == 0 or (
len(self._raw_line_data) > 0 and self._raw_line_data[0].get('v') != v):
self._raw_line_data.clear() self._raw_line_data.clear()
# 或者更简单每次收到命令40时如果版本号不同清空缓存 # 或者更简单每次收到命令40时如果版本号不同清空缓存
if len(self._raw_line_data) > 0: if len(self._raw_line_data) > 0:
@@ -1872,7 +1891,8 @@ class NetworkManager:
# 验证必需字段 # 验证必需字段
if not upload_url or not upload_token or not shoot_id: if not upload_url or not upload_token or not shoot_id:
self.logger.error("[IMAGE_UPLOAD] 缺少必需参数: uploadUrl, token 或 shootId") self.logger.error("[IMAGE_UPLOAD] 缺少必需参数: uploadUrl, token 或 shootId")
self.safe_enqueue({"result": "image_upload_failed", "reason": "missing_params"}, 2) self.safe_enqueue({"result": "image_upload_failed", "reason": "missing_params"},
2)
else: else:
self.logger.info(f"[IMAGE_UPLOAD] 收到图片上传命令shootId: {shoot_id}") self.logger.info(f"[IMAGE_UPLOAD] 收到图片上传命令shootId: {shoot_id}")
# 查找文件名中包含 shoot_id 的图片文件文件名格式shot_{shoot_id}_*.bmp # 查找文件名中包含 shoot_id 的图片文件文件名格式shot_{shoot_id}_*.bmp
@@ -1893,15 +1913,19 @@ class NetworkManager:
reverse=True reverse=True
) )
target_image = os.path.join(photo_dir, matched_images[0]) target_image = os.path.join(photo_dir, matched_images[0])
self.logger.info(f"[IMAGE_UPLOAD] 找到匹配shootId的图片: {matched_images[0]}") self.logger.info(
f"[IMAGE_UPLOAD] 找到匹配shootId的图片: {matched_images[0]}")
else: else:
self.logger.warning(f"[IMAGE_UPLOAD] 未找到包含shootId={shoot_id}的图片文件") self.logger.warning(
f"[IMAGE_UPLOAD] 未找到包含shootId={shoot_id}的图片文件")
except Exception as e: except Exception as e:
self.logger.error(f"[IMAGE_UPLOAD] 查找图片失败: {e}") self.logger.error(f"[IMAGE_UPLOAD] 查找图片失败: {e}")
if not target_image: if not target_image:
self.logger.error(f"[IMAGE_UPLOAD] 未找到shootId={shoot_id}对应的图片文件") self.logger.error(f"[IMAGE_UPLOAD] 未找到shootId={shoot_id}对应的图片文件")
self.safe_enqueue({"result": "image_upload_failed", "reason": "no_image_found", "shootId": shoot_id}, 2) self.safe_enqueue(
{"result": "image_upload_failed", "reason": "no_image_found",
"shootId": shoot_id}, 2)
else: else:
# 构建上传key # 构建上传key
ext = os.path.splitext(target_image)[1].lower() ext = os.path.splitext(target_image)[1].lower()
@@ -1933,14 +1957,16 @@ class NetworkManager:
# 验证必需字段 # 验证必需字段
if not upload_url or not upload_token or not key: if not upload_url or not upload_token or not key:
self.logger.error("[LOG_UPLOAD] 缺少必需参数: uploadUrl, token 或 key") self.logger.error("[LOG_UPLOAD] 缺少必需参数: uploadUrl, token 或 key")
self.safe_enqueue({"result": "log_upload_failed", "reason": "missing_params"}, 2) self.safe_enqueue({"result": "log_upload_failed", "reason": "missing_params"},
2)
else: else:
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令key: {key}") self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令key: {key}")
# 在新线程中执行上传,避免阻塞主循环 # 在新线程中执行上传,避免阻塞主循环
import _thread import _thread
_thread.start_new_thread( _thread.start_new_thread(
self._upload_log_file_v2, self._upload_log_file_v2,
(upload_url, upload_token, key, outlink, include_rotated, max_files, archive_format) (upload_url, upload_token, key, outlink, include_rotated, max_files,
archive_format)
) )
# 立即返回已入队确认 # 立即返回已入队确认
self.safe_enqueue({"result": "log_upload_queued"}, 2) self.safe_enqueue({"result": "log_upload_queued"}, 2)
@@ -2040,10 +2066,12 @@ class NetworkManager:
# 只有同时满足WiFi已连接 且 提供了WiFi凭证才使用WiFi # 只有同时满足WiFi已连接 且 提供了WiFi凭证才使用WiFi
if self.is_wifi_connected() and ssid and password: if self.is_wifi_connected() and ssid and password:
mode = "wifi" mode = "wifi"
self.logger.info("ota auto-selected: wifi (WiFi connected and credentials provided)") self.logger.info(
"ota auto-selected: wifi (WiFi connected and credentials provided)")
else: else:
mode = "4g" mode = "4g"
self.logger.info("ota auto-selected: 4g (WiFi not available or no credentials)") self.logger.info(
"ota auto-selected: 4g (WiFi not available or no credentials)")
hardware_manager.stop_idle_timer() # 停表注意OTA停表之后就没有再开表因为OTA后面会重启会重新开表 hardware_manager.stop_idle_timer() # 停表注意OTA停表之后就没有再开表因为OTA后面会重启会重新开表
@@ -2059,10 +2087,12 @@ class NetworkManager:
self.logger.info(f"ssid: {ssid}") self.logger.info(f"ssid: {ssid}")
self.logger.info(f"password: {password}") self.logger.info(f"password: {password}")
ota_manager._start_update_thread() ota_manager._start_update_thread()
_thread.start_new_thread(ota_manager.handle_wifi_and_update, (ssid, password, ota_url)) _thread.start_new_thread(ota_manager.handle_wifi_and_update,
(ssid, password, ota_url))
elif inner_cmd == 6: elif inner_cmd == 6:
try: try:
ip = os.popen("ifconfig wlan0 2>/dev/null | grep 'inet ' | awk '{print $2}'").read().strip() ip = os.popen(
"ifconfig wlan0 2>/dev/null | grep 'inet ' | awk '{print $2}'").read().strip()
ip = ip if ip else "no_ip" ip = ip if ip else "no_ip"
except: except:
ip = "error_getting_ip" ip = "error_getting_ip"
@@ -2070,11 +2100,13 @@ class NetworkManager:
elif inner_cmd == 44: # 读 4G 本机号码AT+CNUM elif inner_cmd == 44: # 读 4G 本机号码AT+CNUM
cnum = self.get_4g_phone_number() cnum = self.get_4g_phone_number()
self.logger.info(f"4G 本机号码: {cnum}") self.logger.info(f"4G 本机号码: {cnum}")
self.safe_enqueue({"result": "cnum", "number": cnum if cnum is not None else ""}, 2) self.safe_enqueue(
{"result": "cnum", "number": cnum if cnum is not None else ""}, 2)
elif inner_cmd == 45: # 读 MCCIDAT+MCCID elif inner_cmd == 45: # 读 MCCIDAT+MCCID
mccid = self.get_4g_mccid() mccid = self.get_4g_mccid()
self.logger.info(f"4G MCCID: {mccid}") self.logger.info(f"4G MCCID: {mccid}")
self.safe_enqueue({"result": "mccid", "mccid": mccid if mccid is not None else ""}, 2) self.safe_enqueue(
{"result": "mccid", "mccid": mccid if mccid is not None else ""}, 2)
elif inner_cmd == 41: elif inner_cmd == 41:
self.logger.info(f"[TEST] 收到TCP射箭触发命令, {time.time()}") self.logger.info(f"[TEST] 收到TCP射箭触发命令, {time.time()}")
self._manual_trigger_flag = True self._manual_trigger_flag = True
@@ -2111,14 +2143,16 @@ class NetworkManager:
if not upload_url: if not upload_url:
self.logger.error("[LOG_UPLOAD] 缺少 url 参数") self.logger.error("[LOG_UPLOAD] 缺少 url 参数")
self.safe_enqueue({"result": "log_upload_failed", "reason": "missing_url"}, 2) self.safe_enqueue({"result": "log_upload_failed", "reason": "missing_url"},
2)
else: else:
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令目标URL: {upload_url}") self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令目标URL: {upload_url}")
# 在新线程中执行上传,避免阻塞主循环 # 在新线程中执行上传,避免阻塞主循环
import _thread import _thread
_thread.start_new_thread( _thread.start_new_thread(
self._upload_log_file, self._upload_log_file,
(upload_url, wifi_ssid, wifi_password, include_rotated, max_files, archive_format) (upload_url, wifi_ssid, wifi_password, include_rotated, max_files,
archive_format)
) )
elif inner_cmd == 200: elif inner_cmd == 200:
self.logger.info("[LASER] cmd200 在后台线程执行检测") self.logger.info("[LASER] cmd200 在后台线程执行检测")
@@ -2181,7 +2215,8 @@ class NetworkManager:
current_time = time.ticks_ms() current_time = time.ticks_ms()
if logged_in and current_time - last_heartbeat_send_time > config.HEARTBEAT_INTERVAL * 1000: if logged_in and current_time - last_heartbeat_send_time > config.HEARTBEAT_INTERVAL * 1000:
vol_val = get_bus_voltage() vol_val = get_bus_voltage()
if not self.tcp_send_raw(self._netcore.make_packet(4, {"vol": vol_val, "vol_per": voltage_to_percent(vol_val)})): if not self.tcp_send_raw(
self._netcore.make_packet(4, {"vol": vol_val, "vol_per": voltage_to_percent(vol_val)})):
# if not self.tcp_send_raw(self.make_packet(4, {"vol": vol_val, "vol_per": voltage_to_percent(vol_val)})): # if not self.tcp_send_raw(self.make_packet(4, {"vol": vol_val, "vol_per": voltage_to_percent(vol_val)})):
send_hartbeat_fail_count += 1 send_hartbeat_fail_count += 1
# 短暂波动可能导致一次发送失败:连续失败达到阈值才重连,避免重连风暴 # 短暂波动可能导致一次发送失败:连续失败达到阈值才重连,避免重连风暴
@@ -2241,38 +2276,44 @@ class NetworkManager:
# 创建全局单例实例 # 创建全局单例实例
network_manager = NetworkManager() network_manager = NetworkManager()
# ==================== 向后兼容的函数接口 ==================== # ==================== 向后兼容的函数接口 ====================
def tcp_main(): def tcp_main():
"""TCP主循环向后兼容接口""" """TCP主循环向后兼容接口"""
return network_manager.tcp_main() return network_manager.tcp_main()
def read_device_id(): def read_device_id():
"""读取设备ID向后兼容接口""" """读取设备ID向后兼容接口"""
return network_manager.read_device_id() return network_manager.read_device_id()
def safe_enqueue(data_dict, msg_type=2, high=False): def safe_enqueue(data_dict, msg_type=2, high=False):
"""线程安全地加入队列(向后兼容接口)""" """线程安全地加入队列(向后兼容接口)"""
return network_manager.safe_enqueue(data_dict, msg_type, high) return network_manager.safe_enqueue(data_dict, msg_type, high)
def connect_server(): def connect_server():
"""连接服务器(向后兼容接口)""" """连接服务器(向后兼容接口)"""
return network_manager.connect_server() return network_manager.connect_server()
def disconnet_server(): def disconnet_server():
"""断开服务器连接(向后兼容接口)""" """断开服务器连接(向后兼容接口)"""
return network_manager.disconnect_server() return network_manager.disconnect_server()
def is_wifi_connected(): def is_wifi_connected():
"""检查WiFi是否已连接向后兼容接口""" """检查WiFi是否已连接向后兼容接口"""
return network_manager.is_wifi_connected() return network_manager.is_wifi_connected()
def connect_wifi(ssid, password): def connect_wifi(ssid, password):
"""连接WiFi向后兼容接口""" """连接WiFi向后兼容接口"""
return network_manager.connect_wifi(ssid, password) return network_manager.connect_wifi(ssid, password)
def is_server_reachable(host, port=80, timeout=5): def is_server_reachable(host, port=80, timeout=5):
"""检查服务器是否可达(向后兼容接口)""" """检查服务器是否可达(向后兼容接口)"""
return network_manager.is_server_reachable(host, port, timeout) return network_manager.is_server_reachable(host, port, timeout)

View File

@@ -14,4 +14,6 @@
# 1.2.13 修改wifi连接 # 1.2.13 修改wifi连接
# 1.2.14 修改了icc登录部分 # 1.2.14 修改了icc登录部分
# 2.15.3 新版本ota去除ai算环数方法 # 2.15.3 新版本ota去除ai算环数方法
# 2.15.4 更新版本号
# 2.15.5 打印ota进度
# 2.15.6 更新版本号

View File

@@ -4,6 +4,6 @@
应用版本号 应用版本号
每次 OTA 更新时,只需要更新这个文件中的版本号 每次 OTA 更新时,只需要更新这个文件中的版本号
""" """
VERSION = '2.15.3' VERSION = '2.15.6'