C#通过TCP_IP与PLC通信
C#通过TCP/IP与PLC通信
本文将全面介绍如何使用C#通过TCP/IP协议与各种PLC进行通信,包括西门子、罗克韦尔、三菱等主流品牌PLC的连接方法。
一、PLC通信基础
PLC通信协议概览
协议类型 | 适用品牌 | 特点 |
---|---|---|
Modbus TCP | 通用协议 | 简单易用,广泛支持 |
Siemens S7 | 西门子PLC | 高效专用协议 |
EtherNet/IP | 罗克韦尔PLC | CIP协议实现 |
MC Protocol | 三菱PLC | 三菱专用协议 |
FINS/TCP | 欧姆龙PLC | 欧姆龙专用协议 |
通信架构
C#应用程序 (TCP/IP客户端)↓
以太网交换机↓
PLC (TCP/IP服务器)
二、Modbus TCP通信实现
使用NModbus库
// 安装NuGet包:Install-Package NModbus
using Modbus.Device;
using System.Net.Sockets;public class ModbusClient
{private TcpClient _tcpClient;private ModbusIpMaster _master;public void Connect(string ipAddress, int port = 502){_tcpClient = new TcpClient(ipAddress, port);_master = ModbusIpMaster.CreateIp(_tcpClient);}// 读取保持寄存器public ushort[] ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort numRegisters){return _master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);}// 写入单个寄存器public void WriteSingleRegister(byte slaveId, ushort registerAddress, ushort value){_master.WriteSingleRegister(slaveId, registerAddress, value);}// 批量写入寄存器public void WriteMultipleRegisters(byte slaveId, ushort startAddress, ushort[] values){_master.WriteMultipleRegisters(slaveId, startAddress, values);}public void Disconnect(){_tcpClient?.Close();}
}
使用示例
var client = new ModbusClient();
try
{client.Connect("192.168.1.10");// 读取10个保持寄存器(起始地址0)ushort[] values = client.ReadHoldingRegisters(1, 0, 10);// 写入单个寄存器(地址5,值1234)client.WriteSingleRegister(1, 5, 1234);// 批量写入(地址10开始的3个寄存器)client.WriteMultipleRegisters(1, 10, new ushort[] { 100, 200, 300 });
}
finally
{client.Disconnect();
}
三、西门子S7通信实现
使用S7NetPlus库
// 安装NuGet包:Install-Package S7NetPlus
using S7.Net;public class S7Client
{private Plc _plc;public void Connect(CpuType cpuType, string ipAddress, short rack = 0, short slot = 1){_plc = new Plc(cpuType, ipAddress, rack, slot);_plc.Open();}// 读取DB块数据public object ReadDataBlock(int dbNumber, int startByte, VarType varType, int count = 1){return _plc.Read(DataType.DataBlock, dbNumber, startByte, varType, count);}// 写入DB块数据public void WriteDataBlock(int dbNumber, int startByte, object value){_plc.Write(DataType.DataBlock, dbNumber, startByte, value);}// 读取输入/输出点public bool ReadDigital(DataType dataType, int dbNumber, int byteOffset, int bitOffset){return (bool)_plc.Read(dataType, dbNumber, byteOffset, VarType.Bit, 1, bitOffset);}// 写入输入/输出点public void WriteDigital(DataType dataType, int dbNumber, int byteOffset, int bitOffset, bool value){_plc.WriteBit(dataType, dbNumber, byteOffset, bitOffset, value);}public void Disconnect(){_plc?.Close();}
}
使用示例
var s7Client = new S7Client();
try
{s7Client.Connect(CpuType.S71500, "192.168.1.20");// 读取DB10.DBD0 (REAL类型)float temperature = (float)s7Client.ReadDataBlock(10, 0, VarType.Real);// 读取DB10.DBX1.0 (BOOL类型)bool status = s7Client.ReadDigital(DataType.DataBlock, 10, 1, 0);// 写入DB10.DBW10 (INT类型)s7Client.WriteDataBlock(10, 10, (short)100);// 设置输出点Q0.0s7Client.WriteDigital(DataType.Output, 0, 0, 0, true);
}
finally
{s7Client.Disconnect();
}
四、罗克韦尔EtherNet/IP通信
使用libplctag库
// 安装NuGet包:Install-Package libplctag
using libplctag;
using libplctag.DataTypes;public class AllenBradleyClient
{private readonly Tag<Tag> _plcTag = new Tag<Tag>(){Gateway = "192.168.1.30",Path = "1,0",PlcType = PlcType.ControlLogix,Protocol = Protocol.ab_eip,Name = "MyTag"};public void Connect(){_plcTag.Initialize();}public T ReadTag<T>(string tagName){_plcTag.Name = tagName;_plcTag.Read();return (T)Convert.ChangeType(_plcTag.Value, typeof(T));}public void WriteTag<T>(string tagName, T value){_plcTag.Name = tagName;_plcTag.Value = value;_plcTag.Write();}public void Disconnect(){_plcTag?.Dispose();}
}
使用示例
var abClient = new AllenBradleyClient();
try
{abClient.Connect();// 读取BOOL标签bool runStatus = abClient.ReadTag<bool>("Program:MainProgram.RunStatus");// 读取REAL标签float pressure = abClient.ReadTag<float>("PressureSensor");// 写入DINT标签abClient.WriteTag("ProductionCount", 1500);// 写入BOOL标签abClient.WriteTag("MachineStart", true);
}
finally
{abClient.Disconnect();
}
五、高级通信技巧
1. 异步通信实现
public async Task<ushort[]> ReadRegistersAsync(byte slaveId, ushort startAddress, ushort numRegisters)
{return await Task.Run(() => _master.ReadHoldingRegisters(slaveId, startAddress, numRegisters));
}
2. 数据转换工具类
public static class PLCDataConverter
{// 字节数组转浮点数public static float ToFloat(byte[] bytes, bool bigEndian = true){if (bigEndian) Array.Reverse(bytes);return BitConverter.ToSingle(bytes, 0);}// 浮点数转字节数组public static byte[] FromFloat(float value, bool bigEndian = true){byte[] bytes = BitConverter.GetBytes(value);if (bigEndian) Array.Reverse(bytes);return bytes;}// 字节数组转整数public static int ToInt32(byte[] bytes, bool bigEndian = true){if (bigEndian) Array.Reverse(bytes);return BitConverter.ToInt32(bytes, 0);}
}
3. 连接管理与重连机制
public class PLCConnectionManager
{private Timer _reconnectTimer;private bool _isConnected;public event EventHandler ConnectionRestored;public event EventHandler ConnectionLost;public PLCConnectionManager(){_reconnectTimer = new Timer(5000);_reconnectTimer.Elapsed += ReconnectTimer_Elapsed;}public void StartMonitoring(){_reconnectTimer.Start();}private void ReconnectTimer_Elapsed(object sender, ElapsedEventArgs e){try{// 尝试Ping PLCif (!CheckConnection()){_isConnected = false;ConnectionLost?.Invoke(this, EventArgs.Empty);AttemptReconnect();}else if (!_isConnected){_isConnected = true;ConnectionRestored?.Invoke(this, EventArgs.Empty);}}catch { /* 处理异常 */ }}private void AttemptReconnect(){// 实现重连逻辑// 1. 尝试重新连接// 2. 指数退避策略// 3. 最大重试次数限制}
}
参考项目 C#通过TCP/IP和PLC通讯 youwenfan.com/contentcsc/93115.html
六、调试与故障排除
常见问题解决方案
-
连接超时
- 检查物理连接和网络配置
- 确认PLC IP地址和端口正确
- 关闭防火墙或添加例外规则
-
数据不一致
- 确认字节顺序(大端/小端)
- 检查PLC数据格式(INT/DINT/REAL)
- 验证寄存器地址偏移量
-
通信不稳定
- 使用心跳包维持连接
- 实现自动重连机制
- 增加通信超时时间
调试工具推荐
- Wireshark - 网络协议分析
- Modbus Poll - Modbus通信测试
- S7-PLCSIM - 西门子PLC仿真
- RSLinx Classic - 罗克韦尔通信网关
七、安全注意事项
-
网络安全
// 使用加密通信(如TLS) var tcpClient = new TcpClient(); var sslStream = new SslStream(tcpClient.GetStream()); sslStream.AuthenticateAsClient("plc-hostname");
-
身份验证
// PLC端设置用户名/密码 _plc = new Plc(CpuType.S71200, "192.168.1.10", 0, 1) {User = "admin",Password = "securePassword" };
-
访问控制
- 限制PLC网络访问(IP白名单)
- 使用VPN进行远程访问
- 定期更新PLC固件和安全补丁
八、性能优化建议
-
批量读取
// 一次性读取多个寄存器 ushort[] batchData = _master.ReadHoldingRegisters(1, 0, 100);
-
缓存机制
private Dictionary<string, object> _tagCache = new();public T GetCachedValue<T>(string tagName) {if (!_tagCache.ContainsKey(tagName) || _cacheAge[tagName] < DateTime.Now.AddSeconds(-5)){_tagCache[tagName] = ReadTag<T>(tagName);_cacheAge[tagName] = DateTime.Now;}return (T)_tagCache[tagName]; }
-
连接池管理
public class PLCConnectionPool {private ConcurrentBag<TcpClient> _connections = new();public TcpClient GetConnection(){if (_connections.TryTake(out var connection))return connection;return CreateNewConnection();}public void ReturnConnection(TcpClient connection){_connections.Add(connection);} }
通过本文介绍的方法,您可以根据不同PLC品牌选择合适的通信协议和库,实现稳定高效的工业控制系统集成。