- (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); // 200Smartsocket.Connect("192.168.151.200", 102);// 1500
}COTP(socket);// 如果需要判断是否连接成功:通过异常捕获static void COTP(Socket socket)
{List<byte> bytes = new List<byte>();// TPKTbytes.Add(0x03);bytes.Add(0x00);bytes.Add(0x00);bytes.Add(0x16);// COTPbytes.Add(0x11);// 10#17bytes.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 插槽号1socket.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x19);// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.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-ParameterreqBytes.Add(0xf0);// FunctionreqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x03);// CallingreqBytes.Add(0x00);reqBytes.Add(0x03);// CalledreqBytes.Add(0x03);reqBytes.Add(0xc0);// PDUsocket.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x19);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.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-ParameterreqBytes.Add(0x04);// FunctionreqBytes.Add(0x01);// Item的个数reqBytes.AddRange(ItemQ0_4());// 组装请求Q0.4的Itemushort 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 - 0x02items.Add(0x00);items.Add(0x03);//请求三个 MB0 MB1 MB2items.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 - 0x06items.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x00);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x26);// Paremeter部分字节长度。注意后面修改 14 26 1A 38 26reqBytes.Add(0x00);reqBytes.Add(0x00);// Data部分字节长度// S7-ParameterreqBytes.Add(0x04);// FunctionreqBytes.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的Itemushort 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.4items.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 多个Itemitems.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 MW10items.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 MB10items.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 MB1items.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));#endregionreturn items;
}
字符串读取
static void ReadString(Socket socket)
{List<byte> reqBytes = new List<byte>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x00);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.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-ParameterreqBytes.Add(0x04);// FunctionreqBytes.Add(0x01);// Item的个数reqBytes.AddRange(ParamString());// 组装请求Q0.4的Itemushort 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);// Charitems.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x00);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x00);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.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个数匹配// VB10items.Add(0x12);items.Add(0x0a);items.Add(0x10);items.Add(0x02);// 类型 02 Byte 03 Char 04 Word 06 DWorditems.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 DWorditems.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 DWorditems.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/DWorditems.Add(0x00);items.Add(0x08);// 写入的位数items.Add(0x0a);//10#10items.Add(0x00);// Fill byte 当出现奇数个字节 需要填充// VW100 VW102 123 7B 124 7Citems.Add(0x00);items.Add(0x04);//Byte/Word/DWorditems.Add(0x00);items.Add(0x20);// 写入的位数 2个字 4个字节 10#32位 16#20items.Add(0x00);//items.Add(0x7b);//10#123items.Add(0x00);//items.Add(0x7c);//10#124// VD110 4.5 40 90 00 00items.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>();// TPKTreqBytes.Add(0x03);reqBytes.Add(0x00);// 初始化无意义,只做点位,后续做修改 ,注意第161行reqBytes.Add(0x00);reqBytes.Add(0x00);// 注意下后台再修改---// COTPreqBytes.Add(0x02);reqBytes.Add(0xf0);reqBytes.Add(0x80); // 1000 0000// S7-HeaderreqBytes.Add(0x32);reqBytes.Add(0x01);// ROSCTRreqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x00);reqBytes.Add(0x00);// ParameterList<byte> paramList = ParameterItemString();ushort pl = (ushort)paramList.Count;byte[] plenBytes = BitConverter.GetBytes(pl);reqBytes.Add(plenBytes[1]);reqBytes.Add(plenBytes[0]);// DataList<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个数匹配// VB20items.Add(0x12);items.Add(0x0a);items.Add(0x10);items.Add(0x03);// 类型 02 Byte 03 Char 04 Word 06 DWorditems.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);// Stringint 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[] {// TPKT0x03,0x00,0x00,0x25,// COTP0x02,0xf0,0x80,// Header0x32,0x01,0x00,0x00,0x00,0x00,// PL0x00,0x14,// DL0x00,0x00,// Parameter0x28, // 启动标识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[] {// TPKT0x03,0x00,0x00,0x21,// COTP0x02,0xf0,0x80,// Header0x32,0x01,0x00,0x00,0x00,0x00,// PL0x00,0x10,// DL0x00,0x00,// Parameter0x29,// 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[] {// TPKT0x03,0x00,0x00,0x1d,// COTP0x02,0xf0,0x80,// Header0x32,0x07, // UserData0x00,0x00,0x00,0x00,// PL0x00,0x08,// DL0x00,0x04,// Parameter0x00,0x01,0x12,0x04,// Parameter中当前字节后的字节数0x11, 0x47,0x01,// SubFunction Read Clock0x00,// Data0x0a,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 类型