feat(HDMI-Tool): 光机串口下拉显示设备描述, CH344 多路口可辨
This commit is contained in:
+4
-2
@@ -86,8 +86,10 @@ HDMI-Tool/
|
|||||||
`protocols.md` 为准:命令帧 `D5 01 LEN <ASCII命令> CRC`、响应帧 `B5 TYPE LEN <payload> CRC`,
|
`protocols.md` 为准:命令帧 `D5 01 LEN <ASCII命令> CRC`、响应帧 `B5 TYPE LEN <payload> CRC`,
|
||||||
CRC 为第 2 字节起累加和取低 8 位。
|
CRC 为第 2 字节起累加和取低 8 位。
|
||||||
|
|
||||||
GUI"光机控制"区:选串口 → 连接 → 点快捷按钮或命令下拉/自定义输入发送;日志区实时显示
|
GUI"光机控制"区:选串口 → 连接 → 点快捷按钮或命令下拉/自定义输入发送;串口下拉显示
|
||||||
`→ 命令` / `← 译码结果 (原始 hex)`,错误自动附释义(如 `err:3(I2C NACK)`)。
|
"COMx + 设备描述",CH344 等多路适配器每一路描述不同(如 serial-A/B/C/D),据此选口;
|
||||||
|
多口描述相同时自动附物理位置消歧。日志区实时显示 `→ 命令` / `← 译码结果 (原始 hex)`,
|
||||||
|
错误自动附释义(如 `err:3(I2C NACK)`)。
|
||||||
|
|
||||||
| 命令 | 功能 | 响应示例 |
|
| 命令 | 功能 | 响应示例 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
Binary file not shown.
@@ -192,6 +192,7 @@ HDMI-Tool/
|
|||||||
├── release/ # 构建与发布产物(机器生成区, 不手改)
|
├── release/ # 构建与发布产物(机器生成区, 不手改)
|
||||||
│ ├── HDMI-Tool.exe # 打包单文件版(改动跟随源码提交入库)
|
│ ├── HDMI-Tool.exe # 打包单文件版(改动跟随源码提交入库)
|
||||||
│ ├── pattern_slots.json # exe 运行配置(栏位预置 + 串口记忆)
|
│ ├── pattern_slots.json # exe 运行配置(栏位预置 + 串口记忆)
|
||||||
|
│ ├── *.png / README.md # 附属文件平铺副本(由 package_release.py 从 assets/ 同步)
|
||||||
│ └── *.zip # 发布包(不入库, 上传 Gitea Release)
|
│ └── *.zip # 发布包(不入库, 上传 Gitea Release)
|
||||||
├── README.md # 本说明
|
├── README.md # 本说明
|
||||||
└── AGENTS.md # 面向 AI/Agent 的工程档案(结构/约定/构建规程)
|
└── AGENTS.md # 面向 AI/Agent 的工程档案(结构/约定/构建规程)
|
||||||
|
|||||||
@@ -168,11 +168,31 @@ class DLPC485:
|
|||||||
threading.Thread(target=self._run, daemon=True).start()
|
threading.Thread(target=self._run, daemon=True).start()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def list_ports():
|
def list_ports_info():
|
||||||
"""枚举本机串口名(未装 pyserial 返回空表)"""
|
"""枚举串口, 返回 [(设备名, 显示标签), ...]。
|
||||||
|
|
||||||
|
标签 = "COMx 设备描述"(描述里的 "(COMx)" 尾巴去掉, COM 号已在最前),
|
||||||
|
CH344 这类多路适配器的每一路描述不同(如 serial-A/B/C/D), 依此区分;
|
||||||
|
多口描述完全相同(同型号多口)时追加物理位置/序列号消歧。
|
||||||
|
"""
|
||||||
if not HAS_SERIAL:
|
if not HAS_SERIAL:
|
||||||
return []
|
return []
|
||||||
return [p.device for p in serial.tools.list_ports.comports()]
|
items = []
|
||||||
|
for p in serial.tools.list_ports.comports():
|
||||||
|
desc = re.sub(r'\s*\((COM\d+)\)\s*$', '', (p.description or '').strip(),
|
||||||
|
flags=re.I)
|
||||||
|
if desc.upper() == p.device.upper():
|
||||||
|
desc = ''
|
||||||
|
label = f'{p.device} {desc}'.rstrip()
|
||||||
|
tail = p.location or p.serial_number or ''
|
||||||
|
items.append((p.device, label, tail))
|
||||||
|
counts = collections.Counter(label for _d, label, _t in items)
|
||||||
|
out = []
|
||||||
|
for dev, label, tail in items:
|
||||||
|
if counts[label] > 1 and tail: # 同标签多口: 附物理位置消歧
|
||||||
|
label = f'{label} [{tail}]'
|
||||||
|
out.append((dev, label))
|
||||||
|
return out
|
||||||
|
|
||||||
def open(self, port, baud=DEFAULT_BAUD):
|
def open(self, port, baud=DEFAULT_BAUD):
|
||||||
if not HAS_SERIAL:
|
if not HAS_SERIAL:
|
||||||
|
|||||||
+32
-11
@@ -1179,10 +1179,12 @@ def run_gui(auto_close=0):
|
|||||||
r1 = ttk.Frame(grp2)
|
r1 = ttk.Frame(grp2)
|
||||||
r1.pack(fill='x')
|
r1.pack(fill='x')
|
||||||
ttk.Label(r1, text='串口:').pack(side='left')
|
ttk.Label(r1, text='串口:').pack(side='left')
|
||||||
self.cbo_dlpc = ttk.Combobox(r1, width=12, state='readonly',
|
# 下拉显示 "COMx + 设备描述"(CH344 四路按 serial-A/B/C/D 等描述区分)
|
||||||
values=dlpc_rs485.DLPC485.list_ports())
|
self._dlpc_ports = dlpc_rs485.DLPC485.list_ports_info()
|
||||||
|
self.cbo_dlpc = ttk.Combobox(r1, width=22, state='readonly',
|
||||||
|
values=[lb for _d, lb in self._dlpc_ports])
|
||||||
if SERIAL_CFG['port']:
|
if SERIAL_CFG['port']:
|
||||||
self.cbo_dlpc.set(SERIAL_CFG['port'])
|
self._dlpc_pick(SERIAL_CFG['port'])
|
||||||
self.cbo_dlpc.pack(side='left', padx=(4, 2))
|
self.cbo_dlpc.pack(side='left', padx=(4, 2))
|
||||||
ttk.Button(r1, text='刷新', width=6,
|
ttk.Button(r1, text='刷新', width=6,
|
||||||
command=self._dlpc_refresh).pack(side='left')
|
command=self._dlpc_refresh).pack(side='left')
|
||||||
@@ -1341,7 +1343,7 @@ def run_gui(auto_close=0):
|
|||||||
if connected != self._dlpc_connected:
|
if connected != self._dlpc_connected:
|
||||||
self._dlpc_connected = connected
|
self._dlpc_connected = connected
|
||||||
self.btn_dlpc.config(text='断开' if connected else '连接')
|
self.btn_dlpc.config(text='断开' if connected else '连接')
|
||||||
self.after(80, self._poll_dlpc)
|
self._dlpc_after_id = self.after(80, self._poll_dlpc)
|
||||||
|
|
||||||
def _dlpc_log(self, line):
|
def _dlpc_log(self, line):
|
||||||
self.txt_dlpc.configure(state='normal')
|
self.txt_dlpc.configure(state='normal')
|
||||||
@@ -1352,18 +1354,33 @@ def run_gui(auto_close=0):
|
|||||||
self.txt_dlpc.configure(state='disabled')
|
self.txt_dlpc.configure(state='disabled')
|
||||||
|
|
||||||
def _dlpc_refresh(self):
|
def _dlpc_refresh(self):
|
||||||
ports = dlpc_rs485.DLPC485.list_ports()
|
self._dlpc_ports = dlpc_rs485.DLPC485.list_ports_info()
|
||||||
self.cbo_dlpc['values'] = ports
|
self.cbo_dlpc['values'] = [lb for _d, lb in self._dlpc_ports]
|
||||||
if SERIAL_CFG['port'] in ports:
|
devices = [d for d, _l in self._dlpc_ports]
|
||||||
self.cbo_dlpc.set(SERIAL_CFG['port'])
|
if SERIAL_CFG['port'] in devices:
|
||||||
elif ports:
|
self._dlpc_pick(SERIAL_CFG['port'])
|
||||||
self.cbo_dlpc.current(0)
|
elif devices:
|
||||||
|
self._dlpc_pick(devices[0])
|
||||||
|
|
||||||
|
def _dlpc_pick(self, device):
|
||||||
|
"""按 COM 口名选中下拉项(下拉显示的是带描述的标签)"""
|
||||||
|
for i, (d, _label) in enumerate(self._dlpc_ports):
|
||||||
|
if d == device:
|
||||||
|
self.cbo_dlpc.current(i)
|
||||||
|
return
|
||||||
|
|
||||||
|
def _dlpc_selected_port(self):
|
||||||
|
"""当前下拉选中的 COM 口名"""
|
||||||
|
i = self.cbo_dlpc.current()
|
||||||
|
if 0 <= i < len(self._dlpc_ports):
|
||||||
|
return self._dlpc_ports[i][0]
|
||||||
|
return self.cbo_dlpc.get().strip()
|
||||||
|
|
||||||
def _dlpc_toggle(self):
|
def _dlpc_toggle(self):
|
||||||
if self.dlpc.is_connected():
|
if self.dlpc.is_connected():
|
||||||
self.dlpc.close()
|
self.dlpc.close()
|
||||||
return
|
return
|
||||||
port = self.cbo_dlpc.get().strip()
|
port = self._dlpc_selected_port()
|
||||||
if not port:
|
if not port:
|
||||||
self.lbl_dlpc_st.config(text='未选择串口', foreground='#B05000')
|
self.lbl_dlpc_st.config(text='未选择串口', foreground='#B05000')
|
||||||
return
|
return
|
||||||
@@ -1405,6 +1422,10 @@ def run_gui(auto_close=0):
|
|||||||
self._dlpc_send(f'M731S{v}')
|
self._dlpc_send(f'M731S{v}')
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
|
try: # 取消待触发的轮询回调, 避免销毁后 Tcl 报错
|
||||||
|
self.after_cancel(self._dlpc_after_id)
|
||||||
|
except (KeyError, AttributeError, Exception):
|
||||||
|
pass
|
||||||
try: # 两种退出路径(关窗/--seconds 自动关)都收掉串口线程
|
try: # 两种退出路径(关窗/--seconds 自动关)都收掉串口线程
|
||||||
self.dlpc.close()
|
self.dlpc.close()
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|||||||
Reference in New Issue
Block a user