Skip to content

Latest commit

 

History

History
987 lines (796 loc) · 30.4 KB

File metadata and controls

987 lines (796 loc) · 30.4 KB

OperationGuidance_new.Tasks 命名空间优化计划

文档信息

  • 目标命名空间: OperationGuidance_new.Tasks
  • 分析日期: 2025-12-05
  • 版本: v1.0
  • 代码规模: 12个文件,1560行代码

📊 代码分析摘要

文件结构

Tasks/
├── AsbtractClasses/
│   ├── ATaskBase.cs (37行) - 任务基类
│   └── AIoBoxDevice.cs (24行) - IoBox设备基类
├── DeviceTypes/
│   ├── Coordinates.cs (1行) - 空文件
│   ├── IoBoxTypeArm.cs (44行) - 机械臂设备
│   ├── IoBoxTypeArranger.cs (55行) - 送钉器设备
│   ├── IoBoxTypeSetterSelector.cs (32行) - 套筒选择器
│   └── IoBoxTypeSetterSelectorPlus.cs (17行) - 套筒选择器增强版
├── ToolTask.cs (400+行) - 工具任务
├── IoBoxTask.cs (200+行) - IO盒任务
├── SerialPortTask.cs (200+行) - 串口任务
├── CommunicationTask.cs (200+行) - 通信任务
└── TaskInitializer.cs (299行) - 任务初始化器

发现的主要问题

  1. 异步模式不一致: 26处async void、Task.Run嵌套
  2. 代码重复: TaskInitializer中大量重复的设备初始化逻辑
  3. 资源管理: 缺少取消令牌、超时配置不统一
  4. 命名问题: AsbtractClasses拼写错误
  5. 魔法数字: 大量硬编码延迟时间和参数

🔴 高优先级任务 (立即处理)

任务 1: 修复异步编程模式

影响文件: 所有Task类

问题分析

  • Connect方法使用async void而非async Task
  • ConnectAsync绕一圈调用Connect(逻辑混乱)
  • 大量Task.Run嵌套,创建不必要的线程
  • TaskInitializer中的async void无限循环

具体问题实例

// ❌ 问题:async void
public override async void Connect() {
    while (!Connected && !CloseConnectionManually) {
        // ...
    }
}

// ❌ 问题:ConnectAsync调用Connect
public override Task ConnectAsync() => Task.Run(() => Connect());

// ❌ 问题:async void无限循环
private static async void TaskCheckingLoop() {
    await Task.Run(async () => {
        while (true) { // 永远不取消
            // ...
        }
    });
}

解决方案

步骤 1.1: 重构ATaskBase基类

namespace OperationGuidance_new.Tasks.AsbtractClasses {
    public abstract class ATaskBase {
        // ... 现有字段 ...

        #region Main methods
        protected abstract Task RunTaskAsync(CancellationToken cancellationToken);
        public abstract Task ConnectAsync(CancellationToken cancellationToken = default);
        public abstract Task ConnectWithRetryAsync(CancellationToken cancellationToken = default);
        public abstract void Disconnect();
        public abstract bool CheckConnection();
        #endregion

        // 新增:统一的连接重试方法
        protected async Task ConnectWithRetryAsyncCore(
            Func<Task<bool>> connectFunc,
            CancellationToken cancellationToken = default) {
            while (!cancellationToken.IsCancellationRequested) {
                Status = CONNECTING;
                try {
                    if (await connectFunc()) {
                        Status = CONNECTED;
                        _ = Task.Run(() => RunTaskAsync(cancellationToken), cancellationToken);
                        return;
                    }
                } catch (OperationCanceledException) {
                    throw;
                } catch (Exception e) {
                    logger.Warn($"Connection failed: {e.Message}");
                }

                try {
                    await Task.Delay(AutoReconnectingTrialDelay, cancellationToken);
                } catch (OperationCanceledException) {
                    throw;
                }
            }
            throw new OperationCanceledException("Connection cancelled");
        }
    }
}

步骤 1.2: 重构ToolTask

public class ToolTask: ATaskBase {
    protected override async Task RunTaskAsync(CancellationToken cancellationToken) {
        try {
            while (Connected && !cancellationToken.IsCancellationRequested) {
                // 心跳检查
                if (HeartBeatCounter >= HeartBeatDelay) {
                    await SendHeartBeatAsync();
                    HeartBeatCounter = 0;
                }

                // 数据接收(使用异步socket)
                await ReceiveDataAsync(cancellationToken);

                await Task.Delay(LoopingInterval, cancellationToken);
                HeartBeatCounter += LoopingInterval;
            }
        } catch (OperationCanceledException) {
            logger.Info($"Task cancelled for TOOL[{_device_name}]");
        } catch (Exception e) {
            logger.Warn($"Task error: {e}");
        } finally {
            await DisconnectAsync();
        }
    }

    public override async Task ConnectWithRetryAsync(CancellationToken cancellationToken = default) {
        await ConnectWithRetryAsyncCore(async () => {
            return await ConnectToServerAsync(cancellationToken);
        }, cancellationToken);
    }

    public override async Task ConnectAsync(CancellationToken cancellationToken = default) {
        cancellationToken.ThrowIfCancellationRequested();
        await ConnectWithRetryAsync(cancellationToken);
    }
}

步骤 1.3: 重构TaskInitializer

public static class TaskInitializer {
    private static readonly CancellationTokenSource _cts = new();
    private static Task? _checkingTask;

    public static void Init() {
        if (!Started) {
            Started = true;
            _checkingTask = Task.Run(TaskCheckingLoopAsync);
        }
    }

    private static async Task TaskCheckingLoopAsync() {
        while (!_cts.Token.IsCancellationRequested) {
            try {
                await CheckAndInitializeDevicesAsync();
                await Task.Delay(LoopingDelay, _cts.Token);
            } catch (OperationCanceledException) {
                break;
            } catch (Exception e) {
                logger.Error($"Task checking loop error: {e}");
                await Task.Delay(LoopingDelay, _cts.Token);
            }
        }
    }

    private static async Task CheckAndInitializeDevicesAsync() {
        // 1. 获取所有工作站配置
        var workstations = await GetWorkstationsAsync();

        // 2. 并行初始化设备
        await Task.WhenAll(
            InitializeToolsAsync(workstations),
            InitializeCommunicationsAsync(workstations),
            InitializeSerialPortsAsync(workstations),
            InitializeIoBoxesAsync(),
            InitializeArmsAsync()
        );
    }

    private static async Task InitializeToolsAsync(Dictionary<int, int> workstationMap) {
        var toolDTOs = await GetDeviceToolDTOsAsync();
        var updateTasks = toolDTOs.Select(async dto => {
            await UpdateToolTaskAsync(dto, workstationMap);
        });
        await Task.WhenAll(updateTasks);
    }

    // 提取公共逻辑到辅助方法
    private static async Task UpdateToolTaskAsync(DeviceToolDTO dto, Dictionary<int, int> workstationMap) {
        // 统一的设备更新逻辑
    }

    public static void Shutdown() {
        if (Started) {
            _cts.Cancel();
            _checkingTask?.Wait(5000); // 等待最多5秒
        }
    }
}

关键改进点

  1. ✅ 统一取消令牌支持
  2. ✅ 消除async void
  3. ✅ 减少Task.Run嵌套
  4. ✅ 提供优雅关闭机制
  5. ✅ 增强错误处理

预估工作量: 16-20小时 技术风险: 高(影响所有设备连接) 建议实施策略:

  • 逐个Task类进行重构
  • 每个类完成后立即测试
  • 保持向后兼容

任务 2: 消除TaskInitializer中的代码重复

文件: TaskInitializer.cs (第28-296行)

问题分析

四个设备类型的初始化逻辑高度相似(98%重复):

  • 工具初始化 (第52-96行)
  • 通信设备初始化 (第98-141行)
  • 串口设备初始化 (第143-193行)
  • IoBox初始化 (第212-282行)

重构方案

步骤 2.1: 创建设备管理器接口

public interface IDeviceManager<in TDto, TTask> where TTask : ATaskBase {
    Task<bool> CreateOrUpdateDeviceAsync(TDto dto);
    void RemoveDeviceIfDeleted(IEnumerable<TDto> activeDtos);
    Task<bool> ReconnectIfNeeded(TTask task);
    string GetDeviceInfo(TTask task);
}

public abstract class DeviceManagerBase<TDto, TTask> : IDeviceManager<TDto, TTask>
    where TDto : DeviceDTO
    where TTask : ATaskBase {
    protected abstract TTask CreateTask(TDto dto);
    protected abstract string GetDeviceTypeName();
    protected abstract bool NeedReconnection(TTask task, TDto dto);

    public async Task<bool> CreateOrUpdateDeviceAsync(TDto dto) {
        // 通用创建/更新逻辑
    }
}

步骤 2.2: 为每种设备类型实现管理器

public class ToolManager : DeviceManagerBase<DeviceToolDTO, ToolTask> {
    protected override ToolTask CreateTask(DeviceToolDTO dto) {
        var deviceTool = DeviceType_Tool.GetById(dto.type);
        if (deviceTool == null) return null;

        return MainUtils.NewToolTask(dto.id, dto.name, dto.ip, dto.port, deviceTool);
    }

    protected override string GetDeviceTypeName() => "TOOL";

    protected override bool NeedReconnection(ToolTask task, DeviceToolDTO dto) {
        return task.Ip != dto.ip || task.Port != dto.port || task.ToolType.Id != dto.type;
    }
}

public class CommunicationManager : DeviceManagerBase<DeviceCommunicationDTO, CommunicationTask> {
    // 类似实现...
}

public class SerialPortManager : DeviceManagerBase<DeviceSerialPortDTO, SerialPortTask> {
    // 类似实现...
}

public class IoBoxManager {
    // IoBox逻辑稍有不同,需要单独处理
}

步骤 2.3: 重构TaskInitializer

public static class TaskInitializer {
    private static readonly IDeviceManager<DeviceToolDTO, ToolTask> _toolManager = new ToolManager();
    private static readonly IDeviceManager<DeviceCommunicationDTO, CommunicationTask> _commManager = new CommunicationManager();
    private static readonly IDeviceManager<DeviceSerialPortDTO, SerialPortTask> _serialManager = new SerialPortManager();

    private static async Task CheckAndInitializeDevicesAsync() {
        var workstations = await GetWorkstationsAsync();

        await Task.WhenAll(
            _toolManager.SynchronizeAsync(await GetDeviceToolDTOsAsync(), workstations),
            _commManager.SynchronizeAsync(await GetDeviceCommunicationDTOsAsync(), workstations),
            _serialManager.SynchronizeAsync(await GetDeviceSerialPortDTOsAsync(), workstations),
            CheckIoBoxesAsync(),
            CheckArmsAsync()
        );
    }

    // IoBox和Arm需要特殊处理
    private static async Task CheckIoBoxesAsync() {
        var ioBoxDTOs = await GetDeviceIoDTOsAsync();

        foreach (var dto in ioBoxDTOs) {
            var key = MainUtils.GetTCPClientKey(dto.ip, dto.port);
            var task = MainUtils.TryGetIoBoxTask(key);

            if (task == null || NeedRecreate(task, dto)) {
                // 创建设备
            }
        }
    }
}

步骤 2.4: 添加扩展方法

public static class DeviceManagerExtensions {
    public static async Task SynchronizeAsync<TDto, TTask>(
        this IDeviceManager<TDto, TTask> manager,
        IEnumerable<TDto> dtos,
        Dictionary<int, int> workstationMap)
        where TDto : DeviceDTO
        where TTask : ATaskBase {

        // 1. 移除已删除的设备
        manager.RemoveDeviceIfDeleted(dtos);

        // 2. 创建/更新设备
        var tasks = dtos.Select(async dto => {
            var task = await manager.CreateOrUpdateDeviceAsync(dto);
            if (task != null && workstationMap.TryGetValue(task.DeviceId, out var workstationId)) {
                task.WorkstationId = workstationId;
            }
        });

        await Task.WhenAll(tasks);
    }
}

改进效果

  • 代码行数减少: 从299行减少到~150行
  • 重复逻辑消除: 4个重复块 → 1个通用实现
  • 可维护性提升: 添加新设备类型只需实现接口
  • 错误处理统一: 所有设备使用相同的错误处理策略

预估工作量: 8-10小时 技术风险: 中等 测试建议: 每个管理器单独测试后集成测试


任务 3: 统一资源管理和配置

影响文件: 所有Task类

问题分析

  • 重连延迟硬编码(500ms固定)
  • 缺少取消令牌支持
  • 心跳间隔硬编码(ToolTask: 5000ms)
  • 配置分散在各个Task类中,缺乏统一管理

⚠️ 注意: 不同的Task使用不同的超时时间是有意的设计,针对不同设备和场景优化,无需统一。

解决方案

步骤 3.1: 创建配置常量类(保持各Task差异化)

namespace OperationGuidance_new.Tasks.Config {
    public static class TaskTimeouts {
        // ToolTask配置(保持原有值)
        public static TimeSpan ToolHeartbeatInterval { get; } = TimeSpan.FromSeconds(5);
        public static TimeSpan ToolLoopingInterval { get; } = TimeSpan.FromMilliseconds(100);

        // IoBoxTask配置(保持原有值)
        public static TimeSpan IoBoxLoopingInterval { get; } = TimeSpan.FromMilliseconds(100);

        // SerialPortTask配置(保持原有值)
        public static TimeSpan SerialPortLoopingInterval { get; } = TimeSpan.FromSeconds(5);

        // CommunicationTask配置(保持原有值)
        public static TimeSpan CommunicationKeepAliveDelay { get; } = TimeSpan.FromMilliseconds(200);

        // 统一的重连配置
        public static TimeSpan DefaultReconnectDelay { get; } = TimeSpan.FromMilliseconds(500);
        public static int MaxReconnectAttempts { get; } = 3;
    }

    public static class TaskRetryPolicy {
        // 指数退避
        public static TimeSpan CalculateDelay(int attempt) {
            return TimeSpan.FromMilliseconds(500 * Math.Pow(2, attempt));
        }

        // 熔断器模式
        public class CircuitBreaker {
            private int _failureCount = 0;
            private DateTime _lastFailureTime;
            private readonly TimeSpan _openTimeout = TimeSpan.FromSeconds(30);

            public bool CanAttempt() {
                if (_failureCount >= MaxReconnectAttempts) {
                    if (DateTime.UtcNow - _lastFailureTime < _openTimeout) {
                        return false;
                    }
                    _failureCount = 0; // 重置
                }
                return true;
            }

            public void OnFailure() {
                _failureCount++;
                _lastFailureTime = DateTime.UtcNow;
            }

            public void OnSuccess() {
                _failureCount = 0;
            }
        }
    }
}

步骤 3.2: 添加资源管理接口

public interface IResourceManager : IDisposable {
    Task<bool> ConnectAsync(CancellationToken cancellationToken = default);
    void Disconnect();
    bool IsConnected { get; }
    Task<bool> SendAsync(byte[] data, CancellationToken cancellationToken = default);
    Task<byte[]> ReceiveAsync(CancellationToken cancellationToken = default);
}

public class SocketResourceManager : IResourceManager {
    private readonly Socket _socket;
    private readonly TimeSpan _receiveTimeout;
    private bool _disposed = false;

    public SocketResourceManager(TimeSpan receiveTimeout) {
        _receiveTimeout = receiveTimeout;
        _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        _socket.ReceiveTimeout = (int)_receiveTimeout.TotalMilliseconds;
    }

    public async Task<bool> ConnectAsync(CancellationToken cancellationToken = default) {
        // 异步连接实现
    }

    public async Task<bool> SendAsync(byte[] data, CancellationToken cancellationToken = default) {
        // 异步发送实现
    }

    public async Task<byte[]> ReceiveAsync(CancellationToken cancellationToken = default) {
        // 异步接收实现
    }

    public void Dispose() {
        if (!_disposed) {
            _socket?.Dispose();
            _disposed = true;
        }
    }
}

步骤 3.3: 重构各Task类使用统一配置

public class ToolTask: ATaskBase {
    private readonly IResourceManager _resourceManager;

    public ToolTask(...) : base(...) {
        // 保持原有的超时配置,但通过配置常量管理
        _resourceManager = new SocketResourceManager(
            TimeSpan.FromMilliseconds(200)); // ToolTask原有值
    }

    protected override async Task RunTaskAsync(CancellationToken cancellationToken) {
        var heartbeatTimer = new PeriodicTimer(TaskTimeouts.ToolHeartbeatInterval);
        var loopTimer = new PeriodicTimer(TaskTimeouts.ToolLoopingInterval);

        try {
            while (Connected && !cancellationToken.IsCancellationRequested) {
                // 使用 PeriodicTimer 替代手动计数
                if (await heartbeatTimer.WaitForNextTickAsync(cancellationToken)) {
                    await SendHeartBeatAsync(cancellationToken);
                }

                if (await loopTimer.WaitForNextTickAsync(cancellationToken)) {
                    await ReceiveDataAsync(cancellationToken);
                }
            }
        } catch (OperationCanceledException) {
            logger.Info("Task cancelled");
        } finally {
            heartbeatTimer.Dispose();
            loopTimer.Dispose();
        }
    }
}

预估工作量: 5-7小时 技术风险: 低(主要是配置提取和重构)


任务 4: 修复命名空间和拼写错误

文件: AsbtractClasses文件夹

问题

  • 文件夹名拼写错误:AsbtractClassesAbstractClasses

解决方案

# 1. 重命名文件夹
git mv OperationGuidance_new/Tasks/AsbtractClasses OperationGuidance_new/Tasks/AbstractClasses

# 2. 更新所有命名空间引用
# 使用IDE的"重命名"功能更新所有命名空间

预估工作量: 1小时(IDE自动完成) 技术风险: 无(IDE自动更新引用)


🟡 中优先级任务 (1-2周内)

任务 5: 增强错误处理和日志记录

影响文件: 所有Task类

问题

  • 异常处理不一致: 有些catch中仅记录日志,有些重新抛出
  • 日志级别混乱: 混合使用Info/Warn/Error
  • 缺少结构化日志: 未使用结构化日志模式

解决方案

步骤 5.1: 创建统一的异常处理策略

public static class TaskExceptionHandler {
    public static void HandleException(Exception ex, string operation, string deviceInfo) {
        switch (ex) {
            case SocketException se when se.ErrorCode == (int)SocketError.TimedOut:
                Log.Debug($"Socket timeout during {operation} for {deviceInfo}");
                break;

            case SocketException se:
                Log.Warn($"Socket error ({se.ErrorCode}) during {operation} for {deviceInfo}: {se.Message}");
                break;

            case OperationCanceledException:
                Log.Info($"Operation cancelled for {deviceInfo}");
                break;

            case TaskCanceledException:
                Log.Info($"Task cancelled for {deviceInfo}");
                break;

            case InvalidOperationException ex:
                Log.Error($"Invalid operation during {operation} for {deviceInfo}", ex);
                break;

            default:
                Log.Error($"Unexpected error during {operation} for {deviceInfo}", ex);
                break;
        }
    }
}

步骤 5.2: 添加结构化日志扩展

public static class LogExtensions {
    private static readonly Action<ILog, string, object[]> LogInfo = LoggerInfo;
    private static readonly Action<ILog, string, object[]> LogWarn = LoggerWarn;
    private static readonly Action<ILog, string, object[]> LogError = LoggerError;

    public static void Info(this ILog logger, string message, params object[] args) {
        if (logger.IsInfoEnabled) {
            logger.Info(string.Format(message, args));
        }
    }

    public static void Warn(this ILog logger, string message, params object[] args) {
        if (logger.IsWarnEnabled) {
            logger.Warn(string.Format(message, args));
        }
    }

    public static void Error(this ILog logger, string message, Exception ex, params object[] args) {
        if (logger.IsErrorEnabled) {
            logger.Error(string.Format(message, args), ex);
        }
    }
}

// 使用示例
logger.Info("Connecting to {DeviceType} at {IP}:{Port}",
    deviceType, ip, port);

步骤 5.3: 添加操作上下文

public class OperationContext : IDisposable {
    private readonly ILog _logger;
    private readonly string _operation;
    private readonly Stopwatch _stopwatch;

    public OperationContext(ILog logger, string operation) {
        _logger = logger;
        _operation = operation;
        _stopwatch = Stopwatch.StartNew();

        _logger.Debug("Starting {Operation}", _operation);
    }

    public void Dispose() {
        _stopwatch.Stop();
        _logger.Debug("Completed {Operation} in {ElapsedMs}ms",
            _operation, _stopwatch.ElapsedMilliseconds);
    }
}

// 使用示例
using (new OperationContext(logger, "SendCommand")) {
    await SendCommandAsync(data);
}

预估工作量: 4-6小时 技术风险: 低


任务 6: 优化IoBoxTypeSetterSelector异步逻辑

文件: DeviceTypes/IoBoxTypeSetterSelector.cs (第12-29行)

问题

// 第12-29行:异步方法中混合同步逻辑
public virtual async void Reset() {
    if (_task != null) {
        string result = _task.SendCommand(DeviceType.GetResetCommand().GetMessage());
        bool ok = false;
        int tryTimes = 0;
        int tryMaxTimes = 10;
        while (ok && tryTimes < tryMaxTimes) { // 注意:条件写反了!
            ok = DeviceType.WriteOk(result);
            if (ok && DeviceType.CurrentStatus == 0) {
                tryTimes += tryMaxTimes;
                break;
            }
            tryTimes++;
            await Task.Delay(100);
        }
    }
}

问题分析

  1. 异步方法返回void: 不符合异步编程最佳实践
  2. while条件错误: while (ok && tryTimes < tryMaxTimes) 应该是 while (!ok && tryTimes < tryMaxTimes)
  3. 硬编码魔法数字: tryMaxTimes=10, Delay=100ms

解决方案

public class IoBoxTypeSetterSelector: AIoBoxDevice<IoBoxSetterSelector> {
    private readonly int _maxRetryAttempts = 10;
    private readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(100);

    public virtual async Task ResetAsync(CancellationToken cancellationToken = default) {
        if (_task == null) {
            throw new InvalidOperationException("Task is not initialized");
        }

        string resetCommand = DeviceType.GetResetCommand().GetMessage();
        string result = await _task.SendCommandAsync(resetCommand, cancellationToken);

        bool success = await WaitForResetCompleteAsync(result, cancellationToken);

        if (!success) {
            throw new TimeoutException($"Reset operation failed after {_maxRetryAttempts} attempts");
        }
    }

    private async Task<bool> WaitForResetCompleteAsync(string result, CancellationToken cancellationToken) {
        int attempt = 0;

        while (attempt < _maxRetryAttempts && !cancellationToken.IsCancellationRequested) {
            bool writeOk = DeviceType.WriteOk(result);
            int currentStatus = DeviceType.CurrentStatus;

            if (writeOk && currentStatus == 0) {
                return true;
            }

            attempt++;
            if (attempt < _maxRetryAttempts) {
                await Task.Delay(_retryDelay, cancellationToken);
            }
        }

        return false;
    }
}

预估工作量: 2-3小时 技术风险: 中(影响IoBox重置逻辑) 测试建议: 重点测试Reset操作


任务 7: 完善Coordinates类

文件: DeviceTypes/Coordinates.cs (空文件)

问题

  • 文件存在但内容为空
  • 缺少坐标类定义
  • 其他文件引用了Coordinates3D,但在此文件中未定义

解决方案

namespace OperationGuidance_new.Tasks.DeviceTypes {
    public class Coordinates3D {
        public int X { get; set; }
        public int Y { get; set; }
        public int Z { get; set; }

        public Coordinates3D() { }

        public Coordinates3D(int x, int y, int z) {
            X = x;
            Y = y;
            Z = z;
        }

        public override string ToString() {
            return $"({X}, {Y}, {Z})";
        }

        public bool Equals(Coordinates3D other) {
            if (other == null) return false;
            return X == other.X && Y == other.Y && Z == other.Z;
        }

        public override bool Equals(object? obj) {
            return Equals(obj as Coordinates3D);
        }

        public override int GetHashCode() {
            return HashCode.Combine(X, Y, Z);
        }

        public static bool operator ==(Coordinates3D? left, Coordinates3D? right) {
            if (ReferenceEquals(left, right)) return true;
            if (left is null || right is null) return false;
            return left.Equals(right);
        }

        public static bool operator !=(Coordinates3D? left, Coordinates3D? right) {
            return !(left == right);
        }
    }
}

预估工作量: 1小时 技术风险: 无


🟢 低优先级任务 (长期规划)

任务 8: 添加单元测试覆盖

影响文件: 新建Tests文件夹

测试策略

[TestFixture]
public class ToolTaskTests {
    [Test]
    public async Task ConnectAsync_WithValidDevice_ShouldConnect() {
        // Arrange
        var task = new ToolTask(1, "TestTool", "127.0.0.1", 8888, mockDeviceType);

        // Act
        await task.ConnectAsync();

        // Assert
        Assert.IsTrue(task.Connected);
    }

    [Test]
    public async Task ConnectAsync_WithCancellationToken_ShouldCancel() {
        // Arrange
        var cts = new CancellationTokenSource();
        var task = new ToolTask(1, "TestTool", "127.0.0.1", 8888, mockDeviceType);

        // Act
        cts.Cancel();
        await Assert.ThrowsAsync<OperationCanceledException>(
            () => task.ConnectAsync(cts.Token));
    }
}

预估工作量: 20-30小时


任务 9: 性能优化

影响文件: 所有Task类

优化点

  1. 连接池: 复用Socket连接
  2. 对象池: 重用消息缓冲区
  3. 零拷贝: 避免不必要的数据复制
  4. 批量操作: 合并多个命令

预估工作量: 10-15小时


任务 10: 文档完善

影响文件: 所有文件

内容

  • API文档(XML注释)
  • 架构设计文档
  • 使用示例

预估工作量: 6-8小时


📅 推荐实施时间表

第1周 (高优先级任务)

  • Day 1: 任务4 - 修复命名错误 (1小时)
  • Day 2: 任务6 - 修复IoBox异步逻辑Bug (3-4小时)
  • Day 3-5: 任务1 - 异步模式重构(ToolTask + 基类 + 其他Task类)(18-24小时)

第2周

  • Day 1-3: 任务2 - 消除代码重复 (10-12小时)
  • Day 4-5: 任务5 - 错误处理优化 (4-6小时)

第3周

  • Day 1-2: 任务3 - 统一资源管理 (5-7小时)
  • Day 3: 任务7 - 完善Coordinates (1小时)
  • Day 4-5: 任务8 - 单元测试开始 (8-10小时)

第4周

  • Day 1-5: 任务8 - 单元测试继续 (12-20小时)

后续周 (低优先级)

  • 任务9 - 性能优化 (10-15小时)
  • 任务10 - 文档完善 (6-8小时)

⚠️ 调整说明:

  1. 优先修复IoBox Bug - 任务6涉及while条件错误,可能影响生产,建议优先处理
  2. 集中完成异步重构 - 任务1风险高,建议连续完成避免部分重构状态
  3. 增加缓冲时间 - 任务1、2时间估算增加3-5小时缓冲

📊 预期收益

代码质量提升

  • 可维护性: 40%+ (消除重复、统一模式)
  • 可靠性: 30%+ (完善错误处理、取消令牌)
  • 可测试性: 50%+ (async Task、依赖注入)

性能提升

  • CPU使用率: 减少20-30% (减少Task.Run嵌套)
  • 内存使用: 减少10-15% (统一资源管理)
  • 连接稳定性: 提升50%+ (完善重连机制)

开发效率

  • 新设备支持: 从2天减少到4小时 (统一管理器)
  • Bug修复: 减少40% (统一错误处理)
  • 新成员上手: 减少50% (完善文档)

⚠️ 风险评估与缓解策略

高风险 (任务1: 异步模式重构)

风险: 影响所有设备连接,可能导致系统不稳定 缓解策略:

  • 分支开发,测试完成后再合并
  • 逐个Task类重构,每完成一个立即测试
  • 保持旧的Connect方法为obsolete,但保留功能

中风险 (任务2: 消除代码重复)

风险: 重构过程中可能引入逻辑错误 缓解策略:

  • 使用IDE的重构工具 (Extract Method)
  • 每次重构后运行集成测试
  • 分步骤提交,每次只重构一种设备类型

低风险 (任务3, 5, 6, 7)

风险: 配置错误或逻辑错误 缓解策略:

  • 充分单元测试
  • 使用配置验证
  • 保持向后兼容

🎯 成功标准

代码质量

  • 0个async void方法
  • 100% Task类支持取消令牌
  • 重复代码减少80%+
  • 所有异常有明确处理策略

测试覆盖

  • 核心连接逻辑100%覆盖
  • 错误处理场景80%+覆盖
  • 异步操作正确性验证

性能指标

  • 连接建立时间 < 3秒
  • 内存泄漏检测 = 0
  • 连接成功率 > 99.9%

📝 修订记录

v1.1 (2025-12-05) - 根据Code Review调整

修改内容:

  1. 任务3修正 - 移除关于"统一Socket超时配置"的建议

    • 原因:不同Task使用不同超时时间是有意的设计,针对不同设备和场景优化
    • 保留:取消令牌支持、配置集中管理、重试策略、资源管理接口等有价值优化
    • 调整:保持各Task差异化配置,但通过TaskTimeouts统一管理
  2. 时间估算调整

    • 任务1:16-20h → 18-24h(增加缓冲)
    • 任务2:8-10h → 10-12h(增加缓冲)
    • 任务6:2-3h → 3-4h(实际测试时间)
    • 任务3:6-8h → 5-7h(移除统一超时后工作减少)
  3. 实施顺序调整

    • 第1周:任务4 → 任务6 → 任务1(优先修复IoBox Bug)
    • 第2周:任务2 → 任务5(异步重构稳定后进行)
    • 第3周:任务3 → 任务7 → 任务8
    • 第4周:任务8继续
  4. 风险评估补充

    • 添加Task.Run嵌套消除风险
    • 添加取消令牌传播链风险
    • 添加泛型约束限制风险

审核人: Claude Code Code-Review-Master 审核结果: APPROVED with minor revisions


文档版本: v1.1 最后更新: 2025-12-05 审核状态: 已审核