欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

IoTClient开发6 - S7-200SmarTcp协议客户端实现

程序员文章站 2022-06-24 09:36:02
环境和工具 服务端电脑IP:192.168.1.130 客户端电脑IP:192.168.1.120 1、在服务端电脑运行 "IoTClientTool" 2、运行 "Wireshark" 3、在客户端电脑运行 "IoTClientTool" 4、Wireshark得到如下报文 报文分析,plc的连接 ......

环境和工具

服务端电脑ip:192.168.1.130
客户端电脑ip:192.168.1.120
1、在服务端电脑运行iotclienttool
IoTClient开发6 - S7-200SmarTcp协议客户端实现

2、运行wireshark
IoTClient开发6 - S7-200SmarTcp协议客户端实现

3、在客户端电脑运行iotclienttool
IoTClient开发6 - S7-200SmarTcp协议客户端实现

4、wireshark得到如下报文
IoTClient开发6 - S7-200SmarTcp协议客户端实现

报文分析,plc的连接

我们看到上面连接西门子plc抓取到了八条报文。其中有tcp的三次握手、和对最后一次响应的回复,然后就是西门子特有的两次初始化指令的请求和响应。
IoTClient开发6 - S7-200SmarTcp协议客户端实现

两次初始化指令

不同型号的西门子plc有不同的初始化指令,同型号的指令固定不变。
IoTClient开发6 - S7-200SmarTcp协议客户端实现

代码实现对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,抓取到包
IoTClient开发6 - S7-200SmarTcp协议客户端实现
我们可以看到其中很多都是固定的数据,如版本号、协议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,抓取到包
IoTClient开发6 - S7-200SmarTcp协议客户端实现
我们可以以此规律获取对应的指令格式

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
或图形化安装
IoTClient开发6 - S7-200SmarTcp协议客户端实现

使用

//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;

结束