优化ClashDetective集成,移除事务以提高性能

This commit is contained in:
tian 2026-01-13 12:33:39 +08:00
parent 779d043299
commit 0cdae60a00
2 changed files with 197 additions and 203 deletions

View File

@ -2,6 +2,11 @@
## 功能点 ## 功能点
### [2026/1/13]
1. [ ] 优化检查API调用的线程安全问题提高插件的稳定性
2. [ ] 优化减少ClashDetective检测部分的耗时
### [2026/1/6] ### [2026/1/6]
1. [x] BUG虚拟车辆模型每次点击都重建 1. [x] BUG虚拟车辆模型每次点击都重建

View File

@ -614,7 +614,11 @@ namespace NavisworksTransport
_currentTestName = mainTestName; _currentTestName = mainTestName;
LogManager.Info($"[分组测试] 创建主测试: {mainTestName}"); LogManager.Info($"[分组测试] 创建主测试: {mainTestName}");
// 移除事务以优化性能事务开销约30-65%
// 对于碰撞检测场景,事务的必要性很低,即使失败影响也很小
ClashTest addedMainTest = null;
Progress progress = null; Progress progress = null;
try try
{ {
// 开始进度条ClashDetective本身会显示进度这里只提供提示信息 // 开始进度条ClashDetective本身会显示进度这里只提供提示信息
@ -622,244 +626,236 @@ namespace NavisworksTransport
"碰撞检测数据分析中,请稍候..." "碰撞检测数据分析中,请稍候..."
); );
// 使用事务确保操作的原子性 // 第一步:创建主测试(不包含选择集,纯容器)
using (var transaction = doc.BeginTransaction("创建分组碰撞测试")) var mainTest = new ClashTest
{ {
// 第一步:创建主测试(不包含选择集,纯容器) DisplayName = mainTestName,
var mainTest = new ClashTest // HardConservative 模式的缺点是性能低。如果要提高性能可以用Hard模式
{ TestType = ClashTestType.Hard,
DisplayName = mainTestName, Tolerance = detectionGap,
// HardConservative 模式的缺点是性能低。如果要提高性能可以用Hard模式 Guid = Guid.Empty,
TestType = ClashTestType.Hard, MergeComposites = true
Tolerance = detectionGap, };
Guid = Guid.Empty,
MergeComposites = true
};
// 添加主测试到文档 // 添加主测试到文档
_documentClash.TestsData.TestsAddCopy(mainTest); _documentClash.TestsData.TestsAddCopy(mainTest);
LogManager.Info($"[分组测试] 主测试已添加到文档"); LogManager.Info($"[分组测试] 主测试已添加到文档");
// 获取添加后的测试对象引用 // 获取添加后的测试对象引用
var addedMainTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == mainTestName) as ClashTest; addedMainTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == mainTestName) as ClashTest;
if (addedMainTest == null) if (addedMainTest == null)
{
LogManager.Error("无法获取添加后的主测试对象");
return;
}
// 第二步:创建分组并添加碰撞结果(应用智能去重)
// 1. 分组:按碰撞对象对分组
var groupedCollisions = validCollisions
.GroupBy(c => new { Item1 = c.Item1, Item2 = c.Item2 })
.ToList();
LogManager.Info($"[分组测试] 智能去重: {validCollisions.Count} 个检测点 -> {groupedCollisions.Count} 个唯一碰撞对");
// 缓存去重后的碰撞结果(每组取第一个)
lock (_resultsLock)
{
_deduplicatedCollisionResults.Clear();
foreach (var group in groupedCollisions)
{ {
LogManager.Error("无法获取添加后的主测试对象"); _deduplicatedCollisionResults.Add(group.First());
return; }
LogManager.Debug($"[去重缓存] 已缓存 {_deduplicatedCollisionResults.Count} 个去重后的碰撞结果");
}
var collisionGroup = new ClashResultGroup
{
DisplayName = $"碰撞检测组 ({groupedCollisions.Count} 个唯一碰撞对)"
};
LogManager.Info($"[分组测试] 创建碰撞分组: {collisionGroup.DisplayName}");
int confirmedCount = 0;
int skippedCount = 0;
// 2. 遍历每一组
for (int groupIndex = 0; groupIndex < groupedCollisions.Count; groupIndex++)
{
// 检查用户是否取消
if (progress.IsCanceled)
{
LogManager.Info($"[分组测试] 用户取消操作,已处理 {groupIndex}/{groupedCollisions.Count} 组");
break;
} }
var group = groupedCollisions[groupIndex];
// 第二步:创建分组并添加碰撞结果(应用智能去重) // 3. 排序:按距离/重叠深度排序,优先检测最严重的碰撞
// 1. 分组:按碰撞对象对分组 // 注意Distance通常越小或负值越大表示碰撞越深具体取决于计算方式
var groupedCollisions = validCollisions // 这里假设Distance越小越严重
.GroupBy(c => new { Item1 = c.Item1, Item2 = c.Item2 }) var sortedCandidates = group.OrderBy(c => c.Distance).ToList();
.ToList();
LogManager.Info($"[分组测试] 智能去重: {validCollisions.Count} 个检测点 -> {groupedCollisions.Count} 个唯一碰撞对"); bool pairConfirmed = false;
// 缓存去重后的碰撞结果(每组取第一个) // 4. 验证即止:逐个检测候选帧
lock (_resultsLock) for (int i = 0; i < sortedCandidates.Count; i++)
{ {
_deduplicatedCollisionResults.Clear(); var candidate = sortedCandidates[i];
foreach (var group in groupedCollisions)
try
{ {
_deduplicatedCollisionResults.Add(group.First()); // 临时移动动画对象到碰撞位置以执行测试
} var testAnimatedObject = candidate.Item1;
LogManager.Debug($"[去重缓存] 已缓存 {_deduplicatedCollisionResults.Count} 个去重后的碰撞结果"); var modelItems = new ModelItemCollection { testAnimatedObject };
} var targetPosition = candidate.Item1Position;
var collisionGroup = new ClashResultGroup // 计算移动偏移
{ var currentBounds = testAnimatedObject.BoundingBox();
DisplayName = $"碰撞检测组 ({groupedCollisions.Count} 个唯一碰撞对)" var currentPos = new Point3D(
}; (currentBounds.Min.X + currentBounds.Max.X) / 2,
(currentBounds.Min.Y + currentBounds.Max.Y) / 2,
(currentBounds.Min.Z + currentBounds.Max.Z) / 2
);
LogManager.Info($"[分组测试] 创建碰撞分组: {collisionGroup.DisplayName}"); var offset = new Vector3D(
targetPosition.X - currentPos.X,
targetPosition.Y - currentPos.Y,
targetPosition.Z - currentPos.Z
);
int confirmedCount = 0; var transform = Transform3D.CreateTranslation(offset);
int skippedCount = 0; doc.Models.OverridePermanentTransform(modelItems, transform, false);
// 2. 遍历每一组 // 创建临时测试以获取真实碰撞结果
for (int groupIndex = 0; groupIndex < groupedCollisions.Count; groupIndex++) var tempTestName = $"临时验证_{confirmedCount + 1}_{i}_{DateTime.Now:HHmmss_fff}";
{ var tempTest = new ClashTest
// 检查用户是否取消
if (progress.IsCanceled)
{
LogManager.Info($"[分组测试] 用户取消操作,已处理 {groupIndex}/{groupedCollisions.Count} 组");
break;
}
var group = groupedCollisions[groupIndex];
// 3. 排序:按距离/重叠深度排序,优先检测最严重的碰撞
// 注意Distance通常越小或负值越大表示碰撞越深具体取决于计算方式
// 这里假设Distance越小越严重
var sortedCandidates = group.OrderBy(c => c.Distance).ToList();
bool pairConfirmed = false;
// 4. 验证即止:逐个检测候选帧
for (int i = 0; i < sortedCandidates.Count; i++)
{
var candidate = sortedCandidates[i];
try
{ {
// 临时移动动画对象到碰撞位置以执行测试 DisplayName = tempTestName,
var testAnimatedObject = candidate.Item1; TestType = ClashTestType.HardConservative,
var modelItems = new ModelItemCollection { testAnimatedObject }; Tolerance = detectionGap,
var targetPosition = candidate.Item1Position; Guid = Guid.Empty,
MergeComposites = true
};
// 计算移动偏移 // 设置选择集
var currentBounds = testAnimatedObject.BoundingBox(); var selectionA = new ModelItemCollection { candidate.Item1 };
var currentPos = new Point3D( var selectionB = new ModelItemCollection { candidate.Item2 };
(currentBounds.Min.X + currentBounds.Max.X) / 2,
(currentBounds.Min.Y + currentBounds.Max.Y) / 2,
(currentBounds.Min.Z + currentBounds.Max.Z) / 2
);
var offset = new Vector3D( tempTest.SelectionA.Selection.CopyFrom(selectionA);
targetPosition.X - currentPos.X, tempTest.SelectionB.Selection.CopyFrom(selectionB);
targetPosition.Y - currentPos.Y,
targetPosition.Z - currentPos.Z
);
var transform = Transform3D.CreateTranslation(offset); // 添加并运行临时测试
doc.Models.OverridePermanentTransform(modelItems, transform, false); _documentClash.TestsData.TestsAddCopy(tempTest);
var addedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;
// 创建临时测试以获取真实碰撞结果 if (addedTempTest != null)
var tempTestName = $"临时验证_{confirmedCount + 1}_{i}_{DateTime.Now:HHmmss_fff}"; {
var tempTest = new ClashTest // 设置几何类型(非关键操作,失败不影响继续执行)
var copyTest = addedTempTest.CreateCopy() as ClashTest;
copyTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
copyTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
_documentClash.TestsData.TestsEditTestFromCopy(addedTempTest, copyTest);
// 运行测试
_documentClash.TestsData.TestsRunTest(addedTempTest);
// 获取刷新后的测试结果
var refreshedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;
if (refreshedTempTest != null && refreshedTempTest.Children.Count > 0)
{ {
DisplayName = tempTestName, // !!!发现真实碰撞!!!
TestType = ClashTestType.HardConservative, pairConfirmed = true;
Tolerance = detectionGap, confirmedCount++;
Guid = Guid.Empty, skippedCount += (sortedCandidates.Count - 1 - i); // 记录跳过的数量
MergeComposites = true
};
// 设置选择集 // 将碰撞结果复制到分组中
var selectionA = new ModelItemCollection { candidate.Item1 }; int subResultIndex = 1;
var selectionB = new ModelItemCollection { candidate.Item2 }; foreach (var child in refreshedTempTest.Children)
tempTest.SelectionA.Selection.CopyFrom(selectionA);
tempTest.SelectionB.Selection.CopyFrom(selectionB);
// 添加并运行临时测试
_documentClash.TestsData.TestsAddCopy(tempTest);
var addedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;
if (addedTempTest != null)
{
// 使用正确的PrimitiveTypes设置模式
try
{ {
var copyTest = addedTempTest.CreateCopy() as ClashTest; if (child is ClashResult result)
copyTest.SelectionA.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
copyTest.SelectionB.PrimitiveTypes = PrimitiveTypes.Triangles | PrimitiveTypes.Lines | PrimitiveTypes.Points;
_documentClash.TestsData.TestsEditTestFromCopy(addedTempTest, copyTest);
}
catch (Exception geomEx)
{
LogManager.Warning($"设置几何类型失败: {geomEx.Message}");
}
// 运行测试
_documentClash.TestsData.TestsRunTest(addedTempTest);
// 获取刷新后的测试结果
var refreshedTempTest = _documentClash.TestsData.Tests.FirstOrDefault(t => t.DisplayName == tempTestName) as ClashTest;
if (refreshedTempTest != null && refreshedTempTest.Children.Count > 0)
{
// !!!发现真实碰撞!!!
pairConfirmed = true;
confirmedCount++;
skippedCount += (sortedCandidates.Count - 1 - i); // 记录跳过的数量
// 将碰撞结果复制到分组中
int subResultIndex = 1;
foreach (var child in refreshedTempTest.Children)
{ {
if (child is ClashResult result) var copiedResult = result.CreateCopy() as ClashResult;
{ copiedResult.Guid = Guid.NewGuid(); // 生成新的GUID
var copiedResult = result.CreateCopy() as ClashResult;
copiedResult.Guid = Guid.NewGuid(); // 生成新的GUID
// 设置唯一且有意义的碰撞名称 // 设置唯一且有意义的碰撞名称
// 先找到有意义的容器对象,再获取其名称 // 先找到有意义的容器对象,再获取其名称
var container1 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item1); var container1 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item1);
var container2 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item2); var container2 = ModelItemAnalysisHelper.FindNamedParentContainer(result.Item2);
var object1Name = ModelItemAnalysisHelper.GetSafeDisplayName(container1); var object1Name = ModelItemAnalysisHelper.GetSafeDisplayName(container1);
var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(container2); var object2Name = ModelItemAnalysisHelper.GetSafeDisplayName(container2);
var timeStamp = DateTime.Now.ToString("HHmmss"); var timeStamp = DateTime.Now.ToString("HHmmss");
copiedResult.DisplayName = $"物流碰撞#{confirmedCount:00}-{subResultIndex:00}_{timeStamp}: {object1Name} ↔ {object2Name}"; copiedResult.DisplayName = $"物流碰撞#{confirmedCount:00}-{subResultIndex:00}_{timeStamp}: {object1Name} ↔ {object2Name}";
collisionGroup.Children.Add(copiedResult); collisionGroup.Children.Add(copiedResult);
subResultIndex++; subResultIndex++;
}
} }
} }
}
// 删除临时测试 // 清理临时测试
try _documentClash.TestsData.TestsRemove(refreshedTempTest ?? addedTempTest);
{
_documentClash.TestsData.TestsRemove(refreshedTempTest ?? addedTempTest);
}
catch (Exception cleanEx)
{
LogManager.Warning($"清理临时测试失败: {cleanEx.Message}");
}
// 如果已确认碰撞,跳出当前候选循环,不再检测该组剩余帧 // 如果已确认碰撞,跳出当前候选循环,不再检测该组剩余帧
if (pairConfirmed) if (pairConfirmed)
{ {
break; break;
}
} }
} }
catch (Exception itemEx) }
{ catch (Exception itemEx)
LogManager.Error($"候选检测失败: {itemEx.Message}"); {
} LogManager.Error($"候选检测失败: {itemEx.Message}");
} }
} }
}
LogManager.Info($"[分组测试] ClashDetective检测完成: 确认碰撞 {confirmedCount} 组, 跳过 {skippedCount} 个冗余检测点"); LogManager.Info($"[分组测试] ClashDetective检测完成: 确认碰撞 {confirmedCount} 组, 跳过 {skippedCount} 个冗余检测点");
// 第三步:处理碰撞结果 // 第三步:处理碰撞结果
var clashResults = MergeCompositeCollisions(collisionGroup); var clashResults = MergeCompositeCollisions(collisionGroup);
// 缓存结果 // 缓存结果
lock (_clashResultsCacheLock) lock (_clashResultsCacheLock)
{ {
_clashDetectiveResultsCache[_currentTestName] = clashResults; _clashDetectiveResultsCache[_currentTestName] = clashResults;
} }
LogManager.Info($"已缓存ClashDetective结果{clashResults.Count}个碰撞,测试名称:{_currentTestName}"); LogManager.Info($"已缓存ClashDetective结果{clashResults.Count}个碰撞,测试名称:{_currentTestName}");
// 更新碰撞计数器 // 更新碰撞计数器
_clashDetectiveCollisionCount = clashResults.Count; _clashDetectiveCollisionCount = clashResults.Count;
// 保存到数据库 // 保存到数据库
SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle, SaveClashDetectiveResultToDatabase(pathName, routeId, clashResults, frameRate, duration, detectionGap, animatedObject, isVirtualVehicle,
virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight); virtualVehicleLength, virtualVehicleWidth, virtualVehicleHeight);
// 第四步:将分组添加到主测试 // 第四步:将分组添加到主测试
if (collisionGroup.Children.Count > 0) if (collisionGroup.Children.Count > 0)
{ {
_documentClash.TestsData.TestsAddCopy(addedMainTest, collisionGroup); _documentClash.TestsData.TestsAddCopy(addedMainTest, collisionGroup);
} }
else else
{ {
LogManager.Warning("[分组测试] 分组为空(未检测到真实几何碰撞),未添加到主测试"); LogManager.Warning("[分组测试] 分组为空(未检测到真实几何碰撞),未添加到主测试");
}
// 提交事务
transaction.Commit();
} }
} }
catch (Exception groupEx) catch (Exception ex)
{ {
LogManager.Error($"[分组测试] 创建分组测试失败: {groupEx.Message}"); LogManager.Error($"[分组测试] 创建分组测试失败: {ex.Message}");
// 异常时清理已创建的主测试
if (addedMainTest != null)
{
try
{
_documentClash.TestsData.TestsRemove(addedMainTest);
LogManager.Info("[分组测试] 已清理主测试");
}
catch (Exception cleanupEx)
{
LogManager.Warning($"[分组测试] 清理主测试失败: {cleanupEx.Message}");
}
}
} }
finally finally
{ {
@ -910,15 +906,8 @@ namespace NavisworksTransport
RefreshClashDetectiveUI(); RefreshClashDetectiveUI();
// 自动高亮ClashDetective结果 // 自动高亮ClashDetective结果
try ModelHighlightHelper.HighlightClashDetectiveResults(_currentTestName, GetCurrentPathClashResults);
{ LogManager.Info("自动高亮ClashDetective检测结果完成");
ModelHighlightHelper.HighlightClashDetectiveResults(_currentTestName, GetCurrentPathClashResults);
LogManager.Info("自动高亮ClashDetective检测结果完成");
}
catch (Exception clashHighlightEx)
{
LogManager.Error($"自动高亮ClashDetective结果失败: {clashHighlightEx.Message}");
}
LogManager.Info($"=== 碰撞统计最终结果 ==="); LogManager.Info($"=== 碰撞统计最终结果 ===");
LogManager.Info($"动画过程包围盒检测: {_animationCollisionCount}个碰撞 (参考值)"); LogManager.Info($"动画过程包围盒检测: {_animationCollisionCount}个碰撞 (参考值)");