From 9f54a0cb2e83c9865845fe40e6976f705b193ef0 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Tue, 3 Mar 2026 10:24:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=89=93=E5=8D=B0=EF=BC=8C=E5=9B=BE=E5=83=8F?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=A2=9E=E5=8A=A0=E4=B8=BA=E5=8E=9F=E6=9D=A5?= =?UTF-8?q?=E7=9A=84=E4=B8=A4=E5=80=8D=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 5 +++++ remo_disp_server.py | 33 ++++++++++++++++++++++----------- remo_disp_ui.html | 17 ++++++++++------- 3 files changed, 37 insertions(+), 18 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a8c2003 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python-envs.defaultEnvManager": "ms-python.python:conda", + "python-envs.defaultPackageManager": "ms-python.python:conda", + "python-envs.pythonProjects": [] +} \ No newline at end of file diff --git a/remo_disp_server.py b/remo_disp_server.py index bb79dbb..9b4cdd4 100644 --- a/remo_disp_server.py +++ b/remo_disp_server.py @@ -52,8 +52,6 @@ CMD_KEEPLIVE, CMD_INIT, CMD_KEY, CMD_LCDMEM = 0, 1, 2, 3 # ============================================================================= # _sock:与 DTU 装置的 TCP socket,None 表示未连接 _sock: Optional[socket.socket] = None -# _init_info:初始化信息(width/height/mem_size),由 CMD_INIT 获取 -_init_info: Optional[dict] = None # _screen_clients:当前需要接收屏幕推送的 WebSocket 客户端 sid 集合 _screen_clients: Set[str] = set() # _refresh_thread:后台刷新线程,持续拉取屏幕并推送 @@ -94,12 +92,15 @@ def parse_frame(raw: bytes) -> Optional[Tuple[int, bytes]]: 帧格式: [TAG(1)][cmd(1)][len_hi(1)][len_lo(1)][data(length)][crc(1)] """ if len(raw) < 6 or raw[0] != TAG_DEVICE: # 至少 6 字节且帧头为 TAG_DEVICE + print("[parse_frame] 错误: 帧太短或帧头不是 TAG_DEVICE(0xBB), len=%d" % len(raw)) return None length = (raw[2] << 8) | raw[3] # 大端序解析数据区长度 if len(raw) < 4 + length + 1: # 检查是否收齐整帧(头4字节+数据+CRC1字节) + print("[parse_frame] 错误: 数据不完整, 需要 %d 字节, 实际 %d 字节" % (4 + length + 1, len(raw))) return None data = raw[4:4 + length] # 提取数据区 if calc_crc(data) != raw[4 + length]: # CRC 校验失败 + print("[parse_frame] 错误: CRC 校验失败") return None return (raw[1], data) # 返回 (命令码, 数据) @@ -113,7 +114,7 @@ def connect(host: str, port: int = PORT) -> bool: 建立与 DTU 装置的 TCP 连接。 若有旧连接则先关闭;连接时超时 3 秒;连接成功后设为 2 秒。 """ - global _sock, _init_info + global _sock try: if _sock: _sock.close() # 关闭旧连接 @@ -122,18 +123,21 @@ def connect(host: str, port: int = PORT) -> bool: _sock.connect((host, port)) # 连接目标主机和端口 _sock.settimeout(2.0) # 连接成功后收发超时 2 秒 return True - except Exception: + except Exception as e: + print("[connect] 错误: 连接失败 - %s" % e) return False def _send(cmd: int, data: bytes = b"") -> bool: """发送一帧到设备""" if not _sock: + print("[_send] 错误: 未连接设备") return False try: _sock.sendall(build_frame(cmd, data)) # 构造帧并一次性发送 return True - except Exception: + except Exception as e: + print("[_send] 错误: 发送失败 - %s" % e) return False @@ -143,16 +147,20 @@ def _recv() -> Optional[Tuple[int, bytes]]: 至少需 5 字节(4 字节头+1 字节数据或 CRC);根据头中 length 检查是否收齐整帧。 """ if not _sock: + print("[_recv] 错误: 未连接设备") return None try: data = _sock.recv(256 * 1024) # 单次接收最多 256KB if len(data) < 5: # 至少需要 5 字节(4 头 + 1 数据/CRC) + print("[_recv] 错误: 接收数据太短, len=%d" % len(data)) return None length = (data[2] << 8) | data[3] # 解析数据区长度 if len(data) < 4 + length + 1: # 数据不完整 + print("[_recv] 错误: 数据不完整, 需要 %d 字节, 实际 %d 字节" % (4 + length + 1, len(data))) return None return parse_frame(data[:4 + length + 1]) # 解析并返回 (cmd, data) - except Exception: + except Exception as e: + print("[_recv] 错误: 接收异常 - %s" % e) return None @@ -167,8 +175,10 @@ def fetch_screen() -> Optional[bytes]: 发送 CMD_LCDMEM + 起始地址 0;设备回复 payload 前 4 字节为地址,后续为位图,返回 payload[4:]。 """ if not _sock: + print("[fetch_screen] 错误: 未连接设备") return None if not _send(CMD_LCDMEM, struct.pack(">I", 0)): # 发送读取显存命令,起始地址 0(大端 4 字节) + print("[fetch_screen] 错误: 发送 CMD_LCDMEM 失败") return None result = _recv() # 接收设备回复 if result and result[0] == CMD_LCDMEM: # 确认为 LCDMEM 回复 @@ -176,6 +186,7 @@ def fetch_screen() -> Optional[bytes]: if len(payload) >= 4: # 前 4 字节为地址,后面是位图 return payload[4:] return payload or None + print("[fetch_screen] 错误: 未收到有效 LCDMEM 回复") return None @@ -197,19 +208,19 @@ def mono_to_png(data: bytes, width: int, height: int) -> Optional[bytes]: img.save(buf, format="PNG") # 保存为 PNG 格式 return buf.getvalue() # 返回 PNG 字节流 except ImportError: + print("[mono_to_png] 错误: 缺少 PIL/Pillow, 请执行 pip install pillow") return None def disconnect_device(): """关闭与设备的连接""" - global _sock, _init_info + global _sock try: if _sock: _sock.close() # 关闭 socket _sock = None # 清空引用 - _init_info = None # 清空初始化信息 - except Exception: - pass + except Exception as e: + print("[disconnect_device] 错误: 关闭连接时异常 - %s" % e) # ============================================================================= @@ -226,7 +237,7 @@ def _screen_refresh_loop(): if _screen_clients and _sock: # 有客户端且已连接设备 data = fetch_screen() # 拉取屏幕位图 if data: - info = _init_info or {} # 获取宽高,缺省 160x160 + info = {"width": 160, "height": 160} # 设置长宽 160x160 w, h = info.get("width", 160), info.get("height", 160) png = mono_to_png(data, w, h) # 转为 PNG if png: diff --git a/remo_disp_ui.html b/remo_disp_ui.html index a73f513..b07fcb6 100644 --- a/remo_disp_ui.html +++ b/remo_disp_ui.html @@ -402,12 +402,15 @@ // 创建临时 URL const img = new Image(); img.onload = () => { - // 图片加载完成后 - canvas.width = img.width; - canvas.height = img.height; - // 设置 canvas 尺寸 - canvas.getContext('2d').drawImage(img, 0, 0); - // 绘制到 canvas + // 图片加载完成后,放大一倍显示(160x160 -> 320x320) + const scale = 2; + canvas.width = img.width * scale; + canvas.height = img.height * scale; + const ctx = canvas.getContext('2d'); + ctx.imageSmoothingEnabled = false; + // 关闭平滑,像素清晰放大 + ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height); + // 绘制到 canvas(2 倍尺寸) canvas.style.display = 'block'; placeholder.style.display = 'none'; // 显示 canvas,隐藏占位符 @@ -467,7 +470,7 @@ }); document.querySelector('.menu-item.exit').onclick = doDisconnect; // 断开按钮 -> 执行断开 - document.getElementById('btnAbout').onclick = () => alert('远程显示工具\n与 RemoDispBus 协议兼容\n端口 7003'); + document.getElementById('btnAbout').onclick = () => alert('阜阳师范大学物理与电子工程学院\nDTU 远程液晶屏幕通信工具'); // 关于按钮 -> 弹出说明