序言
需要注意的是,如果信号是你写代码发送的,你可以在写入(sendWriteRequest)后,自连接一个信号(&QModbusReply::finished)与槽,实时监控写入后的状态。
而如果信号不是你发送的,同时由于QModbus不带有回调函数,你就只能通过轮询的方式来读取这个点位的数据,也就是下面介绍的这种情况!
代码
以下是一个使用 QModbus
定期轮询特定 PLC 点位(例如 x3
)的简单 Qt 示例代码。这个示例中,我们假设 x3
是一个保持寄存器(Holding Register),并且我们想要定期读取这个点位的值。
首先,确保你的 Qt 项目文件(.pro
文件)中包含了 Modbus 模块:
QT += modbus
然后,在你的 Qt 应用程序中,你可以添加以下代码:
#include <QCoreApplication>
#include <QModbusTcpClient>
#include <QTimer>
#include <QDebug>
class ModbusPolling : public QObject {
Q_OBJECT
public:
ModbusPolling(QObject *parent = nullptr) : QObject(parent) {
client = new QModbusTcpClient(this);
connect(client, &QModbusTcpClient::errorOccurred, this, &ModbusPolling::handleError);
connect(client, &QModbusTcpClient::stateChanged, this, &ModbusPolling::handleStateChanged);
connect(&timer, &QTimer::timeout, this, &ModbusPolling::pollModbus);
}
void startPolling(const QString &ip, quint16 port, quint16 startAddress, quint8 slaveId) {
this->ip = ip;
this->port = port;
this->startAddress = startAddress;
this->slaveId = slaveId;
client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, ip);
client->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
client->connectDevice();
timer.start(1000); // Poll every second
}
private slots:
void pollModbus() {
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, startAddress, 1);
if (auto reply = client->sendReadRequest(unit, slaveId)) {
connect(reply, &QModbusReply::finished, this, &ModbusPolling::handleReply);
}
}
void handleReply() {
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
if (reply->error() == QModbusDevice::NoError) {
QModbusDataUnit unit = reply->result();
quint16 value = unit.value<quint16>(0);
qDebug() << "Polled value from x3:" << value;
} else {
qDebug() << "Error reading x3:" << reply->errorString();
}
reply->deleteLater();
}
void handleError(QModbusDevice::Error e) {
qDebug() << "Modbus error:" << e;
}
void handleStateChanged(QModbusDevice::State state) {
qDebug() << "Modbus state changed:" << state;
}
private:
QModbusTcpClient *client;
QTimer timer;
QString ip;
quint16 port;
quint16 startAddress;
quint8 slaveId;
};
#include "main.moc"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
ModbusPolling polling;
polling.startPolling("192.168.0.100", 502, 1203, 1); // Adjust IP, port, address, and slave ID as needed
return app.exec();
}
在这个示例中,我们创建了一个 ModbusPolling
类,它负责与 Modbus TCP 服务器建立连接,并定期轮询特定的寄存器点位。我们使用 QTimer
来实现定期轮询,每隔一秒钟发送一次读取请求。
startPolling
方法用于设置 PLC 的 IP 地址、端口号、起始寄存器地址和从站 ID,并启动定时器。
pollModbus
槽函数负责发送读取请求,handleReply
槽函数处理读取请求的回复,handleError
和 handleStateChanged
槽函数分别用于处理错误和状态变化。
在 main
函数中,我们创建了 ModbusPolling
对象,并调用 startPolling
方法开始定期轮询。请根据您的实际情况调整 IP 地址、端口号、寄存器地址和从站 ID。
注意
注意,你拿到的数据ID和代码中识别的地址是有换算关系的,比如你拿到的可能是16进制或者是8进制的ID,那你在代码中就需要把它换成10进制,代码可以识别的格式。
例:四信
数据ID【y16】 - 起始地址【16】 - 代码识别的地址【14】
数据ID【x2】 - 起始地址【1202】 - 代码识别的地址【1202】
我对这块了解得也比较浅,欢迎大家留言说出自己的见解和遇到的不同公司对这些引脚的要求!