- 目标命名空间: 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行) - 任务初始化器
- 异步模式不一致: 26处async void、Task.Run嵌套
- 代码重复: TaskInitializer中大量重复的设备初始化逻辑
- 资源管理: 缺少取消令牌、超时配置不统一
- 命名问题: AsbtractClasses拼写错误
- 魔法数字: 大量硬编码延迟时间和参数
影响文件: 所有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秒
}
}
}- ✅ 统一取消令牌支持
- ✅ 消除async void
- ✅ 减少Task.Run嵌套
- ✅ 提供优雅关闭机制
- ✅ 增强错误处理
预估工作量: 16-20小时 技术风险: 高(影响所有设备连接) 建议实施策略:
- 逐个Task类进行重构
- 每个类完成后立即测试
- 保持向后兼容
文件: 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小时 技术风险: 中等 测试建议: 每个管理器单独测试后集成测试
影响文件: 所有Task类
- 重连延迟硬编码(500ms固定)
- 缺少取消令牌支持
- 心跳间隔硬编码(ToolTask: 5000ms)
- 配置分散在各个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小时 技术风险: 低(主要是配置提取和重构)
文件: AsbtractClasses文件夹
- 文件夹名拼写错误:
AsbtractClasses→AbstractClasses
# 1. 重命名文件夹
git mv OperationGuidance_new/Tasks/AsbtractClasses OperationGuidance_new/Tasks/AbstractClasses
# 2. 更新所有命名空间引用
# 使用IDE的"重命名"功能更新所有命名空间预估工作量: 1小时(IDE自动完成) 技术风险: 无(IDE自动更新引用)
影响文件: 所有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小时 技术风险: 低
文件: 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);
}
}
}- 异步方法返回void: 不符合异步编程最佳实践
- while条件错误:
while (ok && tryTimes < tryMaxTimes)应该是while (!ok && tryTimes < tryMaxTimes) - 硬编码魔法数字: 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操作
文件: 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小时 技术风险: 无
影响文件: 新建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小时
影响文件: 所有Task类
- 连接池: 复用Socket连接
- 对象池: 重用消息缓冲区
- 零拷贝: 避免不必要的数据复制
- 批量操作: 合并多个命令
预估工作量: 10-15小时
影响文件: 所有文件
- API文档(XML注释)
- 架构设计文档
- 使用示例
预估工作量: 6-8小时
- Day 1: 任务4 - 修复命名错误 (1小时)
- Day 2: 任务6 - 修复IoBox异步逻辑Bug (3-4小时)
- Day 3-5: 任务1 - 异步模式重构(ToolTask + 基类 + 其他Task类)(18-24小时)
- Day 1-3: 任务2 - 消除代码重复 (10-12小时)
- Day 4-5: 任务5 - 错误处理优化 (4-6小时)
- Day 1-2: 任务3 - 统一资源管理 (5-7小时)
- Day 3: 任务7 - 完善Coordinates (1小时)
- Day 4-5: 任务8 - 单元测试开始 (8-10小时)
- Day 1-5: 任务8 - 单元测试继续 (12-20小时)
- 任务9 - 性能优化 (10-15小时)
- 任务10 - 文档完善 (6-8小时)
- 优先修复IoBox Bug - 任务6涉及while条件错误,可能影响生产,建议优先处理
- 集中完成异步重构 - 任务1风险高,建议连续完成避免部分重构状态
- 增加缓冲时间 - 任务1、2时间估算增加3-5小时缓冲
- 可维护性: 40%+ (消除重复、统一模式)
- 可靠性: 30%+ (完善错误处理、取消令牌)
- 可测试性: 50%+ (async Task、依赖注入)
- CPU使用率: 减少20-30% (减少Task.Run嵌套)
- 内存使用: 减少10-15% (统一资源管理)
- 连接稳定性: 提升50%+ (完善重连机制)
- 新设备支持: 从2天减少到4小时 (统一管理器)
- Bug修复: 减少40% (统一错误处理)
- 新成员上手: 减少50% (完善文档)
风险: 影响所有设备连接,可能导致系统不稳定 缓解策略:
- 分支开发,测试完成后再合并
- 逐个Task类重构,每完成一个立即测试
- 保持旧的Connect方法为obsolete,但保留功能
风险: 重构过程中可能引入逻辑错误 缓解策略:
- 使用IDE的重构工具 (Extract Method)
- 每次重构后运行集成测试
- 分步骤提交,每次只重构一种设备类型
风险: 配置错误或逻辑错误 缓解策略:
- 充分单元测试
- 使用配置验证
- 保持向后兼容
- 0个async void方法
- 100% Task类支持取消令牌
- 重复代码减少80%+
- 所有异常有明确处理策略
- 核心连接逻辑100%覆盖
- 错误处理场景80%+覆盖
- 异步操作正确性验证
- 连接建立时间 < 3秒
- 内存泄漏检测 = 0
- 连接成功率 > 99.9%
修改内容:
-
任务3修正 - 移除关于"统一Socket超时配置"的建议
- 原因:不同Task使用不同超时时间是有意的设计,针对不同设备和场景优化
- 保留:取消令牌支持、配置集中管理、重试策略、资源管理接口等有价值优化
- 调整:保持各Task差异化配置,但通过TaskTimeouts统一管理
-
时间估算调整
- 任务1:16-20h → 18-24h(增加缓冲)
- 任务2:8-10h → 10-12h(增加缓冲)
- 任务6:2-3h → 3-4h(实际测试时间)
- 任务3:6-8h → 5-7h(移除统一超时后工作减少)
-
实施顺序调整
- 第1周:任务4 → 任务6 → 任务1(优先修复IoBox Bug)
- 第2周:任务2 → 任务5(异步重构稳定后进行)
- 第3周:任务3 → 任务7 → 任务8
- 第4周:任务8继续
-
风险评估补充
- 添加Task.Run嵌套消除风险
- 添加取消令牌传播链风险
- 添加泛型约束限制风险
审核人: Claude Code Code-Review-Master 审核结果: APPROVED with minor revisions
文档版本: v1.1 最后更新: 2025-12-05 审核状态: 已审核