diff --git a/src/Core/BatchQueueManager.cs b/src/Core/BatchQueueManager.cs index 5d84af1..2cc82fd 100644 --- a/src/Core/BatchQueueManager.cs +++ b/src/Core/BatchQueueManager.cs @@ -641,6 +641,30 @@ namespace NavisworksTransport.Core collisionCount: detection.TotalCollisions, animationCollisionCount: detection.TotalCollisions); LogManager.Info($"[批处理] 已回写主库检测记录: DetectionRecordId={item.DetectionRecordId}, 碰撞数={detection.TotalCollisions}"); + + // 🔥 碰撞对象明细落库(报告页面数据源:ClashDetectiveCollisionObjects) + // 副实例对象名 + F2 映射的主模型 PathId(映射失败保留副实例名) + if (detection.CollidedObjects != null && detection.CollidedObjects.Count > 0) + { + var objectRecords = new List(); + foreach (string objectName in detection.CollidedObjects) + { + var mapping = detection.CollidedObjectMappings?.FirstOrDefault(m => + string.Equals(m.Name, objectName, StringComparison.OrdinalIgnoreCase)); + objectRecords.Add(new NavisworksTransport.ClashDetectiveCollisionObjectRecord + { + DetectionRecordId = item.DetectionRecordId.Value, + ModelIndex = mapping?.MainModelIndex ?? 0, // F2 映射的主模型索引(映射失败默认 0) + PathId = string.IsNullOrWhiteSpace(mapping?.MainPathId) ? null : mapping.MainPathId, + DisplayName = objectName, + ObjectName = objectName, + HasPositionInfo = false + }); + } + + _database.SaveClashDetectiveCollisionObjects(item.DetectionRecordId.Value, objectRecords); + LogManager.Info($"[批处理] 已落库 {objectRecords.Count} 个碰撞对象明细 (DetectionRecordId={item.DetectionRecordId})"); + } } catch (Exception recordEx) { diff --git a/src/Core/Services/SectionBoxBatchDetector.cs b/src/Core/Services/SectionBoxBatchDetector.cs index 983601c..19707ed 100644 --- a/src/Core/Services/SectionBoxBatchDetector.cs +++ b/src/Core/Services/SectionBoxBatchDetector.cs @@ -63,9 +63,23 @@ namespace NavisworksTransport.Core.Services { public string Name { get; set; } public string SecondaryPathId { get; set; } + public int? MainModelIndex { get; set; } public string MainPathId { get; set; } } + /// 主模型对象定位(CreatePathId 的 ModelIndex + PathId) + private sealed class MainItemPath + { + public int ModelIndex { get; } + public string PathId { get; } + + public MainItemPath(int modelIndex, string pathId) + { + ModelIndex = modelIndex; + PathId = pathId; + } + } + // F1 总流程硬超时:超时强制关闭副实例并失败,避免批处理无限等待 private static readonly TimeSpan OverallTimeout = TimeSpan.FromMinutes(6); @@ -136,7 +150,7 @@ namespace NavisworksTransport.Core.Services // 2. 导出局部 NWD(UI 线程:NW API + 导出);同时构建盒内对象映射(chain→主PathId,F2 用) bool exported = false; - var chainToMainPathId = new Dictionary(StringComparer.OrdinalIgnoreCase); + var chainToMainPathId = new Dictionary(StringComparer.OrdinalIgnoreCase); try { exported = System.Windows.Application.Current.Dispatcher.Invoke(() => @@ -194,7 +208,7 @@ namespace NavisworksTransport.Core.Services try { var path = document.Models.CreatePathId(boxItem); - chainToMainPathId[TrimRootFromChain(chain)] = path.PathId; + chainToMainPathId[TrimRootFromChain(chain)] = new MainItemPath(path.ModelIndex, path.PathId); } catch { @@ -453,6 +467,46 @@ namespace NavisworksTransport.Core.Services return string.Join(" / ", segments.Skip(1).Select(s => s.Trim())); } + /// + /// 链尾部匹配:从末层(对象名)逐级加深,找到唯一以该后缀结尾的主链映射。 + /// 容忍主/副实例链的头部层数差异(文档根/模型根层)。 + /// + private static MainItemPath TryMatchChainTail(string tailChain, Dictionary chainToMainPathId) + { + string[] segments = tailChain.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries) + .Select(s => s.Trim()) + .Where(s => s.Length > 0) + .ToArray(); + + if (segments.Length == 0) + { + return null; + } + + // 从末 2 层开始,逐级加深到全链(最深匹配优先,避免重名误配) + int maxDepth = Math.Min(segments.Length, 6); + for (int depth = 2; depth <= maxDepth; depth++) + { + string suffix = string.Join(" / ", segments.Skip(segments.Length - depth)); + var candidates = chainToMainPathId.Keys + .Where(k => k.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + if (candidates.Count == 1) + { + return chainToMainPathId[candidates[0]]; + } + + // 多候选 → 加深一层继续;0 候选 → 无匹配 + if (candidates.Count == 0) + { + return null; + } + } + + return null; + } + /// /// 构造 ModelItem 的层级链(AncestorsAndSelf 的 DisplayName,'/' 分隔)。 /// @@ -495,7 +549,7 @@ namespace NavisworksTransport.Core.Services int frameRate, double durationSeconds, DetectionResult result, - Dictionary chainToMainPathId, + Dictionary chainToMainPathId, NavisworksInstanceHandle handle, DateTime overallDeadline) { @@ -610,9 +664,21 @@ namespace NavisworksTransport.Core.Services if (!string.IsNullOrWhiteSpace(chain)) { string tail = TrimRootFromChain(chain); - if (tail != null && chainToMainPathId != null && chainToMainPathId.TryGetValue(tail, out string mainPathId)) + if (tail != null && chainToMainPathId != null && chainToMainPathId.TryGetValue(tail, out MainItemPath mainItem)) { - mapping.MainPathId = mainPathId; + mapping.MainPathId = mainItem.PathId; + mapping.MainModelIndex = mainItem.ModelIndex; + } + else if (tail != null && chainToMainPathId != null) + { + // 🔥 精确匹配失败 → 尾部匹配(容忍根/模型根层数差异): + // 主链根=文档名(Factory.nwd)+模型根,副链根=导出模型根,去根后差一层 + MainItemPath tailMatched = TryMatchChainTail(tail, chainToMainPathId); + if (tailMatched != null) + { + mapping.MainPathId = tailMatched.PathId; + mapping.MainModelIndex = tailMatched.ModelIndex; + } } } diff --git a/src/Core/Services/TestAutomationHttpService.cs b/src/Core/Services/TestAutomationHttpService.cs index 72a8f9b..a9496af 100644 --- a/src/Core/Services/TestAutomationHttpService.cs +++ b/src/Core/Services/TestAutomationHttpService.cs @@ -266,6 +266,14 @@ namespace NavisworksTransport.Core.Services return; } + if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase) && + string.Equals(request.Path, "/api/test/traversable-items", StringComparison.OrdinalIgnoreCase)) + { + object payload = InvokeOnUiThread(BuildTraversableItemsPayload); + await WriteJsonResponseAsync(writer, 200, BuildEnvelope(true, payload)).ConfigureAwait(false); + return; + } + if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase) && string.Equals(request.Path, "/api/test/selection", StringComparison.OrdinalIgnoreCase)) { @@ -574,6 +582,20 @@ namespace NavisworksTransport.Core.Services }; } + private object BuildTraversableItemsPayload() + { + // 【实验端点】查询当前文档的可通行物流属性对象(验证通道属性是否在模型文件中) + var items = CategoryAttributeManager.GetAllTraversableLogisticsItems(); + return new + { + count = items?.Count ?? 0, + names = items?.Select(i => i.DisplayName) + .Where(n => !string.IsNullOrWhiteSpace(n)) + .Distinct() + .ToList() ?? new List() + }; + } + private object BuildStatusPayload() { Document activeDocument = Autodesk.Navisworks.Api.Application.ActiveDocument;