feat: 更新图片上位机,修改按钮布局和文字,增加自检回读

图片传输功能由于缺少buffer,会导致缺或多数据,造成图片显示异常;自检回读大概率会卡死导致程序退出
This commit is contained in:
2025-08-28 10:33:19 +08:00
parent b3fa034558
commit 3caea65695
3 changed files with 919 additions and 155 deletions
+140 -155
View File
@@ -30,8 +30,8 @@ except ImportError:
class Plugin(Plugin_Base):
id = "hsjl_time"
name = _("hsjl_time")
id = "hsjl_timer"
name = _("hsjl_timer")
showReceiveDataSignal = pyqtSignal(str)
# 图片1的字节数据(已存在,提取为常量)
IMAGE1_DATA = [
@@ -72,9 +72,11 @@ class Plugin(Plugin_Base):
"version": 1,
"customSendItems": [
{
"caption": "遥测",
"caption": "自检项",
"icon": "fa5.play-circle",
"data": "5A 54 E6 EE 00 D4 02 5A FE"
"data": "5A 54 E1 EE 00 CF 02 5A FE"
#"data": "5A 54 E1 EE 01 00 00 00 01 15 00 55 00 05 55 57 58 80 00 00 00 00 00 00 00\n\
#00 00 00 01 00 00 00 00 75 17 5A FE "
},
{
"caption": "图片传输",
@@ -88,7 +90,7 @@ class Plugin(Plugin_Base):
4E 62 24 11 DE 82 35 D8 00 00 00 00 49 45 4E 44 AE 42 60 82 00 76 C4 5A FE"
},
{
"caption": "总控",
"caption": "配置发送",
"icon": "fa5.play-circle",
"data": "5A 54 E3 EE AA 03 22 00 00 A0 06 5A FE"
},
@@ -98,6 +100,8 @@ class Plugin(Plugin_Base):
for k in default:
if k not in self.config:
self.config[k] = default[k]
self.config["customSendItems"] = default["customSendItems"]
print("初始化的customSendItems:", self.config["customSendItems"])
def createToggleButton(self):
toggleBtn = QPushButton("开关: Off")
@@ -119,31 +123,58 @@ class Plugin(Plugin_Base):
return toggleBtn
def createIdButton(self):
toggleBtn = QPushButton("id: 01")
# 定义ID映射:原始ID -> 显示文本(带标识)
id_display_map = {
"01": "01(主)",
"02": "02(从)",
"03": "03(从)"
}
default_id = "01" # 原始ID默认值
# 按钮显示带标识的文本
toggleBtn = QPushButton(f"id: {id_display_map[default_id]}")
# 存储原始ID(关键:组帧时用这个值)
toggleBtn.setProperty("originalId", default_id)
menu = QMenu(toggleBtn)
ids = ["01", "02", "03"]
def setId(text):
toggleBtn.setText(f"id: {text}")
print(f"id设为 {text}")
def setId(original_id):
# 更新显示文本(带标识)
toggleBtn.setText(f"id: {id_display_map[original_id]}")
# 更新存储的原始ID
toggleBtn.setProperty("originalId", original_id)
print(f"id设为 {original_id}") # 确认原始ID正确
for id_text in ids:
menu.addAction(id_text).triggered.connect(lambda checked, t=id_text: setId(t))
# 菜单显示带标识的文本,但传递原始ID
for original_id, display_text in id_display_map.items():
menu.addAction(display_text).triggered.connect(
lambda checked, id=original_id: setId(id)
)
toggleBtn.setMenu(menu)
return toggleBtn
def createtoIdButton(self):
toggleBtn = QPushButton("目的节点id: 01")
# 目的节点ID同理处理
id_display_map = {
"01": "01(主)",
"02": "02(从)",
"03": "03(从)"
}
default_id = "01"
toggleBtn = QPushButton(f"目的节点id: {id_display_map[default_id]}")
toggleBtn.setProperty("originalId", default_id) # 存储原始ID
menu = QMenu(toggleBtn)
ids = ["01", "02", "03"]
def setId(text):
toggleBtn.setText(f"目的节点id: {text}")
print(f"目的节点id设为 {text}")
def setId(original_id):
toggleBtn.setText(f"目的节点id: {id_display_map[original_id]}")
toggleBtn.setProperty("originalId", original_id)
print(f"目的节点id设为 {original_id}")
for id_text in ids:
menu.addAction(id_text).triggered.connect(lambda checked, t=id_text: setId(t))
for original_id, display_text in id_display_map.items():
menu.addAction(display_text).triggered.connect(
lambda checked, id=original_id: setId(id)
)
toggleBtn.setMenu(menu)
return toggleBtn
@@ -197,9 +228,11 @@ class Plugin(Plugin_Base):
# ID字段,转为整数
try:
id_val = int(id_text.split(":")[1].strip(), 16)
# 直接获取存储的原始ID(如"01")
original_id = self.idButton.property("originalId")
id_val = int(original_id, 16) # 转为16进制整数
except:
id_val = 0x00
id_val = 0x00 # 异常时默认值
frame.append(id_val)
# 速率字段对应映射(参考原来速率的0x22等)
@@ -240,9 +273,10 @@ class Plugin(Plugin_Base):
# ID字段,转为整数
try:
id_val = int(toid_text.split(":")[1].strip(), 16)
original_toid = self.toidButton.property("originalId")
id_val = int(original_toid, 16)
except:
id_val = 0x01
id_val = 0x01 # 异常时默认值
frame.append(id_val)
# 根据索引选择图片数据
@@ -381,19 +415,19 @@ class Plugin(Plugin_Base):
self.mainWidget.addWidget(sendItemsOuterWrapper)
# 网络状态标签初始化
self.networkStatusLabel = QLabel("网络连接状态: 未连接")
self.networkStatusLabel = QLabel("节点状态: 未知")
self.networkStatusLabel.setAlignment(Qt.AlignLeft)
self.networkStatusLabel.setFont(QFont("Arial", 10))
self.networkPhaseLabel = QLabel("网络运行阶段: 未知")
self.networkPhaseLabel = QLabel("天线温度: 未知")
self.networkPhaseLabel.setAlignment(Qt.AlignLeft)
self.networkPhaseLabel.setFont(QFont("Arial", 10))
self.networktemLabel = QLabel("fpga温度: 未知")
self.networktemLabel = QLabel("电压状态: 未知")
self.networktemLabel.setAlignment(Qt.AlignLeft)
self.networktemLabel.setFont(QFont("Arial", 10))
self.networkvoltageLabel = QLabel("电压监测: 未知")
self.networkvoltageLabel = QLabel("fgpa温度: 未知")
self.networkvoltageLabel.setAlignment(Qt.AlignLeft)
self.networkvoltageLabel.setFont(QFont("Arial", 10))
@@ -434,34 +468,32 @@ class Plugin(Plugin_Base):
return itemWidget
def onUiInitDone(self):
# 去掉自检项
self.config["customSendItems"] = [item for item in self.config["customSendItems"] if item["caption"] != "自检"]
# 清空旧布局
self.clearLayout(self.customSendItemsLayout)
# 创建“开关”、“ID”、“时隙表”、“速率”按钮,供“总控”使用
self.toggleButton = self.createToggleButton()
self.customSendItemsLayout.addWidget(self.toggleButton, 1, 0)
self.customSendItemsLayout.addWidget(self.toggleButton, 1, 0, 1, 2)
self.idButton = self.createIdButton()
self.customSendItemsLayout.addWidget(self.idButton, 1, 1)
self.customSendItemsLayout.addWidget(self.idButton, 0, 1)
self.slotButton = self.createSlotButton()
self.customSendItemsLayout.addWidget(self.slotButton, 1, 2)
self.customSendItemsLayout.addWidget(self.slotButton, 0, 2)
self.rateButton = self.createRateButton()
self.customSendItemsLayout.addWidget(self.rateButton, 1, 3)
self.customSendItemsLayout.addWidget(self.rateButton, 0, 0)
self.toidButton = self.createtoIdButton()
self.customSendItemsLayout.addWidget(self.toidButton, 0, 3)
# 第一行固定插入“遥测”、“图片传输”、“总控”
# 第一行固定插入“自检”、“图片传输”、“总控”
for item in self.config["customSendItems"]:
if item["caption"] == "遥测":
if item["caption"] == "自检项":
print("找到自检项,准备添加到(0,0)") # 新增
w = self.createSendItemWidget(item)
self.customSendItemsLayout.addWidget(w, 0, 0)
self.customSendItemsLayout.addWidget(w, 3, 0, 1, 2)
elif item["caption"] == "图片传输":
totalControlWidget = QWidget()
layout = QHBoxLayout()
@@ -545,16 +577,16 @@ class Plugin(Plugin_Base):
layout.addWidget(label)
layout.addWidget(sendBtn)
self.customSendItemsLayout.addWidget(totalControlWidget, 0, 1)
self.customSendItemsLayout.addWidget(totalControlWidget, 3, 2, 1, 2 )
elif item["caption"] == "总控":
elif item["caption"] == "配置发送":
totalControlWidget = QWidget()
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
totalControlWidget.setLayout(layout)
sendBtn = QPushButton("")
label = QLabel("总控")
label = QLabel("配置发送")
utils_ui.setButtonIcon(sendBtn, item.get("icon", "fa.send"))
sendBtn.setProperty("class", "smallBtn")
@@ -568,7 +600,7 @@ class Plugin(Plugin_Base):
# 动态构造指令字符串S
cmd_str = self.buildBindCommand(switch_text, id_text, rate_text, slot_text)
print(f"总控: {cmd_str}")
print(f"配置发送: {cmd_str}")
data_bytes = self.parseSendData(cmd_str,
encoding=self.configGlobal["encoding"],
@@ -579,16 +611,13 @@ class Plugin(Plugin_Base):
layout.addWidget(label)
layout.addWidget(sendBtn)
self.customSendItemsLayout.addWidget(totalControlWidget, 0, 2)
self.customSendItemsLayout.addWidget(totalControlWidget, 1, 2, 1, 2)
# 第三行放4个状态标签
self.customSendItemsLayout.addWidget(self.networkStatusLabel, 2, 0)
self.customSendItemsLayout.addWidget(self.networkPhaseLabel, 2, 1)
self.customSendItemsLayout.addWidget(self.networkvoltageLabel, 2, 2)
self.customSendItemsLayout.addWidget(self.networktemLabel, 2, 3)
# 初始化结构体
self.structStatusCheckRsp = Struct(
"<BB Q IIII IBBBBIB III III III III III i II 3s IIII d Q dBB dB H B 2s BBBbbb HHHBBbB B 3s")
def insertSendItem(self, item = None, load = False):
caption = item["caption"]
@@ -661,8 +690,8 @@ class Plugin(Plugin_Base):
result = self.evalimageTestRsp(binary_data)
self.showReceiveDataSignal.emit(result)
return
elif cmd_id == 0xE6: # 遥测回令
result = self.evalStatusCheckRsp(binary_data)
elif cmd_id == 0xE1: # 自检回令
result = self.evalSelfTestRsp(binary_data)
self.showReceiveDataSignal.emit(result)
return
else:
@@ -700,130 +729,80 @@ class Plugin(Plugin_Base):
ret += '\n'
return ret
def evalStatusCheckRsp(self, data: bytes):
def evalSelfTestRsp(self, data: bytes):
print('“”“”“”“”“”“”“”“”“”“”“”“')
try:
payload = self.structStatusCheckRsp.unpack(data)
except :
return f"\n### evalStatusCheckRsp:: Failed to unpack data (data length {len(data)} byte).\n"
# 1. 先解析头部获取数据长度和图片类型标识
header_struct = Struct("<2s B B") # 前11字节结构
header_data = header_struct.unpack(data[:4])
frame_head, cmd_id, node_type= header_data
print(data)
print(header_data)
img_flag = data[7:8] # 第八个字节
print('m')
# 3. 动态构建完整结构体
struct_format = f"<2s B B BBBBbbb 2B 2B 2B BBbBB 3s B B B 2s"
full_struct = Struct(struct_format)
print('km')
# 计算结构体要求的总长度
required_len = 30 # 头部11 + 数据段data_len + 尾部5
print(f"预期长度: {required_len}, 实际数据长度: {len(data)}")
payload = full_struct.unpack(data)
print('lm')
except Exception as e:
print('x')
return f"\n### evalselfTestRsp:: Failed to unpack data (data length {len(data)} byte).\n错误信息: {str(e)}"
ret = "----------- 自检回令 -----------\n"
ret = "----------- 遥测回令 -----------\n"
ret += f"节点编号: 0x{payload[-2]:02X}\n"
ret += "节点类型: "
if payload[1] == 0xEE:
ret += f"C频段 (0x{payload[1]:02X})\n"
ret += "节点状态: "
if payload[3] == 0xAA:
ret += f"正常 (0x{payload[3]:02X})\n"
network_status='正常'
elif payload[6] == 0x55:
ret += f"异常 (0x{payload[3]:02X})\n"
network_status='异常'
else:
ret += f"【未知错误】(0x{payload[1]:02X})\n"
ret += f"【未知错误】(0x{payload[3]:02X})\n"
network_status='未知错误'
ret += f"时间戳: {payload[2]/1e5:.2f} ms\n"
sysbusOffset = 3
linkOffset = 7
phyOffset = 36
hwOffset = 47
ret += "\n -- 总线状态\n"
ret += f"配置装订: {payload[sysbusOffset + 0]:08X}\n"
ret += f"接收数据计数: {payload[sysbusOffset + 1]:>6d}\n"
ret += f"发送数据计数: {payload[sysbusOffset + 2]:>6d}\n"
ret += f"遥测请求计数: {payload[sysbusOffset + 3]:>6d}\n"
ret += "\n -- 链路层状态\n"
ret += f"时元号: {payload[linkOffset + 0]:>6d}\n"
ret += f"时隙号: {payload[linkOffset + 1]:>3d}\n"
ret += "当前角色: "
if payload[linkOffset + 2] == 0xAA:
ret += f"簇首 (0x{payload[linkOffset + 2]:02X})\n"
elif payload[linkOffset + 2] == 0xCC:
ret += f"成员 (0x{payload[linkOffset + 2]:02X})\n"
else:
ret += f"【未知错误】(0x{payload[linkOffset + 2]:02X})\n"
# 网络连接状态更新
network_status = "未连接"
if payload[linkOffset + 3] == 0x11:
network_status = "已连接"
elif payload[linkOffset + 3] == 0x00:
network_status = "未连接"
else:
network_status = f"未知状态 (0x{payload[linkOffset + 3]:02X})"
# 网络运行阶段更新
phase = "未知"
if payload[linkOffset + 4] == 0x11:
phase = "建网阶段"
elif payload[linkOffset + 4] == 0x22:
phase = "业务通信"
else:
phase = f"未知阶段 (0x{payload[linkOffset + 4]:02X})"
self.networkStatusLabel.setText(f"网络连接状态: {network_status}")
self.networkPhaseLabel.setText(f"网络运行阶段: {phase}")
ret += f"重建/入网计数: {payload[linkOffset + 5]:>6d}\n"
ret += f"通信天线位号: {payload[linkOffset + 6]:>1d}\n"
ret += f"建网帧发送计数: {payload[linkOffset + 7]:>6d}\n"
ret += f"建网帧接收计数: {payload[linkOffset + 8]:>6d}\n"
ret += f"建网帧正确计数: {payload[linkOffset + 9]:>6d}\n"
ret += f"应答帧发送计数: {payload[linkOffset + 10]:>6d}\n"
ret += f"应答帧接收计数: {payload[linkOffset + 11]:>6d}\n"
ret += f"应答帧正确计数: {payload[linkOffset + 12]:>6d}\n"
ret += f"分发帧发送计数: {payload[linkOffset + 13]:>6d}\n"
ret += f"分发帧接收计数: {payload[linkOffset + 14]:>6d}\n"
ret += f"分发帧正确计数: {payload[linkOffset + 15]:>6d}\n"
ret += f"网管帧发送计数: {payload[linkOffset + 16]:>6d}\n"
ret += f"网管帧接收计数: {payload[linkOffset + 17]:>6d}\n"
ret += f"网管帧正确计数: {payload[linkOffset + 18]:>6d}\n"
ret += f"RTT 帧发送计数: {payload[linkOffset + 19]:>6d}\n"
ret += f"RTT 帧接收计数: {payload[linkOffset + 20]:>6d}\n"
ret += f"RTT 帧正确计数: {payload[linkOffset + 21]:>6d}\n"
ret += f"业务帧发送计数: {payload[linkOffset + 22]:>6d}\n"
ret += f"业务帧接收计数: {payload[linkOffset + 23]:>6d}\n"
ret += f"业务帧正确计数: {payload[linkOffset + 24]:>6d}\n"
ret += f"时间偏差记录: {payload[linkOffset + 25]/1e2:.2f} us\n"
ret += f"发送数据 ID: {payload[linkOffset + 26]:>6d}\n"
ret += f"接收数据 ID: {payload[linkOffset + 27]:>6d}\n"
ret += "\n -- 物理层状态\n"
ret += f"捕获计数: {payload[phyOffset + 0]:>6d}\n"
ret += f"帧同步计数: {payload[phyOffset + 1]:>6d}\n"
ret += f"译码计数: {payload[phyOffset + 2]:>6d}\n"
ret += f"CRC 正确计数:{payload[phyOffset + 3]:>6d}\n"
ret += f"多普勒估计: {payload[phyOffset + 4]:.2f} Hz\n"
ret += f"正确接收时刻: {payload[phyOffset + 5]/1e2:.2f} us\n"
ret += f"捕获峰均比: {payload[phyOffset + 6]:.2f}\n"
ret += f"捕获相位值: {payload[phyOffset + 7]:>3d}\n"
ret += f"捕获频偏通道: {payload[phyOffset + 8]:>3d}\n"
ret += f"帧同步峰均比: {payload[phyOffset + 9]:.2f}\n"
ret += f"帧同步频偏通道: {payload[phyOffset + 10]:>3d}\n"
ret += f"交织输入数据幅度统计:\t0x{payload[phyOffset + 11]:04X}\n"
ret += f"天线控制模式: {bin(payload[phyOffset + 10])[2:].zfill(8)}\n"
ret += "\n -- 硬件状态\n"
voltage = f"FPGA 电压监测: {bin(payload[hwOffset+0])[2:].zfill(8)}\n"
hwOffset = 4
ret += f"FPGA 电压监测: {bin(payload[hwOffset+0])[2:].zfill(8)}\n"
ret += f"时钟锁定状态: {bin(payload[hwOffset+1])[2:].zfill(8)}\n"
ret += f"AD 锁定状态: {bin(payload[hwOffset+2])[2:].zfill(8)}\n"
ret += f"AD 温度 1: {payload[hwOffset+3]:>4d}\n"
ret += f"AD 温度 2: {payload[hwOffset+4]:>4d}\n"
temp = f"FPGA 温度: {payload[hwOffset+5]:>4d}\n"
ret += f"FPGA 温度: {payload[hwOffset+5]:>4d}\n"
ret += f"天线-1 工作 PLL: {(payload[hwOffset+6] >> 15) & 0x01:>1d}\n"
ret += f"天线-1 工作频率: {(payload[hwOffset+6] & 0x7FFF)+3700:>4d}\n"
print(payload[3])
ret += f"天线-2 工作 PLL: {(payload[hwOffset+7] >> 15) & 0x01:>1d}\n"
ret += f"天线-2 工作频率: {(payload[hwOffset+7] & 0x7FFF)+3700:>4d}\n"
print(payload[3])
ret += f"天线-3 工作 PLL: {(payload[hwOffset+8] >> 15) & 0x01:>1d}\n"
ret += f"天线-3 工作频率: {(payload[hwOffset+8] & 0x7FFF)+3700:>4d}\n"
print(payload[3])
ret += f"天线 PLL 锁定状态: {bin(payload[hwOffset+9])[2:].zfill(8)}\n"
print(payload[3])
ret += f"天线工作模式: {bin(payload[hwOffset+10])[2:].zfill(8)}\n"
ret += f"天线温度: {payload[hwOffset+11]:>4d}\n"
print('+')
print(voltage)
print('_')
self.networkvoltageLabel.setText(f"电压监测: {voltage}")
self.networktemLabel.setText(f"fgpa温度: {temp}")
print(payload[3])
ret += "--------------------------------\n"
print(payload[3])
print(network_status)
phase=payload[14]
temp=payload[9]
voltage=payload[4]
self.networkStatusLabel.setText(f"节点状态: {network_status}")
self.networkPhaseLabel.setText(f"天线温度: {phase}")
self.networkvoltageLabel.setText(f"电压状态: {voltage}")
self.networktemLabel.setText(f"fgpa温度: {temp}")
return ret + '\n' + self.printFrame(data)
def evalimageTestRsp(self, data: bytes):
print('“”“”“”“”“”“”“”“”“”“”“”“')
try:
@@ -879,22 +858,28 @@ class Plugin(Plugin_Base):
ret += f"帧尾: {frame_tail.hex().upper()}\n"
print('&&&&&')
# 图像显示逻辑(保留原始控件调用方式)
# 截取有效图像数据
# 图像显示逻辑(添加缩放倍数控制)
# 1. 手动设置缩放倍数(可根据需要修改,例如放大2倍)
# 图像显示逻辑(修正后)
if from_id == 0x01 or from_id == 0x02:
# 图像通道 A → 左侧图像控件
if len(self.widgets) >= 1 and isinstance(self.widgets[0], graphWidgets["image"]):
self.widgets[0].onData(image_data) # 发送有效数据
# 仅传递图像数据,缩放倍数已在控件初始化时通过config设置
self.widgets[0].onData(image_data)
else:
self.showReceiveDataSignal.emit("\n### No valid image widget found (A1)\n")
print(from_id)
elif from_id == 0x03:
# 图像通道 B → 右侧图像控件
if len(self.widgets) >= 2 and isinstance(self.widgets[1], graphWidgets["image"]):
self.widgets[1].onData(image_data) # 发送有效数据
# 仅传递图像数据
self.widgets[1].onData(image_data)
else:
self.showReceiveDataSignal.emit("\n### No valid image widget found (A2)\n")
print(from_id)
print('+')
ret += "------------------------------------\n"
return ret + '\n' + self.printFrame(data)
print(from_id)
print('+')
ret += "------------------------------------\n"