基于C#实现基恩士PLC通信
一、通信协议选择
基恩士PLC支持多种通信协议,推荐优先采用以下两种方案:
- TCP/IP Socket通信(通用性强,适用于以太网接口设备)
- Modbus TCP协议(标准工业协议,兼容性强)
二、Socket通信实现(基于TCP/IP)
1. 基础通信类设计
using System.Net.Sockets;
using System.Text;
public class KeyencePLCClient {
private TcpClient _client;
private NetworkStream _stream;
private const int TimeoutMs = 5000;
public bool Connect(string ip, int port = 8500) {
try {
_client = new TcpClient();
_client.ReceiveTimeout = TimeoutMs;
_client.SendTimeout = TimeoutMs;
_client.Connect(ip, port);
_stream = _client.GetStream();
return true;
} catch (SocketException ex) {
LogError($"连接失败: {ex.Message}");
return false;
}
}
public byte[] SendCommand(byte[] command) {
try {
_stream.Write(command, 0, command.Length);
_stream.Flush();
byte[] buffer = new byte[1024];
int bytesRead = _stream.Read(buffer, 0, buffer.Length);
return buffer.Take(bytesRead).ToArray();
} catch (Exception ex) {
LogError($"通信异常: {ex.Message}");
return null;
}
}
public void Disconnect() {
_stream?.Close();
_client?.Close();
}
}
2. 数据帧封装与解析
// 数据包结构示例(需根据具体PLC协议调整)
public class PlcDataFrame {
public byte StartFlag { get; set; } = 0x02;
public ushort Address { get; set; }
public ushort Length { get; set; }
public byte[] Data { get; set; }
public byte Checksum { get; set; }
public byte EndFlag { get; set; } = 0x03;
// 计算LRC校验
public byte CalculateChecksum() {
byte sum = 0;
foreach (var b in Data) sum += b;
return (byte)(-sum & 0xFF);
}
// 打包数据帧
public byte[] ToByteArray() {
using (var ms = new MemoryStream()) {
ms.WriteByte(StartFlag);
ms.Write(BitConverter.GetBytes(Address), 0, 2);
ms.Write(BitConverter.GetBytes(Length), 0, 2);
ms.Write(Data, 0, Data.Length);
ms.WriteByte(Checksum);
ms.WriteByte(EndFlag);
return ms.ToArray();
}
}
}
三、Modbus TCP协议实现
1. 功能码定义
public enum ModbusFunctionCode : byte {
ReadCoils = 0x01,
ReadDiscreteInputs = 0x02,
ReadHoldingRegisters = 0x03,
WriteSingleRegister = 0x06
}
2. 读写寄存器实现
public class ModbusHandler {
private KeyencePLCClient _plcClient;
public ModbusHandler(KeyencePLCClient client) => _plcClient = client;
public ushort[] ReadHoldingRegisters(ushort startAddr, ushort count) {
var frame = new PlcDataFrame {
Address = startAddr,
Length = count,
Data = new byte[] { (byte)ModbusFunctionCode.ReadHoldingRegisters, 0x00 }
};
var response = _plcClient.SendCommand(frame.ToByteArray());
if (response == null || response.Length < 5)
throw new Exception("无效响应");
ushort[] data = new ushort[count];
for (int i = 0; i < count; i++) {
data[i] = (ushort)(response[4 + i * 2] << 8 | response[5 + i * 2]);
}
return data;
}
public void WriteSingleRegister(ushort addr, ushort value) {
byte[] data = { (byte)(value >> 8), (byte)value };
var frame = new PlcDataFrame {
Address = addr,
Data = new byte[] { (byte)ModbusFunctionCode.WriteSingleRegister, 0x00, data[0], data[1] }
};
_plcClient.SendCommand(frame.ToByteArray());
}
}
四、工业级应用优化策略
1. 异常处理机制
public class PlcExceptionHandler {
public static void Handle(SocketException ex) {
if (ex.SocketErrorCode == SocketError.ConnectionRefused) {
// 重连逻辑
Reconnect();
} else if (ex.SocketErrorCode == SocketError.Timeout) {
// 超时处理
LogWarning("通信超时,检查网络状态");
}
}
}
2. 数据缓存与同步
public class PlcDataCache {
private object _lock = new object();
private Dictionary<string, object> _cache = new Dictionary<string, object>();
public T GetData<T>(string key, Func<T> loadFunc) {
lock (_lock) {
if (!_cache.ContainsKey(key) || (DateTime.Now - _cache[key](@ref).LastUpdate).TotalSeconds > 5) {
_cache[key](@ref)= new CacheItem(loadFunc(), DateTime.Now);
}
return (T)_cache[key](@ref).Value;
}
}
private class CacheItem {
public object Value { get; }
public DateTime LastUpdate { get; }
public CacheItem(object value, DateTime time) {
Value = value;
LastUpdate = time;
}
}
}
五、WinForms界面集成
1. 实时数据监控界面
<!-- 数据监控面板 -->
<GroupBox Text="PLC实时数据">
<DataGridView x:Name="dgvData" AutoGenerateColumns="False">
<Columns>
<DataGridViewTextBoxColumn HeaderText="地址" DataPropertyName="Address"/>
<DataGridViewTextBoxColumn HeaderText="值" DataPropertyName="Value"/>
</Columns>
</DataGridView>
<Button Content="刷新" Click="RefreshData"/>
</GroupBox>
2. 数据绑定示例
private void RefreshData() {
var data = _plcClient.ReadHoldingRegisters(0x1000, 10);
dgvData.DataSource = data.Select((v, i) => new {
Address = 0x1000 + i,
Value = v.ToString("X4")
}).ToList();
}
参考代码 基恩士PLC 与C#通信(含c#和vb两个版本) www.youwenfan.com/contentcnh/49366.html
六、高级功能实现
1. 断线自动重连
public class AutoReconnectClient {
private KeyencePLCClient _client;
private string _ip;
private int _port;
private int _retryInterval = 5000;
public AutoReconnectClient(string ip, int port) {
_ip = ip;
_port = port;
Connect();
}
private void Connect() {
Task.Run(async () => {
while (true) {
try {
if (!_client.Connected) {
_client.Connect(_ip, _port);
LogInfo("重新连接成功");
}
await Task.Delay(1000);
} catch {
await Task.Delay(_retryInterval);
}
}
});
}
}
2. 数据加密传输
public class SecurePlcClient {
private Aes _aes = Aes.Create();
public byte[] Encrypt(byte[] data) {
using (var encryptor = _aes.CreateEncryptor()) {
return encryptor.TransformFinalBlock(data, 0, data.Length);
}
}
public byte[] Decrypt(byte[] data) {
using (var decryptor = _aes.CreateDecryptor()) {
return decryptor.TransformFinalBlock(data, 0, data.Length);
}
}
}
七、调试与维护工具
1. 通信日志记录
public static class Logger {
private static readonly string logPath = "plc_communication.log";
public static void WriteLog(string message) {
File.AppendAllText(logPath,
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - {message}{Environment.NewLine}");
}
}
2. 数据包抓取工具
public class PacketSniffer {
public static void Capture(byte[] rawData) {
File.WriteAllBytes($"packet_{DateTime.Now:yyyyMMddHHmmss}.bin", rawData);
}
}
浙公网安备 33010602011771号