付费下载
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、1/ 10C#使用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 header
2、)2.1 数据包格式MODBUS TCP/IP ADUMBAP Header Function codeData2/ 10数据交换过程中,数据包的格式由三部分组成:协议头+功能码+数据(请求或接受的数据)。这里主要用到下列两个功能码(十进制):3:读取寄存器中的值(Read Multiple Register)16:往寄存器中写值(Write Multiple Register2.2 MBAP headerFieldsLengthDescription -ClientServerTransaction identifier2 BytesIdentification of aMODBUS Req
3、uestfResponse transaction.Initialized by theclientRecopied by theserver from thereceived re-q jestProtocol Identifier2 BytesC = M ODBUS prctoGolInitialized Uy theclientRecopied by th#server from there-c&ivedre-qLieSrtLength2 BytesNumber of fallowing bytes Initialized by theclient ( request)Inilializ
4、ad &y tr eserver ( ResponsejUnit Identifier1 ByteIdentificat on of a remoteslave connected on aserial lure or on otherbuses.Initialized oy theclientRecopied by theserver from thereceived req ue&t- - - happyh ippy匚rblogs,匚oiri协议头具体包括下列4个字段: Transaction Identifier:事务ID标识,Client每发送一个Request数据包的时候,需要带上该
5、标识;当Server响应该请求的时候,会把该标识复制到Resp onse中;这样客户端就可以进行容错判断,防止数据包发串了。Protocal Identifier:协议标识,ModBus协议中,该值为0;(3) Length:整个数据包中,从当个前这个字节之后开始计算,后续数据量的大小(按byte计算)。 Unit Identifier:-_-3/ 103.大小端转换6:4/ 10ModBus使用Big-Endian表示地址和数据项。因此在发送或者接受数据 的过程中,需要对数据进行转换。3.1 判断大小端高位地址幣数1在两种机器上的忑示方式0100-jAddress000000000001Li
6、tt1君一End伽Big-Endian低位地址*happy hippy cnblogs.匚uni对于整数1,在两种机器上有两种不同的标示方式,如上图所示;因此,我们可以用&操作符来取其地址,再转换成指向byte的指针(byte*),最后再取 该指针的值;若得到的byte值为1,则为Little-Endian,否则为Big-Endian ,1:unsafe2: 3:int tester = 1;4:bool littleEndian = (*(byte *)(&tester) = ( byte )1;5:3.2 整数/浮点数转换成 Byte 数组.Net提供了现成的API,可以BitConver
7、ter.GetBytes(value)和BitConverter.ToXXOO(Byte data)来进行转换。下面的代码对该转换进行了圭寸 装,加入了Little-Endian转Big-Endian的处理(以int为例):1:publicclass ValueHelper /Big-Endian可以直接转换2:3:public virtual Byte GetBytes(int value )4:5:return BitConverter.GetBytes(value );5/ 1024:25:Array.Reverse(data);26:return data;27:28: 4.事务标识和
8、缓冲处理4.1 Transaction Identifier上面2.2节中提到,Client每发送一个Request数据包的时候,需要带 上一个标识;当Server响应该请求的时候,会把该标识复制到Response中,返回给Client。这样Client就可以用来判断数据包有没有发串。在程序中,可 以可以用一个变量及记录该标识:1:2:3:4:5:6:7:8:private byte dataIndex = 0;protected byte CurrentDataIndexget return this .dataIndex; protected byte NextDataIndex()10:
9、return + this .dataIndex;11:7:publicvirtualint GetInt( byte data)8:9:return BitConverter.Tolnt32(data, 0);10:11:12:13:internalclassLittleEndianValueHelper : ValueHelper/Little-Endian14:15:publicoverride Byte GetBytes(int value )16:17:return this .Reverse(BitConverter.GetBytes(value );18:19:publicvir
10、tualint GetInt( byte data)20:21:return BitConverter.ToInt32(this .Reverse(data), 0);22:23:privateByte Reverse(Byte data),转换时需要做翻转处理。6/ 10每次Client发送数据的时候,调用NextDataIndex()来取得事务标识;接着当Client读取Server的返回值的时候,需要判断数据包中的数据标识是否与发送 时的标志一致;如果一致,则认为数据包有效;否则丢掉无效的数据包。4.2 缓冲处理上节中提到,如果Client接收到的响应数据包中的标识,与发送给Server
11、的数据标识不一致, 则认为Server返回的数据包无效, 并丢弃该数据包如果只考虑正常情况,即数据木有差错,Client每次发送请求后,其请 求包里面包含需要读取的寄存器数量,能算出从Server返回的数据两大小,这 样 就能确定读完Server返回的所有缓冲区中的数据;每次交互后,Socket缓 冲区中都为空,则整个过程没有问题。但是问题是:如果Server端出 错,或者 数据串包等异常情况下,Client不能确定Server返回的数据包(占用的缓冲区)有多大;如果缓冲区中的数据没有读完,下次再从缓冲区中接 着读的时候,数 据包必然是不正确的,而且会错误会一直延续到后续的读取操作中。因此,每
12、次读取数据时,要么全部读完缓冲区中的数据,要么读到错误 的时候,就必须清楚缓冲区中剩余的数据。 网上搜了半天, 木有找到Windows下如何清 理Socket缓冲区的。有篇文章倒是提到一个狠招,每次读完数据后,直接把Socket给咔嚓掉;然后下次需要读取或发送数据的时候,再重新建立Socket连接。回过头再来看,其实,在Client与Server进行交互的过程中,Server每次返回的数据量都不大,也就一个MBAP Header +几十个寄存器的值。因此, 另一个处理方式,就是每次读取尽可能多的数据(多过缓冲区中的数据量),多读 的内容,再忽略掉。暂时这么处理,期待有更好的解决 方法。5.源代
13、码5.1 类图结构:7/ 10Pro f.DCOLLEll二二TCFIFEr ialF optfJllFma.c ti oiaCdeEnwRead.Wri teIy#訂jvdwx属性.罗CurrntDat In -方法M Cratsins t*neeM Dispose”: * ffertll alalndex:SocketYrprwlistVConnectV4 L館MWrite-O性L违-1ID11-0 glilt&if ac=s方达VIFpdIFpd訓?*沪揺翩廿5.2 使用示例写入数据: odBtasTGnTVr ap-丸C11HJ(3c3ctKrafpi2r!-学段./ cruiicte
14、d* InstinctStart mgilddres-方法* ConnectVDi spase ReceiveVSend方这VCozmact 9ispast* ceiV Semi1:this .Wrapper.Send(Encoding.ASCII.GetBytes(this .tbxSendText.Text.Trim();2:3:public override void Send( byte data)4:8/ 105:0:填充0,清掉剩余的寄存器6:if (data.Length 60)7:8:var input = data;9:data =new Byte60;10:Array.Co
15、py(input, data, input.Length);11:12:this .Connect。;13:List values = new List(255);14:15:1.Write Header:MODBUS Application Protocol header16:values.AddRange(ValueHelper.lnstance.GetBytes(this .NextDatalndex();12.(TransactionIdentifier)17:values.AddRange( new Byte 0, 0 );34:Protocol Identifier,。= MODB
16、US protocol18:values.AddRange(ValueHelper.Instance.GetBytes(byte )(data.Length + 7);/56:后续的Byte数量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(ValueHelpe
17、r.Instance.GetBytes(StartingAddress);910.起始地址22:values.AddRange(ValueHelper.Instance.GetBytes(short )(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.防止连续读写引起前台U
18、I线程阻塞32:Application.DoEvents();33:34:5.读取Response:写完后会返回12个byte的结果35:byte responseHeader = this .socketWrapper.Read(12);36:(2)读取数据:1:this .tbxReceiveText.Text = Encoding .A SCII.GetString(this .Wrapper.Receive();9/ 102:3:public override byte Receive()4:5:this .Connect();6:List sendData = new List(25
19、5);7:10/ 108:1.Send9:sendData.AddRange(ValueHelper.lnstance.GetBytes(this .NextDatalndex();12.(TransactionIdentifier)10:sendData.AddRange(new Byte 0, 0 );34:Protocol Identifier,。= MODBUSprotocol11:sendData.AddRange(ValueHelper.Instance.GetBytes(short )6);56:后续的Byte数量(针对读请求,后续为6个byte)12:sendData.Add(
20、O);7:Unit Identifier:This field is used for intra-system routingpurpose.13: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:2.防止连续读写引起前台UI线程阻塞19:Application.DoEvents();20:21:3.读取Response Header :完后会返回8个byte的Response Hea
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年碳足迹追踪系统开发师区域碳足迹差异与影响因素
- 装配式建筑监理实施细则
- 直埋供热管道工程实施方案
- 隧道施工技术交底手册
- 水泥混合材料配比设计手册
- 石油炼制过程安全操作手册
- 石化企业培训方案
- 纳米碳材料生产线安全设施设计报告
- 轻型吊装工装改造实施方案
- 加油站现场作业安全手册
- 2027创新设计一轮生物第14讲 减数分裂和受精作用
- 2026年宁夏惠安市政产业有限公司公开招聘工作人员考试参考题库及答案详解
- 2026上海闵行公安机关专业特保招聘602人笔试模拟试题及答案详解
- 2026河南南阳南召县招聘巡防队员招聘60人笔试模拟试题及答案详解
- 2026年江苏省初级注册安全工程师考试真题及答案
- 2025版老年晚期肺癌治疗专家共识解读课件
- 2026贵州六盘水市消防救援支队面向社会招录政府专职消防员22人笔试参考题库及答案详解
- 小儿支气管哮喘持续状态护理查房
- 临床腹腔内压力经膀胱间接测量技术解读及实践经验共享
- SLT 336-2025水土保持工程全套表格
- 医院合法性审查工作制度
评论
0/150
提交评论