ModBus协议部分功能码设计与实现方法(QT环境)

写在前面

资源
QT环境下实现资源

1.调研

1.1 什么是Modbus?

Modbus由MODICON公司于1979年开发,是一种工业现场总线协议标准。1996年施耐德公司推出基于以太网TCP/IP的Modbus协议:ModbusTCP。

Modbus协议是一项应用层报文传输协议,包括ASCII、RTU、TCP三种报文类型。

​ 标准的Modbus协议物理层接口有RS232、RS485和以太网接口,采用master/slave方式通信。

常用串行接口区别RS232与RS485

RS232传输电平信号

​ 接口的信号电平值较高(信号“1”为“-3V至-15V”,信号“0”为“3至15V”),易损坏接口电路的芯片,又因为与TTL电平(0“<0.8v”,1“>2.0V”)不兼容故需使用电平转换电路方能与TTL电路连接。另外抗干扰能力差。

RS485传输差分信号

​ 逻辑“1”以两线间的电压差为+(2—6) V表示;逻辑“0”以两线间的电压差为-(2—6)V表示。接口信号电平比RS-232降低了,就不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL电路连接。

通讯距离长短

RS232:

​ RS232传输距离有限,最大传输距离标准值为15米,且只能点对点通讯,最大传输速率最大为20kB/s。

RS485:

​ RS485最大无线传输距离为1200米。最大传输速率为10Mbps,在100Kb/S的传输速率下,才可以达到最大的通信距离。

采用阻抗匹配、低衰减的专用电缆可以达到1800米!超过1200米,可加中继器(最多8只),这样传输距离接近10Km。

能否支持多点通讯

​ **RS232:**RS232接口在总线上只允许连接1个收发器,不能支持多站收发能力,所以只能点对点通信,不支持多点通讯。

​ **RS485:**RS485接口在总线上是允许连接多达128个收发器。即具有多站通讯能力,这样用户可以利用单一的RS485接口方便地建立起设备网络。

通讯线的差别

RS232:

可以采用三芯双绞线、三芯屏蔽线等。

RS485:

可以采用两芯双绞线、两芯屏蔽线等。

在低速、短距离、无干扰的场合可以采用普通的双绞线,反之,在高速、长线传输时,则必须采用阻抗匹配(一般为120Ω)的RS485专用电缆(STP-120Ω(用于RS485 & CAN)一对18AWG),而在干扰恶劣的环境下还应采用铠装型双绞屏蔽电缆(ASTP-120Ω(用于RS485 & CAN)一对18AWG)。

补充:

​ 即然RS232传输距离只有15米这么短,那么有什么作用呢?

​ 其实它的应用非常广泛,可以连接各种设备,例如监控、其它的设备升级或调试等都可能需要用到它。功能与USB比较接近,随着USB端口的越来越普遍,将会出现更多的把USB转换成RS-232或其它接口的转换装置。

​ 通过USB接口可连接更多的RS-232设备,不仅可获得更高的传输速度,实现真正的即插即用,同时解决了USB接口不能远距离传输的缺点(USB通讯距离在5米内)。

1.2 使用Modbus有什么好处

​ 在城市轻轨风机控制系统中,变频器凭借着其变频节能,能软启,可调速等特点有着广泛的应用。但变频器本身对数据的计算、存储、分析能力不足,智能化程度不高等缺点也越发明显。传统的控制常采用硬接线方式,只能单纯的实现控制,但对变频器内部的信息不能查询也无法控制。而RS-485协议通信方法控制变频器方式编程工作量较大。通过变频器与PLC通信能有效的避免这些不足,提高变频器控制的自动化水平。

​ Modbus是被广泛应用于PLC与变频器通信的网络协议。采用Modbus通信方式,速度快、距离远、效率高、工作稳定、编程简单等优点。在城市轻轨单向运转的耐高温轴流排热风机控制项目中,风机变频器就是通过Modbus的通信方式实现的,通过Modbus通信将变频器中的电气参数如电流、电压、运行频率和报警信息传送到PLC中,实现PLC对风机启动,旁路切换等自动控制和状态监控。

1.3 Modbus的应用场景

Modbus RTU通常具有优良的通讯能力和更大的存储容量,适用于更恶劣的温度和湿度环境,提供更多的计算功能。广泛应用于智慧水利、智慧环保、工业物联网、智慧市政等,如:水文远程监控、水资源远程监测、水源井远程监控、山洪灾害监测、地质灾害监测、河道监测预警、气象监测、泵站远程监控、污染源远程监控等远程测控领域。

1.4 技术点验证

QT串口通信验证

​ QT 的串口通信,QT为我们提供了一个用于串口通信的类QSerialPort,该类可以帮助我们实现串口通信,在QT中我编写了一个案例验证了是可以实现串口通信的。

​ 首先是创建一个QSerialPort对象,然后调用对应的方法设置串口号等参数,例如调用setPortName方法设置端口号。当串口成功打开后,QSerialPort对象会监听IO口是否有数据,当有数据时会发出readyRead信号,我们只需要编写一个槽去接收即可,当向串口写入数据时,我们只需要调用QSerialPort对象的write方法即可。

QT网络通信验证

​ QT 的网络通信,在Qt中提供了QTcpSocket类来编写客户端程序,使用QTcpServer类编写服务器端程序。我们在服务器端进行端口的设置,一旦发现客户端的连接请求,就会发出newConnection()信号,可以关联这个信号到我们自己的槽进行数据的读取和发送。而在客户端,一旦有数据到来就会发出readyRead()信号,可以关联此信号进行数据的接收。

2.Modbus的整体设计流程

2.1 Modbus_RTU 从站总体设计流程

​ Modbus_RTU从站项目,我是基于QT 5.6.3 版本开发的,使用的是C++作为开发语言,下面是我的项目的目录结构。

在这里插入图片描述

文件夹名称存放文件类型
analysis存放用于报文解析的类文件
uartPort存放串口通信的类文件
dataini存放寄存器和线圈数据的ini文件
logi存放通信历史信息的txt文件
utils存放用到的辅助工具类文件

RTU从站项目总体框图如下所示:

在这里插入图片描述

RTU从站总体流程图

在这里插入图片描述

上述流程描述:

​ 主程序启动后进行界面初始化,界面初始化完成后,开启串口线程,和数据解析线程。串口线程负责数据的收发,数据解析线程负责对接收到的报文进行解析。

​ 当用户在界面上配置好串口参数后,例如串口号,波特率等,用户点击打开串口按钮,UI线程会向串口线程发送upCfg2Uart信号,串口线程会执行对应的槽函数打开串口,如果打开失败,串口线程会向UI线程发送errOccurred信号,UI线程会执行对应的槽函数弹出错误提示框。

​ 当串口的readyread信号被触发表明有数据到来,这时串口线程开启定时器,当定时器时间溢出后,表明接收完了一帧数据,将会发送readyread(sendArray)信号并停止定时器,UI线程会执行对应的槽函数。

​ 当数据解析线程接收到UI线程的Start信号后,数据解析线程会执行对应的槽函数,进行数据的解析,当数据解析完毕后,会向UI线程发送analysis_over信号UI线程会执行对应的槽函数向串口线程发送sendData2Uart信号,串口线程会执行对应的槽函数进行数据发送。

​ 当用户退出程序后结束。

2.2 Modbus_RTU 主站总体设计流程

​ Modbus_RTU主站项目,我是基于QT 5.6.3 版本开发的,使用的是C++作为开发语言,下面是我的项目的目录结构。

在这里插入图片描述

文件夹名称存放文件类型
modbusmsg存放用于报文解析的类文件
uartPort存放串口通信的类文件
config存放寄存器和线圈数据的ini文件,通信历史信息的txt文件
utils存放用到的辅助工具类文件

RTU主站项目总体框图如下所示:

在这里插入图片描述

RTU主站总体流程图

在这里插入图片描述

上述流程描述:

​ 主程序启动后进行界面初始化,界面初始化完成后,开启串口通信线程,和数据封装线程。串口通信线程负责数据的收发,数据封装线程负责对请求报文进行封装。

​ 当用户在界面上配置好串口参数后,例如串口号,波特率等,用户点击打开串口按钮,UI线程会向串口线程发送upCfg2Uart信号,串口线程会执行对应的槽函数打开串口,如果打开失败,串口线程会向UI线程发送errOccurred信号,UI线程会执行对应的槽函数弹出错误提示框。

​ 当用户配置好数据信息后点击发送按钮后,会发送信号给数据封装线程。

​ 当数据封装线程接收到UI线程的Start信号后,会进行报文的封装,封装完毕后会发送package_over信号给主线程,主线程发送信号给串口通信线程去发送数据。

​ 当用户退出程序后结束。

2.3 Modbus_TCP 从站总体设计流程

Modbus_TCP从站项目,我是基于QT 5.6.3 版本开发的,使用的是C++作为开发语言,下面是我的项目的目录结构。
在这里插入图片描述

文件夹名称存放文件类型
analysis存放用于报文解析的类文件
network存放网络通信的类文件
dataini存放寄存器和线圈数据的ini文件
logi存放通信历史信息的txt文件
utils存放用到的辅助工具类文件

TCP从站项目总体框图如下所示:

在这里插入图片描述

TCP从站总体流程图
在这里插入图片描述

上述流程描述:

​ 主程序启动后进行界面初始化,界面初始化完成后,开启网络通信线程,和数据解析线程。网络通信线程负责数据的收发,数据解析线程负责对接收到的报文进行解析。

​ 当用户在界面上配置好网络通信参数后,例如IP地址,端口号,用户点击监听按钮,会创建Myserver对象并调用listen方法进行监听。

​ 当MySocket的readyread信号被触发表明有数据到来,这时会执行deal_readyRead槽函数接收数据,当数据接收完成后会向UI线程发送AddMessage和revcMsg信号。

​ 当数据解析线程接收到UI线程的Start信号后,数据解析线程会执行对应的槽函数,进行数据的解析,当数据解析完毕后,会向UI线程发送analysis_over信号UI线程会执行对应的槽函数向网络线程发送WriteMessage信号,网络线程会执行对应的槽函数进行数据发送。

​ 当用户退出程序后结束。

2.4 Modbus_TCP 主站总体设计流程

​ Modbus_TCP主站项目,我是基于QT 5.6.3 版本开发的,使用的是C++作为开发语言,下面是我的项目的目录结构。

在这里插入图片描述

文件夹名称存放文件类型
modbusmsg存放用于报文解析的类文件
config存放寄存器和线圈数据的ini文件,通信历史信息的txt文件
utils存放用到的辅助工具类文件

TCP主站项目总体框图如下所示:

在这里插入图片描述

TCP主站总体流程图

在这里插入图片描述

上述流程描述:

​ 主程序启动后进行界面初始化,界面初始化完成后,开启网络通信线程,和数据封装线程。网络通信线程负责数据的收发,数据封装线程负责对接收到的报文进行封装。

​ 当用户在界面上配置好网络通信参数后,例如IP地址,端口号,用户点击监听按钮,会创建Myserver对象并调用connect方法进行连接。

​ 当用户配置好数据信息后点击发送按钮后,会发送信号给数据封装线程。

​ 当数据封装线程接收到UI线程的封装信号后,数据封装线程会执行对应的槽函数,进行数据的封装,当数据封装完毕后,会向UI线程发送pack_over信号UI线程会执行对应的槽函数向网络线程发送WriteMessage信号,网络线程会执行对应的槽函数进行数据发送。

当用户退出程序后结束。

3.详细设计

3.1 Modbus_RTU 从站详细设计流程

​ RTU从站详细分为以下几个模块,界面布局模块,串口通信模块,报文解析模块,历史信息记录模块,系统时间显示模块。下面分别详细介绍实现方案。

3.1.1 界面布局

RTU从站界面布局如下图所示:

在这里插入图片描述

3.1.2 串口模块设计

串口类(uartPort)设计:

​ 主要信号与槽设计

信号描述
void openError(bool);传输串口开闭标志给主线程
void readyread(QByteArray);传输读取的串口数据给主线程
void errOccurred(QString);传输串口错误信息给主线程
描述
void updateComCfg(comcfg_t cfg);主线程传入串口配置信息后打开串口
void readyRead();串口对象接受导数据后执行
void writeData(QByteArray);接收到主线程数据后通过串口发送
void timerOut();数据帧间隔处理槽

串口类处理流程:

在这里插入图片描述

上述流程描述:

​ 当用户点击打开串口按钮,UI线程会向串口线程发送upCfg2Uart信号,串口线程会执行对应的槽函数打开串口,如果打开失败,串口线程会向UI线程发送errOccurred信号,UI线程会执行对应的槽函数弹出错误提示框。

​ 当串口的readyread信号被触发表明有数据到来,这时串口线程开启定时器,当定时器时间溢出后,表明接收完了一帧数据,将会发送readyread(sendArray)信号并停止定时器,UI线程会执行对应的槽函数。

​ 当用户点击关闭串口按钮后,UI线程会向串口线程发送closeUart信号,串口线程会执行对应的槽函数关闭串口并销毁串口对象。

3.1.3 报文解析模块设计

报文分析类(analysis)设计:

​ 主要信号与槽设计

信号描述
void analysis_over(QByteArray ba);回应报文
void wirtTablec(quint16 num, quint16 satrtaddr,QString bac);传输数据给主线程显示写入线圈状态
void wirtTabler(quint16 num, quint16 satrtaddr,quint16 bar[]);传输数据给主线程显示写入寄存器数据
描述
void recvModbusMsg(QByteArray msg,quint8 addr);接收报文
void readIniCoilTobyte(QByteArray &ba,quint16 startaddr,quint16 num);封装ini线圈状态to byte
void readByteToCoilIni(QByteArray ba,quint16 startaddr,quint16 num);封装字节数据to ini线圈状态
void parse_Modbus_MB_satae(MB_satae);状态处理
void parse_Modbus_True_Msg(QByteArray msg);正常帧处理
void parse_Modbus_Exception_Handling03(); void parse_Modbus_Exception_Handling02(); void parse_Modbus_Exception_Handling01();异常处理
MB_satae parse_Modbus_Msg(QByteArray msg);解析报文查询状态
void func_01(); void func_03(); void func_0f(); void func_10();功能码处理函数

报文分析类处理流程:

在这里插入图片描述

上述流程描述:

​ 当数据解析线程接收到UI线程的Start信号后,数据解析线程会执行void recvModbusMsg(QByteArray msg,quint8 addr)槽函数接收数据,recvModbusMsg槽函数会调用parse_Modbus_MB_satae进行数据的解析给出报文状态。

1.MB_SLAVE_STATE_PACKET_OTHER 状态:不做响应
Modbus正常报文须满足小于256字节,大于8个字节,CRC校验是否满足条件,当接收到的报文不满足这个条件是会返回

MB_SLAVE_STATE_PACKET_OTHER状态。

2.MB_SLAVE_STATE__ADDR_ERROR状态:从机地址异常。

​ 当发送的报文不是给本机时返回

MB_SLAVE_STATE__ADDR_ERROR

3.MB_SLAVE_STATE_DATA_ERROR状态:数据范围异常

​ Modbus正常报文须满足小于256字节,大于8个字节,读线圈数量需满足小于等于2000,写线圈需满足小于等于1968,读寄存器需满足小于125,写寄存器需满足小于123。如果条件不满足返回

MB_SLAVE_STATE_DATA_ERROR状态。

4.MB_SLAVE_STATE_DATAADDR_ERROR状态:数据地址异常

​ Modbus地址范围0~65535,当数量OK且数量+起始地址OK。如果不满足返回

MB_SLAVE_STATE_DATAADDR_ERROR状态。

5.MB_SLAVE_STATE_FUNCTION_ERROR状态:功能码错误。

​ 当报文里不是允许的功能码返回该状态。

6.MB_SLAVE_STATE_PACKET_PROCESS状态:一切正常。

​ 当报文没有问题返回该状态。

处理完毕后调用parse_Modbus_MB_satae对状态进行处理,根据状态去执行对应的槽,执行完毕后会向UI线程发送analysis_over信号。

3.1.4 历史信息记录模块设计

历史信息记录处理流程:

在这里插入图片描述

​ 开启一个定时器,并定时两分钟中,当定时时间到了就会执行对应的槽去获取界面通信历史信息,并将信息写入到TXT文件中。

3.1.5 系统时间显示模块设计

系统时间显示处理流程:

在这里插入图片描述

开启一个定时器,并定时1秒中,当定时时间到了就会执行对应的槽去获取系统时间,并更新系统时间。

3.2 Modbus_RTU 主站详细设计流程

3.2.1 界面布局

RTU主站界面布局如下图所示

3.2.1.1 主界面布局

在这里插入图片描述

3.2.1.2 数据设置界面布局

在这里插入图片描述

3.2.1.3 历史信息显示界面

在这里插入图片描述

3.2.2 串口模块设计

串口类(uartPort)设计:

​ 主要信号与槽设计

信号描述
void openError(bool);传输串口开闭标志给主线程
void readyread(QByteArray);传输读取的串口数据给主线程
void errOccurred(QString);传输串口错误信息给主线程
描述
void updateComCfg(comcfg_t cfg);主线程传入串口配置信息后打开串口
void readyRead();串口对象接受导数据后执行
void writeData(QByteArray);接收到主线程数据后通过串口发送
void timerOut();数据帧间隔处理槽

串口类处理流程:

在这里插入图片描述

上述流程描述:

​ 当用户点击打开串口按钮,UI线程会向串口线程发送upCfg2Uart信号,串口线程会执行对应的槽函数打开串口,如果打开失败,串口线程会向UI线程发送errOccurred信号,UI线程会执行对应的槽函数弹出错误提示框。

​ 当串口的readyread信号被触发表明有数据到来,这时串口线程开启定时器,当定时器时间溢出后,表明接收完了一帧数据,将会发送readyread(sendArray)信号并停止定时器,UI线程会执行对应的槽函数。

​ 当用户点击关闭串口按钮后,UI线程会向串口线程发送closeUart信号,串口线程会执行对应的槽函数关闭串口并销毁串口对象。

3.2.3 报文封装模块设计

报文封装类(modbusmsg)设计:

​ 主要信号与槽设计

信号描述
void package_over(QByteArray package_msg);报文封装完成
void showMsgtoUi(QString);显示日志信息到ui线程
描述
void packageSartc(quint8,quint8,quint16,QList,QByteArray ba,quint16 cnums);主线程传入数据开始处理100f功能报文
void packageSartr(quint8 addr,quint8 funcode,quint16 startaddr,quint16 numreg);主线程传入数据开始处理0103功能报文
void funCode01(quint8 addr,quint16 startaddr,quint16 numreg); void funCode03(quint8 addr,quint16 startaddr,quint16 numreg); void funCode0f(quint8 addr,quint16 startaddr,QList,QByteArray ba,quint16 cnums); void funCode10(quint8 addr,quint16 startaddr,QList);报文封装函数

报文封装类处理流程:

在这里插入图片描述

	当用户配置好数据信息后点击发送按钮后,会发送信号给数据封装线程。

​ 当数据封装线程接收到UI线程的Start信号后,会进行报文的封装,封装完毕后会发送package_over信号给主线程,主线程发送信号给串口通信线程去发送数据。

3.2.4 历史记录查看模块

当用户点击查看按钮时

1.判断有无文件路径

2.如果有文件路径则进入显示历史消息函数进行显示

3.如果没有文件路径先弹出对话框进行选择路径

4.如果选择路径为空则提示打开失败

5.如果路径合法则提示成功,并进入显示历史消息函数进行显示

历史信息显示处理流程:
在这里插入图片描述

3.2.5 系统时间显示模块

系统时间显示处理流程:

在这里插入图片描述

开启一个定时器,并定时1秒中,当定时时间到了就会执行对应的槽去获取系统时间,并更新系统时间。

3.3 Modbus_TCP 从站详细设计流程

​ TCP从站详细分为以下几个模块,界面布局模块,网络通信模块,报文解析模块,历史信息记录模块,系统时间显示模块。下面分别详细介绍实现方案。

3.1.1 界面布局

TCP从站界面布局如下图所示:
在这里插入图片描述

3.1.2 myserver类设计

TCP服务端(myserver)设计:

​ myserver类继承与QTCPServer类,我重写了QTCPServer类的void incomingConnection(qintptr socketDescriptor)方法,当有客户端连接时会自动执行改槽。

​ 主要信号与槽设计

描述
void SetThread(int num);设置最大线程数
int GetMinLoadThread();获取最少线程数
void incomingConnection(qintptr socketDescriptor);重写的
void AddInf(MySocket* mysocket,int index);添加客户端套接字的信息
void RemoveInf(MySocket* mysocket);移除客户端的套接字信息

myserver类处理流程:

在这里插入图片描述

上述流程描述:

​ 当用户设置好ip和端口信息后点击监听按钮,会进行监听,当有客户端连接时会发送Create信号去创建套接字对象,当创建成功后会发送AddList信号将创建成功的套戒字信息添加到列表中,当客户端断开时会发送RemoveList信号将对应的客户端信息从列表中移除。

​ 然后等待新的客户端连接进来。

3.1.2 mysocket类设计

TCP服务端(mysocket)设计:

​ mysocket类继承与QTcpSocket类。

主要信号与槽设计

信号描述
void AddMessage(QByteArray msg);发送给UI显示
void revcMsg(QByteArray msg);接收客户端数据
void WriteMessage(QByteArray ba);UI发送过来数据
void DeleteSocket();主动关闭socket
描述
void deal_readyRead();读取数据槽函数
void deal_disconnect();断开连接槽函数
void deal_write(QByteArray ba);写入数据槽函数

​ mysocket类处理流程:

在这里插入图片描述

上述流程描述:

​ 当建立好连接后该类负责数据的读取和发送,当有数据到来时会触发readyRead信号去执行deal_readyRead槽去接收数据当数据接收完毕后会向UI线程发送AddMessage和revcMsg信号。

​ 当WriteMessage信号被触发时,会执行deal_write槽去写数据。

​ 当DeleteSocket或Disconnected信号被触发时会断开连接。

3.1.3 mythread类设计

TCP服务端(mythread)设计:

	mythread类继承与QThread类,重写run方法。SocketHelper类用于创建套接字对象并加入到线程中。

SocketHelper类主要信号与槽设计

信号描述
void Create(qintptr socketDescriptor,int index);创建套接字对象
void AddList(MySocket* tcpsocket,int index);添加信息套接字信息
void RemoveList(MySocket* tcpsocket);移除信息套接字信息
描述
void CreateSocket(qintptr socketDescriptor,int index);创建一个socket,传入socket通信标识符,和线程编号

​ mythread类处理流程:

在这里插入图片描述

上述流程描述:

​ 当有新的客户端连接进来时会触发Creat信号去创建Socket对象,当创建完毕后会发送Addlist信号,当连接断开会发送RemoveList。

3.1.3 报文解析模块设计

报文分析类(analysis)设计:

​ 主要信号与槽设计

信号描述
void analysis_over(QByteArray ba);回应报文
void wirtTablec(quint16 num, quint16 satrtaddr,QString bac);传输数据给主线程显示写入线圈状态
void wirtTabler(quint16 num, quint16 satrtaddr,quint16 bar[]);传输数据给主线程显示写入寄存器数据
描述
void recvModbusMsg(QByteArray msg,quint8 addr);接收报文
void readIniCoilTobyte(QByteArray &ba,quint16 startaddr,quint16 num);封装ini线圈状态to byte
void readByteToCoilIni(QByteArray ba,quint16 startaddr,quint16 num);封装字节数据to ini线圈状态
void parse_Modbus_MB_satae(MB_satae);状态处理
void parse_Modbus_True_Msg(QByteArray msg);正常帧处理
void parse_Modbus_Exception_Handling03(); void parse_Modbus_Exception_Handling02(); void parse_Modbus_Exception_Handling01();异常处理
MB_satae parse_Modbus_Msg(QByteArray msg);解析报文查询状态
void func_01(); void func_03(); void func_0f(); void func_10();功能码处理函数

报文分析类处理流程:

在这里插入图片描述

上述流程描述:

​ 当数据解析线程接收到UI线程的Start信号后,数据解析线程会执行void recvModbusMsg(QByteArray msg,quint8 addr)槽函数接收数据,recvModbusMsg槽函数会调用parse_Modbus_MB_satae进行数据的解析给出报文状态。

1.MB_SLAVE_STATE_PACKET_OTHER 状态:不做响应
Modbus正常报文须满足小于259字节,大于12个字节,协议标识是否满足条件,当接收到的报文不满足这个条件是会返回

MB_SLAVE_STATE_PACKET_OTHER状态。

2.MB_SLAVE_STATE__ADDR_ERROR状态:从机地址异常。

​ 当发送的报文不是给本机时返回

MB_SLAVE_STATE__ADDR_ERROR

3.MB_SLAVE_STATE_DATA_ERROR状态:数据范围异常

​ Modbus正常报文须满足小于259字节,大于12个字节,读线圈数量需满足小于等于2000,写线圈需满足小于等于1968,读寄存器需满足小于125,写寄存器需满足小于123。如果条件不满足返回

MB_SLAVE_STATE_DATA_ERROR状态。

4.MB_SLAVE_STATE_DATAADDR_ERROR状态:数据地址异常

​ Modbus地址范围0~65535,当数量OK且数量+起始地址OK。如果不满足返回

MB_SLAVE_STATE_DATAADDR_ERROR状态。

5.MB_SLAVE_STATE_FUNCTION_ERROR状态:功能码错误。

​ 当报文里不是允许的功能码返回该状态。

6.MB_SLAVE_STATE_PACKET_PROCESS状态:一切正常。

​ 当报文没有问题返回该状态。

处理完毕后调用parse_Modbus_MB_satae对状态进行处理,根据状态去执行对应的槽,执行完毕后会向UI线程发送analysis_over信号。

3.1.4 历史信息记录模块设计

历史信息记录处理流程:
在这里插入图片描述

​ 开启一个定时器,并定时两分钟中,当定时时间到了就会执行对应的槽去获取界面通信历史信息,并将信息写入到TXT文件中。

3.1.5 系统时间显示模块设计

系统时间显示处理流程:
在这里插入图片描述

开启一个定时器,并定时1秒中,当定时时间到了就会执行对应的槽去获取系统时间,并更新系统时间。

3.4 Modbus_TCP 主站详细设计流程

3.4.1 界面布局

3.4.1.1 主界面布局

在这里插入图片描述

3.4.1.2 数据设置界面布局

在这里插入图片描述

3.4.1.3 历史信息显示界面

在这里插入图片描述

3.4.2 报文封装模块设计

报文封装类(modbusmsg)设计:

​ 主要信号与槽设计

信号描述
void package_over(QByteArray package_msg);报文封装完成
void showMsgtoUi(QString);显示日志信息到ui线程
描述
void packageSartc(quint8,quint8,quint16,QList,QByteArray ba,quint16 cnums);主线程传入数据开始处理100f功能报文
void packageSartr(quint8 addr,quint8 funcode,quint16 startaddr,quint16 numreg);主线程传入数据开始处理0103功能报文
void funCode01(quint8 addr,quint16 startaddr,quint16 numreg); void funCode03(quint8 addr,quint16 startaddr,quint16 numreg); void funCode0f(quint8 addr,quint16 startaddr,QList,QByteArray ba,quint16 cnums); void funCode10(quint8 addr,quint16 startaddr,QList);报文封装函数

报文封装类处理流程:

在这里插入图片描述

	当用户配置好数据信息后点击发送按钮后,会发送信号给数据封装线程。

​ 当数据封装线程接收到UI线程的Start信号后,会进行报文的封装,封装完毕后会发送package_over信号给主线程,主线程发送信号给串口通信线程去发送数据。

3.4.3 历史记录查看模块设计

当用户点击查看按钮时

1.判断有无文件路径

2.如果有文件路径则进入显示历史消息函数进行显示

3.如果没有文件路径先弹出对话框进行选择路径

4.如果选择路径为空则提示打开失败

5.如果路径合法则提示成功,并进入显示历史消息函数进行显示

历史信息显示处理流程:

在这里插入图片描述

3.4.4 系统时间显示模块设计

系统时间显示处理流程:

在这里插入图片描述

umreg); void funCode0f(quint8 addr,quint16 startaddr,QList,QByteArray ba,quint16 cnums); void funCode10(quint8 addr,quint16 startaddr,QList); | 报文封装函数 |

报文封装类处理流程:

	当用户配置好数据信息后点击发送按钮后,会发送信号给数据封装线程。

​ 当数据封装线程接收到UI线程的Start信号后,会进行报文的封装,封装完毕后会发送package_over信号给主线程,主线程发送信号给串口通信线程去发送数据。

3.4.3 历史记录查看模块设计

当用户点击查看按钮时

1.判断有无文件路径

2.如果有文件路径则进入显示历史消息函数进行显示

3.如果没有文件路径先弹出对话框进行选择路径

4.如果选择路径为空则提示打开失败

5.如果路径合法则提示成功,并进入显示历史消息函数进行显示

历史信息显示处理流程:

[外链图片转存中…(img-U0iuJrR3-1687692972775)]

3.4.4 系统时间显示模块设计

系统时间显示处理流程:

[外链图片转存中…(img-FZM8bRyY-1687692972775)]

开启一个定时器,并定时1秒中,当定时时间到了就会执行对应的槽去获取系统时间,并更新系统时间。

  • 6
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Qt中使用Modbus RTU(串行通信)协议可以通过一些第三方库来实现。以下是一个基本的示例代: 首先,你需要安装QModbus库,可以通过Qt Maintenance Tool或者在.pro文件中添加`QT += modbus`来安装。 ```cpp #include <QCoreApplication> #include <QModbusDataUnit> #include <QModbusRtuSerialMaster> #include <QSerialPort> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 创建串口对象 QSerialPort serialPort; serialPort.setPortName("COM1"); // 设置串口名称 serialPort.setBaudRate(QSerialPort::Baud9600); // 设置波特率 serialPort.setDataBits(QSerialPort::Data8); // 设置数据位 serialPort.setParity(QSerialPort::NoParity); // 设置校验位 serialPort.setStopBits(QSerialPort::OneStop); // 设置停止位 serialPort.setFlowControl(QSerialPort::NoFlowControl); // 设置流控制 // 打开串口 if (!serialPort.open(QIODevice::ReadWrite)) { qDebug() << "Failed to open serial port:" << serialPort.errorString(); return 1; } // 创建Modbus RTU主站对象 QModbusRtuSerialMaster modbusMaster; modbusMaster.setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1"); modbusMaster.setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600); modbusMaster.setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); modbusMaster.setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); modbusMaster.setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop); modbusMaster.setConnectionParameter(QModbusDevice::SerialFlowControlParameter, QSerialPort::NoFlowControl); // 设置Modbus RTU主站对象的串口 modbusMaster.setSerialPort(&serialPort); // 连接Modbus设备 if (!modbusMaster.connectDevice()) { qDebug() << "Failed to connect to Modbus device:" << modbusMaster.errorString(); return 1; } // 读取Modbus保持寄存器 QModbusDataUnit readUnit(QModbusDataUnit::HoldingRegisters, 0, 3); // 从地址0开始读取3个寄存器 if (auto *reply = modbusMaster.sendReadRequest(readUnit, 1)) { // Unit Identifier为1 if (!reply->isFinished()) { // 等待读取完成 QEventLoop loop; QObject::connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit); loop.exec(); } // 检查是否发生错误 if (reply->error() == QModbusDevice::NoError) { // 读取成功,处理返回数据 const QModbusDataUnit unit = reply->result(); for (int i = 0; i < unit.valueCount(); ++i) { qDebug() << "Register" << i << "=" << unit.value(i); } } else { qDebug() << "Read request error:" << reply->errorString(); } // 释放reply对象 reply->deleteLater(); } else { qDebug() << "Failed to send read request:" << modbusMaster.errorString(); } // 关闭Modbus设备连接 modbusMaster.disconnectDevice(); // 关闭串口 serialPort.close(); return 0; } ``` 以上示例代演示了如何使用QModbus库进行Modbus RTU通信。你可以根据自己的需求对代进行修改和扩展。注意要修改串口的名称和其他参数,使其与你的实际环境匹配。还可以根据Modbus设备的要求修改读取或写入的寄存器地址和数量。 需要注意的是,这只是一个简单的示例,实际使用中可能还需要处理异常情况、错误处理、信号与槽的连接等。请参考官方文档和其他资源以获取更详细的信息和示例代

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值