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

2023-10-27

写在前面

资源
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秒中,当定时时间到了就会执行对应的槽去获取系统时间,并更新系统时间。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 如何通过 Qt 创建网络服务 [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • QGraphicsScene没有删除QWidget的功能

    QGraphicsScene 有一个addWidget QWidget 有函数 但是没有对应的removeWidget QWidget 它只有removeItem QGraphicsItem 如何删除 QWidget 这是一个基本示例 看看
  • 第一个随机数始终小于其余随机数

    我碰巧注意到 在 C 中 使用 std rand 方法调用的第一个随机数大多数时候都明显小于第二个随机数 关于 Qt 实现 第一个几乎总是小几个数量级 qsrand QTime currentTime msec qDebug lt lt q
  • 如何在qt中进行异步文件io?

    我想知道如何在qt中实现异步文件io 这在普通的 qt 中是否可以实现 或者有人需要使用另一个库 例如 libuv 来实现这样的事情 我正在查看 QDataStream 但即使它是一个 流 它也不是非阻塞的 我想一种解决方案是制作一个在内部
  • SWI-Prolog 与 C++ 接口的问题

    我试图让 SWI Prolog 与 C 很好地配合 现在束手无策 现在 在我开始准确解释我的问题是什么之前 我想首先说明我的项目是关于什么的以及我选择了哪些工具来开发解决方案 我的教授分配给我的任务是开发一个 GUI 程序 作为 SWI p
  • 仅在内部/外部抚摸路径?

    Given a QPainterPath http qt project org doc qt 4 8 qpainterpath html如何仅在路径的内侧或外侧边缘 或非闭合路径的左侧或右侧 描边路径 QPainter strokePat
  • 如何在 Qt Creator 中编辑 QtWebKit 的右键上下文菜单?

    好吧 这是我的困境 我正在使用 Qt Creator 制作一个使用 Webkit 的简单应用程序 我认为 Qt Creator 会有一种简单的方法来使用信号和槽编辑器编辑右键单击上下文菜单 但事实证明这不是真的 我知道 webkit 有与上
  • Qt 人工鼠标点击无法正常工作

    小玩具应用程序可以在这里找到 http gist github com 517445 http gist github com 517445 我正在尝试将人工鼠标事件发送到小部件 我使用QApplication sendEvent为此 接下
  • new 运算符(以及 malloc)无法分配约 450 MB 的内存 [重复]

    这个问题在这里已经有答案了 我正在开发一个程序 该程序在内存中存储大约 2 2 亿个短值的数组 该数据块的分配方式如下 short arrayName new short SIZE OF ARRAY 然后将文件的内容读入内存 在团队中的另一
  • QMutex 是否需要是静态的,以便此类实例的其他线程调用知道暂停其操作?

    从多个线程调用以下附加函数 我不希望数据重写附加 因为计数器尚未增加 除了当前使用 Append 的线程之外 这是否会挂起所有进入的线程 或者其他线程会继续运行而不追加数据吗 互斥锁是否需要是 静态 的 或者每个实例都知道要暂停操作吗 如果
  • 获取 QListView 中所有可见项目的简单方法

    我正在尝试使用 Qt Framework 开发一个图像库应用程序 应用程序从所选文件夹加载所有图像 并使用 QListView 控件显示这些图像 但现在我想通过仅加载用户可见的图像来减少内存消耗 由于没有直接函数来获取视图中的所有可见项目
  • 使用 CMake 编译时更改头文件位置会导致缺少 vtable 错误

    对于一个大型 C 项目 我需要从 qmake 过渡到 CMake 但是在处理一个玩具示例时 我遇到了一些我不理解的行为 示例代码具有单个头文件 当该头文件移动到子目录中时 我收到 MainWindow 类缺少 vtable 的错误 CMak
  • 清除pyqt中布局中的所有小部件

    有没有办法清除 删除 布局中的所有小部件 self plot layout QtGui QGridLayout self plot layout setGeometry QtCore QRect 200 200 200 200 self r
  • 如何声明一个带有成员函数指针的函数

    我有一个类 其中的成员变量指向库对象 class myClassA private libraryClass libraryObject 该库类发出事件 以字符串为特征 并提供一种机制 允许客户端类指定在发出事件时应调用的成员函数 因此在m
  • 使用 OpenGL 渲染 QImage

    与我相关的其他问题 https stackoverflow com questions 20126354 render qimage from sooffscreenrenderer in qglwidget 我认为更核心的问题是 如何渲染
  • QML 中可重用的字体属性[重复]

    这个问题在这里已经有答案了 在 QML 中 我希望能够定义一组字体属性以进行简单的语义重用 例如 代替 Text text This is a header font family Encode Sans weight Font Black
  • PyQt4 QPalette 不工作

    btn QtGui QPushButton Button self palettes btn palette palettes setColor btn backgroundRole QtCore Qt green btn setPalet
  • 连接到 QNetworkReply::error 信号

    我正在使用 Qt5 的新连接语法 QNetworkReply 有一个名为error http qt project org doc qt 5 0 qtnetwork qnetworkreply html error 2还有一个函数叫做err
  • QML 列表视图拖放

    我想创建两个 qml 列表视图 可以执行两个功能 拖放一个列表中的项目以更改项目的顺序 跨列表拖放项目 项目将从一个列表中删除并添加到另一个列表中 根据 Qt 文档中的拖放示例 我决定创建两个访问同一列表模型的列表视图 列表模型中的每个项目
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap

随机推荐

  • 我们这一代人的机会是什么?

    大家好 我是苍何 今天作为专业嘉宾参观了 2023 年中国国际智能产业博览会 智博会 是一场以 智汇八方 博采众长 为主题的汇聚全球智能技术和产业创新的盛会 感触颇深 随着中国商业大模型的落地 各大模型百花齐放 落地深耕各大行业细分 toB
  • C#异步委托检测线程结束的方式

    1 异步委托开启多线程 通过BeginInvoke开启异步委托 BeginInvoke返回IAsyncResult类型的对象 可以通过这个对象获得线程状态 比如线程执行状态 TakesAWhileDelegate d1 TakesAWhil
  • Python数据存储:pickle模块的使用讲解

    在机器学习中 我们常常需要把训练好的模型存储起来 这样在进行决策时直接将模型读出 而不需要重新训练模型 这样就大大节约了时间 Python提供的pickle模块就很好地解决了这个问题 它可以序列化对象并保存到磁盘中 并在需要的时候读取出来
  • 后端开发, 接口幂等性是什么意思

    在后端开发中 接口的幂等性是指同一个请求的多次执行所产生的效果与执行一次的效果相同 简而言之 对于同一个接口请求 无论发送多少次 其对资源的状态修改结果都是一致的 幂等性在接口设计和实现中非常重要 特别是在涉及数据修改或资源状态变更的情况下
  • fiddle修改请求返回

    背景 访问某个URL地址 正常返回结果是 code 0 msg success rows US CA GB AR AU AT BE BR CL CN CO HR DK DO EG FI FR DE GR 而我们需要将response在返回之
  • Docker之docker run、docker exec、数据卷、Dockerfile

    文章目录 Docker之docker run docker exec 1 docker run命令介绍 2 docker run it name c1 centos 7 bin bash 3 docker run id name c2 ce
  • android28 startActivity启动流程分析(二)--ActivityStarter对象的获取和execute()

    ActivityManagerService startActivityAsUser gt ActivityStarter execute 在startActivityAsUser 方法中 先获取ActivityStarter实例 然后通过
  • 创建和管理表【DDL】

    目录 引入 一 创建和管理数据库 lt 一 gt 创建数据库 lt 二 gt 管理数据库之使用数据库 lt 三 gt 管理数据库之删除数据库 lt 四 gt 管理数据库之修改数据库 一般情况下不会改库 二 创建和管理数据表 lt 一 gt
  • java输入对话框_Java语言程序设计(五)从对话框获取输入及String类型

    1 String类型 char类型只能表示一个字符 为了表示一串字符 使用成为String 字符串 的数据类型 例如下述代码将消息声明为一个字符串 String message Welcome to Java String实际上与Syste
  • 敏捷开发“松结对编程”实践之五:代码检查篇(大型研发团队,学习型团队,139团队,师徒制度,代码审查)

    转载自 http blog csdn net cheny com article details 6594507 本文是 松结对编程 系列的第五篇 之一 之二 之三 之四 之五 之六 之七 之八 此系列之九及之后文章请见栏目总目录 松结对和
  • 基础 - JSON

    JSON JavaScript Object Notation 时下最流行的数据交换格式 特点 键和值 都必须包裹在引号中 语法 数据在键值对中 数据由逗号分隔 花括号保存对象 方括号保存数组 格式 var JSON name WeWeZh
  • Java Future接口、Future模式理解

    Future接口介绍 在Java中 如果需要设定代码执行的最长时间 即超时 可以用Java线程池ExecutorService类配合Future接口来实现 Future接口是Java标准API的一部分 在java util concurre
  • google-java-app-engine

    http code google com appengine docs java tools uploadinganapp html Uploading the App java app 的说明 PS google已经统治了地球了
  • 如何解决二分类中的样本不平衡问题

    在搭建模型时 二分类中 经常会遇到目标变量的分类数量相差很大 比如分类是1的数量是5000 分类是0的数量是100 这样如果对数据的不平衡性不做处理 模型的效果也会很差 今天用一个案例来进行实操 1 案例目的 找出有资金需求的中小企业借贷户
  • 中手游上半年扭亏为盈,仙剑IP魅力不减?

    你也曾有过江湖梦吗 你也曾因 为国为民 为友为邻 的侠者心无处可安而苦恼吗 那么 仙剑 系列游戏或许可以成为你的灵魂寄托之所 而能让侠者的江湖梦具象化的幕后厂商 便是中手游 两年前 中手游斥巨资买下了仙剑IP的大陆地区权益 两年内 中手游不
  • Zookeeper集群启、停、状态查看脚本

    zk sh bin bash if lt 1 then echo Please type parameter like
  • 工业物联网与其他物联网应用有哪些不同?

    物联网在众多的细分领域里早已让相关的企业实现了颠覆性的转型 这些领域涵盖了消费物联网 智慧农业领域 医疗保健 制造业领域和公用事业领域等等 工业物联网作为物联网一个不容忽视的子集 它专注于解决工业应用中的特殊要求 如制造业 石油和天然气及公
  • [MySql]基本知识及表的创建

    目录 1 数据库的操作 2 常用的数据类型 2 1数值类型 2 2字符串类型 2 3日期类型 3 表的操作 4 使用示例 1 数据库的操作 show databases 显示当前数据库 create database database na
  • linux服务器安装mysql步骤详解

    还是按照菜鸟教程上的顺序进行安装 踩的坑还是比较少的 MySQL 安装 菜鸟教程 1 安装前 我们可以检测系统是否自带安装 MySQL rpm qa grep mysql 2 如果你系统有安装 那可以选择进行卸载 rpm e mysql 普
  • ModBus协议部分功能码设计与实现方法(QT环境)

    写在前面 资源 QT环境下实现资源 1 调研 1 1 什么是Modbus Modbus由MODICON公司于1979年开发 是一种工业现场总线协议标准 1996年施耐德公司推出基于以太网TCP IP的Modbus协议 ModbusTCP M