欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

.NET 8實(shí)現(xiàn)modbus通訊工具類(lèi)封裝的操作方法

 更新時(shí)間:2025年08月25日 10:55:59   作者:code_shenbing  
Modbus 協(xié)議是工業(yè)自動(dòng)化領(lǐng)域應(yīng)用最廣泛的通信協(xié)議之一,廣泛應(yīng)用于 PLC、傳感器、儀表等設(shè)備之間的數(shù)據(jù)交換,在 .NET 8 中實(shí)現(xiàn) Modbus 通訊工具類(lèi)可以大大簡(jiǎn)化工業(yè)控制系統(tǒng)的開(kāi)發(fā)工作,本文將詳細(xì)介紹如何封裝一個(gè)功能完整的 Modbus 工具類(lèi),需要的朋友可以參考下

引言

Modbus 協(xié)議是工業(yè)自動(dòng)化領(lǐng)域應(yīng)用最廣泛的通信協(xié)議之一,廣泛應(yīng)用于 PLC、傳感器、儀表等設(shè)備之間的數(shù)據(jù)交換。在 .NET 8 中實(shí)現(xiàn) Modbus 通訊工具類(lèi)可以大大簡(jiǎn)化工業(yè)控制系統(tǒng)的開(kāi)發(fā)工作。本文將詳細(xì)介紹如何封裝一個(gè)功能完整的 Modbus 工具類(lèi),支持 RTU 和 TCP 兩種傳輸模式。

1. Modbus 協(xié)議基礎(chǔ)

Modbus 協(xié)議主要有兩種傳輸模式:

  • ??Modbus RTU??:基于串行通信(RS232/RS485),使用二進(jìn)制數(shù)據(jù)格式
  • ??Modbus TCP??:基于以太網(wǎng)通信,使用 TCP/IP 協(xié)議棧

1.1 Modbus 功能碼

常用功能碼:

  • 0x01:讀線圈狀態(tài)
  • 0x02:讀輸入狀態(tài)
  • 0x03:讀保持寄存器
  • 0x04:讀輸入寄存器
  • 0x05:寫(xiě)單個(gè)線圈
  • 0x06:寫(xiě)單個(gè)寄存器
  • 0x0F:寫(xiě)多個(gè)線圈
  • 0x10:寫(xiě)多個(gè)寄存器

2. Modbus 工具類(lèi)設(shè)計(jì)

2.1 基礎(chǔ)接口和枚舉

public enum ModbusType
{
    RTU,
    TCP
}
 
public enum ModbusFunctionCode : byte
{
    ReadCoils = 0x01,
    ReadDiscreteInputs = 0x02,
    ReadHoldingRegisters = 0x03,
    ReadInputRegisters = 0x04,
    WriteSingleCoil = 0x05,
    WriteSingleRegister = 0x06,
    WriteMultipleCoils = 0x0F,
    WriteMultipleRegisters = 0x10
}
 
public interface IModbusClient : IDisposable
{
    Task<bool> ConnectAsync();
    void Disconnect();
    bool IsConnected { get; }
    
    // 讀取操作
    Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort numberOfCoils);
    Task<bool[]> ReadDiscreteInputsAsync(byte slaveId, ushort startAddress, ushort numberOfInputs);
    Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfRegisters);
    Task<ushort[]> ReadInputRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfRegisters);
    
    // 寫(xiě)入操作
    Task<bool> WriteSingleCoilAsync(byte slaveId, ushort coilAddress, bool value);
    Task<bool> WriteSingleRegisterAsync(byte slaveId, ushort registerAddress, ushort value);
    Task<bool> WriteMultipleCoilsAsync(byte slaveId, ushort startAddress, bool[] values);
    Task<bool> WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] values);
}

2.2 Modbus 異常處理

public class ModbusException : Exception
{
    public byte ExceptionCode { get; }
 
    public ModbusException(byte exceptionCode, string message) 
        : base(message)
    {
        ExceptionCode = exceptionCode;
    }
}
 
public static class ModbusErrorCodes
{
    public static readonly Dictionary<byte, string> Errors = new Dictionary<byte, string>
    {
        { 0x01, "非法功能碼" },
        { 0x02, "非法數(shù)據(jù)地址" },
        { 0x03, "非法數(shù)據(jù)值" },
        { 0x04, "從站設(shè)備故障" },
        { 0x05, "確認(rèn)" },
        { 0x06, "從屬設(shè)備忙" },
        { 0x07, "存儲(chǔ)奇偶性錯(cuò)誤" },
        { 0x08, "不可用網(wǎng)關(guān)路徑" },
        { 0x09, "網(wǎng)關(guān)目標(biāo)設(shè)備響應(yīng)失敗" },
        { 0x0A, "網(wǎng)關(guān)目標(biāo)設(shè)備響應(yīng)失敗" }
    };
}

3. Modbus RTU 實(shí)現(xiàn)

3.1 ModbusRtuClient 類(lèi)

using System.IO.Ports;
using System.Threading.Tasks;
 
public class ModbusRtuClient : IModbusClient
{
    private readonly SerialPort _serialPort;
    private readonly int _responseTimeout;
    private readonly byte _defaultSlaveId;
    
    public bool IsConnected => _serialPort?.IsOpen ?? false;
    
    public ModbusRtuClient(string portName, int baudRate = 9600, Parity parity = Parity.None, 
                          int dataBits = 8, StopBits stopBits = StopBits.One, 
                          int responseTimeout = 1000, byte defaultSlaveId = 1)
    {
        _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits)
        {
            ReadTimeout = responseTimeout,
            WriteTimeout = responseTimeout
        };
        _responseTimeout = responseTimeout;
        _defaultSlaveId = defaultSlaveId;
    }
    
    public async Task<bool> ConnectAsync()
    {
        if (IsConnected) return true;
        
        try
        {
            _serialPort.Open();
            return true;
        }
        catch
        {
            return false;
        }
    }
    
    public void Disconnect()
    {
        if (IsConnected)
        {
            _serialPort.Close();
        }
    }
    
    public void Dispose()
    {
        Disconnect();
        _serialPort.Dispose();
    }
    
    // 核心通信方法
    private async Task<byte[]> SendReceiveAsync(byte slaveId, byte[] request)
    {
        if (!IsConnected) throw new InvalidOperationException("串口未連接");
        
        // 添加CRC校驗(yàn)
        byte[] frame = new byte[request.Length + 2];
        Array.Copy(request, frame, request.Length);
        ushort crc = CalculateCrc(request);
        frame[request.Length] = (byte)(crc & 0xFF);
        frame[request.Length + 1] = (byte)((crc >> 8) & 0xFF);
        
        // 發(fā)送請(qǐng)求
        _serialPort.DiscardInBuffer();
        _serialPort.Write(frame, 0, frame.Length);
        
        // 接收響應(yīng)
        int expectedLength = GetExpectedResponseLength(request[1]);
        byte[] response = new byte[expectedLength + 2]; // +2 for CRC
        
        int bytesRead = 0;
        int totalBytesToRead = response.Length;
        DateTime startTime = DateTime.Now;
        
        while (bytesRead < totalBytesToRead)
        {
            if ((DateTime.Now - startTime).TotalMilliseconds > _responseTimeout)
            {
                throw new TimeoutException("讀取響應(yīng)超時(shí)");
            }
            
            if (_serialPort.BytesToRead > 0)
            {
                bytesRead += _serialPort.Read(response, bytesRead, totalBytesToRead - bytesRead);
            }
            else
            {
                await Task.Delay(10);
            }
        }
        
        // 驗(yàn)證CRC
        ushort receivedCrc = (ushort)(response[response.Length - 2] | (response[response.Length - 1] << 8));
        ushort calculatedCrc = CalculateCrc(response, 0, response.Length - 2);
        
        if (receivedCrc != calculatedCrc)
        {
            throw new ModbusException(0, "CRC校驗(yàn)失敗");
        }
        
        // 檢查異常響應(yīng)
        if ((response[1] & 0x80) != 0)
        {
            byte exceptionCode = response[2];
            string errorMessage = ModbusErrorCodes.Errors.TryGetValue(exceptionCode, out string msg) 
                ? msg : $"未知異常代碼: 0x{exceptionCode:X2}";
            throw new ModbusException(exceptionCode, errorMessage);
        }
        
        return response;
    }
    
    private ushort CalculateCrc(byte[] data, int offset = 0, int length = -1)
    {
        if (length == -1) length = data.Length - offset;
        
        ushort crc = 0xFFFF;
        
        for (int i = offset; i < offset + length; i++)
        {
            crc ^= data[i];
            for (int j = 0; j < 8; j++)
            {
                if ((crc & 0x0001) != 0)
                {
                    crc >>= 1;
                    crc ^= 0xA001;
                }
                else
                {
                    crc >>= 1;
                }
            }
        }
        
        return crc;
    }
    
    private int GetExpectedResponseLength(byte functionCode)
    {
        switch (functionCode)
        {
            case (byte)ModbusFunctionCode.ReadCoils:
            case (byte)ModbusFunctionCode.ReadDiscreteInputs:
                return 3; // SlaveId + FC + ByteCount + (Data bytes) + CRC
            case (byte)ModbusFunctionCode.ReadHoldingRegisters:
            case (byte)ModbusFunctionCode.ReadInputRegisters:
                return 3; // SlaveId + FC + ByteCount + (Data bytes) + CRC
            case (byte)ModbusFunctionCode.WriteSingleCoil:
            case (byte)ModbusFunctionCode.WriteSingleRegister:
                return 6; // SlaveId + FC + Address + Value + CRC
            case (byte)ModbusFunctionCode.WriteMultipleCoils:
            case (byte)ModbusFunctionCode.WriteMultipleRegisters:
                return 6; // SlaveId + FC + Address + Quantity + CRC
            default:
                throw new ArgumentException($"未知功能碼: 0x{functionCode:X2}");
        }
    }
    
    // 具體功能實(shí)現(xiàn)將在下面展開(kāi)...
}

3.2 Modbus RTU 功能實(shí)現(xiàn)

// 在 ModbusRtuClient 類(lèi)中添加以下方法
 
public async Task<bool[]> ReadCoilsAsync(byte slaveId, ushort startAddress, ushort numberOfCoils)
{
    if (numberOfCoils < 1 || numberOfCoils > 2000)
        throw new ArgumentException("線圈數(shù)量必須在1-2000之間");
    
    byte[] request = new byte[6];
    request[0] = slaveId;
    request[1] = (byte)ModbusFunctionCode.ReadCoils;
    request[2] = (byte)(startAddress >> 8);
    request[3] = (byte)(startAddress & 0xFF);
    request[4] = (byte)(numberOfCoils >> 8);
    request[5] = (byte)(numberOfCoils & 0xFF);
    
    byte[] response = await SendReceiveAsync(slaveId, request);
    
    int byteCount = response[2];
    bool[] coils = new bool[numberOfCoils];
    
    for (int i = 0; i < numberOfCoils; i++)
    {
        int byteIndex = 3 + i / 8;
        int bitIndex = i % 8;
        coils[i] = (response[byteIndex] & (1 << bitIndex)) != 0;
    }
    
    return coils;
}
 
public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfRegisters)
{
    if (numberOfRegisters < 1 || numberOfRegisters > 125)
        throw new ArgumentException("寄存器數(shù)量必須在1-125之間");
    
    byte[] request = new byte[6];
    request[0] = slaveId;
    request[1] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
    request[2] = (byte)(startAddress >> 8);
    request[3] = (byte)(startAddress & 0xFF);
    request[4] = (byte)(numberOfRegisters >> 8);
    request[5] = (byte)(numberOfRegisters & 0xFF);
    
    byte[] response = await SendReceiveAsync(slaveId, request);
    
    int byteCount = response[2];
    ushort[] registers = new ushort[numberOfRegisters];
    
    for (int i = 0; i < numberOfRegisters; i++)
    {
        int offset = 3 + i * 2;
        registers[i] = (ushort)((response[offset] << 8) | response[offset + 1]);
    }
    
    return registers;
}
 
public async Task<bool> WriteSingleCoilAsync(byte slaveId, ushort coilAddress, bool value)
{
    byte[] request = new byte[6];
    request[0] = slaveId;
    request[1] = (byte)ModbusFunctionCode.WriteSingleCoil;
    request[2] = (byte)(coilAddress >> 8);
    request[3] = (byte)(coilAddress & 0xFF);
    request[4] = value ? (byte)0xFF : (byte)0x00;
    request[5] = 0x00;
    
    byte[] response = await SendReceiveAsync(slaveId, request);
    
    // 驗(yàn)證響應(yīng)
    if (response.Length != 6) return false;
    if (response[2] != request[2] || response[3] != request[3]) return false;
    if (response[4] != request[4] || response[5] != request[5]) return false;
    
    return true;
}
 
public async Task<bool> WriteMultipleRegistersAsync(byte slaveId, ushort startAddress, ushort[] values)
{
    if (values == null || values.Length == 0 || values.Length > 123)
        throw new ArgumentException("寄存器數(shù)量必須在1-123之間");
    
    byte[] request = new byte[7 + values.Length * 2];
    request[0] = slaveId;
    request[1] = (byte)ModbusFunctionCode.WriteMultipleRegisters;
    request[2] = (byte)(startAddress >> 8);
    request[3] = (byte)(startAddress & 0xFF);
    request[4] = (byte)(values.Length >> 8);
    request[5] = (byte)(values.Length & 0xFF);
    request[6] = (byte)(values.Length * 2);
    
    for (int i = 0; i < values.Length; i++)
    {
        request[7 + i * 2] = (byte)(values[i] >> 8);
        request[8 + i * 2] = (byte)(values[i] & 0xFF);
    }
    
    byte[] response = await SendReceiveAsync(slaveId, request);
    
    // 驗(yàn)證響應(yīng)
    if (response.Length != 6) return false;
    if (response[2] != request[2] || response[3] != request[3]) return false;
    if (response[4] != request[4] || response[5] != request[5]) return false;
    
    return true;
}
 
// 其他功能實(shí)現(xiàn)類(lèi)似...

4. Modbus TCP 實(shí)現(xiàn)

4.1 ModbusTcpClient 類(lèi)

using System.Net.Sockets;
using System.Threading.Tasks;
 
public class ModbusTcpClient : IModbusClient
{
    private TcpClient _tcpClient;
    private NetworkStream _networkStream;
    private readonly string _host;
    private readonly int _port;
    private readonly int _responseTimeout;
    private readonly byte _defaultSlaveId;
    private ushort _transactionId = 0;
    
    public bool IsConnected => _tcpClient?.Connected ?? false;
    
    public ModbusTcpClient(string host, int port = 502, int responseTimeout = 1000, byte defaultSlaveId = 1)
    {
        _host = host;
        _port = port;
        _responseTimeout = responseTimeout;
        _defaultSlaveId = defaultSlaveId;
    }
    
    public async Task<bool> ConnectAsync()
    {
        if (IsConnected) return true;
        
        try
        {
            _tcpClient = new TcpClient();
            await _tcpClient.ConnectAsync(_host, _port);
            _networkStream = _tcpClient.GetStream();
            _networkStream.ReadTimeout = _responseTimeout;
            return true;
        }
        catch
        {
            return false;
        }
    }
    
    public void Disconnect()
    {
        if (IsConnected)
        {
            _networkStream?.Close();
            _tcpClient?.Close();
            _tcpClient = null;
            _networkStream = null;
        }
    }
    
    public void Dispose()
    {
        Disconnect();
        _tcpClient?.Dispose();
        _networkStream?.Dispose();
    }
    
    // 核心通信方法
    private async Task<byte[]> SendReceiveAsync(byte slaveId, byte[] pdu)
    {
        if (!IsConnected) throw new InvalidOperationException("TCP連接未建立");
        
        // 構(gòu)建MBAP頭
        byte[] mbapHeader = new byte[7];
        ushort transactionId = _transactionId++;
        mbapHeader[0] = (byte)(transactionId >> 8);
        mbapHeader[1] = (byte)(transactionId & 0xFF);
        mbapHeader[2] = 0x00; // 協(xié)議標(biāo)識(shí)符高位
        mbapHeader[3] = 0x00; // 協(xié)議標(biāo)識(shí)符低位
        mbapHeader[4] = (byte)((pdu.Length + 1) >> 8); // 長(zhǎng)度高位
        mbapHeader[5] = (byte)((pdu.Length + 1) & 0xFF); // 長(zhǎng)度低位
        mbapHeader[6] = slaveId; // 單元標(biāo)識(shí)符
        
        // 構(gòu)建完整請(qǐng)求
        byte[] request = new byte[mbapHeader.Length + pdu.Length];
        Array.Copy(mbapHeader, request, mbapHeader.Length);
        Array.Copy(pdu, 0, request, mbapHeader.Length, pdu.Length);
        
        // 發(fā)送請(qǐng)求
        await _networkStream.WriteAsync(request, 0, request.Length);
        
        // 接收MBAP頭
        byte[] mbapResponse = new byte[7];
        int bytesRead = await _networkStream.ReadAsync(mbapResponse, 0, mbapResponse.Length);
        
        if (bytesRead != mbapResponse.Length)
        {
            throw new IOException("讀取MBAP頭不完整");
        }
        
        // 驗(yàn)證事務(wù)ID
        ushort responseTransactionId = (ushort)((mbapResponse[0] << 8) | mbapResponse[1]);
        if (responseTransactionId != transactionId)
        {
            throw new ModbusException(0, "事務(wù)ID不匹配");
        }
        
        // 獲取PDU長(zhǎng)度
        int pduLength = (mbapResponse[4] << 8) | mbapResponse[5];
        
        // 接收PDU
        byte[] pduResponse = new byte[pduLength - 1]; // 減去單元標(biāo)識(shí)符
        bytesRead = await _networkStream.ReadAsync(pduResponse, 0, pduResponse.Length);
        
        if (bytesRead != pduResponse.Length)
        {
            throw new IOException("讀取PDU不完整");
        }
        
        // 檢查異常響應(yīng)
        if ((pduResponse[0] & 0x80) != 0)
        {
            byte exceptionCode = pduResponse[1];
            string errorMessage = ModbusErrorCodes.Errors.TryGetValue(exceptionCode, out string msg) 
                ? msg : $"未知異常代碼: 0x{exceptionCode:X2}";
            throw new ModbusException(exceptionCode, errorMessage);
        }
        
        return pduResponse;
    }
    
    // 具體功能實(shí)現(xiàn)與RTU類(lèi)似,只是使用PDU而不是完整幀
    public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfRegisters)
    {
        if (numberOfRegisters < 1 || numberOfRegisters > 125)
            throw new ArgumentException("寄存器數(shù)量必須在1-125之間");
        
        byte[] pdu = new byte[5];
        pdu[0] = (byte)ModbusFunctionCode.ReadHoldingRegisters;
        pdu[1] = (byte)(startAddress >> 8);
        pdu[2] = (byte)(startAddress & 0xFF);
        pdu[3] = (byte)(numberOfRegisters >> 8);
        pdu[4] = (byte)(numberOfRegisters & 0xFF);
        
        byte[] response = await SendReceiveAsync(slaveId, pdu);
        
        int byteCount = response[1];
        ushort[] registers = new ushort[numberOfRegisters];
        
        for (int i = 0; i < numberOfRegisters; i++)
        {
            int offset = 2 + i * 2;
            registers[i] = (ushort)((response[offset] << 8) | response[offset + 1]);
        }
        
        return registers;
    }
    
    // 其他功能實(shí)現(xiàn)類(lèi)似...
}

5. 工廠模式創(chuàng)建 Modbus 客戶(hù)端

public static class ModbusClientFactory
{
    public static IModbusClient CreateClient(ModbusType type, string connectionString, byte defaultSlaveId = 1)
    {
        switch (type)
        {
            case ModbusType.RTU:
                // 連接字符串格式: COM1,9600,None,8,One
                string[] rtuParams = connectionString.Split(',');
                if (rtuParams.Length < 1) throw new ArgumentException("無(wú)效的RTU連接字符串");
                
                string portName = rtuParams[0];
                int baudRate = rtuParams.Length > 1 ? int.Parse(rtuParams[1]) : 9600;
                Parity parity = rtuParams.Length > 2 ? (Parity)Enum.Parse(typeof(Parity), rtuParams[2]) : Parity.None;
                int dataBits = rtuParams.Length > 3 ? int.Parse(rtuParams[3]) : 8;
                StopBits stopBits = rtuParams.Length > 4 ? (StopBits)Enum.Parse(typeof(StopBits), rtuParams[4]) : StopBits.One;
                
                return new ModbusRtuClient(portName, baudRate, parity, dataBits, stopBits, defaultSlaveId: defaultSlaveId);
                
            case ModbusType.TCP:
                // 連接字符串格式: 192.168.1.100:502
                string[] tcpParams = connectionString.Split(':');
                if (tcpParams.Length < 1) throw new ArgumentException("無(wú)效的TCP連接字符串");
                
                string host = tcpParams[0];
                int port = tcpParams.Length > 1 ? int.Parse(tcpParams[1]) : 502;
                
                return new ModbusTcpClient(host, port, defaultSlaveId: defaultSlaveId);
                
            default:
                throw new ArgumentException("不支持的Modbus類(lèi)型");
        }
    }
}

6. 高級(jí)功能擴(kuò)展

6.1 批量讀取優(yōu)化

public static class ModbusExtensions
{
    public static async Task<Dictionary<ushort, ushort>> ReadHoldingRegistersRangeAsync(
        this IModbusClient client, byte slaveId, 
        ushort startAddress, ushort endAddress, int maxPerRequest = 100)
    {
        var results = new Dictionary<ushort, ushort>();
        ushort current = startAddress;
        
        while (current <= endAddress)
        {
            ushort count = (ushort)Math.Min(maxPerRequest, endAddress - current + 1);
            ushort[] values = await client.ReadHoldingRegistersAsync(slaveId, current, count);
            
            for (int i = 0; i < values.Length; i++)
            {
                results[(ushort)(current + i)] = values[i];
            }
            
            current += count;
        }
        
        return results;
    }
}

6.2 自動(dòng)重連機(jī)制

public class AutoReconnectModbusClient : IModbusClient
{
    private readonly IModbusClient _innerClient;
    private readonly int _maxRetries;
    private readonly int _retryDelay;
    
    public bool IsConnected => _innerClient.IsConnected;
    
    public AutoReconnectModbusClient(IModbusClient innerClient, int maxRetries = 3, int retryDelay = 1000)
    {
        _innerClient = innerClient;
        _maxRetries = maxRetries;
        _retryDelay = retryDelay;
    }
    
    public async Task<bool> ConnectAsync()
    {
        for (int i = 0; i < _maxRetries; i++)
        {
            if (await _innerClient.ConnectAsync()) 
                return true;
            
            await Task.Delay(_retryDelay);
        }
        return false;
    }
    
    public void Disconnect() => _innerClient.Disconnect();
    
    public void Dispose() => _innerClient.Dispose();
    
    private async Task<T> ExecuteWithRetry<T>(Func<Task<T>> operation)
    {
        for (int i = 0; i < _maxRetries; i++)
        {
            try
            {
                if (!_innerClient.IsConnected)
                {
                    await ConnectAsync();
                }
                
                return await operation();
            }
            catch (IOException) when (i < _maxRetries - 1)
            {
                // 連接錯(cuò)誤,嘗試重連
                Disconnect();
                await Task.Delay(_retryDelay);
            }
            catch (TimeoutException) when (i < _maxRetries - 1)
            {
                // 超時(shí)錯(cuò)誤,嘗試重試
                await Task.Delay(_retryDelay);
            }
        }
        
        // 最后一次嘗試
        return await operation();
    }
    
    public async Task<ushort[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort numberOfRegisters)
    {
        return await ExecuteWithRetry(() => 
            _innerClient.ReadHoldingRegistersAsync(slaveId, startAddress, numberOfRegisters));
    }
    
    // 其他方法實(shí)現(xiàn)類(lèi)似...
}

7. 使用示例

class Program
{
    static async Task Main(string[] args)
    {
        // 創(chuàng)建Modbus TCP客戶(hù)端
        var tcpClient = ModbusClientFactory.CreateClient(
            ModbusType.TCP, "192.168.1.100:502", defaultSlaveId: 1);
        
        // 創(chuàng)建帶自動(dòng)重連的客戶(hù)端
        var autoReconnectClient = new AutoReconnectModbusClient(tcpClient);
        
        try
        {
            // 連接設(shè)備
            if (await autoReconnectClient.ConnectAsync())
            {
                Console.WriteLine("Modbus連接成功");
                
                // 讀取保持寄存器
                ushort[] registers = await autoReconnectClient.ReadHoldingRegistersAsync(1, 0, 10);
                Console.WriteLine("寄存器值: " + string.Join(", ", registers));
                
                // 寫(xiě)入單個(gè)寄存器
                bool writeResult = await autoReconnectClient.WriteSingleRegisterAsync(1, 5, 1234);
                Console.WriteLine($"寫(xiě)入寄存器結(jié)果: {writeResult}");
                
                // 批量讀取寄存器范圍
                var registerMap = await autoReconnectClient.ReadHoldingRegistersRangeAsync(1, 0, 100);
                foreach (var kvp in registerMap)
                {
                    Console.WriteLine($"地址 {kvp.Key}: {kvp.Value}");
                }
            }
            else
            {
                Console.WriteLine("Modbus連接失敗");
            }
        }
        catch (ModbusException ex)
        {
            Console.WriteLine($"Modbus錯(cuò)誤: {ex.Message} (代碼: 0x{ex.ExceptionCode:X2})");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"發(fā)生錯(cuò)誤: {ex.Message}");
        }
        finally
        {
            autoReconnectClient.Disconnect();
        }
        
        // 使用Modbus RTU
        var rtuClient = ModbusClientFactory.CreateClient(
            ModbusType.RTU, "COM3,9600,None,8,One", defaultSlaveId: 2);
        
        try
        {
            if (await rtuClient.ConnectAsync())
            {
                // 讀取線圈狀態(tài)
                bool[] coils = await rtuClient.ReadCoilsAsync(2, 0, 8);
                Console.WriteLine("線圈狀態(tài): " + string.Join(", ", coils.Select(c => c ? "1" : "0")));
            }
        }
        finally
        {
            rtuClient.Disconnect();
        }
    }
}

8. 最佳實(shí)踐

1.??連接管理??:

  • 保持連接時(shí)間盡可能短
  • 使用自動(dòng)重連機(jī)制處理網(wǎng)絡(luò)波動(dòng)
  • 合理設(shè)置超時(shí)時(shí)間

2.??錯(cuò)誤處理??:

  • 捕獲并處理所有Modbus異常
  • 實(shí)現(xiàn)重試機(jī)制處理臨時(shí)錯(cuò)誤
  • 記錄詳細(xì)錯(cuò)誤日志

3.?性能優(yōu)化??:

  • 批量讀取數(shù)據(jù)減少請(qǐng)求次數(shù)
  • 使用異步方法避免阻塞
  • 合理設(shè)置請(qǐng)求大?。ū苊膺^(guò)大PDU)

4.?線程安全??:

  • Modbus客戶(hù)端不是線程安全的
  • 在多線程環(huán)境中使用鎖或創(chuàng)建多個(gè)實(shí)例
  • 避免并發(fā)訪問(wèn)同一連接

5.??配置管理??:

  • 將Modbus配置存儲(chǔ)在配置文件或數(shù)據(jù)庫(kù)中
  • 支持動(dòng)態(tài)更新配置
  • 提供配置驗(yàn)證功能

9. 總結(jié)

本文介紹了如何使用 .NET 8 實(shí)現(xiàn)一個(gè)功能完整的 Modbus 通訊工具類(lèi),主要特點(diǎn)包括:

1.??雙協(xié)議支持??:

  • 完整實(shí)現(xiàn) Modbus RTU 和 Modbus TCP 協(xié)議
  • 支持所有常用功能碼

2.??優(yōu)雅封裝??:

  • 統(tǒng)一的接口設(shè)計(jì)
  • 工廠模式創(chuàng)建客戶(hù)端
  • 擴(kuò)展方法提供高級(jí)功能

3.??健壯性設(shè)計(jì)??:

  • 完善的錯(cuò)誤處理機(jī)制
  • 自動(dòng)重連功能
  • CRC 和事務(wù)ID驗(yàn)證

4.??易用性??:

  • 簡(jiǎn)潔的API設(shè)計(jì)
  • 詳細(xì)的異常信息
  • 豐富的使用示例

通過(guò)這種封裝,開(kāi)發(fā)者可以快速集成 Modbus 通訊功能到各種工業(yè)控制系統(tǒng)中,大大提高了開(kāi)發(fā)效率和系統(tǒng)可靠性。該工具類(lèi)適用于 SCADA 系統(tǒng)、MES 系統(tǒng)、設(shè)備監(jiān)控平臺(tái)等各種工業(yè)自動(dòng)化場(chǎng)景。

以上就是.NET 8實(shí)現(xiàn)modbus通訊工具類(lèi)封裝的操作方法的詳細(xì)內(nèi)容,更多關(guān)于.NET 8 modbus工具類(lèi)封裝的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論