diff --git a/.gitignore b/.gitignore
index 31137a9..833d550 100644
--- a/.gitignore
+++ b/.gitignore
@@ -171,6 +171,9 @@ cython_debug/
# Ruff stuff:
.ruff_cache/
+# Qwen Code
+.qwen/
+
# PyPI configuration file
.pypirc
diff --git a/gateway_config.py b/gateway_config.py
new file mode 100644
index 0000000..e8a1281
--- /dev/null
+++ b/gateway_config.py
@@ -0,0 +1,670 @@
+"""
+网关配置工具
+串口配置:波特率500000,数据位8,停止位1,校验位None
+"""
+
+import sys
+import os
+import re
+import time
+import threading
+from datetime import datetime
+
+from PyQt5.QtWidgets import (
+ QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
+ QGroupBox, QLabel, QComboBox, QPushButton, QLineEdit, QTextEdit,
+ QMessageBox, QGridLayout, QSplitter
+)
+from PyQt5.QtCore import Qt, QThread, pyqtSignal, QRegExp
+from PyQt5.QtGui import QFont, QColor, QPalette, QRegExpValidator, QIntValidator, QIcon
+
+import serial
+import serial.tools.list_ports
+
+
+def resource_path(relative_path):
+ if hasattr(sys, '_MEIPASS'):
+ return os.path.join(sys._MEIPASS, relative_path)
+ return os.path.join(os.path.abspath(os.path.dirname(__file__)), relative_path)
+
+
+class SerialThread(QThread):
+ data_received = pyqtSignal(str)
+
+ def __init__(self, ser):
+ super().__init__()
+ self.ser = ser
+ self.running = True
+ self._buffer = ""
+
+ 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)
+ else:
+ time.sleep(0.01)
+ except Exception:
+ break
+
+ def stop(self):
+ self.running = False
+ self.wait()
+
+
+class GatewayConfigTool(QMainWindow):
+ def __init__(self):
+ super().__init__()
+ self.serial_port = None
+ self.serial_thread = None
+ self._recv_buffer = ""
+ self._para_mode = False
+ self._cur_comm = "Cat1" # 当前通讯方式: Cat1 或 Eth
+ self.init_ui()
+ self.refresh_ports()
+
+ def init_ui(self):
+ self.setWindowTitle("网关配置工具")
+ self.setWindowIcon(QIcon(resource_path("物联网网关.png")))
+ self.setMinimumSize(800, 800)
+ self.resize(800, 800)
+
+ central = QWidget()
+ self.setCentralWidget(central)
+ main_layout = QVBoxLayout(central)
+ main_layout.setSpacing(4)
+ main_layout.setContentsMargins(4, 4, 4, 4)
+
+ # === 串口连接区 ===
+ conn_group = QGroupBox("串口连接")
+ conn_layout = QHBoxLayout(conn_group)
+
+ conn_layout.addWidget(QLabel("串口:"))
+ self.combo_port = QComboBox()
+ self.combo_port.setMinimumWidth(120)
+ conn_layout.addWidget(self.combo_port)
+
+ self.btn_refresh = QPushButton("刷新")
+ self.btn_refresh.setFixedWidth(60)
+ self.btn_refresh.clicked.connect(self.refresh_ports)
+ conn_layout.addWidget(self.btn_refresh)
+
+ conn_layout.addSpacing(20)
+ conn_layout.addWidget(QLabel("波特率: 500000"))
+ conn_layout.addWidget(QLabel("数据位: 8"))
+ conn_layout.addWidget(QLabel("停止位: 1"))
+ conn_layout.addWidget(QLabel("校验: None"))
+
+ conn_layout.addStretch()
+
+ self.btn_connect = QPushButton("打开串口")
+ self.btn_connect.setFixedWidth(100)
+ self.btn_connect.setStyleSheet("font-weight:bold;")
+ self.btn_connect.clicked.connect(self.toggle_serial)
+ conn_layout.addWidget(self.btn_connect)
+
+ main_layout.addWidget(conn_group)
+
+ # === 配置区 + 日志区(上下布局)===
+ splitter = QSplitter(Qt.Vertical)
+
+ # -- 上方:配置区 --
+ config_widget = QWidget()
+ config_layout = QVBoxLayout(config_widget)
+ config_layout.setContentsMargins(0, 0, 0, 0)
+ config_layout.setSpacing(2)
+
+ # 基本指令 + 通讯方式(一行)
+ top_row = QHBoxLayout()
+ top_row.setSpacing(4)
+
+ self.btn_para = QPushButton("查看配置(para)")
+ self.btn_para.clicked.connect(self.on_para_clicked)
+ top_row.addWidget(self.btn_para)
+
+ self.btn_cat1 = QPushButton("Cat1(comm cat1)")
+ self.btn_cat1.clicked.connect(lambda: self.set_comm_mode("Cat1"))
+ top_row.addWidget(self.btn_cat1)
+
+ self.btn_eth = QPushButton("ETH(comm eth)")
+ self.btn_eth.clicked.connect(lambda: self.set_comm_mode("Eth"))
+ top_row.addWidget(self.btn_eth)
+
+ top_row.addStretch()
+ config_layout.addLayout(top_row)
+
+ # 当前配置显示
+ info_group = QGroupBox("当前网关配置")
+ info_grid = QGridLayout(info_group)
+ info_grid.setSpacing(4)
+
+ def _bold_label(color="#1565C0"):
+ lb = QLabel("-")
+ lb.setStyleSheet(f"font-weight:bold; color:{color};")
+ return lb
+
+ row = 0
+ info_grid.addWidget(QLabel("软件版本:"), row, 0)
+ self.label_sw_ver = _bold_label()
+ info_grid.addWidget(self.label_sw_ver, row, 1)
+ info_grid.addWidget(QLabel("硬件版本:"), row, 2)
+ self.label_hw_ver = _bold_label()
+ info_grid.addWidget(self.label_hw_ver, row, 3)
+
+ row = 1
+ info_grid.addWidget(QLabel("电池:"), row, 0)
+ self.label_battery = _bold_label()
+ info_grid.addWidget(self.label_battery, row, 1)
+ info_grid.addWidget(QLabel("注册状态:"), row, 2)
+ self.label_reg_status = _bold_label()
+ info_grid.addWidget(self.label_reg_status, row, 3)
+
+ row = 2
+ info_grid.addWidget(QLabel("网关MAC:"), row, 0)
+ self.label_gw_mac = _bold_label()
+ info_grid.addWidget(self.label_gw_mac, row, 1, 1, 3)
+
+ row = 3
+ info_grid.addWidget(QLabel("通讯方式:"), row, 0)
+ self.label_cur_comm = _bold_label()
+ info_grid.addWidget(self.label_cur_comm, row, 1)
+ info_grid.addWidget(QLabel("读取间隔:"), row, 2)
+ self.label_read_interval = _bold_label()
+ info_grid.addWidget(self.label_read_interval, row, 3)
+
+ row = 4
+ info_grid.addWidget(QLabel("Cat1 IMEI:"), row, 0)
+ self.label_imei = _bold_label()
+ info_grid.addWidget(self.label_imei, row, 1, 1, 3)
+
+ row = 5
+ info_grid.addWidget(QLabel("Cat1 SIM:"), row, 0)
+ self.label_sim = _bold_label()
+ info_grid.addWidget(self.label_sim, row, 1, 1, 3)
+
+ row = 6
+ info_grid.addWidget(QLabel("Lora频率:"), row, 0)
+ self.label_cur_lora_fc = _bold_label()
+ info_grid.addWidget(self.label_cur_lora_fc, row, 1)
+
+ # Eth参数(紧凑两列)
+ eth_info_group = QGroupBox("ETH / Cat1网络参数")
+ eth_info_grid = QGridLayout(eth_info_group)
+ eth_info_grid.setSpacing(4)
+
+ row = 0
+ eth_info_grid.addWidget(QLabel("网关IP:"), row, 0)
+ self.label_cur_eth_ip = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_ip, row, 1)
+ eth_info_grid.addWidget(QLabel("网关端口:"), row, 2)
+ self.label_cur_eth_port = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_port, row, 3)
+
+ row = 1
+ eth_info_grid.addWidget(QLabel("子网掩码:"), row, 0)
+ self.label_cur_eth_mask = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_mask, row, 1)
+ eth_info_grid.addWidget(QLabel("服务器网关:"), row, 2)
+ self.label_cur_eth_gw = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_gw, row, 3)
+
+ row = 2
+ eth_info_grid.addWidget(QLabel("服务器IP:"), row, 0)
+ self.label_cur_eth_dip = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_dip, row, 1)
+ eth_info_grid.addWidget(QLabel("服务器端口:"), row, 2)
+ self.label_cur_eth_dport = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_dport, row, 3)
+
+ row = 3
+ eth_info_grid.addWidget(QLabel("IP模式:"), row, 0)
+ self.label_cur_eth_ipmode = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_ipmode, row, 1)
+ eth_info_grid.addWidget(QLabel("工作模式:"), row, 2)
+ self.label_cur_eth_workmode = _bold_label()
+ eth_info_grid.addWidget(self.label_cur_eth_workmode, row, 3)
+
+ # 低功耗配置
+ misc_group = QGroupBox("低功耗配置")
+ misc_grid = QGridLayout(misc_group)
+ misc_grid.setSpacing(2)
+ misc_grid.setContentsMargins(10, 4, 4, 4)
+
+ row = 0
+ misc_grid.addWidget(QLabel("采集间隔:"), row, 0)
+ self.label_lpcfg_ci = _bold_label()
+ misc_grid.addWidget(self.label_lpcfg_ci, row, 1)
+ misc_grid.addWidget(QLabel("上报间隔:"), row, 2)
+ self.label_lpcfg_ri = _bold_label()
+ misc_grid.addWidget(self.label_lpcfg_ri, row, 3)
+
+ row = 1
+ misc_grid.addWidget(QLabel("紧急上报间隔:"), row, 0)
+ self.label_lpcfg_ei = _bold_label()
+ misc_grid.addWidget(self.label_lpcfg_ei, row, 1)
+ misc_grid.addWidget(QLabel("紧急上报时长:"), row, 2)
+ self.label_lpcfg_et = _bold_label()
+ misc_grid.addWidget(self.label_lpcfg_et, row, 3)
+
+ config_layout.addWidget(info_group)
+ config_layout.addWidget(eth_info_group)
+ config_layout.addWidget(misc_group)
+
+ # Eth参数(紧凑两列)
+ eth_group = QGroupBox("ETH参数(需先切换到eth模式)")
+ eth_grid = QGridLayout(eth_group)
+ eth_grid.setSpacing(4)
+
+ ip_validator = QRegExpValidator(QRegExp(r"^(\d{1,3}\.){3}\d{1,3}$"))
+
+ eth_grid.addWidget(QLabel("网关IP:"), 0, 0)
+ self.edit_eth_ip = QLineEdit("192.168.1.101")
+ self.edit_eth_ip.setValidator(ip_validator)
+ eth_grid.addWidget(self.edit_eth_ip, 0, 1)
+ self.btn_eth_ip = QPushButton("发送")
+ self.btn_eth_ip.setFixedWidth(50)
+ self.btn_eth_ip.clicked.connect(lambda: self.send_param("eth ip", self.edit_eth_ip))
+ eth_grid.addWidget(self.btn_eth_ip, 0, 2)
+
+ eth_grid.addWidget(QLabel("网关端口:"), 0, 3)
+ self.edit_eth_port = QLineEdit("10000")
+ self.edit_eth_port.setValidator(QIntValidator(1, 65535))
+ eth_grid.addWidget(self.edit_eth_port, 0, 4)
+ self.btn_eth_port = QPushButton("发送")
+ self.btn_eth_port.setFixedWidth(50)
+ self.btn_eth_port.clicked.connect(lambda: self.send_param("eth port", self.edit_eth_port))
+ eth_grid.addWidget(self.btn_eth_port, 0, 5)
+
+ eth_grid.addWidget(QLabel("服务器网关:"), 1, 0)
+ self.edit_eth_gw = QLineEdit("192.168.1.1")
+ self.edit_eth_gw.setValidator(ip_validator)
+ eth_grid.addWidget(self.edit_eth_gw, 1, 1)
+ self.btn_eth_gw = QPushButton("发送")
+ self.btn_eth_gw.setFixedWidth(50)
+ self.btn_eth_gw.clicked.connect(lambda: self.send_param("eth gw", self.edit_eth_gw))
+ eth_grid.addWidget(self.btn_eth_gw, 1, 2)
+
+ eth_grid.addWidget(QLabel("服务器IP:"), 1, 3)
+ self.edit_eth_dip = QLineEdit("192.168.1.100")
+ self.edit_eth_dip.setValidator(ip_validator)
+ eth_grid.addWidget(self.edit_eth_dip, 1, 4)
+ self.btn_eth_dip = QPushButton("发送")
+ self.btn_eth_dip.setFixedWidth(50)
+ self.btn_eth_dip.clicked.connect(lambda: self.send_param("eth dip", self.edit_eth_dip))
+ eth_grid.addWidget(self.btn_eth_dip, 1, 5)
+
+ eth_grid.addWidget(QLabel("服务器端口:"), 2, 0)
+ self.edit_eth_dport = QLineEdit("8080")
+ self.edit_eth_dport.setValidator(QIntValidator(1, 65535))
+ eth_grid.addWidget(self.edit_eth_dport, 2, 1)
+ self.btn_eth_dport = QPushButton("发送")
+ self.btn_eth_dport.setFixedWidth(50)
+ self.btn_eth_dport.clicked.connect(lambda: self.send_param("eth dport", self.edit_eth_dport))
+ eth_grid.addWidget(self.btn_eth_dport, 2, 2)
+
+ self.btn_eth_apply = QPushButton("应用ETH配置 (eth apply)")
+ self.btn_eth_apply.setStyleSheet("background-color:#4CAF50; color:white; font-weight:bold; padding:4px;")
+ self.btn_eth_apply.clicked.connect(self.send_eth_apply)
+ eth_grid.addWidget(self.btn_eth_apply, 2, 3, 1, 3)
+
+ config_layout.addWidget(eth_group)
+
+ # Lora参数 + 自定义指令(一行)
+ bottom_row = QHBoxLayout()
+ bottom_row.setSpacing(4)
+
+ bottom_row.addWidget(QLabel("Lora频率:"))
+ self.combo_lora_fc = QComboBox()
+ self.combo_lora_fc.setFixedWidth(120)
+ lora_freqs = [
+ ("433100000", "信道1: 433.1M"),
+ ("433300000", "信道2: 433.3M"),
+ ("433500000", "信道3: 433.5M"),
+ ("433700000", "信道4: 433.7M"),
+ ("433900000", "信道5: 433.9M"),
+ ("434100000", "信道6: 434.1M"),
+ ("434300000", "信道7: 434.3M"),
+ ("434500000", "信道8: 434.5M"),
+ ("434700000", "信道9: 434.7M"),
+ ("434900000", "信道10: 434.9M"),
+ ("435100000", "信道11: 435.1M"),
+ ("435300000", "信道12: 435.3M"),
+ ("435500000", "信道13: 435.5M"),
+ ("435700000", "信道14: 435.7M"),
+ ("435900000", "信道15: 435.9M"),
+ ("436100000", "信道16: 436.1M"),
+ ("436300000", "信道17: 436.3M"),
+ ("436500000", "信道18: 436.5M"),
+ ("436700000", "信道19: 436.7M"),
+ ("436900000", "信道20: 436.9M"),
+ ("437100000", "信道21: 437.1M"),
+ ("437300000", "信道22: 437.3M"),
+ ("437500000", "信道23: 437.5M"),
+ ("437700000", "信道24: 437.7M"),
+ ("437900000", "信道25: 437.9M"),
+ ("438100000", "信道26: 438.1M"),
+ ("438300000", "信道27: 438.3M"),
+ ("438500000", "信道28: 438.5M"),
+ ("438700000", "信道29: 438.7M"),
+ ("438900000", "信道30: 438.9M"),
+ ("439100000", "信道31: 439.1M"),
+ ("439300000", "信道32: 439.3M"),
+ ]
+ for freq, desc in lora_freqs:
+ self.combo_lora_fc.addItem(desc, freq)
+ bottom_row.addWidget(self.combo_lora_fc)
+ self.btn_lora_fc = QPushButton("发送")
+ self.btn_lora_fc.setFixedWidth(50)
+ self.btn_lora_fc.clicked.connect(self.on_lora_send)
+ bottom_row.addWidget(self.btn_lora_fc)
+
+ bottom_row.addSpacing(10)
+ bottom_row.addWidget(QLabel("自定义:"))
+ self.edit_custom = QLineEdit()
+ self.edit_custom.setPlaceholderText("输入指令...")
+ self.edit_custom.returnPressed.connect(self.send_custom_command)
+ bottom_row.addWidget(self.edit_custom)
+ self.btn_custom_send = QPushButton("发送")
+ self.btn_custom_send.clicked.connect(self.send_custom_command)
+ bottom_row.addWidget(self.btn_custom_send)
+
+ config_layout.addLayout(bottom_row)
+
+ # -- 下方:日志区 --
+ log_widget = QWidget()
+ log_layout = QVBoxLayout(log_widget)
+ log_layout.setContentsMargins(0, 0, 0, 0)
+
+ log_group = QGroupBox("通讯日志")
+ log_inner = QVBoxLayout(log_group)
+
+ self.text_log = QTextEdit()
+ self.text_log.setReadOnly(True)
+ self.text_log.setFont(QFont("Consolas", 9))
+ self.text_log.setStyleSheet(
+ "QTextEdit { background-color: #1e1e1e; color: #d4d4d4; }"
+ )
+ log_inner.addWidget(self.text_log)
+
+ log_btn_layout = QHBoxLayout()
+ self.btn_clear_log = QPushButton("清空日志")
+ self.btn_clear_log.clicked.connect(self.text_log.clear)
+ log_btn_layout.addWidget(self.btn_clear_log)
+ log_btn_layout.addStretch()
+ log_inner.addLayout(log_btn_layout)
+
+ log_layout.addWidget(log_group)
+
+ splitter.addWidget(config_widget)
+ splitter.addWidget(log_widget)
+ splitter.setSizes([280, 220])
+
+ main_layout.addWidget(splitter)
+ self.statusBar().showMessage("就绪 - 请选择串口并连接")
+
+ def refresh_ports(self):
+ self.combo_port.clear()
+ ports = serial.tools.list_ports.comports()
+ if not ports:
+ self.combo_port.addItem("无可用串口")
+ return
+ for p in ports:
+ self.combo_port.addItem(f"{p.device} - {p.description}")
+
+ def get_port_name(self):
+ text = self.combo_port.currentText()
+ if not text or text.startswith("无"):
+ return None
+ return text.split(" - ")[0]
+
+ def toggle_serial(self):
+ if self.serial_port and self.serial_port.is_open:
+ self.close_serial()
+ else:
+ self.open_serial()
+
+ def open_serial(self):
+ port_name = self.get_port_name()
+ if not port_name:
+ QMessageBox.warning(self, "错误", "请选择有效的串口!")
+ return
+ try:
+ self.serial_port = serial.Serial(
+ port=port_name,
+ baudrate=500000,
+ bytesize=serial.EIGHTBITS,
+ stopbits=serial.STOPBITS_ONE,
+ parity=serial.PARITY_NONE,
+ timeout=0.1
+ )
+ self.serial_thread = SerialThread(self.serial_port)
+ self.serial_thread.data_received.connect(self.on_data_received)
+ self.serial_thread.start()
+
+ self.btn_connect.setText("关闭串口")
+ self.btn_connect.setStyleSheet("background-color:#f44336; color:white; font-weight:bold;")
+ self.log_send(f"已连接 {port_name} @ 500000")
+ self.statusBar().showMessage(f"已连接: {port_name}")
+ except Exception as e:
+ QMessageBox.critical(self, "串口错误", f"无法打开串口:\n{e}")
+
+ def close_serial(self):
+ if self.serial_thread:
+ self.serial_thread.stop()
+ self.serial_thread = None
+ if self.serial_port and self.serial_port.is_open:
+ port_name = self.serial_port.port
+ self.serial_port.close()
+ self.log_send(f"已断开 {port_name}")
+ self.serial_port = None
+
+ self.btn_connect.setText("打开串口")
+ self.btn_connect.setStyleSheet("font-weight:bold;")
+ self.statusBar().showMessage("已断开")
+
+ def on_para_clicked(self):
+ self._recv_buffer = ""
+ self._para_mode = True
+ self.send_command("para")
+
+ def on_lora_send(self):
+ value = self.combo_lora_fc.currentData()
+ self.send_command(f"lora fc {value}")
+
+ def send_command(self, cmd):
+ if not self.serial_port or not self.serial_port.is_open:
+ QMessageBox.warning(self, "错误", "请先打开串口!")
+ return
+ try:
+ data = (cmd + "\r\n").encode('utf-8')
+ self.serial_port.write(data)
+ self.log_send(f">>> {cmd}")
+ except Exception as e:
+ self.log_send(f"[发送错误] {e}")
+
+ def set_comm_mode(self, mode):
+ self._cur_comm = mode
+ self.send_command(f"comm {mode.lower()}")
+
+ def send_param(self, prefix, edit_widget):
+ if prefix.startswith("eth") and self._cur_comm != "Eth":
+ QMessageBox.warning(self, "提示", "当前为Cat1通讯模式,请先切换到ETH模式!")
+ return
+ value = edit_widget.text().strip()
+ if not value:
+ QMessageBox.warning(self, "错误", "参数值不能为空!")
+ return
+ cmd = f"{prefix} {value}"
+ self.send_command(cmd)
+
+ def send_eth_apply(self):
+ if self._cur_comm != "Eth":
+ QMessageBox.warning(self, "提示", "当前为Cat1通讯模式,请先切换到ETH模式!")
+ return
+ self.send_command("eth apply")
+
+ def send_custom_command(self):
+ cmd = self.edit_custom.text().strip()
+ if not cmd:
+ return
+ self.send_command(cmd)
+ self.edit_custom.clear()
+
+ def on_data_received(self, text):
+ self._recv_buffer += text
+ self.log_recv(text)
+ if self._para_mode:
+ self._try_parse_para()
+
+ def _try_parse_para(self):
+ buf = self._recv_buffer.replace('\r', '')
+ lines_all = [l.strip() for l in buf.split('\n')]
+
+ # 查找 *** 包围的块
+ star_indices = [i for i, l in enumerate(lines_all) if l and all(c == '*' for c in l)]
+ if len(star_indices) < 2:
+ return
+
+ start_idx = star_indices[0]
+ end_idx = star_indices[1]
+ lines = lines_all[start_idx + 1:end_idx]
+
+ def _update(label, value, ok_color="#2E7D32"):
+ label.setText(value)
+ label.setStyleSheet(f"font-weight:bold; color:{ok_color};")
+
+ section = ""
+ for line in lines:
+ if line.startswith('==='):
+ section = line.strip('= ').strip()
+ continue
+
+ # SoftWare V:1.2, HardWare V:1.2
+ m = re.match(r'SoftWare\s+V:([\d.]+),\s*HardWare\s+V:([\d.]+)', line)
+ if m:
+ _update(self.label_sw_ver, m.group(1))
+ _update(self.label_hw_ver, m.group(2))
+ continue
+
+ # Key: Value 行
+ m = re.match(r'([\w\s/()]+?):\s*(.*)', line)
+ if not m:
+ continue
+ key = m.group(1).strip()
+ val = m.group(2).strip()
+
+ if key == 'GateWay MAC':
+ _update(self.label_gw_mac, val)
+ elif key == 'Battery':
+ _update(self.label_battery, val)
+ elif key == 'CommInf':
+ _update(self.label_cur_comm, val)
+ self._cur_comm = val
+ elif key == 'Cat1 IMEI':
+ _update(self.label_imei, val)
+ elif key == 'Cat1 SIM':
+ _update(self.label_sim, val)
+ elif key == 'CommUnitReadInterval':
+ _update(self.label_read_interval, f"{val}s")
+ elif key == 'Register Status':
+ status_map = {"0": "未注册", "1": "已注册"}
+ _update(self.label_reg_status, status_map.get(val, val))
+ # --- LTENet (Cat1) section ---
+ elif key == 'DestAddr' and 'Cat1' in section:
+ pass # Cat1远端地址暂不单独显示
+ elif key == 'DestPort' and 'Cat1' in section:
+ pass
+ elif key == 'LocalAddr':
+ pass
+ elif key == 'LocalPort' and 'Cat1' in section:
+ pass
+ # --- EthNet section ---
+ elif key == 'IpMode':
+ _update(self.label_cur_eth_ipmode, val)
+ elif key == 'LocalIP':
+ _update(self.label_cur_eth_ip, val)
+ self.edit_eth_ip.setText(val)
+ elif key == 'LocalPort' and 'Eth' in section:
+ _update(self.label_cur_eth_port, val)
+ self.edit_eth_port.setText(val)
+ elif key == 'WorkMode':
+ _update(self.label_cur_eth_workmode, val)
+ elif key == 'SubnetMask':
+ _update(self.label_cur_eth_mask, val)
+ elif key == 'Gateway':
+ _update(self.label_cur_eth_gw, val)
+ self.edit_eth_gw.setText(val)
+ elif key == 'DestIP':
+ _update(self.label_cur_eth_dip, val)
+ self.edit_eth_dip.setText(val)
+ elif key == 'DestPort' and 'Eth' in section:
+ _update(self.label_cur_eth_dport, val)
+ self.edit_eth_dport.setText(val)
+ # --- Lora ---
+ elif key == 'Lora':
+ fc_m = re.search(r'fc\s+(\d+)', val)
+ if fc_m:
+ fc_val = fc_m.group(1)
+ _update(self.label_cur_lora_fc, fc_val)
+ idx = self.combo_lora_fc.findData(fc_val)
+ if idx >= 0:
+ self.combo_lora_fc.setCurrentIndex(idx)
+ elif key == 'LpCfg':
+ def _conv_unit(u):
+ if u == 'sec': return '秒'
+ if u == 'min': return '分钟'
+ return u
+ ci_m = re.search(r'CI:(\d+)(\w+)', val)
+ ri_m = re.search(r'RI:(\d+)(\w+)', val)
+ ei_m = re.search(r'EI:(\d+)(\w+)', val)
+ et_m = re.search(r'ET:(\d+)(\w+)', val)
+ if ci_m:
+ _update(self.label_lpcfg_ci, f"{ci_m.group(1)}{_conv_unit(ci_m.group(2))}")
+ if ri_m:
+ _update(self.label_lpcfg_ri, f"{ri_m.group(1)}{_conv_unit(ri_m.group(2))}")
+ if ei_m:
+ _update(self.label_lpcfg_ei, f"{ei_m.group(1)}{_conv_unit(ei_m.group(2))}")
+ if et_m:
+ _update(self.label_lpcfg_et, f"{et_m.group(1)}{_conv_unit(et_m.group(2))}")
+
+ self._para_mode = False
+ self._recv_buffer = ""
+ self.statusBar().showMessage("配置读取完成")
+
+ def log_send(self, text):
+ timestamp = datetime.now().strftime("%H:%M:%S")
+ self.text_log.append(f'[{timestamp}] {text}')
+
+ def log_recv(self, text):
+ timestamp = datetime.now().strftime("%H:%M:%S")
+ clean = text.replace('\r', '').replace('\n', '
')
+ self.text_log.append(f'[{timestamp}] <<< {clean}')
+
+ def closeEvent(self, event):
+ self.close_serial()
+ event.accept()
+
+
+def main():
+ app = QApplication(sys.argv)
+ app.setStyle("Fusion")
+
+ palette = QPalette()
+ palette.setColor(QPalette.Window, QColor(240, 240, 240))
+ palette.setColor(QPalette.WindowText, QColor(30, 30, 30))
+ palette.setColor(QPalette.Base, QColor(255, 255, 255))
+ palette.setColor(QPalette.Button, QColor(225, 225, 225))
+ app.setPalette(palette)
+
+ window = GatewayConfigTool()
+ window.show()
+ sys.exit(app.exec_())
+
+
+if __name__ == "__main__":
+ main()
diff --git a/logo.ico b/logo.ico
new file mode 100644
index 0000000..21fbc11
Binary files /dev/null and b/logo.ico differ
diff --git a/make_ico.py b/make_ico.py
new file mode 100644
index 0000000..26eb859
--- /dev/null
+++ b/make_ico.py
@@ -0,0 +1,34 @@
+from PIL import Image
+import struct
+import io
+import os
+
+png_path = r'D:\keilProject\HC32F460\Git\GateWay_ConfigureAPP\物联网网关.png'
+ico_path = r'D:\keilProject\HC32F460\Git\GateWay_ConfigureAPP\logo.ico'
+
+img = Image.open(png_path).convert('RGBA')
+sizes = [16, 32, 48, 64, 128, 256]
+
+ico_data = []
+for size in sizes:
+ resized = img.resize((size, size), Image.Resampling.LANCZOS)
+ png_bytes = io.BytesIO()
+ resized.save(png_bytes, format='PNG')
+ ico_data.append((size, png_bytes.getvalue()))
+
+header = struct.pack('= 256 else size
+ h = 0 if size >= 256 else size
+ entries += struct.pack('