2.15.23
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+37
-1
@@ -69,7 +69,7 @@ class ATClient:
|
||||
# 同上:避免在 _reader_loop 持锁期间二次 acquire
|
||||
self._http_events.append(ev)
|
||||
|
||||
def send(self, cmd: str, expect: str = "OK", timeout_ms: int = 2000):
|
||||
def send(self, cmd: str, expect: str = "OK", timeout_ms: int = 2000, abort_event=None):
|
||||
"""
|
||||
发送 AT 命令并等待 expect(子串匹配)。
|
||||
注意:expect=">" 用于等待 prompt。
|
||||
@@ -90,6 +90,9 @@ class ATClient:
|
||||
|
||||
t0 = time.ticks_ms()
|
||||
while abs(time.ticks_diff(time.ticks_ms(), t0)) < timeout_ms:
|
||||
if abort_event is not None and abort_event.is_set():
|
||||
self._waiting = False
|
||||
break
|
||||
if (not self._waiting) or (self._expect in self._resp):
|
||||
self._waiting = False
|
||||
break
|
||||
@@ -102,6 +105,39 @@ class ATClient:
|
||||
except:
|
||||
return str(self._resp)
|
||||
|
||||
def send_raw_and_wait(self, data: bytes, expect: str = "OK", timeout_ms: int = 1000,
|
||||
suffix: bytes = b""):
|
||||
"""Register the response waiter before writing raw UART data."""
|
||||
expect_b = expect.encode() if isinstance(expect, str) else expect
|
||||
with self._cmd_lock:
|
||||
with self._q_lock:
|
||||
self._waiting = True
|
||||
self._expect = expect_b
|
||||
self._resp = b""
|
||||
|
||||
total = 0
|
||||
while total < len(data):
|
||||
n = self.uart.write(data[total:])
|
||||
if not n or n < 0:
|
||||
time.sleep_ms(1)
|
||||
continue
|
||||
total += n
|
||||
if suffix:
|
||||
self.uart.write(suffix)
|
||||
|
||||
t0 = time.ticks_ms()
|
||||
while abs(time.ticks_diff(time.ticks_ms(), t0)) < timeout_ms:
|
||||
if (not self._waiting) or (self._expect in self._resp):
|
||||
self._waiting = False
|
||||
break
|
||||
time.sleep_ms(5)
|
||||
|
||||
self._waiting = False
|
||||
try:
|
||||
return self._resp.decode(errors="ignore")
|
||||
except:
|
||||
return str(self._resp)
|
||||
|
||||
def _find_urc_tag(self, tag: bytes):
|
||||
"""
|
||||
只在"真正的 URC 边界"查找 tag,避免误命中 HTTP payload 内容。
|
||||
|
||||
@@ -346,10 +346,13 @@ AUTO_POWER_OFF_IN_SECONDS = 10 * 60 # 自动关机时间(秒),0表示不
|
||||
# 实机数据:正常放电约为正电流,插入充电线后约为负电流。
|
||||
CHARGING_SHUTDOWN_ENABLED = True # True=充电时退出应用,False=关闭充电关机功能
|
||||
CHARGING_DIAGNOSTIC_LOG_ENABLED = False
|
||||
CHARGING_CHECK_INTERVAL_MS = 5000
|
||||
CHARGING_CHECK_INTERVAL_MS = 3000
|
||||
CHARGING_CURRENT_THRESHOLD_MA = 100.0
|
||||
CHARGING_CONFIRM_COUNT = 2
|
||||
CHARGING_NOTIFY_TIMEOUT_MS = 30000
|
||||
CHARGING_4G_UART_LOCK_TIMEOUT_SEC = 2.5
|
||||
CHARGING_4G_PROMPT_TIMEOUT_MS = 1500
|
||||
CHARGING_4G_CONFIRM_TIMEOUT_MS = 1000
|
||||
CHARGING_EXIT_SCRIPT = APP_DIR + "/charging_exit.sh"
|
||||
|
||||
BATTERY_SOC_LPF_ALPHA = 0.5
|
||||
|
||||
+120
-3
@@ -67,6 +67,7 @@ class NetworkManager:
|
||||
self._queue_lock = threading.Lock()
|
||||
self._send_event = threading.Event()
|
||||
self._uart4g_lock = threading.Lock()
|
||||
self._terminal_send_event = threading.Event()
|
||||
self._device_id = None
|
||||
self._password = None
|
||||
self._raw_line_data = []
|
||||
@@ -676,7 +677,7 @@ class NetworkManager:
|
||||
except OSError:
|
||||
pass
|
||||
w = network.wifi.Wifi()
|
||||
e = w.connect(ssid, password, wait=True, timeout=15)
|
||||
e = w.connect(ssid, password, wait=True, timeout=10)
|
||||
err.check_raise(e, "connect wifi failed")
|
||||
if self.logger:
|
||||
self.logger.info(f"[ota] Connect success, got ip{w.get_ip()}")
|
||||
@@ -715,6 +716,29 @@ class NetworkManager:
|
||||
self._enqueue((msg_type, data_dict, sent_event), high)
|
||||
return bool(sent_event.wait(max(0, int(timeout_ms)) / 1000.0))
|
||||
|
||||
def safe_replace_queue_and_wait(self, data_dict, msg_type=2, timeout_ms=30000):
|
||||
"""Drop queued messages, enqueue one terminal message, and wait for its TCP write."""
|
||||
sent_event = threading.Event()
|
||||
with self._queue_lock:
|
||||
self._high_send_queue.clear()
|
||||
self._normal_send_queue.clear()
|
||||
self._high_send_queue.append((msg_type, data_dict, sent_event))
|
||||
self._send_event.set()
|
||||
return bool(sent_event.wait(max(0, int(timeout_ms)) / 1000.0))
|
||||
|
||||
def safe_terminal_send_and_wait(self, data_dict, msg_type=2, timeout_ms=30000):
|
||||
"""Cancel ordinary 4G waits and replace queued work with one terminal message."""
|
||||
sent_event = threading.Event()
|
||||
result = {"sent": False}
|
||||
self._terminal_send_event.set()
|
||||
with self._queue_lock:
|
||||
self._high_send_queue.clear()
|
||||
self._normal_send_queue.clear()
|
||||
self._high_send_queue.append((msg_type, data_dict, sent_event, "terminal", result))
|
||||
self._send_event.set()
|
||||
completed = sent_event.wait(max(0, int(timeout_ms)) / 1000.0)
|
||||
return bool(completed and result["sent"])
|
||||
|
||||
def connect_server(self):
|
||||
"""
|
||||
连接到服务器(自动选择WiFi或4G)
|
||||
@@ -1114,8 +1138,12 @@ class NetworkManager:
|
||||
return False
|
||||
try:
|
||||
for _ in range(max_retries):
|
||||
if self._terminal_send_event.is_set():
|
||||
return False
|
||||
cmd = f'AT+MIPSEND={link_id},{len(data)}'
|
||||
if ">" not in hardware_manager.at_client.send(cmd, ">", 2000):
|
||||
if self._terminal_send_event.is_set():
|
||||
return False
|
||||
time.sleep_ms(50)
|
||||
continue
|
||||
|
||||
@@ -1130,14 +1158,73 @@ class NetworkManager:
|
||||
hardware_manager.uart4g.write(b"\x1A")
|
||||
with hardware_manager.at_client._q_lock:
|
||||
hardware_manager.at_client._rx = b""
|
||||
r = hardware_manager.at_client.send("", "OK", 8000)
|
||||
r = hardware_manager.at_client.send(
|
||||
"", "OK", 8000, abort_event=self._terminal_send_event
|
||||
)
|
||||
if ("SEND OK" in r) or ("OK" in r) or ("+MIPSEND" in r):
|
||||
return True
|
||||
if self._terminal_send_event.is_set():
|
||||
return False
|
||||
time.sleep_ms(50)
|
||||
return False
|
||||
finally:
|
||||
self._uart4g_lock.release()
|
||||
|
||||
def _tcp_send_terminal_raw(self, data: bytes) -> bool:
|
||||
if not self._tcp_connected:
|
||||
return False
|
||||
if self._network_type == "wifi":
|
||||
return self._tcp_send_raw_via_wifi(data, max_retries=1)
|
||||
if self._network_type != "4g":
|
||||
return False
|
||||
|
||||
link_id = getattr(config, "TCP_LINK_ID", 0)
|
||||
lock_timeout_sec = float(
|
||||
getattr(config, "CHARGING_4G_UART_LOCK_TIMEOUT_SEC", 2.5)
|
||||
)
|
||||
prompt_timeout_ms = int(
|
||||
getattr(config, "CHARGING_4G_PROMPT_TIMEOUT_MS", 1500)
|
||||
)
|
||||
confirm_timeout_ms = int(
|
||||
getattr(config, "CHARGING_4G_CONFIRM_TIMEOUT_MS", 1000)
|
||||
)
|
||||
lock_start_ms = time.ticks_ms()
|
||||
if not self._uart4g_lock.acquire(timeout=max(0.0, lock_timeout_sec)):
|
||||
self.logger.warning(
|
||||
f"[CHARGE-4G] uart_lock timeout timeout_sec={lock_timeout_sec}"
|
||||
)
|
||||
return False
|
||||
try:
|
||||
lock_elapsed_ms = abs(time.ticks_diff(time.ticks_ms(), lock_start_ms))
|
||||
cmd = f'AT+MIPSEND={link_id},{len(data)}'
|
||||
prompt_start_ms = time.ticks_ms()
|
||||
if ">" not in hardware_manager.at_client.send(
|
||||
cmd, ">", max(0, prompt_timeout_ms)):
|
||||
prompt_elapsed_ms = abs(time.ticks_diff(time.ticks_ms(), prompt_start_ms))
|
||||
self.logger.warning(
|
||||
f"[CHARGE-4G] prompt failed lock_ms={lock_elapsed_ms} "
|
||||
f"prompt_ms={prompt_elapsed_ms}"
|
||||
)
|
||||
return False
|
||||
prompt_elapsed_ms = abs(time.ticks_diff(time.ticks_ms(), prompt_start_ms))
|
||||
confirm_start_ms = time.ticks_ms()
|
||||
r = hardware_manager.at_client.send_raw_and_wait(
|
||||
data,
|
||||
expect="OK",
|
||||
timeout_ms=max(0, confirm_timeout_ms),
|
||||
suffix=b"\x1A",
|
||||
)
|
||||
confirm_elapsed_ms = abs(time.ticks_diff(time.ticks_ms(), confirm_start_ms))
|
||||
sent = ("SEND OK" in r) or ("OK" in r) or ("+MIPSEND" in r)
|
||||
self.logger.warning(
|
||||
f"[CHARGE-4G] send_done lock_ms={lock_elapsed_ms} "
|
||||
f"prompt_ms={prompt_elapsed_ms} confirm_ms={confirm_elapsed_ms} "
|
||||
f"sent={sent}"
|
||||
)
|
||||
return sent
|
||||
finally:
|
||||
self._uart4g_lock.release()
|
||||
|
||||
def _configure_ssl_before_connect(self, link_id: int) -> bool:
|
||||
"""按手册:MSSLCFG(auth) -> (可选) MSSLCERTWR -> MSSLCFG(cert) -> MIPCFG(ssl)"""
|
||||
ssl_id = getattr(config, "SSL_ID", 1)
|
||||
@@ -1874,12 +1961,25 @@ class NetworkManager:
|
||||
pending_cleared = False
|
||||
last_heartbeat_ack_time = time.ticks_ms()
|
||||
last_heartbeat_send_time = time.ticks_ms()
|
||||
last_wifi_sta_check_time = time.ticks_ms()
|
||||
|
||||
while True:
|
||||
# 如果底层连接已断开,尽快跳出内层循环触发重连/重选网络
|
||||
if not self._tcp_connected:
|
||||
break
|
||||
|
||||
if self._network_type == "wifi":
|
||||
now_ms = time.ticks_ms()
|
||||
if abs(time.ticks_diff(now_ms, last_wifi_sta_check_time)) >= 1000:
|
||||
last_wifi_sta_check_time = now_ms
|
||||
if not wifi_manager.is_sta_associated():
|
||||
self.logger.warning(
|
||||
"[WIFI-TCP] STA disconnected; leave WiFi session and reselect network"
|
||||
)
|
||||
wifi_manager.disconnect_wifi()
|
||||
self._tcp_connected = False
|
||||
break
|
||||
|
||||
# OTA 期间暂停 TCP 活动
|
||||
try:
|
||||
from ota_manager import ota_manager
|
||||
@@ -2312,8 +2412,23 @@ class NetworkManager:
|
||||
if item:
|
||||
msg_type, data_dict = item[:2]
|
||||
sent_event = item[2] if len(item) > 2 else None
|
||||
item_is_terminal = len(item) > 3 and item[3] == "terminal"
|
||||
terminal_result = item[4] if item_is_terminal and len(item) > 4 else None
|
||||
pkt = self._netcore.make_packet(msg_type, data_dict)
|
||||
if not self.tcp_send_raw(pkt):
|
||||
send_ok = (
|
||||
self._tcp_send_terminal_raw(pkt)
|
||||
if item_is_terminal
|
||||
else self.tcp_send_raw(pkt)
|
||||
)
|
||||
if not send_ok:
|
||||
if item_is_terminal:
|
||||
if terminal_result is not None:
|
||||
terminal_result["sent"] = False
|
||||
if sent_event is not None:
|
||||
sent_event.set()
|
||||
break
|
||||
if self._terminal_send_event.is_set():
|
||||
continue
|
||||
# 发送失败:将消息放回队首(队列满则丢弃)
|
||||
with self.get_queue_lock():
|
||||
if item_is_high:
|
||||
@@ -2329,6 +2444,8 @@ class NetworkManager:
|
||||
pass
|
||||
break
|
||||
if sent_event is not None:
|
||||
if terminal_result is not None:
|
||||
terminal_result["sent"] = True
|
||||
sent_event.set()
|
||||
|
||||
# 发送激光校准结果
|
||||
|
||||
@@ -179,6 +179,11 @@ def charging_shutdown_monitor():
|
||||
|
||||
if current_ma < -abs(threshold_ma):
|
||||
confirm_count += 1
|
||||
if logger:
|
||||
logger.warning(
|
||||
f"[CHARGE-TIMING] sample tick_ms={maix_time.ticks_ms()} "
|
||||
f"current={current_ma:.1f}mA confirm={confirm_count}/{confirm_required}"
|
||||
)
|
||||
if logger:
|
||||
logger.info(
|
||||
f"[CHARGE] INA226 充电电流 {current_ma:.1f}mA "
|
||||
@@ -209,12 +214,30 @@ def charging_shutdown_monitor():
|
||||
0,
|
||||
int(getattr(config, "CHARGING_NOTIFY_TIMEOUT_MS", 30000)),
|
||||
)
|
||||
notification_sent = network_manager.safe_enqueue_and_wait(
|
||||
notify_start_ms = maix_time.ticks_ms()
|
||||
if logger:
|
||||
logger.warning(
|
||||
f"[CHARGE-TIMING] enqueue_start tick_ms={notify_start_ms} "
|
||||
f"network={network_manager.network_type} "
|
||||
f"tcp_connected={network_manager.tcp_connected} "
|
||||
f"timeout_ms={notify_timeout_ms}"
|
||||
)
|
||||
notification_sent = network_manager.safe_terminal_send_and_wait(
|
||||
{"poweroff": "充电中"},
|
||||
2,
|
||||
high=True,
|
||||
timeout_ms=notify_timeout_ms,
|
||||
)
|
||||
notify_end_ms = maix_time.ticks_ms()
|
||||
notify_elapsed_ms = abs(
|
||||
maix_time.ticks_diff(notify_end_ms, notify_start_ms)
|
||||
)
|
||||
if logger:
|
||||
logger.warning(
|
||||
f"[CHARGE-TIMING] enqueue_done tick_ms={notify_end_ms} "
|
||||
f"elapsed_ms={notify_elapsed_ms} sent={bool(notification_sent)} "
|
||||
f"network={network_manager.network_type} "
|
||||
f"tcp_connected={network_manager.tcp_connected}"
|
||||
)
|
||||
if notification_sent:
|
||||
if logger:
|
||||
logger.info("[CHARGE] 充电状态已发送到服务器")
|
||||
|
||||
Binary file not shown.
@@ -36,3 +36,4 @@
|
||||
# 2.15.24 空改测试
|
||||
# 2.15.25 修复整合后关机失败和ota格式更新问题
|
||||
# 2.15.26
|
||||
# 2.15.33 26-8-12 14:03 修改充4g电关机时间 修复切换网络卡住bug
|
||||
|
||||
+1
-1
@@ -4,6 +4,6 @@
|
||||
应用版本号
|
||||
每次 OTA 更新时,只需要更新这个文件中的版本号
|
||||
"""
|
||||
VERSION = '2.15.31'
|
||||
VERSION = '2.15.33'
|
||||
|
||||
|
||||
|
||||
@@ -549,15 +549,20 @@ class WiFiManager:
|
||||
on_poor_quality_callback: WiFi质量差时的回调函数
|
||||
"""
|
||||
with self._wifi_quality_lock:
|
||||
if self._wifi_quality_monitor_thread is not None and self._wifi_quality_monitor_thread.is_alive():
|
||||
current_thread = self._wifi_quality_monitor_thread
|
||||
current_stop_event = self._wifi_quality_stop_event
|
||||
if (current_thread is not None and current_thread.is_alive()
|
||||
and not current_stop_event.is_set()):
|
||||
self.logger.warning("[WiFi Monitor] 监测线程已在运行")
|
||||
return
|
||||
|
||||
self._network_type_callback = network_type_callback
|
||||
self._on_poor_quality_callback = on_poor_quality_callback
|
||||
self._wifi_quality_stop_event.clear()
|
||||
stop_event = threading.Event()
|
||||
self._wifi_quality_stop_event = stop_event
|
||||
self._wifi_quality_monitor_thread = threading.Thread(
|
||||
target=self._quality_monitor_loop,
|
||||
args=(stop_event,),
|
||||
daemon=True,
|
||||
name="wifi_quality_monitor"
|
||||
)
|
||||
@@ -568,13 +573,14 @@ class WiFiManager:
|
||||
"""停止 WiFi 质量监测线程"""
|
||||
with self._wifi_quality_lock:
|
||||
t = self._wifi_quality_monitor_thread
|
||||
stop_event = self._wifi_quality_stop_event
|
||||
if t is None:
|
||||
return
|
||||
if not t.is_alive():
|
||||
self._wifi_quality_monitor_thread = None
|
||||
return
|
||||
|
||||
self._wifi_quality_stop_event.set()
|
||||
stop_event.set()
|
||||
try:
|
||||
t.join(timeout=2.0)
|
||||
except Exception as e:
|
||||
@@ -588,12 +594,12 @@ class WiFiManager:
|
||||
self._wifi_quality_monitor_thread = None
|
||||
self.logger.info("[WiFi Monitor] 已停止后台监测线程")
|
||||
|
||||
def _quality_monitor_loop(self):
|
||||
def _quality_monitor_loop(self, stop_event):
|
||||
"""
|
||||
WiFi 质量监测循环(后台线程)
|
||||
每 5 秒检查 STA 关联状态和 RSSI,发现断链或质量差则触发切换
|
||||
"""
|
||||
while not self._wifi_quality_stop_event.is_set():
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
# 只在 WiFi 连接时才测量
|
||||
network_type = self._network_type_callback()
|
||||
@@ -631,7 +637,8 @@ class WiFiManager:
|
||||
self.logger.warning("[WiFi Monitor] 质量差,切换前快速重试 2 次(每次间隔1秒)")
|
||||
|
||||
for retry_idx in range(2):
|
||||
time.sleep_ms(1000)
|
||||
if stop_event.wait(1.0):
|
||||
return
|
||||
reachable2 = self.is_sta_associated()
|
||||
rtt2 = None
|
||||
rssi2 = self._get_wifi_rssi_dbm()
|
||||
@@ -660,7 +667,7 @@ class WiFiManager:
|
||||
self._on_poor_quality_callback()
|
||||
|
||||
# 休眠 5 秒
|
||||
time.sleep(5)
|
||||
stop_event.wait(5.0)
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"[WiFi Monitor] 监测异常:{e}")
|
||||
|
||||
Reference in New Issue
Block a user