Browse Source

fix: ui卡死问题优化
1. 使用task重构测试逻辑
2. 测试报表生成优化
3. 新测试界面优化

kindring 1 week ago
parent
commit
0bec6ac8da
4 changed files with 357 additions and 186 deletions
  1. 1 0
      bird_tool/ProgressPanel.cs
  2. 339 182
      bird_tool/TestEngine.cs
  3. 14 4
      bird_tool/bird_tool.cs
  4. 3 0
      bird_tool/bird_tool.csproj

+ 1 - 0
bird_tool/ProgressPanel.cs

@@ -412,6 +412,7 @@ namespace Bird_tool
             });
             });
         }
         }
 
 
+        // 测试面板重置
 
 
         // 重置测试面板
         // 重置测试面板
         public void ResetPanel()
         public void ResetPanel()

+ 339 - 182
bird_tool/TestEngine.cs

@@ -10,6 +10,7 @@ using System.Text;
 using System.Text.RegularExpressions;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using System.Threading.Channels;
 
 
 namespace Bird_tool
 namespace Bird_tool
 {
 {
@@ -36,6 +37,17 @@ namespace Bird_tool
                 DeviceInfoItems[key] = value;
                 DeviceInfoItems[key] = value;
             }
             }
         }
         }
+        // 新增:获取设备信息的键值对
+        public IEnumerable<KeyValuePair<string, string>> GetDeviceInfoPairs()
+        {
+            return DeviceInfoItems;
+        }
+
+        // 新增:获取测试项结果
+        public IEnumerable<TestReportItem> GetTestItems()
+        {
+            return Items;
+        }
     }
     }
 
 
     public class TestReportItem
     public class TestReportItem
@@ -123,7 +135,6 @@ namespace Bird_tool
         }
         }
 
 
 
 
-
     }
     }
 
 
 
 
@@ -272,8 +283,21 @@ namespace Bird_tool
         public bool IsRunning { get; set; }
         public bool IsRunning { get; set; }
     }
     }
 
 
+    public static class TaskExtensions
+    {
+        public static async Task<T> WaitAsync<T>(this Task<T> task, CancellationToken cancellationToken)
+        {
+            var tcs = new TaskCompletionSource<T>();
+            using (cancellationToken.Register(() => tcs.TrySetCanceled()))
+            {
+                return await await Task.WhenAny(task, tcs.Task);
+            }
+        }
+    }
+
     class TestExecutor
     class TestExecutor
     {
     {
+
         public delegate void LogHandler(string message, LogLevel level);
         public delegate void LogHandler(string message, LogLevel level);
         public delegate void LogShowHandler(string message);
         public delegate void LogShowHandler(string message);
 
 
@@ -283,6 +307,8 @@ namespace Bird_tool
         public delegate void TestFailHandler(TestStepConfig step, TestContext context);
         public delegate void TestFailHandler(TestStepConfig step, TestContext context);
         public delegate void TestSuccessHandler(TestContext context);
         public delegate void TestSuccessHandler(TestContext context);
 
 
+       
+
 
 
         public event LogHandler OnLog;
         public event LogHandler OnLog;
         public event LogShowHandler OnLogShow;
         public event LogShowHandler OnLogShow;
@@ -298,63 +324,115 @@ namespace Bird_tool
         // 步骤表
         // 步骤表
         private List<TestStepConfig> _step_map;
         private List<TestStepConfig> _step_map;
 
 
+        private readonly Channel<Func<CancellationToken, Task>> _taskQueue;
+        private CancellationTokenSource _cts = new CancellationTokenSource();
+        private Task _queueProcessor;
+
         public bool isStart { get; private set; } = false;
         public bool isStart { get; private set; } = false;
         public bool isInit { get; private set; } = false;
         public bool isInit { get; private set; } = false;
+        public bool enableMakeResult { get; private set; } = false;
 
 
         public string ReportFileNameTemplate { get; set; } = "TestReport_{{mac}}_{{timestamp}}";
         public string ReportFileNameTemplate { get; set; } = "TestReport_{{mac}}_{{timestamp}}";
+        // 新增:CSV文件路径
+        private string _csvReportPath = "TestReport/report.csv";
+        private static readonly object _csvLock = new object();
 
 
         public TestExecutor(SerialManager serialManager)
         public TestExecutor(SerialManager serialManager)
         {
         {
             bird_tool.Log("TestExecutor ");
             bird_tool.Log("TestExecutor ");
             _serialManager = serialManager;
             _serialManager = serialManager;
-            _serialManager.OnLineReceived += HandleResponse;
+            _taskQueue = Channel.CreateBounded<Func<CancellationToken, Task>>(1000);
 
 
+            StartQueueProcessor();
         }
         }
 
 
-        // 新增方法:将重试操作加入队列异步执行
-        private void QueueRetryOperation(Action operation)
+        private void StartQueueProcessor()
         {
         {
-            OnLog?.Invoke($"排队重试操作: {operation.Method.Name}", LogLevel.debug);
-            Task.Run(() =>
+            // 确保之前的队列处理器已停止
+            if (_queueProcessor != null && !_queueProcessor.IsCompleted)
             {
             {
-                OnLog?.Invoke($"开始执行排队操作1: {operation.Method.Name}", LogLevel.debug);
-                // 添加短暂延迟避免立即重试
-                Thread.Sleep(100);
-                
-                lock (_lock)
+                _queueProcessor.Dispose();
+            }
+
+            _queueProcessor = Task.Run(async () =>
+            {
+                while (!_cts.Token.IsCancellationRequested &&
+                       await _taskQueue.Reader.WaitToReadAsync(_cts.Token))
                 {
                 {
-                    // 检查测试是否仍在运行
-                    if (_context != null && _context.IsRunning)
+                    if (_taskQueue.Reader.TryRead(out var taskFunc))
                     {
                     {
-                        OnLog?.Invoke($"开始执行排队操作2: {operation.Method.Name}", LogLevel.debug);
-                        operation();
+                        try
+                        {
+                            await taskFunc(_cts.Token);
+                        }
+                        catch (OperationCanceledException)
+                        {
+                            // 正常取消
+                        }
+                        catch (Exception ex)
+                        {
+                            OnLog?.Invoke($"任务执行失败: {ex.Message}", LogLevel.error);
+                            OnLogShow?.Invoke($"任务执行失败: {ex.Message}");
+                        }
+                        
                     }
                     }
                 }
                 }
             });
             });
         }
         }
 
 
-        
-        public void StopTest()
+        private void EnqueueTask(Func<CancellationToken, Task> task)
         {
         {
-            
-            Thread.Sleep(2000);
+            if (!_taskQueue.Writer.TryWrite(task))
+            {
+                OnLog?.Invoke("任务队列已满,无法添加新任务", LogLevel.error);
+            }
+        }
 
 
-            lock (_lock)
+        public async Task StopTestAsync(bool waitForCompletion = false)
+        {
+            try
             {
             {
-                
-                if (isStart)
-                {
-                    isStart = false;
-                    // 确保释放计时器资源
-                    _timeoutTimer?.Dispose();
-                    _timeoutTimer = null;
+                // 取消所有任务
+                _cts.Cancel();
 
 
-                    _context.IsRunning = false;
-                    _context = null;
+                // 等待队列处理完成(如果需要)
+                if (waitForCompletion && _queueProcessor != null && !_queueProcessor.IsCompleted)
+                {
+                    try
+                    {
+                        await _queueProcessor;
+                    }
+                    catch (OperationCanceledException)
+                    {
+                        // 正常取消
+                    }
                 }
                 }
-                
+            }
+            catch (Exception ex)
+            {
+                OnLog?.Invoke($"停止测试时出错: {ex.Message}", LogLevel.error);
+            }
+            finally
+            {
+                // 清理资源
+                if (_serialManager != null)
+                {
+                    _serialManager.OnLineReceived -= HandleResponse;
+                }
+
+                // 重置状态
+                _context = null;
+                isStart = false;
+
+                // 重新创建 CTS 以便下次使用
+                _cts.Dispose();
+                _cts = new CancellationTokenSource();
+
+                // 重新启动队列处理器
+                StartQueueProcessor();
             }
             }
         }
         }
+
         private bool StartTestWithSteps(List<TestStepConfig> steps)
         private bool StartTestWithSteps(List<TestStepConfig> steps)
         {
         {
             lock (_lock)
             lock (_lock)
@@ -370,16 +448,22 @@ namespace Bird_tool
                     step.stepStatus = StepStatus.NotRun;
                     step.stepStatus = StepStatus.NotRun;
                     step.RetryCount = 0;
                     step.RetryCount = 0;
                 }
                 }
-                // 初始化上下文
-                _context = new TestContext
+                _serialManager.OnLineReceived += HandleResponse;
+                EnqueueTask(async ct =>
                 {
                 {
-                    Steps = steps, // 使用传入的步骤列表
-                    CurrentStepIndex = -1
-                };
-                
-                isStart = true;
+                    // 初始化上下文
+                    _context = new TestContext
+                    {
+                        Steps = steps, // 使用传入的步骤列表
+                        CurrentStepIndex = -1,
+                        Report = new TestReport(),
+                        IsRunning = true,
+                    };
+                    isStart = true;
+                    MoveToNextStep();
+
+                });
 
 
-                MoveToNextStep();
                 return true;
                 return true;
             }
             }
         }
         }
@@ -392,6 +476,7 @@ namespace Bird_tool
                 {
                 {
                     return false;
                     return false;
                 }
                 }
+                enableMakeResult = true;
                 StartTestWithSteps(_step_map);
                 StartTestWithSteps(_step_map);
                 return true;
                 return true;
             }
             }
@@ -420,6 +505,7 @@ namespace Bird_tool
                     step.stepStatus = StepStatus.NotRun;
                     step.stepStatus = StepStatus.NotRun;
                     step.RetryCount = 0;
                     step.RetryCount = 0;
                 }
                 }
+                enableMakeResult = false;
                 return StartTestWithSteps(stepsFromGroup);
                 return StartTestWithSteps(stepsFromGroup);
             }
             }
         }
         }
@@ -440,8 +526,8 @@ namespace Bird_tool
                     return false;
                     return false;
                 }
                 }
                 OnLog?.Invoke($"找到分组ID为 {groupId} 的步骤", LogLevel.info);
                 OnLog?.Invoke($"找到分组ID为 {groupId} 的步骤", LogLevel.info);
-                
 
 
+                enableMakeResult = false;
                 // 使用提取的分组步骤启动测试
                 // 使用提取的分组步骤启动测试
                 return StartTestWithSteps(groupSteps);
                 return StartTestWithSteps(groupSteps);
             }
             }
@@ -585,9 +671,6 @@ namespace Bird_tool
             }
             }
         }
         }
 
 
-        // 从特定方法开始执行
-
-
         // 字符串模板替换方法
         // 字符串模板替换方法
         public string ReplaceTemplateVariables(string input, Dictionary<string, string> variables)
         public string ReplaceTemplateVariables(string input, Dictionary<string, string> variables)
         {
         {
@@ -671,7 +754,7 @@ namespace Bird_tool
             return false;
             return false;
         }
         }
 
 
-        private async void ExecuteCurrentStep()
+        private async Task ExecuteCurrentStepAsync(CancellationToken ct)
         {
         {
             var step = _context.CurrentStep;
             var step = _context.CurrentStep;
             RecordStepResult(step, TestStatus.Running, "步骤开始执行");
             RecordStepResult(step, TestStatus.Running, "步骤开始执行");
@@ -730,10 +813,7 @@ namespace Bird_tool
                 // 发送命令
                 // 发送命令
                 if (!string.IsNullOrEmpty(command))
                 if (!string.IsNullOrEmpty(command))
                 {
                 {
-                    if (!step.PrivateCammand)
-                    {
-                        OnLog?.Invoke($"发送命令: {command.Trim()}", LogLevel.info);
-                    }
+                    OnLog?.Invoke($"发送命令: {command.Trim()}", step.PrivateCammand? LogLevel.debug: LogLevel.info);
                     _serialManager.SendCommand(command);
                     _serialManager.SendCommand(command);
                 }
                 }
 
 
@@ -745,60 +825,75 @@ namespace Bird_tool
                 // 让当前任务的状态转变未Waiting, 用于在发送消息在进行匹配
                 // 让当前任务的状态转变未Waiting, 用于在发送消息在进行匹配
                 step.stepStatus = StepStatus.Waiting;
                 step.stepStatus = StepStatus.Waiting;
 
 
-                OnLog?.Invoke($"配置定时器 {step.Timeout}", LogLevel.debug);
-                _context.IsRunning = true;
-                _timeoutTimer?.Dispose();
-                _timeoutTimer = null;
-                _timeoutTimer = new System.Threading.Timer(
-                    _ => HandleTimeout(),
-                    null,
-                    step.Timeout,
-                    Timeout.Infinite
-                );
+                // 重构响应等待逻辑
+                var timeoutCts = new CancellationTokenSource(step.Timeout);
+                var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCts.Token) ;
+                OnLog?.Invoke($"发送命令 test",  LogLevel.info);
+                try
+                {
+                    if (step.RequiresUserPrompt &&
+                        string.IsNullOrEmpty(step.SuccessPattern) &&
+                        string.IsNullOrEmpty(step.SuccessText))
+                    {
+                        await PromptQuestionAsync(step, linkedCts.Token);
+                    }
+                    else
+                    {
+                        OnLog?.Invoke($"等待响应(发送后延迟)", LogLevel.debug);
+                        var response = await WaitForResponseAsync(step, linkedCts.Token);
+                        ProcessResponse(response, step, linkedCts.Token);
+                    }
+                }
+                catch (OperationCanceledException) when (timeoutCts.IsCancellationRequested)
+                {
+                    HandleStepFailure("操作超时", false);
+                }
 
 
                 // 如果没有SuccessPattern 与 SuccessText则直接进行询问
                 // 如果没有SuccessPattern 与 SuccessText则直接进行询问
                 if (step.RequiresUserPrompt && string.IsNullOrEmpty(step.SuccessPattern) && string.IsNullOrEmpty(step.SuccessText))
                 if (step.RequiresUserPrompt && string.IsNullOrEmpty(step.SuccessPattern) && string.IsNullOrEmpty(step.SuccessText))
                 {
                 {
-                    PromptQuestion(step);
+                   await PromptQuestionAsync(step, linkedCts.Token);
                 }
                 }
             }
             }
 
 
         }
         }
-
-        public void HandleResponse(string data)
+        private async Task<string> WaitForResponseAsync(TestStepConfig step, CancellationToken ct)
         {
         {
+            var tcs = new TaskCompletionSource<string>();
+            Action<string> responseHandler = (data) =>
+            {
+                if ((!string.IsNullOrEmpty(step.SuccessPattern) && Regex.IsMatch(data, step.SuccessPattern)) ||
+                    (!string.IsNullOrEmpty(step.SuccessText) && data.Contains(step.SuccessText)) ||
+                    (!string.IsNullOrEmpty(step.FailurePattern) && Regex.IsMatch(data, step.FailurePattern)) ||
+                    (!string.IsNullOrEmpty(step.FailureText) && data.Contains(step.FailureText)))
+                {
+                    tcs.TrySetResult(data);
+                }
+            };
 
 
-            // 减少锁的持有时间
-            TestContext localContext = null;
-            TestStepConfig step = null;
-            OnLog?.Invoke($"收到响应 {data} ", LogLevel.debug);
-
-            lock (_lock)
+            try
             {
             {
-                if (_context == null || !_context.IsRunning) return;
-                localContext = _context;
-                step = _context.CurrentStep;
+                _serialManager.OnLineReceived += responseHandler;
+                return await tcs.Task.WaitAsync(ct);
             }
             }
-
-            // 判断当前任务是否已经开始
-            if (step.stepStatus != StepStatus.Waiting)
+            finally
             {
             {
-                return;
+                _serialManager.OnLineReceived -= responseHandler;
             }
             }
+        }
 
 
-            // 检查成功模式
-            if (
-                (!string.IsNullOrEmpty(step.SuccessPattern) && Regex.IsMatch(data, step.SuccessPattern))
-                ||
-                (!string.IsNullOrEmpty(step.SuccessText) && data.Contains(step.SuccessText))
-                )
-            {
 
 
+        // 处理响应
+        private async void ProcessResponse(string data, TestStepConfig step, CancellationToken ct)
+        {
+            if ((!string.IsNullOrEmpty(step.SuccessPattern) && Regex.IsMatch(data, step.SuccessPattern)) ||
+                (!string.IsNullOrEmpty(step.SuccessText) && data.Contains(step.SuccessText)))
+            {
                 if (!string.IsNullOrEmpty(step.ExtractPattern) && !ExtractVariables(data, step))
                 if (!string.IsNullOrEmpty(step.ExtractPattern) && !ExtractVariables(data, step))
                 {
                 {
                     if (!ExtractVariables(data, step))
                     if (!ExtractVariables(data, step))
                     {
                     {
-                        OnLog?.Invoke($"匹配成功, 但是无法提取变量 {data} ", LogLevel.info);
+                        OnLog?.Invoke($"匹配成功, 但是无法提取变量 {data} ", LogLevel.error);
                         return;
                         return;
                     }
                     }
                 }
                 }
@@ -820,28 +915,24 @@ namespace Bird_tool
                     // 判断是否需要进行提问. 如果需要提问则先进行提问
                     // 判断是否需要进行提问. 如果需要提问则先进行提问
                     if (step.RequiresUserPrompt)
                     if (step.RequiresUserPrompt)
                     {
                     {
-                        PromptQuestion(step);
+                        await PromptQuestionAsync(step, ct);
                     }
                     }
                     else
                     else
                     {
                     {
                         HandleSetpSuccess("匹配到成功关键词");
                         HandleSetpSuccess("匹配到成功关键词");
                     }
                     }
                 }
                 }
-
-                return;
             }
             }
-
-            // 检查失败模式
-            if ((!string.IsNullOrEmpty(step.FailurePattern) && Regex.IsMatch(data, step.FailurePattern))
-                ||
-                (!string.IsNullOrEmpty(step.FailureText) && data.Contains(step.FailureText))
-                )
+            else if ((!string.IsNullOrEmpty(step.FailurePattern) && Regex.IsMatch(data, step.FailurePattern)) ||
+                     (!string.IsNullOrEmpty(step.FailureText) && data.Contains(step.FailureText)))
             {
             {
                 HandleStepFailure("匹配到失败关键词", false);
                 HandleStepFailure("匹配到失败关键词", false);
-                return;
             }
             }
-            
+        }
 
 
+        public void HandleResponse(string data)
+        {
+            OnLog?.Invoke($"收到响应 {data} ", LogLevel.debug);
         }
         }
 
 
         private bool ExtractVariables(string data, TestStepConfig step)
         private bool ExtractVariables(string data, TestStepConfig step)
@@ -860,7 +951,7 @@ namespace Bird_tool
                     _context.Variables[varName] = match.Groups[i].Value;
                     _context.Variables[varName] = match.Groups[i].Value;
                     matchTotal++;
                     matchTotal++;
                     isMatch = true;
                     isMatch = true;
-                    OnLog?.Invoke(msg, LogLevel.info);
+                    OnLog?.Invoke(msg, step.PrivateCammand? LogLevel.debug: LogLevel.info);
                 }
                 }
             }
             }
             // 记录缺失变量
             // 记录缺失变量
@@ -877,35 +968,49 @@ namespace Bird_tool
         {
         {
             HandleTestEnd(false);
             HandleTestEnd(false);
             OnFailed?.Invoke(_context.CurrentStep, _context);
             OnFailed?.Invoke(_context.CurrentStep, _context);
-            StopTest();
+            StopTestAsync();
         }
         }
         private void HandleTestSuccess()
         private void HandleTestSuccess()
         {
         {
             HandleTestEnd(true);
             HandleTestEnd(true);
             OnSuccess?.Invoke(_context);
             OnSuccess?.Invoke(_context);
-            StopTest();
+            StopTestAsync();
         }
         }
 
 
-        private async void PromptQuestion(TestStepConfig step)
+        // 替换现有的 PromptQuestion 方法
+        private async Task PromptQuestionAsync(TestStepConfig step, CancellationToken ct)
         {
         {
             OnLog?.Invoke($"等待用户确认: {step.PromptQuestion}", LogLevel.info);
             OnLog?.Invoke($"等待用户确认: {step.PromptQuestion}", LogLevel.info);
-            // 停止计时器, 防止用户在确认阶段导致失败
-            _timeoutTimer?.Dispose();
-            _timeoutTimer = null;
 
 
-            if (step.DelayBefore > 0)
+            if (step.DelayAfterPrompt > 0)
             {
             {
-                OnLog?.Invoke($"等待 {step.DelayBefore}ms (等待确认前延迟)", LogLevel.debug);
-                await Task.Delay(step.DelayBefore);
+                OnLog?.Invoke($"等待 {step.DelayAfterPrompt}ms (提示延迟)", LogLevel.debug);
+                await Task.Delay(step.DelayAfterPrompt, ct);
             }
             }
 
 
+            var promptTcs = new TaskCompletionSource<bool>();
+
+            // 在主线程执行UI提示
             OnPrompt?.Invoke(
             OnPrompt?.Invoke(
                 ReplaceTemplateVariables(step.PromptQuestion, _context.Variables),
                 ReplaceTemplateVariables(step.PromptQuestion, _context.Variables),
-                () => HandleSetpSuccess("用户确认成功"),
-                () => HandleStepFailure("用户确认失败", true)
+                () => promptTcs.TrySetResult(true),
+                () => promptTcs.TrySetResult(false)
             );
             );
-        }
 
 
+            try
+            {
+                bool result = await promptTcs.Task.WaitAsync(ct);
+
+                if (result)
+                    HandleSetpSuccess("用户确认成功");
+                else
+                    HandleStepFailure("用户确认失败", true);
+            }
+            catch (OperationCanceledException)
+            {
+                HandleStepFailure("用户响应超时", true);
+            }
+        }
 
 
         private void HandleSetpSuccess(string message)
         private void HandleSetpSuccess(string message)
         {
         {
@@ -1118,10 +1223,11 @@ namespace Bird_tool
         // 在测试结束时调用生成报告
         // 在测试结束时调用生成报告
         private void HandleTestEnd(bool saveReport)
         private void HandleTestEnd(bool saveReport)
         {
         {
-            GenerateTestReport();
-            if (saveReport)
+            if (saveReport && enableMakeResult)
             {
             {
+                GenerateTestReport();
                 SaveReportToHtml(_context.Report);
                 SaveReportToHtml(_context.Report);
+                AppendReportToCsv(_context.Report); // 新增CSV记录
             }
             }
         }
         }
 
 
@@ -1192,59 +1298,62 @@ namespace Bird_tool
                 }
                 }
             }
             }
 
 
-
-            if (allowRetry)
-            {
-                // 重新执行任务
-                QueueRetryOperation(() => ExecuteCurrentStep());
-            }
-            else if (isJump && jumpIdx >= 0)
+            EnqueueTask(async ct =>
             {
             {
-                // 跳转至特定任务重新执行
-                QueueRetryOperation(() => JumpToStep(jumpIdx));
-            }
-            else
-            {
-                _context.IsRunning = false;
-               OnLog?.Invoke(failStr, level);
-                // 自定义失败处理
-                step.OnFailure?.Invoke(_context);
-                OnStepChanged?.Invoke(step, _context, false);
-                // 测试失败, 判断是直接失败还是忽略此项
-                if (step.FailContinue)
+                if (allowRetry)
+                {
+                    // 重新执行任务
+                    await ExecuteCurrentStepAsync(ct);
+                }
+                else if (isJump && jumpIdx >= 0)
                 {
                 {
-                    QueueRetryOperation(MoveToNextStep);
+                    // 跳转至特定任务重新执行
+                    JumpToStep(jumpIdx);
                 }
                 }
                 else
                 else
                 {
                 {
-                    HandleTestFail();
+                    _context.IsRunning = false;
+                    OnLog?.Invoke(failStr, level);
+                    // 自定义失败处理
+                    step.OnFailure?.Invoke(_context);
+                    OnStepChanged?.Invoke(step, _context, false);
+                    // 测试失败, 判断是直接失败还是忽略此项
+                    if (step.FailContinue)
+                    {
+                        MoveToNextStep();
+                    }
+                    else
+                    {
+                        HandleTestFail();
+                    }
                 }
                 }
-            }
+            });
         }
         }
 
 
         private void MoveToNextStep()
         private void MoveToNextStep()
         {
         {
-            // 判断是否需要等待 DelayAfter
+            EnqueueTask(async ct =>
+            {
+                // 确保上下文存在
+                if (_context == null || !_context.IsRunning)
+                {
+                    OnLog?.Invoke($"========== 程序未执行,无法进入下一步 ==========", LogLevel.error);
+                    OnLogShow?.Invoke($"========== 程序未执行,无法进入下一步 ==========");
+                    return;
+                }
+                _context.CurrentStepIndex++;
 
 
-            _context.CurrentStepIndex++;
+                if (_context.CurrentStepIndex >= _context.Steps.Count)
+                {
+                    HandleTestSuccess();
+                    return;
+                }
 
 
-            if (_context.CurrentStepIndex >= _context.Steps.Count)
-            {
-                // 测试完成
-                _context.IsRunning = false;
-                OnLog?.Invoke("所有测试步骤完成", LogLevel.info);
-                HandleTestSuccess();
-                return;
-            }
-            if (_context.CurrentStepIndex < _context.Steps.Count)
-            {
                 OnStepChanged?.Invoke(_context.CurrentStep, _context, true);
                 OnStepChanged?.Invoke(_context.CurrentStep, _context, true);
                 OnLog?.Invoke($"========== 进入步骤: {_context.CurrentStep.Name} ==========", LogLevel.info);
                 OnLog?.Invoke($"========== 进入步骤: {_context.CurrentStep.Name} ==========", LogLevel.info);
-            }
-            _context.RetryCount = 0;
 
 
-            // 执行下一步
-            ExecuteCurrentStep();
+                await ExecuteCurrentStepAsync(ct);
+            });
         }
         }
         private int FindKeyByIndex(string key)
         private int FindKeyByIndex(string key)
         {
         {
@@ -1258,58 +1367,106 @@ namespace Bird_tool
             }
             }
             return -1;
             return -1;
         }
         }
+        
         private void JumpToStep(int newIndex)
         private void JumpToStep(int newIndex)
         {
         {
-            if (newIndex < 0 || newIndex >= _context.Steps.Count)
-            {
-                OnLog?.Invoke($"无效的步骤索引: {newIndex}", LogLevel.error);
-                return;
-            }
-            // 跳转计数器增加
-            _context.CurrentStep.JumpCount++;
-            OnLog?.Invoke($"跳转到步骤: {_context.Steps[newIndex].Name}", LogLevel.debug);
-            // 当前步骤的失败保留, 前面任务的失败进行移除
-            int oldIdx = _context.CurrentStepIndex;
-            if (oldIdx >= 0)
+            EnqueueTask(async ct =>
             {
             {
-                if (oldIdx < newIndex)
+                if (newIndex < 0 || newIndex >= _context.Steps.Count)
                 {
                 {
-                    // 跳转到后方, 一般不会出现
-                    for (int i = oldIdx; i <= newIndex; i++)
-                    {
-                        var step = _context.Steps[i];
-                        step.RetryCount = 0;
-                    }
-
+                    OnLog?.Invoke($"无效的步骤索引: {newIndex}", LogLevel.error);
+                    return;
                 }
                 }
-                else if (newIndex < oldIdx)
+                // 跳转计数器增加
+                _context.CurrentStep.JumpCount++;
+                OnLog?.Invoke($"跳转到步骤: {_context.Steps[newIndex].Name}", LogLevel.debug);
+                // 当前步骤的失败保留, 前面任务的失败进行移除
+                int oldIdx = _context.CurrentStepIndex;
+                if (oldIdx >= 0)
                 {
                 {
-                    // 跳转到前方执行
-                    for (int i = newIndex; i < oldIdx; i++)
+                    if (oldIdx < newIndex)
+                    {
+                        // 跳转到后方, 一般不会出现
+                        for (int i = oldIdx; i <= newIndex; i++)
+                        {
+                            var step = _context.Steps[i];
+                            step.RetryCount = 0;
+                        }
+
+                    }
+                    else if (newIndex < oldIdx)
                     {
                     {
-                        var step = _context.Steps[i];
-                        step.RetryCount = 0;
+                        // 跳转到前方执行
+                        for (int i = newIndex; i < oldIdx; i++)
+                        {
+                            var step = _context.Steps[i];
+                            step.RetryCount = 0;
+                        }
                     }
                     }
                 }
                 }
-            }
 
 
-            _context.CurrentStepIndex = newIndex;
-            ExecuteCurrentStep();
+                _context.CurrentStepIndex = newIndex;
+                await ExecuteCurrentStepAsync(ct);
+            });
         }
         }
 
 
-        private void HandleTimeout()
+
+        // 新增:追加CSV记录
+        private void AppendReportToCsv(TestReport report)
         {
         {
-            // 使用 try-catch 避免锁嵌套问题
-            bool shouldHandle = false;
-            lock (_lock)
+            lock (_csvLock)
             {
             {
-                shouldHandle = (_context != null && _context.IsRunning);
-            }
+                bool fileExists = File.Exists(_csvReportPath);
 
 
-            if (shouldHandle)
-            {
-                OnLog?.Invoke("操作超时", LogLevel.info);
-                HandleStepFailure("操作超时", false);
+                using (var writer = new StreamWriter(_csvReportPath, true, Encoding.UTF8))
+                {
+                    // 写入表头(如果文件不存在)
+                    if (!fileExists)
+                    {
+                        var headers = new List<string>
+                    {
+                        "StartTime", "EndTime", "Result"
+                    };
+
+                        // 添加设备信息列
+                        foreach (var kv in report.GetDeviceInfoPairs())
+                        {
+                            headers.Add(kv.Key);
+                        }
+
+                        // 添加测试项列
+                        foreach (var item in report.GetTestItems())
+                        {
+                            headers.Add($"{item.GroupName}_Status");
+                            headers.Add($"{item.GroupName}_Details");
+                        }
+
+                        writer.WriteLine(string.Join(",", headers));
+                    }
+
+                    // 构建数据行
+                    var rowData = new List<string>
+                {
+                    report.StartTime.ToString("yyyy-MM-dd HH:mm:ss"),
+                    report.EndTime.ToString("yyyy-MM-dd HH:mm:ss"),
+                    report.TestResult
+                };
+
+                    // 添加设备信息值
+                    foreach (var kv in report.GetDeviceInfoPairs())
+                    {
+                        rowData.Add(kv.Value);
+                    }
+
+                    // 添加测试项结果
+                    foreach (var item in report.GetTestItems())
+                    {
+                        rowData.Add(item.Status.ToString());
+                        rowData.Add(item.Details.Replace(",", ";"));
+                    }
+
+                    writer.WriteLine(string.Join(",", rowData));
+                }
             }
             }
         }
         }
 
 

+ 14 - 4
bird_tool/bird_tool.cs

@@ -822,6 +822,15 @@ namespace Bird_tool
                 return;
                 return;
             }
             }
             Log($"testType{testType} by {groupId}");
             Log($"testType{testType} by {groupId}");
+            progressPanel.ResetPanel();
+            var groups = _testExecutor.GetGroupInfos();
+            foreach (var group in groups)
+            {
+                if (group.ShowStep)
+                {
+                    progressPanel.AddTestStep(group.RowKey, group.GroupName);
+                }
+            }
             progressPanel.StartTimer();
             progressPanel.StartTimer();
             // 初始化测试状态
             // 初始化测试状态
             progressPanel.ProgressValue = 0;
             progressPanel.ProgressValue = 0;
@@ -864,7 +873,7 @@ namespace Bird_tool
 
 
                     // 添加日志
                     // 添加日志
                     logBuilder.AppendLine($"[{DateTime.Now}] 测试已取消");
                     logBuilder.AppendLine($"[{DateTime.Now}] 测试已取消");
-                    _testExecutor.StopTest();
+                    _testExecutor.StopTestAsync();
                     progressPanel.Message = "用户手动停止测试";
                     progressPanel.Message = "用户手动停止测试";
                     Log($"[{DateTime.Now}] 测试中止");
                     Log($"[{DateTime.Now}] 测试中止");
                 }
                 }
@@ -880,7 +889,7 @@ namespace Bird_tool
         private void TestFailedhandle(TestStepConfig step, TestContext context)
         private void TestFailedhandle(TestStepConfig step, TestContext context)
         {
         {
             string str = $"------ 测试失败-------";
             string str = $"------ 测试失败-------";
-            _testExecutor.StopTest();
+            _testExecutor.StopTestAsync();
             progressPanel.ShowTestResult(false, str);
             progressPanel.ShowTestResult(false, str);
             Log(str);
             Log(str);
             Log($"------测试结束-------");
             Log($"------测试结束-------");
@@ -975,7 +984,7 @@ namespace Bird_tool
                     Tips = "等待任务自动执行",
                     Tips = "等待任务自动执行",
                     Command = "AT+RBOT=0\r\n",
                     Command = "AT+RBOT=0\r\n",
                     SuccessPattern = "OS start",
                     SuccessPattern = "OS start",
-                    Timeout = 3000,
+                    Timeout = 10000,
                     DelayBefore = 1000,
                     DelayBefore = 1000,
                     MaxRetries = 5,
                     MaxRetries = 5,
                 },
                 },
@@ -1119,7 +1128,7 @@ namespace Bird_tool
                     Tips = "设备版本检查地址",
                     Tips = "设备版本检查地址",
                     IsDeviceInfoItem = true,
                     IsDeviceInfoItem = true,
                     InfoDisplayName = "设备固件",
                     InfoDisplayName = "设备固件",
-                    InfoDisplayTemplate = "{{hwVersion}}{{hwTime}}",
+                    InfoDisplayTemplate = "{{hwTime}} - {{hwVersion}}",
                     FailTips = "设备固件异常, 版本号:{{hwVersion}}{{hwTime}}",
                     FailTips = "设备固件异常, 版本号:{{hwVersion}}{{hwTime}}",
                     Command = "AT+SEND=1, AT+HWCUST?\r\n",
                     Command = "AT+SEND=1, AT+HWCUST?\r\n",
                     SuccessPattern = @"\+HWCUST:",
                     SuccessPattern = @"\+HWCUST:",
@@ -1326,6 +1335,7 @@ namespace Bird_tool
                     Tips = "正在连接WiFi中",
                     Tips = "正在连接WiFi中",
                     Command = "AT+WIFI=\"{{ssid}}\",\"{{password}}\",\"CN\",\"{{activeCode}}\"\r\n",
                     Command = "AT+WIFI=\"{{ssid}}\",\"{{password}}\",\"CN\",\"{{activeCode}}\"\r\n",
                     SuccessText = "WiFi_STAT: Connected",
                     SuccessText = "WiFi_STAT: Connected",
+                    PrivateCammand = true,
                     Timeout = 10000,
                     Timeout = 10000,
                     DelayBefore = 500,
                     DelayBefore = 500,
                     MaxRetries = 5,
                     MaxRetries = 5,

+ 3 - 0
bird_tool/bird_tool.csproj

@@ -149,6 +149,9 @@
     <PackageReference Include="System.Text.Json">
     <PackageReference Include="System.Text.Json">
       <Version>9.0.0</Version>
       <Version>9.0.0</Version>
     </PackageReference>
     </PackageReference>
+    <PackageReference Include="System.Threading.Channels">
+      <Version>9.0.6</Version>
+    </PackageReference>
     <PackageReference Include="System.Threading.Tasks.Extensions">
     <PackageReference Include="System.Threading.Tasks.Extensions">
       <Version>4.5.4</Version>
       <Version>4.5.4</Version>
     </PackageReference>
     </PackageReference>