C#使用TCPIP与ModBus进行通讯_第1页
C#使用TCPIP与ModBus进行通讯_第2页
C#使用TCPIP与ModBus进行通讯_第3页
C#使用TCPIP与ModBus进行通讯_第4页
C#使用TCPIP与ModBus进行通讯_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、,.c#使用 tcp/ip 与 modbus进行通讯1. modbus 的 client/server模型2. 数据包格式及 mbap header (modbus application protocol header)3. 大小端转换4. 事务标识和缓冲清理5. 示例代码1. modbus 的 client/server模型client 与 server 之间有两种通讯方式:一种是 tcp/ip,另一种是通过串口 (serial port) ,本文重点介绍第一种通讯方式。第二种方式留了接口,暂时还没有实现。2.数据包格式及mbap header (modbus application pr

2、otocol header)2.1数据包格式数据交换过程中,数据包的格式由三部分组成:协议头+ 功能码 + 数据 ( 请求或接受的数据 ) 。;.,.这里主要用到下列两个功能码(十进制):3:读取寄存器中的值( read multiple register)16:往寄存器中写值( write multiple register)2.2 mbap header协议头具体包括下列4 个字段:(1) transaction identifier:事务 id 标识, client每发送一个 request 数据包的时候,需要带上该标识;当 server 响应该请求的时候,会把该标识复制到respons

3、e中;这样客户端就可以进行容错判断,防止数据包发串了。(2) protocal identifier:协议标识, modbus协议中,该值为 0;(3) length :整个数据包中,从当个前这个字节之后开始计算,后续数据量的大小 ( 按 byte 计算 ) 。(4) unit identifier:-_-3. 大小端转换modbus使用 big-endian 表示地址和数据项。因此在发送或者接受数据的过程中,需要对数据进行转换。;.,.3.1判断大小端对于整数 1,在两种机器上有两种不同的标示方式,如上图所示;因此,我们可以用 &操作符来取其地址,再转换成指向 byte 的指针 (byte*

4、) ,最后再取该指针的值;若得到的 byte 值为 1,则为 little-endian ,否则为 big-endian 。1: unsafe2: 3:inttester = 1;4:boollittleendian = (*(byte *)(&tester) = (byte )1;5: 3.2整数 / 浮点数转换成byte 数组.net提供了现成的 api,可以 bitconverter.getbytes(value)和bitconverter.toxxoo(byte data) 来进行转换。 下面的代码对该转换进行了封装,加入了 little-endian 转 big-endian 的处理

5、(以 int 为例):1:publicclassvaluehelper/big-endian可以直接转换2: 3:publicvirtualbyte getbytes(intvalue )4: 5:returnbitconverter.getbytes(value );6: 7:publicvirtualintgetint(byte data)8: 9:returnbitconverter.toint32(data, 0);.,.10: 11: 12:13:internalclasslittleendianvaluehelper : valuehelper/little-endian,转换时需

6、要做翻转处理。14: 15:publicoverridebyte getbytes(intvalue )16: 17:returnthis.reverse(bitconverter.getbytes(value );18: 19:publicvirtualintgetint(byte data)20: 21:returnbitconverter.toint32(this.reverse(data), 0);22: 23:privatebyte reverse(byte data)24: 25: array.reverse(data);26:returndata;27: 28: 4. 事务标识和

7、缓冲处理4.1 transaction identifier上面 2.2 节中提到, client 每发送一个 request 数据包的时候,需要带上一个标识;当 server 响应该请求的时候,会把该标识复制到 response 中,返回给 client 。这样 client 就可以用来判断数据包有没有发串。在程序中,可以可以用一个变量及记录该标识:1:privatebyte dataindex = 0;2:3: protected byte currentdataindex4: 5:get returnthis.dataindex; 6: 7:8: protected byte nextd

8、ataindex()9: 10:return+ this.dataindex;11: 每次 client 发送数据的时候,调用 nextdataindex() 来取得事务标识;接着当 client 读取 server 的返回值的时候, 需要判断数据包中的数据标识是否与发送时的标志一致;如果一致,则认为数据包有效;否则丢掉无效的数据包。;.,.4.2缓冲处理上节中提到,如果 client 接收到的响应数据包中的标识,与发送给 server 的数据标识不一致, 则认为 server 返回的数据包无效, 并丢弃该数据包。如果只考虑正常情况,即数据木有差错, client 每次发送请求后,其请求包里面

9、包含需要读取的寄存器数量,能算出从 server 返回的数据两大小,这样 就能确定读完 server 返回的所有缓冲区中的数据;每次交互后, socket 缓冲区中都为空,则整个过程没有问题。但是问题是:如果 server 端出 错,或者数据串包等异常情况下, client 不能确定 server 返回的数据包 ( 占用的缓冲区 )有多大;如果缓冲区中的数据没有读完,下次再从缓冲区中接 着读的时候,数据包必然是不正确的,而且会错误会一直延续到后续的读取操作中。因此,每次读取数据时,要么全部读完缓冲区中的数据,要么读到错误的时候,就必须清楚缓冲区中剩余的数据。网上搜了半天,木有找到 window

10、s 下如何清 理 socket 缓冲区的。有篇文章倒是提到一个狠招,每次读完数据后,直接把 socket 给咔嚓掉;然后下次需要读取或发送数据的时候,再重新建立socket 连接。回过头再来看,其实,在 client 与 server 进行交互的过程中, server 每次返回的数据量都不大,也就一个 mbap header + 几十个寄存器的值。因此,另一个处理方式, 就是每次读取尽可能多的数据 ( 多过缓冲区中的数据量 ) ,多读的内容,再忽略掉。暂时这么处理,期待有更好的解决方法。5. 源代码5.1类图结构:;.,.5.2使用示例(1) 写入数据:1:this .wrapper.send

11、(encoding.ascii.getbytes(this .tbxsendtext.text.trim();2:3:public overridevoid send( byte data)4: ;.,.5: /0: 填充 0,清掉剩余的寄存器6:if(data.length 60)7: 8: var input = data;9:data =new byte60;10: array.copy(input, data, input.length);11: 12: this .connect();13:list values =new list(255);14:15: /1.write head

12、er:modbus application protocol header16:values.addrange(valuehelper.instance.getbytes(this .nextdataindex();/12.(transactionidentifier)17:values.addrange(new byte 0, 0 );/34:protocol identifier,0 = modbus protocol18:values.addrange(valuehelper.instance.getbytes(byte )(data.length + 7);/56: 后续的byte数量

13、19:values.add(0);/7:unit identifier:this field is used for intra-system routing purpose.20:values.add(byte )functioncode.write);/8.function code : 16 (write multiple register)21:values.addrange(valuehelper.instance.getbytes(startingaddress);/910.起始地址22:values.addrange(valuehelper.instance.getbytes(s

14、hort )(data.length / 2);/1112. 寄存器数量23:values.add(byte )data.length);/13. 数据的 byte 数量24:25:/2.增加数据26:values.addrange(data);/14end:需要发送的数据27:28:/3.写数据29: this .socketwrapper.write(values.toarray();30:31: /4. 防止连续读写引起前台 ui 线程阻塞32: application.doevents();33:34: /5. 读取 response: 写完后会返回 12 个 byte 的结果35:b

15、yte responseheader =this.socketwrapper.read(12);36: (2) 读取数据:1:this .tbxreceivetext.text = encoding.ascii.getstring(this .wrapper.receive();2:3:publicoverridebyte receive()4: 5:this.connect();6:list senddata =new list(255);7:;.,.8:/1.send9:senddata.addrange(valuehelper.instance.getbytes(this .nextda

16、taindex();/12.(transactionidentifier)10:senddata.addrange(new byte 0, 0 );/34:protocol identifier,0 = modbusprotocol11:senddata.addrange(valuehelper.instance.getbytes(short)6);/56: 后续的 byte数量(针对读请求,后续为6 个 byte)12:senddata.add(0);/7:unit identifier:this field is used for intra-system routingpurpose.1

17、3:senddata.add(byte )functioncode.read);/8.function code : 3 (read multipleregister)14:senddata.addrange(valuehelper.instance.getbytes(startingaddress);/910. 起始地址15:senddata.addrange(valuehelper.instance.getbytes(short)30);/1112. 需要读取的寄存器数量16:this.socketwrapper.write(senddata.toarray();/ 发送读请求17:18:

18、/2.防止连续读写引起前台ui 线程阻塞19: application.doevents();20:21:/3.读取 response header :完后会返回 8个 byte 的 response header22:byte receivedata =this.socketwrapper.read(256);/ 缓冲区中的数据总量不超过256byte,一次读 256byte,防止残余数据影响下次读取23:shortidentifier = (short)( short )receivedata0) 8) + receivedata1);24:25:/4.读取返回数据:根据 responseheader ,读取后续的数据26:if (identifier !=this .currentdataindex)/ 请求的数据标识与返回的标识不一致,则丢掉数据包27: 28:returnnew byte0;29: 30:byte length = receivedata8;/ 最后一个字节,记录寄存器中数据的byte 数31:byte result =new byte length;32: array.co

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论