fix: F1 碰撞对象明细落库(报告页面可定位)+ F2 映射率提升
问题:F1 副实例检测结果只回写碰撞总数,碰撞对象明细未落库 → 报告页面数据全 0(无法知道碰撞了哪些物体);且 F2 层级链精确 匹配率低(1/9)——主链根=文档名+模型根、副链根=导出模型根, 去根后差一层导致匹配失败。 修复: - F1 分支把碰撞对象明细写入 ClashDetectiveCollisionObjects (对象名 + F2 映射的主 ModelIndex/PathId——真正可定位的物体) - F2 映射增强:精确匹配失败后尾部匹配(末 2-6 层逐级加深, 唯一候选才映射,避免重名误配) - 盒内映射/映射结果携带 ModelIndex(多模型正确定位) 验证:F1 测试 30 秒通过;映射率 1/9 → 3/3;主库明细含 PathId(Chair-Stacking→0/0/679/0/1 等)
This commit is contained in:
parent
6b13e3126b
commit
54255516fd
@ -641,6 +641,30 @@ namespace NavisworksTransport.Core
|
|||||||
collisionCount: detection.TotalCollisions,
|
collisionCount: detection.TotalCollisions,
|
||||||
animationCollisionCount: detection.TotalCollisions);
|
animationCollisionCount: detection.TotalCollisions);
|
||||||
LogManager.Info($"[批处理] 已回写主库检测记录: DetectionRecordId={item.DetectionRecordId}, 碰撞数={detection.TotalCollisions}");
|
LogManager.Info($"[批处理] 已回写主库检测记录: DetectionRecordId={item.DetectionRecordId}, 碰撞数={detection.TotalCollisions}");
|
||||||
|
|
||||||
|
// 🔥 碰撞对象明细落库(报告页面数据源:ClashDetectiveCollisionObjects)
|
||||||
|
// 副实例对象名 + F2 映射的主模型 PathId(映射失败保留副实例名)
|
||||||
|
if (detection.CollidedObjects != null && detection.CollidedObjects.Count > 0)
|
||||||
|
{
|
||||||
|
var objectRecords = new List<NavisworksTransport.ClashDetectiveCollisionObjectRecord>();
|
||||||
|
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)
|
catch (Exception recordEx)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -63,9 +63,23 @@ namespace NavisworksTransport.Core.Services
|
|||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string SecondaryPathId { get; set; }
|
public string SecondaryPathId { get; set; }
|
||||||
|
public int? MainModelIndex { get; set; }
|
||||||
public string MainPathId { get; set; }
|
public string MainPathId { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>主模型对象定位(CreatePathId 的 ModelIndex + PathId)</summary>
|
||||||
|
private sealed class MainItemPath
|
||||||
|
{
|
||||||
|
public int ModelIndex { get; }
|
||||||
|
public string PathId { get; }
|
||||||
|
|
||||||
|
public MainItemPath(int modelIndex, string pathId)
|
||||||
|
{
|
||||||
|
ModelIndex = modelIndex;
|
||||||
|
PathId = pathId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// F1 总流程硬超时:超时强制关闭副实例并失败,避免批处理无限等待
|
// F1 总流程硬超时:超时强制关闭副实例并失败,避免批处理无限等待
|
||||||
private static readonly TimeSpan OverallTimeout = TimeSpan.FromMinutes(6);
|
private static readonly TimeSpan OverallTimeout = TimeSpan.FromMinutes(6);
|
||||||
|
|
||||||
@ -136,7 +150,7 @@ namespace NavisworksTransport.Core.Services
|
|||||||
|
|
||||||
// 2. 导出局部 NWD(UI 线程:NW API + 导出);同时构建盒内对象映射(chain→主PathId,F2 用)
|
// 2. 导出局部 NWD(UI 线程:NW API + 导出);同时构建盒内对象映射(chain→主PathId,F2 用)
|
||||||
bool exported = false;
|
bool exported = false;
|
||||||
var chainToMainPathId = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
var chainToMainPathId = new Dictionary<string, MainItemPath>(StringComparer.OrdinalIgnoreCase);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
exported = System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
exported = System.Windows.Application.Current.Dispatcher.Invoke(() =>
|
||||||
@ -194,7 +208,7 @@ namespace NavisworksTransport.Core.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var path = document.Models.CreatePathId(boxItem);
|
var path = document.Models.CreatePathId(boxItem);
|
||||||
chainToMainPathId[TrimRootFromChain(chain)] = path.PathId;
|
chainToMainPathId[TrimRootFromChain(chain)] = new MainItemPath(path.ModelIndex, path.PathId);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@ -453,6 +467,46 @@ namespace NavisworksTransport.Core.Services
|
|||||||
return string.Join(" / ", segments.Skip(1).Select(s => s.Trim()));
|
return string.Join(" / ", segments.Skip(1).Select(s => s.Trim()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 链尾部匹配:从末层(对象名)逐级加深,找到唯一以该后缀结尾的主链映射。
|
||||||
|
/// 容忍主/副实例链的头部层数差异(文档根/模型根层)。
|
||||||
|
/// </summary>
|
||||||
|
private static MainItemPath TryMatchChainTail(string tailChain, Dictionary<string, MainItemPath> 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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造 ModelItem 的层级链(AncestorsAndSelf 的 DisplayName,'/' 分隔)。
|
/// 构造 ModelItem 的层级链(AncestorsAndSelf 的 DisplayName,'/' 分隔)。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -495,7 +549,7 @@ namespace NavisworksTransport.Core.Services
|
|||||||
int frameRate,
|
int frameRate,
|
||||||
double durationSeconds,
|
double durationSeconds,
|
||||||
DetectionResult result,
|
DetectionResult result,
|
||||||
Dictionary<string, string> chainToMainPathId,
|
Dictionary<string, MainItemPath> chainToMainPathId,
|
||||||
NavisworksInstanceHandle handle,
|
NavisworksInstanceHandle handle,
|
||||||
DateTime overallDeadline)
|
DateTime overallDeadline)
|
||||||
{
|
{
|
||||||
@ -610,9 +664,21 @@ namespace NavisworksTransport.Core.Services
|
|||||||
if (!string.IsNullOrWhiteSpace(chain))
|
if (!string.IsNullOrWhiteSpace(chain))
|
||||||
{
|
{
|
||||||
string tail = TrimRootFromChain(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -266,6 +266,14 @@ namespace NavisworksTransport.Core.Services
|
|||||||
return;
|
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) &&
|
if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase) &&
|
||||||
string.Equals(request.Path, "/api/test/selection", 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<string>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private object BuildStatusPayload()
|
private object BuildStatusPayload()
|
||||||
{
|
{
|
||||||
Document activeDocument = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
Document activeDocument = Autodesk.Navisworks.Api.Application.ActiveDocument;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user