diff --git a/src/Core/PathDatabase.cs b/src/Core/PathDatabase.cs
index decfcf3..b1757cd 100644
--- a/src/Core/PathDatabase.cs
+++ b/src/Core/PathDatabase.cs
@@ -193,7 +193,7 @@ namespace NavisworksTransport
FrameRate INTEGER NOT NULL DEFAULT 30,
DurationSeconds REAL NOT NULL DEFAULT 10.0,
-- 碰撞检测参数
- DetectionToleranceMeters REAL NOT NULL DEFAULT 0.05,
+ DetectionTolerance REAL NOT NULL DEFAULT 0.05,
-- 运动物体参数
IsVirtualObject INTEGER NOT NULL DEFAULT 0,
AnimatedObjectName TEXT,
@@ -292,7 +292,7 @@ namespace NavisworksTransport
// 12. 设置数据库版本(SQLite内置user_version)
// 版本号格式:主版本*10000 + 次版本*100 + 修订号
// 例如:2.1.3 = 20103
- ExecuteNonQuery("PRAGMA user_version = 20102"); // v2.1.2 - 增加时标配置和事件表
+ ExecuteNonQuery("PRAGMA user_version = 20103"); // v2.1.3 - 重命名DetectionToleranceMeters为DetectionTolerance
// 创建索引
ExecuteNonQuery("CREATE INDEX IF NOT EXISTS idx_reports_route ON CollisionReports(RouteId)");
@@ -319,7 +319,7 @@ namespace NavisworksTransport
ErrorMessage TEXT,
FrameRate INTEGER NOT NULL,
DurationSeconds REAL NOT NULL,
- DetectionToleranceMeters REAL NOT NULL,
+ DetectionTolerance REAL NOT NULL,
IsVirtualObject INTEGER NOT NULL,
VirtualObjectLength REAL,
VirtualObjectWidth REAL,
@@ -1187,7 +1187,175 @@ namespace NavisworksTransport
return results;
}
+ ///
+ /// 获取检测记录的排除对象列表
+ ///
+ public List GetDetectionExcludedObjects(int detectionRecordId)
+ {
+ var results = new List();
+
+ try
+ {
+ var sql = @"
+ SELECT Id, DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName, Reason
+ FROM CollisionDetectionExcludedObjects
+ WHERE DetectionRecordId = @detectionRecordId
+ ";
+
+ using (var cmd = new SQLiteCommand(sql, _connection))
+ {
+ cmd.Parameters.AddWithValue("@detectionRecordId", detectionRecordId);
+ using (var reader = cmd.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ results.Add(new CollisionDetectionExcludedObjectRecord
+ {
+ Id = Convert.ToInt32(reader["Id"]),
+ DetectionRecordId = Convert.ToInt32(reader["DetectionRecordId"]),
+ ModelIndex = reader["ModelIndex"] == DBNull.Value ? 0 : Convert.ToInt32(reader["ModelIndex"]),
+ PathId = reader["PathId"] == DBNull.Value ? null : reader["PathId"].ToString(),
+ DisplayName = reader["DisplayName"]?.ToString(),
+ ObjectName = reader["ObjectName"]?.ToString(),
+ Reason = reader["Reason"]?.ToString()
+ });
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"获取排除对象列表失败: {ex.Message}", ex);
+ }
+
+ return results;
+ }
+
+ ///
+ /// 获取检测记录的手工碰撞目标列表
+ ///
+ public List GetDetectionManualTargets(int detectionRecordId)
+ {
+ var results = new List();
+
+ try
+ {
+ var sql = @"
+ SELECT Id, DetectionRecordId, ModelIndex, PathId, DisplayName, ObjectName
+ FROM CollisionDetectionManualTargets
+ WHERE DetectionRecordId = @detectionRecordId
+ ";
+
+ using (var cmd = new SQLiteCommand(sql, _connection))
+ {
+ cmd.Parameters.AddWithValue("@detectionRecordId", detectionRecordId);
+ using (var reader = cmd.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ results.Add(new CollisionDetectionManualTargetRecord
+ {
+ Id = Convert.ToInt32(reader["Id"]),
+ DetectionRecordId = Convert.ToInt32(reader["DetectionRecordId"]),
+ ModelIndex = reader["ModelIndex"] == DBNull.Value ? 0 : Convert.ToInt32(reader["ModelIndex"]),
+ PathId = reader["PathId"] == DBNull.Value ? null : reader["PathId"].ToString(),
+ DisplayName = reader["DisplayName"]?.ToString(),
+ ObjectName = reader["ObjectName"]?.ToString()
+ });
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"获取手工目标列表失败: {ex.Message}", ex);
+ }
+
+ return results;
+ }
+
#endregion
+
+ ///
+ /// 通过基础参数查询匹配的检测记录(用于检查配置是否重复)
+ /// 注意:数值参数使用容差匹配,返回的记录还需要进一步比较排除列表和手工目标
+ ///
+ public List FindDetectionRecordsByBaseParams(
+ string routeId,
+ int frameRate,
+ double durationSeconds,
+ double detectionTolerance,
+ bool isVirtualObject,
+ double? virtualObjectLength,
+ double? virtualObjectWidth,
+ double? virtualObjectHeight,
+ double objectRotationCorrection,
+ bool isManualMode)
+ {
+ var results = new List();
+
+ try
+ {
+ // 使用容差匹配浮动数值参数
+ var sql = @"
+ SELECT cdr.*, pr.Name AS PathName
+ FROM CollisionDetectionRecords cdr
+ INNER JOIN PathRoutes pr ON cdr.RouteId = pr.Id
+ WHERE cdr.RouteId = @routeId
+ AND cdr.FrameRate = @frameRate
+ AND ABS(cdr.DurationSeconds - @duration) < 0.01
+ AND ABS(cdr.DetectionTolerance - @tolerance) < 0.001
+ AND cdr.IsVirtualObject = @isVirtual
+ AND ABS(cdr.ObjectRotationCorrection - @rotation) < 0.01
+ AND cdr.DetectAllObjects = @detectAll
+ AND cdr.TestName IS NOT NULL
+ ";
+
+ using (var cmd = new SQLiteCommand(sql, _connection))
+ {
+ cmd.Parameters.AddWithValue("@routeId", routeId);
+ cmd.Parameters.AddWithValue("@frameRate", frameRate);
+ cmd.Parameters.AddWithValue("@duration", durationSeconds);
+ cmd.Parameters.AddWithValue("@tolerance", detectionTolerance);
+ cmd.Parameters.AddWithValue("@isVirtual", isVirtualObject ? 1 : 0);
+ cmd.Parameters.AddWithValue("@rotation", objectRotationCorrection);
+ cmd.Parameters.AddWithValue("@detectAll", isManualMode ? 0 : 1);
+
+ using (var reader = cmd.ExecuteReader())
+ {
+ while (reader.Read())
+ {
+ var record = ReadCollisionDetectionRecordFromReader(reader);
+
+ // 虚拟物体尺寸比较(如果是虚拟物体)
+ if (isVirtualObject && virtualObjectLength.HasValue)
+ {
+ if (!record.VirtualObjectLength.HasValue ||
+ Math.Abs(record.VirtualObjectLength.Value - virtualObjectLength.Value) > 0.01)
+ continue;
+ if (!record.VirtualObjectWidth.HasValue ||
+ Math.Abs(record.VirtualObjectWidth.Value - virtualObjectWidth.Value) > 0.01)
+ continue;
+ if (!record.VirtualObjectHeight.HasValue ||
+ Math.Abs(record.VirtualObjectHeight.Value - virtualObjectHeight.Value) > 0.01)
+ continue;
+ }
+
+ results.Add(record);
+ }
+ }
+ }
+
+ LogManager.Debug($"[FindByBaseParams] 找到 {results.Count} 条基础参数匹配的记录");
+ }
+ catch (Exception ex)
+ {
+ LogManager.Error($"通过基础参数查询检测记录失败: {ex.Message}", ex);
+ }
+
+ return results;
+ }
+
///
/// 保存分析评分结果
///
@@ -2086,14 +2254,14 @@ namespace NavisworksTransport
{
var sql = @"
INSERT INTO CollisionDetectionRecords (
- RouteId, CreatedTime, FrameRate, DurationSeconds, DetectionToleranceMeters,
+ RouteId, CreatedTime, FrameRate, DurationSeconds, DetectionTolerance,
IsVirtualObject, AnimatedObjectName, ObjectModelIndex, ObjectPathId,
VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
DetectAllObjects, ObjectRotationCorrection, Description,
TestName, TestTime, CollisionCount, AnimationCollisionCount
)
VALUES (
- @RouteId, @CreatedTime, @FrameRate, @DurationSeconds, @DetectionToleranceMeters,
+ @RouteId, @CreatedTime, @FrameRate, @DurationSeconds, @DetectionTolerance,
@IsVirtualObject, @AnimatedObjectName, @ObjectModelIndex, @ObjectPathId,
@VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
@DetectAllObjects, @ObjectRotationCorrection, @Description,
@@ -2109,7 +2277,7 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@FrameRate", record.FrameRate);
cmd.Parameters.AddWithValue("@DurationSeconds", record.DurationSeconds);
// 内部使用模型单位,写入数据库时转换为米单位
- cmd.Parameters.AddWithValue("@DetectionToleranceMeters", record.DetectionTolerance * UnitsConverter.GetUnitsToMetersConversionFactor());
+ cmd.Parameters.AddWithValue("@DetectionTolerance", record.DetectionTolerance); // 模型单位,不转换
cmd.Parameters.AddWithValue("@IsVirtualObject", record.IsVirtualObject ? 1 : 0);
cmd.Parameters.AddWithValue("@AnimatedObjectName", record.AnimatedObjectName ?? "");
cmd.Parameters.AddWithValue("@ObjectModelIndex", record.ObjectModelIndex ?? (object)DBNull.Value);
@@ -2417,7 +2585,7 @@ namespace NavisworksTransport
FrameRate = Convert.ToInt32(reader["FrameRate"]),
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
// 数据库中存储米单位,读取时转换为模型单位
- DetectionTolerance = Convert.ToDouble(reader["DetectionToleranceMeters"]) * UnitsConverter.GetMetersToUnitsConversionFactor(),
+ DetectionTolerance = Convert.ToDouble(reader["DetectionTolerance"]), // 模型单位,不转换
IsVirtualObject = Convert.ToInt32(reader["IsVirtualObject"]) == 1,
AnimatedObjectName = reader["AnimatedObjectName"]?.ToString(),
ObjectModelIndex = reader["ObjectModelIndex"] != DBNull.Value ? Convert.ToInt32(reader["ObjectModelIndex"]) : (int?)null,
@@ -2581,7 +2749,7 @@ namespace NavisworksTransport
cmd.CommandText = @"
INSERT INTO BatchQueueItems (
RouteId, Status, CreatedTime, StartTime, EndTime, ErrorMessage,
- FrameRate, DurationSeconds, DetectionToleranceMeters,
+ FrameRate, DurationSeconds, DetectionTolerance,
IsVirtualObject, VirtualObjectLength, VirtualObjectWidth, VirtualObjectHeight,
DetectAllObjects,
ClashDetectiveTestName, CollisionCount, ObjectRotationCorrection,
@@ -2589,7 +2757,7 @@ namespace NavisworksTransport
)
VALUES (
@RouteId, @Status, @CreatedTime, @StartTime, @EndTime, @ErrorMessage,
- @FrameRate, @DurationSeconds, @DetectionToleranceMeters,
+ @FrameRate, @DurationSeconds, @DetectionTolerance,
@IsVirtualObject, @VirtualObjectLength, @VirtualObjectWidth, @VirtualObjectHeight,
@DetectAllObjects,
@ClashDetectiveTestName, @CollisionCount, @ObjectRotationCorrection,
@@ -2606,7 +2774,7 @@ namespace NavisworksTransport
cmd.Parameters.AddWithValue("@FrameRate", item.FrameRate);
cmd.Parameters.AddWithValue("@DurationSeconds", item.DurationSeconds);
// 内部使用模型单位,写入数据库时转换为米单位
- cmd.Parameters.AddWithValue("@DetectionToleranceMeters", item.DetectionTolerance * UnitsConverter.GetUnitsToMetersConversionFactor());
+ cmd.Parameters.AddWithValue("@DetectionTolerance", item.DetectionTolerance); // 模型单位,不转换
cmd.Parameters.AddWithValue("@IsVirtualObject", item.IsVirtualObject ? 1 : 0);
cmd.Parameters.AddWithValue("@VirtualObjectLength", item.VirtualObjectLength);
cmd.Parameters.AddWithValue("@VirtualObjectWidth", item.VirtualObjectWidth);
@@ -2651,7 +2819,7 @@ namespace NavisworksTransport
FrameRate = Convert.ToInt32(reader["FrameRate"]),
DurationSeconds = Convert.ToDouble(reader["DurationSeconds"]),
// 数据库中存储米单位,读取时转换为模型单位
- DetectionTolerance = Convert.ToDouble(reader["DetectionToleranceMeters"]) * UnitsConverter.GetMetersToUnitsConversionFactor(),
+ DetectionTolerance = Convert.ToDouble(reader["DetectionTolerance"]), // 模型单位,不转换
IsVirtualObject = isVirtualObject,
VirtualObjectLength = Convert.ToDouble(reader["VirtualObjectLength"]),
VirtualObjectWidth = Convert.ToDouble(reader["VirtualObjectWidth"]),
@@ -2690,7 +2858,7 @@ namespace NavisworksTransport
{
var sql = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
- bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
+ bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionTolerance,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection,
@@ -2750,7 +2918,7 @@ namespace NavisworksTransport
{
cmd.CommandText = @"
SELECT bqi.Id, bqi.RouteId, pr.Name AS PathRouteName, pr.PathType, bqi.Status, bqi.CreatedTime, bqi.StartTime, bqi.EndTime, bqi.ErrorMessage,
- bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionToleranceMeters,
+ bqi.FrameRate, bqi.DurationSeconds, bqi.DetectionTolerance,
bqi.IsVirtualObject, bqi.VirtualObjectLength, bqi.VirtualObjectWidth, bqi.VirtualObjectHeight,
bqi.DetectAllObjects,
bqi.ClashDetectiveTestName, bqi.CollisionCount, bqi.ObjectRotationCorrection,
diff --git a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
index 5021cdd..b69a560 100644
--- a/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
+++ b/src/UI/WPF/ViewModels/AnimationControlViewModel.cs
@@ -3255,73 +3255,87 @@ namespace NavisworksTransport.UI.WPF.ViewModels
///
/// 检查是否已有相同配置的检测记录
- /// 通过动画配置哈希匹配已完成的检测记录
+ /// 策略:1.先检查内存缓存 2.SQL过滤基础参数 3.比较排除列表和手工目标
///
/// (记录ID, 用户选择),不存在或用户取消则返回null
private (int id, UserChoice userChoice)? CheckExistingDetectionRecord()
{
try
{
- // 计算当前动画配置的哈希
- var animatedObject = UseVirtualObject ? VirtualObjectManager.Instance.CurrentVirtualObject : SelectedAnimatedObject;
- var route = CurrentPathRoute?.Route;
-
- if (animatedObject == null || route == null)
+ var routeId = CurrentPathRoute?.Id;
+ if (string.IsNullOrEmpty(routeId))
{
return null;
}
- var currentHash = _pathAnimationManager.ComputeAnimationConfigHashFromRoute(animatedObject, route);
-
- // 查询PathAnimationManager中是否有相同配置的记录
- var existingRecordId = _pathAnimationManager.GetDetectionRecordIdByHash(currentHash);
-
- if (existingRecordId.HasValue)
+ var pathDatabase = _pathPlanningManager?.GetPathDatabase();
+ if (pathDatabase == null)
{
- // 验证数据库中该记录是否仍然存在
- var pathDatabase = _pathPlanningManager?.GetPathDatabase();
- var record = pathDatabase?.GetDetectionResultById(existingRecordId.Value);
-
- if (record != null)
+ return null;
+ }
+
+ var animatedObject = UseVirtualObject ? VirtualObjectManager.Instance.CurrentVirtualObject : SelectedAnimatedObject;
+ if (animatedObject == null)
+ {
+ return null;
+ }
+
+ // 1. 先检查内存缓存(程序运行期间已处理过的)
+ var currentHash = _pathAnimationManager.ComputeAnimationConfigHashFromRoute(animatedObject, CurrentPathRoute.Route);
+ var cachedRecordId = _pathAnimationManager.GetDetectionRecordIdByHash(currentHash);
+ if (cachedRecordId.HasValue)
+ {
+ var cachedRecord = pathDatabase.GetDetectionResultById(cachedRecordId.Value);
+ if (cachedRecord != null)
{
- LogManager.Info($"[CheckExistingDetectionRecord] 找到相同配置的检测记录: Hash={currentHash.Substring(0, 8)}..., RecordId={existingRecordId}");
-
- // 弹出提示框询问用户
- var message = $"检测配置已存在!\n\n" +
- $"找到一条相同配置的历史检测记录:\n" +
- $"• 测试名称:{record.TestName ?? "未命名"}\n" +
- $"• 检测时间:{record.TestTime:yyyy-MM-dd HH:mm:ss}\n" +
- $"• 碰撞数量:{record.CollisionCount} 个\n\n" +
- $"您可以:\n" +
- $"• 【是】直接使用历史记录(跳过重复检测)\n" +
- $"• 【否】重新生成新记录";
-
- var result = System.Windows.MessageBox.Show(
- message,
- "检测配置已存在",
- System.Windows.MessageBoxButton.YesNo,
- System.Windows.MessageBoxImage.Question);
-
- if (result == System.Windows.MessageBoxResult.Yes)
- {
- // 用户选择使用历史记录
- _lastReusedRecordId = existingRecordId.Value;
- return (existingRecordId.Value, UserChoice.UseExisting);
- }
- else
- {
- // 用户选择重新生成
- _lastReusedRecordId = null;
- return (existingRecordId.Value, UserChoice.CreateNew);
- }
- }
- else
- {
- // 记录已被删除,清除缓存
- _pathAnimationManager.RemoveDetectionRecordHash(currentHash);
+ LogManager.Info($"[CheckExistingDetectionRecord] 内存缓存命中: RecordId={cachedRecordId}");
+ return ShowDuplicateConfigDialog(cachedRecord);
}
}
-
+
+ // 2. SQL过滤基础参数匹配的记录
+ // 注意:数据库存储的是模型单位(不带Meters后缀),查询时传模型单位
+ var factor = UnitsConverter.GetMetersToUnitsConversionFactor();
+ var matchingRecords = pathDatabase.FindDetectionRecordsByBaseParams(
+ routeId,
+ _animationFrameRate,
+ AnimationDuration,
+ _detectionTolerance * factor, // 米 → 模型单位
+ UseVirtualObject,
+ UseVirtualObject ? VirtualObjectLengthInMeters * factor : (double?)null, // 米 → 模型单位
+ UseVirtualObject ? VirtualObjectWidthInMeters * factor : (double?)null,
+ UseVirtualObject ? VirtualObjectHeightInMeters * factor : (double?)null,
+ _objectRotationCorrection,
+ IsManualCollisionTargetEnabled
+ );
+
+ if (matchingRecords == null || matchingRecords.Count == 0)
+ {
+ LogManager.Info("[CheckExistingDetectionRecord] SQL基础参数过滤无匹配");
+ return null;
+ }
+
+ LogManager.Info($"[CheckExistingDetectionRecord] SQL找到 {matchingRecords.Count} 条记录,开始比较排除列表");
+
+ // 3. 逐个比较排除列表和手工目标
+ var currentExcluded = GetCurrentExcludedObjects();
+ var currentTargets = GetCurrentManualTargets();
+
+ foreach (var record in matchingRecords)
+ {
+ if (IsExcludedListMatch(record.Id, currentExcluded, pathDatabase) &&
+ IsManualTargetListMatch(record.Id, currentTargets, pathDatabase))
+ {
+ LogManager.Info($"[CheckExistingDetectionRecord] 找到完全匹配: RecordId={record.Id}");
+
+ // 注册到内存缓存
+ _pathAnimationManager.RegisterAnimationHashToRecordId(currentHash, record.Id);
+
+ return ShowDuplicateConfigDialog(record);
+ }
+ }
+
+ LogManager.Info("[CheckExistingDetectionRecord] 未找到完全匹配的记录");
return null;
}
catch (Exception ex)
@@ -3331,6 +3345,115 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}
}
+ ///
+ /// 显示重复配置对话框
+ ///
+ private (int id, UserChoice userChoice)? ShowDuplicateConfigDialog(CollisionDetectionRecord record)
+ {
+ var message = $"检测配置已存在!\n\n" +
+ $"找到一条相同配置的历史检测记录:\n" +
+ $"• 测试名称:{record.TestName ?? "未命名"}\n" +
+ $"• 检测时间:{record.TestTime:yyyy-MM-dd HH:mm:ss}\n" +
+ $"• 碰撞数量:{record.CollisionCount} 个\n\n" +
+ $"您可以:\n" +
+ $"• 【是】直接使用历史记录(跳过重复检测)\n" +
+ $"• 【否】重新生成新记录";
+
+ var result = System.Windows.MessageBox.Show(
+ message,
+ "检测配置已存在",
+ System.Windows.MessageBoxButton.YesNo,
+ System.Windows.MessageBoxImage.Question);
+
+ if (result == System.Windows.MessageBoxResult.Yes)
+ {
+ _lastReusedRecordId = record.Id;
+ return (record.Id, UserChoice.UseExisting);
+ }
+ else
+ {
+ _lastReusedRecordId = null;
+ return (record.Id, UserChoice.CreateNew);
+ }
+ }
+
+ ///
+ /// 获取当前排除对象列表(ModelIndex + PathId)
+ ///
+ private List<(int ModelIndex, string PathId)> GetCurrentExcludedObjects()
+ {
+ var result = new List<(int, string)>();
+ var excludedObjects = _pathAnimationManager?.GetExcludedObjects();
+
+ if (excludedObjects != null)
+ {
+ foreach (var obj in excludedObjects)
+ {
+ if (obj == null) continue;
+ try
+ {
+ var pathId = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(obj);
+ result.Add((pathId.ModelIndex, pathId.PathId));
+ }
+ catch { }
+ }
+ }
+
+ return result.OrderBy(x => x.Item1).ThenBy(x => x.Item2).ToList();
+ }
+
+ ///
+ /// 获取当前手工碰撞目标列表(ModelIndex + PathId)
+ ///
+ private List<(int ModelIndex, string PathId)> GetCurrentManualTargets()
+ {
+ var result = new List<(int, string)>();
+
+ if (IsManualCollisionTargetEnabled && _manualCollisionTargets != null)
+ {
+ foreach (var target in _manualCollisionTargets)
+ {
+ if (target?.ModelItem == null) continue;
+ try
+ {
+ var pathId = Autodesk.Navisworks.Api.Application.ActiveDocument.Models.CreatePathId(target.ModelItem);
+ result.Add((pathId.ModelIndex, pathId.PathId));
+ }
+ catch { }
+ }
+ }
+
+ return result.OrderBy(x => x.Item1).ThenBy(x => x.Item2).ToList();
+ }
+
+ ///
+ /// 比较排除列表是否匹配
+ ///
+ private bool IsExcludedListMatch(int recordId, List<(int ModelIndex, string PathId)> currentList, PathDatabase db)
+ {
+ var dbList = db.GetDetectionExcludedObjects(recordId)
+ .Select(e => (e.ModelIndex, e.PathId))
+ .OrderBy(x => x.ModelIndex)
+ .ThenBy(x => x.PathId)
+ .ToList();
+
+ return dbList.SequenceEqual(currentList);
+ }
+
+ ///
+ /// 比较手工目标列表是否匹配
+ ///
+ private bool IsManualTargetListMatch(int recordId, List<(int ModelIndex, string PathId)> currentList, PathDatabase db)
+ {
+ var dbList = db.GetDetectionManualTargets(recordId)
+ .Select(t => (t.ModelIndex, t.PathId))
+ .OrderBy(x => x.ModelIndex)
+ .ThenBy(x => x.PathId)
+ .ToList();
+
+ return dbList.SequenceEqual(currentList);
+ }
+
private void ExecuteToggleWireframeMode()
{
try