基于C#实现基恩士PLC通信

一、通信协议选择

基恩士PLC支持多种通信协议,推荐优先采用以下两种方案:

  1. TCP/IP Socket通信(通用性强,适用于以太网接口设备)
  2. 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);
    }
}
posted @ 2025-09-18 14:06  chen_yig  阅读(225)  评论(0)    收藏  举报