西门子S7通信 含C#代码

西门子S7通信 含C#代码

  • 前言
  • 一、S7协议
    • 1.协议概述
        • 第5层TPKT
        • 第6层COTP
        • 第7层S7 communication
    • 2.一次完整的S7(读)通讯
  • 二、完整代码
  • 三、附错误码


前言

本文主要介绍西门子的S7Comm协议(含源码)

和西门子PLC通讯,.Net平台中有不少已经封装好的动态库,使用起来有一些局限性。此文章主要是自己拼装S7协议报文来实现通讯。


一、S7协议

S7Comm(S7 Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。

1.协议概述

完整的一个S7通信请求包含7层。

OSI层S7协议
第一层:物理层Frame
第二层:数据链路层Ethernet II
第三层:网络层Internet Protocol Version4
第四层:传输层Transmission Control Protocol
第五层:会话层TPKT
第六层:表示层COTP
第七层:应用层S7 Communication

S7协议完整报文

其中,第1~4层会由计算机自己完成(底层驱动程序,对应C#代码中socket对象,我们只需拼接后3层报文)。关于后三层报文有大量博客阐述,其中有些说法是错误的,最好的是有个PLC自己去尝试总结。

第5层TPKT

应用程数据传输协议,介于TCP和COTP协议之间;这是一个传输服务协议,主要用来在COTP和TCP之间建立桥梁;

第6层COTP

COTP 是 OSI 7层协议定义的位于TCP之上的协议。COTP 以“Packet”为基本单位来传输数据,这样接收方会得到与发送方具有相同边界的数据;

第7层S7 communication

这一层和用户数据相关,对PLC数据的读取报文在这里完成;

2.一次完整的S7(读)通讯

在这里插入图片描述
完整的一次plc通讯应包含

  1. TCP三次握手,对应C#中socket.Connect()方法;
  2. 发起COTP请求,报文第六个字节为PDU类型 0x0e;
  3. 建立通信,此时包含完整的7层S7协议,S7 communication中功能码0xf0可以判断;
  4. 发送请求数据;
  5. 实际还应有断开请求,关闭socket(TCP四次挥手),代码中未包含。
    对于一些字节的处理在代码中有相应描述,好在有很多字节是固定的。
PDU typ类型有:​
0x1: ED Expedited Data,加急数据​
0x2: EA Expedited Data Acknowledgement,加急数据确认​
0x4: UD,用户数据​
0x5: RJ Reject,拒绝​
0x6: AK Data Acknowledgement,数据确认​
0x7: ER TPDU Error,TPDU错误​
0x8: DR Disconnect Request,断开请求​
0xC: DC Disconnect Confirm,断开确认​
0xD: CC Connect Confirm,连接确认​
0xE: CR Connect Request,连接请求​
0xF: DT Data,数据传输

功能码附录:
0x00    CPU services    CPU服务
0xf0    Setup communication 建立通信
0x04    Read Var        读取值
0x05    Write Var       写入值
0x1a    Request download    请求下载
0x1b    Download block  下载块
0x1c    Download ended  下载结束
0x1d    Start upload    开始上传
0x1e    Upload  上传
0x1f    End upload      上传结束
0x28    PI-Service      程序调用服务
0x29    PLC Stop        关闭PLC

二、完整代码

代码如下(示例):

using System;
using System.Linq;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Threading;

namespace comtest
{
    internal class Program
    {
        static Task task;
        static void Main(string[] args)
        {
            JpS7();
            while (true)
            {

            }
        }

        private static void JpS7()
        {
            Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            socket.Connect("192.168.188.102", 102);

            task = new Task(() =>
            {
                while (true)
                {
                    byte[] bytes = new byte[4];

                    socket.Receive(bytes);
                    Console.WriteLine(BitConverter.ToString(bytes).Replace("-", " 0x"));

                    byte[] num = bytes.ToList().GetRange(2, 2).ToArray();
                    Array.Reverse(num);
                    ushort num1 = BitConverter.ToUInt16(num, 0);
                    byte[] bytes1 = new byte[num1 - 4];
                    socket.Receive(bytes1);
                    Console.WriteLine(BitConverter.ToString(bytes1).Replace("-", " 0x"));

                    Console.WriteLine(num1);

                    if (bytes1[15] == 0x04)
                    {
                        if (bytes1[14] == 0x00)//errorcode 成功了才有下面分析,否则根据错误码提示相应错,错误字典可见西门子
                        {
                            var bytedatalength = bytes1.ToList().GetRange(11, 2).ToArray();
                            Array.Reverse(bytedatalength);
                            ushort datalenth = BitConverter.ToUInt16(bytedatalength, 0);
                            if (bytes1[16] == 1)//暂时分析一个item 多item逐渐扩展
                            {
                                byte[] bytedatas = new byte[datalenth - 4];
                                //bytedatas就是整个返回的数据,根据位置,可以推测出字节范围处理求值
                                bytedatas = bytes1.ToList().GetRange(bytes1.Count() - (datalenth - 4), datalenth - 4).ToArray();

                                byte[] bytef2 = new byte[4];
                                bytef2 = bytedatas.ToList().GetRange(4, 4).ToArray();
                                Array.Reverse(bytef2);
                                var f2data = BitConverter.ToSingle(bytef2, 0);
                                Console.WriteLine(f2data);

                                byte[] bytef3 = new byte[4];
                                bytef2 = bytedatas.ToList().GetRange(0, 4).ToArray();
                                Array.Reverse(bytef2);
                                var f3data = BitConverter.ToSingle(bytef2, 0);
                                Console.WriteLine(f3data);
                            }
                        }
                    }
                }
            });
            task.Start();

            ConnectRequest(socket);
            SetupCommunication(socket);
            ReadVar(socket);
        }

        private static void ConnectRequest(Socket socket)
        {
            byte[] sendcotp = new byte[]
                        {
                0x03,//版本号
                0x00,//保留字段
                0x00,0x16,//该报文长度22  假如大于255,则0x00有值,最大长度
                0x11,//cotp协议段字节数  17
                0xe0,//PDU类型  0xe0代表CR  connect request连接请求
                0x00,0x00,//源标识
                0x00,0x2e,//目标标识
                0x00,
                0xc1 ,//src-tsap
                0x02,//参数长度
                0x01,//01:PG通讯、02:OP通讯、03:S7单边通讯、10:S7双边通讯
                0x00,//
                0xc2,//dst-tsap
                0x02,
                0x03,
                0x00,//前一位为rack*2,后一位为slot插槽,假如机架号为2插槽为10则应为0x4a
                0xc0,//TPDU的size
                0x01,
                0x0a//2^10
                        };

            socket.Send(sendcotp);
        }

        private static void SetupCommunication(Socket socket)
        {
            byte[] s7con = new byte[]
                        {
                //tpkt段
                0x03,
                0x00,
                0x00,
                0x19,
                //cotp段 固定
                0x02,
                0xf0,
                0x80,
                //s7communication段(header)
                0x32,//固定0x32开头  72是访问优化的地址块
                0x01,//发送请求  0x02相应请求不带数据  0x03响应并带数据 0x07 扩展 
                0x00,0x00,0xff,0xff,//暂时固定
                0x00,0x08,//parameter长度
                0x00,0x00,//data长度(请求固定为0)
                //s7communication段(parameter)
                0xf0, //功能码,f0建立通信
                0x00,
                0x00,0x03,//可能是请求队列
                0x00,0x03,//可能是响应队列
                0x03,0xc0,//960
                        };
            socket.Send(s7con);
            Thread.Sleep(100);
        }

        private static void ReadVar(Socket socket)
        {
            byte[] buffer = new byte[]
                        {
                0x03 ,0x00 ,0x00 ,0x1f ,
                0x02 , 0xf0 , 0x80 ,
                0x32 ,
                0x01 ,
                0x00 ,
                0x00 ,
                0x00 ,
                0x00 ,
                0x00 , 0x0e ,//parameter里面14个字节
                0x00 ,0x00 ,
                //Db20 db20 = (Db20)plc.ReadStruct(typeof(Db20), 20, 420);
                0x04 ,//读请求
                0x01 ,//item个数    即结构个数            
                0x12 ,//结构标识
                0x0a ,//当前item下数据个数10
                0x10 ,//寻址模式 对应DB20.DBD0
                0x02 ,//读取数据类型01 bit,02 byte,03 char,04 word,05 int,dword,counter  
                0x00 ,0x0a ,//读取长度,对应Db20结构体共21个字节
                0x00 ,0x14 ,//块编号  对应DB20中的20
                0x84 ,//84对应DB 83M,82Q,81I,80直接访问外设 87全局变量 86局部变量  1c计数器 1d定时器
                0x00 ,0x0d ,0x20//开始字节以及位
                                //0000 0000 0000 0 000
                                //        字节地址 位地址  
                                //0x0d20转2进制0000 1101 0010 0000 因为一个字节8位用3位2进制即可标识,此处末3位000代表从0位开始取,1101 0010 0转10进制即为420
                         };
            socket.Send(buffer);
        }
    }
}

三、附错误码

0x0000 成功
0x0110 块号无效 
0x0111 请求长度无效 
0x0112 参数无效 
0x0113 块类型无效 
0x0114 找不到块 
0x0115 块已存在 
0x0116 块被写保护 
0x0117/操作系统更新太大 
0x0118 块号无效 
0x0119 输入的密码不正确 
0x011A PG资源错误 
0x011B PLC资源错误 
0x011C 协议错误 
0x011D 块太多(与模块相关的限制) 
0x011E 不再与数据库建立连接,或者S7DOS句柄无效 
0x011F 结果缓冲区太小 
0x0120 块结束列表 
0x0140 可用内存不足 
0x0141 由于缺少资源,无法处理作业 
0x8001 当块处于当前状态时,无法执行请求的服务 
0x8003 S7协议错误:传输块时发生错误 
0x8100 应用程序,一般错误:远程模块未知的服务 
0x8104 未在模块上实现此服务或报告了帧错误 
0x8204 对象的类型规范不一致 
0x8205 复制的块已存在且未链接 
0x8301 模块上的内存空间或工作内存不足,或者指定的存储介质不可访问 
0x8302 可用资源太少或处理器资源不可用 
0x8304 无法进一步并行上传。存在资源瓶颈 
0x8305 功能不可用 
0x8306 工作内存不足(用于复制,链接,加载AWP) 
0x8307 保持性工作记忆不够(用于复制,链接,加载AWP) 
0x8401 S7协议错误:无效的服务序列(例如,加载或上载块) 
0x8402 由于寻址对象的状态,服务无法执行 
0x8404 S7协议:无法执行该功能 
0x8405 远程块处于DISABLE状态(CFB)。该功能无法执行 
0x8500 S7协议错误:帧错误 
0x8503 来自模块的警报:服务过早取消 
0x8701 寻址通信伙伴上的对象时出错(例如,区域长度错误) 
0x8702 模块不支持所请求的服务 
0x8703 拒绝访问对象 
0x8704 访问错误:对象已损坏 
0xD001 协议错误:非法的作业号 
0xD002 参数错误:非法的作业变体 
0xD003 参数错误:模块不支持调试功能 
0xD004 参数错误:作业状态非法 
0xD005 参数错误:作业终止非法 
0xD006 参数错误:非法链路断开ID 
0xD007 参数错误:缓冲区元素数量非法 
0xD008 参数错误:扫描速率非法 
0xD009 参数错误:执行次数非法 
0xD00A 参数错误:非法触发事件 
0xD00B 参数错误:非法触发条件 
0xD011 调用环境路径中的参数错误:块不存在 
0xD012 参数错误:块中的地址错误 
0xD014 参数错误:正在删除/覆盖块 
0xD015 参数错误:标签地址非法 
0xD016 参数错误:由于用户程序错误,无法测试作业 
0xD017 参数错误:非法触发号 
0xD025 参数错误:路径无效 
0xD026 参数错误:非法访问类型 
0xD027 参数错误:不允许此数据块数 
0xD031 内部协议错误 
0xD032 参数错误:结果缓冲区长度错误 
0xD033 协议错误:作业长度错误 
0xD03F 编码错误:参数部分出错(例如,保留字节不等于00xD041 数据错误:非法状态列表ID 
0xD042 数据错误:标签地址非法 
0xD043 数据错误:找不到引用的作业,检查作业数据 
0xD044 数据错误:标签值非法,检查作业数据 
0xD045 数据错误:HOLD中不允许退出ODIS控制 
0xD046 数据错误:运行时测量期间非法测量阶段 
0xD047 数据错误:“读取作业列表”中的非法层次结构 
0xD048 数据错误:“删除作业”中的非法删除ID 
0xD049 “替换作业”中的替换ID无效 
0xD04A 执行'程序状态'时出错 
0xD05F 编码错误:数据部分出错(例如,保留字节不等于0...0xD061 资源错误:没有作业的内存空间 
0xD062 资源错误:作业列表已满 
0xD063 资源错误:触发事件占用 
0xD064 资源错误:没有足够的内存空间用于一个结果缓冲区元素 
0xD065 资源错误:没有足够的内存空间用于多个结果缓冲区元素 
0xD066 资源错误:可用于运行时测量的计时器被另一个作业占用 
0xD067 资源错误:“修改标记”作业过多(特别是多处理器操作) 
0xD081 当前模式下不允许使用的功能 
0xD082 模式错误:无法退出HOLD模式 
0xD0A1 当前保护级别不允许使用的功能 
0xD0A2 目前无法运行,因为正在运行的函数会修改内存 
0xD0A3 I / O上活动的“修改标记”作业太多(特别是多处理器操作) 
0xD0A4 '强制'已经建立 

参考以下作者文章:
链接: 西门子S7协议及报文格式详解

  • 29
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
西门子 S7 协议 C 是一种通信协议,用于西门子工业自动化设备之间的通信S7 协议 C 是一种基于客户端-服务器架构的协议,其中客户端是指请求某种服务的设备或软件,服务器是提供服务的设备或软件。 S7 协议 C 的特点是高效、稳定和可靠。它采用了二进制协议,传输效率高,能够实现快速的数据传输。同时,S7 协议 C 使用了错误检测和纠正机制,保证数据传输的准确性和可靠性。这使得 S7 协议 C 在工业自动化控制系统中得到广泛应用。 S7 协议 C 的应用范围广泛。它可以用于连接不同类型的设备,如 PLC(可编程逻辑控制器)、传感器、执行机构等。通过 S7 协议 C,这些设备可以相互通信,实现数据的共享和交换。这为工业生产过程的监控和控制提供了便利,提高了生产效率。 S7 协议 C 还支持多种通信方式。它可以通过以太网、串口等不同的物理介质进行通信,适应不同的工业环境。同时,S7 协议 C 也支持多种通信协议,如 TCP/IP、ISO-on-TCP、OPC 等。这使得 S7 协议 C 可以与其他设备和系统进行无缝集成和通信。 总之,S7 协议 C 是一种高效、稳定和可靠的通信协议,被广泛应用于工业自动化控制系统中。它的特点是支持客户端-服务器架构、采用二进制协议、具有错误检测和纠正机制,适用于连接不同类型的设备,并支持多种通信方式和协议

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溧阳苏东坡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值