zoukankan      html  css  js  c++  java
  • IoTClient开发6

    环境和工具

    服务端电脑IP:192.168.1.130
    客户端电脑IP:192.168.1.120
    1、在服务端电脑运行IoTClientTool

    2、运行Wireshark

    3、在客户端电脑运行IoTClientTool

    4、Wireshark得到如下报文

    报文分析,plc的连接

    我们看到上面连接西门子plc抓取到了八条报文。其中有tcp的三次握手、和对最后一次响应的回复,然后就是西门子特有的两次初始化指令的请求和响应。

    两次初始化指令

    不同型号的西门子plc有不同的初始化指令,同型号的指令固定不变。

    代码实现对plc的连接

    //直接是Wireshark抓取到的报文数据
    var Command1 = new byte[22]
    {
        0x03,0x00,0x00,0x16,0x11,0xE0,0x00,0x00,
        0x00,0x01,0x00,0xC1,0x02,0x10,0x00,0xC2,
        0x02,0x03,0x00,0xC0,0x01,0x0A
    };
    
    var Command2 = new byte[25]
    {
        0x03,0x00,0x00,0x19,0x02,0xF0,0x80,0x32,
        0x01,0x00,0x00,0xCC,0xC1,0x00,0x08,0x00,
        0x00,0xF0,0x00,0x00,0x01,0x00,0x01,0x03,0xC0
    };
            
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
    
    //第一次初始化指令交互
    socket.Send(Command1);
    var head1 = SocketRead(socket, SiemensConstant.InitHeadLength);
    SocketRead(socket, GetContentLength(head1));
    
    //第二次初始化指令交互
    socket.Send(Command2);
    var head2 = SocketRead(socket, SiemensConstant.InitHeadLength);
    SocketRead(socket, GetContentLength(head2));
    

    对寄存器的读取

    我们在客户端电用IoTClientTool读取地址V2634,抓取到包

    我们可以看到其中很多都是固定的数据,如版本号、协议id等等。所以,我们可以以此规律获取对应的指令格式

    protected byte[] GetReadCommand(byte type, int beginAddress, ushort dbAddress, ushort length)
    {
        byte[] command = new byte[31];
        command[0] = 0x03;
        command[1] = 0x00;//[0][1]固定报文头
        command[2] = (byte)(command.Length / 256);
        command[3] = (byte)(command.Length % 256);//[2][3]整个读取请求长度为0x1F= 31 
        command[4] = 0x02;
        command[5] = 0xF0;
        command[6] = 0x80;//COTP
        command[7] = 0x32;//协议ID
        command[8] = 0x01;//1  客户端发送命令 3 服务器回复命令
        command[9] = 0x00;
        command[10] = 0x00;//[4]-[10]固定6个字节
        command[11] = 0x00;
        command[12] = 0x01;//[11][12]两个字节,标识序列号,回复报文相同位置和这个完全一样;范围是0~65535
        command[13] = 0x00;
        command[14] = 0x0E;//parameter length([17]-[30]都为parameter刚好14也就是0x0E)
        command[15] = 0x00;
        command[16] = 0x00;//data length
        command[17] = 0x04;//04读 05写
        command[18] = 0x01;//读取数据块个数
        command[19] = 0x12;//variable specification
        command[20] = 0x0A;//Length of following address specification
        command[21] = 0x10;//Syntax Id: S7ANY 
        command[22] = 0x02;//Transport size: BYTE 
        command[23] = (byte)(length / 256);
        command[24] = (byte)(length % 256);//[23][24]两个字节,访问数据的个数,以byte为单位;
        command[25] = (byte)(dbAddress / 256);
        command[26] = (byte)(dbAddress % 256);//[25][26]DB块的编号
        command[27] = type;//访问数据块的类型
        command[28] = (byte)(beginAddress / 256 / 256);
        command[29] = (byte)(beginAddress / 256);
        command[30] = (byte)(beginAddress % 256);//[28][29][30]访问DB块的偏移量
        return command;
    }
    

    读取数据

    public Result<byte[]> ReadString(string address, ushort length)
    {
        Connect();
        var result = new Result<byte[]>(); 
        //发送读取信息
        var arg = ConvertArg(address);
        byte[] command = GetReadCommand(arg.TypeCode, arg.BeginAddress, arg.DbBlock, length);
        result.Requst = string.Join(" ", command.Select(t => t.ToString("X2")));
        var dataPackage = SendPackage(command);
        byte[] requst = new byte[length];
        Array.Copy(dataPackage, 25, requst, 0, length);
        //Array.Copy(dataPackage, dataPackage.Length - length, requst, 0, length);
        result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2")));
        result.Value = requst; 
        return result;
    }
    

    对寄存器的写入

    我们在客户端电用IoTClientTool对地址V2634写入值666,抓取到包

    我们可以以此规律获取对应的指令格式

    protected byte[] GetWriteCommand(byte type, int beginAddress, ushort dbAddress, byte[] data)
    {
        byte[] command = new byte[35 + data.Length];
        command[0] = 0x03;
        command[1] = 0x00;//[0][1]固定报文头
        command[2] = (byte)((35 + data.Length) / 256);
        command[3] = (byte)((35 + data.Length) % 256);//[2][3]整个读取请求长度
        command[4] = 0x02;
        command[5] = 0xF0;
        command[6] = 0x80;
        command[7] = 0x32;//[4]-[7]固定数据
        command[8] = 0x01;//1  客户端发送命令 3 服务器回复命令
        command[9] = 0x00;
        command[10] = 0x00;
        command[11] = 0x00;
        command[12] = 0x01;//[9]-[12]标识序列号
        command[13] = 0x00;
        command[14] = 0x0E;
        command[15] = (byte)((4 + data.Length) / 256);
        command[16] = (byte)((4 + data.Length) % 256);//[15][16]写入长度+4
        command[17] = 0x05;//04读 05写
        command[18] = 0x01;//写入数据块个数
        command[19] = 0x12;
        command[20] = 0x0A;
        command[21] = 0x10;//[19]-[21]固定
        command[22] = 0x02;//写入方式,1是按位,2是按字
        command[23] = (byte)(data.Length / 256);
        command[24] = (byte)(data.Length % 256);//写入数据个数
        command[25] = (byte)(dbAddress / 256);
        command[26] = (byte)(dbAddress % 256);//DB块的编号
        command[27] = type;
        command[28] = (byte)(beginAddress / 256 / 256 % 256); ;
        command[29] = (byte)(beginAddress / 256 % 256);
        command[30] = (byte)(beginAddress % 256);//[28][29][30]访问DB块的偏移量
        command[31] = 0x00;
        command[32] = 0x04;//04 byte(字节) 03bit(位)
        command[33] = (byte)(data.Length * 8 / 256);
        command[34] = (byte)(data.Length * 8 % 256);//按位计算出的长度
        data.CopyTo(command, 35);
        return command;
    }
    

    写入数据

    public Result Write(string address, byte[] data)
    {
        if (!socket?.Connected ?? true) Connect();
        Result result = new Result(); 
        Array.Reverse(data);
        //发送写入信息
        var arg = ConvertArg(address);
        byte[] command = GetWriteCommand(arg.TypeCode, arg.BeginAddress, arg.DbBlock, data);
        result.Requst = string.Join(" ", command.Select(t => t.ToString("X2")));
        var dataPackage = SendPackage(command);
        result.Response = string.Join(" ", dataPackage.Select(t => t.ToString("X2")));
        return result;
    }
    

    IoTClient中S7-200SmarTcp协议的使用

    安装

    Nuget安装 Install-Package IoTClient
    或图形化安装

    使用

    //1、实例化客户端 - 输入正确的IP和端口
    SiemensClient client = new SiemensClient(SiemensVersion.S7_200Smart, "127.0.0.1",102);
    
    //2、写操作
    client.Write("Q1.3", true);
    client.Write("V2205", (short)11);
    client.Write("V2209", 33);
    
    //3、读操作
    var value1 = client.ReadBoolean("Q1.3").Value;
    var value2 = client.ReadInt16("V2205").Value;
    var value3 = client.ReadInt32("V2209").Value;
    
    //4、如果没有主动Open,则会每次读写操作的时候自动打开自动和关闭连接,这样会使读写效率大大减低。所以建议手动Open和Close。
    client.Open();
    
    //5、读写操作都会返回操作结果对象Result
    var result = client.ReadInt16("V2205");
    //5.1 读取是否成功(true或false)
    var isSucceed = result.IsSucceed;
    //5.2 读取失败的异常信息
    var errMsg = result.Err;
    //5.3 读取操作实际发送的请求报文
    var requst  = result.Requst;
    //5.4 读取操作服务端响应的报文
    var response = result.Response;
    //5.5 读取到的值
    var value3 = result.Value;
    

    结束

  • 相关阅读:
    Java中读取.properties配置文件的通用类
    静态工厂方法中单例的延迟加载技术
    AVL树C++实现
    初探Java反射机制
    项目中一个 1 毫秒引发的问题
    用Java操作数据库Datetime数据
    【转】Linux中的EAGAIN含义
    【转】关于编译链接——gcc/g++
    《Linux多线程编程手册》读书笔记
    《Linux多线程服务端编程——使用muduo C++网络库》读书笔记
  • 原文地址:https://www.cnblogs.com/zhaopei/p/12834787.html
Copyright © 2011-2022 走看看