下面是一个基于.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地址和端口配置正确。
- 对于实际应用,需要处理通信过程中的异常,例如连接超时、网络问题等。
- 这个示例实现了最基本的读数据功能,如果需要写数据或其他高级操作,需要进一步扩展代码。
这个示例提供了一个基本的框架,你可以根据需要进行修改和扩展。