帮我用qtc++做一个跟这个效果差不多的import sys
import serial
import serial.tools.list_ports
from PyQt5.QtWidgets import (
QApplication, QWidget, QLabel, QLineEdit, QPushButton, QTextEdit,
QVBoxLayout, QHBoxLayout, QComboBox, QCheckBox, QGroupBox,
QFormLayout, QGridLayout, QSpacerItem, QSizePolicy
)
from PyQt5.QtCore import QTimer, QThread, pyqtSignal, Qt
from PyQt5.QtGui import QFont
def crc16_modbus(data: bytes) -> int:
crc = 0xFFFF
for byte in data:
crc ^= byte
for _ in range(8):
if crc & 0x0001:
crc >>= 1
crc ^= 0xA001 # 异或反向多项式
else:
crc >>= 1
return crc
class SerialReader(QThread):
data_received = pyqtSignal(bytes)
def __init__(self, port, baudrate):
super().__init__()
self.port = port
self.baudrate = baudrate
self.serial_port = None
self.running = True
def run(self):
try:
self.serial_port = serial.Serial(self.port, self.baudrate, timeout=1)
while self.running:
data = self.serial_port.read(256)
if data:
self.data_received.emit(data)
except Exception as e:
print("串口读取错误:", e)
def stop(self):
self.running = False
if self.serial_port and self.serial_port.is_open:
self.serial_port.close()
self.wait()
class SerialDataDisplay(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("串口数据显示界面")
self.resize(1500, 1000)
self.serial_reader = None
self.buffer = bytearray()
self.max_lines = 100
self.band_data_cache = {
"band1": {},
"band2": {},
"band3": {},
"band4": {}
}
self.loop_timer = QTimer()
self.loop_timer.timeout.connect(self.do_send_data)
# 新增:保存上一次发送的预设帧
self.last_preset_data = None
self.last_combined_frame = None # 保存最后一次发送的组合帧
# 新增:缓存原始数据用于显示
self.raw_data_lines = []
self.init_ui()
self.refresh_ports()
# 设置定时器刷新界面
self.update_timer = QTimer()
self.update_timer.timeout.connect(self.flush_display)
self.update_timer.start(100)
def init_ui(self):
main_layout = QVBoxLayout()
# 版本查询按钮组
version_group = QGroupBox("版本查询")
version_group.setFont(QFont("Arial", 12))
version_layout = QHBoxLayout()
self.version_button = QPushButton("查询版本")
self.version_button.setFont(QFont("Arial", 12))
self.version_button.clicked.connect(self.send_version_query)
version_layout.addWidget(self.version_button)
version_group.setLayout(version_layout)
main_layout.addWidget(version_group)
# 串口设置区域
port_config_layout = QHBoxLayout()
self.port_combo = QComboBox()
self.port_combo.setEditable(True)
self.port_combo.setFont(QFont("Arial", 12))
self.port_combo.setFixedWidth(200)
self.baud_combo = QComboBox()
self.baud_combo.addItems(["9600", "19200", "38400", "57600", "115200"])
self.baud_combo.setCurrentText("115200")
self.baud_combo.setFont(QFont("Arial", 12))
self.baud_combo.setFixedWidth(100)
self.refresh_button = QPushButton("刷新串口")
self.refresh_button.setFont(QFont("Arial", 12))
self.connect_button = QPushButton("连接串口")
self.connect_button.setFont(QFont("Arial", 12))
self.status_label = QLabel("未连接")
self.status_label.setFont(QFont("Arial", 12))
self.status_label.setStyleSheet("color: red;")
port_config_layout.addWidget(QLabel("串口号:"))
port_config_layout.addWidget(self.port_combo)
port_config_layout.addWidget(QLabel("波特率:"))
port_config_layout.addWidget(self.baud_combo)
port_config_layout.addWidget(self.refresh_button)
port_config_layout.addWidget(self.connect_button)
port_config_layout.addWidget(self.status_label)
port_config_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
main_layout.addLayout(port_config_layout)
# 频段信息显示区域
band_layout = QGridLayout()
self.band_widgets = {}
for i in range(4):
group = QGroupBox(f"频段 {i + 1}")
group.setFont(QFont("Arial", 12))
form = QFormLayout()
form.setLabelAlignment(Qt.AlignLeft)
enabled = QCheckBox()
enabled.setEnabled(False)
temp = QLineEdit(" ")
temp.setReadOnly(True)
temp.setFont(QFont("Arial", 11))
current = QLineEdit(" ")
current.setReadOnly(True)
current.setFont(QFont("Arial", 11))
voltage = QLineEdit(" ")
voltage.setReadOnly(True)
voltage.setFont(QFont("Arial", 11))
form.addRow("开启状态:", enabled)
form.addRow("温度:", temp)
form.addRow("电流:", current)
form.addRow("电压:", voltage)
self.band_widgets[f"band{i + 1}"] = {
"enabled": enabled,
"temp": temp,
"current": current,
"voltage": voltage
}
group.setLayout(form)
band_layout.addWidget(group, i // 2, i % 2)
main_layout.addLayout(band_layout)
# 发送数据帧输入框
send_layout = QHBoxLayout()
self.send_input = QLineEdit()
self.send_input.setFont(QFont("Arial", 12))
self.send_input.setPlaceholderText("请输入Hex数据,如:5A 5A 5A 5A 01 02 03")
self.send_button = QPushButton("发送")
self.send_button.setFont(QFont("Arial", 12))
self.send_status = QLabel("")
self.send_status.setFont(QFont("Arial", 11))
self.send_status.setStyleSheet("color: blue;")
self.loop_checkbox = QCheckBox("循环发送")
self.loop_checkbox.setFont(QFont("Arial", 12))
self.interval_input = QLineEdit("1000") # 默认间隔1000ms
self.interval_input.setFixedWidth(80)
self.interval_input.setFont(QFont("Arial", 12))
self.interval_input.setToolTip("发送间隔(毫秒)")
send_layout.addWidget(QLabel("发送数据帧 (Hex):"))
send_layout.addWidget(self.send_input)
send_layout.addWidget(self.send_button)
send_layout.addWidget(self.send_status)
send_layout.addWidget(self.loop_checkbox)
send_layout.addWidget(QLabel("间隔(ms):"))
send_layout.addWidget(self.interval_input)
main_layout.addLayout(send_layout)
# 频段组合发送组
preset_group = QGroupBox("频段组合发送")
preset_group.setFont(QFont("Arial", 12))
preset_layout = QGridLayout()
self.band_checkboxes = []
# 每个频段的位掩码
self.band_masks = {
"频段1": 0x01,
"频段2": 0x02,
"频段3": 0x04,
"频段4": 0x08,
}
# 原始帧模板(去掉CRC部分)
self.preset_frame_template = bytes.fromhex(
"5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 02 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
)
row = 0
for i, (name, mask) in enumerate(self.band_masks.items()):
checkbox = QCheckBox(name)
checkbox.setFont(QFont("Arial", 11))
checkbox.stateChanged.connect(self.update_combined_frame)
self.band_checkboxes.append(checkbox)
preset_layout.addWidget(checkbox, row // 2, row % 2)
row += 1
self.send_combined_button = QPushButton("发送组合帧")
self.send_combined_button.setFont(QFont("Arial", 12))
self.send_combined_button.clicked.connect(self.send_combined_frame)
preset_layout.addWidget(self.send_combined_button, row // 2 + 1, 0, 1, 2)
preset_group.setLayout(preset_layout)
main_layout.addWidget(preset_group)
# 自定义帧按钮组
custom_frame_group = QGroupBox("自定义帧")
custom_frame_group.setFont(QFont("Arial", 12))
custom_frame_layout = QHBoxLayout()
self.custom_frame_button = QPushButton("持续时间")
self.custom_frame_button.setFont(QFont("Arial", 12))
self.custom_frame_button.clicked.connect(self.send_custom_frame)
self.duration_input = QLineEdit("0")
self.duration_input.setFixedWidth(80)
self.duration_input.setToolTip("输入0~65535的持续时间")
custom_frame_layout.addWidget(self.custom_frame_button)
custom_frame_layout.addWidget(QLabel("持续时间 (0~65535):"))
custom_frame_layout.addWidget(self.duration_input)
custom_frame_layout.addSpacerItem(QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum))
custom_frame_group.setLayout(custom_frame_layout)
main_layout.addWidget(custom_frame_group)
# 干扰码切换模块
jammer_group = QGroupBox("干扰码切换")
jammer_group.setFont(QFont("Arial", 12))
jammer_layout = QGridLayout()
# 按钮列表
self.jammer_buttons = {
"900M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"900M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"1600M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"1600M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"2400M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"2400M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"5800M-1": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 16 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"5800M-2": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 18 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
}
row = 0
col = 0
for name, hex_data in self.jammer_buttons.items():
btn = QPushButton(name)
btn.setFont(QFont("Arial", 11))
btn.clicked.connect(lambda checked, data=hex_data: self.send_jammer_frame(data))
jammer_layout.addWidget(btn, row, col)
col += 1
if col > 2:
col = 0
row += 1
jammer_group.setLayout(jammer_layout)
main_layout.addWidget(jammer_group)
# 新增:功率控制模块
power_group = QGroupBox("功率控制")
power_group.setFont(QFont("Arial", 12))
power_layout = QHBoxLayout()
# 功率帧模板(A1 12 / A1 13 / A1 15)
self.power_frames = {
"低功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"中功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 13 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",
"高功率": "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 15 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
}
for name, frame_hex in self.power_frames.items():
btn = QPushButton(name)
btn.setFont(QFont("Arial", 11))
btn.clicked.connect(lambda checked, data=frame_hex: self.send_power_frame(data))
power_layout.addWidget(btn)
power_group.setLayout(power_layout)
main_layout.addWidget(power_group)
# 原始数据帧显示框
raw_data_group = QGroupBox("原始数据帧显示")
raw_data_group.setFont(QFont("Arial", 12))
self.raw_data_display = QTextEdit()
self.raw_data_display.setReadOnly(True)
self.raw_data_display.setFont(QFont("Courier", 10))
layout = QVBoxLayout()
layout.addWidget(self.raw_data_display)
raw_data_group.setLayout(layout)
main_layout.addWidget(raw_data_group)
# 信号连接
self.refresh_button.clicked.connect(self.refresh_ports)
self.connect_button.clicked.connect(self.toggle_serial_connection)
self.send_button.clicked.connect(self.handle_send_button_click)
self.send_input.returnPressed.connect(self.handle_send_button_click)
self.setLayout(main_layout)
self.update_timer = QTimer()
self.update_timer.timeout.connect(self.flush_display)
self.update_timer.start(100)
#新增方法:发送功率帧
def send_power_frame(self, hex_data):
try:
data_bytes = bytes.fromhex(hex_data.replace(' ', ''))
except ValueError:
self.send_status.setText("功率帧数据格式错误")
self.send_status.setStyleSheet("color: red;")
return
try:
# 取第12字节之后的数据计算 CRC
crc_data = data_bytes[12:]
crc_value = crc16_modbus(crc_data)
# 转为可变列表插入 CRC
data_list = list(data_bytes)
data_list[8] = (crc_value >> 8) & 0xFF # CRC 高位 → 第8字节
data_list[9] = crc_value & 0xFF # CRC 低位 → 第9字节
final_bytes = bytes(data_list)
# 格式化为带空格的 Hex 字符串
hex_with_crc = ''.join(f"{b:02X}" for b in final_bytes)
formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2))
self.send_input.setText(formatted_hex)
# 发送
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.serial_reader.serial_port.write(final_bytes)
cmd_code = hex_data.split()[13]
self.send_status.setText(f"功率设置({cmd_code})发送成功")
self.send_status.setStyleSheet("color: green;")
else:
self.send_status.setText("串口未连接")
self.send_status.setStyleSheet("color: red;")
except Exception as e:
self.send_status.setText(f"功率帧发送失败: {str(e)}")
self.send_status.setStyleSheet("color: red;")
def send_jammer_frame(self, hex_data):
try:
data_bytes = bytes.fromhex(hex_data.replace(' ', ''))
except ValueError:
self.send_status.setText("干扰码数据格式错误")
self.send_status.setStyleSheet("color: red;")
return
try:
crc_data = data_bytes[12:]
crc_value = crc16_modbus(crc_data)
data_list = list(data_bytes)
data_list[8] = (crc_value >> 8) & 0xFF # 高位
data_list[9] = crc_value & 0xFF # 低位
hex_with_crc = ''.join(f"{b:02X}" for b in data_list)
formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2))
self.send_input.setText(formatted_hex)
except Exception as e:
self.send_status.setText("CRC计算失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
return
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
try:
self.serial_reader.serial_port.write(data_bytes)
#self.send_status.setText(f"干扰码 {hex_data} 发送成功")
self.send_status.setStyleSheet("color: green;")
except Exception as e:
self.send_status.setText("发送失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
else:
self.send_status.setText("串口未连接")
self.send_status.setStyleSheet("color: red;")
def handle_send_button_click(self):
if self.loop_checkbox.isChecked():
try:
interval = int(self.interval_input.text())
if interval < 100:
self.send_status.setText("间隔太小(至少100ms)")
self.send_status.setStyleSheet("color: red;")
return
self.loop_timer.start(interval)
self.send_status.setText("循环发送中...")
self.send_status.setStyleSheet("color: green;")
except ValueError:
self.send_status.setText("请输入有效的数字作为间隔")
self.send_status.setStyleSheet("color: red;")
else:
self.loop_timer.stop()
self.send_hex_data()
def send_custom_frame(self):
if not hasattr(self, 'last_combined_frame') or self.last_combined_frame is None:
self.send_status.setText("请先发送一次组合帧")
self.send_status.setStyleSheet("color: red;")
return
# 获取用户输入的持续时间
duration_str = self.duration_input.text().strip()
try:
duration = int(duration_str)
if not (0 <= duration <= 65535):
raise ValueError("范围必须在 0 ~ 65535 之间")
except ValueError as e:
self.send_status.setText(f"输入错误:{e}")
self.send_status.setStyleSheet("color: red;")
return
# 将整数转换为两个字节(高位在前)
byte16 = (duration >> 8) & 0xFF # 高位字节
byte17 = duration & 0xFF # 低位字节
# 拷贝上一次的组合帧
data_bytes = bytearray(self.last_combined_frame)
# 插入到帧的第16和17个字节位置(索引16、17)
data_bytes[16] = byte16
data_bytes[17] = byte17
try:
# 计算 CRC(从第12字节开始)
crc_data = bytes(data_bytes[12:])
crc_value = crc16_modbus(crc_data)
# 插入 CRC 到第8、9字节
data_bytes[8] = (crc_value >> 8) & 0xFF
data_bytes[9] = crc_value & 0xFF
hex_with_crc = ''.join(f"{b:02X}" for b in data_bytes)
self.send_input.setText(' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2)))
except Exception as e:
self.send_status.setText("CRC计算失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
return
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.send_hex_data()
else:
self.send_status.setText("请先连接串口")
self.send_status.setStyleSheet("color: red;")
def send_version_query(self):
hex_data = "5A 5A 5A 5A 43 00 42 4E 7A 0E 4C 47 A1 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
try:
data_bytes = bytes.fromhex(hex_data.replace(' ', ''))
except ValueError:
self.send_status.setText("版本查询数据格式错误")
self.send_status.setStyleSheet("color: red;")
return
try:
crc_data = data_bytes[12:]
crc_value = crc16_modbus(crc_data)
data_list = list(data_bytes)
data_list[8] = (crc_value >> 8) & 0xFF # 高位
data_list[9] = crc_value & 0xFF # 低位
hex_with_crc = ''.join(f"{b:02X}" for b in data_list)
self.send_input.setText(' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2)))
except Exception as e:
self.send_status.setText("CRC计算失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
return
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.send_hex_data()
else:
self.send_status.setText("请先连接串口")
self.send_status.setStyleSheet("color: red;")
def do_send_data(self):
hex_str = self.send_input.text().strip()
if not hex_str:
return
try:
hex_bytes = bytes.fromhex(hex_str.replace(' ', ''))
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.serial_reader.serial_port.write(hex_bytes)
else:
self.loop_timer.stop()
self.send_status.setText("串口已断开,停止循环发送")
self.send_status.setStyleSheet("color: red;")
except Exception as e:
self.loop_timer.stop()
self.send_status.setText("发送失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
def send_hex_data(self):
hex_str = self.send_input.text().strip()
if not hex_str:
self.send_status.setText("请输入有效的Hex数据")
self.send_status.setStyleSheet("color: red;")
return
try:
hex_bytes = bytes.fromhex(hex_str.replace(' ', ''))
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.serial_reader.serial_port.write(hex_bytes)
self.send_status.setText("发送成功")
self.send_status.setStyleSheet("color: green;")
else:
self.send_status.setText("串口未连接")
self.send_status.setStyleSheet("color: red;")
except Exception as e:
self.send_status.setText("发送失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
def refresh_ports(self):
current_text = self.port_combo.currentText()
self.port_combo.clear()
ports = serial.tools.list_ports.comports()
for port in ports:
self.port_combo.addItem(port.device)
if current_text and current_text not in [self.port_combo.itemText(i) for i in range(self.port_combo.count())]:
self.port_combo.addItem(current_text)
self.port_combo.setCurrentText(current_text)
else:
self.port_combo.setCurrentText(current_text if current_text else "")
def toggle_serial_connection(self):
if self.serial_reader and self.serial_reader.isRunning():
self.serial_reader.stop()
self.status_label.setText("已断开")
self.status_label.setStyleSheet("color: red;")
self.connect_button.setText("连接串口")
self.loop_timer.stop()
else:
port = self.port_combo.currentText()
baud = int(self.baud_combo.currentText())
self.serial_reader = SerialReader(port, baud)
self.serial_reader.data_received.connect(self.handle_serial_data)
self.serial_reader.start()
self.status_label.setText("已连接")
self.status_label.setStyleSheet("color: green;")
self.connect_button.setText("断开串口")
def handle_serial_data(self, data):
self.buffer.extend(data)
self.raw_data_lines.append("Received (Hex): " + ' '.join(f"{b:02X}" for b in data)) # 将原始数据转换为Hex格式并添加到缓冲区
self.process_buffer()
def process_buffer(self):
while len(self.buffer) >= 78:
found = False
for i in range(len(self.buffer) - 77):
if self.buffer[i + 12] == 0xA2 and self.buffer[i + 13] == 0x02:
frame = self.buffer[i:i+78]
self.buffer = self.buffer[i+78:]
self.raw_data_lines.append("Received Frame (Hex): " + ' '.join(f"{b:02X}" for b in frame))
self.parse_hex_data(frame)
found = True
break
if not found:
break
def flush_display(self):
if self.raw_data_lines:
lines = self.raw_data_lines
self.raw_data_lines = []
for line in lines:
cursor = self.raw_data_display.textCursor()
cursor.movePosition(cursor.End)
cursor.select(cursor.LineUnderCursor)
line_count = self.raw_data_display.document().lineCount()
if line_count >= self.max_lines:
cursor.movePosition(cursor.Start)
cursor.select(cursor.LineUnderCursor)
cursor.removeSelectedText()
cursor.deleteChar()
cursor.movePosition(cursor.End)
cursor.insertText(line + '\n')
self.raw_data_display.setTextCursor(cursor)
self.raw_data_display.ensureCursorVisible()
for i in range(4):
band_name = f"band{i + 1}"
self.update_band_display(band_name, self.band_data_cache[band_name])
def parse_hex_data(self, data):
try:
hex_data = list(data)
# 检查长度是否足够
if len(hex_data) < 78:
return
switch_byte = hex_data[15]
switches = [(switch_byte >> i) & 1 for i in range(4)]
if len(hex_data) > 29:
temp = hex_data[25]
volt = (hex_data[26] << 8) | hex_data[27]
curr = (hex_data[28] << 8) | hex_data[29]
self.band_data_cache["band1"]["temp"] = f"{temp}°C"
self.band_data_cache["band1"]["voltage"] = f"{volt / 1000:.3f}V"
self.band_data_cache["band1"]["current"] = f"{curr / 1000:.3f}A"
self.band_data_cache["band1"]["enabled"] = "on" if switches[0] else "off"
if len(hex_data) > 38:
temp = hex_data[33]
volt = (hex_data[34] << 8) | hex_data[35]
curr = (hex_data[36] << 8) | hex_data[37]
self.band_data_cache["band2"]["temp"] = f"{temp}°C"
self.band_data_cache["band2"]["voltage"] = f"{volt / 1000:.3f}V"
self.band_data_cache["band2"]["current"] = f"{curr / 1000:.3f}A"
self.band_data_cache["band2"]["enabled"] = "on" if switches[1] else "off"
if len(hex_data) > 46:
temp = hex_data[41]
volt = (hex_data[42] << 8) | hex_data[43]
curr = (hex_data[44] << 8) | hex_data[45]
self.band_data_cache["band3"]["temp"] = f"{temp}°C"
self.band_data_cache["band3"]["voltage"] = f"{volt / 1000:.3f}V"
self.band_data_cache["band3"]["current"] = f"{curr / 1000:.3f}A"
self.band_data_cache["band3"]["enabled"] = "on" if switches[2] else "off"
if len(hex_data) > 54:
temp = hex_data[49]
volt = (hex_data[50] << 8) | hex_data[51]
curr = (hex_data[52] << 8) | hex_data[53]
self.band_data_cache["band4"]["temp"] = f"{temp}°C"
self.band_data_cache["band4"]["voltage"] = f"{volt / 1000:.3f}V"
self.band_data_cache["band4"]["current"] = f"{curr / 1000:.3f}A"
self.band_data_cache["band4"]["enabled"] = "on" if switches[3] else "off"
except Exception as e:
print("解析Hex数据失败:", e)
def update_band_display(self, band_name, data):
widgets = self.band_widgets.get(band_name)
if not widgets:
return
if "enabled" in data:
widgets["enabled"].setChecked(data["enabled"] == "on")
if "temp" in data:
widgets["temp"].setText(data["temp"])
if "current" in data:
widgets["current"].setText(data["current"])
if "voltage" in data:
widgets["voltage"].setText(data["voltage"])
def update_combined_frame(self):
# 构造帧副本
frame = bytearray(self.preset_frame_template)
# 计算启用的频段掩码
combined_mask = 0x00
for checkbox in self.band_checkboxes:
if checkbox.isChecked():
band_name = checkbox.text()
combined_mask |= self.band_masks[band_name]
# 设置第16个字节
frame[15] = combined_mask
# 计算 CRC(从第12字节开始)
crc_data = bytes(frame[12:])
crc_value = crc16_modbus(crc_data)
# 插入 CRC
frame[8] = (crc_value >> 8) & 0xFF # 高位
frame[9] = crc_value & 0xFF # 低位
# 保存组合帧用于后续自定义帧使用
self.last_combined_frame = frame
# 显示在发送框中
hex_with_crc = ''.join(f"{b:02X}" for b in frame)
formatted_hex = ' '.join(hex_with_crc[i:i+2] for i in range(0, len(hex_with_crc), 2))
self.send_input.setText(formatted_hex)
def send_combined_frame(self):
hex_str = self.send_input.text().strip()
if not hex_str:
self.send_status.setText("请输入有效的Hex数据")
self.send_status.setStyleSheet("color: red;")
return
try:
hex_bytes = bytes.fromhex(hex_str.replace(' ', ''))
if self.serial_reader and self.serial_reader.serial_port and self.serial_reader.serial_port.is_open:
self.serial_reader.serial_port.write(hex_bytes)
self.send_status.setText("组合帧发送成功")
self.send_status.setStyleSheet("color: green;")
else:
self.send_status.setText("串口未连接")
self.send_status.setStyleSheet("color: red;")
except Exception as e:
self.send_status.setText("发送失败:" + str(e))
self.send_status.setStyleSheet("color: red;")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = SerialDataDisplay()
window.show()
sys.exit(app.exec_())