首页 > 其他分享 >西门子通讯协议-S7COMM报文

西门子通讯协议-S7COMM报文

时间:2024-05-11 18:20:10浏览次数:19  
标签:西门子 报文 0x00 Item Add items S7COMM reqBytes List

     - (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

相关文章

  • 西门子博途软件安装及使用
    西门子博途软件安装及使用一、博途软件的简介博途软件可以对西门子300、400、1200及1500产品进行组态、编程和调试。TIA博途软件是一个系统,里面包含有多种软件,可以满足用户在不同自动化控制系统中的各种需求。因此,博途软件要求的电脑配置较高,且安装文件较大,但安装过程还算比较......
  • HTTP 报文详解
    报文的语法所有的HTTP报文都可以分为两类:请求报文和响应报文。请求报文会向Web服务器请求一个动作,响应报文会将请求的结果返回给客户端。请求和响应报文的基本报文结构相同请求报文的格式:<method><request-URL><version><headers><entity-body>响应报文的格式:<vers......
  • 02_Modbus的功能码与报文详解
     Modbus协议类型  Modbus从站四张表类型 主站常用功能码  ModbusTCP请求报文,功能码03ModbusTCP应答报文,功能码03 0017为23个字节:请求长度加应答长度06+17=23;14为20长度:14+06=20  ModbusUDP请求报文,功能码03ModbusUDP应答报文,功能码03  M......
  • 西门子S200伺服如何清除<安全配置未受保护>报警提示?
    1,新建用户 2,设置用户名和密码及权限范围; 3,默认用户权限限制;  断电重启即可消除报警;......
  • C#S7.NET实现西门子PLCDB块数据采集的完整步骤
    前言本文介绍了如何使用S7.NET库实现对西门子PLCDB块数据的读写,记录了使用计算机仿真,模拟PLC,自至完成测试的详细流程,并重点介绍了在这个过程中的易错点,供参考。 用到的软件:1.Windows环境下链路层网络访问的行业标准工具(WinPcap_4_1_3.exe)下载链接:https://www.winpcap.org/in......
  • 手写协议报文 c语言手法
    鉴于绝大部分文件、网络通信协议、非网络通信协议都有类似的结构{类型,长度,校验,不定长数据,结束标志},再高级点的会包含多个单层TLV,甚至嵌套TLV,状态机流转标志等等。所以编程语言上也需要采用一定的手法。建立结构结构体和联合体例如//结构体对齐宏#ifdefined(__GNUC__)#defin......
  • C++ 上位软件通过Snap7开源库访问西门子S7-1200/S7-1500数据块的方法
    前言本人一直从事C++上位软件开发工作较多,在之前的项目中通过C++访问西门子PLCS7-200/S7-1200/S7-1500并进行数据交互的应用中一直使用的是ModbusTCP/ModbusRTU协议进行。Modbus上位开源库采用的LibModbus。经过实际应用发现Modbus开源库单次发送和接受的数据不能超......
  • Siemens 西门子 S7协议及报文格式详解
    一、简介S7Comm(S7Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、PRO......
  • Python 使用Snap7读写西门子S7系列PLC
    1.简介Snap7Snap7是一个基于s7通信协议的开源软件包,作者是DavideNardella,该软件包封装了S7通信的底层协议,可使用普通电脑通过编程与西门子S7系列PLC进行通信Snap7三大对象组件:客户端,服务器,合作者。下面是三者关系,更详细介绍可看官网。本篇主要讲述的是Client模式,我们的pc机作......
  • 马扎克,海德汉,哈斯,兄弟,发那科,三菱,西门子,华中数控,knd,广数,宝元,发格,无授权源码采集。机床
    机床联网cnc采集设备联网车间数字化生产追踪 无需授权可跨平台运行任何平台Linuxwindows等 可+:cnccaiji机床数据采集MDC,DNC,可定制开发,有采集驱动支持多品牌cnc系统多设备采集支持转发mqtt推送HTTP马扎克机床数据采集海德汉机床数据采集哈斯机床数据采集......