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

下载本文档

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

文档简介

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 protocol hea

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

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

4、ttle-endian,否则为big-endian。 1: unsafe 2: 3: int tester = 1; 4: bool littleendian = (*(byte*)(&tester) = (byte)1; 5: 3.2 整数/浮点数转换成byte数组 .net提供了现成的api,可以bitconverter.getbytes(value)和bitconverter.toxxoo(byte data)来进行转换。下面的代码对该转换进行了封装,加入了little-endian转big-endian的处理(以int为例): 1: public class valuehelper /

5、big-endian可以直接转换 2: 3: public virtual byte getbytes(int value) 4: 5: return bitconverter.getbytes(value); 6: 7: public virtual int getint(byte data) 8: 9: return bitconverter.toint32(data, 0); 10: 11: 12: 13: internal class littleendianvaluehelper : valuehelper /little-endian,转换时需要做翻转处理。精品. 14: 15:

6、public override byte getbytes(int value) 16: 17: return this.reverse(bitconverter.getbytes(value); 18: 19: public virtual int getint(byte data) 20: 21: return bitconverter.toint32(this.reverse(data), 0); 22: 23: private byte reverse(byte data) 24: 25: array.reverse(data); 26: return data; 27: 28: 4.

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

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

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

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

11、text.text.trim(); 2: 3: public override void 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 header:modbus application pr

12、otocol header 16: values.addrange(valuehelper.instance.getbytes(this.nextdataindex();/12.(transaction identifier) 17: values.addrange(new byte 0, 0 );/34:protocol identifier,0 = modbus protocol 18: values.addrange(valuehelper.instance.getbytes(byte)(data.length + 7);/56:后续的byte数量 19: values.add(0);/

13、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(short)(data.le

14、ngth / 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: byte respons

15、eheader = this.socketwrapper.read(12); 36: (2) 读取数据: 1: this.tbxreceivetext.text = encoding.ascii.getstring(this.wrapper.receive(); 2: 3: public override byte receive() 4: 5: this.connect(); 6: list senddata = new list(255); 7: 8: /1.send 9: senddata.addrange(valuehelper.instance.getbytes(this.nextd

16、ataindex();/12.(transaction identifier) 10: senddata.addrange(new byte 0, 0 );/34:protocol identifier,0 = modbus protocol精品. 11: 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 routing

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

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

温馨提示

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

评论

0/150

提交评论