- (1)建立TCP连接 Socket.Connect
- (2)发送访问请求 COTP
- (3)交换通信信息 Setup Communication
- (4)执行相关操作 读、写、PLC启停、时间、上传下载
一、CTOP请求连接
static void Main(string[] args)
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//socket.Connect("192.168.2.1", 102); // 200Smart
socket.Connect("192.168.151.200", 102);// 1500
}
COTP(socket);// 如果需要判断是否连接成功:通过异常捕获
static void COTP(Socket socket)
{
List<byte> bytes = new List<byte>();
// TPKT
bytes.Add(0x03);
bytes.Add(0x00);
bytes.Add(0x00);
bytes.Add(0x16);
// COTP
bytes.Add(0x11);// 10#17
bytes.Add(0xe0);
bytes.Add(0x00);
bytes.Add(0x00);
bytes.Add(0x00);
bytes.Add(0x00);
bytes.Add(0x00);
bytes.Add(0xc0);
bytes.Add(0x01);
bytes.Add(0x0a);
bytes.Add(0xc1); // 源设备(上位机PC)通信配置 S7协议 西门子设备之间也是要使用
bytes.Add(0x02);
bytes.Add(0x01); //[17]
bytes.Add(0x00); //[18]
bytes.Add(0xc2); // PLC
bytes.Add(0x02);
bytes.Add(0x03); // [21]
bytes.Add(0x01); // [22] 机架号0 插槽号1
socket.Send(bytes.ToArray(), 0, bytes.Count, SocketFlags.None);
}
TCP为Socket对象的三次握手,发送COTP连接请求后收到PLC的响应报文。
二、SetupCommunication通讯数据交换
PLC响应数据:S7-Parameter-PDU length 240,表示PLC最大处理数据能力为240个字节
static void SetupCommunication(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x19);
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x08);// Paremeter部分字节长度
reqBytes.Add(0x00);
reqBytes.Add(0x00);// Data部分字节长度
// S7-Parameter
reqBytes.Add(0xf0);// Function
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x03);// Calling
reqBytes.Add(0x00);
reqBytes.Add(0x03);// Called
reqBytes.Add(0x03);
reqBytes.Add(0xc0);// PDU
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
三、S7COMM-Read报文
TPKT-整个请求字节数:后台会自动修改
Item[*]-数据块编号:请求数据地址为DB数据块时为DB数据块编号,请求其他数据时写0;
Item[*]-数据地址:举例M100.3的位置100.3表示为
单次读响应报文
static void Read(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x19);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x0e);// Paremeter部分字节长度。注意后面修改
reqBytes.Add(0x00);
reqBytes.Add(0x00);// Data部分字节长度
// S7-Parameter
reqBytes.Add(0x04);// Function
reqBytes.Add(0x01);// Item的个数
reqBytes.AddRange(ItemQ0_4());// 组装请求Q0.4的Item
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);// 小端
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
// Q0.4 位 Q区0号字节中的4号位
static List<byte> ItemQ0_4()
{
List<byte> items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x01);
items.Add(0x00);
items.Add(0x01);//请求一个
items.Add(0x00);
items.Add(0x00);
items.Add(0x82);
// 地址计算
int byteAddr = 0;
byte bitAddr = 4;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> ItemMB10()
{
List<byte> items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x02);// Byte - 0x02
items.Add(0x00);
items.Add(0x03);//请求三个 MB0 MB1 MB2
items.Add(0x00);
items.Add(0x00);
items.Add(0x83); //Q区
// 地址计算
int byteAddr = 10;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> ItemDBD2()
{
List<byte> items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x06);// DWord - 0x06
items.Add(0x00);
items.Add(0x01);//请求一个
items.Add(0x00);
items.Add(0x01);// DB编号
items.Add(0x84); //DB区
// 地址计算
int byteAddr = 2;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
请求地址MB10读取一个B返回的数据长度为一个字节时,PLC响应的数据长度为0x08,数据长度位数。
连续读响应报文
连续请求返回的数据不为最后一个且位奇数字节时,会出现占位字节
下图位连续请求两个Q区单位数据时PLC的响应报文
请求的即将返回长度<PDU(PLC最大处理数据量,通过数据交换通讯获取)
/// 关于Fill Byte:
/// 明确:读请求的时候 可以进行多个Item的分组
/// 比如:第一个Item Q0.4(1) 返回Data中一个字节
/// 第二个Item MW10(1) 返回Data中二个字节 ,不管请求多少都是偶数个
/// 第三个Item MB5(3) 返回Data中一个字节 ,
/// 第四个Item MB1(1) 返回Data中一个字节
/// Parameter中请求三个Item Data中分别响应三个Item
/// Item[1] 1Byte 奇数个 需要填充
/// Item[2] 2Byte 偶数个 不需要填充
/// Item[3] 3Byte 奇数个 需要填充
/// Item[4] 1Byte 奇数个 不需要填充
/// 结论:
/// 1、返回的Data中的Item是否在最后一个
/// 2、返回的Data中的Item中的数据字节数是否奇数个
/// 如果前两条件满足,则添加Fill Byte
连续请求示例:
static void Read(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x00);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x26);// Paremeter部分字节长度。注意后面修改 14 26 1A 38 26
reqBytes.Add(0x00);
reqBytes.Add(0x00);// Data部分字节长度
// S7-Parameter
reqBytes.Add(0x04);// Function
reqBytes.Add(0x03);// Item的个数
reqBytes.AddRange(ItemQ0_4());// 组装请求Q0.4的Item
//reqBytes.AddRange(ItemMB10());// 组装请求MB10的Item
//reqBytes.AddRange(ItemVW100());// 组装请求VW100的Item
//reqBytes.AddRange(ItemVD0());// 组装请求VD0的Item
//reqBytes.AddRange(ItemDBD2());// 组装请求VD0的Item
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);// 小端
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
static List<byte> ItemQ0_4()
{
List<byte> items = new List<byte>();
#region Q0.4
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x01);
items.Add(0x00);
items.Add(0x01);//请求一个 Q0.4 Q0.5 Q0.6 结果:返回异常。如何一个
// 1、按字节、字、双字的方式一个次请求
// 2、要求一个Item一个Bit 多个Item
items.Add(0x00);
items.Add(0x00);
items.Add(0x82); //Q区
// 地址计算
int byteAddr = 0;
byte bitAddr = 4;
byteAddr = (byteAddr << 3) + bitAddr;
// int [3][2][1][0]
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
#endregion
#region MW10
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x04);
items.Add(0x00);
items.Add(0x01);//请求一个
items.Add(0x00);
items.Add(0x00);
items.Add(0x83); //M区
// 地址计算
byteAddr = 10;
bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
#endregion
#region MB10
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x02);
items.Add(0x00);
items.Add(0x01);//请求一个
items.Add(0x00);
items.Add(0x00);
items.Add(0x83); //M区
// 地址计算
byteAddr = 10;
bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
#endregion
#region MB1
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x02);
items.Add(0x00);
items.Add(0x01);//请求一个
items.Add(0x00);
items.Add(0x00);
items.Add(0x83); //Q区
// 地址计算
byteAddr = 1;
bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
#endregion
return items;
}
字符串读取
static void ReadString(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x00);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x0e);// Paremeter部分字节长度。注意后面修改 14 26 1A
reqBytes.Add(0x00);
reqBytes.Add(0x00);// Data部分字节长度
// S7-Parameter
reqBytes.Add(0x04);// Function
reqBytes.Add(0x01);// Item的个数
reqBytes.AddRange(ParamString());// 组装请求Q0.4的Item
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);// 小端
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
//DB1.DBB6
static List<byte> ParamString()
{
List<byte> items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x03);// Char
items.Add(0x00);
items.Add(0x07);//请求五个
items.Add(0x00);
items.Add(0x01);// DB编号
items.Add(0x84); //DB区
// 地址计算
int byteAddr = 6;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
//BitConverter.GetBytes(byteAddr); // [0][1][2][]
// /256 进行计算
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
请求字符串数据时需要加增加两个读取位置;
PLC响应返回的数据中前两个0a代表DB块中分配的控件,05表示当前所存的字符长度;
四、S7COMM-Write报文
单次写响应报文
static void Write(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x00);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
//reqBytes.Add(0x00);
//reqBytes.Add(0x26);// Paremeter部分字节长度。注意后面修改 14 26 1A 38 26
//reqBytes.Add(0x00);
//reqBytes.Add(0x00);// Data部分字节长度
List<byte> paramList = ParameterItemM0_0();
//List<byte> paramList = ParameterItemVW10();
ushort pl = (ushort)paramList.Count;
byte[] plenBytes = BitConverter.GetBytes(pl);
reqBytes.Add(plenBytes[1]);
reqBytes.Add(plenBytes[0]);
List<byte> dataList = DataItemM0_0();
//List<byte> dataList = DataItemVW10();
ushort dl = (ushort)dataList.Count;
byte[] dlenBytes = BitConverter.GetBytes(dl);
reqBytes.Add(dlenBytes[1]);
reqBytes.Add(dlenBytes[0]);
//
reqBytes.AddRange(paramList);
reqBytes.AddRange(dataList);
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
static List<byte> ParameterItemM0_0()
{
List<byte> items = new List<byte>();
items.Add(0x05);//功能码:写入动作
items.Add(0x01);// Items的个数 Data的Item个数据与Parameter的Item个数匹配
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x01);
items.Add(0x00);
items.Add(0x01);
items.Add(0x00);
items.Add(0x00);
items.Add(0x83); //Q区
// 地址计算
int byteAddr = 0;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> DataItemM0_0()
{
List<byte> items = new List<byte>();
items.Add(0x00);
items.Add(0x03);
items.Add(0x00);
items.Add(0x01);
items.Add(0x01);// 表示写入数据,Bit(0x00 0x01)
return items;
}
static List<byte> ParameterItemVW10()
{
List<byte> items = new List<byte>();
items.Add(0x05);//功能码:写入动作
items.Add(0x01);// Items的个数 Data的Item个数据与Parameter的Item个数匹配
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x04);
items.Add(0x00);
items.Add(0x02);
items.Add(0x00);
items.Add(0x01);
items.Add(0x84); //Q区
// 地址计算
int byteAddr = 10;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> DataItemVW10()
{
List<byte> items = new List<byte>();
items.Add(0x00);
items.Add(0x04);
items.Add(0x00);
items.Add(0x20);// 写入的位数
items.Add(0x00);
items.Add(0x7b);// 表示写入数据
items.Add(0x00);
items.Add(0x7c);// 表示写入数据
return items;
}
连续写响应报文
static void Write(Socket socket)
{
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x00);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
//reqBytes.Add(0x00);
//reqBytes.Add(0x26);// Paremeter部分字节长度。注意后面修改 14 26 1A 38 26
//reqBytes.Add(0x00);
//reqBytes.Add(0x00);// Data部分字节长度
//List<byte> paramList = ParameterItemM0_0();
//List<byte> paramList = ParameterItemVW10();
List<byte> paramList = ParameterItemMulit();
ushort pl = (ushort)paramList.Count;
byte[] plenBytes = BitConverter.GetBytes(pl);
reqBytes.Add(plenBytes[1]);
reqBytes.Add(plenBytes[0]);
//List<byte> dataList = DataItemM0_0();
//List<byte> dataList = DataItemVW10();
List<byte> dataList = DataItemMulit();
ushort dl = (ushort)dataList.Count;
byte[] dlenBytes = BitConverter.GetBytes(dl);
reqBytes.Add(dlenBytes[1]);
reqBytes.Add(dlenBytes[0]);
//
reqBytes.AddRange(paramList);
reqBytes.AddRange(dataList);
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
static List<byte> ParameterItemMulit()
{
List<byte> items = new List<byte>();
items.Add(0x05);//功能码:写入动作
items.Add(0x03);// Items的个数 Data的Item个数据与Parameter的Item个数匹配
// VB10
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x02);// 类型 02 Byte 03 Char 04 Word 06 DWord
items.Add(0x00);
items.Add(0x01);// 写一个值
items.Add(0x00);
items.Add(0x01);
items.Add(0x84); //V
// 地址计算
int byteAddr = 10;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
// VB100 VB101 VB102 VB103
// VW100 VW102
//items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x04);// 类型 02 Byte 03 Char 04 Word 06 DWord
items.Add(0x00);
items.Add(0x02);// 写一个值
items.Add(0x00);
items.Add(0x01);
items.Add(0x84); //V
// 地址计算
byteAddr = 100;
bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
// VD110
//items = new List<byte>();
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x06);// 类型 02 Byte 03 Char 04 Word 06 DWord
items.Add(0x00);
items.Add(0x01);// 写一个值
items.Add(0x00);
items.Add(0x01);
items.Add(0x84); //V
// 地址计算
byteAddr = 110;
bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> DataItemMulit()
{
// VB10 Byte
List<byte> items = new List<byte>();
items.Add(0x00);
items.Add(0x04);//Byte/Word/DWord
items.Add(0x00);
items.Add(0x08);// 写入的位数
items.Add(0x0a);//10#10
items.Add(0x00);// Fill byte 当出现奇数个字节 需要填充
// VW100 VW102 123 7B 124 7C
items.Add(0x00);
items.Add(0x04);//Byte/Word/DWord
items.Add(0x00);
items.Add(0x20);// 写入的位数 2个字 4个字节 10#32位 16#20
items.Add(0x00);//
items.Add(0x7b);//10#123
items.Add(0x00);//
items.Add(0x7c);//10#124
// VD110 4.5 40 90 00 00
items.Add(0x00);
items.Add(0x04);
items.Add(0x00);
items.Add(0x20);
items.Add(0x40);
items.Add(0x90);
items.Add(0x00);
items.Add(0x00);
return items;
}
与对于奇数字节的数据需要手动调整占位字节
字符串写入
测试PLC为西门子200,西门子1200/1500写入,需要再字符Byte前必须添加两个字节:
1.0x00 空间 一般大于等于字符数即可,小于不行
2.0x00 字符数
static void S7String(Socket socket)
{
//
// 针对Byte数据进行处理
List<byte> reqBytes = new List<byte>();
// TPKT
reqBytes.Add(0x03);
reqBytes.Add(0x00);
// 初始化无意义,只做点位,后续做修改 ,注意第161行
reqBytes.Add(0x00);
reqBytes.Add(0x00);// 注意下后台再修改---
// COTP
reqBytes.Add(0x02);
reqBytes.Add(0xf0);
reqBytes.Add(0x80); // 1000 0000
// S7-Header
reqBytes.Add(0x32);
reqBytes.Add(0x01);// ROSCTR
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
reqBytes.Add(0x00);
// Parameter
List<byte> paramList = ParameterItemString();
ushort pl = (ushort)paramList.Count;
byte[] plenBytes = BitConverter.GetBytes(pl);
reqBytes.Add(plenBytes[1]);
reqBytes.Add(plenBytes[0]);
// Data
List<byte> dataList = DataItemString();
ushort dl = (ushort)dataList.Count;
byte[] dlenBytes = BitConverter.GetBytes(dl);
reqBytes.Add(dlenBytes[1]);
reqBytes.Add(dlenBytes[0]);
//
reqBytes.AddRange(paramList);
reqBytes.AddRange(dataList);
ushort len = (ushort)reqBytes.Count;
byte[] lenBytes = BitConverter.GetBytes(len);
reqBytes[2] = lenBytes[1];
reqBytes[3] = lenBytes[0];
//
socket.Send(reqBytes.ToArray(), 0, reqBytes.Count, SocketFlags.None);
}
static List<byte> ParameterItemString()
{
List<byte> items = new List<byte>();
items.Add(0x05);//功能码:写入动作
items.Add(0x01);// Items的个数 Data的Item个数据与Parameter的Item个数匹配
// VB20
items.Add(0x12);
items.Add(0x0a);
items.Add(0x10);
items.Add(0x03);// 类型 02 Byte 03 Char 04 Word 06 DWord
items.Add(0x00);
items.Add(0x05);// 写一个值
items.Add(0x00);
items.Add(0x01);
items.Add(0x84); //V
// 地址计算
int byteAddr = 20;
byte bitAddr = 0;
byteAddr = (byteAddr << 3) + bitAddr;
items.Add((byte)(byteAddr / 256 / 256 % 256));
items.Add((byte)(byteAddr / 256 % 256));
items.Add((byte)(byteAddr % 256));
return items;
}
static List<byte> DataItemString()
{
string str = "Hello";
byte[] strBytes = Encoding.UTF8.GetBytes(str);
List<byte> items = new List<byte>();
items.Add(0x00);
items.Add(0x09);// String
int bitCount = strBytes.Length;
items.Add(BitConverter.GetBytes(bitCount)[1]);
items.Add(BitConverter.GetBytes(bitCount)[0]);// 写入的位数
items.AddRange(strBytes);
return items;
}
五、S7COMM-其他功能报文
PLC 200Smart 并不对所有PLC有效 1200 1500 无法操作
Step7 s7 0x32标准协议 对200Smart进行操作
博途 S7 0x72标准协议 对1200 1500
区别:结构变化很大 72 包含的内容更多
S7COMM通讯限制:
PLC Run 请求与响应
static void Run(Socket socket)
{
string s = "P_PROGRAM";
byte[] sb = Encoding.ASCII.GetBytes(s);
byte[] runBytes = new byte[] {
// TPKT
0x03,0x00,0x00,0x25,
// COTP
0x02,0xf0,0x80,
// Header
0x32,0x01,
0x00,0x00,0x00,0x00,
// PL
0x00,0x14,
// DL
0x00,0x00,
// Parameter
0x28, // 启动标识
0x00,0x00,0x00,0x00,0x00,0x00,0xfd,
0x00, 0x00,
0x09,
// P_PROGRAM 9个字符对应的16进制Ascii值
0x50,0x5f,0x50,0x52,0x4f,0x47,0x52,0x41,0x4d
};
try
{
socket.Send(runBytes);
int count = socket.Receive(new byte[20]);
}
catch (Exception ex)
{
}
}
PLC Stop请求与响应
static void Stop(Socket socket)
{
byte[] stopBytes = new byte[] {
// TPKT
0x03,0x00,0x00,0x21,
// COTP
0x02,0xf0,0x80,
// Header
0x32,0x01,
0x00,0x00,0x00,0x00,
// PL
0x00,0x10,
// DL
0x00,0x00,
// Parameter
0x29,// Stop标识
0x00,0x00,0x00,0x00,0x00,
0x09,
// P_PROGRAM 9个字符对应的16进制Ascii值
0x50,0x5f,0x50,0x52,0x4f,0x47,0x52,0x41,0x4d
};
socket.Send(stopBytes);
}
时间获取与设置请求与响应
static void ReadTime(Socket socket)
{
byte[] readTimeBytes = new byte[] {
// TPKT
0x03,0x00,0x00,0x1d,
// COTP
0x02,0xf0,0x80,
// Header
0x32,0x07, // UserData
0x00,0x00,0x00,0x00,
// PL
0x00,0x08,
// DL
0x00,0x04,
// Parameter
0x00,0x01,0x12,
0x04,// Parameter中当前字节后的字节数
0x11, 0x47,
0x01,// SubFunction Read Clock
0x00,
// Data
0x0a,0x00,0x00,0x00
};
socket.Send(readTimeBytes);
}
PLC响应数据:
Year 1:值为90-99时表示19XX年,否则表示20XX年;
获取的数据是16进制,需要直接转成10进制,例如Year2表示22年数据是16#22需要直接转为10#22而不是10#34;
六、附录:
附录1:COTP->PDU type已知枚举值
附录2:S7Header->ROSCTR已知枚举值
附录3:S7Header->Error class已知枚举值
附录4:S7Parameter->Error code已知枚举值
附录5:S7Parameter->Function已知枚举值
附录6:S7Parameter->Item->Syntax Id已知枚举值
附录7:S7Parameter->Item->Transport size常见值
附录8:S7Parameter->Item->Area常见值
附录9:S7Data->Item->Return code已知枚举值
附录10:Userdata已知枚举值
附录11:PI service names已知枚举值
附录14:SZL-ID 类型
标签:西门子,报文,0x00,Item,Add,items,S7COMM,reqBytes,List From: https://www.cnblogs.com/ZHIZRL/p/18184553