feat: AutomationModeContext 统一自动模式(窗口自动选择默认动作并立即关闭)

新增 src/Utils/AutomationModeContext.cs:
- 自动模式统一入口(运行时计数 EnterAutomaticMode + 环境变量
  TRANSPORTPLUGIN_TEST_AUTOMATION 兼容,可嵌套)
- 自动模式下窗口自动选默认动作/跳过弹窗,保证自动测试与批处理流畅

接入点:
- BatchQueueManager.ProcessQueueAsync 全程自动模式:
  排除列表确认框 → 默认继续;完成汇总弹窗 → 跳过(日志保留)
- ClashDetectiveIntegration.ShowCancellationMessage → 跳过
- CollisionAnalysisDialog.Loaded 自动关闭(并入统一机制)
- AnimationControlViewModel:重复配置对话框默认创建新记录、
  预计算分析跳过、重复检查跳过(3 处统一)
- DialogHelper.ShowAutoClosingMessageBox → 统一判断

验证:集成测试 25/25、F1 批处理 2 分钟跑完 NW 零残留窗口
(EnumWindows 确认仅主窗口)
This commit is contained in:
tian 2026-08-06 11:22:03 +08:00
parent f13e60c979
commit 0d43cda5d7
7 changed files with 95 additions and 7 deletions

View File

@ -339,6 +339,7 @@
<Compile Include="src\Utils\CoordinateConverter.cs" />
<Compile Include="src\Utils\FloorDetector.cs" />
<Compile Include="src\Utils\ModelItemAnalysisHelper.cs" />
<Compile Include="src\Utils\AutomationModeContext.cs" />
<Compile Include="src\Utils\ModelItemTreeWalker.cs" />
<Compile Include="src\Utils\GeometryHelper.cs" />
<Compile Include="src\Utils\GeometryCacheManager.cs" />

View File

@ -11,6 +11,7 @@ using NavisworksTransport.Core.Collision;
using NavisworksTransport.Core.Config;
using NavisworksTransport.Core.Models;
using NavisworksTransport.Core.Services;
using NavisworksTransport.Utils;
namespace NavisworksTransport.Core
{
@ -203,6 +204,9 @@ namespace NavisworksTransport.Core
/// 处理队列(公共方法,供外部调用)
/// </summary>
public async Task ProcessQueueAsync()
{
// 自动模式:批处理全程窗口自动选择默认动作并立即关闭(排除列表确认/完成汇总/错误提示等)
using (AutomationModeContext.EnterAutomaticMode())
{
Autodesk.Navisworks.Api.Progress progress = null;
@ -407,6 +411,7 @@ namespace NavisworksTransport.Core
ShowBatchSummary(startTime, endTime, duration, totalCount, successCount, failedCount, skippedCount);
}
}
}
/// <summary>
/// 显示批处理执行汇总信息
@ -464,6 +469,13 @@ namespace NavisworksTransport.Core
LogManager.Info($"[批处理队列] 执行汇总 - 总计:{totalCount}, 成功:{successCount}, 失败:{failedCount}, 跳过:{skippedCount}, 耗时:{durationText}");
// 自动模式下跳过完成弹窗(汇总已写入日志)
if (AutomationModeContext.IsAutomaticMode)
{
LogManager.Info("[批处理队列] 自动模式下跳过完成汇总弹窗");
return;
}
// 在UI线程显示消息框
System.Windows.Application.Current?.Dispatcher.Invoke(() =>
{
@ -1044,6 +1056,13 @@ namespace NavisworksTransport.Core
// 排除列表为空,提示用户
LogManager.Warning("[批处理队列] 排除列表为空,需要用户确认");
// 自动模式下跳过确认框,默认继续(不使用排除列表)
if (AutomationModeContext.IsAutomaticMode)
{
LogManager.Info("[批处理队列] 自动模式下跳过排除列表确认,默认继续");
return new List<ModelItem>();
}
bool shouldContinue = false;
System.Windows.Application.Current?.Dispatcher.Invoke(() =>
{

View File

@ -1305,6 +1305,13 @@ namespace NavisworksTransport
/// </summary>
private void ShowCancellationMessage()
{
// 自动模式下跳过取消提示(日志已记录,避免残留弹窗阻塞自动化)
if (AutomationModeContext.IsAutomaticMode)
{
LogManager.Info("[ClashDetective] 自动模式下跳过检测取消提示框");
return;
}
try
{
// 获取 Navisworks 主窗口句柄作为父窗口

View File

@ -3623,7 +3623,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
try
{
if (DialogHelper.IsTestAutomationModeEnabled)
if (AutomationModeContext.IsAutomaticMode)
{
LogManager.Info("[碰撞分析] 自动测试模式下跳过预计算碰撞分析");
SaveCollisionDetectionRecord();
@ -3861,7 +3861,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
string routeId = CurrentPathRoute?.Id ?? "";
if (!DialogHelper.IsTestAutomationModeEnabled)
if (!AutomationModeContext.IsAutomaticMode)
{
// 🔥 检查是否已有相同配置的检测记录
var existingRecordResult = CheckExistingDetectionRecord();
@ -4154,7 +4154,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
/// </summary>
private (int id, UserChoice userChoice)? ShowDuplicateConfigDialog(CollisionDetectionRecord record)
{
if (TestAutomationHttpService.ShouldAutoChooseCreateNewDetectionRecord)
if (TestAutomationHttpService.ShouldAutoChooseCreateNewDetectionRecord ||
AutomationModeContext.IsAutomaticMode)
{
_lastReusedRecordId = null;
LogManager.Info($"[检测记录] 测试自动化已接管重复配置对话框,自动选择重新生成新记录 (ExistingId={record.Id})");

View File

@ -67,7 +67,8 @@ namespace NavisworksTransport.UI.WPF.Views
private void CollisionAnalysisDialog_Loaded(object sender, RoutedEventArgs e)
{
if (!TestAutomationHttpService.ShouldAutoConfirmCollisionAnalysisDialogs)
if (!TestAutomationHttpService.ShouldAutoConfirmCollisionAnalysisDialogs &&
!AutomationModeContext.IsAutomaticMode)
{
return;
}

View File

@ -0,0 +1,59 @@
using System;
using System.Threading;
namespace NavisworksTransport.Utils
{
/// <summary>
/// 自动模式上下文(自动测试 / 批处理执行期间)。
///
/// 自动模式下:
/// - 窗口(对话框/提示框)自动选择默认动作并立即关闭(或直接跳过弹窗)
/// - 保证自动测试与批处理流畅进行,无需人工干预
///
/// 进入方式:
/// - 批处理执行BatchQueueManager.ProcessQueueAsync全程
/// - 测试 HTTP 端点TestAutomationHttpService 计数器,兼容保留)
/// - 环境变量 TRANSPORTPLUGIN_TEST_AUTOMATION=1副实例/测试进程)
/// </summary>
public static class AutomationModeContext
{
private static int _requestCount;
/// <summary>
/// 当前是否处于自动模式(任一来源生效:运行时计数 / 环境变量)。
/// </summary>
public static bool IsAutomaticMode =>
Interlocked.CompareExchange(ref _requestCount, 0, 0) > 0 ||
DialogHelper.IsTestAutomationModeEnabled;
/// <summary>
/// 进入自动模式可嵌套Dispose 时退出。
/// </summary>
public static IDisposable EnterAutomaticMode()
{
Interlocked.Increment(ref _requestCount);
return new ActionOnDispose(() =>
{
if (Interlocked.Decrement(ref _requestCount) < 0)
{
Interlocked.Exchange(ref _requestCount, 0);
}
});
}
private sealed class ActionOnDispose : IDisposable
{
private readonly Action _action;
public ActionOnDispose(Action action)
{
_action = action;
}
public void Dispose()
{
_action?.Invoke();
}
}
}
}

View File

@ -180,7 +180,7 @@ namespace NavisworksTransport.Utils
MessageBoxImage icon = MessageBoxImage.Information,
int autoCloseMilliseconds = 4000)
{
if (IsTestAutomationModeEnabled)
if (AutomationModeContext.IsAutomaticMode)
{
LogManager.Info($"[DialogHelper] 自动测试模式下跳过自动关闭提示框: {caption}");
return;