一杯清酒邀明月
天下本无事,庸人扰之而烦耳。

一、编写缘由
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.封装函数

1 void writeCoil(quint16 address,bool value);
2 void writeCoils(quint16 address,QVector<bool> values);
3 void writeRegist(quint16 address,quint16 value);
4 void writeRegists(quint16 address,QVector<quint16> values);

我共封装了以上4个函数,分别是写单个线圈、写多个线圈、写单个保持寄存器、写多个保持寄存器。
具体实现如下:

  1 void ModbusTcp::writeRegist(quint16 address,quint16 value)
  2 {
  3     QByteArray request;
  4     request.resize(12);
  5     request[0]=0x0;
  6     request[1]=0x0;
  7     request[2]=0x0;
  8     request[3]=0x0;
  9     request[4]=0x0;
 10     request[5]=0x06;
 11     request[6]=0x01;
 12     request[7]=0x06;
 13     uchar addh = address/256;
 14     request[8]=addh;//起始地址h
 15     uchar addl = address%256;
 16     request[9]=addl;//起始地址l
 17     uchar valueh = value/256;
 18     uchar valuel = value%256;
 19     request[10]=valueh;//值1h
 20     request[11]=valuel;//值1l
 21     client->write(request);
 22 }
 23 
 24 void ModbusTcp::writeRegists(quint16 address, QVector<quint16> values)
 25 {
 26     QByteArray request;
 27     request.resize(13+values.count()*2);
 28     request[0]=0x0;
 29     request[1]=0x0;
 30     request[2]=0x0;
 31     request[3]=0x0;
 32     uchar lenh = (7+values.count()*2)/256;
 33     uchar lenl = (7+values.count()*2)%256;
 34     request[4]=lenh;//往后包长度h
 35     request[5]=lenl;//往后包长度l
 36     request[6]=0x01;
 37     request[7]=0x10;
 38     uchar addh = address/256;
 39     request[8]=addh;//起始地址h
 40     uchar addl = address%256;
 41     request[9]=addl;//起始地址l
 42     uchar numh = values.count()/256;
 43     uchar numl = values.count()%256;
 44     request[10]=numh;//数量h
 45     request[11]=numl;//数量l
 46     request[12]=values.count()*2;//字节数
 47     for(int i=0;i<values.count();i++){
 48         uchar valueh = values.at(i)/256;
 49         uchar valuel = values.at(i)%256;
 50         request[13+i*2]=valueh;
 51         request[14+i*2]=valuel;
 52     }
 53     client->write(request);
 54 }
 55 
 56 void ModbusTcp::writeCoil(quint16 address, bool value)
 57 {
 58     QByteArray request;
 59     request.resize(12);
 60     request[0]=0x0;
 61     request[1]=0x0;
 62     request[2]=0x0;
 63     request[3]=0x0;
 64     request[4]=0x0;
 65     request[5]=0x06;
 66     request[6]=0x01;
 67     request[7]=0x05;
 68     uchar addh = address/256;
 69     uchar addl = address%256;
 70     request[8]=addh;//起始地址h
 71     request[9]=addl;//起始地址l
 72     if(value)
 73         request[10]=0xFF;
 74     else
 75         request[10]=0x0;
 76     request[11]=0x0;
 77     client->write(request);
 78 }
 79 
 80 void ModbusTcp::writeCoils(quint16 address, QVector<bool> values)
 81 {
 82     QByteArray request;
 83     request.resize(13+ceil(values.count()/8));
 84     request[0]=0x0;
 85     request[1]=0x0;
 86     request[2]=0x0;
 87     request[3]=0x0;
 88     uchar lenh = (7+ceil(values.count()/8))/256;
 89     uchar lenl = int(7+ceil(values.count()/8))%256;
 90     request[4]=lenh;//往后包长度h
 91     request[5]=lenl;//往后包长度l
 92     request[6]=0x01;
 93     request[7]=0xF;
 94     uchar addh = address/256;
 95     request[8]=addh;//起始地址h
 96     uchar addl = address%256;
 97     request[9]=addl;//起始地址l
 98     uchar numh = values.count()/256;
 99     uchar numl = values.count()%256;
100     request[10]=numh;//数量h
101     request[11]=numl;//数量l
102     request[12]=ceil(values.count()/8);//字节数
103     QVector<uchar> bs;
104     uchar a=0;
105     uchar dy=values.count()%8;
106     for(uchar i=0;i<dy;i++){
107         if(values.at(values.count()-1-i))
108             a+=pow(2,i);
109     }
110     bs.append(a);
111     for(uchar i=0;i<values.count()/8;i++){
112         a = 0;
113         for(uchar j=dy+i*8;j<dy+i*8+8;j++){
114             if(values.at(values.count()-1-j))
115                 a+=pow(2,(j-dy)%8);
116         }
117         bs.append(a);
118     }
119     for(uchar k=0;k<bs.count();k++){
120         request[13+k]=bs.at(bs.count()-1-k);
121     }
122     client->write(request);
123 }

四个函数中除了写多个线圈还有问题外,其他都已验证,可以正确写入。
最后,我的tcp是作为一个子线程的,线程初始化函数如下:

 1 void ModbusTcp::initModbus()
 2 {
 3     client = new QTcpSocket(this);
 4     connect(client,&QTcpSocket::readyRead,this,&ModbusTcp::parseData);
 5     client->connectToHost("192.168.1.100",502);
 6     if(client->waitForConnected(3000)){
 7         qDebug()<<"trans connect success";
 8         timer = new QTimer(this);
 9         connect(timer,&QTimer::timeout,this,&ModbusTcp::startThread);
10         timer->setSingleShot(false);
11         timer->setInterval(1000);
12         timer->start();
13     }
14     else{
15         qDebug()<<"trans connect faild";
16     }
17 }

 

posted on 2024-03-09 15:27  一杯清酒邀明月  阅读(406)  评论(0编辑  收藏  举报