1. 修改了,远程服务端断开就卡死的bug,
2. 修改图像解析逻辑,使其支持DTU-HMI 的图像格式,不再支持原来的图像格式,不能显示原来设备发来的图像
This commit is contained in:
@@ -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] 刷新线程退出")
|
||||
|
||||
Reference in New Issue
Block a user