Modbus服务器

此示例实现了Modbus服务器应用程序。

此示例充当Modbus服务器。它接收标准的Modbus请求,根据请求调整其内部状态,并返回适当的响应。

此示例必须与Modbus客户端示例一起使用。在启动Modbus客户端示例之前,应先启动此示例并将其置于监听状态。这两个示例之间的后续交互使用Modbus协议。

本示例使用的关键类

创建一个QModbusServer

需要QModbusServer的一个实例来执行任何通信。根据指定的连接类型,示例可以实例化一个QModbusRtuSerialServer(用于串行通信)或一个QModbusTcpServer(用于基于TCP的通信)。

    auto type = static_cast<ModbusConnection>(index);
    if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
        modbusDevice = new QModbusRtuSerialServer(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 QModbusTcpServer(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"));
    }

服务器创建后,使用QModbusServer::setMap() 方法设置寄存器映射。客户端应用程序使用此寄存器映射来读取和写入服务器数据。

        QModbusDataUnitMap reg;
        reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 });
        reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 });
        reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 });
        reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 });

        modbusDevice->setMap(reg);

然后指定通信参数和服务器地址。通信参数取决于通信类型

        if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
            modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                m_settingsDialog->settings().parity);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                m_settingsDialog->settings().baud);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                m_settingsDialog->settings().dataBits);
            modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                m_settingsDialog->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->setServerAddress(ui->serverEdit->text().toInt());

在创建服务器并指定所有参数后,使用QModbusServer::connectDevice() 连接到Modbus网络。

修改本地值

示例允许通过提供的组合框或行编辑器更改所有受支持的寄存器类型的值。一旦在UI中更新了值,就使用QModbusServer::setData() 方法在服务器上更新实际值

void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value)
{
    if (!modbusDevice)
        return;

    if (!modbusDevice->setData(table, quint16(id), value))
        statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000);
}

void MainWindow::setRegister(const QString &value)
{
    if (!modbusDevice)
        return;

    const QString objectName = QObject::sender()->objectName();
    if (registers.contains(objectName)) {
        bool ok = true;
        const quint16 id = quint16(QObject::sender()->property("ID").toUInt());
        if (objectName.startsWith(QStringLiteral("inReg"))) {
            const auto uval = value.toUShort(&ok, 16);
            if (ok)
                ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, uval);
        } else if (objectName.startsWith(QStringLiteral("holdReg"))) {
            const auto uval = value.toUShort(&ok, 16);
            if (ok)
                ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, uval);
        }

        if (!ok)
            statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(),
                                     5000);
    }
}

处理远程写入

Modbus客户端可以通过发送写请求来更新线圈保持寄存器。一旦使用此类请求在服务器端更新了值,就会发出一个QModbusServer::dataWritten() 信号。

        connect(modbusDevice, &QModbusServer::dataWritten,
                this, &MainWindow::updateWidgets);

Modbus服务器示例连接到此信号,提取更新后的值并根据需要进行UI更新

void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size)
{
    for (int i = 0; i < size; ++i) {
        quint16 value;
        QString text;
        switch (table) {
        case QModbusDataUnit::Coils:
            modbusDevice->data(QModbusDataUnit::Coils, quint16(address + i), &value);
            coilButtons.button(address + i)->setChecked(value);
            break;
        case QModbusDataUnit::HoldingRegisters:
            modbusDevice->data(QModbusDataUnit::HoldingRegisters, quint16(address + i), &value);
            registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text
                .setNum(value, 16));
            break;
        default:
            break;
        }
    }
}

运行示例

要从Qt Creator运行此示例,请在“欢迎”模式下打开,并从“示例”中选择该示例。有关更多信息,请参阅构建和运行示例

示例项目 @ code.qt.io

© 2024 The Qt Company Ltd. 本文档中包含的贡献内容均为各自所有者的版权。本文档提供的文档许可证受GNU自由文档许可证第1.3版的约束,此许可证由自由软件基金会发布。Qt及其相关标志为芬兰及/或其他国家的The Qt Company Ltd.的商标。所有其他商标均为各自所有者的财产。