Modbus 客户端
本示例实现了一个Modbus客户端应用。
该示例作为Modbus客户端,通过串行线或TCP发送Modbus请求。显示的对话框允许定义标准请求并显示传入的响应。
该示例必须与Modbus服务器示例或其他通过TCP或串行端口连接的Modbus设备一起使用。
本示例中使用的关键类
创建QModbusClient
要执行任何通信,需要实例化一个QModbusClient。根据指定的连接类型,示例可以实例化一个QModbusRtuSerialClient(用于串行通信)或一个QModbusTcpClient(用于基于TCP的通信)。
auto type = static_cast<ModbusConnection>(index); if (type == Serial) { #if QT_CONFIG(modbus_serialport) modbusDevice = new QModbusRtuSerialClient(this); // Try to fill in the first available serial port name if the line edit // is empty, or contains a url (assume that ':' is only a part of url). const auto ports = QSerialPortInfo::availablePorts(); const auto currentText = ui->portEdit->text(); if (!ports.isEmpty() && (currentText.isEmpty() || currentText.contains(u':'))) ui->portEdit->setText(ports.front().portName()); #endif } else if (type == Tcp) { modbusDevice = new QModbusTcpClient(this); const QUrl currentUrl = QUrl::fromUserInput(ui->portEdit->text()); // Check if we already have <ip address>:<port> if (currentUrl.port() <= 0) ui->portEdit->setText(QLatin1String("127.0.0.1:50200")); }
创建客户端并指定所有参数后,使用setConnectionParameter() 方法指定连接参数。参数取决于通信类型
const auto settings = m_settingsDialog->settings(); if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) { modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, ui->portEdit->text()); #if QT_CONFIG(modbus_serialport) modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, settings.parity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, settings.baud); modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, settings.dataBits); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, settings.stopBits); #endif } else { const QUrl url = QUrl::fromUserInput(ui->portEdit->text()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host()); } modbusDevice->setTimeout(settings.responseTime); modbusDevice->setNumberOfRetries(settings.numberOfRetries);
创建客户端并指定所有参数后,使用QModbusClient::connectDevice() 连接到Modbus网络。
读取数据
要从Modbus服务器读取数据,客户端需要指定服务器地址及其想要读取的对象参数
对象参数由QModbusDataUnit 类表示
QModbusDataUnit MainWindow::readRequest() const { const auto table = ui->writeTable->currentData().value<QModbusDataUnit::RegisterType>(); int startAddress = ui->readAddress->value(); Q_ASSERT(startAddress >= 0 && startAddress < 10); // do not go beyond 10 entries quint16 numberOfEntries = qMin(ui->readSize->currentText().toUShort(), quint16(10 - startAddress)); return QModbusDataUnit(table, startAddress, numberOfEntries); }
收集参数后,使用sendReadRequest() 方法发送实际请求。此方法返回一个QModbusReply,应在异步方式中处理,因此使用QModbusReply::finished() 信号来检查何时准备好响应。
if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) { if (!reply->isFinished()) connect(reply, &QModbusReply::finished, this, &MainWindow::onReadReady); else delete reply; // broadcast replies return immediately } else { statusBar()->showMessage(tr("Read error: %1").arg(modbusDevice->errorString()), 5000); }
收到QModbusReply::finished() 信号后,可以使用回复对象获取数据或检查读取错误。
void MainWindow::onReadReady() { auto reply = qobject_cast<QModbusReply *>(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); for (qsizetype i = 0, total = unit.valueCount(); i < total; ++i) { const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i) .arg(QString::number(unit.value(i), unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16)); ui->readValue->addItem(entry); } } else if (reply->error() == QModbusDevice::ProtocolError) { statusBar()->showMessage(tr("Read response error: %1 (Modbus exception: 0x%2)"). arg(reply->errorString()). arg(reply->rawResult().exceptionCode(), -1, 16), 5000); } else { statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)"). arg(reply->errorString()). arg(reply->error(), -1, 16), 5000); } reply->deleteLater(); }
写入数据
要将数据写入Modbus服务器,客户端需要指定服务器地址及其想要写入的对象参数。与读取数据一样,使用QModbusDataUnit 类表示要写入数据的有关信息。这次,数据还包括所需的值。使用sendWriteRequest() 方法写入所需数据
QModbusDataUnit writeUnit = writeRequest(); QModbusDataUnit::RegisterType table = writeUnit.registerType(); for (qsizetype i = 0, total = writeUnit.valueCount(); i < total; ++i) { const auto addr = i + writeUnit.startAddress(); if (table == QModbusDataUnit::Coils) writeUnit.setValue(i, writeModel->m_coils[addr]); else writeUnit.setValue(i, writeModel->m_holdingRegisters[addr]); } if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, this, [this, reply]() { const auto error = reply->error(); if (error == QModbusDevice::ProtocolError) { statusBar()->showMessage(tr("Write response error: %1 (Modbus exception: 0x%2)") .arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16), 5000); } else if (error != QModbusDevice::NoError) { statusBar()->showMessage(tr("Write response error: %1 (code: 0x%2)"). arg(reply->errorString()).arg(error, -1, 16), 5000); } reply->deleteLater(); }); } else { // broadcast replies return immediately reply->deleteLater(); } } else { statusBar()->showMessage(tr("Write error: %1").arg(modbusDevice->errorString()), 5000); }
与读取数据一样,返回的QModbusReply 对象用于检查写入错误。
运行示例
要从 Qt Creator 运行示例,请打开 欢迎 模式,并从 示例 中选择示例。有关更多信息,请访问 构建和运行示例。
© 2024 Qt公司有限公司。本文件中所包含的文档贡献属于其各自的版权所有者。本文件中的文档根据Free Software Foundation发布的GNU自由文档许可证第1.3版的条款提供。Qt及其相关标志是芬兰的Qt公司及其在全球的其他国家的商标。所有其他商标都属于其各自的拥有者。