Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Modbus TCP handle asynchronous communication
QtWS25 Last Chance

Modbus TCP handle asynchronous communication

Scheduled Pinned Locked Moved Unsolved General and Desktop
modbustcpclientasynchronous
1 Posts 1 Posters 771 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    derlucae98
    wrote on 24 Dec 2020, 23:31 last edited by
    #1

    Hi,

    I am currently designing an Modbus TCP client application and I'm struggeling with the asynchronous communication. Let's say I have a class "mb" which handles all the modbus related stuff. I need to periodically retrieve values from the server. While my code works so far, my solution doesn't quite feel right.

    mb has the member function "initModbus" which sets up a connection to a server. If that was successful I send a signal "modbusReady" to the mainwindow class to start the periodic timer.
    I request new values with "requestModbusData". This member function will then request all necessary modbus registers. As soon as the new values arrive they get decomposed and the corresponding variables in the values struct are updated.
    I use flags to keep track of new incoming values. This allows me to pull the new data all at once (only if all requested values have been updated).
    This is where things get ugly. In the "pollUpdatedValueFlags" function I have to "and" all flags together. While this seems fine with only two registers this gets hard to maintain if you have to keep track of a lot of registers.
    I had the idea to just use one flag for the last requested value. But this looks a bit unsafe to me.
    There is probably a much better solution.
    It would be nice if you could push me onto the right track.

    mb.h:

    #ifndef mb_H
    #define mb_H
    
    #include <QObject>
    #include <QModbusTcpClient>
    #include <QUrl>
    #include <QModbusDataUnit>
    #include <cstring>
    
    
    
    class mb : public QObject
    {
        Q_OBJECT
    public:
        mb();
        ~mb();
    
        struct values_t{
            QString Register1;
            quint8 Register2;
        };
    
        QModbusDevice::Error initModbus(const QUrl& credentials);
        const values_t& getValues();
        void requestModbusData();
    
       
        struct updatedValueFlags_t{
            bool f_Register1;
            bool f_Register2;
        }updatedValueFlags;
    
        bool pollUpdatedValueFlags();
    
    
    private:
        QModbusTcpClient *mb_handle = nullptr;
        
        enum modbusRegister{
            r_Register1 = 30042,
            r_Register2 = 30053
        };
    
        values_t values;
        void getRegister1();
        void getRegister2();
        void retrieveModbusRegister(const QModbusDataUnit::RegisterType& regType, modbusRegister reg, quint8 unitID);
        void modbusReceiveFinished();
        void resolveReply(const QModbusDataUnit& unit);
    
    signals:
        void modbusReady();
    
    private slots:
        void modbusStateChanged(QModbusDevice::State state);
    
    };
    
    
    #endif // mb_H
    
    
    

    mb.cpp:

    #include "mb.h"
    
    
    mb::mb()
    {
        //initialize the updatedValueFlags with 0
        memset(&this->updatedValueFlags, 0, sizeof(this->updatedValueFlags));
    }
    
    mb::~mb()
    {
    
    }
    
    
    QModbusDevice::Error mb::initModbus(const QUrl &credentials)
    {
        mb_handle = new QModbusTcpClient;
        mb_handle->setConnectionParameter(QModbusDevice::NetworkAddressParameter, credentials.host());
        mb_handle->setConnectionParameter(QModbusDevice::NetworkPortParameter, credentials.port());
        mb_handle->setTimeout(1000);
        mb_handle->setNumberOfRetries(5);
        connect(mb_handle, &QModbusClient::stateChanged, this, &mb::modbusStateChanged);
    
        mb_handle->connectDevice();
    
        return mb_handle->error();
    
    }
    
    void mb::modbusStateChanged(QModbusDevice::State state)
    {
        if (state == QModbusDevice::State::ConnectedState){
            emit modbusReady();
        }
    }
    
    void mb::getRegister1()
    {
        retrieveModbusRegister(QModbusDataUnit::InputRegisters, r_Register1, 3);
    }
    
    void mb::getRegister2()
    {
        retrieveModbusRegister(QModbusDataUnit::InputRegisters, r_Register2, 3);
    }
    
    
    void mb::retrieveModbusRegister(const QModbusDataUnit::RegisterType &regType, mb::modbusRegister reg, quint8 unitID)
    {
        QModbusDataUnit unit(regType, reg, 10);
        if (QModbusReply *reply = mb_handle->sendReadRequest(unit, unitID)){
            if (!reply->isFinished()){
                connect(reply, &QModbusReply::finished, this, &mb::modbusReceiveFinished);
            } else {
                delete reply;
            }
        }
    }
    
    void mb::modbusReceiveFinished()
    {
        auto reply = qobject_cast<QModbusReply*>(sender());
        if (!reply){
            return;
        }
        if (reply->error() == QModbusDevice::NoError){
            QModbusDataUnit unit = reply->result();
            resolveReply(unit);
        }
        reply->deleteLater();
    }
    
    void mb::resolveReply(const QModbusDataUnit &unit)
    {
        switch (unit.startAddress()){
        
        case r_Register1:{
            switch (unit.value(0)){
            case 1:
                this->values.Register1 = "value 1";
                break;
            case 2:
                this->values.Register1 = "value 2";
                break;
            case 3:
                this->values.Register1 = "value 3";
                break;
            default:
                this->values.Register1 = "invalid";
                break;
            }
            updatedValueFlags.f_Register1 = true;
            break;
        }
    
        case r_Register2:{
            switch (unit.value(0)){
            case 1:
                this->values.Register2 = 42;
                break;
            case 2:
                this->values.Register2 = 24;
                break;
            default:
                this->values.Register2 =-1;
                break;
            }
            updatedValueFlags.Register2 = true;
            break;
        }
    
        default:
            break;
        }
    }
    
    void mb::requestModbusData()
    {
        getRegister1();
        getRegister2();
    }
    
    bool mb::pollUpdatedValueFlags()
    {
        return updatedValueFlags.f_Register1 && updatedValueFlags.f_Register2; // && updatedValueFlags.so_on && updatedValueFlags.so_forth && ...;
    }
    
    const mb::values_t &mb::getValues()
    {
        memset(&this->updatedValueFlags, 0, sizeof(this->updatedValueFlags));
        return values;
    }
    
    

    mainwindow.h:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H
    
    #include <QMainWindow>
    #include <QTimer>
    #include <QUrl>
    #include "mb.h"
    
    QT_BEGIN_NAMESPACE
    namespace Ui { class MainWindow; }
    QT_END_NAMESPACE
    
    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    
    private:
        Ui::MainWindow *ui;
        mb *modbusDevice;
        QTimer *periodicWorker;
    
    private slots:
        void on_btnConnect_clicked();
        void worker();
        void modbusReady();
    };
    #endif // MAINWINDOW_H
    
    

    mainwindow.cpp:

    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    
    
    MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent)
        , ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        modbusDevice = new mb;
        periodicWorker = new QTimer;
        periodicWorker->setInterval(500);
        periodicWorker->connect(periodicWorker, SIGNAL(timeout()), this, SLOT(worker()));
    
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    
    void MainWindow::on_btnConnect_clicked()
    {
    	QUrl modbus_credentials;
    	modbus_credentials.setHost(ui->lemodbusDeviceIP->text());
    	modbus_credentials.setPort(ui->cbmodbusDevicePort->currentText().toInt());
    	modbusDevice->connect(modbusDevice, SIGNAL(modbusReady()), this, SLOT(modbusReady()));
    	modbusDevice->initModbus(modbus_credentials);
    }
    
    void MainWindow::worker()
    {
        static bool newDataRequested = false;
    
        if (modbusDevice->pollUpdatedValueFlags() == false){ //no new values
            if (newDataRequested == false){ //no new data requested
                modbusDevice->requestModbusData();
                newDataRequested = true;
            }
        } else{ //new data available
            mb::values_t newData = modbusDevice->getValues();
            qDebug() << "Register1: " << newData.Register1;
            qDebug() << "Register2: " << newData.Register2;
            newDataRequested = false;
        }
    
    
    }
    
    void MainWindow::modbusReady()
    {
        periodicWorker->start();
    }
    
    
    1 Reply Last reply
    0

    1/1

    24 Dec 2020, 23:31

    • Login

    • Login or register to search.
    1 out of 1
    • First post
      1/1
      Last post
    0
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Get Qt Extensions
    • Unsolved