From 5cabf2ab657ed1369a133135d1c2fae2f6f4920a Mon Sep 17 00:00:00 2001 From: Gatsby Van Date: Thu, 22 Feb 2024 16:17:43 +0800 Subject: [PATCH] add comtool python plugin --- comtool-plugin-duifei.py | 388 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 388 insertions(+) create mode 100644 comtool-plugin-duifei.py diff --git a/comtool-plugin-duifei.py b/comtool-plugin-duifei.py new file mode 100644 index 0000000..882e797 --- /dev/null +++ b/comtool-plugin-duifei.py @@ -0,0 +1,388 @@ +''' + @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('Menlo,Consolas,Bitstream Vera 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 = '' + for i in data: + ret += f'{i:02X} ' + print(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) \ No newline at end of file