forked from gatsby/df_datalink
feat: 更新图片上位机,修改按钮布局和文字,增加自检回读
图片传输功能由于缺少buffer,会导致缺或多数据,造成图片显示异常;自检回读大概率会卡死导致程序退出
This commit is contained in:
+217
@@ -0,0 +1,217 @@
|
|||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[codz]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# UV
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
#uv.lock
|
||||||
|
|
||||||
|
# poetry
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||||
|
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||||
|
# commonly ignored for libraries.
|
||||||
|
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||||
|
#poetry.lock
|
||||||
|
#poetry.toml
|
||||||
|
|
||||||
|
# pdm
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||||
|
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
||||||
|
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
||||||
|
#pdm.lock
|
||||||
|
#pdm.toml
|
||||||
|
.pdm-python
|
||||||
|
.pdm-build/
|
||||||
|
|
||||||
|
# pixi
|
||||||
|
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
||||||
|
#pixi.lock
|
||||||
|
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
||||||
|
# in the .venv directory. It is recommended not to include this directory in version control.
|
||||||
|
.pixi
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# Redis
|
||||||
|
*.rdb
|
||||||
|
*.aof
|
||||||
|
*.pid
|
||||||
|
|
||||||
|
# RabbitMQ
|
||||||
|
mnesia/
|
||||||
|
rabbitmq/
|
||||||
|
rabbitmq-data/
|
||||||
|
|
||||||
|
# ActiveMQ
|
||||||
|
activemq-data/
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.envrc
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
# PyCharm
|
||||||
|
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||||
|
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||||
|
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||||
|
#.idea/
|
||||||
|
|
||||||
|
# Abstra
|
||||||
|
# Abstra is an AI-powered process automation framework.
|
||||||
|
# Ignore directories containing user credentials, local state, and settings.
|
||||||
|
# Learn more at https://abstra.io/docs
|
||||||
|
.abstra/
|
||||||
|
|
||||||
|
# Visual Studio Code
|
||||||
|
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
||||||
|
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
||||||
|
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
||||||
|
# you could uncomment the following to ignore the entire vscode folder
|
||||||
|
# .vscode/
|
||||||
|
|
||||||
|
# Ruff stuff:
|
||||||
|
.ruff_cache/
|
||||||
|
|
||||||
|
# PyPI configuration file
|
||||||
|
.pypirc
|
||||||
|
|
||||||
|
# Marimo
|
||||||
|
marimo/_static/
|
||||||
|
marimo/_lsp/
|
||||||
|
__marimo__/
|
||||||
|
|
||||||
|
# Streamlit
|
||||||
|
.streamlit/secrets.toml
|
||||||
@@ -0,0 +1,562 @@
|
|||||||
|
# from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QSlider, QLineEdit, QGridLayout, QPushButton, QCheckBox, QHBoxLayout, QInputDialog)
|
||||||
|
# from PyQt5.QtCore import pyqtSignal
|
||||||
|
# from PyQt5.QtGui import QDoubleValidator
|
||||||
|
import os
|
||||||
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QLabel, QSlider, QLineEdit,
|
||||||
|
QGridLayout, QPushButton, QCheckBox, QHBoxLayout, QInputDialog, QComboBox, QFileDialog)
|
||||||
|
from PyQt5.QtCore import pyqtSignal, Qt, QTimer
|
||||||
|
from PyQt5.QtGui import QDoubleValidator, QPixmap
|
||||||
|
|
||||||
|
try:
|
||||||
|
from i18n import _
|
||||||
|
import utils_ui
|
||||||
|
import utils
|
||||||
|
from widgets import EditRemarDialog
|
||||||
|
except Exception:
|
||||||
|
from COMTool.i18n import _
|
||||||
|
from COMTool import utils_ui
|
||||||
|
from COMTool import utils
|
||||||
|
from COMTool.widgets import EditRemarDialog
|
||||||
|
|
||||||
|
# from .graph_widget_metasenselite import Graph_MetaSenseLite
|
||||||
|
from .graph_widgets_base import Graph_Widget_Base
|
||||||
|
import pyqtgraph as pg
|
||||||
|
from struct import unpack, pack
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
class Graph_Image(Graph_Widget_Base):
|
||||||
|
id = "image"
|
||||||
|
updateSignal = pyqtSignal(bytes)
|
||||||
|
startTimeoutSignal = pyqtSignal()
|
||||||
|
stopTimeoutSignal = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self, parent=None, hintSignal=lambda type, title, msg: None,
|
||||||
|
rmCallback=lambda widget: None, send=lambda x: None, config=None):
|
||||||
|
# 初始化配置,默认缩放倍数为10
|
||||||
|
default = {"scale_factor": 10}
|
||||||
|
if config:
|
||||||
|
default.update(config)
|
||||||
|
super().__init__(parent, hintSignal, rmCallback, send, default, default)
|
||||||
|
|
||||||
|
self.image_data = bytearray()
|
||||||
|
# 从配置获取缩放倍数
|
||||||
|
self.scale_factor = self.config.get("scale_factor", 10)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
self.imageLabel = QLabel("[ Waiting for image... ]")
|
||||||
|
self.imageLabel.setAlignment(Qt.AlignCenter)
|
||||||
|
layout.addWidget(self.imageLabel)
|
||||||
|
|
||||||
|
self.clearBtn = QPushButton("Clear Image")
|
||||||
|
self.clearBtn.clicked.connect(self.clear)
|
||||||
|
layout.addWidget(self.clearBtn)
|
||||||
|
|
||||||
|
self.updateSignal.connect(self.update_image)
|
||||||
|
|
||||||
|
self.timeoutTimer = QTimer(self)
|
||||||
|
self.timeoutTimer.setInterval(2000) # 2 seconds timeout
|
||||||
|
self.timeoutTimer.timeout.connect(self.clear_buffer)
|
||||||
|
|
||||||
|
self.startTimeoutSignal.connect(lambda: self.timeoutTimer.start())
|
||||||
|
self.stopTimeoutSignal.emit()
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.image_data.clear()
|
||||||
|
self.imageLabel.clear()
|
||||||
|
self.imageLabel.setText("[ Cleared ]")
|
||||||
|
self.stopTimeoutSignal.emit()
|
||||||
|
|
||||||
|
def clear_buffer(self):
|
||||||
|
self.image_data.clear()
|
||||||
|
self.imageLabel.setText("[ Timeout: buffer cleared ]")
|
||||||
|
self.stopTimeoutSignal.emit()
|
||||||
|
|
||||||
|
def onData(self, data: bytes):
|
||||||
|
self.stopTimeoutSignal.emit() # Stop any running timer when data is received
|
||||||
|
self.image_data.extend(data)
|
||||||
|
|
||||||
|
# Check for common image signatures
|
||||||
|
if self._is_complete_jpeg(self.image_data) or \
|
||||||
|
self._is_complete_png(self.image_data) or \
|
||||||
|
self._is_complete_bmp(self.image_data):
|
||||||
|
self.updateSignal.emit(bytes(self.image_data))
|
||||||
|
self.image_data.clear()
|
||||||
|
else:
|
||||||
|
self.startTimeoutSignal.emit() # Restart timer only if not complete
|
||||||
|
|
||||||
|
def _is_complete_jpeg(self, data):
|
||||||
|
return data[:2] == b'\xFF\xD8' and data[-2:] == b'\xFF\xD9'
|
||||||
|
|
||||||
|
def _is_complete_png(self, data):
|
||||||
|
return data[:8] == b'\x89PNG\r\n\x1a\n' and b'IEND' in data[-20:]
|
||||||
|
|
||||||
|
def _is_complete_bmp(self, data):
|
||||||
|
return data[:2] == b'BM' and len(data) >= int.from_bytes(data[2:6], 'little')
|
||||||
|
|
||||||
|
def update_image(self, img_bytes: bytes):
|
||||||
|
pixmap = QPixmap()
|
||||||
|
if pixmap.loadFromData(img_bytes):
|
||||||
|
# 使用配置的缩放倍数进行缩放
|
||||||
|
scaled_width = pixmap.width() * self.scale_factor
|
||||||
|
scaled_height = pixmap.height() * self.scale_factor
|
||||||
|
scaled = pixmap.scaled(
|
||||||
|
scaled_width,
|
||||||
|
scaled_height,
|
||||||
|
Qt.KeepAspectRatio,
|
||||||
|
Qt.SmoothTransformation
|
||||||
|
)
|
||||||
|
self.imageLabel.setPixmap(scaled)
|
||||||
|
else:
|
||||||
|
self.imageLabel.setText("[ Failed to decode image ]")
|
||||||
|
|
||||||
|
class Graph_Plot(Graph_Widget_Base):
|
||||||
|
updateSignal = pyqtSignal(dict)
|
||||||
|
id = "plot"
|
||||||
|
|
||||||
|
def __init__(self, parent=None, hintSignal=lambda type, title, msg: None, rmCallback=lambda widget: None, send=lambda x: None, config=None):
|
||||||
|
default = {
|
||||||
|
"xRange": 10,
|
||||||
|
"xRangeEnable": True,
|
||||||
|
"header": "\\xAA\\xCC\\xEE\\xBB",
|
||||||
|
"binary_protocol": False,
|
||||||
|
}
|
||||||
|
super().__init__(parent, hintSignal=hintSignal, rmCallback=rmCallback,
|
||||||
|
send=send, config=config, defaultConfig=default)
|
||||||
|
self.headerBytes = utils.str_to_bytes(
|
||||||
|
self.config["header"], escape=True, encoding="utf-8")
|
||||||
|
self.layout = QGridLayout()
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
self.plotWin = pg.GraphicsLayoutWidget()
|
||||||
|
self.plotWin.setMinimumHeight(200)
|
||||||
|
self.default_x = 0;
|
||||||
|
pg.setConfigOptions(antialias=True)
|
||||||
|
rmBtn = QPushButton(_("Remove"))
|
||||||
|
clearBtn = QPushButton(_("Clear"))
|
||||||
|
rangeLabel = QLabel(_("Range:"))
|
||||||
|
rangeConf = QLineEdit(str(self.config["xRange"]))
|
||||||
|
rangeEnable = QCheckBox(_("Enable"))
|
||||||
|
rangeEnable.setChecked(self.config["xRangeEnable"])
|
||||||
|
self.binary_protocol = QCheckBox(_("Binary Protocol"))
|
||||||
|
headerLabel = QLabel(_("Header:"))
|
||||||
|
headerConf = QLineEdit(self.config["header"])
|
||||||
|
self.headerBtn = QPushButton(_("Set"))
|
||||||
|
hint = _("Protocol: header + 1Byte name length + name + 8Bytes x(double) + 8Bytes y(double) + 1Byte sum\n"
|
||||||
|
"Protocol example code see help")
|
||||||
|
headerConf.setToolTip(hint)
|
||||||
|
self.binary_widgets = [headerLabel, headerConf, self.headerBtn]
|
||||||
|
self.binary_protocol.setToolTip(_('Check this if you want to use binary protocol, or use ASCII protocol(e.g. "$data1,10.0,200.0\\n"), more see help button on the top of window'))
|
||||||
|
self.show_binary_protocol_widgets(self.config["binary_protocol"])
|
||||||
|
headerLabel.setToolTip(hint)
|
||||||
|
self.headerBtn.setToolTip(hint)
|
||||||
|
validator = QDoubleValidator()
|
||||||
|
rangeConf.setValidator(validator)
|
||||||
|
self.layout.addWidget(self.plotWin, 0, 0, 1, 3)
|
||||||
|
self.layout.addWidget(rmBtn, 1, 0, 1, 1)
|
||||||
|
self.layout.addWidget(clearBtn, 1, 2, 1, 1)
|
||||||
|
self.layout.addWidget(rangeLabel, 2, 0, 1, 1)
|
||||||
|
self.layout.addWidget(rangeConf, 2, 1, 1, 1)
|
||||||
|
self.layout.addWidget(rangeEnable, 2, 2, 1, 1)
|
||||||
|
self.layout.addWidget(self.binary_protocol, 3, 0, 1, 1)
|
||||||
|
self.layout.addWidget(headerLabel, 4, 0, 1, 1)
|
||||||
|
self.layout.addWidget(headerConf, 4, 1, 1, 1)
|
||||||
|
self.layout.addWidget(self.headerBtn, 4, 2, 1, 1)
|
||||||
|
self.resize(600, 400)
|
||||||
|
self.p = self.plotWin.addPlot(colspan=2)
|
||||||
|
# self.p.setLabel('bottom', 'x', '')
|
||||||
|
self.p.addLegend()
|
||||||
|
self.p.setXRange(0, self.config["xRange"])
|
||||||
|
self.updateSignal.connect(self.update)
|
||||||
|
self.curves = {}
|
||||||
|
self.rawData = b''
|
||||||
|
self.data = {}
|
||||||
|
self.builtinColors = [
|
||||||
|
"#BD4B4B",
|
||||||
|
"#3BB273",
|
||||||
|
"#FFFFFA",
|
||||||
|
"#307473",
|
||||||
|
"#3C6997",
|
||||||
|
"#746D75",
|
||||||
|
"#228CDB",
|
||||||
|
"#824C71",
|
||||||
|
"#7768AE",
|
||||||
|
"#DC6BAD",
|
||||||
|
"#607d8b",
|
||||||
|
"#F18701",
|
||||||
|
"#912F40",
|
||||||
|
"#414288",
|
||||||
|
"#ED4D6E",
|
||||||
|
"#FFD29D",
|
||||||
|
"#B56576",
|
||||||
|
"#503B31",
|
||||||
|
"#93E1D8",
|
||||||
|
"#596157",
|
||||||
|
]
|
||||||
|
self.notUsedColors = self.builtinColors.copy()
|
||||||
|
self.colors = {
|
||||||
|
|
||||||
|
}
|
||||||
|
rangeConf.textChanged.connect(self.setRange)
|
||||||
|
rangeEnable.clicked.connect(
|
||||||
|
lambda: self.setEnableRange(rangeEnable.isChecked()))
|
||||||
|
self.headerBtn.clicked.connect(
|
||||||
|
lambda: self.setHeader(headerConf.text()))
|
||||||
|
rmBtn.clicked.connect(self.remove)
|
||||||
|
headerConf.textChanged.connect(self.headerChanged)
|
||||||
|
clearBtn.clicked.connect(self.clear)
|
||||||
|
self.binary_protocol.clicked.connect(self.en_binary_protocol)
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
self.rmCallback(self)
|
||||||
|
|
||||||
|
def en_binary_protocol(self):
|
||||||
|
self.rawData = b''
|
||||||
|
self.config["binary_protocol"] = self.binary_protocol.isChecked()
|
||||||
|
self.show_binary_protocol_widgets(self.config["binary_protocol"])
|
||||||
|
|
||||||
|
def show_binary_protocol_widgets(self, show=True):
|
||||||
|
for w in self.binary_widgets:
|
||||||
|
w.setVisible(show)
|
||||||
|
|
||||||
|
def clear(self):
|
||||||
|
self.data = {}
|
||||||
|
self.curves = {}
|
||||||
|
self.notUsedColors = self.builtinColors.copy()
|
||||||
|
self.colors = {}
|
||||||
|
self.p.clear()
|
||||||
|
|
||||||
|
def setRange(self, text):
|
||||||
|
if text:
|
||||||
|
self.config["xRange"] = float(text)
|
||||||
|
|
||||||
|
def setEnableRange(self, en):
|
||||||
|
self.config["xRangeEnable"] = en
|
||||||
|
|
||||||
|
def headerChanged(self, text):
|
||||||
|
if self.config["header"] != text:
|
||||||
|
self.headerBtn.setText(_("Set") + " *")
|
||||||
|
else:
|
||||||
|
self.headerBtn.setText(_("Set"))
|
||||||
|
|
||||||
|
def setHeader(self, text):
|
||||||
|
if text:
|
||||||
|
try:
|
||||||
|
textBytes = utils.str_to_bytes(
|
||||||
|
text, escape=True, encoding="utf-8")
|
||||||
|
self.config["header"] = text
|
||||||
|
self.headerBytes = textBytes
|
||||||
|
self.headerBtn.setText(_("Set"))
|
||||||
|
except Exception:
|
||||||
|
self.hintSignal.emit("error", _("Error"), _("Format error"))
|
||||||
|
|
||||||
|
def decodeData(self, data: bytes):
|
||||||
|
'''
|
||||||
|
@data bytes, protocol:
|
||||||
|
| header(4B) | line name len(1B) | line name | x(8B) | y(8B) | sum(1B) |
|
||||||
|
| AA CC EE BB | 1~255 | roll | double | double | uint8 |
|
||||||
|
@return haveFrame, dict {
|
||||||
|
"name": {
|
||||||
|
"x": [],
|
||||||
|
"y": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
# append data
|
||||||
|
self.rawData += data
|
||||||
|
# find header
|
||||||
|
header = self.headerBytes
|
||||||
|
idx = self.rawData.find(header)
|
||||||
|
if idx < 0:
|
||||||
|
return False, self.data
|
||||||
|
self.rawData = self.rawData[idx:]
|
||||||
|
# check data length
|
||||||
|
nameLen = unpack("B", self.rawData[len(header): len(header) + 1])[0]
|
||||||
|
frameLen = len(header) + nameLen + 18 # 5 + nameLen + 16 + 1
|
||||||
|
if len(self.rawData) < frameLen:
|
||||||
|
return False, self.data
|
||||||
|
# get data
|
||||||
|
frame = self.rawData[:frameLen]
|
||||||
|
self.rawData = self.rawData[frameLen:]
|
||||||
|
_sum = frame[-1]
|
||||||
|
# check sum
|
||||||
|
if _sum != sum(frame[:frameLen - 1]) % 256:
|
||||||
|
return True, self.data
|
||||||
|
name = frame[len(header) + 1: len(header) +
|
||||||
|
1 + nameLen].decode("utf-8")
|
||||||
|
x = unpack("d", frame[-17:-9])[0]
|
||||||
|
y = unpack("d", frame[-9:-1])[0]
|
||||||
|
if not name in self.data:
|
||||||
|
self.data[name] = {
|
||||||
|
"x": [x],
|
||||||
|
"y": [y]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
self.data[name]["x"].append(x)
|
||||||
|
self.data[name]["y"].append(y)
|
||||||
|
return True, self.data
|
||||||
|
|
||||||
|
def decodeDataAscii(self, data: bytes):
|
||||||
|
'''
|
||||||
|
@data bytes, protocol:
|
||||||
|
$[line name],[x],[y]<,checksum>\n
|
||||||
|
or $[line name],[y]
|
||||||
|
"$" means start of frame, "," means separator,
|
||||||
|
checksum is optional, checksum is sum of all bytes in frame except ",checksum".
|
||||||
|
e.g.
|
||||||
|
"$roll,2.0\n"
|
||||||
|
"$roll,1.0,2.0\n"
|
||||||
|
"$pitch,1.0,2.0\r\n"
|
||||||
|
"$pitch,1.0,2.0,179\n" , the 179 = sum(ord(c) for c in "$pitch,1.0,2.0") % 256
|
||||||
|
@return haveFrame, dict {
|
||||||
|
"name": {
|
||||||
|
"x": [],
|
||||||
|
"y": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
# append data
|
||||||
|
self.rawData += data
|
||||||
|
# find header
|
||||||
|
header = b"$"
|
||||||
|
end = b"\n"
|
||||||
|
idx = self.rawData.find(header)
|
||||||
|
if idx < 0:
|
||||||
|
return False, self.data
|
||||||
|
self.rawData = self.rawData[idx:]
|
||||||
|
# find \n
|
||||||
|
idx = self.rawData.find(end)
|
||||||
|
if idx < 0:
|
||||||
|
return False, self.data
|
||||||
|
# get data
|
||||||
|
frame = self.rawData[1:idx] # pitch,1.0,2.0 pitch,1.0,2.0,179
|
||||||
|
self.rawData = self.rawData[idx + 1:]
|
||||||
|
items = frame.split(b",")
|
||||||
|
if len(items) != 2 and len(items) != 3 and len(items) != 4:
|
||||||
|
print("-- format error, frame item len(%s, should 3 or 4) error: %s" % (len(items), frame))
|
||||||
|
return False, self.data
|
||||||
|
try:
|
||||||
|
if len(items) == 2:
|
||||||
|
x = float(self.default_x)
|
||||||
|
y = float(items[1])
|
||||||
|
self.default_x += 1
|
||||||
|
else:
|
||||||
|
x = float(items[1])
|
||||||
|
y = float(items[2])
|
||||||
|
except Exception:
|
||||||
|
print("-- x or y format error, frame: %s" % frame)
|
||||||
|
return False, self.data
|
||||||
|
# checksum
|
||||||
|
if len(items) == 4:
|
||||||
|
try:
|
||||||
|
_sum = int(items[3])
|
||||||
|
except Exception:
|
||||||
|
print("-- checksum format error({} not int), frame: {}".format(items[3], frame))
|
||||||
|
return False, self.data
|
||||||
|
# get last , index
|
||||||
|
idx = frame.rfind(b",")
|
||||||
|
if sum(header + frame[:idx]) % 256 != _sum:
|
||||||
|
print("-- checksum error")
|
||||||
|
return False, self.data
|
||||||
|
name = items[0].decode("utf-8")
|
||||||
|
if not name in self.data:
|
||||||
|
self.data[name] = {
|
||||||
|
"x": [x],
|
||||||
|
"y": [y]
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
self.data[name]["x"].append(x)
|
||||||
|
self.data[name]["y"].append(y)
|
||||||
|
return True, self.data
|
||||||
|
|
||||||
|
def pickColor(self, name: str):
|
||||||
|
if name in self.colors:
|
||||||
|
return self.colors[name]
|
||||||
|
else:
|
||||||
|
if not self.notUsedColors:
|
||||||
|
self.notUsedColors = self.builtinColors.copy()
|
||||||
|
color = self.notUsedColors.pop(0)
|
||||||
|
self.colors[name] = color
|
||||||
|
return color
|
||||||
|
|
||||||
|
def update(self, data: dict):
|
||||||
|
for k, v in data.items():
|
||||||
|
if not k in self.curves:
|
||||||
|
print("add curve:", k)
|
||||||
|
color = self.pickColor(k)
|
||||||
|
self.colors[k] = color
|
||||||
|
self.curves[k] = self.p.plot(pen=pg.mkPen(color=color, width=2),
|
||||||
|
name=k,)
|
||||||
|
# symbolBrush=color, symbolPen='w', symbol='o', symbolSize=1)
|
||||||
|
if self.config["xRangeEnable"] and self.config["xRange"] > 0:
|
||||||
|
self.p.setXRange(
|
||||||
|
v["x"][-1] - self.config["xRange"], v["x"][-1])
|
||||||
|
self.curves[k].setData(x=v["x"], y=v["y"])
|
||||||
|
|
||||||
|
def onData(self, data: bytes):
|
||||||
|
while 1:
|
||||||
|
haveFrame, allData = self.decodeData(data) if self.config["binary_protocol"] else self.decodeDataAscii(data)
|
||||||
|
if not haveFrame:
|
||||||
|
break
|
||||||
|
data = b''
|
||||||
|
self.updateSignal.emit(allData)
|
||||||
|
|
||||||
|
|
||||||
|
class Graph_Button(Graph_Widget_Base):
|
||||||
|
class Button(QWidget):
|
||||||
|
def __init__(self, rmCallback, addCallback, clickCallback, changeCallback,
|
||||||
|
value=("", "hello", "hello", "fa.circle", [])):
|
||||||
|
super().__init__()
|
||||||
|
if not value[0]:
|
||||||
|
self.id = str(int(time.time() * 1000))
|
||||||
|
else:
|
||||||
|
self.id = value[0]
|
||||||
|
self.rmCallback = rmCallback
|
||||||
|
self.changeCallback = changeCallback
|
||||||
|
self.setProperty("class", "graphBtn")
|
||||||
|
layout = QHBoxLayout()
|
||||||
|
layoutSetting = QVBoxLayout()
|
||||||
|
self.setLayout(layout)
|
||||||
|
self.button = QPushButton(value[2])
|
||||||
|
self.button.setProperty("class", "bigBtn")
|
||||||
|
self.value = value[2]
|
||||||
|
self.icon = value[3]
|
||||||
|
self.shortcut = value[4]
|
||||||
|
layout.addLayout(layoutSetting)
|
||||||
|
layout.addWidget(self.button)
|
||||||
|
self.addBtn = QPushButton()
|
||||||
|
self.rmBtn = QPushButton()
|
||||||
|
self.editBtn = QPushButton()
|
||||||
|
self.addBtn.setProperty("class", "smallBtn3")
|
||||||
|
self.rmBtn.setProperty("class", "smallBtn3")
|
||||||
|
self.editBtn.setProperty("class", "smallBtn3")
|
||||||
|
utils_ui.setButtonIcon(self.addBtn, "fa.plus")
|
||||||
|
utils_ui.setButtonIcon(self.rmBtn, "fa.minus")
|
||||||
|
utils_ui.setButtonIcon(self.editBtn, "fa.edit")
|
||||||
|
if self.icon:
|
||||||
|
utils_ui.setButtonIcon(self.button, self.icon)
|
||||||
|
if self.shortcut:
|
||||||
|
tip = self.getShortcutTip()
|
||||||
|
self.button.setToolTip(tip)
|
||||||
|
layoutSetting.addStretch()
|
||||||
|
layoutSetting.addWidget(self.rmBtn)
|
||||||
|
layoutSetting.addWidget(self.addBtn)
|
||||||
|
layoutSetting.addWidget(self.editBtn)
|
||||||
|
self.rmBtn.clicked.connect(self.onRm)
|
||||||
|
self.addBtn.clicked.connect(lambda: addCallback(self))
|
||||||
|
self.editBtn.clicked.connect(self.editButton)
|
||||||
|
self.button.clicked.connect(lambda: clickCallback(
|
||||||
|
self, utils.str_to_bytes(self.value, escape=True, encoding="utf-8")))
|
||||||
|
|
||||||
|
def getInfo(self):
|
||||||
|
return [self.id, self.button.text(), self.value, self.icon, self.shortcut]
|
||||||
|
|
||||||
|
def onRm(self):
|
||||||
|
utils_ui.clearButtonIcon(self.addBtn)
|
||||||
|
utils_ui.clearButtonIcon(self.rmBtn)
|
||||||
|
utils_ui.clearButtonIcon(self.editBtn)
|
||||||
|
utils_ui.clearButtonIcon(self.button)
|
||||||
|
self.rmCallback(self)
|
||||||
|
|
||||||
|
def editButton(self):
|
||||||
|
ok, remark, value, icon, shortcut = EditRemarDialog(
|
||||||
|
self.button.text(), self.icon, shortcut=self.shortcut, value=self.value).exec()
|
||||||
|
if ok:
|
||||||
|
self.value = value
|
||||||
|
self.icon = icon
|
||||||
|
self.shortcut = shortcut
|
||||||
|
self.button.setText(remark)
|
||||||
|
if self.shortcut:
|
||||||
|
tip = self.getShortcutTip()
|
||||||
|
self.button.setToolTip(tip)
|
||||||
|
utils_ui.setButtonIcon(self.button, icon)
|
||||||
|
self.changeCallback(self)
|
||||||
|
|
||||||
|
def getShortcutTip(self):
|
||||||
|
return _("Shortcut") + ": " + "+".join((name for v, name in self.shortcut))
|
||||||
|
|
||||||
|
id = "button"
|
||||||
|
|
||||||
|
def __init__(self, parent=None, hintSignal=lambda type, title, msg: None, rmCallback=lambda widget: None, send=lambda x: None, config=None):
|
||||||
|
default = {
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
|
super().__init__(parent, hintSignal, rmCallback, send, config, default)
|
||||||
|
self.layout = QHBoxLayout()
|
||||||
|
self.setLayout(self.layout)
|
||||||
|
if not self.config["items"]:
|
||||||
|
button = Graph_Button.Button(
|
||||||
|
self.onRm, self.onAdd, self.onClick, self.onChange)
|
||||||
|
self.layout.addWidget(button)
|
||||||
|
self.buttons = [button]
|
||||||
|
self.config["items"] = [button.getInfo()]
|
||||||
|
else:
|
||||||
|
self.buttons = []
|
||||||
|
for item in self.config["items"]:
|
||||||
|
button = Graph_Button.Button(
|
||||||
|
self.onRm, self.onAdd, self.onClick, self.onChange, value=item)
|
||||||
|
self.layout.addWidget(button)
|
||||||
|
self.buttons.append(button)
|
||||||
|
self.pressedKeys = []
|
||||||
|
|
||||||
|
def onRm(self, widget):
|
||||||
|
'''
|
||||||
|
@widget Graph_Button.Button
|
||||||
|
'''
|
||||||
|
item = widget.getInfo()
|
||||||
|
for item_ in self.config["items"]:
|
||||||
|
if item_[0] == item[0]: # id
|
||||||
|
self.config["items"].remove(item_)
|
||||||
|
break
|
||||||
|
self.buttons.remove(widget)
|
||||||
|
widget.setParent(None)
|
||||||
|
if not self.buttons:
|
||||||
|
self.rmCallback(self)
|
||||||
|
|
||||||
|
def onAdd(self, widget):
|
||||||
|
'''
|
||||||
|
@widget Graph_Button.Button
|
||||||
|
'''
|
||||||
|
button = Graph_Button.Button(
|
||||||
|
self.onRm, self.onAdd, self.onClick, self.onChange)
|
||||||
|
self.layout.addWidget(button)
|
||||||
|
self.buttons.append(button)
|
||||||
|
self.config["items"].append(button.getInfo())
|
||||||
|
|
||||||
|
def onClick(self, widget, data: bytes):
|
||||||
|
self.send(data)
|
||||||
|
|
||||||
|
def onChange(self, widget):
|
||||||
|
self.config["items"][self.buttons.index(widget)] = widget.getInfo()
|
||||||
|
|
||||||
|
def onKeyPressEvent(self, event):
|
||||||
|
key = event.key()
|
||||||
|
self.pressedKeys.append(key)
|
||||||
|
for uid, name, value, icon, shortcut in self.config["items"]:
|
||||||
|
if not shortcut:
|
||||||
|
continue
|
||||||
|
if len(shortcut) == len(self.pressedKeys):
|
||||||
|
same = True
|
||||||
|
for i in range(len(shortcut)):
|
||||||
|
if shortcut[i][0] != self.pressedKeys[i]:
|
||||||
|
same = False
|
||||||
|
break
|
||||||
|
if same:
|
||||||
|
value = utils.str_to_bytes(
|
||||||
|
value, escape=True, encoding="utf-8")
|
||||||
|
self.send(value)
|
||||||
|
|
||||||
|
def onKeyReleaseEvent(self, event):
|
||||||
|
key = event.key()
|
||||||
|
if key in self.pressedKeys:
|
||||||
|
self.pressedKeys.remove(key)
|
||||||
|
|
||||||
|
|
||||||
|
graphWidgets = {
|
||||||
|
Graph_Plot.id: Graph_Plot,
|
||||||
|
Graph_Button.id: Graph_Button,
|
||||||
|
Graph_Image.id: Graph_Image
|
||||||
|
# Graph_DragTof.id: Graph_DragTof,
|
||||||
|
# Graph_MetaSenseLite.id: Graph_MetaSenseLite,
|
||||||
|
}
|
||||||
+140
-155
@@ -30,8 +30,8 @@ except ImportError:
|
|||||||
|
|
||||||
class Plugin(Plugin_Base):
|
class Plugin(Plugin_Base):
|
||||||
|
|
||||||
id = "hsjl_time"
|
id = "hsjl_timer"
|
||||||
name = _("hsjl_time")
|
name = _("hsjl_timer")
|
||||||
showReceiveDataSignal = pyqtSignal(str)
|
showReceiveDataSignal = pyqtSignal(str)
|
||||||
# 图片1的字节数据(已存在,提取为常量)
|
# 图片1的字节数据(已存在,提取为常量)
|
||||||
IMAGE1_DATA = [
|
IMAGE1_DATA = [
|
||||||
@@ -72,9 +72,11 @@ class Plugin(Plugin_Base):
|
|||||||
"version": 1,
|
"version": 1,
|
||||||
"customSendItems": [
|
"customSendItems": [
|
||||||
{
|
{
|
||||||
"caption": "遥测",
|
"caption": "自检项",
|
||||||
"icon": "fa5.play-circle",
|
"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": "图片传输",
|
"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"
|
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",
|
"icon": "fa5.play-circle",
|
||||||
"data": "5A 54 E3 EE AA 03 22 00 00 A0 06 5A FE"
|
"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:
|
for k in default:
|
||||||
if k not in self.config:
|
if k not in self.config:
|
||||||
self.config[k] = default[k]
|
self.config[k] = default[k]
|
||||||
|
self.config["customSendItems"] = default["customSendItems"]
|
||||||
|
print("初始化的customSendItems:", self.config["customSendItems"])
|
||||||
|
|
||||||
def createToggleButton(self):
|
def createToggleButton(self):
|
||||||
toggleBtn = QPushButton("开关: Off")
|
toggleBtn = QPushButton("开关: Off")
|
||||||
@@ -119,31 +123,58 @@ class Plugin(Plugin_Base):
|
|||||||
return toggleBtn
|
return toggleBtn
|
||||||
|
|
||||||
def createIdButton(self):
|
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)
|
menu = QMenu(toggleBtn)
|
||||||
ids = ["01", "02", "03"]
|
|
||||||
|
|
||||||
def setId(text):
|
def setId(original_id):
|
||||||
toggleBtn.setText(f"id: {text}")
|
# 更新显示文本(带标识)
|
||||||
print(f"id设为 {text}")
|
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:
|
# 菜单显示带标识的文本,但传递原始ID
|
||||||
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)
|
toggleBtn.setMenu(menu)
|
||||||
return toggleBtn
|
return toggleBtn
|
||||||
|
|
||||||
def createtoIdButton(self):
|
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)
|
menu = QMenu(toggleBtn)
|
||||||
ids = ["01", "02", "03"]
|
|
||||||
|
|
||||||
def setId(text):
|
def setId(original_id):
|
||||||
toggleBtn.setText(f"目的节点id: {text}")
|
toggleBtn.setText(f"目的节点id: {id_display_map[original_id]}")
|
||||||
print(f"目的节点id设为 {text}")
|
toggleBtn.setProperty("originalId", original_id)
|
||||||
|
print(f"目的节点id设为 {original_id}")
|
||||||
|
|
||||||
for id_text in ids:
|
for original_id, display_text in id_display_map.items():
|
||||||
menu.addAction(id_text).triggered.connect(lambda checked, t=id_text: setId(t))
|
menu.addAction(display_text).triggered.connect(
|
||||||
|
lambda checked, id=original_id: setId(id)
|
||||||
|
)
|
||||||
|
|
||||||
toggleBtn.setMenu(menu)
|
toggleBtn.setMenu(menu)
|
||||||
return toggleBtn
|
return toggleBtn
|
||||||
@@ -197,9 +228,11 @@ class Plugin(Plugin_Base):
|
|||||||
|
|
||||||
# ID字段,转为整数
|
# ID字段,转为整数
|
||||||
try:
|
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:
|
except:
|
||||||
id_val = 0x00
|
id_val = 0x00 # 异常时默认值
|
||||||
frame.append(id_val)
|
frame.append(id_val)
|
||||||
|
|
||||||
# 速率字段对应映射(参考原来速率的0x22等)
|
# 速率字段对应映射(参考原来速率的0x22等)
|
||||||
@@ -240,9 +273,10 @@ class Plugin(Plugin_Base):
|
|||||||
|
|
||||||
# ID字段,转为整数
|
# ID字段,转为整数
|
||||||
try:
|
try:
|
||||||
id_val = int(toid_text.split(":")[1].strip(), 16)
|
original_toid = self.toidButton.property("originalId")
|
||||||
|
id_val = int(original_toid, 16)
|
||||||
except:
|
except:
|
||||||
id_val = 0x01
|
id_val = 0x01 # 异常时默认值
|
||||||
frame.append(id_val)
|
frame.append(id_val)
|
||||||
|
|
||||||
# 根据索引选择图片数据
|
# 根据索引选择图片数据
|
||||||
@@ -381,19 +415,19 @@ class Plugin(Plugin_Base):
|
|||||||
self.mainWidget.addWidget(sendItemsOuterWrapper)
|
self.mainWidget.addWidget(sendItemsOuterWrapper)
|
||||||
|
|
||||||
# 网络状态标签初始化
|
# 网络状态标签初始化
|
||||||
self.networkStatusLabel = QLabel("网络连接状态: 未连接")
|
self.networkStatusLabel = QLabel("节点状态: 未知")
|
||||||
self.networkStatusLabel.setAlignment(Qt.AlignLeft)
|
self.networkStatusLabel.setAlignment(Qt.AlignLeft)
|
||||||
self.networkStatusLabel.setFont(QFont("Arial", 10))
|
self.networkStatusLabel.setFont(QFont("Arial", 10))
|
||||||
|
|
||||||
self.networkPhaseLabel = QLabel("网络运行阶段: 未知")
|
self.networkPhaseLabel = QLabel("天线温度: 未知")
|
||||||
self.networkPhaseLabel.setAlignment(Qt.AlignLeft)
|
self.networkPhaseLabel.setAlignment(Qt.AlignLeft)
|
||||||
self.networkPhaseLabel.setFont(QFont("Arial", 10))
|
self.networkPhaseLabel.setFont(QFont("Arial", 10))
|
||||||
|
|
||||||
self.networktemLabel = QLabel("fpga温度: 未知")
|
self.networktemLabel = QLabel("电压状态: 未知")
|
||||||
self.networktemLabel.setAlignment(Qt.AlignLeft)
|
self.networktemLabel.setAlignment(Qt.AlignLeft)
|
||||||
self.networktemLabel.setFont(QFont("Arial", 10))
|
self.networktemLabel.setFont(QFont("Arial", 10))
|
||||||
|
|
||||||
self.networkvoltageLabel = QLabel("电压监测: 未知")
|
self.networkvoltageLabel = QLabel("fgpa温度: 未知")
|
||||||
self.networkvoltageLabel.setAlignment(Qt.AlignLeft)
|
self.networkvoltageLabel.setAlignment(Qt.AlignLeft)
|
||||||
self.networkvoltageLabel.setFont(QFont("Arial", 10))
|
self.networkvoltageLabel.setFont(QFont("Arial", 10))
|
||||||
|
|
||||||
@@ -434,34 +468,32 @@ class Plugin(Plugin_Base):
|
|||||||
return itemWidget
|
return itemWidget
|
||||||
|
|
||||||
def onUiInitDone(self):
|
def onUiInitDone(self):
|
||||||
# 去掉自检项
|
|
||||||
self.config["customSendItems"] = [item for item in self.config["customSendItems"] if item["caption"] != "自检"]
|
|
||||||
|
|
||||||
# 清空旧布局
|
# 清空旧布局
|
||||||
self.clearLayout(self.customSendItemsLayout)
|
self.clearLayout(self.customSendItemsLayout)
|
||||||
|
|
||||||
# 创建“开关”、“ID”、“时隙表”、“速率”按钮,供“总控”使用
|
# 创建“开关”、“ID”、“时隙表”、“速率”按钮,供“总控”使用
|
||||||
self.toggleButton = self.createToggleButton()
|
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.idButton = self.createIdButton()
|
||||||
self.customSendItemsLayout.addWidget(self.idButton, 1, 1)
|
self.customSendItemsLayout.addWidget(self.idButton, 0, 1)
|
||||||
|
|
||||||
self.slotButton = self.createSlotButton()
|
self.slotButton = self.createSlotButton()
|
||||||
self.customSendItemsLayout.addWidget(self.slotButton, 1, 2)
|
self.customSendItemsLayout.addWidget(self.slotButton, 0, 2)
|
||||||
|
|
||||||
self.rateButton = self.createRateButton()
|
self.rateButton = self.createRateButton()
|
||||||
self.customSendItemsLayout.addWidget(self.rateButton, 1, 3)
|
self.customSendItemsLayout.addWidget(self.rateButton, 0, 0)
|
||||||
|
|
||||||
self.toidButton = self.createtoIdButton()
|
self.toidButton = self.createtoIdButton()
|
||||||
self.customSendItemsLayout.addWidget(self.toidButton, 0, 3)
|
self.customSendItemsLayout.addWidget(self.toidButton, 0, 3)
|
||||||
|
|
||||||
# 第一行固定插入“遥测”、“图片传输”、“总控”
|
# 第一行固定插入“自检”、“图片传输”、“总控”
|
||||||
for item in self.config["customSendItems"]:
|
for item in self.config["customSendItems"]:
|
||||||
if item["caption"] == "遥测":
|
if item["caption"] == "自检项":
|
||||||
|
print("找到自检项,准备添加到(0,0)") # 新增
|
||||||
w = self.createSendItemWidget(item)
|
w = self.createSendItemWidget(item)
|
||||||
self.customSendItemsLayout.addWidget(w, 0, 0)
|
self.customSendItemsLayout.addWidget(w, 3, 0, 1, 2)
|
||||||
|
|
||||||
elif item["caption"] == "图片传输":
|
elif item["caption"] == "图片传输":
|
||||||
totalControlWidget = QWidget()
|
totalControlWidget = QWidget()
|
||||||
layout = QHBoxLayout()
|
layout = QHBoxLayout()
|
||||||
@@ -545,16 +577,16 @@ class Plugin(Plugin_Base):
|
|||||||
|
|
||||||
layout.addWidget(label)
|
layout.addWidget(label)
|
||||||
layout.addWidget(sendBtn)
|
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()
|
totalControlWidget = QWidget()
|
||||||
layout = QHBoxLayout()
|
layout = QHBoxLayout()
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
totalControlWidget.setLayout(layout)
|
totalControlWidget.setLayout(layout)
|
||||||
|
|
||||||
sendBtn = QPushButton("")
|
sendBtn = QPushButton("")
|
||||||
label = QLabel("总控")
|
label = QLabel("配置发送")
|
||||||
utils_ui.setButtonIcon(sendBtn, item.get("icon", "fa.send"))
|
utils_ui.setButtonIcon(sendBtn, item.get("icon", "fa.send"))
|
||||||
sendBtn.setProperty("class", "smallBtn")
|
sendBtn.setProperty("class", "smallBtn")
|
||||||
|
|
||||||
@@ -568,7 +600,7 @@ class Plugin(Plugin_Base):
|
|||||||
# 动态构造指令字符串S
|
# 动态构造指令字符串S
|
||||||
|
|
||||||
cmd_str = self.buildBindCommand(switch_text, id_text, rate_text, slot_text)
|
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,
|
data_bytes = self.parseSendData(cmd_str,
|
||||||
encoding=self.configGlobal["encoding"],
|
encoding=self.configGlobal["encoding"],
|
||||||
@@ -579,16 +611,13 @@ class Plugin(Plugin_Base):
|
|||||||
|
|
||||||
layout.addWidget(label)
|
layout.addWidget(label)
|
||||||
layout.addWidget(sendBtn)
|
layout.addWidget(sendBtn)
|
||||||
self.customSendItemsLayout.addWidget(totalControlWidget, 0, 2)
|
self.customSendItemsLayout.addWidget(totalControlWidget, 1, 2, 1, 2)
|
||||||
# 第三行放4个状态标签
|
# 第三行放4个状态标签
|
||||||
self.customSendItemsLayout.addWidget(self.networkStatusLabel, 2, 0)
|
self.customSendItemsLayout.addWidget(self.networkStatusLabel, 2, 0)
|
||||||
self.customSendItemsLayout.addWidget(self.networkPhaseLabel, 2, 1)
|
self.customSendItemsLayout.addWidget(self.networkPhaseLabel, 2, 1)
|
||||||
self.customSendItemsLayout.addWidget(self.networkvoltageLabel, 2, 2)
|
self.customSendItemsLayout.addWidget(self.networkvoltageLabel, 2, 2)
|
||||||
self.customSendItemsLayout.addWidget(self.networktemLabel, 2, 3)
|
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):
|
def insertSendItem(self, item = None, load = False):
|
||||||
caption = item["caption"]
|
caption = item["caption"]
|
||||||
@@ -661,8 +690,8 @@ class Plugin(Plugin_Base):
|
|||||||
result = self.evalimageTestRsp(binary_data)
|
result = self.evalimageTestRsp(binary_data)
|
||||||
self.showReceiveDataSignal.emit(result)
|
self.showReceiveDataSignal.emit(result)
|
||||||
return
|
return
|
||||||
elif cmd_id == 0xE6: # 遥测回令
|
elif cmd_id == 0xE1: # 自检回令
|
||||||
result = self.evalStatusCheckRsp(binary_data)
|
result = self.evalSelfTestRsp(binary_data)
|
||||||
self.showReceiveDataSignal.emit(result)
|
self.showReceiveDataSignal.emit(result)
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
@@ -700,130 +729,80 @@ class Plugin(Plugin_Base):
|
|||||||
ret += '\n'
|
ret += '\n'
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def evalStatusCheckRsp(self, data: bytes):
|
def evalSelfTestRsp(self, data: bytes):
|
||||||
|
|
||||||
|
print('“”“”“”“”“”“”“”“”“”“”“”“')
|
||||||
try:
|
try:
|
||||||
payload = self.structStatusCheckRsp.unpack(data)
|
# 1. 先解析头部获取数据长度和图片类型标识
|
||||||
except :
|
header_struct = Struct("<2s B B") # 前11字节结构
|
||||||
return f"\n### evalStatusCheckRsp:: Failed to unpack data (data length {len(data)} byte).\n"
|
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 += "节点类型: "
|
ret += "节点状态: "
|
||||||
if payload[1] == 0xEE:
|
if payload[3] == 0xAA:
|
||||||
ret += f"C频段 (0x{payload[1]:02X})\n"
|
ret += f"正常 (0x{payload[3]:02X})\n"
|
||||||
|
network_status='正常'
|
||||||
|
elif payload[6] == 0x55:
|
||||||
|
ret += f"异常 (0x{payload[3]:02X})\n"
|
||||||
|
network_status='异常'
|
||||||
else:
|
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"
|
hwOffset = 4
|
||||||
|
ret += f"FPGA 电压监测: {bin(payload[hwOffset+0])[2:].zfill(8)}\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"
|
|
||||||
ret += f"时钟锁定状态: {bin(payload[hwOffset+1])[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 锁定状态: {bin(payload[hwOffset+2])[2:].zfill(8)}\n"
|
||||||
ret += f"AD 温度 1: {payload[hwOffset+3]:>4d}\n"
|
ret += f"AD 温度 1: {payload[hwOffset+3]:>4d}\n"
|
||||||
ret += f"AD 温度 2: {payload[hwOffset+4]:>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 工作 PLL: {(payload[hwOffset+6] >> 15) & 0x01:>1d}\n"
|
||||||
ret += f"天线-1 工作频率: {(payload[hwOffset+6] & 0x7FFF)+3700:>4d}\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 工作 PLL: {(payload[hwOffset+7] >> 15) & 0x01:>1d}\n"
|
||||||
ret += f"天线-2 工作频率: {(payload[hwOffset+7] & 0x7FFF)+3700:>4d}\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 工作 PLL: {(payload[hwOffset+8] >> 15) & 0x01:>1d}\n"
|
||||||
ret += f"天线-3 工作频率: {(payload[hwOffset+8] & 0x7FFF)+3700:>4d}\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"
|
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"天线工作模式: {bin(payload[hwOffset+10])[2:].zfill(8)}\n"
|
||||||
ret += f"天线温度: {payload[hwOffset+11]:>4d}\n"
|
ret += f"天线温度: {payload[hwOffset+11]:>4d}\n"
|
||||||
print('+')
|
print(payload[3])
|
||||||
print(voltage)
|
|
||||||
print('_')
|
|
||||||
self.networkvoltageLabel.setText(f"电压监测: {voltage}")
|
|
||||||
self.networktemLabel.setText(f"fgpa温度: {temp}")
|
|
||||||
ret += "--------------------------------\n"
|
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)
|
return ret + '\n' + self.printFrame(data)
|
||||||
|
|
||||||
|
|
||||||
def evalimageTestRsp(self, data: bytes):
|
def evalimageTestRsp(self, data: bytes):
|
||||||
print('“”“”“”“”“”“”“”“”“”“”“”“')
|
print('“”“”“”“”“”“”“”“”“”“”“”“')
|
||||||
try:
|
try:
|
||||||
@@ -879,26 +858,32 @@ class Plugin(Plugin_Base):
|
|||||||
ret += f"帧尾: {frame_tail.hex().upper()}\n"
|
ret += f"帧尾: {frame_tail.hex().upper()}\n"
|
||||||
print('&&&&&')
|
print('&&&&&')
|
||||||
|
|
||||||
# 图像显示逻辑(保留原始控件调用方式)
|
# 图像显示逻辑(添加缩放倍数控制)
|
||||||
# 截取有效图像数据
|
# 1. 手动设置缩放倍数(可根据需要修改,例如放大2倍)
|
||||||
|
|
||||||
|
# 图像显示逻辑(修正后)
|
||||||
if from_id == 0x01 or from_id == 0x02:
|
if from_id == 0x01 or from_id == 0x02:
|
||||||
# 图像通道 A → 左侧图像控件
|
# 图像通道 A → 左侧图像控件
|
||||||
if len(self.widgets) >= 1 and isinstance(self.widgets[0], graphWidgets["image"]):
|
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:
|
else:
|
||||||
self.showReceiveDataSignal.emit("\n### No valid image widget found (A1)\n")
|
self.showReceiveDataSignal.emit("\n### No valid image widget found (A1)\n")
|
||||||
print(from_id)
|
|
||||||
elif from_id == 0x03:
|
elif from_id == 0x03:
|
||||||
# 图像通道 B → 右侧图像控件
|
# 图像通道 B → 右侧图像控件
|
||||||
if len(self.widgets) >= 2 and isinstance(self.widgets[1], graphWidgets["image"]):
|
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:
|
else:
|
||||||
self.showReceiveDataSignal.emit("\n### No valid image widget found (A2)\n")
|
self.showReceiveDataSignal.emit("\n### No valid image widget found (A2)\n")
|
||||||
print(from_id)
|
print(from_id)
|
||||||
print('+')
|
print('+')
|
||||||
ret += "------------------------------------\n"
|
ret += "------------------------------------\n"
|
||||||
return ret + '\n' + self.printFrame(data)
|
return ret + '\n' + self.printFrame(data)
|
||||||
|
print(from_id)
|
||||||
|
print('+')
|
||||||
|
ret += "------------------------------------\n"
|
||||||
|
return ret + '\n' + self.printFrame(data)
|
||||||
|
|
||||||
def onSent(self, ok, msg, length, path):
|
def onSent(self, ok, msg, length, path):
|
||||||
if ok:
|
if ok:
|
||||||
|
|||||||
Reference in New Issue
Block a user