利用Qt5已有QModbus相关类实现ModbusTcpServer总结

利用Qt5已有QModbus相关类实现ModbusTcpServer总结



在疫情环境下催生出了很多的无人或者减少人员接触的项目,比如无人智慧餐厅项目中的无人送餐项目,主要是由送餐小车和一个中控屏和部分协助发餐的设备组成,由于餐厅一般的范围不会很大,考虑到Wi-Fi通信可能比较麻烦,我们前期组网协议使用的是 zigbee,这样的话小车可以无网络运行且待电能力较高,zigbee无线通信方案也比较较成熟,有一些现成的zigbee串口通信芯片,硬件和软件实现都方便一些。随着版本的迭代,有一些新的需求,我们需要接入一些第三方的设备,这些设备可能是 PLC设备,而和这些设备通信的时候可能需要用到 Modbus协议,而中控屏恰好是使用Qt开发的,因此我们借助于Qt5自带的一些功能来实现Modbus-TCP服务端和客户端做一下实验。

1、Modbus/TCP协议简单了解

可以查看这篇文章:https://blog.csdn.net/weixin_39510813/article/details/115027911?spm=1001.2014.3001.5501

2、Qt5 Modbus客户机(master主)服务器(slave从)实现示例分析学习

(1)、搜索Modbus

打开Qt creator后在示例中搜索Modbus,可以看到Modbus主/从的两个示例。

在这里插入图片描述

(2)、运行后结果

我们将master和slave都运行起来,可以看到Modbus\TCP协议的Port是502,本地使用的127.0.0.1的IP地址,然后我们点击connect开始server,下面的勾选是输入和接收输出的回调,右侧客户端的Holding Registers输入要发送的值,左侧服务端我们将各个字节勾选上,然后左侧Input Registers的各个字节输入要发送的内容,之后点击客户端的Read-Write进行读写测试即可:

在这里插入图片描述

(3)、slave代码分析

我们通过tree /f查看文件树:

C:\Qt\Qt5.9.1\Examples\Qt-5.9.1\serialbus\modbus\slave>tree /f
文件夹 PATH 列表
卷序列号为 00000087 0856:6C30
C:.
│  main.cpp
│  mainwindow.cpp
│  mainwindow.h
│  mainwindow.ui
│  settingsdialog.cpp
│  settingsdialog.h
│  settingsdialog.ui
│  slave.pro
│  slave.pro.user
│  slave.qrc
│
├─doc
│  ├─images
│  │      modbusserver.png
│  │
│  └─src
│          modbusslave.qdoc
│
└─images
        application-exit.png
        connect.png
        disconnect.png
        settings.png

可以看出来基本就是main、mainwindow、settingsdialog(settingdialog是对串口属性的设置,所以这里也不用看了)相关的内容,所以我们只需要看两个cpp文件就差不多可以掌握Qt5关于Modbus/TCP的接口使用了,此外可能就是检查一下.pro里面如何添加modbus相关的模块到我们的项目中。

main.cpp(注意一下如何获取modbus的日志即可,其它的没有啥特别的):

#include "mainwindow.h"

#include <QApplication>
#include <QLoggingCategory>

int main(int argc, char *argv[])
{
    // TODO uncomment this line before release
    // right now we always need it
    QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true"));
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    return a.exec();
}

mainwindow.cpp:

初始化->建立连接:通看的话基本上就是initActions->on_connectButton_clicked来确认进行modbus类型选择以及判断是否已连接,如果是modbus/tcp的话则设置端口和url即可,一般来说端口就是502,url则需要根据我们局域网配置的url来定。

读写:setRegister、updateWidgets两个槽函数中有读写的接口,在on_connectType_currentIndexChanged方法中我们点击connect建立连接后就可以对server设置读取的信号槽连接。

#include "mainwindow.h"
#include "settingsdialog.h"
#include "ui_mainwindow.h"

#include <QModbusRtuSerialSlave>
#include <QModbusTcpServer>
#include <QRegularExpression>
#include <QStatusBar>
#include <QUrl>

enum ModbusConnection {
    Serial,
    Tcp
};

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , modbusDevice(nullptr)
{
    ui->setupUi(this);
    setupWidgetContainers();

    ui->connectType->setCurrentIndex(0);
    on_connectType_currentIndexChanged(0);

    m_settingsDialog = new SettingsDialog(this);
    initActions();
}

MainWindow::~MainWindow()
{
    if (modbusDevice)
        modbusDevice->disconnectDevice();
    delete modbusDevice;

    delete ui;
}

void MainWindow::initActions()
{
    ui->actionConnect->setEnabled(true);
    ui->actionDisconnect->setEnabled(false);
    ui->actionExit->setEnabled(true);
    ui->actionOptions->setEnabled(true);

    connect(ui->actionConnect, &QAction::triggered,
            this, &MainWindow::on_connectButton_clicked);
    connect(ui->actionDisconnect, &QAction::triggered,
            this, &MainWindow::on_connectButton_clicked);

    connect(ui->actionExit, &QAction::triggered, this, &QMainWindow::close);
    connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
}

void MainWindow::on_connectType_currentIndexChanged(int index)
{
    if (modbusDevice) {
        modbusDevice->disconnect();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    ModbusConnection type = static_cast<ModbusConnection> (index);
    if (type == Serial) {
        modbusDevice = new QModbusRtuSerialSlave(this);
    } else if (type == Tcp) {
        modbusDevice = new QModbusTcpServer(this);
        if (ui->portEdit->text().isEmpty())
            ui->portEdit->setText(QLatin1Literal("127.0.0.1:502"));
    }
    ui->listenOnlyBox->setEnabled(type == Serial);

    if (!modbusDevice) {
        ui->connectButton->setDisabled(true);
        if (type == Serial)
            statusBar()->showMessage(tr("Could not create Modbus slave."), 5000);
        else
            statusBar()->showMessage(tr("Could not create Modbus server."), 5000);
    } else {
        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);

        connect(modbusDevice, &QModbusServer::dataWritten,
                this, &MainWindow::updateWidgets);
        connect(modbusDevice, &QModbusServer::stateChanged,
                this, &MainWindow::onStateChanged);
        connect(modbusDevice, &QModbusServer::errorOccurred,
                this, &MainWindow::handleDeviceError);

        connect(ui->listenOnlyBox, &QCheckBox::toggled, this, [this](bool toggled) {
            if (modbusDevice)
                modbusDevice->setValue(QModbusServer::ListenOnlyMode, toggled);
        });
        emit ui->listenOnlyBox->toggled(ui->listenOnlyBox->isChecked());
        connect(ui->setBusyBox, &QCheckBox::toggled, this, [this](bool toggled) {
            if (modbusDevice)
                modbusDevice->setValue(QModbusServer::DeviceBusy, toggled ? 0xffff : 0x0000);
        });
        emit ui->setBusyBox->toggled(ui->setBusyBox->isChecked());

        setupDeviceData();
    }
}

void MainWindow::handleDeviceError(QModbusDevice::Error newError)
{
    if (newError == QModbusDevice::NoError || !modbusDevice)
        return;

    statusBar()->showMessage(modbusDevice->errorString(), 5000);
}

void MainWindow::on_connectButton_clicked()
{
    bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState);

    statusBar()->clearMessage();

    if (intendToConnect) {
        if (static_cast<ModbusConnection> (ui->connectType->currentIndex()) == Serial) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                ui->portEdit->text());
            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);
        } 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());
        if (!modbusDevice->connectDevice()) {
            statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
        } else {
            ui->actionConnect->setEnabled(false);
            ui->actionDisconnect->setEnabled(true);
        }
    } else {
        modbusDevice->disconnectDevice();
        ui->actionConnect->setEnabled(true);
        ui->actionDisconnect->setEnabled(false);
    }
}

void MainWindow::onStateChanged(int state)
{
    bool connected = (state != QModbusDevice::UnconnectedState);
    ui->actionConnect->setEnabled(!connected);
    ui->actionDisconnect->setEnabled(connected);

    if (state == QModbusDevice::UnconnectedState)
        ui->connectButton->setText(tr("Connect"));
    else if (state == QModbusDevice::ConnectedState)
        ui->connectButton->setText(tr("Disconnect"));
}

void MainWindow::coilChanged(int id)
{
    QAbstractButton *button = coilButtons.button(id);
    bitChanged(id, QModbusDataUnit::Coils, button->isChecked());
}

void MainWindow::discreteInputChanged(int id)
{
    QAbstractButton *button = discreteButtons.button(id);
    bitChanged(id, QModbusDataUnit::DiscreteInputs, button->isChecked());
}

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

    if (!modbusDevice->setData(table, 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 int id = QObject::sender()->property("ID").toInt();
        if (objectName.startsWith(QStringLiteral("inReg")))
            ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toInt(&ok, 16));
        else if (objectName.startsWith(QStringLiteral("holdReg")))
            ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toInt(&ok, 16));

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

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, address + i, &value);
            coilButtons.button(address + i)->setChecked(value);
            break;
        case QModbusDataUnit::HoldingRegisters:
            modbusDevice->data(QModbusDataUnit::HoldingRegisters, address + i, &value);
            registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text
                .setNum(value, 16));
            break;
        default:
            break;
        }
    }
}

// -- private

void MainWindow::setupDeviceData()
{
    if (!modbusDevice)
        return;

    for (int i = 0; i < coilButtons.buttons().count(); ++i)
        modbusDevice->setData(QModbusDataUnit::Coils, i, coilButtons.button(i)->isChecked());

    for (int i = 0; i < discreteButtons.buttons().count(); ++i) {
        modbusDevice->setData(QModbusDataUnit::DiscreteInputs, i,
            discreteButtons.button(i)->isChecked());
    }

    bool ok;
    for (QLineEdit *widget : qAsConst(registers)) {
        if (widget->objectName().startsWith(QStringLiteral("inReg"))) {
            modbusDevice->setData(QModbusDataUnit::InputRegisters, widget->property("ID").toInt(),
                widget->text().toInt(&ok, 16));
        } else if (widget->objectName().startsWith(QStringLiteral("holdReg"))) {
            modbusDevice->setData(QModbusDataUnit::HoldingRegisters, widget->property("ID").toInt(),
                widget->text().toInt(&ok, 16));
        }
    }
}

void MainWindow::setupWidgetContainers()
{
    coilButtons.setExclusive(false);
    discreteButtons.setExclusive(false);

    QRegularExpression regexp(QStringLiteral("coils_(?<ID>\\d+)"));
    const QList<QCheckBox *> coils = findChildren<QCheckBox *>(regexp);
    for (QCheckBox *cbx : coils)
        coilButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
    connect(&coilButtons, SIGNAL(buttonClicked(int)), this, SLOT(coilChanged(int)));

    regexp.setPattern(QStringLiteral("disc_(?<ID>\\d+)"));
    const QList<QCheckBox *> discs = findChildren<QCheckBox *>(regexp);
    for (QCheckBox *cbx : discs)
        discreteButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
    connect(&discreteButtons, SIGNAL(buttonClicked(int)), this, SLOT(discreteInputChanged(int)));

    regexp.setPattern(QLatin1String("(in|hold)Reg_(?<ID>\\d+)"));
    const QList<QLineEdit *> qle = findChildren<QLineEdit *>(regexp);
    for (QLineEdit *lineEdit : qle) {
        registers.insert(lineEdit->objectName(), lineEdit);
        lineEdit->setProperty("ID", regexp.match(lineEdit->objectName()).captured("ID").toInt());
        lineEdit->setValidator(new QRegExpValidator(QRegExp(QStringLiteral("[0-9a-f]{0,4}"),
            Qt::CaseInsensitive), this));
        connect(lineEdit, &QLineEdit::textChanged, this, &MainWindow::setRegister);
    }
}
(4)、master代码分析

同样我们先查看文件树:

C:\Qt\Qt5.9.1\Examples\Qt-5.9.1\serialbus\modbus\master>tree /f
文件夹 PATH 列表
卷序列号为 000000E4 0856:6C30
C:.
│  main.cpp
│  mainwindow.cpp
│  mainwindow.h
│  mainwindow.ui
│  master.pro
│  master.pro.user
│  master.qrc
│  settingsdialog.cpp
│  settingsdialog.h
│  settingsdialog.ui
│  writeregistermodel.cpp
│  writeregistermodel.h
│
├─doc
│  ├─images
│  │      modbusmaster.png
│  │
│  └─src
│          modbusmaster.qdoc
│
└─images
        application-exit.png
        connect.png
        disconnect.png
        settings.png

基本和slave的接口类似,主要modbus\tcp相关的操作都是在mainwindow下,settingsdialog还是对串口的设置,writeregistermodel是对QAbstractTableModel的继承和部分接口重写,完成双击输入内容的功能。

建立连接:

modbusDevice = new QModbusTcpClient(this);
if (ui->portEdit->text().isEmpty())
		ui->portEdit->setText(QLatin1Literal("127.0.0.1:502"));

modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());

void MainWindow::on_readWriteButton_clicked()
{
    if (!modbusDevice)
        return;
    ui->readValue->clear();
    statusBar()->clearMessage();

    QModbusDataUnit writeUnit = writeRequest();
    QModbusDataUnit::RegisterType table = writeUnit.registerType();
    for (uint i = 0; i < writeUnit.valueCount(); i++) {
        if (table == QModbusDataUnit::Coils)
            writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
        else
            writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
    }

    if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit,
        ui->serverEdit->value())) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::readReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
}

if (modbusDevice)
		modbusDevice->disconnectDevice();
delete modbusDevice;
(5)、QModbusServer和QModbusClient类了解

打开Assistant,搜索QModbusTcp来查看QModbusTcpClient和QModbusTcpServer相关的内容(首先可以确定的是从Qt 5.8开始支持的):

在这里插入图片描述

下面是所有的实现的方法:

This is the complete list of members for QModbusTcpServer, including inherited members.

enum ConnectionParameter
enum Error
enum Option
enum State
QModbusTcpServer(QObject *)
~QModbusTcpServer()
blockSignals(bool )
childEvent(QChildEvent *)
children() const
close()
close()
connect(const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType )
connect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &, Qt::ConnectionType )
connect(const QObject *, const char *, const char *, Qt::ConnectionType ) const
connect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction , Qt::ConnectionType )
connect(const QObject *, PointerToMemberFunction , Functor )
connect(const QObject *, PointerToMemberFunction , const QObject *, Functor , Qt::ConnectionType )
connectDevice() : bool
connectNotify(const QMetaMethod &)
connectionParameter(int ) const : QVariant
customEvent(QEvent *)
d_ptr : 
data(QModbusDataUnit *) const : bool
data(QModbusDataUnit::RegisterType , quint16 , quint16 *) const : bool
dataWritten(QModbusDataUnit::RegisterType , int , int )
deleteLater()
destroyed(QObject *)
disconnect(const QObject *, const char *, const QObject *, const char *)
disconnect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &)
disconnect(const QMetaObject::Connection &)
disconnect(const char *, const QObject *, const char *) const
disconnect(const QObject *, const char *) const
disconnect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction )
disconnectDevice()
disconnectNotify(const QMetaMethod &)
dumpObjectInfo() const
dumpObjectTree() const
dynamicPropertyNames() const
error() const : Error
errorOccurred(QModbusDevice::Error )
errorString() const : QString
event(QEvent *)
eventFilter(QObject *, QEvent *)
findChild(const QString &, Qt::FindChildOptions ) const
findChildren(const QString &, Qt::FindChildOptions ) const
findChildren(const QRegExp &, Qt::FindChildOptions ) const
findChildren(const QRegularExpression &, Qt::FindChildOptions ) const
inherits(const char *) const
installEventFilter(QObject *)
isSignalConnected(const QMetaMethod &) const
isWidgetType() const
isWindowType() const
killTimer(int )
metaObject() const
moveToThread(QThread *)
objectName() const
objectNameChanged(const QString &)
open() : bool
open() : bool
parent() const
processPrivateRequest(const QModbusPdu &) : QModbusResponse
processRequest(const QModbusPdu &) : QModbusResponse
processRequest(const QModbusPdu &) : QModbusResponse
processesBroadcast() const : bool
property(const char *) const
readData(QModbusDataUnit *) const : bool
receivers(const char *) const
removeEventFilter(QObject *)
sender() const
senderSignalIndex() const
serverAddress() const : int
setConnectionParameter(int , const QVariant &)
setData(const QModbusDataUnit &) : bool
setData(QModbusDataUnit::RegisterType , quint16 , quint16 ) : bool
setError(const QString &, QModbusDevice::Error )
setMap(const QModbusDataUnitMap &) : bool
setObjectName(const QString &)
setParent(QObject *)
setProperty(const char *, const QVariant &)
setServerAddress(int )
setState(QModbusDevice::State )
setValue(int , const QVariant &) : bool
signalsBlocked() const
startTimer(int , Qt::TimerType )
startTimer(std::chrono::milliseconds , Qt::TimerType )
state() const : State
stateChanged(QModbusDevice::State )
staticMetaObject : 
staticQtMetaObject : 
thread() const
timerEvent(QTimerEvent *)
tr(const char *, const char *, int )
value(int ) const : QVariant
writeData(const QModbusDataUnit &) : bool

可以针对性的了解一些方法。

3、实现一个modbus/tcp服务进行测试

基本上对于上位机来说作为modbus/tcp服务器的情况比较多。

.pro中添加:

QT       += core gui sql serialport serialbus

主要创建内容和读写操作(结合example和这里的例子):

#ifndef MODBUSSERVER_H
#define MODBUSSERVER_H

#include <QObject>
#include <QModbusServer>
#include <QModbusRtuSerialSlave>
#include <QModbusTcpServer>
#include <QSerialPort>

/*
*
*
*       modbus slave 从站
*
*       modbusSlove_* m_slave = new modbusSlove_(this);
*
*       initModbusSerialSlove()
*
*       connectDevice()
*
*       //寄存器值发生改变,连接这个信号
        void registerData_signal(int address,int value);
*
*/
class ModbusServer : public QObject
{
    Q_OBJECT
public:
    explicit ModbusServer(QObject *parent = nullptr);

    /**
* @projectName   testMyClass
* @brief         初始化串口modbusSlave
* 其他参数 波特率 数据位 校验位 停止位
* @author        SMY
* @date          2019-03-27
*/
    bool initModbusSerialServer(QString portName, qint32 baudRate, QSerialPort::DataBits dataBits,
                         QSerialPort::Parity parity, QSerialPort::StopBits stopBits);
    /**
* @projectName   testMyClass
* @brief         初始化网口modbusSlave
* ip地址,端口号
* @author        SMY
* @date          2019-03-27
*/
    bool initModbusNetworkServer(QString address,int port);

    /**
* @projectName   testMyClass
* @brief         连接设备
* @author        SMY
* @date          2019-03-27
*/
    bool connectDevice();
    /**
* @projectName   testMyClass
* @brief         网口还是串口连接
* @author        SMY
* @date          2019-03-26
*/
    enum modbusConnection
    {
        Serial,
        Tcp
    };

signals:
    //寄存器值发生改变
    void registerData_signal(int address,int value);
    //发生错误
    void error_signal(QString errorString);
    /*state :1 connect ,0:unconnect
    *状态发生改变
    */
    void stateChanged_signal(int state);
public slots:
private slots:
    /**
* @projectName   testMyClass
* @brief         更新寄存器数据
* @author        SMY
* @date          2019-03-26
*/
    void updateData(QModbusDataUnit::RegisterType table, int address, int size);
    /**
* @projectName   testMyClass
* @brief         device error
* @author        SMY
* @date          2019-03-27
*/
    void handleDeviceError(QModbusDevice::Error newError);
    /**
* @projectName   testMyClass
* @brief         连接状态改变
* @author        SMY
* @date          2019-03-27
*/
    void onStateChanged(int state);
private:
    modbusConnection m_mode;
    QModbusServer* modbusServer;
};

#endif // MODBUSSERVER__H

#include "modbusserver.h"
#include <QDebug>

ModbusServer::ModbusServer(QObject *parent) : QObject(parent)
{

}

bool ModbusServer::initModbusSerialServer(QString portName, qint32 baudRate, QSerialPort::DataBits dataBits,
                                   QSerialPort::Parity parity,
                                   QSerialPort::StopBits stopBits)
{
    //串口

    modbusServer = new QModbusRtuSerialSlave(this);

    m_mode = Serial;

    if(!modbusServer)
    {
        qDebug()<<"could not create modbus slave";
        return 0;
    }

    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 });

    modbusServer->setMap(reg);

    modbusServer->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                    portName);
    modbusServer->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                    baudRate);
    modbusServer->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                    dataBits);
    modbusServer->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                    parity);
    modbusServer->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                    stopBits);


    //更新寄存器值
    connect(modbusServer,&QModbusServer::dataWritten,this,
            &ModbusServer::updateData);
    //更新连接状态
    connect(modbusServer, &QModbusServer::stateChanged,
            this, &ModbusServer::onStateChanged);
    //错误发生
    connect(modbusServer, &QModbusServer::errorOccurred,
            this, &ModbusServer::handleDeviceError);
    return  1;

}

bool ModbusServer::initModbusNetworkServer(QString address, int port)
{
//    if(modbusServer)
//    {
//        modbusServer->disconnect();
//        delete modbusServer;
//        modbusServer = nullptr;
//    }

    //网口
    modbusServer = new QModbusTcpServer(this);

    m_mode = Tcp;

    if(!modbusServer)
    {
        qDebug()<<"could not create modbus slave";
        return false;
    }

    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 });

    modbusServer->setMap(reg);

    modbusServer->setConnectionParameter(QModbusDevice::NetworkAddressParameter,address);
    modbusServer->setConnectionParameter(QModbusDevice::NetworkPortParameter,port);

    //更新寄存器值
    connect(modbusServer,&QModbusServer::dataWritten,this,
            &ModbusServer::updateData);
    //更新连接状态
    connect(modbusServer, &QModbusServer::stateChanged,
            this, &ModbusServer::onStateChanged);
    //错误发生
    connect(modbusServer, &QModbusServer::errorOccurred,
            this, &ModbusServer::handleDeviceError);

    return true;
}

bool ModbusServer::connectDevice()
{
	//设置modbusServer的modbus地址固定为1
	modbusServer->setServerAddress(1);
    return modbusServer->connectDevice();
}

void ModbusServer::updateData(QModbusDataUnit::RegisterType table, int address, int size)
{
    for (int i = 0; i < size; ++i) {
        quint16 value;
        QString text;
        switch (table) {
        case QModbusDataUnit::Coils:
            modbusServer->data(QModbusDataUnit::Coils, address + i, &value);

            break;
        case QModbusDataUnit::HoldingRegisters:
            modbusServer->data(QModbusDataUnit::HoldingRegisters, address + i, &value);

            break;
        default:
            break;
        }

        emit registerData_signal(address+i,value);

    }
}

void ModbusServer::handleDeviceError(QModbusDevice::Error newError)
{
    if(newError == QModbusDevice::NoError || !modbusServer)
        return;
    emit error_signal(modbusServer->errorString());
}

void ModbusServer::onStateChanged(int state)
{
    if(state == QModbusDevice::UnconnectedState)
        emit stateChanged_signal(0);
    else if(state == QModbusDevice::ConnectedState)
        emit stateChanged_signal(1);
}

main.cpp中添加modbus协议调试(参考示例):

QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = true"));

调用我们封装的modbusServer类:

ModbusServer *modbusServer = new ModbusServer(this);

modbusServer->initModbusNetworkServer("127.0.0.1", 502);
modbusServer->connectDevice();

其实还应该添加析构方法断开连接释放资源,自己加一下哦~

接收成功了,我们可以根据需求再进行一些修改:
在这里插入图片描述

  • 15
    点赞
  • 73
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
Qt 是一个强大的跨平台应用程序开发框架,可以实现多种通信协议,包括 Modbus TCP。下面是一些实现 Modbus TCP 的步骤: 1. 首先,确保你已经在计算机上安装了 Qt 开发框架。可以从 Qt 官方网站下载并安装最新版本的 Qt。 2. 在 Qt 项目中创建一个新的工程或打开一个现有的工程。 3. 导入所需的 Modbus TCP 库。在 Qt 中,有一些第三方库可以帮助你实现 Modbus TCP,比如 QModbus 和 libmodbus。你可以选择其中一个库来使用。 - 对于 QModbus,你需要在项目文件 (.pro) 中添加以下行: ``` QT += modbus ``` - 对于 libmodbus,你需要下载该库的源码,并将其编译为静态库或动态库,然后将库文件添加到项目中。 4. 在代码中导入所需的模块。对于 QModbus,你需要导入 QModbusTcpClient 头文件: ```cpp #include <QModbusTcpClient> ``` 5. 创建一个 QModbusTcpClient 实例,并进行相关设置: ```cpp QModbusTcpClient* modbusClient = new QModbusTcpClient(this); modbusClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, "IP地址"); modbusClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, 端口号); ``` 6. 连接到 Modbus TCP 设备: ```cpp modbusClient->connectDevice(); ``` 7. 实现读取和写入 Modbus 寄存器的功能。使用 QModbusDataUnit 来定义你要读取或写入的寄存器的相关信息,比如起始地址、寄存器数量等: ```cpp QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 起始地址, 寄存器数量); modbusClient->sendReadRequest(readUnit, 设备地址); QModbusDataUnit writeUnit(QModbusDataUnit::HoldingRegisters, 起始地址, 寄存器数量); writeUnit.setValue(要写入的值); modbusClient->sendWriteRequest(writeUnit, 设备地址); ``` 8. 处理读取和写入的响应。你可以通过连接到 QModbusTcpClient 的 finished() 信号,并在槽函数中处理响应数据: ```cpp // 读取响应 connect(modbusClient, &QModbusTcpClient::finished, this, [this, modbusClient]() { if (modbusClient->error() == QModbusDevice::NoError) { QModbusReply* reply = modbusClient->reply(); if (reply) { if (reply->error() == QModbusDevice::NoError) { // 处理读取到的数据 } else { // 处理错误 } reply->deleteLater(); } } else { // 处理错误 } }); // 写入响应 connect(modbusClient, &QModbusTcpClient::finished, this, [this, modbusClient]() { if (modbusClient->error() == QModbusDevice::NoError) { QModbusReply* reply = modbusClient->reply(); if (reply) { if (reply->error() == QModbusDevice::NoError) { // 处理写入成功 } else { // 处理写入错误 } reply->deleteLater(); } } else { // 处理错误 } }); ``` 这是一个简单的示例,用于演示如何在 Qt 中实现 Modbus TCP。你可以根据自己的需求进行修改和扩展。请注意,在实际使用中,你可能还需要处理连接、断开连接等事件,并进行错误处理和异常处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值