c# MOdbus rto读写串口,如何不相互影响

在C# Modbus RTU串口通信中,避免读写操作相互影响的关键在于合理的同步机制和资源管理。以下是几种有效的解决方案:

1. 使用锁机制(推荐)

基本锁实现

复制代码
public class ModbusRTUManager
{
    private readonly SerialPort _serialPort;
    private readonly object _lockObject = new object();
    
    public ModbusRTUManager(string portName, int baudRate)
    {
        _serialPort = new SerialPort(portName, baudRate)
        {
            Parity = Parity.None,
            StopBits = StopBits.One,
            DataBits = 8,
            ReadTimeout = 1000,
            WriteTimeout = 1000
        };
    }
    
    public byte[] ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort count)
    {
        lock (_lockObject)
        {
            if (!_serialPort.IsOpen)
                _serialPort.Open();
                
            // 发送读取请求
            var request = CreateReadHoldingRegistersRequest(slaveId, startAddress, count);
            _serialPort.Write(request, 0, request.Length);
            
            // 读取响应
            return ReadResponse(slaveId, 3); // 3是功能码
        }
    }
    
    public void WriteSingleRegister(byte slaveId, ushort address, ushort value)
    {
        lock (_lockObject)
        {
            if (!_serialPort.IsOpen)
                _serialPort.Open();
                
            // 发送写入请求
            var request = CreateWriteSingleRegisterRequest(slaveId, address, value);
            _serialPort.Write(request, 0, request.Length);
            
            // 读取响应(如果需要)
            ReadResponse(slaveId, 6); // 6是功能码
        }
    }
    
    private byte[] ReadResponse(byte expectedSlaveId, byte expectedFunctionCode)
    {
        // 实现响应读取逻辑,包括CRC校验
        // ...
    }
}

2. 使用SemaphoreSlim(支持异步)

复制代码
public class AsyncModbusRTUManager
{
    private readonly SerialPort _serialPort;
    private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
    
    public async Task<byte[]> ReadHoldingRegistersAsync(byte slaveId, ushort startAddress, ushort count)
    {
        await _semaphore.WaitAsync();
        try
        {
            if (!_serialPort.IsOpen)
                _serialPort.Open();
                
            // 清除输入缓冲区
            _serialPort.DiscardInBuffer();
            
            // 发送请求
            var request = CreateReadHoldingRegistersRequest(slaveId, startAddress, count);
            await WriteAsync(request);
            
            // 读取响应
            return await ReadResponseAsync(slaveId, 3);
        }
        finally
        {
            _semaphore.Release();
        }
    }
    
    private async Task WriteAsync(byte[] data)
    {
        await Task.Run(() => _serialPort.Write(data, 0, data.Length));
    }
    
    private async Task<byte[]> ReadResponseAsync(byte expectedSlaveId, byte expectedFunctionCode)
    {
        // 异步读取实现
        // ...
    }
}

3. 使用读写锁(ReadWriteLockSlim)

复制代码
public class ReadWriteModbusRTUManager
{
    private readonly SerialPort _serialPort;
    private readonly ReaderWriterLockSlim _lockSlim = new ReaderWriterLockSlim();
    
    // 用于读取操作(允许多个读取,但实际串口需要独占)
    public byte[] ReadData(byte slaveId, ushort address, ushort count)
    {
        _lockSlim.EnterWriteLock(); // 串口读取也需要写锁,因为涉及实际的IO操作
        try
        {
            // 读取操作
            return PerformReadOperation(slaveId, address, count);
        }
        finally
        {
            _lockSlim.ExitWriteLock();
        }
    }
    
    // 用于写入操作
    public void WriteData(byte slaveId, ushort address, ushort value)
    {
        _lockSlim.EnterWriteLock();
        try
        {
            // 写入操作
            PerformWriteOperation(slaveId, address, value);
        }
        finally
        {
            _lockSlim.ExitWriteLock();
        }
    }
}

4. 完整的线程安全实现示例

复制代码
public class ThreadSafeModbusRTU : IDisposable
{
    private readonly SerialPort _serialPort;
    private readonly object _syncRoot = new object();
    private bool _disposed = false;
    
    public ThreadSafeModbusRTU(string portName, int baudRate = 9600)
    {
        _serialPort = new SerialPort(portName, baudRate, Parity.None, 8, StopBits.One)
        {
            ReadTimeout = 1000,
            WriteTimeout = 1000,
            Handshake = Handshake.None
        };
    }
    
    public ModbusResult ReadHoldingRegisters(byte slaveId, ushort startAddress, ushort count)
    {
        lock (_syncRoot)
        {
            EnsureNotDisposed();
            EnsurePortOpen();
            
            try
            {
                _serialPort.DiscardInBuffer();
                _serialPort.DiscardOutBuffer();
                
                // 1. 发送请求
                var request = BuildReadRequest(slaveId, startAddress, count);
                _serialPort.Write(request, 0, request.Length);
                
                // 2. 读取响应
                var response = ReadExpectedBytes(5 + count * 2); // 基础5字节 + 数据
                
                // 3. 验证响应
                if (!ValidateResponse(response, slaveId, 0x03))
                    return ModbusResult.CreateErrorResult("响应验证失败");
                
                // 4. 解析数据
                var data = ParseRegisterData(response, count);
                return ModbusResult.CreateSuccessResult(data);
            }
            catch (TimeoutException ex)
            {
                return ModbusResult.CreateErrorResult($"读取超时: {ex.Message}");
            }
            catch (Exception ex)
            {
                return ModbusResult.CreateErrorResult($"读取失败: {ex.Message}");
            }
        }
    }
    
    public ModbusResult WriteSingleRegister(byte slaveId, ushort address, ushort value)
    {
        lock (_syncRoot)
        {
            EnsureNotDisposed();
            EnsurePortOpen();
            
            try
            {
                _serialPort.DiscardInBuffer();
                _serialPort.DiscardOutBuffer();
                
                // 发送写入请求
                var request = BuildWriteRequest(slaveId, address, value);
                _serialPort.Write(request, 0, request.Length);
                
                // 读取响应
                var response = ReadExpectedBytes(8); // 写入响应固定8字节
                
                if (!ValidateResponse(response, slaveId, 0x06))
                    return ModbusResult.CreateErrorResult("写入响应验证失败");
                
                return ModbusResult.CreateSuccessResult();
            }
            catch (Exception ex)
            {
                return ModbusResult.CreateErrorResult($"写入失败: {ex.Message}");
            }
        }
    }
    
    private void EnsurePortOpen()
    {
        if (!_serialPort.IsOpen)
            _serialPort.Open();
    }
    
    private void EnsureNotDisposed()
    {
        if (_disposed)
            throw new ObjectDisposedException("ModbusRTU已被释放");
    }
    
    public void Dispose()
    {
        lock (_syncRoot)
        {
            if (!_disposed)
            {
                _serialPort?.Close();
                _serialPort?.Dispose();
                _disposed = true;
            }
        }
    }
}

5. 使用建议

  1. 单一职责原则:每个串口由一个管理器实例负责

  2. 超时设置:合理设置读写超时,避免线程长时间阻塞

  3. 缓冲区清理:每次通信前清理缓冲区

  4. 异常处理:确保异常情况下锁能被正确释放

  5. 资源释放:正确实现IDisposable接口

6. 调用示例

复制代码
using (var modbus = new ThreadSafeModbusRTU("COM1", 9600))
{
    // 读取操作 - 自动同步,不会相互干扰
    var result1 = modbus.ReadHoldingRegisters(1, 0, 10);
    
    // 写入操作 - 会等待读取完成后执行
    var result2 = modbus.WriteSingleRegister(1, 5, 100);
    
    // 并发调用也会自动序列化
    Task.WaitAll(
        Task.Run(() => modbus.ReadHoldingRegisters(1, 0, 5)),
        Task.Run(() => modbus.WriteSingleRegister(1, 6, 200))
    );
}

这种设计确保了串口读写的原子性,避免了多个线程同时访问串口导致的通信混乱。

相关推荐
ShineWinsu18 小时前
对于C++:继承的解析—上
开发语言·数据结构·c++·算法·面试·笔试·继承
pp起床18 小时前
动态规划 | part05
算法·动态规划
GuangHeAI_ATing18 小时前
国密算法SSD怎么选?这3款国产固态硬盘安全又高速
算法
左手厨刀右手茼蒿18 小时前
Flutter for OpenHarmony: Flutter 三方库 hashlib 为鸿蒙应用提供军用级加密哈希算法支持(安全数据完整性卫士)
安全·flutter·华为·c#·哈希算法·linq·harmonyos
雨泪丶19 小时前
代码随想录算法训练营-Day34
算法
Yzzz-F19 小时前
牛客寒假算法训练营2
算法
甄心爱学习19 小时前
【python】获取所有长度为 k 的二进制字符串
python·算法
iAkuya19 小时前
(leetcode)力扣100 76数据流的中位数(堆)
算法·leetcode·职场和发展
键盘鼓手苏苏20 小时前
Flutter for OpenHarmony: Flutter 三方库 ntp 精准同步鸿蒙设备系统时间(分布式协同授时利器)
android·分布式·算法·flutter·华为·中间件·harmonyos
董董灿是个攻城狮20 小时前
AI 视觉连载5:传统 CV 之均值滤波
算法