Qt QML - QModBus 读取被 QML BusyIndicator/Animation 破坏 - SingleThread
Posted
技术标签:
【中文标题】Qt QML - QModBus 读取被 QML BusyIndicator/Animation 破坏 - SingleThread【英文标题】:Qt QML - QModBus read corrupted by QML BusyIndicator/Animation - SingleThread 【发布时间】:2021-12-12 17:02:55 【问题描述】:我有一个单线程 QQuick 应用程序,它有一个主窗口和一个处理 Modbus 写/读功能的类。到目前为止一切正常,但是当我在我的 qml 窗口中放置 BusyIndicator 以显示总线繁忙时,我得到 CRC 不匹配和响应超时,例如:
“丢弃带有错误 CRC 的响应,收到:64580,计算出的 CRC:55067” “读取响应错误:响应超时。(代码:0x5)” - qt.modbus:(RTU 客户端)无法将响应与打开的请求匹配,忽略main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "modbusinterface.h"
int main(int argc, char *argv[])
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
ModbusInterface modbus;
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl)
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
, Qt::QueuedConnection);
engine.load(url);
engine.rootContext()->setContextProperty("modbus", &modbus);
return app.exec();
modbusinterface.h
#ifndef MODBUSINTERFACE_H
#define MODBUSINTERFACE_H
#include <QObject>
#include <QSerialPort>
#include <QModbusRtuSerialMaster>
#include <QModbusDevice>
#include <QModbusClient>
#include <QVariant>
#include <QDebug>
class ModbusInterface : public QObject
Q_OBJECT
Q_PROPERTY(bool busBusy READ busBusy NOTIFY busBusyChanged)
public:
explicit ModbusInterface(QObject *parent = nullptr);
bool busBusy(void) return m_busBusy;
Q_INVOKABLE bool read(int deviceId, int startAddress, quint16 count);
public slots:
void readReady();
signals:
void busBusyChanged();
private:
bool m_busBusy = false;
QModbusReply *m_lastRequest = nullptr;
QModbusClient *m_client = nullptr;
;
#endif // MODBUSINTERFACE_H
modbusinterface.cpp
#include "modbusinterface.h"
ModbusInterface::ModbusInterface(QObject *parent) : QObject(parent)
m_client = new QModbusRtuSerialMaster();
m_client->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "ttyUSB0");
m_client->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud19200);
m_client->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
m_client->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
m_client->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
m_client->setTimeout(1000);
m_client->setNumberOfRetries(1);
if (!m_client->connectDevice())
qDebug() << "Connect failed: " << m_client->errorString();
else
qDebug() << "Modbus Client is Connected";
bool ModbusInterface::read(int deviceId, int startAddress, quint16 count)
QModbusDataUnit RxData;
if(startAddress>=40000) RxData.setRegisterType(QModbusDataUnit::HoldingRegisters);
else RxData.setRegisterType(QModbusDataUnit::InputRegisters);
RxData.setStartAddress(startAddress);
RxData.setValueCount(count);
if (!m_client)
qDebug() << "!m_client";
return false;
if (m_client->state() != QModbusDevice::ConnectedState)
qDebug() << "Modbus Client is not Connected in read section";
return false;
if (auto *reply = m_client->sendReadRequest(RxData, deviceId))
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &ModbusInterface::readReady);
m_lastRequest = reply;
m_busBusy = true;
emit busBusyChanged();
else
delete reply;
return true;
return false;
void ModbusInterface::readReady()
auto reply = qobject_cast<QModbusReply *>(sender());
if (!reply) return;
if( reply == m_lastRequest)
m_busBusy = false;
emit busBusyChanged();
reply->deleteLater();
if (reply->error() == QModbusDevice::NoError)
qDebug() << reply;
else if (reply->error() == QModbusDevice::ProtocolError)
qDebug() << QString("Read response error: %1 (Mobus exception: 0x%2)").
arg(reply->errorString()).
arg(reply->rawResult().exceptionCode(), -1, 16);
else
qDebug() << QString("Read response error: %1 (code: 0x%2)").
arg(reply->errorString()).
arg(reply->error(), -1, 16);
main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.5
Window
width: 640
height: 480
visible: true
title: qsTr("ModbusTrial")
Button
id: button
anchors.centerIn: parent
text: "read Modbus"
onClicked:
for(var i=1; i<=10; i++)
modbus.read(i, 30001, 1)
BusyIndicator
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: button.bottom
visible: modbus.busBusy //false
在这个最小的工作示例中,我通过我的 modbusinterface 类的 qml/c++ 接口触发/排队 10 个读取请求。当我访问不需要知道哪个服务器地址的设备时,我需要扫描多个 ID 以获取有效响应,并希望在此期间显示一个 busyIndicator。这会导致上述超时/CRC 错误。 如果我的窗口中没有运行动画,则接收到的数据没有错误。
能否通过使用单独的线程运行 modbus 读/写方法来解决此问题,我将如何实现?还是我只会通过将串行函数放在单独的线程中来增加误读?据我所知,由于我的应用程序在单线程中运行,GUI 的持续更新在某种程度上干扰了串行数据的接收。
我使用了 linux 命令行工具“stress”来查看在高 cpu 负载下我是否也会丢失数据,但事实并非如此。
有趣的是,我得到的第一个响应总是有效的。那么像我这样排队请求会不会有问题呢?在文档中它说:
注意:QModbusClient 将它收到的请求排队。并行执行的请求数取决于协议。例如,桌面平台上的 HTTP 协议为一个主机/端口组合发出 6 个并行请求。
亲切的问候
【问题讨论】:
没有minimal reproducible example,我们无法直接回答您的问题。但我肯定会尝试一个单独的线程,看看是否能解决它。 请提供足够的代码,以便其他人更好地理解或重现问题。 “公交车很忙”是什么意思? 我更新了我的代码示例。总线很忙,我的意思是我发送了多个“readRequests”,在处理它们的时间里——有时直到超时发生——我认为总线很忙 如果你正在读取连续寄存器,更改count
会比读取10个单个寄存器更好。
【参考方案1】:
我发现了问题:
我认为 qml 渲染引擎、场景图或任何你称之为的东西都会导致 modbus 接收帧丢失。我想不知何故线程可能对此有所帮助,但我无法通过在单独的线程中运行 modbusInterface 来修复它。
最终解决方案是启用场景图的线程渲染循环,如下所述:https://doc.qt.io/qt-5/qtquick-visualcanvas-scenegraph.html#threaded-render-loop-threaded
即通过放
qputenv("QSG_RENDER_LOOP","threaded");
在我的 main() 中。
【讨论】:
以上是关于Qt QML - QModBus 读取被 QML BusyIndicator/Animation 破坏 - SingleThread的主要内容,如果未能解决你的问题,请参考以下文章