一、编写缘由
1.发现问题
最近项目上要把之前的modbus RTU改为TCP形式,因此之前的modbus通讯线程得重构,一开始当然是使用Qt自带的QModbusTcpClient类,很快就重构好线程,读取数据没有问题,但是只要一发送写数据请求,整个tcp连接就会断开,做了很多尝试,排除了从站的问题,即使直接连modbusslave也是出现这种问题。
2.查找问题
于是自己写了一个tcp server,抓取QModbusTcpClient写数据的报文,和modbuspoll上的对比,果然对不上,qt中的报文比modbuspoll上的多出来一截,想必是协议错误了。
3.解决策略
QModbusTcpClient不就是在tcp通讯上添加了modbus协议嘛,既然它的协议都错了,那就没有使用的必要了,我们直接用QTcpSocket手搓一个ModbusTcp类就好了。
二、代码编写
1.协议解析
通过modbuspoll上的通信日志和网络上的modbustcp协议分析文章对比,研究出协议的标准格式。ModbusTCP协议报文分析
2.封装函数
void writeCoil(quint16 address,bool value);
void writeCoils(quint16 address,QVector<bool> values);
void writeRegist(quint16 address,quint16 value);
void writeRegists(quint16 address,QVector<quint16> values);
我共封装了以上4个函数,分别是写单个线圈、写多个线圈、写单个保持寄存器、写多个保持寄存器。
具体实现如下:
void ModbusTcp::writeRegist(quint16 address,quint16 value)
{
QByteArray request;
request.resize(12);
request[0]=0x0;
request[1]=0x0;
request[2]=0x0;
request[3]=0x0;
request[4]=0x0;
request[5]=0x06;
request[6]=0x01;
request[7]=0x06;
uchar addh = address/256;
request[8]=addh;//起始地址h
uchar addl = address%256;
request[9]=addl;//起始地址l
uchar valueh = value/256;
uchar valuel = value%256;
request[10]=valueh;//值1h
request[11]=valuel;//值1l
client->write(request);
}
void ModbusTcp::writeRegists(quint16 address, QVector<quint16> values)
{
QByteArray request;
request.resize(13+values.count()*2);
request[0]=0x0;
request[1]=0x0;
request[2]=0x0;
request[3]=0x0;
uchar lenh = (7+values.count()*2)/256;
uchar lenl = (7+values.count()*2)%256;
request[4]=lenh;//往后包长度h
request[5]=lenl;//往后包长度l
request[6]=0x01;
request[7]=0x10;
uchar addh = address/256;
request[8]=addh;//起始地址h
uchar addl = address%256;
request[9]=addl;//起始地址l
uchar numh = values.count()/256;
uchar numl = values.count()%256;
request[10]=numh;//数量h
request[11]=numl;//数量l
request[12]=values.count()*2;//字节数
for(int i=0;i<values.count();i++){
uchar valueh = values.at(i)/256;
uchar valuel = values.at(i)%256;
request[13+i*2]=valueh;
request[14+i*2]=valuel;
}
client->write(request);
}
void ModbusTcp::writeCoil(quint16 address, bool value)
{
QByteArray request;
request.resize(12);
request[0]=0x0;
request[1]=0x0;
request[2]=0x0;
request[3]=0x0;
request[4]=0x0;
request[5]=0x06;
request[6]=0x01;
request[7]=0x05;
uchar addh = address/256;
uchar addl = address%256;
request[8]=addh;//起始地址h
request[9]=addl;//起始地址l
if(value)
request[10]=0xFF;
else
request[10]=0x0;
request[11]=0x0;
client->write(request);
}
void ModbusTcp::writeCoils(quint16 address, QVector<bool> values)
{
QByteArray request;
request.resize(13+ceil(values.count()/8));
request[0]=0x0;
request[1]=0x0;
request[2]=0x0;
request[3]=0x0;
uchar lenh = (7+ceil(values.count()/8))/256;
uchar lenl = int(7+ceil(values.count()/8))%256;
request[4]=lenh;//往后包长度h
request[5]=lenl;//往后包长度l
request[6]=0x01;
request[7]=0xF;
uchar addh = address/256;
request[8]=addh;//起始地址h
uchar addl = address%256;
request[9]=addl;//起始地址l
uchar numh = values.count()/256;
uchar numl = values.count()%256;
request[10]=numh;//数量h
request[11]=numl;//数量l
request[12]=ceil(values.count()/8);//字节数
QVector<uchar> bs;
uchar a=0;
uchar dy=values.count()%8;
for(uchar i=0;i<dy;i++){
if(values.at(values.count()-1-i))
a+=pow(2,i);
}
bs.append(a);
for(uchar i=0;i<values.count()/8;i++){
a = 0;
for(uchar j=dy+i*8;j<dy+i*8+8;j++){
if(values.at(values.count()-1-j))
a+=pow(2,(j-dy)%8);
}
bs.append(a);
}
for(uchar k=0;k<bs.count();k++){
request[13+k]=bs.at(bs.count()-1-k);
}
client->write(request);
}
四个函数中除了写多个线圈还有问题外,其他都已验证,可以正确写入。
最后,我的tcp是作为一个子线程的,线程初始化函数如下:文章来源:https://www.toymoban.com/news/detail-770074.html
void ModbusTcp::initModbus()
{
client = new QTcpSocket(this);
connect(client,&QTcpSocket::readyRead,this,&ModbusTcp::parseData);
client->connectToHost("192.168.1.100",502);
if(client->waitForConnected(3000)){
qDebug()<<"trans connect success";
timer = new QTimer(this);
connect(timer,&QTimer::timeout,this,&ModbusTcp::startThread);
timer->setSingleShot(false);
timer->setInterval(1000);
timer->start();
}
else{
qDebug()<<"trans connect faild";
}
}
三、源码下载
模块下载文章来源地址https://www.toymoban.com/news/detail-770074.html
四、最后的最后再吐槽一下QModbusTcpClient是真的垃圾,完全不能用。另外,大家在网上找到的QtModbusTCP资源,无一例外都是不能用的!!!处非和我一样纯手搓。
到了这里,关于QT+ModbusTCP 全网唯一好用,基于QTcpSocket纯手搓modbustcp协议的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!