1. 修改了,远程服务端断开就卡死的bug,

2. 修改图像解析逻辑,使其支持DTU-HMI 的图像格式,不再支持原来的图像格式,不能显示原来设备发来的图像
This commit is contained in:
2026-03-12 18:06:07 +08:00
parent fb2d4ffc00
commit f7898adc5d
4 changed files with 38 additions and 908 deletions

View File

@@ -236,6 +236,7 @@ def fetch_screen() -> Optional[bytes]:
if not _send(CMD_LCDMEM, struct.pack(">I", 0)): # 发送读取显存命令,起始地址 0大端 4 字节)
logger.error("[fetch_screen] 发送 CMD_LCDMEM 失败")
return None
logger.debug("[fetch_screen] 发送 CMD_LCDMEM 成功")
result = _recv() # 接收设备回复
if not result:
logger.error("[fetch_screen] 未收到任何回复result=None")
@@ -268,11 +269,16 @@ def mono_to_png(data: bytes, width: int, height: int) -> Optional[bytes]:
)
img = Image.new("1", (width, height)) # 创建 1bpp 黑白图像
pix = img.load() # 获取像素访问对象
'''
for y in range(height):
for x in range(width):
bi = (y * width + x) // 8 # 字节索引:每 8 像素一字节
bit = 7 - (x % 8) # 位索引:高位在前
pix[x, y] = 0 if (bi < len(data) and (data[bi] >> bit) & 1) else 255 # 1→白 0→黑
'''
for y in range(height):
for x in range(width):
pix[x, y] = data[y * width + x]
buf = io.BytesIO() # 内存缓冲区
img.save(buf, format="PNG") # 保存为 PNG 格式
png_bytes = buf.getvalue()
@@ -305,15 +311,21 @@ def disconnect_device():
def _screen_refresh_loop():
"""
后台线程:每约 100ms 拉取一帧屏幕,转 PNG 后 base64 推送给各 WebSocket 客户端。
若检测到远程服务端断开(连续多次拉取失败),则断开设备并停止重复请求。
_refresh_stop.wait(0.1) 既作间隔,也便于收到 set 时快速退出。
"""
global _screen_clients, _refresh_stop
logger.info("[_screen_refresh_loop] 刷新线程启动")
# 连续拉取失败次数,超过阈值则认为远程已断开
MAX_CONSECUTIVE_FAILURES = 5
consecutive_failures = 0
try:
while not _refresh_stop.is_set(): # 未被要求停止时持续循环
if _screen_clients and _sock: # 有客户端且已连接设备
data = fetch_screen() # 拉取屏幕位图
logger.debug("[fetch_screen] 拉取屏幕位图数据: {data}", data=data)
if data:
consecutive_failures = 0 # 成功则清零
# 当前协议未返回宽高,这里固定为 160x160可根据实际情况调整
w, h = 160, 160
png = mono_to_png(data, w, h) # 转为 PNG
@@ -332,6 +344,24 @@ def _screen_refresh_loop():
sid=sid,
err=e,
)
else:
# 拉取失败,累计连续失败次数
consecutive_failures += 1
if consecutive_failures >= MAX_CONSECUTIVE_FAILURES:
logger.warning(
"[_screen_refresh_loop] 连续 {n} 次拉取失败,判定远程服务端已断开,停止请求并断开连接",
n=consecutive_failures,
)
disconnect_device()
# 通知所有订阅客户端:设备已断开
for sid in list(_screen_clients):
try:
socketio.emit("device_disconnected", {"reason": "remote_closed"}, room=sid)
except Exception as e:
logger.debug("[_screen_refresh_loop] 推送 device_disconnected 失败 sid={sid} - {err}", sid=sid, err=e)
consecutive_failures = 0
else:
consecutive_failures = 0 # 无客户端或未连接时清零,便于下次连接后重新计数
_refresh_stop.wait(timeout=0.1) # 等待 100ms若被 set 则立即返回
finally:
logger.info("[_screen_refresh_loop] 刷新线程退出")