''' @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 = "DEIFEI" name = _("DUIFEI") showReceiveDataSignal = pyqtSignal(str) def onInit(self, config): ''' init params ''' default = { "version": 1, "customSendItems": [ { "caption": "Self Test", "icon": "fa5.question-circle", "data": "5A 54 E1 DD 00 BE 02 5A FE" }, { "caption": "Config - FLJ Low Power", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 88 11 00 00 00 59 06 5A FE" },{ "caption": "Config - FLJ Full Power", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 66 11 00 00 00 37 06 5A FE" }, { "caption": "Config - FLJ Silent", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 00 11 00 00 00 D1 06 5A FE" },{ "caption": "Config - LJ Low Power", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 88 00 00 00 00 48 06 5A FE" },{ "caption": "Config - LJ Full Power", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 66 00 00 00 00 26 06 5A FE" },{ "caption": "Config - LJ Silent", "icon": "fa5.play-circle", "data": "5A 54 E3 DD 00 00 00 00 00 C0 06 5A FE" },{ "caption": "StatusCheck - FLJ", "icon": "fa5.play-circle", "data": "5A 54 E6 DD 99 00 5C 03 5A FE" },{ "caption": "StatusCheck - LJ", "icon": "fa5.play-circle", "data": "5A 54 F6 DD 99 00 6C 03 5A FE" },{ "caption": "DataRetrieve - FLJ", "icon": "fa5.play-circle", "data": "5A 54 E5 DD 77 00 39 03 5A FE" },{ "caption": "DataRetrieve - LJ", "icon": "fa5.play-circle", "data": "5A 54 F5 DD 77 00 49 03 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"电压监测: {bin(payload[hwOffset+6])[2:].zfill(8)}\n" ret += f"射频温度: {payload[hwOffset+7]:>4d}\n" ret += f"锁定指示: {bin(payload[hwOffset+8])[2:].zfill(8)}\n" ret += f"MGC 1~2: {payload[hwOffset+9] & 0x0f:>2d} - {payload[hwOffset+9] >> 4:>2d}\n" ret += f"MGC 3~4: {payload[hwOffset+10] & 0x0f:>2d} - {payload[hwOffset+10] >> 4:>2d}\n" ret += "--------------------------------\n" return ret def printFrame(self, data: bytes): ret = 'Retrieved Data Frame:\n' 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 += "节点类型: " ret += "K频段(0xDD)\n" if payload[1] == 0xDD else "S频段(0xDD)\n" ret += f"时间戳: {payload[2]/1e2:.2f} us\n" sysbusOffset = 3 linkOffset = 7 phyOffset = 30 hwOffset = 43 ret += "\n -- 总线状态\n" ret += f"配置装订: {payload[sysbusOffset + 0]:04X}\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]:>2d}\n" ret += "通信波束宽度: " if payload[linkOffset + 7] == 0x00: ret += f"8° 窄波束 (0x{payload[linkOffset + 7]:02X})\n" elif payload[linkOffset + 7] == 0x10: ret += f"25° 次波束 (0x{payload[linkOffset + 7]:02X})\n" elif payload[linkOffset + 7] == 0x20: ret += f"60° 次波束 (0x{payload[linkOffset + 7]:02X})\n" elif payload[linkOffset + 7] == 0x30: ret += f"120° 宽波束 (0x{payload[linkOffset + 7]:02X})\n" else: ret += f"【未知错误】(0x{payload[linkOffset + 7]:02X})\n" ret += f"离轴角: {payload[linkOffset + 8]*0.1:.2f}°\n" ret += f"偏航角: {payload[linkOffset + 9]*0.1:.2f}°\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"时间偏差记录: {payload[linkOffset + 19]/1e2:.2f} us\n" ret += f"发送数据 ID: {payload[linkOffset + 20]:>6d}\n" ret += f"接收数据 ID: {payload[linkOffset + 21]:>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 += "\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"电压监测: {bin(payload[hwOffset+6])[2:].zfill(8)}\n" ret += f"射频温度: {payload[hwOffset+7]:>4d}\n" ret += f"锁定指示: {bin(payload[hwOffset+8])[2:].zfill(8)}\n" ret += f"MGC 1~2: {payload[hwOffset+9] & 0x0f:>2d} - {payload[hwOffset+9] >> 4:>2d}\n" ret += f"MGC 3~4: {payload[hwOffset+10] & 0x0f:>2d} - {payload[hwOffset+10] >> 4:>2d}\n" ret += "--------------------------------\n" return ret 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)