pref: 版本说明
This commit is contained in:
2
app.yaml
2
app.yaml
@@ -1,6 +1,6 @@
|
||||
id: t11
|
||||
name: t11
|
||||
version: 2.15.3
|
||||
version: 2.15.6
|
||||
author: t11
|
||||
icon: ''
|
||||
desc: t11
|
||||
|
||||
@@ -309,7 +309,7 @@ LASER_THICKNESS = 1
|
||||
LASER_LENGTH = 2
|
||||
|
||||
# ==================== 图像保存配置 ====================
|
||||
SAVE_IMAGE_ENABLED = False # 是否保存图像(True=保存,False=不保存)
|
||||
SAVE_IMAGE_ENABLED = True # 是否保存图像(True=保存,False=不保存)
|
||||
PHOTO_DIR = "/root/phot" # 照片存储目录
|
||||
MAX_IMAGES = 1000
|
||||
# Stage2 调试目录(默认 PHOTO_DIR/stage2_roi)内 JPEG 最多保留张数;None 表示与 MAX_IMAGES 相同
|
||||
|
||||
119
network.py
119
network.py
@@ -8,7 +8,7 @@ import json
|
||||
import re
|
||||
from math import e
|
||||
import struct
|
||||
from maix import time,network,err
|
||||
from maix import time, network, err
|
||||
import hmac
|
||||
import hashlib
|
||||
import ujson
|
||||
@@ -24,7 +24,6 @@ from wifi import wifi_manager
|
||||
import subprocess
|
||||
|
||||
|
||||
|
||||
def _wifi_tls_would_block(exc):
|
||||
"""
|
||||
非阻塞 TLS 下 recv/send 常抛出 WANT_READ / WANT_WRITE(或等价文案),
|
||||
@@ -84,7 +83,8 @@ class NetworkManager:
|
||||
try:
|
||||
import archery_netcore as _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")
|
||||
else:
|
||||
print("[NET] archery_netcore not found parse_packet or make_packet")
|
||||
@@ -147,7 +147,6 @@ class NetworkManager:
|
||||
|
||||
# ==================== 内部状态管理方法 ====================
|
||||
|
||||
|
||||
def set_manual_trigger(self, value=True):
|
||||
"""设置手动触发标志(公共方法)"""
|
||||
self._manual_trigger_flag = value
|
||||
@@ -203,6 +202,7 @@ class NetworkManager:
|
||||
|
||||
def read_device_id(self):
|
||||
"""从 /device_key 文件读取设备唯一 ID,失败则使用默认值"""
|
||||
|
||||
def _set_password_for_device_id(device_id):
|
||||
if getattr(config, "USE_TCP_SSL", False):
|
||||
iccid = self.get_4g_mccid()
|
||||
@@ -242,6 +242,7 @@ class NetworkManager:
|
||||
连接 Wi-Fi:委托 ``wifi_manager.connect_wifi``。
|
||||
未指定 ``verify_host``/``verify_port`` 时,可达性校验使用本管理器配置的 ``_server_ip``/``_server_port``。
|
||||
"""
|
||||
|
||||
def _verify(ip: str):
|
||||
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
|
||||
@@ -552,7 +553,6 @@ class NetworkManager:
|
||||
self._session_force_4g = False
|
||||
return False
|
||||
|
||||
|
||||
def _cmd200_detect_laser(self):
|
||||
"""后台线程执行 cmd200 激光检测,避免阻塞主循环"""
|
||||
from laser_manager import laser_manager
|
||||
@@ -598,8 +598,24 @@ class NetworkManager:
|
||||
err.check_raise(e, "connect wifi failed")
|
||||
if self.logger:
|
||||
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(
|
||||
["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:
|
||||
self.logger.error(f"[ota] cmd300 失败: {e}")
|
||||
self.safe_enqueue(
|
||||
@@ -615,9 +631,6 @@ class NetworkManager:
|
||||
"""线程安全地将消息加入队列(公共方法)"""
|
||||
self._enqueue((msg_type, data_dict), high)
|
||||
|
||||
|
||||
|
||||
|
||||
def connect_server(self):
|
||||
"""
|
||||
连接到服务器(自动选择WiFi或4G)
|
||||
@@ -894,7 +907,6 @@ class NetworkManager:
|
||||
finally:
|
||||
self._uart4g_lock.release()
|
||||
|
||||
|
||||
def tcp_send_raw(self, data: bytes, max_retries=2) -> bool:
|
||||
"""
|
||||
统一的TCP发送接口(自动选择WiFi或4G)
|
||||
@@ -942,7 +954,7 @@ class NetworkManager:
|
||||
raise
|
||||
if sent == 0:
|
||||
# socket连接已断开
|
||||
self.logger.warning(f"[WIFI-TCP] 发送失败,socket已断开(尝试 {attempt+1}/{max_retries})")
|
||||
self.logger.warning(f"[WIFI-TCP] 发送失败,socket已断开(尝试 {attempt + 1}/{max_retries})")
|
||||
raise OSError("wifi socket closed (send returned 0)")
|
||||
total_sent += sent
|
||||
|
||||
@@ -953,7 +965,7 @@ class NetworkManager:
|
||||
time.sleep_ms(50)
|
||||
|
||||
except OSError as e:
|
||||
self.logger.error(f"[WIFI-TCP] 发送异常: {e}(尝试 {attempt+1}/{max_retries})")
|
||||
self.logger.error(f"[WIFI-TCP] 发送异常: {e}(尝试 {attempt + 1}/{max_retries})")
|
||||
# 发送异常通常意味着连接已不可用,主动关闭以触发重连
|
||||
try:
|
||||
wifi_manager.wifi_socket.close()
|
||||
@@ -963,7 +975,7 @@ class NetworkManager:
|
||||
self._tcp_connected = False
|
||||
return False
|
||||
except Exception as e:
|
||||
self.logger.error(f"[WIFI-TCP] 未知错误: {e}(尝试 {attempt+1}/{max_retries})")
|
||||
self.logger.error(f"[WIFI-TCP] 未知错误: {e}(尝试 {attempt + 1}/{max_retries})")
|
||||
try:
|
||||
wifi_manager.wifi_socket.close()
|
||||
except:
|
||||
@@ -1054,7 +1066,6 @@ class NetworkManager:
|
||||
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}")
|
||||
|
||||
|
||||
# 3) 引用根证书
|
||||
r = hardware_manager.at_client.send(f'AT+MSSLCFG="cert",{ssl_id},"{cert_filename}"', "OK", 3000)
|
||||
if "OK" not in r:
|
||||
@@ -1112,7 +1123,8 @@ class NetworkManager:
|
||||
self.logger.error(f"[WIFI-TCP] 接收数据异常: {e}")
|
||||
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
|
||||
|
||||
Args:
|
||||
@@ -1245,7 +1257,8 @@ class NetworkManager:
|
||||
staged_paths.append(dst)
|
||||
except Exception as 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:
|
||||
shutil.rmtree(staging_dir)
|
||||
except:
|
||||
@@ -1273,7 +1286,8 @@ class NetworkManager:
|
||||
self.logger.info(f"[LOG_UPLOAD] 日志压缩包已生成: {archive_path}")
|
||||
except Exception as 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:
|
||||
shutil.rmtree(staging_dir)
|
||||
except:
|
||||
@@ -1322,7 +1336,8 @@ class NetworkManager:
|
||||
"status_code": response.status_code
|
||||
}, 2)
|
||||
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({
|
||||
"result": "log_upload_failed",
|
||||
"reason": f"http_{response.status_code}",
|
||||
@@ -1458,7 +1473,8 @@ class NetworkManager:
|
||||
except Exception as 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 双路径)
|
||||
|
||||
流程:准备日志归档 -> 自动检测网络 -> WiFi(requests) 或 4G(AT命令) 上传
|
||||
@@ -1793,7 +1809,8 @@ class NetworkManager:
|
||||
|
||||
if not logged_in:
|
||||
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:
|
||||
pass
|
||||
|
||||
@@ -1819,7 +1836,8 @@ class NetworkManager:
|
||||
pending_obj = json.load(f)
|
||||
except:
|
||||
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")
|
||||
except Exception as e:
|
||||
self.logger.error(f"[OTA] ota_ok 上报失败: {e}")
|
||||
@@ -1838,7 +1856,8 @@ class NetworkManager:
|
||||
t = body.get('t', 0)
|
||||
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()
|
||||
# 或者更简单:每次收到命令40时,如果版本号不同,清空缓存
|
||||
if len(self._raw_line_data) > 0:
|
||||
@@ -1855,7 +1874,7 @@ class NetworkManager:
|
||||
file.write("\n".join(stock_array))
|
||||
ota_manager.apply_ota_and_reboot(None, local_filename)
|
||||
else:
|
||||
self.safe_enqueue({'data':{'l': len(self._raw_line_data), 'v': v}, 'cmd': 41})
|
||||
self.safe_enqueue({'data': {'l': len(self._raw_line_data), 'v': v}, 'cmd': 41})
|
||||
self.logger.info(f"已下载{len(self._raw_line_data)} 全部:{t} 版本:{v}")
|
||||
|
||||
elif logged_in and msg_type == 100:
|
||||
@@ -1872,7 +1891,8 @@ class NetworkManager:
|
||||
# 验证必需字段
|
||||
if not upload_url or not upload_token or not shoot_id:
|
||||
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:
|
||||
self.logger.info(f"[IMAGE_UPLOAD] 收到图片上传命令,shootId: {shoot_id}")
|
||||
# 查找文件名中包含 shoot_id 的图片文件(文件名格式:shot_{shoot_id}_*.bmp)
|
||||
@@ -1893,15 +1913,19 @@ class NetworkManager:
|
||||
reverse=True
|
||||
)
|
||||
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:
|
||||
self.logger.warning(f"[IMAGE_UPLOAD] 未找到包含shootId={shoot_id}的图片文件")
|
||||
self.logger.warning(
|
||||
f"[IMAGE_UPLOAD] 未找到包含shootId={shoot_id}的图片文件")
|
||||
except Exception as e:
|
||||
self.logger.error(f"[IMAGE_UPLOAD] 查找图片失败: {e}")
|
||||
|
||||
if not target_image:
|
||||
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:
|
||||
# 构建上传key
|
||||
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:
|
||||
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:
|
||||
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令,key: {key}")
|
||||
# 在新线程中执行上传,避免阻塞主循环
|
||||
import _thread
|
||||
_thread.start_new_thread(
|
||||
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)
|
||||
@@ -2040,10 +2066,12 @@ class NetworkManager:
|
||||
# 只有同时满足:WiFi已连接 且 提供了WiFi凭证,才使用WiFi
|
||||
if self.is_wifi_connected() and ssid and password:
|
||||
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:
|
||||
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后面会重启,会重新开表
|
||||
|
||||
@@ -2059,10 +2087,12 @@ class NetworkManager:
|
||||
self.logger.info(f"ssid: {ssid}")
|
||||
self.logger.info(f"password: {password}")
|
||||
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:
|
||||
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"
|
||||
except:
|
||||
ip = "error_getting_ip"
|
||||
@@ -2070,11 +2100,13 @@ class NetworkManager:
|
||||
elif inner_cmd == 44: # 读 4G 本机号码(AT+CNUM)
|
||||
cnum = self.get_4g_phone_number()
|
||||
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: # 读 MCCID(AT+MCCID)
|
||||
mccid = self.get_4g_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:
|
||||
self.logger.info(f"[TEST] 收到TCP射箭触发命令, {time.time()}")
|
||||
self._manual_trigger_flag = True
|
||||
@@ -2111,14 +2143,16 @@ class NetworkManager:
|
||||
|
||||
if not 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:
|
||||
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令,目标URL: {upload_url}")
|
||||
# 在新线程中执行上传,避免阻塞主循环
|
||||
import _thread
|
||||
_thread.start_new_thread(
|
||||
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:
|
||||
self.logger.info("[LASER] cmd200 在后台线程执行检测")
|
||||
@@ -2181,7 +2215,8 @@ class NetworkManager:
|
||||
current_time = time.ticks_ms()
|
||||
if logged_in and current_time - last_heartbeat_send_time > config.HEARTBEAT_INTERVAL * 1000:
|
||||
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)})):
|
||||
send_hartbeat_fail_count += 1
|
||||
# 短暂波动可能导致一次发送失败:连续失败达到阈值才重连,避免重连风暴
|
||||
@@ -2241,38 +2276,44 @@ class NetworkManager:
|
||||
# 创建全局单例实例
|
||||
network_manager = NetworkManager()
|
||||
|
||||
|
||||
# ==================== 向后兼容的函数接口 ====================
|
||||
|
||||
def tcp_main():
|
||||
"""TCP主循环(向后兼容接口)"""
|
||||
return network_manager.tcp_main()
|
||||
|
||||
|
||||
def read_device_id():
|
||||
"""读取设备ID(向后兼容接口)"""
|
||||
return network_manager.read_device_id()
|
||||
|
||||
|
||||
def safe_enqueue(data_dict, msg_type=2, high=False):
|
||||
"""线程安全地加入队列(向后兼容接口)"""
|
||||
return network_manager.safe_enqueue(data_dict, msg_type, high)
|
||||
|
||||
|
||||
def connect_server():
|
||||
"""连接服务器(向后兼容接口)"""
|
||||
return network_manager.connect_server()
|
||||
|
||||
|
||||
def disconnet_server():
|
||||
"""断开服务器连接(向后兼容接口)"""
|
||||
return network_manager.disconnect_server()
|
||||
|
||||
|
||||
def is_wifi_connected():
|
||||
"""检查WiFi是否已连接(向后兼容接口)"""
|
||||
return network_manager.is_wifi_connected()
|
||||
|
||||
|
||||
def connect_wifi(ssid, password):
|
||||
"""连接WiFi(向后兼容接口)"""
|
||||
return network_manager.connect_wifi(ssid, password)
|
||||
|
||||
|
||||
def is_server_reachable(host, port=80, timeout=5):
|
||||
"""检查服务器是否可达(向后兼容接口)"""
|
||||
return network_manager.is_server_reachable(host, port, timeout)
|
||||
|
||||
|
||||
|
||||
@@ -14,4 +14,6 @@
|
||||
# 1.2.13 修改wifi连接
|
||||
# 1.2.14 修改了icc登录部分
|
||||
# 2.15.3 新版本ota,去除ai算环数方法
|
||||
|
||||
# 2.15.4 更新版本号
|
||||
# 2.15.5 打印ota进度
|
||||
# 2.15.6 更新版本号
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
应用版本号
|
||||
每次 OTA 更新时,只需要更新这个文件中的版本号
|
||||
"""
|
||||
VERSION = '2.15.3'
|
||||
VERSION = '2.15.6'
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user