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 ネットワークに接続します。

ローカル値の変更

この例では、提供されているコンボボックスまたは行編集を使用して、サポートされているすべてのregister types の値を変更できます。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 クライアントは書き込み要求を送信することでCoilsHoldingRegisters を更新することができます。このようなリクエストを使用してサーバー側で値が更新されると、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 からサンプルを実行するには、Welcome モードを開き、Examples からサンプルを選択します。詳細については、Building and Running an Example を参照してください。

サンプルプロジェクト @ code.qt.io

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 ここで提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。