diff --git a/gateway_config.py b/gateway_config.py index 9a30be1..0ab30a0 100644 --- a/gateway_config.py +++ b/gateway_config.py @@ -1,6 +1,6 @@ """ 网关配置工具 -串口配置:波特率500000,数据位8,停止位1,校验位None +串口配置:波特率500000(可修改),数据位8,停止位1,校验位None """ import sys @@ -83,19 +83,39 @@ class SerialThread(QThread): super().__init__() self.ser = ser self.running = True - self._buffer = "" + self._buffer = b"" def run(self): while self.running: try: if self.ser and self.ser.is_open and self.ser.in_waiting > 0: data = self.ser.read(self.ser.in_waiting) - try: - text = data.decode('utf-8', errors='replace') - except Exception: - text = data.decode('gbk', errors='replace') - self.data_received.emit(text) self.data_received_raw.emit(data) + buf = self._buffer + data + text = None + # 优先 UTF-8;若结尾是不完整字符序列则保留尾部待下次拼接,避免乱码缺字 + for enc in ('utf-8', 'gbk'): + try: + text = buf.decode(enc) + self._buffer = b"" + break + except UnicodeDecodeError: + keep = b"" + for tail in range(1, min(4, len(buf)) + 1): + try: + text = buf[:-tail].decode(enc) + keep = buf[-tail:] + break + except UnicodeDecodeError: + continue + if text is not None: + self._buffer = keep + break + if text is None: + text = data.decode('utf-8', errors='replace') + self._buffer = b"" + if text: + self.data_received.emit(text) else: time.sleep(0.01) except Exception: @@ -113,6 +133,11 @@ class GatewayConfigTool(QMainWindow): self.serial_thread = None self._recv_buffer = "" self._para_mode = False + # para 大数据量返回时,用定时器延迟解析,避免每段数据全量解析阻塞UI导致串口缓冲溢出丢包 + self._para_timer = QTimer(self) + self._para_timer.setSingleShot(True) + self._para_timer.setInterval(250) + self._para_timer.timeout.connect(self._on_para_parse) self.upgrade_dialog = None self.dev_dialog = None self.init_ui() @@ -145,7 +170,13 @@ class GatewayConfigTool(QMainWindow): conn_layout.addWidget(self.btn_refresh) conn_layout.addSpacing(20) - conn_layout.addWidget(QLabel("波特率: 500000")) + conn_layout.addWidget(QLabel("波特率:")) + self.combo_baud = QComboBox() + self.combo_baud.setEditable(True) + self.combo_baud.addItems(["115200", "230400", "460800", "500000", "921600", "1000000"]) + self.combo_baud.setCurrentText("500000") + self.combo_baud.setFixedWidth(90) + conn_layout.addWidget(self.combo_baud) conn_layout.addWidget(QLabel("数据位: 8")) conn_layout.addWidget(QLabel("停止位: 1")) conn_layout.addWidget(QLabel("校验: None")) @@ -174,12 +205,12 @@ class GatewayConfigTool(QMainWindow): self.combo_lora_fc = QComboBox() self.combo_lora_fc.setFixedWidth(200) lora_freqs = [] - fc_hz = 410100000 - ch_idx = 1 - while fc_hz < 493000000: + fc_hz = 470100000 + ch_idx = 0 + while fc_hz <= 492600000: mhz = fc_hz / 1000000 lora_freqs.append((str(fc_hz), f"信道{ch_idx}: {mhz:.1f}MHz")) - fc_hz += 200000 + fc_hz += 500000 ch_idx += 1 for freq, desc in lora_freqs: self.combo_lora_fc.addItem(desc, freq) @@ -356,10 +387,15 @@ class GatewayConfigTool(QMainWindow): if not port_name: QMessageBox.warning(self, "错误", "请选择有效的串口!") return + try: + baud = int(self.combo_baud.currentText().strip()) + except ValueError: + QMessageBox.warning(self, "错误", "波特率无效,请输入数字!") + return try: self.serial_port = serial.Serial( port=port_name, - baudrate=500000, + baudrate=baud, bytesize=serial.EIGHTBITS, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE, @@ -372,7 +408,7 @@ class GatewayConfigTool(QMainWindow): self.btn_connect.setText("关闭串口") self.btn_connect.setStyleSheet("background-color:#f44336; color:white; font-weight:bold;") - self.log_send(f"已连接 {port_name} @ 500000") + self.log_send(f"已连接 {port_name} @ {baud}") self.statusBar().showMessage(f"已连接: {port_name}") except Exception as e: QMessageBox.critical(self, "串口错误", f"无法打开串口:\n{e}") @@ -395,6 +431,14 @@ class GatewayConfigTool(QMainWindow): self._recv_buffer = "" self._para_mode = True self.send_command("para") + self._para_timer.start() + + def _on_para_parse(self): + if not self._para_mode: + return + self._try_parse_para() + if not self._para_mode: + self._para_timer.stop() def on_lora_send(self): value = self.combo_lora_fc.currentData() @@ -467,7 +511,7 @@ class GatewayConfigTool(QMainWindow): if self.dev_dialog is not None and self.dev_dialog.isVisible(): self.dev_dialog.feed_text(text) if self._para_mode: - self._try_parse_para() + self._para_timer.start() def _try_parse_para(self): buf = self._recv_buffer.replace('\r', '') @@ -544,11 +588,15 @@ class GatewayConfigTool(QMainWindow): self._recv_buffer = "" self.statusBar().showMessage("配置读取完成") + @staticmethod + def _escape_html(s): + return s.replace('&', '&').replace('<', '<').replace('>', '>') + def log_send(self, text): - self.text_log.append(f'{text}') + self.text_log.append(f'{self._escape_html(text)}') def log_recv(self, text): - clean = text.replace('\r', '').replace('\n', '
') + clean = self._escape_html(text).replace('\r', '').replace('\n', '
') self.text_log.append(f'<<< {clean}') def closeEvent(self, event): @@ -673,7 +721,7 @@ class UpgradeDialog(QDialog): def _log(self, msg, tag="info"): color = {"ok": "#2E7D32", "err": "#D32F2F", "info": "#1565C0"}.get(tag, "#000000") - self.log_text.append(f'{msg}') + self.log_text.append(f'{GatewayConfigTool._escape_html(str(msg))}') def _log_debug(self, data): if not data: diff --git a/generate_manual.py b/generate_manual.py index d360bf5..dbc2049 100644 --- a/generate_manual.py +++ b/generate_manual.py @@ -148,7 +148,7 @@ story.append(Spacer(1, 10 * mm)) cover_info = [ ["软件名称", "网关配置工具"], ["适用设备", "物联网网关(HC32F460 + 4G/Cat1 + Ethernet)"], - ["通信接口", "串口(UART),波特率 500000"], + ["通信接口", "串口(UART),波特率默认500000可修改"], ["运行环境", "Windows 7/10/11"], ["文档版本", "V1.0"], ] @@ -208,9 +208,9 @@ story.append(Spacer(1, 2 * mm)) features = [ ["功能模块", "说明"], - ["串口通信", "通过USB转串口连接网关设备,波特率500000,实时收发指令"], + ["串口通信", "通过USB转串口连接网关设备,波特率默认500000可自行修改,实时收发指令"], ["配置读取", "一键读取网关全部参数,包括软件/硬件版本、MAC地址、电池状态、注册状态等"], - ["Lora信道配置", "选择并设置Lora通信信道频率(410.1MHz ~ 492.9MHz)"], + ["Lora信道配置", "选择并设置Lora通信信道频率(470.1MHz ~ 492.6MHz)"], ["自定义指令", "支持手动输入任意指令与网关交互"], ["通讯日志", "实时显示发送和接收的通讯数据,便于调试"], ] @@ -272,9 +272,9 @@ story.append(hr()) # 3.1 story.append(heading2("3.1 串口连接")) -story.append(body("串口连接是使用本工具的前提。软件固定使用以下串口参数:")) +story.append(body("串口连接是使用本工具的前提。串口参数(波特率可修改,默认500000):")) serial_rows = [ - ["波特率", "500000"], + ["波特率", "500000(可在下拉框中选择或手动输入)"], ["数据位", "8"], ["停止位", "1"], ["校验位", "None(无校验)"], @@ -304,8 +304,8 @@ story.append(make_table(["显示项", "指令关键字", "说明"], para_rows, c # 3.3 story.append(heading2("3.3 Lora信道配置")) story.append(body( - "Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖410.1MHz至492.9MHz范围," - "频率间隔0.2MHz,共415个信道可供选择。" + "Lora信道配置用于设置网关的Lora无线通信频率。信道列表覆盖470.1MHz至492.6MHz范围," + "频率间隔0.5MHz,共46个信道可供选择。" )) story.append(Spacer(1, 2 * mm)) @@ -319,9 +319,9 @@ for i, s in enumerate(lora_steps, 1): story.append(Spacer(1, 2 * mm)) lora_range = [ - ["起始频率", "410.1 MHz(信道1)"], - ["终止频率", "492.9 MHz(信道415)"], - ["频率间隔", "0.2 MHz(200 kHz)"], + ["起始频率", "470.1 MHz(信道0)"], + ["终止频率", "492.6 MHz(信道45)"], + ["频率间隔", "0.5 MHz(500 kHz)"], ["指令格式", "lora fc [频率值,单位Hz]"], ] story.append(make_table(["项目", "值"], lora_range, col_widths=[35 * mm, 115 * mm])) @@ -401,7 +401,7 @@ story.append(hr()) warnings = [ "串口独占:同一串口在同一时间只能被一个程序打开,请确保使用前关闭其他可能占用串口的软件。", - "波特率固定:本工具固定使用500000波特率,与网关固件匹配,请勿尝试修改。", + "波特率:波特率默认500000,可在下拉框选择或手动输入修改,请与网关固件实际波特率保持一致。", f"数据备份:修改配置前建议先通过{LQ}查看配置{RQ}记录当前参数,以便需要时恢复。", "断电保护:配置修改后请确认网关已保存参数,避免在参数写入过程中断电。", "驱动安装:首次使用需安装USB转串口芯片驱动(如CH340、CP2102等),具体型号取决于使用的转接线。",