Почему QModbusClient не читает данные после оператора open? - PullRequest
2 голосов
/ 05 августа 2020

Я пытаюсь запустить простой Modbus, и у меня возникают проблемы с последовательностью команд.

Сначала я понял, что не могу запускать несколько функций в функции. Если я сделаю это, то будет похоже, что соединение было установлено, но оно не работает. Если я создам 2 кнопки («Подключить», «Прочитать») и сначала щелкну «Подключиться», а затем «Прочитать», значит, подключение было успешным, и я могу прочитать ответ.

Итак, как я могу изменить код, чтобы он подключился к TCP Modbus, прочитал некоторые данные, а затем закрыл соединение с помощью одной функции / кнопки?

Это пример моего кода:

В файле modbusmaster.hpp:

#ifndef MODBUSMASTER_HPP
#define MODBUSMASTER_HPP
#include <QMainWindow>
#include <QModbusTcpClient>
#include <QModbusDevice>
#include <QModbusDataUnit>
#include <QDebug>
#include <QUrl>

class QModbusClient;

class ModbusMaster : public QMainWindow
{
    Q_OBJECT
public:
    explicit ModbusMaster(QWidget *parent = nullptr);

    QModbusClient *_modbus = nullptr;
    QModbusClient *modbusDevice = nullptr;
    bool open(QString host, int port);
    bool read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count);
    void readReady();

signals:

};

#endif // MODBUSMASTER_HPP

В файле modbusmaster. cpp:

#include "modbusmaster.hpp"

ModbusMaster::ModbusMaster(QWidget *parent) : QMainWindow(parent)
{
}

bool ModbusMaster::open(QString host, int port)
{
    if (_modbus) {
        _modbus->disconnectDevice();
        delete _modbus;
        _modbus = nullptr;
    }
    _modbus = new QModbusTcpClient(this);

    connect(_modbus, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        qDebug() << _modbus->errorString();
    });

    if(!_modbus) {
        qDebug() << "Could not create Modbus Client.";
    } else {
        qDebug() << "Modbus Client is created.";
    }

    if (_modbus->state() != QModbusDevice::ConnectedState) {
        _modbus->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
        _modbus->setConnectionParameter(QModbusDevice::NetworkAddressParameter, host);
        _modbus->setTimeout(1000);
        _modbus->setNumberOfRetries(3);

        if (!_modbus->connectDevice()) {
            qDebug() << "Connect failed: " << _modbus->errorString();
        } else {
            qDebug() << "Modbus Client is Connected";
            return true;
        }
    }
    return false;
}

bool ModbusMaster::read(QModbusDataUnit::RegisterType type, int startAddress, quint16 count)
{
    if (!_modbus) {
        qDebug() << "!_modbus";
        return false;
    }

    if (_modbus->state() != QModbusDevice::ConnectedState){
        qDebug() << "Modbus Client is not Connected in read section";
        return false;
    }

    QModbusDataUnit req(type, startAddress, count);
    if (auto *reply = _modbus->sendReadRequest(req, 1))
    {
        qDebug() << "auto *reply = _modbus->sendReadRequest(req, 1)";
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &ModbusMaster::readReady);
        else
            delete reply;
        return true;
    }
    return false;
}

void ModbusMaster::readReady()
{
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply) return;
    reply->deleteLater();

    if (reply->error() == QModbusDevice::NoError)
    {
        qDebug() << reply;
    }
    else if (reply->error() == QModbusDevice::ProtocolError)
    {
        qDebug() << QString("Read response error: %1 (Mobus exception: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->rawResult().exceptionCode(), -1, 16);
    } else {
        qDebug() << QString("Read response error: %1 (code: 0x%2)").
                                    arg(reply->errorString()).
                                    arg(reply->error(), -1, 16);
    }
}

В файле mainwindow. cpp:

#include "modbusmaster.hpp"
.......
void mainwindow::on_button_clicked()
{
    ModbusMaster test;
    test.open("172.19.1.54", 54);
    test.read(QModbusDataUnit::HoldingRegisters, 0, 10);

}
.......

"on_button_clicked" не работай. Он показывает только результаты qDebug ():

Modbus Client is created.
Modbus Client is Connected
Modbus Client is not Connected in read section

Если я использую 2 кнопки, одну для test.open, а другую с test.read, тогда все в порядке.

И что я здесь пропал?

1 Ответ

1 голос
/ 07 августа 2020

Причина

Многие люди не понимают, что общение требует времени. Итак, в вашем случае вы открываете устройство и немедленно инициируете запрос на чтение, который терпит неудачу, потому что устройство еще не открыто (потому что это требует времени, как и все остальное).

Решение

В приложении GUI, вероятно, лучше всего, когда код реагирует на события, вместо того, чтобы пытаться выполнить все линейно. Здесь в игру вступают сигналы и слоты.

Рассмотрим следующий рабочий процесс:

при создании QModbusDevice вы также подключаете его сигналы (например, stateChanged и errorOccurred) в слоты вашего кода (например, MainWindow::onModbusStateChanged или MainWindow::onModbusErrorOccurred). Их следует рассматривать как функции обратного вызова, которые выполняются при возникновении соответствующего события: изменяется состояние соединения, данные принимаются, данные отправляются, возникает ошибка.

Таким образом вы будете точно знать, когда что делать.

Пример

Вот пример кода, который я подготовил для вас, чтобы показать вам, как можно подойти к проблеме:

в MainWindow.h

...
private:
    QModbusClient *m_modbus;
...
private slots:
    void onModbusStateChanged(QModbusDevice::State state);
...

в MainWindow. cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    m_modbus(new QModbusClient(this))
{
    ...
    connect(m_modbus, &QModbusClient::stateChanged, this, MainWindow::onModbusStateChanged);
    ...
}

Для чтения данных сразу после подключения устройства слот может выглядеть как это:

void MainWindow::onModbusStateChanged(QModbusDevice::State state)
{
    switch (state) {
        ...
        case QModbusDevice::ConnectedState:
            m_modbus->read(QModbusDataUnit::HoldingRegisters, 0, 10);
        break;
        ...
    }
}

Осталось инициировать подключение по нажатию кнопки. В вашем случае:

void MainWindow::on_button_clicked()
{
    m_modbus->connectDevice();
}
...