fix:
This commit is contained in:
75
network.py
75
network.py
@@ -72,6 +72,10 @@ class NetworkManager:
|
||||
self._raw_line_data = []
|
||||
self._manual_trigger_flag = False
|
||||
|
||||
# 限制并发命令线程数
|
||||
self._cmd_thread_lock = threading.Lock()
|
||||
self._cmd_thread_count = 0
|
||||
|
||||
# 网络类型状态
|
||||
self._network_type = None # "wifi" 或 "4G" 或 None
|
||||
# 本次上电曾因 WiFi 质量差切换到 4G 后,直至关机不再改回 WiFi
|
||||
@@ -165,11 +169,15 @@ class NetworkManager:
|
||||
self._password = password
|
||||
|
||||
def _enqueue(self, item, high=False):
|
||||
"""线程安全地加入队列(内部方法)"""
|
||||
"""线程安全地加入队列(内部方法),队列满时丢弃最旧消息"""
|
||||
with self._queue_lock:
|
||||
if high:
|
||||
if len(self._high_send_queue) >= config.MAX_SEND_QUEUE_SIZE:
|
||||
self._high_send_queue.pop(0)
|
||||
self._high_send_queue.append(item)
|
||||
else:
|
||||
if len(self._normal_send_queue) >= config.MAX_SEND_QUEUE_SIZE:
|
||||
self._normal_send_queue.pop(0)
|
||||
self._normal_send_queue.append(item)
|
||||
self._send_event.set()
|
||||
|
||||
@@ -198,6 +206,29 @@ class NetworkManager:
|
||||
"""获取队列锁(用于with语句)"""
|
||||
return self._queue_lock
|
||||
|
||||
def _spawn_cmd_thread(self, target, args=()):
|
||||
"""安全创建命令线程,限制并发数,防止无限创建导致内存耗尽"""
|
||||
with self._cmd_thread_lock:
|
||||
if self._cmd_thread_count >= config.MAX_CMD_THREADS:
|
||||
self.logger.warning(
|
||||
f"[NET] 并发命令线程已达上限({config.MAX_CMD_THREADS}),跳过: {getattr(target, '__name__', str(target))}"
|
||||
)
|
||||
return False
|
||||
self._cmd_thread_count += 1
|
||||
|
||||
def _wrapper(*a):
|
||||
try:
|
||||
target(*a)
|
||||
except Exception as e:
|
||||
self.logger.error(f"[NET] 命令线程异常: {e}")
|
||||
finally:
|
||||
with self._cmd_thread_lock:
|
||||
self._cmd_thread_count -= 1
|
||||
|
||||
import _thread
|
||||
_thread.start_new_thread(_wrapper, args)
|
||||
return True
|
||||
|
||||
# ==================== 业务方法 ====================
|
||||
|
||||
def read_device_id(self):
|
||||
@@ -1728,8 +1759,6 @@ class NetworkManager:
|
||||
|
||||
def tcp_main(self):
|
||||
"""TCP 主通信循环:登录、心跳、处理指令、发送数据"""
|
||||
import _thread
|
||||
|
||||
self.logger.info("[NET] TCP主线程启动")
|
||||
|
||||
send_hartbeat_fail_count = 0
|
||||
@@ -1965,8 +1994,7 @@ class NetworkManager:
|
||||
self.logger.info(f"[IMAGE_UPLOAD] 准备上传: {target_image} -> {key}")
|
||||
|
||||
# 在新线程中执行上传,避免阻塞主循环
|
||||
import _thread
|
||||
_thread.start_new_thread(
|
||||
self._spawn_cmd_thread(
|
||||
self._upload_image_file,
|
||||
(target_image, upload_url, upload_token, key, shoot_id, outlink)
|
||||
)
|
||||
@@ -1994,8 +2022,7 @@ class NetworkManager:
|
||||
else:
|
||||
self.logger.info(f"[LOG_UPLOAD] 收到日志上传命令,key: {key}")
|
||||
# 在新线程中执行上传,避免阻塞主循环
|
||||
import _thread
|
||||
_thread.start_new_thread(
|
||||
self._spawn_cmd_thread(
|
||||
self._upload_log_file_v2,
|
||||
(upload_url, upload_token, key, outlink, include_rotated, max_files,
|
||||
archive_format)
|
||||
@@ -2110,7 +2137,7 @@ class NetworkManager:
|
||||
if mode == "4g":
|
||||
ota_manager._set_ota_url(ota_url) # 记录 OTA URL,供命令7使用
|
||||
ota_manager._start_update_thread()
|
||||
_thread.start_new_thread(ota_manager.direct_ota_download_via_4g, (ota_url,))
|
||||
self._spawn_cmd_thread(ota_manager.direct_ota_download_via_4g, (ota_url,))
|
||||
else: # mode == "wifi"
|
||||
if not ssid or not password:
|
||||
self.logger.error("ota wifi mode requires ssid and password")
|
||||
@@ -2119,7 +2146,7 @@ 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,
|
||||
self._spawn_cmd_thread(ota_manager.handle_wifi_and_update,
|
||||
(ssid, password, ota_url))
|
||||
elif inner_cmd == 6:
|
||||
try:
|
||||
@@ -2179,25 +2206,21 @@ class NetworkManager:
|
||||
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)
|
||||
)
|
||||
# 在新线程中执行上传,避免阻塞主循环
|
||||
self._spawn_cmd_thread(
|
||||
self._upload_log_file,
|
||||
(upload_url, wifi_ssid, wifi_password, include_rotated, max_files,
|
||||
archive_format)
|
||||
)
|
||||
elif inner_cmd == 200:
|
||||
self.logger.info("[LASER] cmd200 在后台线程执行检测")
|
||||
import _thread
|
||||
_thread.start_new_thread(self._cmd200_detect_laser, ())
|
||||
self._spawn_cmd_thread(self._cmd200_detect_laser, ())
|
||||
elif inner_cmd == 300:
|
||||
self.logger.info("[New Ota] cmd300 在后台线程执行OTA")
|
||||
import _thread
|
||||
_thread.start_new_thread(self._cmd300_ota, (data_obj,))
|
||||
self._spawn_cmd_thread(self._cmd300_ota, (data_obj,))
|
||||
elif inner_cmd == 600:
|
||||
self.logger.info("[conn wifi] cmd600 在后台线程执行连接wifi: {data_obj}")
|
||||
import _thread
|
||||
_thread.start_new_thread(self._cmd600_conn_wifi, (data_obj,))
|
||||
self._spawn_cmd_thread(self._cmd600_conn_wifi, (data_obj,))
|
||||
elif inner_cmd == 601:
|
||||
pass
|
||||
else: # data的结构不是 dict
|
||||
@@ -2228,12 +2251,14 @@ class NetworkManager:
|
||||
msg_type, data_dict = item
|
||||
pkt = self._netcore.make_packet(msg_type, data_dict)
|
||||
if not self.tcp_send_raw(pkt):
|
||||
# 发送失败:将消息放回队首,触发重连(避免丢消息)
|
||||
# 发送失败:将消息放回队首(队列满则丢弃)
|
||||
with self.get_queue_lock():
|
||||
if item_is_high:
|
||||
self._high_send_queue.insert(0, item)
|
||||
if len(self._high_send_queue) < config.MAX_SEND_QUEUE_SIZE:
|
||||
self._high_send_queue.insert(0, item)
|
||||
else:
|
||||
self._normal_send_queue.insert(0, item)
|
||||
if len(self._normal_send_queue) < config.MAX_SEND_QUEUE_SIZE:
|
||||
self._normal_send_queue.insert(0, item)
|
||||
self._tcp_connected = False
|
||||
try:
|
||||
self.disconnect_server()
|
||||
|
||||
Reference in New Issue
Block a user