Qt QModbus相关类实现ModbusTcpServer总结


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

1、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的各个字节输入要发送的内容,之后点击客户端的ReadWrite进行读写测试即可:

(3)、slave代码分析

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

1 C:\Qt\Qt5.9.1\Examples\Qt5.9.1\serialbus\modbus\slave>tree /f 2 文件夹 PATH 列表 3 卷序列号为 00000087 0856:6C30 4 C:. 5 │ mainpp 6 │ mainwindocpp 7 │ mainwindoh 8 │ mainwindoui 9 │ settingsdialogpp 10 │ settingsdialog.h 11 │ settingsdialog.ui 12 │ slave.pro 13 │ slave.pro.user 14 │ slave.qrc 15 │ 16 ├─doc 17 │ ├─images 18 │ │ modbusserver.png 19 │ │ 20 │ └─src 21 │ modbusslave.qdoc 22 │ 23 └─images 24 applicationexit.png 25 connect.png 26 disconnect.png 27 settings.png

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

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

1 #include "mainwindoh" 2 3 #include <QApplication> 4 #include <QLoggingCategory> 5 6 int main(int argc, char *argv[]) 7

  mainwindocpp:

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

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

1 #include "mainwindoh" 2 #include "settingsdialog.h" 3 #include "ui_mainwindoh" 4 5 #include <QModbusRtuSerialSlave> 6 #include <QModbusTcpServer> 7 #include <QRegularExpression> 8 #include <QStatusBar> 9 #include <QUrl> 10 11 enum ModbusConnection ; 15 16 MainWindow::MainWindow(QWidget *parent) 17 : QMainWindow(parent) 18 , ui(new Ui::MainWindow) 19 , modbusDevice(nullptr) 20 30 31 MainWindow::~MainWindow() 32 39 40 void MainWindow::initActions() 41 55 56 void MainWindow::on_connectType_currentIndexChanged(int index) 57 64 ModbusConnection type = static_cast<ModbusConnection> (index); 65 if (type == Serial) else if (type == Tcp) 72 ui>listenOnlyBox>setEnabled(type == Serial); 73 74 if (!modbusDevice) else ); 83 reg.insert(QModbusDataUnit::DiscreteInputs, ); 84 reg.insert(QModbusDataUnit::InputRegisters, ); 85 reg.insert(QModbusDataUnit::HoldingRegisters, ); 86 87 modbusDevice>setMap(reg); 88 89 connect(modbusDevice, &QModbusServer::dataWritten, 90 this, &MainWindow::updateWidgets); 91 connect(modbusDevice, &QModbusServer::stateChanged, 92 this, &MainWindow::onStateChanged); 93 connect(modbusDevice, &QModbusServer::errorOccurred, 94 this, &MainWindow::handleDeviceError); 95 96 connect(ui>listenOnlyBox, &QCheckBox::toggled, this, [this](bool toggled) ); 100 emit ui>listenOnlyBox>toggled(ui>listenOnlyBox>isChecked()); 101 connect(ui>setBusyBox, &QCheckBox::toggled, this, [this](bool toggled) ); 105 emit ui>setBusyBox>toggled(ui>setBusyBox>isChecked()); 106 107 setupDeviceData(); 108 } 109 } 110 111 void MainWindow::handleDeviceError(QModbusDevice::Error newError) 112 118 119 void MainWindow::on_connectButton_clicked() 120 else 142 modbusDevice>setServerAddress(ui>serverEdit>text().toInt()); 143 if (!modbusDevice>connectDevice()) else 149 } else 154 } 155 156 void MainWindow::onStateChanged(int state) 157 167 168 void MainWindow::coilChanged(int id) 169 173 174 void MainWindow::discreteInputChanged(int id) 175 179 180 void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value) 181 188 189 void MainWindow::setRegister(const QString &value) 190 207 } 208 209 void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size) 210 227 } 228 } 229 230 // private 231 232 void MainWindow::setupDeviceData() 233 244 245 bool ok; 246 for (QLineEdit *widget : qAsConst(registers)) else if (widget>objectName().startsWith(QStringLiteral("holdReg"))) 254 } 255 } 256 257 void MainWindow::setupWidgetContainers() 258 "), 280 Qt::CaseInsensitive), this)); 281 connect(lineEdit, &QLineEdit::textChanged, this, &MainWindow::setRegister); 282 } 283 }
(4)、master代码分析

  同样我们先查看文件树:

1 C:\Qt\Qt5.9.1\Examples\Qt5.9.1\serialbus\modbus\master>tree /f 2 文件夹 PATH 列表 3 卷序列号为 000000E4 0856:6C30 4 C:. 5 │ mainpp 6 │ mainwindocpp 7 │ mainwindoh 8 │ mainwindoui 9 │ master.pro 10 │ master.pro.user 11 │ master.qrc 12 │ settingsdialogpp 13 │ settingsdialog.h 14 │ settingsdialog.ui 15 │ writeregistermodelpp 16 │ writeregistermodel.h 17 │ 18 ├─doc 19 │ ├─images 20 │ │ modbusmaster.png 21 │ │ 22 │ └─src 23 │ modbusmaster.qdoc 24 │ 25 └─images 26 applicationexit.png 27 connect.png 28 disconnect.png 29 settings.png

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

  建立连接:

1 modbusDevice = new QModbusTcpClient(this); 2 if (ui>portEdit>text().isEmpty()) 3 ui>portEdit>setText(QLatin1Literal("127.0.0.1:502")); 4 5 modbusDevice>setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port()); 6 modbusDevice>setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host()); 7 8 void MainWindow::on_readWriteButton_clicked() 9 23 24 if (auto *reply = modbusDevice>sendReadWriteRequest(readRequest(), writeUnit, 25 ui>serverEdit>value())) else 33 } 34 35 if (modbusDevice) 36 modbusDevice>disconnectDevice(); 37 delete modbusDevice;
(5)、QModbusServer和QModbusClient类了解

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

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

1 This is the cplete list of members for QModbusTcpServer, including inherited members. 2 3 enum ConnectionParameter 4 enum Error 5 enum Option 6 enum State 7 QModbusTcpServer(QObject *) 8 ~QModbusTcpServer() 9 blockSignals(bool ) 10 childEvent(QChildEvent *) 11 children() const 12 close() 13 close() 14 connect(const QObject *, const char *, const QObject *, const char *, Qt::ConnectionType ) 15 connect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &, Qt::ConnectionType ) 16 connect(const QObject *, const char *, const char *, Qt::ConnectionType ) const 17 connect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction , Qt::ConnectionType ) 18 connect(const QObject *, PointerToMemberFunction , Functor ) 19 connect(const QObject *, PointerToMemberFunction , const QObject *, Functor , Qt::ConnectionType ) 20 connectDevice() : bool 21 connectNotify(const QMetaMethod &) 22 connectionParameter(int ) const : QVariant 23 custEvent(QEvent *) 24 d_ptr : 25 data(QModbusDataUnit *) const : bool 26 data(QModbusDataUnit::RegisterType , quint16 , quint16 *) const : bool 27 dataWritten(QModbusDataUnit::RegisterType , int , int ) 28 deleteLater() 29 destroyed(QObject *) 30 disconnect(const QObject *, const char *, const QObject *, const char *) 31 disconnect(const QObject *, const QMetaMethod &, const QObject *, const QMetaMethod &) 32 disconnect(const QMetaObject::Connection &) 33 disconnect(const char *, const QObject *, const char *) const 34 disconnect(const QObject *, const char *) const 35 disconnect(const QObject *, PointerToMemberFunction , const QObject *, PointerToMemberFunction ) 36 disconnectDevice() 37 disconnectNotify(const QMetaMethod &) 38 dumpObjectInfo() const 39 dumpObjectTree() const 40 dynamicPropertyNames() const 41 error() const : Error 42 errorOccurred(QModbusDevice::Error ) 43 errorString() const : QString 44 event(QEvent *) 45 eventFilter(QObject *, QEvent *) 46 findChild(const QString &, Qt::FindChildOptions ) const 47 findChildren(const QString &, Qt::FindChildOptions ) const 48 findChildren(const QRegExp &, Qt::FindChildOptions ) const 49 findChildren(const QRegularExpression &, Qt::FindChildOptions ) const 50 inherits(const char *) const 51 installEventFilter(QObject *) 52 isSignalConnected(const QMetaMethod &) const 53 isWidgetType() const 54 isWindowType() const 55 killTimer(int ) 56 metaObject() const 57 moveToThread(QThread *) 58 objectName() const 59 objectNameChanged(const QString &) 60 open() : bool 61 open() : bool 62 parent() const processPrivateRequest(const QModbusPdu &) : QModbusResponse 64 processRequest(const QModbusPdu &) : QModbusResponse 65 processRequest(const QModbusPdu &) : QModbusResponse 66 processesBroadcast() const : bool 67 property(const char *) const 68 readData(QModbusDataUnit *) const : bool 69 receivers(const char *) const 70 removeEventFilter(QObject *) 71 sender() const 72 senderSignalIndex() const 73 serverAddress() const : int 74 setConnectionParameter(int , const QVariant &) 75 setData(const QModbusDataUnit &) : bool 76 setData(QModbusDataUnit::RegisterType , quint16 , quint16 ) : bool 77 setError(const QString &, QModbusDevice::Error ) 78 setMap(const QModbusDataUnitMap &) : bool 79 setObjectName(const QString &) 80 setParent(QObject *) 81 setProperty(const char *, const QVariant &) 82 setServerAddress(int ) 83 setState(QModbusDevice::State ) 84 setValue(int , const QVariant &) : bool 85 signalsBlocked() const 86 startTimer(int , Qt::TimerType ) 87 startTimer(std::chrono::milliseconds , Qt::TimerType ) 88 state() const : State 89 stateChanged(QModbusDevice::State ) 90 staticMetaObject : 91 staticQtMetaObject : 92 thread() const 93 timerEvent(QTimerEvent *) 94 tr(const char *, const char *, int ) 95 value(int ) const : QVariant 96 writeData(const QModbusDataUnit &) : bool

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

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

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

  .pro中添加:

QT += core gui sql serialport serialbus

  主要创建内容和读写操作

1 #ifndef MODBUSSERVER_H 2 #define MODBUSSERVER_H 3 4 #include <QObject> 5 #include <QModbusServer> 6 #include <QModbusRtuSerialSlave> 7 #include <QModbusTcpServer> 8 #include <QSerialPort> 9 10 /* 11 * 12 * 13 * modbus slave 从站 14 * 15 * modbusSlove_* m_slave = new modbusSlove_(this); 16 * 17 * initModbusSerialSlove() 18 * 19 * connectDevice() 20 * 21 * //寄存器值发生改变,连接这个信号 22 void registerData_signal(int address,int value); 23 * 24 */ 25 class ModbusServer : public QObject 26 ; 67 68 signals: 69 //寄存器值发生改变 70 void registerData_signal(int address,int value); 71 //发生错误 72 void error_signal(QString errorString); 73 /*state :1 connect ,0:unconnect 74 *状态发生改变 75 */ 76 void stateChanged_signal(int state); 77 public slots: 78 private slots: 79 /** 80 * @projectName testMyClass 81 * @brief 更新寄存器数据 82 * @author SMY 83 * @date 20190326 84 */ 85 void updateData(QModbusDataUnit::RegisterType table, int address, int size); 86 /** 87 * @projectName testMyClass 88 * @brief device error 89 * @author SMY 90 * @date 20190327 91 */ 92 void handleDeviceError(QModbusDevice::Error newError); 93 /** 94 * @projectName testMyClass 95 * @brief 连接状态改变 96 * @author SMY 97 * @date 20190327 98 */ 99 void onStateChanged(int state); 100 private: 101 modbusConnection m_mode; 102 QModbusServer* modbusServer; 103 }; 104 105 #endif // MODBUSSERVER__H
1 #include "modbusserver.h" 2 #include <QDebug> 3 4 ModbusServer::ModbusServer(QObject *parent) : QObject(parent) 5 8 9 bool ModbusServer::initModbusSerialServer(QString portName, qint32 baudRate, QSerialPort::DataBits dataBits, 10 QSerialPort::Parity parity, 11 QSerialPort::StopBits stopBits) 12 24 25 QModbusDataUnitMap reg; 26 reg.insert(QModbusDataUnit::Coils, ); 27 reg.insert(QModbusDataUnit::DiscreteInputs, ); 28 reg.insert(QModbusDataUnit::InputRegisters, ); 29 reg.insert(QModbusDataUnit::HoldingRegisters, ); 30 31 modbusServer>setMap(reg); 32 33 modbusServer>setConnectionParameter(QModbusDevice::SerialPortNameParameter, 34 portName); 35 modbusServer>setConnectionParameter(QModbusDevice::SerialBaudRateParameter, 36 baudRate); 37 modbusServer>setConnectionParameter(QModbusDevice::SerialDataBitsParameter, 38 dataBits); 39 modbusServer>setConnectionParameter(QModbusDevice::SerialParityParameter, 40 parity); 41 modbusServer>setConnectionParameter(QModbusDevice::SerialStopBitsParameter, 42 stopBits); 43 44 45 //更新寄存器值 46 connect(modbusServer,&QModbusServer::dataWritten,this, 47 &ModbusServer::updateData); 48 //更新连接状态 49 connect(modbusServer, &QModbusServer::stateChanged, 50 this, &ModbusServer::onStateChanged); 51 //错误发生 52 connect(modbusServer, &QModbusServer::errorOccurred, 53 this, &ModbusServer::handleDeviceError); 54 return 1; 55 56 } 57 58 bool ModbusServer::initModbusNetworkServer(QString address, int port) 59 66 67 //网口 68 modbusServer = new QModbusTcpServer(this); 69 70 m_mode = Tcp; 71 72 if(!modbusServer) 73 77 78 QModbusDataUnitMap reg; 79 reg.insert(QModbusDataUnit::Coils, ); 80 reg.insert(QModbusDataUnit::DiscreteInputs, ); 81 reg.insert(QModbusDataUnit::InputRegisters, ); 82 reg.insert(QModbusDataUnit::HoldingRegisters, ); 83 84 modbusServer>setMap(reg); 85 86 modbusServer>setConnectionParameter(QModbusDevice::NetworkAddressParameter,address); 87 modbusServer>setConnectionParameter(QModbusDevice::NetworkPortParameter,port); 88 89 //更新寄存器值 90 connect(modbusServer,&QModbusServer::dataWritten,this, 91 &ModbusServer::updateData); 92 //更新连接状态 93 connect(modbusServer, &QModbusServer::stateChanged, 94 this, &ModbusServer::onStateChanged); 95 //错误发生 96 connect(modbusServer, &QModbusServer::errorOccurred, 97 this, &ModbusServer::handleDeviceError); 98 99 return true; 100 } 101 102 bool ModbusServer::connectDevice() 103 108 109 void ModbusServer::updateData(QModbusDataUnit::RegisterType table, int address, int size) 110 126 127 emit registerData_signal(address+i,value); 128 129 } 130 } 131 132 void ModbusServer::handleDeviceError(QModbusDevice::Error newError) 133 138 139 void ModbusServer::onStateChanged(int state) 140

mainpp中添加modbus协议调试(参考示例):

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

调用我们封装的modbusServer类:

1 ModbusServer *modbusServer = new ModbusServer(this); 2 3 modbusServer>initModbusNetworkServer("127.0.0.1", 502); 4 modbusServer>connectDevice();

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

接收成功了,我们可以根据需求再进行一些修改:



上一篇:Qt MSVC使用内存泄露检测工具 VLD(Visual Leak Detector)

下一篇:Windows 关于报错:“In included file: too few arguments provided to function-like macro invocat”解决方法


Qt Modbus
Copyright © 2002-2019 k262电脑网 www.k262.cn 皖ICP备2020016292号
温馨提示:部分文章图片数据来源与网络,仅供参考!版权归原作者所有,如有侵权请联系删除!QQ:251442993 热门搜索 网站地图