This commit is contained in:
gcw_4spBpAfv
2026-02-07 17:09:39 +08:00
parent 61096ba190
commit 8aea76d99b
5 changed files with 158 additions and 42 deletions

View File

@@ -1,6 +1,6 @@
id: t11 id: t11
name: t11 name: t11
version: 1.2.1 version: 1.2.3
author: t11 author: t11
icon: '' icon: ''
desc: t11 desc: t11

View File

@@ -58,9 +58,12 @@ REG_CURRENT = 0x04 # 电流寄存器
REG_CALIBRATION = 0x05 REG_CALIBRATION = 0x05
CALIBRATION_VALUE = 0x1400 CALIBRATION_VALUE = 0x1400
# ==================== 空气传感器配置 ====================
ADC_TRIGGER_THRESHOLD = 50000 # TODO:只是用于测试,最终需要改为正常值
AIR_PRESSURE_lOG = False # TODO: 在正式环境中关闭
# ADC配置 # ADC配置
ADC_CHANNEL = 0 ADC_CHANNEL = 0
ADC_TRIGGER_THRESHOLD = 3000
ADC_LASER_THRESHOLD = 3000 ADC_LASER_THRESHOLD = 3000
# ==================== 激光配置 ==================== # ==================== 激光配置 ====================
@@ -110,6 +113,8 @@ SAVE_IMAGE_ENABLED = True # 是否保存图像True=保存False=不保存
PHOTO_DIR = "/root/phot" # 照片存储目录 PHOTO_DIR = "/root/phot" # 照片存储目录
MAX_IMAGES = 1000 MAX_IMAGES = 1000
SHOW_CAMERA_PHOTO_WHILE_SHOOTING = False # 是否在拍摄时显示摄像头图像True=显示False=不显示建议在连着USB测试过程中打开
# ==================== OTA配置 ==================== # ==================== OTA配置 ====================
MAX_BACKUPS = 5 MAX_BACKUPS = 5
LOG_MAX_BYTES = 10 * 1024 * 1024 # 10MB LOG_MAX_BYTES = 10 * 1024 * 1024 # 10MB

58
main.py
View File

@@ -212,6 +212,39 @@ def cmd_str():
logger.info("系统准备完成...") logger.info("系统准备完成...")
last_adc_trigger = 0 last_adc_trigger = 0
# 气压采样:减少日志频率(每 N 个点输出一条),避免 logger.debug 拖慢采样
PRESSURE_BATCH_SIZE = 100
pressure_buf = []
pressure_sum = 0
pressure_min = 4095
pressure_max = 0
pressure_t0_ms = None
def _flush_pressure_buf(reason: str):
if not config.AIR_PRESSURE_lOG:
return
nonlocal pressure_buf, pressure_sum, pressure_min, pressure_max, pressure_t0_ms, logger
if not pressure_buf:
return
t1_ms = time.ticks_ms()
n = len(pressure_buf)
avg = (pressure_sum / n) if n else 0
# 一行输出:方便后处理画曲线;同时带上统计信息便于快速看波峰
line = (
f"[气压批量] reason={reason} "
f"t0={pressure_t0_ms} t1={t1_ms} n={n} "
f"min={pressure_min} max={pressure_max} avg={avg:.1f} "
f"values={','.join(map(str, pressure_buf))}"
)
if logger:
logger.debug(line)
else:
print(line)
pressure_buf = []
pressure_sum = 0
pressure_min = 4095
pressure_max = 0
pressure_t0_ms = None
# 主循环:检测扳机触发 → 拍照 → 分析 → 上报 # 主循环:检测扳机触发 → 拍照 → 分析 → 上报
while not app.need_exit(): while not app.need_exit():
@@ -246,11 +279,26 @@ def cmd_str():
time.sleep_ms(100) time.sleep_ms(100)
continue continue
if adc_val > config.ADC_TRIGGER_THRESHOLD: # ====== 气压采样缓存(每次循环都记录,批量输出日志)======
if pressure_t0_ms is None:
pressure_t0_ms = current_time
pressure_buf.append(adc_val)
pressure_sum += adc_val
if adc_val < pressure_min:
pressure_min = adc_val
if adc_val > pressure_max:
pressure_max = adc_val
if len(pressure_buf) >= PRESSURE_BATCH_SIZE:
_flush_pressure_buf("batch")
# if adc_val >= 2000:
# print(f"adc :{adc_val}")
if adc_val >= config.ADC_TRIGGER_THRESHOLD:
diff_ms = current_time - last_adc_trigger diff_ms = current_time - last_adc_trigger
if diff_ms < 3000: if diff_ms < 3000:
continue continue
last_adc_trigger = current_time last_adc_trigger = current_time
# 触发前先把缓存刷出来,避免波形被长耗时处理截断
_flush_pressure_buf("before_trigger")
try: try:
frame = camera_manager.read_frame() frame = camera_manager.read_frame()
@@ -397,13 +445,14 @@ def cmd_str():
time.sleep_ms(100) time.sleep_ms(100)
continue continue
else: else:
if config.SHOW_CAMERA_PHOTO_WHILE_SHOOTING:
try: try:
camera_manager.show(camera_manager.read_frame()) camera_manager.show(camera_manager.read_frame())
except Exception as e: except Exception as e:
logger = logger_manager.logger logger = logger_manager.logger
if logger: if logger:
logger.error(f"[MAIN] 显示异常: {e}") logger.error(f"[MAIN] 显示异常: {e}")
time.sleep_ms(50) time.sleep_ms(1)
except Exception as e: except Exception as e:
# 主循环的顶层异常捕获,防止程序静默退出 # 主循环的顶层异常捕获,防止程序静默退出
@@ -416,6 +465,11 @@ def cmd_str():
print(f"[MAIN] 主循环异常: {e}") print(f"[MAIN] 主循环异常: {e}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()
# 异常发生时尽量把缓存刷盘,方便定位问题
try:
_flush_pressure_buf("exception")
except:
pass
time.sleep_ms(1000) # 等待1秒后继续 time.sleep_ms(1000) # 等待1秒后继续

View File

@@ -466,11 +466,29 @@ class NetworkManager:
if not self._wifi_socket: if not self._wifi_socket:
return False return False
try: try:
# 尝试发送0字节来检测连接状态 # send(b"") 在很多实现里是 no-op无法可靠探测断线。
self._wifi_socket.send(b"", socket.MSG_DONTWAIT) # 用非阻塞 peek 来判断若对端已关闭recv 会返回 b""。
data = self._wifi_socket.recv(1, socket.MSG_PEEK | socket.MSG_DONTWAIT)
if data == b"":
raise OSError("wifi socket closed")
return True return True
except BlockingIOError:
# 无数据可读但连接仍在EAGAIN
return True
except OSError as e:
# 兼容不同平台的 EAGAIN / would block
err = getattr(e, "errno", None)
if err in (11, 35, 10035): # EAGAIN/EWOULDBLOCK on linux/mac/win
return True
# socket已断开或不可用清理
try:
self._wifi_socket.close()
except: except:
# socket已断开 pass
self._wifi_socket = None
self._tcp_connected = False
return False
except Exception:
try: try:
self._wifi_socket.close() self._wifi_socket.close()
except: except:
@@ -524,6 +542,9 @@ class NetworkManager:
# 根据网络类型选择发送方式 # 根据网络类型选择发送方式
if self._network_type == "wifi": if self._network_type == "wifi":
# 先快速校验 WiFi socket 是否仍有效,避免卡在半开连接上
if not self._check_wifi_connection():
return False
return self._tcp_send_raw_via_wifi(data, max_retries) return self._tcp_send_raw_via_wifi(data, max_retries)
elif self._network_type == "4g": elif self._network_type == "4g":
return self._tcp_send_raw_via_4g(data, max_retries) return self._tcp_send_raw_via_4g(data, max_retries)
@@ -546,7 +567,7 @@ class NetworkManager:
if sent == 0: if sent == 0:
# socket连接已断开 # socket连接已断开
self.logger.warning(f"[WIFI-TCP] 发送失败socket已断开尝试 {attempt+1}/{max_retries}") self.logger.warning(f"[WIFI-TCP] 发送失败socket已断开尝试 {attempt+1}/{max_retries}")
break raise OSError("wifi socket closed (send returned 0)")
total_sent += sent total_sent += sent
if total_sent == len(data): if total_sent == len(data):
@@ -557,10 +578,23 @@ class NetworkManager:
except OSError as e: 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}")
time.sleep_ms(50) # 发送异常通常意味着连接已不可用,主动关闭以触发重连
try:
self._wifi_socket.close()
except:
pass
self._wifi_socket = None
self._tcp_connected = False
return False
except Exception as e: 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}")
time.sleep_ms(50) try:
self._wifi_socket.close()
except:
pass
self._wifi_socket = None
self._tcp_connected = False
return False
return False return False
@@ -743,6 +777,9 @@ class NetworkManager:
temp_dir = "/tmp" temp_dir = "/tmp"
temp_file_path = os.path.join(temp_dir, new_filename) temp_file_path = os.path.join(temp_dir, new_filename)
# 先 sync确保所有日志都已写入磁盘
os.system("sync")
# 复制日志文件到临时位置 # 复制日志文件到临时位置
shutil.copy2(log_file_path, temp_file_path) shutil.copy2(log_file_path, temp_file_path)
self.logger.info(f"[LOG_UPLOAD] 日志文件已复制到: {temp_file_path}") self.logger.info(f"[LOG_UPLOAD] 日志文件已复制到: {temp_file_path}")
@@ -853,6 +890,10 @@ class NetworkManager:
# if not self.tcp_send_raw(self.make_packet(1, login_data)): # if not self.tcp_send_raw(self.make_packet(1, login_data)):
if not self.tcp_send_raw(self._netcore.make_packet(1, login_data)): if not self.tcp_send_raw(self._netcore.make_packet(1, login_data)):
self._tcp_connected = False self._tcp_connected = False
try:
self.disconnect_server()
except:
pass
time.sleep_ms(2000) time.sleep_ms(2000)
continue continue
@@ -863,6 +904,10 @@ class NetworkManager:
last_heartbeat_send_time = time.ticks_ms() last_heartbeat_send_time = time.ticks_ms()
while True: while True:
# 如果底层连接已断开,尽快跳出内层循环触发重连/重选网络
if not self._tcp_connected:
break
# OTA 期间暂停 TCP 活动 # OTA 期间暂停 TCP 活动
try: try:
from ota_manager import ota_manager from ota_manager import ota_manager
@@ -1090,6 +1135,8 @@ class NetworkManager:
except: except:
pass pass
time.sleep_ms(2000) time.sleep_ms(2000)
os.system("sync") # 刷新文件系统缓存到磁盘,防止数据丢失
time.sleep_ms(500)
os.system("poweroff") os.system("poweroff")
return return
elif inner_cmd == 43: # 上传日志命令 elif inner_cmd == 43: # 上传日志命令
@@ -1111,23 +1158,33 @@ class NetworkManager:
time.sleep_ms(5) time.sleep_ms(5)
# 发送队列中的业务数据 # 发送队列中的业务数据
if logged_in and (self._high_send_queue or self._normal_send_queue): if logged_in:
msg_type = None item = None
data_dict = None item_is_high = False
if self.get_queue_lock().acquire(blocking=False): # 出队:发送失败时会把 item 放回队首,避免丢数据
try: with self.get_queue_lock():
if self._high_send_queue: if self._high_send_queue:
msg_type, data_dict = self._high_send_queue.pop(0) item = self._high_send_queue.pop(0)
item_is_high = True
elif self._normal_send_queue: elif self._normal_send_queue:
msg_type, data_dict = self._normal_send_queue.pop(0) item = self._normal_send_queue.pop(0)
finally: item_is_high = False
self.get_queue_lock().release()
if msg_type is not None and data_dict is not None: if item:
msg_type, data_dict = item
pkt = self._netcore.make_packet(msg_type, data_dict) pkt = self._netcore.make_packet(msg_type, data_dict)
# pkt = self.make_packet(msg_type, data_dict)
if not self.tcp_send_raw(pkt): if not self.tcp_send_raw(pkt):
# 发送失败:将消息放回队首,触发重连(避免丢消息)
with self.get_queue_lock():
if item_is_high:
self._high_send_queue.insert(0, item)
else:
self._normal_send_queue.insert(0, item)
self._tcp_connected = False self._tcp_connected = False
try:
self.disconnect_server()
except:
pass
break break
# 发送激光校准结果 # 发送激光校准结果
@@ -1144,15 +1201,15 @@ class NetworkManager:
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)})):
self.logger.error("心跳发送失败") # 心跳失败说明链路不可用:立刻触发重连,避免长时间卡住
time.sleep_ms(3000) self.logger.error("心跳发送失败,准备重连")
send_hartbeat_fail_count += 1 send_hartbeat_fail_count += 1
if send_hartbeat_fail_count >= 3: self._tcp_connected = False
send_hartbeat_fail_count = 0 try:
self.logger.error("连续3次发送心跳失败重连") self.disconnect_server()
except:
pass
break break
else:
continue
else: else:
send_hartbeat_fail_count = 0 send_hartbeat_fail_count = 0
last_heartbeat_send_time = current_time last_heartbeat_send_time = current_time

View File

@@ -4,14 +4,14 @@
应用版本号 应用版本号
每次 OTA 更新时,只需要更新这个文件中的版本号 每次 OTA 更新时,只需要更新这个文件中的版本号
""" """
VERSION = '1.2.3' VERSION = '1.2.4'
# 1.2.0 开始使用C++编译成.so替换部分代码 # 1.2.0 开始使用C++编译成.so替换部分代码
# 1.2.1 ota使用加密包 # 1.2.1 ota使用加密包
# 1.2.2 支持wifi ota并且设定时区并使用单独线程保存图片 # 1.2.2 支持wifi ota并且设定时区并使用单独线程保存图片
# 1.2.3 修改ADC_TRIGGER_THRESHOLD 为2300支持上传日志到服务器 # 1.2.3 修改ADC_TRIGGER_THRESHOLD 为2300支持上传日志到服务器
# 1.2.4 修改ADC_TRIGGER_THRESHOLD 为3000并默认关闭摄像头的显示并把ADC的采样间隔从50ms降低到10ms
# 1.2.5 支持空气传感器采样,并默认关闭日志。优化断网时的发送队列丢消息问题,解决 WiFi 断线检测不可靠问题。