近期小细节总结

串口并发调用

单个串口通讯(Modbus RTU也算),业务流程中的串口调用,既有定时轮询又有依次发送,也就是涉及到并发调用通讯的,不要犹豫,为通讯管理模块建立消息队列

  • 不用纠结锁的问题,天然线程安全

  • 定时任务和手动操作随便调用,自动排队

// 请求队列
private readonly Channel<ModbusRequest> _requestChannel = Channel.CreateBounded<ModbusRequest>(20);
private readonly CancellationTokenSource _processingCts = new CancellationTokenSource();
private Task _processingTask;


        // 添加请求类
        private class ModbusRequest
        {
            public byte DeviceAddress { get; set; }
            public byte FunctionCode { get; set; }
            public ushort RegisterAddress { get; set; }
            public ushort Value { get; set; }
            public ushort RegisterCount { get; set; }
            public TaskCompletionSource<byte[]> CompletionSource { get; set; }
        }


            // 启动请求处理任务
            _processingTask = Task.Run(ProcessModbusRequests);



        /// <summary>
        /// Modbus命令内部实现(无锁版本)
        /// </summary>
        private async Task<byte[]> SendModbusCommandInternal(byte deviceAddress, byte functionCode, ushort registerAddress, ushort value = 0, ushort registerCount = 1)
        {
            try
            {
                // 构建请求帧(原SendModbusCommand的逻辑移到这里)
                byte[] request;
                if (functionCode == 0x03) // 读取保持寄存器
                {
                    request = new byte[6];
                    request[0] = deviceAddress;
                    request[1] = functionCode;
                    request[2] = (byte)(registerAddress >> 8);
                    request[3] = (byte)(registerAddress & 0xFF);
                    request[4] = (byte)(registerCount >> 8);
                    request[5] = (byte)(registerCount & 0xFF);
                }
                else if (functionCode == 0x06) // 写单个寄存器
                {
                    request = new byte[6];
                    request[0] = deviceAddress;
                    request[1] = functionCode;
                    request[2] = (byte)(registerAddress >> 8);
                    request[3] = (byte)(registerAddress & 0xFF);
                    request[4] = (byte)(value >> 8);
                    request[5] = (byte)(value & 0xFF);
                }
                else
                {
                    throw new ArgumentException($"不支持的功能码: {functionCode}");
                }

                // 添加CRC校验
                byte[] crc = CalculateCRC(request);
                byte[] fullRequest = new byte[request.Length + 2];
                Array.Copy(request, fullRequest, request.Length);
                fullRequest[request.Length] = crc[0];
                fullRequest[request.Length + 1] = crc[1];

                // 清空缓冲区
                _serialPort.DiscardInBuffer();
                _serialPort.DiscardOutBuffer();

                // 发送请求
                IoC.Get<MessageManagement>().AddMessage($"Write: {BitConverter.ToString(fullRequest).Replace("-", " ")}");
                _serialPort.Write(fullRequest, 0, fullRequest.Length);

                // 异步读取响应
                return await ReadModbusResponse();
            }
            catch (Exception ex)
            {
                IoC.Get<MessageManagement>().AddMessage($"Modbus通信失败: {ex.Message}", MessageType.警告);
                return null;
            }
        }

        /// <summary>
        /// 异步读取Modbus响应
        /// </summary>
        private async Task<byte[]> ReadModbusResponse()
        {
            await Task.Delay(500); // 等待设备响应

            List<byte> response = new List<byte>();
            DateTime startTime = DateTime.Now;

            while (DateTime.Now - startTime < TimeSpan.FromMilliseconds(READ_TIMEOUT))
            {
                if (_serialPort.BytesToRead > 0)
                {
                    byte[] buffer = new byte[_serialPort.BytesToRead];
                    int bytesRead = _serialPort.Read(buffer, 0, buffer.Length);
                    response.AddRange(buffer.Take(bytesRead));

                    // 检查是否收到完整帧
                    if (response.Count >= 5) // 最小响应长度
                    {
                        // 验证CRC
                        byte[] receivedData = response.ToArray();
                        byte[] receivedCRC = new byte[] { receivedData[receivedData.Length - 2], receivedData[receivedData.Length - 1] };
                        byte[] calculatedCRC = CalculateCRC(receivedData.Take(receivedData.Length - 2).ToArray());

                        if (receivedCRC[0] == calculatedCRC[0] && receivedCRC[1] == calculatedCRC[1])
                        {
                            return receivedData;
                        }
                        else
                        {
                            IoC.Get<MessageManagement>().AddMessage($"CRC error: {BitConverter.ToString(receivedData).Replace("-", " ")}");
                        }
                    }
                }
                await Task.Delay(10);
            }

            throw new TimeoutException("读取响应超时");
        }
        /// <summary>
        /// 发送Modbus命令(队列版本)
        /// </summary>
        private async Task<byte[]> SendModbusCommand(byte deviceAddress, byte functionCode, ushort registerAddress, ushort value = 0, ushort registerCount = 1)
        {
            var request = new ModbusRequest
            {
                DeviceAddress = deviceAddress,
                FunctionCode = functionCode,
                RegisterAddress = registerAddress,
                Value = value,
                RegisterCount = registerCount,
                CompletionSource = new TaskCompletionSource<byte[]>()
            };

            // 修复:使用带CancellationToken的WaitToWriteAsync
            if (await _requestChannel.Writer.WaitToWriteAsync(_processingCts.Token))
            {
                await _requestChannel.Writer.WriteAsync(request, _processingCts.Token);

                // 使用带超时的Task等待
                using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
                var timeoutTask = Task.Delay(Timeout.Infinite, timeoutCts.Token);
                var responseTask = request.CompletionSource.Task;

                var completedTask = await Task.WhenAny(responseTask, timeoutTask);
                if (completedTask == responseTask)
                {
                    return await responseTask;
                }
                else
                {
                    throw new TimeoutException("Modbus操作超时");
                }
            }
            else
            {
                throw new TimeoutException("Modbus请求队列已满");
            }
        }
        /// <summary>
        /// 读取保持寄存器
        /// </summary>
        async private Task<ushort[]> ReadHoldingRegisters(byte deviceAddress, ushort startAddress, ushort registerCount)
        {
            byte[] response = await SendModbusCommand(deviceAddress, 0x03, startAddress, 0, registerCount);

            if (response != null && response.Length >= 5 + registerCount * 2)
            {
                ushort[] result = new ushort[registerCount];
                for (int i = 0; i < registerCount; i++)
                {
                    int offset = 3 + i * 2;
                    result[i] = (ushort)((response[offset] << 8) | response[offset + 1]);
                }
                return result;
            }

            return null;
        }

        /// <summary>
        /// 写单个寄存器
        /// </summary>
        async private Task<bool> WriteSingleRegister(byte deviceAddress, ushort registerAddress, ushort value)
        {
            byte[] response = await SendModbusCommand(deviceAddress, 0x06, registerAddress, value);
            return response != null && response.Length >= 6;
        }

 

CPU占用

减少Task.Factory.StartNew,线程池都给干满了,非必要不要用

posted @ 2025-11-06 12:02  dyfisgod  阅读(10)  评论(0)    收藏  举报