zoukankan      html  css  js  c++  java
  • C#读取Modbus数据

            最近在做采集的一些任务所以学了一下Modbus通信,学了好几天昨天终于把大概弄明白了,其实简单来说就是客户端向设备发送一个请求报文请求数据,服务器端根据请求报文向客户端端回发一个报文,客户端在接收到响应报文之后对响应报文进行解析,解析之后在将数据存到数据库里或者打印出来。

           报文格式:

           下面让我们来详细的了解一下这个过程。

           首先,做就是在客户端添加一个SOCKET与设备建立连接。说简单点,你不要认为是什么Modbus,就认为是一台电脑,开了SOCKET服务在这里。参考代码如下:

    public static void testframe () {
     TcpClient client = new TcpClient ();
     client.Connect (IPAddress.Parse ("127.0.0.1"), 5556);
    }

          这里IP和端口号都是由设备方提供的。真实程序一般都把这两个参数写配置文件中。

          设备连上以后,下一步当然就是读取数据。Modbus的基本原理就是程序向设备请求,需要读取哪个数据,设备就会返回相应的数据。我们知道机器或者说是电脑是只认识01001这样的字符串的。所以所谓的Modbus协议,说得简单一点,就是规定这样一个0101字符各代表什么含义。

    下面是核心代码即请求报文和响应报文的代码:

     if (client.Connected) {
    
                    NetworkStream sm = client.GetStream ();     // start address = 99 , count = 3
                    sm.Write (new byte[] {
    					0x01, 0x00,   // transfer flag     (little-endian)
    					0x00, 0x00,   // protocol flag     
    					0x00, 0x06,   // length            (big-endian)
    					0x01,         // device id 
    					0x03,         // function code
    					0x00, 0x63,   // start address     (big-endian)
    					0x00, 0x03    // count             (big-endian)
    				}, 0, 12);
    
    				byte [] frame = new byte [256];
    				int read_len = sm.Read (frame, 0, frame.Length);
    				if (read_len > 9) {
    					Console.WriteLine ("successful in receiving:");
    					int cnt = (int) frame [8];
    					for (int i = 0; i < cnt; i += 2) {
    						uint val = (uint) frame [i + 9];
    						val <<= 8;
    						val |= (uint) frame[i + 10];
    						Console.WriteLine (val);
    					}
    				} else if (read_len == 9) {
    					Console.WriteLine ("rcv error!!");
    					Console.WriteLine ("error code : " + (frame[7] - 0x80) + frame[8]);
    				}
    			
                }

    下面对这段代码进行一下解释:

           MSDN里对NetworkStream 类的解释是“提供用于网络访问的基础数据流。”,并且“要创建 NetworkStream,必须提供连接的Socket。”。

           NetworkStream.Write 方法         //将数据写入 NetworkStream。(发送请求报文)  ,方法里有三个参数分别是

    buffer
    类型:System.Byte[]
    类型 Byte 的数组,该数组包含要写入NetworkStream 的数据。
    offset
    类型:System.Int32
    buffer 中开始写入数据的位置。
    size
    类型:System.Int32
    要写入 NetworkStream 的字节数。

               NetworkStream.Read方法和write方法类似,只不过Read方法接受的是响应报文,并且需要先将接收到的数据存在frame数组里,然后再对这一串数据进行解析得到想要的数据 。
              下面单独对解析过程做一下解释:
                      byte [] frame = new byte [256];    //定义一个标识名为frame的byte类型的数组用于存放接收到的数据流
    	          int read_len = sm.Read (frame, 0, frame.Length);    //NetworkStream.Read的返回值是<span><span><span class="sentence" id="mt5">从 <span><a target=_blank href="https://msdn.microsoft.com/zh-cn/library/system.net.sockets.networkstream.aspx" target="_blank">NetworkStream</a></span> 中读取的字节数。</span></span></span>
            		if (read_len > 9) {
             			Console.WriteLine ("successful in receiving:");
    				int cnt = (int) frame [8];     //响应报文的第9个byte表示的是数据部分的长度
    				for (int i = 0; i < cnt; i += 2) {      //i+=2是因为每个数据是2byte
    			       	 uint val = (uint) frame [i + 9]; //一个数据是2byte,其中前一个byte是高位,后一个byte是低位
    				 val <<= 8;         //左移8位将高位的数放入高位
    				 val |= (uint) frame[i + 10];    //微运算,将低位的数与高位的数取或
    			       	Console.WriteLine (val);
    				}
    			}

    下面是几张Modbus报文的图:
    请求报文:

    响应报文:






  • 相关阅读:
    linux添加超级用户
    MongDB配置方法
    【bzoj5174】[Jsoi2013]哈利波特与死亡圣器 二分+树形dp
    【bzoj3560】DZY Loves Math V 欧拉函数
    【bzoj5157】[Tjoi2014]上升子序列 树状数组
    【uoj#317】[NOI2017]游戏 2-SAT
    【bzoj5146】有趣的概率 微积分
    【bzoj4695】最假女选手 线段树区间最值操作
    【bzoj4355】Play with sequence 线段树区间最值操作
    【loj2319】[NOIP2017]列队 Splay(卡过)
  • 原文地址:https://www.cnblogs.com/haxianhe/p/9271254.html
Copyright © 2011-2022 走看看