''' @brief This is an example plugin, can receive and send data @author @date @license LGPL-3.0 ''' from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QPushButton, QLabel, QScrollArea, QSplitter from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtGui import QFont, QTextCursor from struct import Struct try: import utils_ui from plugins.base import Plugin_Base from conn import ConnectionStatus from i18n import _ from widgets import statusBar except ImportError: from COMTool import utils_ui from COMTool.plugins.base import Plugin_Base from COMTool.i18n import _ from COMTool.conn import ConnectionStatus from COMTool.widgets import statusBar class Plugin(Plugin_Base): id = "HJ DATALINK" name = _("HJ DATALINK") showReceiveDataSignal = pyqtSignal(str) def onInit(self, config): ''' init params ''' default = { "version": 1, "customSendItems": [ { "caption": "自检", "icon": "fa5.question-circle", "data": "5A 54 E1 EE 00 CF 02 5A FE" }, # { # "caption": "数据发送测试 - 1", # "icon": "fa5.play-circle", # "data": "" # }, # { # "caption": "数据发送测试 - 2", # "icon": "fa5.play-circle", # "data": "" # }, { "caption": "装订 - 【主 - 0x01】128 kbps,时隙表 0", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 01 22 00 00 7B 06 5A FE" }, { "caption": "装订 - 【主 - 0x01】2 Mbps,时隙表 1", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 01 44 01 00 C1 06 5A FE" }, { "caption": "装订 - 【主 - 0x01】静默", "icon": "fa5.play-circle", "data": "5A 54 E3 EE 55 01 22 00 00 49 06 5A FE" }, { "caption": "装订 - 【从 - 0x02】128 kbps,时隙表 0", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 02 22 00 00 9F 06 5A FE" }, { "caption": "装订 - 【从 - 0x02】2 Mbps,时隙表 1", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 02 44 01 00 C2 06 5A FE" }, { "caption": "装订 - 【从 - 0x02】静默", "icon": "fa5.play-circle", "data": "5A 54 E3 EE 55 02 22 00 00 0C 06 5A FE" }, { "caption": "装订 - 【从 - 0x03】128 kbps,时隙表 0", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 03 22 00 00 A0 06 5A FE" }, { "caption": "装订 - 【从 - 0x03】2 Mbps,时隙表 1", "icon": "fa5.play-circle", "data": "5A 54 E3 EE AA 03 44 01 00 C3 06 5A FE" }, { "caption": "装订 - 【从 - 0x03】静默", "icon": "fa5.play-circle", "data": "5A 54 E3 EE 55 03 22 00 00 0D 06 5A FE" }, { "caption": "遥测", "icon": "fa5.play-circle", "data": "5A 54 E6 EE 00 D4 02 5A FE" }, ] } self.config = config for k in default: if not k in self.config: self.config[k] = default[k] def onConnChanged(self, status:ConnectionStatus, msg:str): print("-- connection changed: {}, msg: {}".format(status, msg)) def onWidgetMain(self, parent): ''' main widget, just return a QWidget object ''' self.mainWidget = QSplitter(Qt.Vertical) # receive widget self.receiveWidget = QTextEdit("") font = QFont('Consolas, DejaVu Sans Mono, Courier New, monospace, Microsoft YaHei', 10) self.receiveWidget.setFont(font) self.receiveWidget.setLineWrapMode(QTextEdit.NoWrap) # clear button self.clearBtn = QPushButton("") utils_ui.setButtonIcon(self.clearBtn, "mdi6.broom") # send scroll self.customSendScroll = QScrollArea() self.customSendScroll.setMinimumHeight(20) self.customSendScroll.setWidgetResizable(True) self.customSendScroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.customSendScroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) cutomSendItemsWraper = QWidget() self.customSendScroll.setWidget(cutomSendItemsWraper) # -- wrapper widget cutomSendItemsWraper0 = QWidget() cutomSendItemsWraper0.setProperty("class", "scrollbar2") layout0 = QVBoxLayout() layout0.setContentsMargins(0,8,0,0) cutomSendItemsWraper0.setLayout(layout0) layout0.addWidget(self.customSendScroll) customSendItemsLayoutWrapper = QVBoxLayout() customSendItemsLayoutWrapper.setContentsMargins(0,0,0,0) cutomSendItemsWraper.setLayout(customSendItemsLayoutWrapper) # -- items container self.customItems = QWidget() self.customSendItemsLayout = QVBoxLayout() self.customSendItemsLayout.setContentsMargins(0,0,0,0) self.customItems.setLayout(self.customSendItemsLayout) customSendItemsLayoutWrapper.addWidget(self.customItems) customSendItemsLayoutWrapper.addStretch(0) # add widgets self.mainWidget.addWidget(self.receiveWidget) self.mainWidget.addWidget(self.clearBtn) self.mainWidget.addWidget(cutomSendItemsWraper0) self.mainWidget.setStretchFactor(0, 2) self.mainWidget.setStretchFactor(1, 1) self.mainWidget.setStretchFactor(2, 11) # event def clearReceived(): self.receiveWidget.clear();self.statusBar.clear() self.clearBtn.clicked.connect(clearReceived) return self.mainWidget def onWidgetStatusBar(self, parent): self.statusBar = statusBar(rxTxCount=True) return self.statusBar def onUiInitDone(self): ''' UI init done, you can update your widget here this method runs in UI thread, do not block too long ''' newItems = [] print(self.config["customSendItems"]) for item in self.config["customSendItems"]: print(item) item = self.insertSendItem(item, load=True) newItems.append(item) self.config["customSendItems"] = newItems self.showReceiveDataSignal.connect(self.showReceivedData) # frame struct self.structSelfTestRsp = Struct("4d}\n" ret += f"AD 温度 2: {payload[hwOffset+4]:>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" ret += f"天线-2 工作 PLL: {(payload[hwOffset+7] >> 15) & 0x01:>1d}\n" ret += f"天线-2 工作频率: {(payload[hwOffset+7] & 0x7FFF)+3700:>4d}\n" ret += f"天线-3 工作 PLL: {(payload[hwOffset+8] >> 15) & 0x01:>1d}\n" ret += f"天线-3 工作频率: {(payload[hwOffset+8] & 0x7FFF)+3700:>4d}\n" ret += f"天线 PLL 锁定状态: {bin(payload[hwOffset+9])[2:].zfill(8)}\n" ret += f"天线工作模式: {bin(payload[hwOffset+10])[2:].zfill(8)}\n" ret += f"天线温度: {payload[hwOffset+11]:>4d}\n" ret += "--------------------------------\n" return ret + '\n' + self.printFrame(data) def printFrame(self, data: bytes): ret = '' for i in range(len(data)): ret += f'{data[i]:02X}' ret += '\n' if i%16 == 15 else ' ' ret += '\n' return ret def evalStatusCheckRsp(self, data: bytes): try: payload = self.structStatusCheckRsp.unpack(data) except : return f"\n### evalStatusCheckRsp:: Failed to unpack data (data length {len(data)} byte).\n" ret = "----------- 遥测回令 -----------\n" ret += "节点类型: " if payload[1] == 0xEE: ret += f"C频段 (0x{payload[1]:02X})\n" else: ret += f"【未知错误】(0x{payload[1]:02X})\n" 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" ret += "网络连接状态: " if payload[linkOffset + 3] == 0x11: ret += f"已连接 (0x{payload[linkOffset + 3]:02X})\n" elif payload[linkOffset + 3] == 0x00: ret += f"未连接 (0x{payload[linkOffset + 3]:02X})\n" else: ret += f"【未知错误】(0x{payload[linkOffset + 3]:02X})\n" ret += "网络运行阶段: " if payload[linkOffset + 4] == 0x11: ret += f"建网阶段 (0x{payload[linkOffset + 4]:02X})\n" elif payload[linkOffset + 4] == 0x22: ret += f"业务通信 (0x{payload[linkOffset + 4]:02X})\n" else: ret += f"【未知错误】(0x{payload[linkOffset + 4]:02X})\n" 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" 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" 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" ret += f"天线-2 工作 PLL: {(payload[hwOffset+7] >> 15) & 0x01:>1d}\n" ret += f"天线-2 工作频率: {(payload[hwOffset+7] & 0x7FFF)+3700:>4d}\n" ret += f"天线-3 工作 PLL: {(payload[hwOffset+8] >> 15) & 0x01:>1d}\n" ret += f"天线-3 工作频率: {(payload[hwOffset+8] & 0x7FFF)+3700:>4d}\n" ret += f"天线 PLL 锁定状态: {bin(payload[hwOffset+9])[2:].zfill(8)}\n" ret += f"天线工作模式: {bin(payload[hwOffset+10])[2:].zfill(8)}\n" ret += f"天线温度: {payload[hwOffset+11]:>4d}\n" ret += "--------------------------------\n" return ret + '\n' + self.printFrame(data) def onSent(self, ok, msg, length, path): if ok: self.statusBar.addTx(length) else: self.hintSignal.emit("error", _("Error"), _("Send data failed!") + " " + msg) def sendCustomItem(self, item): data = item["data"] dateBytes = self.parseSendData(data, encoding = self.configGlobal["encoding"], isHexStr = True) if dateBytes: self.send(dateBytes, callback=self.onSent)