工业自动化领域里,PLC作为可编程逻辑控制器,承担着设备逻辑控制的核心职能,而C#凭借完善的生态和高效的开发效率,成为很多工业软件开发者实现PLC通信与数据采集的首选语言。实际开发中,开发者需要根据PLC支持的通信协议选择合适的技术方案,保障数据传输的稳定性和实时性。

C#对接PLC的常用通信方式
目前工业场景中PLC支持的通信协议种类繁多,C#开发中常用的有以下几种:
- Modbus协议:分为Modbus RTU和Modbus TCP两类,是工业领域通用性最高的协议,多数主流PLC都支持该协议,开发门槛较低。
- OPC UA协议:面向工业4.0的标准通信协议,支持跨平台、加密传输,适合对数据安全性和兼容性要求较高的场景。
- 厂商私有协议:比如西门子S7协议、三菱MC协议,针对特定品牌PLC设计,通信效率更高,但需要依赖对应厂商的开发库。
Modbus TCP协议下的C#通信实现
Modbus TCP是基于TCP/IP的Modbus协议变种,默认端口为502,无需额外串口配置,适合局域网内的PLC通信场景。我们可以使用第三方库NModbus4快速实现通信功能,首先需要通过NuGet安装该库。
建立PLC连接
下面的代码实现了C#与Modbus TCP类型PLC的连接建立:
using System;
using System.Net.Sockets;
using Modbus.Device;
namespace PlcCommunicationDemo
{
class ModbusTcpClient
{
private TcpClient tcpClient;
private IModbusMaster modbusMaster;
// PLC的IP地址
private string plcIp = "192.168.0.1";
// Modbus TCP默认端口
private int port = 502;
/// <summary>
/// 连接PLC设备
/// </summary>
/// <returns>是否连接成功</returns>
public bool ConnectPlc()
{
try
{
tcpClient = new TcpClient();
// 建立TCP连接
tcpClient.Connect(plcIp, port);
// 创建Modbus TCP主站实例
modbusMaster = ModbusIpMaster.CreateIp(tcpClient);
// 设置超时时间
modbusMaster.Transport.ReadTimeout = 2000;
modbusMaster.Transport.WriteTimeout = 2000;
Console.WriteLine("PLC连接成功");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"PLC连接失败:{ex.Message}");
return false;
}
}
}
}
读取PLC数据
Modbus协议中,常用的数据区包括保持寄存器(功能码03)、输入寄存器(功能码04)、线圈(功能码01)、离散输入(功能码02)。下面的代码演示了读取保持寄存器的操作,假设PLC的从站地址为1,需要读取起始地址为0的10个寄存器数据:
/// <summary>
/// 读取PLC保持寄存器数据
/// </summary>
/// <param name="slaveId">从站地址</param>
/// <param name="startAddress">起始寄存器地址</param>
/// <param name="num">读取寄存器数量</param>
/// <returns>寄存器数据数组</returns>
public ushort[] ReadHoldRegisters(byte slaveId, ushort startAddress, ushort num)
{
if (modbusMaster == null)
{
Console.WriteLine("请先建立PLC连接");
return null;
}
try
{
// 调用Modbus读取保持寄存器方法
ushort[] result = modbusMaster.ReadHoldingRegisters(slaveId, startAddress, num);
Console.WriteLine($"读取到{num}个寄存器数据,起始地址:{startAddress}");
return result;
}
catch (Exception ex)
{
Console.WriteLine($"读取寄存器失败:{ex.Message}");
return null;
}
}
写入PLC数据
如果需要向PLC写入数据,比如修改某个寄存器的数值,可以使用下面的方法实现:
/// <summary>
/// 向PLC保持寄存器写入数据
/// </summary>
/// <param name="slaveId">从站地址</param>
/// <param name="startAddress">起始寄存器地址</param>
/// <param name="values">要写入的数据数组</param>
/// <returns>是否写入成功</returns>
public bool WriteHoldRegisters(byte slaveId, ushort startAddress, ushort[] values)
{
if (modbusMaster == null)
{
Console.WriteLine("请先建立PLC连接");
return false;
}
try
{
// 调用Modbus写入保持寄存器方法
modbusMaster.WriteMultipleRegisters(slaveId, startAddress, values);
Console.WriteLine($"成功向起始地址{startAddress}写入{values.Length}个寄存器数据");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"写入寄存器失败:{ex.Message}");
return false;
}
}
数据采集实战流程
实际工业数据采集场景中,通常需要按照固定周期读取PLC数据,并将数据存储到数据库或者推送到前端展示。整体流程可以分为以下几步:
- 初始化PLC连接,检测连接状态,连接失败则进行重试。
- 根据业务需求定义采集点位,比如温度、压力、设备运行状态对应的寄存器地址。
- 使用定时器按照设定周期(比如1秒)调用读取方法获取对应点位的数据。
- 对采集到的数据进行解析、校验,过滤异常数据。
- 将有效数据存储到时序数据库,或者推送到消息队列供其他系统消费。
注意事项
- 通信过程中需要做好异常处理,比如网络中断、PLC断电等场景的重连逻辑,避免程序崩溃。
- 寄存器地址的映射需要参考PLC厂商提供的地址表,不同品牌的PLC地址定义规则存在差异。
- 高频采集场景下需要合理设置采集周期,避免占用过多PLC资源,影响设备正常运行。
- 如果涉及多设备同时采集,建议使用异步编程方式,提升程序运行效率。