Bootstrap

基于.NET 6的C# socket实现与西门子S7-1200 PLC通信

下面是一个基于.NET 6的C# socket实现与西门子S7-1200 PLC通信的完整代码,包含详细的注释和参数说明。这个示例假设你对S7协议的基本理解和PLC IP地址、端口、数据块(DB)地址等信息有一定了解。

在代码中添加十六进制参数的详细注释有助于理解每个字节的含义。以下是更新后的代码,包含了这些注释:

1. 引用必要的库

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

2. 创建S7通信类

public class S7Client
{
    private readonly string _ip;
    private readonly int _port;
    private Socket _socket;

    // 初始化S7Client对象
    public S7Client(string ip, int port = 102)
    {
        _ip = ip;
        _port = port;
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    }

    // 连接到PLC
    public async Task ConnectAsync()
    {
        await _socket.ConnectAsync(_ip, _port);
        // 建立连接后,发送S7连接请求
        byte[] connectionRequest = GetConnectionRequest();
        await _socket.SendAsync(connectionRequest, SocketFlags.None);
        
        // 接收PLC的响应
        byte[] response = new byte[1024];
        int received = await _socket.ReceiveAsync(response, SocketFlags.None);
        if (received == 0)
        {
            throw new Exception("PLC未响应");
        }
    }

    // 发送读取数据请求
    public async Task<byte[]> ReadDataAsync(int dbNumber, int startAddress, int size)
    {
        byte[] readRequest = GetReadRequest(dbNumber, startAddress, size);
        await _socket.SendAsync(readRequest, SocketFlags.None);

        // 接收PLC响应
        byte[] response = new byte[1024];
        int received = await _socket.ReceiveAsync(response, SocketFlags.None);
        if (received == 0)
        {
            throw new Exception("PLC未响应");
        }

        // 解析响应数据
        return ParseReadResponse(response, size);
    }

    // 关闭连接
    public void Disconnect()
    {
        _socket.Shutdown(SocketShutdown.Both);
        _socket.Close();
    }

    // 生成S7连接请求
    private byte[] GetConnectionRequest()
    {
        return new byte[]
        {
            0x03, 0x00, 0x00, 0x16, // TPKT Header
            0x11, 0xE0, 0x00, 0x00, // COTP Header
            0x00, 0x01, 0x00, 0xC1, // COTP Header
            0x02, 0x01, 0x00, 0xC2, // COTP Header
            0x02, 0x01, 0x02, 0xC0, // COTP Header
            0x01, 0x09              // COTP Header
        };
    }

    // 生成读取数据请求
    private byte[] GetReadRequest(int dbNumber, int startAddress, int size)
    {
        return new byte[]
        {
            0x03, 0x00, 0x00, 0x1F, // TPKT Header
            0x02, 0xF0, 0x80,       // COTP Header
            0x32,                   // S7 Protocol ID
            0x01,                   // Job Type: 1 (Read request)
            0x00, 0x00,             // Redundancy Identification
            0x00, 0x01,             // Protocol Data Unit Reference
            0x00, 0x0E,             // Parameter Length
            0x00, 0x04,             // Data Length
            0x01,                   // Function: 4 (Read)
            0x12,                   // Item Count
            0x0A,                   // Item Head Length
            0x10,                   // Syntax ID: 10 (S7 Any)
            0x02,                   // Transport Size: 2 (BYTE)
            (byte)(dbNumber / 256), (byte)(dbNumber % 256), // DB Number
            (byte)(startAddress / 256 / 256), (byte)((startAddress / 256) % 256), (byte)(startAddress % 256), // Start Address
            (byte)(size / 256), (byte)(size % 256) // Data Size
        };
    }

    // 解析读取数据响应
    private byte[] ParseReadResponse(byte[] response, int size)
    {
        byte[] data = new byte[size];
        Array.Copy(response, 21, data, 0, size);
        return data;
    }
}

3. 使用示例

public class Program
{
    public static async Task Main(string[] args)
    {
        S7Client client = new S7Client("192.168.0.1"); // 替换为你的PLC IP地址
        try
        {
            await client.ConnectAsync();
            Console.WriteLine("成功连接到PLC");

            byte[] data = await client.ReadDataAsync(1, 0, 4); // 从DB1的起始地址0读取4字节数据
            Console.WriteLine("读取的数据: " + BitConverter.ToString(data));

            client.Disconnect();
            Console.WriteLine("断开连接");
        }
        catch (Exception ex)
        {
            Console.WriteLine("发生错误: " + ex.Message);
        }
    }
}

4. 参数说明

  • IP地址 (_ip):PLC的IP地址。
  • 端口 (_port):PLC的通信端口,通常为102。
  • DB号 (dbNumber):数据块编号,例如DB1。
  • 起始地址 (startAddress):读取数据的起始地址。
  • 数据长度 (size):读取数据的长度(字节)。

5. 注意事项

  • 请确保PLC处于可连接状态,并且IP地址和端口配置正确。
  • 对于实际应用,需要处理通信过程中的异常,例如连接超时、网络问题等。
  • 这个示例实现了最基本的读数据功能,如果需要写数据或其他高级操作,需要进一步扩展代码。

这个示例提供了一个基本的框架,你可以根据需要进行修改和扩展。

;