모드버스 클라이언트

이 예제에서는 모드버스 클라이언트 애플리케이션을 구현합니다.

이 예제는 직렬 회선 또는 TCP를 통해 모드버스 요청을 전송하는 모드버스 클라이언트 역할을 합니다. 표시된 대화 상자에서 표준 요청을 정의하고 수신 응답을 표시할 수 있습니다.

이 예제는 모드버스 서버 예제 또는 TCP 또는 직렬 포트를 통해 연결된 다른 모드버스 장치와 함께 사용해야 합니다.

이 예제에서 사용된 주요 클래스:

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 네트워크에 연결합니다.

데이터 읽기

모드버스 서버에서 데이터를 읽으려면 클라이언트가 서버 주소와 읽으려는 객체의 매개변수를 지정해야 합니다:

객체 매개변수는 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();
}

데이터 쓰기

모드버스 서버에 데이터를 쓰려면 클라이언트는 서버 주소와 쓰고자 하는 개체의 매개변수를 지정해야 합니다. 데이터 읽기와 마찬가지로 QModbusDataUnit 클래스를 사용하여 기록할 데이터에 대한 정보를 나타냅니다. 이번에는 데이터에 원하는 values 도 포함됩니다. 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에서 예제를 실행하려면 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 예제 빌드 및 실행하기를 참조하세요.

예제 프로젝트 @ code.qt.io

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.