环境箱的优化13

针对串口通信不支持中断的问题,我们需要优化 EvnChamber 类的代码,解决 m_Hardware.QueryData("ChamberRead") 同步阻塞导致 StopACQAsync 无法快速响应的时间差问题,同时通过硬件命令中断或通信协议优化来实现取消功能。此外,我们将增加代码的可读性和扩展性,并提供模拟硬件返回数据的测试方法,方便调试。以下是详细的优化方案、代码示例和中文解释。


问题分析

  1. 串口通信不支持中断:

    • 串口通信(如 SerialPort 的 Read 或 Write)是阻塞操作,CancellationToken 无法直接中断。

    • 原代码中的 Write 方法使用 Thread.Sleep(500) 模拟阻塞,真实串口操作可能需要数百毫秒到数秒,导致 StopACQAsync 延迟响应。

  2. 时间差问题:

    • Task.WhenAll 等待所有 ReadTRHAsync 任务完成,如果某个任务在 QueryData 中阻塞,取消信号需等待硬件操作结束。

    • UninitializeAsync 可能在查询完成前清空 m_Hardware,导致空引用或状态不一致。

  3. 可读性和扩展性不足:

    • 原代码中,硬件操作、状态管理和错误处理逻辑混杂,难以维护。

    • 日志和错误处理分散,缺乏统一的异常管理策略。

    • 缺乏明确的硬件模拟机制,不便于测试。

  4. 硬件命令中断需求:

    • 需要通过发送特定中断命令(如 "ABORT")或关闭串口来模拟中断。

    • 优化通信协议,减少不必要的等待时间(如设置读取超时)。


优化目标

  1. 实现硬件命令中断:

    • 使用特定命令(如 "ABORT")通知硬件停止当前操作。

    • 在取消时关闭串口,强制中断阻塞操作。

    • 使用 Task.WhenAny 结合超时(2秒)确保查询快速失败。

  2. 优化串口通信协议:

    • 设置串口读取超时(例如 500ms),避免长时间阻塞。

    • 使用异步 SerialPort.BaseStream.ReadAsync/WriteAsync,支持 CancellationToken。

    • 优化数据包格式,确保命令和响应高效解析。

  3. 增加可读性和扩展性:

    • 重构代码,分离硬件操作、状态管理和错误处理逻辑。

    • 使用接口和抽象类,支持不同硬件驱动(如串口、TCP)。

    • 添加注释和日志,清晰记录操作流程和异常。

  4. 模拟硬件返回数据:

    • 提供 MockHardware 类,模拟串口返回的温度、湿度等数据。

    • 支持配置延迟和错误,方便测试取消、超时和错误场景。

  5. 保持高效率:

    • 保留 Task.WhenAll 的并行查询优势。

    • 优化巡检间隔(10ms)和超时(2秒)。


优化方案

1. 硬件命令中断

  • 中断命令:发送 "ABORT" 命令,通知硬件停止操作。

  • 关闭串口:在取消时调用 SerialPort.Close(),中断阻塞的读写。

  • 超时控制:Task.WhenAny 确保查询在 2 秒内超时,抛出 OperationCanceledException。

2. 串口通信协议优化

  • 异步读写:使用 SerialPort.BaseStream.ReadAsync/WriteAsync,支持取消。

  • 读取超时:设置 SerialPort.ReadTimeout = 500(毫秒),避免无限等待。

  • 数据包格式:定义简单的命令-响应协议,例如:

    命令: "ChamberRead\r\n"
    响应: "T:25.0,RH:50.0,Status:Running,Error:0000\r\n"
    中断命令: "ABORT\r\n"

3. 可读性和扩展性

  • 分离逻辑:将硬件操作封装到 IHardwareAdaptor,状态管理移到 ChamberStateManager。

  • 统一错误处理:使用 HandleException 集中处理异常,触发 SystemNotifEvent。

  • 日志增强:为每个关键操作添加时间戳和上下文。

  • 接口扩展:IHardwareAdaptor 支持不同通信协议(如串口、TCP)。

4. 模拟硬件

  • 实现 MockHardware 类,返回预定义的温度(25.0°C)、湿度(50.0%)等数据。

  • 支持配置延迟(例如 500ms)和错误代码(例如 "E001")。

  • 提供 IsMock 属性,切换真实/模拟硬件。


完整代码示例

以下是优化后的 EvnChamber 类,包含串口通信优化、硬件命令中断和模拟硬件。

csharp

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO.Ports;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using log4net;

namespace YourNamespace
{
    public class EvnChamber : IHardwareFlow
    {
        #region Fields
        private static readonly ILog m_Log = LogManager.GetLogger("ROBOT");
        private readonly Dictionary<string, TestSection> m_TestAreaMap = new Dictionary<string, TestSection>();
        private readonly ChamberTestCondition m_ChamberTestCondition = new ChamberTestCondition();
        private readonly Dictionary<string, ChamberTestCondition> m_ChamConditionMap = new Dictionary<string, ChamberTestCondition>();
        private readonly ChamberState m_ChamberRealTimeState = new ChamberState();
        private IHardwareAdaptor m_Hardware;
        private CancellationTokenSource _cts;
        private readonly SemaphoreSlim m_Semaphore = new SemaphoreSlim(1, 1); // 异步锁
        private string LastExceptionMsg;
        private string LastErrorCode;
        private bool m_Dis = true;
        private bool m_RateQurey = false;
        private bool StoppedQuery = false;
        private bool InitializeStatus = false;
        private bool Reading = false;
        private bool FirstRate = true;
        private bool TemBreak = false;
        private readonly bool HumBreak = false;
        public bool StopExcute = false;
        public double SetTem = -1000;
        public double SetHum = 0;
        public bool EnableHum = true;
        private readonly ConcurrentQueue<double> SetHumQueue = new ConcurrentQueue<double>();
        private readonly Dictionary<string, double> tempList = new Dictionary<string, double>();
        private readonly Dictionary<string, double> tempOverList = new Dictionary<string, double>();
        private readonly Dictionary<string, Parameter> m_ParameterMap = new Dictionary<string, Parameter>();
        private volatile bool IsQuerying = false; // 跟踪查询状态
        private const int UNFINDHARDWARE = -1;
        private const int UNCONNECTIONHARDWARE = -2;
        #endregion

        #region Properties
        public string Name { get; set; }
        public string HardClass { get; set; }
        public string Com { get; set; }
        public Dictionary<string, TestSection> TestAreaMap { get => m_TestAreaMap; set => m_TestAreaMap.Clear(); m_TestAreaMap.AddRange(value); }
        public ChamberState ChamberRealTimeState { get => m_ChamberRealTimeState; set => m_ChamberRealTimeState.Update(value); }
        public Dictionary<string, ChamberTestCondition> ChamConditionMap { get => m_ChamConditionMap; set => m_ChamConditionMap.Clear(); m_ChamConditionMap.AddRange(value); }
        public string errCode { get => LastErrorCode; private set => LastErrorCode = value; }
        public bool ManualControl { get; set; }
        public RunningStatus RunningStatus { get; set; }
        public HardwareStatus HardwareStatus { get; set; }
        public SetupStatus SetupStatus { get; set; }
        public HardwareConnectStatus HardwareConnectStatus { get; set; }
        public FlowStatus FlowStatus { get; set; }
        public bool IsMock { get; set; } // 是否使用模拟硬件
        #endregion

        #region Construction
        public EvnChamber()
        {
            RunningStatus = RunningStatus.Idle;
            HardwareStatus = HardwareStatus.Enabled;
            SetupStatus = SetupStatus.None;
        }
        #endregion

        #region 执行动作
        public void Initialize()
        {
            try
            {
                InitializeStatus = false;
                m_Dis = false;
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Initializing hardware {Name}");

                m_Hardware = IsMock ? new MockHardware() : IoManager.Hardware(Com);
                if (m_Hardware == null)
                {
                    ErrExceptionGrad(UNFINDHARDWARE.ToString());
                    return;
                }

                if (!m_Hardware.Connected || HardwareConnectStatus == HardwareConnectStatus.Idle)
                {
                    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 正在初始化", false);
                    InitDataMap();
                    m_Hardware.Initialize();
                    m_Hardware.OpenSession();

                    if (m_Hardware.ReadData() is HardwareResult result)
                    {
                        if (result.Error.ToString() == "0000")
                        {
                            HardwareConnectStatus = HardwareConnectStatus.InUsing;
                            SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 初始化完成", false);
                            m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} initialized successfully");
                        }
                        else
                        {
                            ProcessError(result, HardwareConnectStatus.Malfunction);
                            SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: 初始化失败", result.Error.ToString());
                            m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} initialization failed: {result.Error}");
                        }
                    }
                    else
                    {
                        ErrExceptionGrad(UNFINDHARDWARE.ToString());
                        m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} read data failed during initialization");
                    }
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, "Initialization failed");
            }
            finally
            {
                InitializeStatus = true;
            }
        }

        public async Task UninitializeAsync()
        {
            try
            {
                if (m_Hardware == null)
                {
                    ErrExceptionGrad(UNFINDHARDWARE.ToString());
                    return;
                }
                if (!m_Hardware.Connected)
                {
                    m_Dis = true;
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} already disconnected");
                    return;
                }

                await m_Semaphore.WaitAsync();
                try
                {
                    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 正在进行停止卸载操作", false);
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Uninitializing hardware {Name}");

                    var hardwareResult = await m_Hardware.QueryDataAsync("CHAMBEROFF");
                    if (hardwareResult?.Error.ToString() == "0000")
                    {
                        SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 停止测试操作完成", false);
                        m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} stopped successfully");
                    }
                    else
                    {
                        SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: 停止操作异常", hardwareResult?.Error.ToString() ?? "Unknown");
                        m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} stop failed: {hardwareResult?.Error}");
                    }

                    m_Hardware.CloseSession();
                    m_Hardware.UnInitialize();
                    m_Hardware = null;
                }
                finally
                {
                    m_Semaphore.Release();
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, "Uninitialization failed");
            }
            finally
            {
                HardwareConnectStatus = HardwareConnectStatus.Idle;
                m_Dis = true;
            }
        }

        public async Task RiseAsync(object obj, CancellationToken cancellationToken = default)
        {
            DataTable dataTable = obj as DataTable;
            if (!ValidateHardware()) return;

            try
            {
                if (HardwareConnectStatus == HardwareConnectStatus.InUsing)
                {
                    StopExcute = false;
                    FlowStatus = FlowStatus.POS;
                    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 环境参数上升中", false);

                    await m_Semaphore.WaitAsync(cancellationToken);
                    try
                    {
                        if (HardClass == "ESPECTRH")
                        {
                            SetTem = Convert.ToDouble(dataTable.Rows[2].ItemArray[1]);
                            EnableHum = Convert.ToBoolean(dataTable.Rows[3].ItemArray[1]);
                            if (EnableHum)
                            {
                                SetHum = Convert.ToDouble(dataTable.Rows[5].ItemArray[1] ?? 45);
                                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Rise SetHum: {SetHum}");
                            }
                        }
                        else
                        {
                            SetTem = Convert.ToDouble(dataTable.Rows[1].ItemArray[1]);
                        }

                        string jsonConfig = Utils.DataTableToJson(dataTable);
                        string instruction = $"CHAMBERSET {jsonConfig}";
                        m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Rise {Name}: {instruction}");
                        var result = await m_Hardware.QueryDataAsync(instruction, cancellationToken);
                        errCode = result.Error as string;
                        ProcessError(result, HardwareConnectStatus.InUsing);

                        if (result.Error.ToString() == "0000")
                        {
                            while (!StopExcute && FlowStatus != FlowStatus.STABLE && HardwareConnectStatus == HardwareConnectStatus.InUsing && SinySystem.InterLock == 0)
                            {
                                await Task.Delay(50, cancellationToken);
                            }
                            SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 环境参数上升完成", false);
                        }
                        else
                        {
                            SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: 环境参数上升失败", "");
                        }
                    }
                    finally
                    {
                        m_Semaphore.Release();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Rise operation canceled for {Name}");
            }
            catch (Exception ex)
            {
                HandleException(ex, "Rise operation failed");
            }
            finally
            {
                StopExcute = false;
            }
        }

        public async Task DropAsync(DataTable dataTable, CancellationToken cancellationToken = default)
        {
            if (!ValidateHardware()) return;

            try
            {
                if (HardwareConnectStatus == HardwareConnectStatus.InUsing)
                {
                    StopExcute = false;
                    FlowStatus = FlowStatus.POS;
                    SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 环境参数下降中", false);

                    await m_Semaphore.WaitAsync(cancellationToken);
                    try
                    {
                        if (HardClass == "ESPECTRH")
                        {
                            dataTable.Rows[1][1] = m_ChamberRealTimeState.ChamberRealT;
                            SetTem = Convert.ToDouble(dataTable.Rows[2].ItemArray[1]);
                            EnableHum = Convert.ToBoolean(dataTable.Rows[3].ItemArray[1]);
                            if (EnableHum && m_ChamberRealTimeState.ChamberRealRH != -1)
                            {
                                dataTable.Rows[4][1] = m_ChamberRealTimeState.ChamberRealRH;
                                SetHum = Convert.ToDouble(dataTable.Rows[5].ItemArray[1] ?? 45);
                                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Drop SetHum: {SetHum}");
                            }
                        }
                        else
                        {
                            SetTem = Convert.ToDouble(dataTable.Rows[1].ItemArray[1]);
                        }

                        string jsonConfig = Utils.DataTableToJson(dataTable);
                        string instruction = $"ChamberSet {jsonConfig}";
                        m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Drop {Name}: {instruction}");
                        var result = await m_Hardware.QueryDataAsync(instruction, cancellationToken);
                        errCode = result.Error as string;
                        ProcessError(result, HardwareConnectStatus.InUsing);

                        if (result.Error.ToString() == "0000")
                        {
                            while (!StopExcute && FlowStatus != FlowStatus.STABLE && HardwareConnectStatus == HardwareConnectStatus.InUsing && SinySystem.InterLock == 0)
                            {
                                await Task.Delay(50, cancellationToken);
                            }
                            SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 环境参数下降完成", false);
                        }
                        else
                        {
                            SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: 环境参数下降失败", "");
                        }
                    }
                    finally
                    {
                        m_Semaphore.Release();
                    }
                }
            }
            catch (OperationCanceledException)
            {
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Drop operation canceled for {Name}");
            }
            catch (Exception ex)
            {
                HandleException(ex, "Drop operation failed");
            }
            finally
            {
                StopExcute = false;
            }
        }
        #endregion

        #region 读数据
        public void StartACQ(CancellationToken externalToken = default)
        {
            if (_cts != null)
            {
                _cts.Dispose();
            }
            _cts = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
            _cts.CancelAfter(TimeSpan.FromSeconds(30)); // 整体巡检 30 秒超时
            _cts.Token.Register(() => m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} patrol canceled (timeout or manual)"));
            StoppedQuery = false;
            IsQuerying = false;
            Task.Run(() => _StartACQAsync(_cts.Token));
        }

        private async Task _StartACQAsync(CancellationToken cancellationToken)
        {
            try
            {
                if (!ValidateHardware(initialize: true)) return;

                FirstRate = true;
                while (!cancellationToken.IsCancellationRequested && m_Hardware != null && m_Hardware.Connected && HardwareConnectStatus == HardwareConnectStatus.InUsing)
                {
                    m_RateQurey = true;
                    var stopwatch = Stopwatch.StartNew();
                    var tasks = new[]
                    {
                        ReadTRHAsync(cancellationToken, "ChamberRead"),
                        ReadTRHAsync(cancellationToken, "ChamberStatus")
                    };
                    await Task.WhenAll(tasks);
                    stopwatch.Stop();
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Task.WhenAll completed in {stopwatch.ElapsedMilliseconds}ms");
                    await Task.Delay(10, cancellationToken); // 10ms 间隔
                }
            }
            catch (OperationCanceledException)
            {
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} patrol canceled");
            }
            catch (Exception ex)
            {
                HandleException(ex, "Patrol failed");
            }
            finally
            {
                SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 已退出状态巡检", false);
                m_Dis = true;
                StoppedQuery = true;
                IsQuerying = false;
            }
        }

        private async Task ReadTRHAsync(CancellationToken cancellationToken, string command)
        {
            try
            {
                cancellationToken.ThrowIfCancellationRequested();
                Reading = true;
                IsQuerying = true;

                using var queryTimeoutCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
                queryTimeoutCts.CancelAfter(TimeSpan.FromSeconds(2)); // 每次查询 2 秒超时

                var stopwatch = Stopwatch.StartNew();
                var hardwareTask = Task.Run(async () => await m_Hardware?.QueryDataAsync(command, queryTimeoutCts.Token), queryTimeoutCts.Token);
                var timeoutTask = Task.Delay(TimeSpan.FromSeconds(2), queryTimeoutCts.Token);
                var completedTask = await Task.WhenAny(hardwareTask, timeoutTask);

                if (completedTask == timeoutTask)
                {
                    m_Hardware.Abort(); // 中断硬件操作
                    m_Log.Warn($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} query ({command}) timed out after 2 seconds");
                    throw new OperationCanceledException($"Hardware query ({command}) timed out");
                }

                var result = await hardwareTask;
                stopwatch.Stop();
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware query ({command}) completed in {stopwatch.ElapsedMilliseconds}ms");

                if (result == null)
                {
                    throw new InvalidOperationException($"Hardware query ({command}) returned null");
                }

                UpdateErrorState(result);
                UpdateChamberState(result);
                CheckTemperatureStability(result);
                CheckTemperatureAbnormal();
                CheckOverTemperatureProtection();
            }
            catch (OperationCanceledException)
            {
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} read ({command}) canceled");
            }
            catch (Exception ex)
            {
                HandleException(ex, $"ReadTRHAsync ({command}) failed");
            }
            finally
            {
                Reading = false;
                FirstRate = false;
                IsQuerying = false;
            }
        }

        public async Task StopACQAsync()
        {
            try
            {
                if (_cts != null && !_cts.IsCancellationRequested)
                {
                    _cts.Cancel();
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} cancel requested");
                    if (m_Hardware != null)
                    {
                        await m_Hardware.QueryDataAsync("ABORT"); // 发送中断命令
                        m_Hardware.Abort(); // 强制中断
                    }
                }

                // 等待正在进行的查询完成
                int waitCount = 0;
                while (IsQuerying && waitCount < 20) // 最多等待 2 秒
                {
                    await Task.Delay(100);
                    waitCount++;
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Waiting for query to complete: {waitCount}/20");
                }

                if (!ValidateHardware()) return;
                if (HardwareConnectStatus == HardwareConnectStatus.InUsing)
                {
                    await UninitializeAsync();
                }
            }
            catch (Exception ex)
            {
                HandleException(ex, "StopACQAsync failed");
            }
            finally
            {
                m_Dis = true;
                StoppedQuery = true;
                if (_cts != null)
                {
                    _cts.Dispose();
                    _cts = null;
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} CTS disposed");
                }
            }
        }
        #endregion

        #region 状态管理
        private void UpdateErrorState(HardwareResult result)
        {
            bool isErrorChanged = LastErrorCode != result.Error.ToString();
            LastErrorCode = result.Error.ToString();

            if (result.Error.ToString() == "0000")
            {
                if (isErrorChanged && HardwareConnectStatus != HardwareConnectStatus.InUsing)
                {
                    HardwareConnectStatus = HardwareConnectStatus.InUsing;
                    if (!FirstRate)
                    {
                        SystemNotifEvent.HardInfoNotifEvent(this, $"{Name}: 恢复正常", true);
                    }
                }
            }
            else if (isErrorChanged)
            {
                ProcessError(result, HardwareConnectStatus.InUsing);
            }
        }

        private void UpdateChamberState(HardwareResult result)
        {
            if (result.ResultMap.TryGetValue("ChamberRealRH", out var rhValue) && rhValue is double rh)
            {
                ChamberRealTimeState.ChamberRealRH = EnableHum && !double.IsNaN(rh) ? rh : -1;
            }
            else
            {
                ChamberRealTimeState.ChamberRealRH = double.NaN;
            }

            ChamberRealTimeState.ChamberRealT = (double)result.ResultMap["ChamberRealT"];
            ChamberRealTimeState.ChamberStatus = (string)result.ResultMap["ChamberStatus"];
            ChamberRealTimeState.Error = result.Error.ToString();
        }

        private void CheckTemperatureStability(HardwareResult result)
        {
            double tempResolution = Convert.ToDouble(m_ParameterMap["TempResolution"].Value);
            if (Math.Abs(ChamberRealTimeState.ChamberRealT - SetTem) <= tempResolution)
            {
                if (HardClass == "ESPECTRH" && EnableHum)
                {
                    double humResolution = Convert.ToDouble(m_ParameterMap["HumResolution"].Value);
                    if (Math.Abs(ChamberRealTimeState.ChamberRealRH - SetHum) <= humResolution)
                    {
                        FlowStatus = FlowStatus.STABLE;
                    }
                }
                else
                {
                    FlowStatus = FlowStatus.STABLE;
                }
            }
        }

        private void CheckTemperatureAbnormal()
        {
            if (!m_ParameterMap.TryGetValue("TempAbnormal", out var param)) return;

            double tempAbnormal = Convert.ToDouble(param.Value);
            if (!m_Dis && FlowStatus == FlowStatus.STABLE && Math.Abs(SetTem - ChamberRealTimeState.ChamberRealT) >= tempAbnormal)
            {
                string dateTime = DateTime.Now.ToString("F");
                if (!tempList.ContainsKey(dateTime))
                {
                    tempList.Add(dateTime, ChamberRealTimeState.ChamberRealT);
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] {Name} 温箱异常, STABLE, SetTem:{SetTem}, ChamberRealT:{ChamberRealTimeState.ChamberRealT}, TempAbnormal:{tempAbnormal}");
                }
            }
            else
            {
                tempList.Clear();
            }

            if (tempList.Count >= 5)
            {
                string warnStr = $"温箱出现异常,请联系工程师排查解决。温箱编号:{Name}, 温箱设置值:{SetTem}, 当前温度:{ChamberRealTimeState.ChamberRealT}, 状态:{ChamberRealTimeState.ChamberStatus}";
                SystemNotifEvent.HardWarnNotifEvent(this, warnStr, "");
                HardwareMgr.AuxCtrlBoard.AUXError();
                tempList.Clear();
            }
        }

        private void CheckOverTemperatureProtection()
        {
            if (!m_ParameterMap.TryGetValue("OverTemperature", out var param)) return;

            double overTemperature = Convert.ToDouble(param.Value);
            if (!m_Dis && FlowStatus == FlowStatus.STABLE && (ChamberRealTimeState.ChamberRealT - SetTem) >= overTemperature)
            {
                string dateTime = DateTime.Now.ToString("F");
                if (!tempOverList.ContainsKey(dateTime))
                {
                    tempOverList.Add(dateTime, ChamberRealTimeState.ChamberRealT);
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] {Name} 温箱超温保护, STABLE, ChamberRealT:{ChamberRealTimeState.ChamberRealT}, SetTem:{SetTem}, OverTemperature:{overTemperature}");
                }
            }
            else
            {
                tempOverList.Clear();
            }

            if (tempOverList.Count >= 5)
            {
                string warnStr = $"温箱出现异常,超温保护,请联系工程师排查解决。温箱编号:{Name}, 温箱设置值:{SetTem}, 当前温度:{ChamberRealTimeState.ChamberRealT}, 状态:{ChamberRealTimeState.ChamberStatus}";
                bool stopTest = m_ParameterMap.ContainsKey("OverTemperatureIsStopTest");
                if (stopTest)
                {
                    SystemNotifEvent.HardErrorNotifEvent(this, warnStr, "");
                }
                else
                {
                    SystemNotifEvent.HardWarnNotifEvent(this, warnStr, "");
                }
                HardwareMgr.AuxCtrlBoard.AUXError();
                tempOverList.Clear();
            }
        }
        #endregion

        #region 辅助方法
        private bool ValidateHardware(bool initialize = false)
        {
            if (m_Hardware == null)
            {
                if (initialize)
                {
                    Initialize();
                    if (m_Hardware == null)
                    {
                        ErrExceptionGrad(UNFINDHARDWARE.ToString());
                        return false;
                    }
                }
                else
                {
                    ErrExceptionGrad(UNFINDHARDWARE.ToString());
                    return false;
                }
            }
            if (!m_Hardware.Connected)
            {
                ErrExceptionGrad(UNCONNECTIONHARDWARE.ToString());
                return false;
            }
            return true;
        }

        private void ProcessError(HardwareResult hHardwareResult, HardwareConnectStatus hardwareRunningStatus)
        {
            string errorCode = hHardwareResult.Error.ToString();
            if (errorCode != "0000")
            {
                string codeMean = HardwareMgr.ErrorCodeMap.TryGetValue(HardClass, out var auxCodeMap) &&
                    ((Dictionary<string, object>)auxCodeMap).TryGetValue(errorCode, out var errorInfo)
                    ? GlobalCache.Language == "CN"
                        ? (errorInfo as Dictionary<string, object>)["CN"].ToString()
                        : (errorInfo as Dictionary<string, object>)["EN"].ToString()
                    : GlobalCache.Language == "CN" ? "未知错误" : "Unknown error";

                if (((Dictionary<string, object>)auxCodeMap)?.GetValueOrDefault(errorCode)?["TYPE"]?.ToString() == "WARN")
                {
                    m_Log.Warn($"[{DateTime.Now:HH:mm:ss.fff}] {Name}: {codeMean}");
                    HardwareConnectStatus = hardwareRunningStatus;
                    SystemNotifEvent.HardWarnNotifEvent(this, $"{Name}: {codeMean}", errorCode);
                }
                else
                {
                    HardwareConnectStatus = HardwareConnectStatus.Malfunction;
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] {Name}: {codeMean}, Code: {errorCode}");
                    RemoteMgr.SendAlarm("Error", $"{Name} 温箱, {codeMean}", errCode, HardClass);
                    SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: {codeMean}", errorCode);
                }
            }
            else
            {
                HardwareConnectStatus = hardwareRunningStatus;
            }
        }

        private void ErrExceptionGrad(string errorCode)
        {
            if (SinySystem.SysInteruptDesc.TryGetValue(int.Parse(errorCode), out var errorDesc) && HardwareConnectStatus != HardwareConnectStatus.Malfunction && InitializeStatus)
            {
                HardwareInterupt(int.Parse(errorCode), errorDesc);
            }
            else
            {
                m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] Hardware {Name} error code {errorCode} not found");
                SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: 未知错误代码 {errorCode}", errorCode);
            }
        }

        private void HardwareInterupt(int interuptCode, string errorMessage)
        {
            SinySystem.Interupt = interuptCode;
            m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] {Name}: {errorMessage}");
            if (HardwareConnectStatus != HardwareConnectStatus.Malfunction)
            {
                LastErrorCode = interuptCode.ToString();
                HardwareConnectStatus = HardwareConnectStatus.Malfunction;
                SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: {errorMessage}", interuptCode.ToString());
            }
        }

        private void HandleException(Exception ex, string context)
        {
            if (LastExceptionMsg != ex.Message)
            {
                LastExceptionMsg = ex.Message;
                HardwareConnectStatus = HardwareConnectStatus.Malfunction;
                m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] {Name} {context}: {ex.Message}, StackTrace: {ex.StackTrace}");
                SystemNotifEvent.HardErrorNotifEvent(this, $"{Name}: {context}", ex.Message);
                NotificationMgr.Notify(this, new EventNotification { Type = EventType.Exception, Sender = this, Param = ex });
            }
        }

        private void InitDataMap()
        {
            m_ParameterMap["TempResolution"] = new Parameter { Value = "0.5" };
            m_ParameterMap["HumResolution"] = new Parameter { Value = "1.0" };
            m_ParameterMap["TempAbnormal"] = new Parameter { Value = "50" };
            m_ParameterMap["OverTemperature"] = new Parameter { Value = "50" };
            m_ParameterMap["AdapterBoardTempDiff"] = new Parameter { Value = "5" };
        }
        #endregion

        #region NONE
        private string ConfigToJson(ChamberTestCondition con)
        {
            return con.ChamberRHEN
                ? $"RHEN:{con.ChamberRHEN},StartT:{con.ChamberStartT},StopT:{con.ChamberStopT},StartRH:{con.ChamberStartRH},StopRH:{con.ChamberStopRH}"
                : $"RHEN:{con.ChamberRHEN},StartT:{con.ChamberStartT},StopT:{con.ChamberStopT}";
        }

        public object Stable() => true;
        public object QueryStatus() => throw new NotImplementedException();
        public void Config(HardwareConfig config) => throw new NotImplementedException();
        public object Write(object obj) => true;
        public void Read() { }
        internal void Suspend() { }
        internal void Resume() { }
        #endregion

        #region 硬件驱动
        private class SerialPortHardware : IHardwareAdaptor
        {
            private readonly SerialPort _serialPort;
            private readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
            private volatile bool _isAborted = false;

            public SerialPortHardware(string portName)
            {
                _serialPort = new SerialPort(portName, 9600, Parity.None, 8, StopBits.One)
                {
                    ReadTimeout = 500, // 读取超时 500ms
                    WriteTimeout = 500
                };
            }

            public bool Connected => _serialPort?.IsOpen ?? false;

            public void Initialize()
            {
                try
                {
                    _serialPort.Open();
                    _isAborted = false;
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort { _serialPort.PortName} initialized");
                }
                catch (Exception ex)
                {
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort initialization failed: {ex.Message}");
                    throw;
                }
            }

            public void UnInitialize()
            {
                _isAborted = true;
                if (_serialPort?.IsOpen == true)
                {
                    _serialPort.Close();
                }
                _serialPort?.Dispose();
                _writeSemaphore.Dispose();
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort uninitialized");
            }

            public void OpenSession()
            {
                if (!_serialPort.IsOpen)
                {
                    _serialPort.Open();
                }
                _isAborted = false;
            }

            public void CloseSession()
            {
                _isAborted = true;
                if (_serialPort.IsOpen)
                {
                    _serialPort.Close();
                }
            }

            public void Abort()
            {
                _isAborted = true;
                if (_serialPort.IsOpen)
                {
                    try
                    {
                        _serialPort.Write("ABORT\r\n"); // 发送中断命令
                        _serialPort.Close();
                        m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort communication aborted");
                    }
                    catch (Exception ex)
                    {
                        m_Log.Warn($"[{DateTime.Now:HH:mm:ss.fff}] Failed to send ABORT command: {ex.Message}");
                    }
                }
            }

            public object QueryData(string command)
            {
                return QueryDataAsync(command).GetAwaiter().GetResult();
            }

            public async Task<HardwareResult> QueryDataAsync(string command, CancellationToken cancellationToken = default)
            {
                await _writeSemaphore.WaitAsync(cancellationToken);
                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    if (_isAborted)
                    {
                        throw new OperationCanceledException($"Hardware communication aborted for command: {command}");
                    }

                    // 写入命令
                    byte[] buffer = Encoding.ASCII.GetBytes(command + "\r\n");
                    await _serialPort.BaseStream.WriteAsync(buffer, 0, buffer.Length, cancellationToken);

                    // 读取响应
                    byte[] response = new byte[1024];
                    int bytesRead = await _serialPort.BaseStream.ReadAsync(response, 0, response.Length, cancellationToken);
                    string responseStr = Encoding.ASCII.GetString(response, 0, bytesRead).Trim();

                    return ParseResponse(responseStr);
                }
                catch (TimeoutException)
                {
                    m_Log.Warn($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort read timeout for command: {command}");
                    throw new OperationCanceledException($"SerialPort read timeout for command: {command}");
                }
                catch (OperationCanceledException)
                {
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort query canceled for command: {command}");
                    throw;
                }
                catch (Exception ex)
                {
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] SerialPort query failed for command {command}: {ex.Message}");
                    throw;
                }
                finally
                {
                    _writeSemaphore.Release();
                }
            }

            public object ReadData()
            {
                return QueryData("ChamberRead");
            }

            private HardwareResult ParseResponse(string response)
            {
                var result = new HardwareResult { ResultMap = new Dictionary<string, object>() };
                try
                {
                    var parts = response.Split(',');
                    foreach (var part in parts)
                    {
                        var keyValue = part.Split(':');
                        if (keyValue.Length == 2)
                        {
                            string key = keyValue[0].Trim();
                            string value = keyValue[1].Trim();
                            if (key == "Error")
                            {
                                result.Error = value;
                            }
                            else if (key == "T")
                            {
                                result.ResultMap["ChamberRealT"] = double.Parse(value);
                            }
                            else if (key == "RH")
                            {
                                result.ResultMap["ChamberRealRH"] = double.Parse(value);
                            }
                            else if (key == "Status")
                            {
                                result.ResultMap["ChamberStatus"] = value;
                            }
                            else
                            {
                                result.ResultMap[key] = value;
                            }
                        }
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    m_Log.Error($"[{DateTime.Now:HH:mm:ss.fff}] Failed to parse response: {response}, Error: {ex.Message}");
                    result.Error = "E001";
                    return result;
                }
            }
        }

        private class MockHardware : IHardwareAdaptor
        {
            private readonly SemaphoreSlim _writeSemaphore = new SemaphoreSlim(1, 1);
            private volatile bool _isAborted = false;
            private readonly Random _random = new Random();

            public bool Connected { get; private set; } = false;

            public void Initialize()
            {
                Connected = true;
                _isAborted = false;
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] MockHardware initialized");
            }

            public void UnInitialize()
            {
                Connected = false;
                _isAborted = true;
                _writeSemaphore.Dispose();
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] MockHardware uninitialized");
            }

            public void OpenSession()
            {
                Connected = true;
                _isAborted = false;
            }

            public void CloseSession()
            {
                Connected = false;
                _isAborted = true;
            }

            public void Abort()
            {
                _isAborted = true;
                m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] MockHardware communication aborted");
            }

            public object QueryData(string command)
            {
                return QueryDataAsync(command).GetAwaiter().GetResult();
            }

            public async Task<HardwareResult> QueryDataAsync(string command, CancellationToken cancellationToken = default)
            {
                await _writeSemaphore.WaitAsync(cancellationToken);
                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    if (_isAborted)
                    {
                        throw new OperationCanceledException($"MockHardware communication aborted for command: {command}");
                    }

                    // 模拟硬件延迟
                    await Task.Delay(_random.Next(300, 600), cancellationToken); // 随机延迟 300-600ms

                    if (command == "ABORT")
                    {
                        _isAborted = true;
                        return new HardwareResult { Error = "0000", ResultMap = new Dictionary<string, object> { { "Status", "Aborted" } } };
                    }

                    return command switch
                    {
                        "ChamberRead" => new HardwareResult
                        {
                            Error = _random.Next(0, 10) < 2 ? "E001" : "0000", // 20% 概率模拟错误
                            ResultMap = new Dictionary<string, object>
                            {
                                { "ChamberRealT", 25.0 + _random.NextDouble() * 2 - 1 }, // 24-26°C
                                { "ChamberRealRH", 50.0 + _random.NextDouble() * 5 - 2.5 }, // 47.5-52.5%
                                { "ChamberStatus", "Running" }
                            }
                        },
                        "ChamberStatus" => new HardwareResult
                        {
                            Error = "0000",
                            ResultMap = new Dictionary<string, object>
                            {
                                { "ChamberStatus", "Running" },
                                { "ExtraStatus", "OK" }
                            }
                        },
                        "CHAMBEROFF" => new HardwareResult
                        {
                            Error = "0000",
                            ResultMap = new Dictionary<string, object> { { "Status", "Off" } }
                        },
                        _ => new HardwareResult { Error = "E002", ResultMap = new Dictionary<string, object> { { "Status", "Unknown command" } } }
                    };
                }
                catch (OperationCanceledException)
                {
                    m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] MockHardware query canceled for command: {command}");
                    throw;
                }
                finally
                {
                    _writeSemaphore.Release();
                }
            }

            public object ReadData()
            {
                return QueryData("ChamberRead");
            }
        }
        #endregion

        #region 辅助类
        private enum HardwareConnectStatus { Idle, InUsing, Malfunction }
        private enum RunningStatus { Idle, Running }
        private enum HardwareStatus { Enabled, Disabled }
        private enum SetupStatus { None }
        private enum FlowStatus { POS, STABLE }
        private enum EventType { Exception }

        private class HardwareResult
        {
            public object Error { get; set; } = "0000";
            public Dictionary<string, object> ResultMap { get; set; } = new Dictionary<string, object>();
        }

        private class EventNotification
        {
            public EventType Type { get; set; }
            public object Sender { get; set; }
            public object Param { get; set; }
        }

        private class Parameter
        {
            public string Value { get; set; }
        }

        private class TestSection { }

        private class ChamberTestCondition
        {
            public bool ChamberRHEN { get; set; }
            public double ChamberStartT { get; set; }
            public double ChamberStopT { get; set; }
            public double ChamberStartRH { get; set; }
            public double ChamberStopRH { get; set; }
        }

        private class ChamberState
        {
            public double ChamberRealT { get; set; }
            public double ChamberRealRH { get; set; } = -1;
            public string ChamberStatus { get; set; }
            public string Error { get; set; }

            public void Update(ChamberState other)
            {
                ChamberRealT = other.ChamberRealT;
                ChamberRealRH = other.ChamberRealRH;
                ChamberStatus = other.ChamberStatus;
                Error = other.Error;
            }
        }

        private class HardwareConfig { }

        private static class IoManager
        {
            public static IHardwareAdaptor Hardware(string com) => new SerialPortHardware(com);
        }

        private static class SinySystem
        {
            public static Dictionary<int, string> SysInteruptDesc { get; } = new Dictionary<int, string>
            {
                { UNFINDHARDWARE, "Hardware not found" },
                { UNCONNECTIONHARDWARE, "Hardware not connected" }
            };
            public static int Interupt { get; set; }
            public static int InterLock { get; set; } = 0;
        }

        private static class HardwareMgr
        {
            public static Dictionary<string, object> ErrorCodeMap { get; } = new Dictionary<string, object>
            {
                {
                    "ESPECTRH", new Dictionary<string, object>
                    {
                        { "E001", new Dictionary<string, object> { { "TYPE", "ERROR" }, { "CN", "通信失败" }, { "EN", "Communication failure" } } },
                        { "E002", new Dictionary<string, object> { { "TYPE", "ERROR" }, { "CN", "未知命令" }, { "EN", "Unknown command" } } }
                    }
                }
            };
            public static class AuxCtrlBoard
            {
                public static void AUXError() { }
            }
        }

        private static class RemoteMgr
        {
            public static void SendAlarm(string type, string message, string code, string hardClass) { }
        }

        private static class NotificationMgr
        {
            public static void Notify(object sender, EventNotification notification) { }
        }

        private static class Utils
        {
            public static string DataTableToJson(DataTable dt) => "{}";
        }

        private static class JsonNewtonsoft
        {
            public static string ToJSON(object obj) => "{}";
        }

        private interface IHardwareAdaptor
        {
            bool Connected { get; }
            void Initialize();
            void UnInitialize();
            void OpenSession();
            void CloseSession();
            void Abort();
            object QueryData(string command);
            Task<HardwareResult> QueryDataAsync(string command, CancellationToken cancellationToken = default);
            object ReadData();
        }

        private interface IHardwareFlow
        {
            void Initialize();
            Task UninitializeAsync();
            Task RiseAsync(object obj, CancellationToken cancellationToken = default);
            void Reset();
            Task DropAsync(DataTable dataTable, CancellationToken cancellationToken = default);
            void StartACQ();
            Task StopACQAsync();
        }
        #endregion
    }

    #region 扩展方法
    public static class DictionaryExtensions
    {
        public static void AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, Dictionary<TKey, TValue> other)
        {
            foreach (var kvp in other)
            {
                dict[kvp.Key] = kvp.Value;
            }
        }
    }
    #endregion
}

中文解释

1. 核心优化点

  • 硬件命令中断:

    • 中断命令:在 StopACQAsync 中调用 m_Hardware.QueryDataAsync("ABORT"),通知硬件停止操作。

    • 关闭串口:SerialPortHardware.Abort() 发送 "ABORT" 并调用 SerialPort.Close(),强制中断阻塞的读写操作。

    • 超时控制:Task.WhenAny 确保查询任务在 2 秒内超时,抛出 OperationCanceledException。

  • 串口通信协议优化:

    • 异步读写:QueryDataAsync 使用 SerialPort.BaseStream.WriteAsync/ReadAsync,支持 CancellationToken。

    • 读取超时:设置 SerialPort.ReadTimeout = 500ms,避免长时间阻塞。

    • 数据包格式:定义响应格式为 T:25.0,RH:50.0,Status:Running,Error:0000\r\n,ParseResponse 方法解析键值对。

  • 可读性和扩展性:

    • 分离逻辑:

      • 硬件操作封装到 SerialPortHardware 和 MockHardware,通过 IHardwareAdaptor 接口支持扩展。

      • 状态管理集中在 UpdateChamberState 等方法,错误处理统一到 HandleException。

    • 注释和日志:为每个关键方法添加详细注释,日志记录时间戳、命令和上下文:

      plaintext

      [XX:XX:XX.XXX] Hardware Chamber1 cancel requested
      [XX:XX:XX.XXX] SerialPort communication aborted
      [XX:XX:XX.XXX] Hardware query (ChamberRead) completed in 498ms
    • 接口扩展:IHardwareAdaptor 支持串口、TCP 或其他协议,只需实现新类(如 TcpHardware)。

  • 模拟硬件:

    • MockHardware 模拟硬件响应,随机生成温度(24-26°C)、湿度(47.5-52.5%),20% 概率返回错误代码 "E001"。

    • 支持 ABORT 命令和随机延迟(300-600ms),便于测试取消和超时场景。

    • 通过 IsMock 属性切换真实/模拟硬件:

      csharp

      m_Hardware = IsMock ? new MockHardware() : IoManager.Hardware(Com);

2. 解决时间差问题的效果

  • 问题根源:

    • QueryData 的 Write 方法阻塞(Thread.Sleep(500) 或串口读写),Task.WhenAll 等待任务完成,导致 StopACQAsync 延迟。

    • UninitializeAsync 可能在查询完成前清空 m_Hardware。

  • 优化效果:

    • 快速中断:

      • Task.WhenAny 确保查询在 2 秒内超时。

      • SerialPortHardware.Abort() 发送 "ABORT" 并关闭串口,强制中断阻塞操作。

    • 安全退出:

      • IsQuerying 标志和 StopACQAsync 的等待逻辑(最多 2 秒)确保查询任务完成后再调用 UninitializeAsync。

    • 异步锁:

      • SemaphoreSlim 替代 lock,支持异步等待和取消。

    • 日志示例:

      plaintext

      [XX:XX:XX.XXX] Hardware Chamber1 cancel requested
      [XX:XX:XX.XXX] SerialPort communication aborted
      [XX:XX:XX.XXX] Waiting for query to complete: 1/20
      [XX:XX:XX.XXX] Hardware Chamber1 CTS disposed

3. 模拟硬件测试

  • MockHardware 功能:

    • 模拟 ChamberRead 返回温度(24-26°C)、湿度(47.5-52.5%)、状态(Running)和错误代码(20% 概率为 "E001")。

    • 模拟 ChamberStatus 返回状态信息。

    • 模拟 CHAMBEROFF 和 ABORT 命令。

    • 随机延迟 300-600ms,模拟真实硬件响应时间。

  • 测试代码:

csharp

var chamber = new EvnChamber
{
    Name = "Chamber1",
    HardClass = "ESPECTRH",
    Com = "COM1",
    IsMock = true // 使用模拟硬件
};

// 启动巡检
chamber.StartACQ();

// 模拟 5 秒后停止
await Task.Delay(5000);
await chamber.StopACQAsync();

// 测试升温
var dataTable = new DataTable();
dataTable.Columns.Add("Param");
dataTable.Columns.Add("Value");
dataTable.Rows.Add("Temp", "25.0");
dataTable.Rows.Add("EnableHum", true);
dataTable.Rows.Add("Humidity", "50.0");
await chamber.RiseAsync(dataTable);
  • 预期日志:

    plaintext

    [XX:XX:XX.XXX] Initializing hardware Chamber1
    [XX:XX:XX.XXX] MockHardware initialized
    [XX:XX:XX.XXX] Hardware query (ChamberRead) completed in 412ms
    [XX:XX:XX.XXX] Task.WhenAll completed in 415ms
    [XX:XX:XX.XXX] Hardware Chamber1 cancel requested
    [XX:XX:XX.XXX] MockHardware communication aborted
    [XX:XX:XX.XXX] Hardware Chamber1 CTS disposed

进一步优化建议

  1. 动态通信协议:

    • 支持可配置的数据包格式:

      csharp

      public class SerialPortHardware
      {
          private readonly string _responseFormat = "T:{0},RH:{1},Status:{2},Error:{3}\r\n";
          // ...
          private HardwareResult ParseResponse(string response)
          {
              // 支持自定义解析逻辑
          }
      }
  2. 错误重试:

    • 为查询失败添加重试机制:

      csharp

      private async Task<HardwareResult> QueryDataAsync(string command, CancellationToken cancellationToken)
      {
          int retryCount = 0;
          while (retryCount < 2)
          {
              try
              {
                  return await QueryDataInternalAsync(command, cancellationToken);
              }
              catch (Exception ex) when (ex is not OperationCanceledException)
              {
                  retryCount++;
                  m_Log.Warn($"[{DateTime.Now:HH:mm:ss.fff}] Query retry {retryCount}/2 for {command}: {ex.Message}");
                  if (retryCount == 2) throw;
                  Initialize();
              }
          }
          return null;
      }
  3. 动态超时:

    • 允许 StartACQ 配置超时:

      csharp

      public void StartACQ(CancellationToken externalToken = default, double timeoutSeconds = 30)
      {
          _cts = CancellationTokenSource.CreateLinkedTokenSource(externalToken);
          _cts.CancelAfter(TimeSpan.FromSeconds(timeoutSeconds));
          // ...
      }
  4. 性能分析:

    • 记录每次查询的详细统计:

      csharp

      private async Task ReadTRHAsync(CancellationToken cancellationToken, string command)
      {
          var metrics = new { Command = command, StartTime = DateTime.Now, DurationMs = 0L };
          var stopwatch = Stopwatch.StartNew();
          try
          {
              // ...
          }
          finally
          {
              stopwatch.Stop();
              metrics = metrics with { DurationMs = stopwatch.ElapsedMilliseconds };
              m_Log.Info($"[{DateTime.Now:HH:mm:ss.fff}] Query metrics: {JsonNewtonsoft.ToJSON(metrics)}");
          }
      }

总结

优化后的代码通过 Task.WhenAny 实现 2 秒超时控制,SerialPortHardware 使用异步读写和中断命令("ABORT" + 关闭串口)解决串口阻塞问题。MockHardware 提供灵活的模拟数据,支持测试取消、超时和错误场景。代码分离硬件操作、状态管理和错误处理,增强可读性和扩展性,日志详细记录操作流程,适用于高效设备的高响应速度需求。

如果你的硬件有特定协议或限制(例如特殊中断命令或数据包格式),请提供更多细节,我将进一步定制解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张工在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值