为什么 QModbusClient 在 open 语句后不读取数据?
Why QModbusClient does not read data after open statement?
我正在尝试获得一个简单的 Modbus 运行ning,但我遇到了命令序列方面的问题。
我首先想到的是,我不能 运行 一个函数中有多个函数。如果我这样做,那么它看起来像是建立了连接,但它失败了。如果我创建 2 个按钮(“连接”、“读取”)并先单击连接然后读取,则连接成功并且我能够读取响应。
那么我该如何更改代码,以便它连接到 TCP Modbus,读取一些数据,然后关闭与一个 function/button 的连接?
这是我的代码示例:
在文件 modbusmaster.hpp 中:
#ifndef MODBUSMASTER_HPP
#define MODBUSMASTER_HPP
#include <QMainWindow>
#include <QModbusTcpClient>
#include <QModbusDevice>
#include <QModbusDataUnit>
#include <QDebug>
#include <QUrl>
class QModbusClient;
class ModbusMaster : public QMainWindow
{
Q_OBJECT
public:
explicit ModbusMaster(QWidget *parent = nullptr);
QModbusClient *_modbus = nullptr;
QModbusClient *modbusDevice = nullptr;
bool open(QString host, int port);
bool read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count);
void readReady();
signals:
};
#endif // MODBUSMASTER_HPP
在文件 modbusmaster.cpp 中:
#include "modbusmaster.hpp"
ModbusMaster::ModbusMaster(QWidget *parent) : QMainWindow(parent)
{
}
bool ModbusMaster::open(QString host, int port)
{
if (_modbus) {
_modbus->disconnectDevice();
delete _modbus;
_modbus = nullptr;
}
_modbus = new QModbusTcpClient(this);
connect(_modbus, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
qDebug() << _modbus->errorString();
});
if(!_modbus) {
qDebug() << "Could not create Modbus Client.";
} else {
qDebug() << "Modbus Client is created.";
}
if (_modbus->state() != QModbusDevice::ConnectedState) {
_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, host);
_modbus->setTimeout(1000);
_modbus->setNumberOfRetries(3);
if (!_modbus->connectDevice()) {
qDebug() << "Connect failed: " << _modbus->errorString();
} else {
qDebug() << "Modbus Client is Connected";
return true;
}
}
return false;
}
bool ModbusMaster::read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count)
{
if (!_modbus) {
qDebug() << "!_modbus";
return false;
}
if (_modbus->state() != QModbusDevice::ConnectedState){
qDebug() << "Modbus Client is not Connected in read section";
return false;
}
QModbusDataUnit req(type, startAddress, count);
if (auto *reply = _modbus->sendReadRequest(req, 1))
{
qDebug() << "auto *reply = _modbus->sendReadRequest(req, 1)";
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &ModbusMaster::readReady);
else
delete reply;
return true;
}
return false;
}
void ModbusMaster::readReady()
{
auto reply = qobject_cast<QModbusReply *>(sender());
if (!reply) return;
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);
}
}
在文件 mainwindow.cpp 中:
#include "modbusmaster.hpp"
.......
void mainwindow::on_button_clicked()
{
ModbusMaster test;
test.open("172.19.1.54", 54);
test.read(QModbusDataUnit::HoldingRegisters, 0, 10);
}
.......
“on_button_clicked”不起作用。它仅显示 qDebug() 结果:
Modbus Client is created.
Modbus Client is Connected
Modbus Client is not Connected in read section
如果我使用 2 个按钮,一个用于 test.open,另一个用于 test.read,那么就可以了。
那么我在这里错过了什么?
原因
很多人没有意识到,沟通需要时间。因此,在您的情况下,您打开设备并立即发起读取请求,该请求失败,因为设备尚未打开(因为它需要时间,其他一切)。
解决方案
在 GUI 应用程序中,当代码对事件做出反应时,它可能是最好的,而不是试图线性地执行所有内容。这就是信号和插槽发挥作用的地方。
考虑以下工作流程:
当您创建 QModbusDevice
时,您还将其信号(例如 stateChanged
and errorOccurred
)连接到代码中的插槽(例如 MainWindow::onModbusStateChanged
或 MainWindow::onModbusErrorOccurred
)。那些应该被认为是回调函数,当相应的事件发生时执行:连接状态改变,接收数据,发送数据,发生错误。
通过这种方式,您可以确切地知道什么时候该做什么。
例子
这是我为您准备的示例代码,用于向您展示如何解决该问题:
在MainWindow.h
...
private:
QModbusClient *m_modbus;
...
private slots:
void onModbusStateChanged(QModbusDevice::State state);
...
在MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_modbus(new QModbusClient(this))
{
...
connect(m_modbus, &QModbusClient::stateChanged, this, MainWindow::onModbusStateChanged);
...
}
要在设备连接后立即读取数据,插槽可能如下所示:
void MainWindow::onModbusStateChanged(QModbusDevice::State state)
{
switch (state) {
...
case QModbusDevice::ConnectedState:
m_modbus->read(QModbusDataUnit::HoldingRegisters, 0, 10);
break;
...
}
}
点击按钮即可启动连接。在你的情况下:
void MainWindow::on_button_clicked()
{
m_modbus->connectDevice();
}
我正在尝试获得一个简单的 Modbus 运行ning,但我遇到了命令序列方面的问题。
我首先想到的是,我不能 运行 一个函数中有多个函数。如果我这样做,那么它看起来像是建立了连接,但它失败了。如果我创建 2 个按钮(“连接”、“读取”)并先单击连接然后读取,则连接成功并且我能够读取响应。
那么我该如何更改代码,以便它连接到 TCP Modbus,读取一些数据,然后关闭与一个 function/button 的连接?
这是我的代码示例:
在文件 modbusmaster.hpp 中:
#ifndef MODBUSMASTER_HPP
#define MODBUSMASTER_HPP
#include <QMainWindow>
#include <QModbusTcpClient>
#include <QModbusDevice>
#include <QModbusDataUnit>
#include <QDebug>
#include <QUrl>
class QModbusClient;
class ModbusMaster : public QMainWindow
{
Q_OBJECT
public:
explicit ModbusMaster(QWidget *parent = nullptr);
QModbusClient *_modbus = nullptr;
QModbusClient *modbusDevice = nullptr;
bool open(QString host, int port);
bool read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count);
void readReady();
signals:
};
#endif // MODBUSMASTER_HPP
在文件 modbusmaster.cpp 中:
#include "modbusmaster.hpp"
ModbusMaster::ModbusMaster(QWidget *parent) : QMainWindow(parent)
{
}
bool ModbusMaster::open(QString host, int port)
{
if (_modbus) {
_modbus->disconnectDevice();
delete _modbus;
_modbus = nullptr;
}
_modbus = new QModbusTcpClient(this);
connect(_modbus, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
qDebug() << _modbus->errorString();
});
if(!_modbus) {
qDebug() << "Could not create Modbus Client.";
} else {
qDebug() << "Modbus Client is created.";
}
if (_modbus->state() != QModbusDevice::ConnectedState) {
_modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
_modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, host);
_modbus->setTimeout(1000);
_modbus->setNumberOfRetries(3);
if (!_modbus->connectDevice()) {
qDebug() << "Connect failed: " << _modbus->errorString();
} else {
qDebug() << "Modbus Client is Connected";
return true;
}
}
return false;
}
bool ModbusMaster::read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count)
{
if (!_modbus) {
qDebug() << "!_modbus";
return false;
}
if (_modbus->state() != QModbusDevice::ConnectedState){
qDebug() << "Modbus Client is not Connected in read section";
return false;
}
QModbusDataUnit req(type, startAddress, count);
if (auto *reply = _modbus->sendReadRequest(req, 1))
{
qDebug() << "auto *reply = _modbus->sendReadRequest(req, 1)";
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &ModbusMaster::readReady);
else
delete reply;
return true;
}
return false;
}
void ModbusMaster::readReady()
{
auto reply = qobject_cast<QModbusReply *>(sender());
if (!reply) return;
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);
}
}
在文件 mainwindow.cpp 中:
#include "modbusmaster.hpp"
.......
void mainwindow::on_button_clicked()
{
ModbusMaster test;
test.open("172.19.1.54", 54);
test.read(QModbusDataUnit::HoldingRegisters, 0, 10);
}
.......
“on_button_clicked”不起作用。它仅显示 qDebug() 结果:
Modbus Client is created.
Modbus Client is Connected
Modbus Client is not Connected in read section
如果我使用 2 个按钮,一个用于 test.open,另一个用于 test.read,那么就可以了。
那么我在这里错过了什么?
原因
很多人没有意识到,沟通需要时间。因此,在您的情况下,您打开设备并立即发起读取请求,该请求失败,因为设备尚未打开(因为它需要时间,其他一切)。
解决方案
在 GUI 应用程序中,当代码对事件做出反应时,它可能是最好的,而不是试图线性地执行所有内容。这就是信号和插槽发挥作用的地方。
考虑以下工作流程:
当您创建 QModbusDevice
时,您还将其信号(例如 stateChanged
and errorOccurred
)连接到代码中的插槽(例如 MainWindow::onModbusStateChanged
或 MainWindow::onModbusErrorOccurred
)。那些应该被认为是回调函数,当相应的事件发生时执行:连接状态改变,接收数据,发送数据,发生错误。
通过这种方式,您可以确切地知道什么时候该做什么。
例子
这是我为您准备的示例代码,用于向您展示如何解决该问题:
在MainWindow.h
...
private:
QModbusClient *m_modbus;
...
private slots:
void onModbusStateChanged(QModbusDevice::State state);
...
在MainWindow.cpp
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_modbus(new QModbusClient(this))
{
...
connect(m_modbus, &QModbusClient::stateChanged, this, MainWindow::onModbusStateChanged);
...
}
要在设备连接后立即读取数据,插槽可能如下所示:
void MainWindow::onModbusStateChanged(QModbusDevice::State state)
{
switch (state) {
...
case QModbusDevice::ConnectedState:
m_modbus->read(QModbusDataUnit::HoldingRegisters, 0, 10);
break;
...
}
}
点击按钮即可启动连接。在你的情况下:
void MainWindow::on_button_clicked()
{
m_modbus->connectDevice();
}