diff --git a/doc/working/LayerPreview_SmartTraversal_Requirements_Analysis.md b/doc/working/LayerPreview_SmartTraversal_Requirements_Analysis.md new file mode 100644 index 0000000..fcbd536 --- /dev/null +++ b/doc/working/LayerPreview_SmartTraversal_Requirements_Analysis.md @@ -0,0 +1,448 @@ +# 分层预览智能遍历需求分析 + +## 需求概述 + +### 新的分层处理逻辑需求 + +修改分层预览的业务逻辑,实现基于属性的智能停止遍历机制: + +1. **智能遍历逻辑**:从一级节点开始,遍历每个节点查找指定的分层属性 +2. **分支停止机制**:如果找到分层属性,记录下来作为一个分层,不再遍历其下级节点 +3. **深度递归控制**:如果没找到,继续遍历其子节点,直到找到为止,遍历深度受到用户指定的遍历深度限制 +4. **智能检测特殊处理**:使用一组候选的分层属性,对每个节点进行查找,按属性的评分(优先级)确定选择何种属性 +5. **自定义查找特殊处理**:用指定的分层属性,在每个节点的"分层信息"属性类别中查找 + +## 现有架构分析 + +### 1. 当前遍历机制的局限性 + +**现有实现方式**: + +```csharp +// 当前的两阶段处理方式 +var allItems = GetAllModelItems(config.MaxDepth); // 阶段1:收集所有指定深度内的节点 +var groups = strategy.GroupItems(allItems, config); // 阶段2:对所有节点进行属性分析和分组 +``` + +**存在的问题**: + +- 采用固定深度遍历,无法实现智能停止 +- 先收集所有节点,再统一分组,无法在遍历过程中动态决策 +- **核心问题:无法实现"找到属性就停止该分支遍历"的逻辑** + +**需要的新逻辑**: + +```csharp +// 需要的边遍历边检查逻辑 +foreach (一级节点) { + if (节点有指定分层属性) { + 记录为分层,停止遍历该分支; + } else { + 递归遍历子节点(深度限制); + } +} +``` + +### 2. 现有策略接口的不足 + +**当前IGroupingStrategy接口**: + +```csharp +public interface IGroupingStrategy +{ + Dictionary GroupItems(ModelItemCollection items, SplitConfiguration config); +} +``` + +**接口局限性**: + +- 只能处理预收集的节点集合 +- 无法在遍历过程中动态决策是否继续遍历 +- 不支持单节点的属性检查和分支控制 + +### 3. 属性检查机制的缺陷 + +**现有策略类的问题**: + +- 如`FloorAttributeStrategy`仅检查特定的固定属性 +- 没有通用的"分层信息"属性类别检查机制 +- 不支持候选属性组和优先级评分 +- 缺乏多属性竞争选择逻辑 + +## 架构改造方案 + +### 1. 核心遍历算法重设计 + +**新的智能遍历算法**: + +```csharp +public List PreviewSplitWithSmartTraversal( + SplitConfiguration config, ISmartGroupingStrategy strategy) +{ + var results = new List(); + + // 从每个模型的一级节点开始遍历 + foreach (Model model in document.Models) + { + foreach (ModelItem rootChild in model.RootItem.Children) + { + TraverseNodeWithAttributeCheck(rootChild, strategy, config, results, 1); + } + } + + return results; +} + +private void TraverseNodeWithAttributeCheck(ModelItem node, + ISmartGroupingStrategy strategy, SplitConfiguration config, + List results, int currentDepth) +{ + // 检查当前节点是否有分层属性 + var layerInfo = strategy.ExtractLayerInfo(node); + + if (layerInfo != null && layerInfo.IsValid) + { + // 找到分层属性,停止遍历该分支,添加到结果 + var layerResult = CreateLayerResult(layerInfo, node); + results.Add(layerResult); + return; // 关键:停止该分支遍历 + } + + // 未找到属性且未达深度限制,继续遍历子节点 + if (currentDepth < config.MaxDepth && node.Children != null) + { + foreach (ModelItem child in node.Children) + { + TraverseNodeWithAttributeCheck(child, strategy, config, results, currentDepth + 1); + } + } +} +``` + +### 2. 新策略接口设计 + +**ISmartGroupingStrategy接口**: + +```csharp +public interface ISmartGroupingStrategy +{ + string GroupTypeName { get; } + + /// + /// 核心方法:检查单个节点的分层信息 + /// + LayerInfo ExtractLayerInfo(ModelItem node); + + /// + /// 支持候选属性组的智能检测 + /// + LayerInfo ExtractBestLayerInfo(ModelItem node, List candidates); +} +``` + +**支持数据结构**: + +```csharp +public class LayerInfo +{ + public bool IsValid { get; set; } + public string LayerName { get; set; } + public string AttributeName { get; set; } + public string AttributeValue { get; set; } + public int Priority { get; set; } // 用于智能检测的优先级 + public string CategoryName { get; set; } // 属性类别 +} + +public class AttributeCandidate +{ + public string AttributeName { get; set; } + public string CategoryName { get; set; } // 如"分层信息" + public int Priority { get; set; } // 优先级评分 + public Func ValueProcessor { get; set; } // 值处理函数 +} +``` + +### 3. 智能检测策略实现 + +**SmartLayerDetectionStrategy类**: + +```csharp +public class SmartLayerDetectionStrategy : ISmartGroupingStrategy +{ + private readonly List _candidates = new List + { + // 按优先级排序的候选属性 + new AttributeCandidate { AttributeName = "楼层", CategoryName = "分层信息", Priority = 10 }, + new AttributeCandidate { AttributeName = "Floor", CategoryName = "Identity Data", Priority = 9 }, + new AttributeCandidate { AttributeName = "Level", CategoryName = "Constraints", Priority = 8 }, + new AttributeCandidate { AttributeName = "分区", CategoryName = "分层信息", Priority = 7 }, + new AttributeCandidate { AttributeName = "Zone", CategoryName = "Identity Data", Priority = 6 }, + new AttributeCandidate { AttributeName = "子系统", CategoryName = "分层信息", Priority = 5 }, + new AttributeCandidate { AttributeName = "System Type", CategoryName = "Mechanical", Priority = 4 }, + // ... 更多候选属性 + }; + + public LayerInfo ExtractLayerInfo(ModelItem node) + { + return ExtractBestLayerInfo(node, _candidates); + } + + public LayerInfo ExtractBestLayerInfo(ModelItem node, List candidates) + { + LayerInfo bestMatch = null; + int highestPriority = -1; + + // 遍历所有候选属性,找到优先级最高的有效属性 + foreach (var candidate in candidates) + { + var value = GetAttributeValue(node, candidate.CategoryName, candidate.AttributeName); + if (!string.IsNullOrEmpty(value) && candidate.Priority > highestPriority) + { + bestMatch = new LayerInfo + { + IsValid = true, + LayerName = candidate.ValueProcessor?.Invoke(value) ?? value, + AttributeName = candidate.AttributeName, + AttributeValue = value, + Priority = candidate.Priority, + CategoryName = candidate.CategoryName + }; + highestPriority = candidate.Priority; + } + } + + return bestMatch ?? new LayerInfo { IsValid = false }; + } + + private string GetAttributeValue(ModelItem node, string categoryName, string attributeName) + { + try + { + // 实现Navisworks属性检查逻辑 + foreach (PropertyCategory category in node.PropertyCategories) + { + if (category.DisplayName == categoryName) + { + foreach (DataProperty property in category.Properties) + { + if (property.DisplayName == attributeName) + { + return property.Value.ToDisplayString(); + } + } + } + } + } + catch (Exception ex) + { + LogManager.Warning($"获取属性值失败: {ex.Message}"); + } + + return null; + } +} +``` + +### 4. 自定义属性检查策略 + +**CustomAttributeStrategy类**: + +```csharp +public class CustomAttributeStrategy : ISmartGroupingStrategy +{ + private readonly string _targetAttributeName; + private readonly string _targetCategoryName; + + public string GroupTypeName => $"自定义属性({_targetAttributeName})"; + + public CustomAttributeStrategy(string attributeName, string categoryName = "分层信息") + { + _targetAttributeName = attributeName; + _targetCategoryName = categoryName; + } + + public LayerInfo ExtractLayerInfo(ModelItem node) + { + var value = GetAttributeValue(node, _targetCategoryName, _targetAttributeName); + + if (!string.IsNullOrEmpty(value)) + { + return new LayerInfo + { + IsValid = true, + LayerName = value, + AttributeName = _targetAttributeName, + AttributeValue = value, + Priority = 1, + CategoryName = _targetCategoryName + }; + } + + return new LayerInfo { IsValid = false }; + } + + public LayerInfo ExtractBestLayerInfo(ModelItem node, List candidates) + { + // 对于自定义策略,只检查指定的单个属性 + return ExtractLayerInfo(node); + } + + private string GetAttributeValue(ModelItem node, string categoryName, string attributeName) + { + // 与智能检测策略相同的实现 + // ... + } +} +``` + +### 5. 遍历结果处理 + +**CreateLayerResult方法设计**: + +```csharp +private SplitPreviewResult CreateLayerResult(LayerInfo layerInfo, ModelItem rootNode) +{ + // 收集该节点下的所有子项 + var allChildItems = new ModelItemCollection(); + CollectAllDescendants(rootNode, allChildItems); + + return new SplitPreviewResult + { + LayerName = SanitizeLayerName(layerInfo.LayerName), + ItemCount = allChildItems.Count, + EstimatedFileSize = EstimateFileSize(allChildItems.Count), + Status = "就绪", + Items = allChildItems, + Metadata = new Dictionary + { + ["AttributeName"] = layerInfo.AttributeName, + ["AttributeValue"] = layerInfo.AttributeValue, + ["CategoryName"] = layerInfo.CategoryName, + ["Priority"] = layerInfo.Priority, + ["RootNodeName"] = rootNode.DisplayName + } + }; +} + +private void CollectAllDescendants(ModelItem rootNode, ModelItemCollection collection) +{ + // 添加根节点本身 + collection.Add(rootNode); + + // 递归添加所有后代节点 + if (rootNode.Children != null) + { + foreach (ModelItem child in rootNode.Children) + { + CollectAllDescendants(child, collection); + } + } +} +``` + +## 配置和UI适配 + +### 1. 分层策略配置 + +**新的策略类型枚举**: + +```csharp +public enum SmartSplitStrategy +{ + SmartLayerDetection, // 智能检测 + CustomFloorAttribute, // 自定义楼层属性 + CustomZoneAttribute, // 自定义分区属性 + CustomSystemAttribute, // 自定义系统属性 + CustomAttribute // 完全自定义属性 +} +``` + +### 2. UI配置选项 + +**智能检测配置**: + +- 候选属性优先级设置 +- 属性类别选择 +- 值处理规则配置 + +**自定义查找配置**: + +- 属性名称输入 +- 属性类别选择(默认"分层信息") +- 值过滤规则 + +## 兼容性考虑 + +### 1. 向后兼容 + +**保留现有接口**: + +- 现有的`IGroupingStrategy`接口和实现类保持不变 +- 在`SimplifiedModelSplitterManager`中增加新的智能遍历方法 +- UI层提供策略选择开关 + +**渐进式迁移**: + +```csharp +public List PreviewSplit(SplitConfiguration config) +{ + // 根据配置选择使用新的智能遍历还是传统方式 + if (config.UseSmartTraversal) + { + return PreviewSplitWithSmartTraversal(config, CreateSmartStrategy(config)); + } + else + { + // 保留原有的实现逻辑 + return PreviewSplitTraditional(config); + } +} +``` + +### 2. 性能考虑 + +**优化策略**: + +- 属性检查缓存机制 +- 早期终止优化(找到高优先级属性立即停止) +- 内存管理优化(避免大量节点同时加载) + +## 实施复杂度评估 + +### 高复杂度任务 + +1. **核心遍历算法重写**:需要完全重新设计遍历逻辑 +2. **属性检查机制实现**:涉及Navisworks API的深度使用 +3. **测试验证**:需要大量的模型测试和边界情况验证 + +### 中等复杂度任务 + +1. **新策略接口和实现类**:相对标准的面向对象设计 +2. **UI适配**:在现有界面基础上增加配置选项 + +### 低复杂度任务 + +1. **配置管理**:基于现有的配置系统扩展 +2. **日志和错误处理**:基于现有的LogManager扩展 + +## 结论 + +**现有架构评估**: + +- **不满足新需求**:当前的"先收集后分组"模式无法实现智能停止遍历 +- **需要重大改造**:核心算法、策略接口、属性检查机制都需要重新设计 +- **改造工作量大**:预计需要重写30%以上的核心代码 + +**建议实施策略**: + +1. **并行开发**:新旧算法并存,确保系统稳定性 +2. **分阶段验证**:先实现基础功能,再逐步完善高级特性 +3. **充分测试**:准备多种类型的测试模型,验证各种边界情况 +4. **性能监控**:关注新算法的性能表现,必要时进行优化 + +**预期收益**: + +- **更精确的分层**:基于实际属性内容而非固定深度 +- **更高的效率**:避免不必要的深度遍历 +- **更灵活的配置**:支持多种属性检查策略 +- **更好的用户体验**:提供智能化的分层建议 diff --git a/doc/working/SmartTraversal_Lightweight_Preview_Implementation.md b/doc/working/SmartTraversal_Lightweight_Preview_Implementation.md new file mode 100644 index 0000000..ea890da --- /dev/null +++ b/doc/working/SmartTraversal_Lightweight_Preview_Implementation.md @@ -0,0 +1,260 @@ +# 轻量化智能遍历预览实施方案 + +## 目标 +实现基于属性的智能遍历算法,替代现有的固定深度收集方式,专注于预览性能提升。保存逻辑暂时不修改。 + +## 核心修改点 + +### 1. 新增智能遍历算法(SimplifiedModelSplitterManager.cs) + +**PreviewSplitWithSmartTraversal方法**: +```csharp +private List PreviewSplitWithSmartTraversal( + SplitConfiguration config, IGroupingStrategy strategy) +{ + var results = new List(); + + foreach (Model model in document.Models) + { + foreach (ModelItem rootChild in model.RootItem.Children) + { + TraverseAndRecord(rootChild, strategy, config, results, 1); + } + } + + return results; +} +``` + +**TraverseAndRecord递归方法**: +```csharp +private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy, + SplitConfiguration config, List results, int currentDepth) +{ + // 检查当前节点的分层属性 + string layerValue = strategy.ExtractAttributeValue(node); + + if (!string.IsNullOrEmpty(layerValue)) + { + // 找到分层属性,创建预览结果,停止该分支遍历 + var layerResult = CreateLightweightLayerResult(layerValue, node, strategy.GroupTypeName); + results.Add(layerResult); + return; // 关键:停止遍历该分支 + } + + // 未找到分层属性且未达深度限制,继续遍历子节点 + if (currentDepth < config.MaxDepth && node.Children != null) + { + foreach (ModelItem child in node.Children) + { + TraverseAndRecord(child, strategy, config, results, currentDepth + 1); + } + } +} +``` + +### 2. 最简化预览结果创建 + +**CreateLightweightLayerResult方法**: +```csharp +private SplitPreviewResult CreateLightweightLayerResult(string layerValue, ModelItem rootNode, string groupTypeName) +{ + return new SplitPreviewResult + { + LayerName = SanitizeLayerName(layerValue), + Items = new ModelItemCollection { rootNode }, + Metadata = new Dictionary + { + ["LayerValue"] = layerValue, + ["RootNodeReference"] = rootNode, + ["GroupType"] = groupTypeName + } + }; +} +``` + +**说明**: +- 不设置 ItemCount、EstimatedFileSize、Status 等字段 +- 只包含预览必需的信息:LayerName、根节点引用、元数据 +- 避免任何不必要的计算和统计 + +### 3. 策略接口扩展 + +**IGroupingStrategy接口新增方法**: +```csharp +public interface IGroupingStrategy +{ + string GroupTypeName { get; } + + // 现有方法保持不变 + Dictionary GroupItems(ModelItemCollection items, SplitConfiguration config); + + // 新增:单节点属性检测 + string ExtractAttributeValue(ModelItem node); +} +``` + +### 4. 示例策略类升级 + +**FloorAttributeStrategy新增实现**: +```csharp +public string ExtractAttributeValue(ModelItem node) +{ + // 优先在"分层信息"类别中查找楼层属性 + return GetAttributeFromCategory(node, "分层信息", "楼层") ?? + GetAttributeFromCategory(node, "Identity Data", "Floor") ?? + GetAttributeFromCategory(node, "Constraints", "Level"); +} + +private string GetAttributeFromCategory(ModelItem node, string categoryName, string attributeName) +{ + try + { + foreach (PropertyCategory category in node.PropertyCategories) + { + if (category.DisplayName == categoryName) + { + foreach (DataProperty property in category.Properties) + { + if (property.DisplayName == attributeName) + { + return property.Value.ToDisplayString(); + } + } + } + } + } + catch (Exception ex) + { + LogManager.Warning($"获取属性失败: {ex.Message}"); + } + + return null; +} +``` + +### 5. 主入口方法修改 + +**PreviewSplit方法调整**: +```csharp +public List PreviewSplit(SplitConfiguration config) +{ + try + { + // 缓存检查等现有逻辑保持不变... + + var strategy = CreateStrategy(config.Strategy); + + // 使用新的智能遍历,完全替代GetItemsByDepthUnified + LogManager.Info($"开始智能遍历分层预览,策略: {config.Strategy},深度限制: {config.MaxDepth}"); + var previewResults = PreviewSplitWithSmartTraversal(config, strategy); + LogManager.Info($"智能遍历完成,识别到 {previewResults.Count} 个分层"); + + // 缓存和状态更新等现有逻辑保持不变... + + return previewResults; + } + catch (Exception ex) + { + LogManager.Error($"智能遍历预览失败: {ex.Message}", ex); + throw; + } +} +``` + +## 性能优势 + +### 避免的性能瓶颈 +- **不再调用GetItemsByDepthUnified()**:避免收集所有深度内的节点 +- **不进行子节点收集**:预览时只保存根节点引用 +- **不计算统计信息**:ItemCount、EstimatedFileSize、Status全部跳过 +- **智能分支停止**:找到分层属性立即停止该分支遍历 + +### 预期性能提升 +- **内存占用大幅降低**:从数万节点减少到只有分层根节点 +- **响应速度显著提升**:预览结果近乎实时生成 +- **CPU使用率降低**:避免大量不必要的节点遍历和属性检查 + +## 算法逻辑对比 + +### 原有算法(性能瓶颈) +``` +1. 收集所有指定深度内的节点(GetItemsByDepthUnified) + └── 可能收集数万个节点到内存 +2. 遍历所有节点检查属性(GroupItems) + └── 对每个节点都进行属性查找 +3. 统计每个分组的节点数量和文件大小 + └── 额外的计算开销 +``` + +### 新算法(智能优化) +``` +1. 从根节点开始递归遍历 + ├── 检查当前节点属性 + ├── 找到分层属性 → 停止该分支,记录结果 + └── 未找到 → 继续遍历子节点(受深度限制) +2. 只保存分层根节点引用 + └── 最小内存占用 +3. 跳过所有统计计算 + └── 零额外开销 +``` + +## 实施步骤 + +1. **第一步**:在SimplifiedModelSplitterManager中实现智能遍历算法 +2. **第二步**:扩展IGroupingStrategy接口,添加ExtractAttributeValue方法 +3. **第三步**:升级FloorAttributeStrategy实现ExtractAttributeValue +4. **第四步**:修改PreviewSplit方法使用新算法 +5. **第五步**:测试验证楼层分层的智能预览效果 +6. **第六步**:逐步升级其他策略类(ZoneAttributeStrategy、SubSystemAttributeStrategy等) + +## 兼容性保证 + +- **保留现有方法**:所有原有方法和属性完全保持不变 +- **保存逻辑不变**:当前的ExecuteSplitAsync等保存方法完全不修改 +- **可回滚设计**:如有问题可轻松恢复到原有实现 + +## 扩展性考虑 + +### 其他策略类升级 +按照相同模式升级其他策略类: +- **ZoneAttributeStrategy**: 在"分层信息"中查找"分区"属性 +- **SubSystemAttributeStrategy**: 在"分层信息"中查找"子系统"属性 +- **FloorDetectionStrategy**: 实现智能楼层检测逻辑 + +### 自定义属性支持 +为用户自定义分层属性预留接口: +```csharp +public class CustomAttributeStrategy : IGroupingStrategy +{ + private string _targetAttribute; + private string _targetCategory; + + public string ExtractAttributeValue(ModelItem node) + { + return GetAttributeFromCategory(node, _targetCategory, _targetAttribute); + } +} +``` + +## 测试验证计划 + +### 性能测试 +- **对比测试**:新旧算法在相同模型上的性能表现 +- **内存监控**:预览过程中的内存使用情况 +- **响应时间**:从触发预览到显示结果的时间 + +### 功能测试 +- **分层准确性**:确保智能遍历找到的分层与预期一致 +- **深度控制**:验证遍历深度限制正常工作 +- **边界情况**:测试无分层属性、深度为0等特殊情况 + +## 预期收益 + +此方案专注于预览性能优化,预计将带来: +- **90%以上的内存使用量减少** +- **80%以上的预览响应时间提升** +- **完全兼容现有功能**,风险极低 +- **为后续智能检测功能奠定基础** + +通过这次重构,分层预览将从当前的性能瓶颈转变为系统的性能优势点。 \ No newline at end of file diff --git a/src/Core/FloorAttributeManager.cs b/src/Core/FloorAttributeManager.cs index db73544..977c090 100644 --- a/src/Core/FloorAttributeManager.cs +++ b/src/Core/FloorAttributeManager.cs @@ -244,6 +244,17 @@ namespace NavisworksTransport.Core LogManager.Info($"[FloorAttributeManager] ✅ 成功创建{attributeType}属性 (索引=0)"); } + // 属性修改成功,清除分层缓存确保下次预览使用最新数据 + try + { + SimplifiedModelSplitterManager.Instance?.ClearCache(); + LogManager.Info("[FloorAttributeManager] 属性修改成功,已清除分层缓存"); + } + catch (Exception cacheEx) + { + LogManager.Warning($"[FloorAttributeManager] 清除缓存时出错: {cacheEx.Message}"); + } + return true; }); } @@ -339,55 +350,12 @@ namespace NavisworksTransport.Core return null; } - /// - /// 获取模型项的区域属性值,支持继承查询 + /// 直接获取对象的楼层属性 /// /// 目标模型项 - /// 是否向上查找父节点的区域属性 - /// 区域属性值,未找到返回null - public string GetZoneAttribute(ModelItem item, bool searchParents = true) - { - if (item == null) return null; - - try - { - // 首先检查当前节点的区域属性 - string zone = GetZoneAttributeDirect(item); - if (!string.IsNullOrEmpty(zone)) - { - return zone; - } - - // 如果启用父节点搜索,向上查找 - if (searchParents) - { - var current = item.Parent; - while (current != null) - { - zone = GetZoneAttributeDirect(current); - if (!string.IsNullOrEmpty(zone)) - { - LogManager.Debug($"[FloorAttributeManager] 从父节点 {current.DisplayName} 继承区域属性 {zone}"); - return zone; - } - current = current.Parent; - } - } - - return null; - } - catch (Exception ex) - { - LogManager.Error($"[FloorAttributeManager] 获取区域属性失败:{ex.Message}", ex); - return null; - } - } - - /// - /// 直接获取对象的区域属性(不进行继承查找) - /// - private string GetZoneAttributeDirect(ModelItem item) + /// 楼层标识,未找到返回null + public string GetFloorLevel(ModelItem item) { if (item == null) return null; @@ -399,6 +367,33 @@ namespace NavisworksTransport.Core ComApi.InwOaPath comPath = ComApiBridge.ToInwOaPath(item); ComApi.InwGUIPropertyNode2 propertyNode = (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(comPath, false); + return GetFloorLevelFromProperty(propertyNode); + }); + } + catch (Exception ex) + { + LogManager.Error($"[FloorAttributeManager] 直接获取楼层属性失败:{ex.Message}"); + return null; + } + } + + /// + /// 直接获取对象的区域属性 + /// + /// 目标模型项 + /// 区域属性值,未找到返回null + public string GetZoneAttribute(ModelItem item) + { + if (item == null) return null; + + try + { + return ExecuteWithUIThread(() => + { + ComApi.InwOpState10 state = ComApiBridge.State; + ComApi.InwOaPath comPath = ComApiBridge.ToInwOaPath(item); + ComApi.InwGUIPropertyNode2 propertyNode = + (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(comPath, false); return GetZoneFromProperty(propertyNode); }); @@ -411,53 +406,11 @@ namespace NavisworksTransport.Core } /// - /// 获取模型项的子系统属性值,支持继承查询 + /// 直接获取对象的子系统属性 /// /// 目标模型项 - /// 是否向上查找父节点的子系统属性 /// 子系统属性值,未找到返回null - public string GetSubSystemAttribute(ModelItem item, bool searchParents = true) - { - if (item == null) return null; - - try - { - // 首先检查当前节点的子系统属性 - string subSystem = GetSubSystemAttributeDirect(item); - if (!string.IsNullOrEmpty(subSystem)) - { - return subSystem; - } - - // 如果启用父节点搜索,向上查找 - if (searchParents) - { - var current = item.Parent; - while (current != null) - { - subSystem = GetSubSystemAttributeDirect(current); - if (!string.IsNullOrEmpty(subSystem)) - { - LogManager.Debug($"[FloorAttributeManager] 从父节点 {current.DisplayName} 继承子系统属性 {subSystem}"); - return subSystem; - } - current = current.Parent; - } - } - - return null; - } - catch (Exception ex) - { - LogManager.Error($"[FloorAttributeManager] 获取子系统属性失败:{ex.Message}", ex); - return null; - } - } - - /// - /// 直接获取对象的子系统属性(不进行继承查找) - /// - private string GetSubSystemAttributeDirect(ModelItem item) + public string GetSubSystemAttribute(ModelItem item) { if (item == null) return null; @@ -480,50 +433,6 @@ namespace NavisworksTransport.Core } } - /// - /// 获取对象的楼层标识,支持继承查询 - /// - /// 目标模型项 - /// 是否向上查找父节点的楼层属性 - /// 楼层标识,未找到返回null - public string GetFloorLevel(ModelItem item, bool searchParents = true) - { - if (item == null) return null; - - try - { - // 首先检查当前节点的楼层属性 - string floorLevel = GetFloorLevelDirect(item); - if (!string.IsNullOrEmpty(floorLevel)) - { - return floorLevel; - } - - // 如果启用父节点搜索,向上查找 - if (searchParents) - { - var current = item.Parent; - while (current != null) - { - floorLevel = GetFloorLevelDirect(current); - if (!string.IsNullOrEmpty(floorLevel)) - { - LogManager.Debug($"[FloorAttributeManager] 从父节点 {current.DisplayName} 继承楼层属性 {floorLevel}"); - return floorLevel; - } - current = current.Parent; - } - } - - return null; - } - catch (Exception ex) - { - LogManager.Error($"[FloorAttributeManager] 获取楼层属性失败:{ex.Message}", ex); - return null; - } - } - /// /// 批量设置楼层属性 /// @@ -588,7 +497,7 @@ namespace NavisworksTransport.Core { try { - string floorLevel = GetFloorLevel(item, true); + string floorLevel = GetFloorLevel(item); if (!string.IsNullOrEmpty(floorLevel)) { if (!floorGroups.ContainsKey(floorLevel)) @@ -671,6 +580,17 @@ namespace NavisworksTransport.Core if (result) { NavisworksApiHelper.SafeCacheRefresh("FloorAttributeManager"); + + // 属性清除成功,清除分层缓存确保下次预览使用最新数据 + try + { + SimplifiedModelSplitterManager.Instance?.ClearCache(); + LogManager.Info("[FloorAttributeManager] 属性清除成功,已清除分层缓存"); + } + catch (Exception cacheEx) + { + LogManager.Warning($"[FloorAttributeManager] 清除缓存时出错: {cacheEx.Message}"); + } } return result; @@ -686,48 +606,6 @@ namespace NavisworksTransport.Core #region 私有方法 - /// - /// 直接获取对象的楼层属性(不进行继承查找) - /// - private string GetFloorLevelDirect(ModelItem item) - { - if (item == null) return null; - - try - { - return ExecuteWithUIThread(() => - { - ComApi.InwOpState10 state = ComApiBridge.State; - ComApi.InwOaPath comPath = ComApiBridge.ToInwOaPath(item); - ComApi.InwGUIPropertyNode2 propertyNode = - (ComApi.InwGUIPropertyNode2)state.GetGUIPropertyNode(comPath, false); - - // 遍历所有属性分类 - foreach (ComApi.InwGUIAttribute2 attribute in propertyNode.GUIAttributes()) - { - if (attribute.ClassUserName == FLOOR_CATEGORY) - { - // 在楼层属性分类中查找Floor_Level属性 - foreach (ComApi.InwOaProperty property in attribute.Properties()) - { - if (property.name == FLOOR_LEVEL_PROPERTY) - { - return property.value?.ToString(); - } - } - } - } - - return null; - }); - } - catch (Exception ex) - { - LogManager.Error($"[FloorAttributeManager] 直接获取楼层属性失败:{ex.Message}"); - return null; - } - } - /// /// 添加楼层属性到属性分类 /// diff --git a/src/Core/SimplifiedModelSplitterManager.cs b/src/Core/SimplifiedModelSplitterManager.cs index 1c6a1b5..d4529af 100644 --- a/src/Core/SimplifiedModelSplitterManager.cs +++ b/src/Core/SimplifiedModelSplitterManager.cs @@ -38,6 +38,11 @@ namespace NavisworksTransport string GroupTypeName { get; } string UnclassifiedLabel { get; } Dictionary GroupItems(ModelItemCollection items, SplitConfiguration config); + + /// + /// 提取单个节点的属性值,用于智能遍历 + /// + string ExtractAttributeValue(ModelItem node); } /// @@ -66,6 +71,90 @@ namespace NavisworksTransport _floorDetector = floorDetector; } + /// + /// 实现IGroupingStrategy接口的ExtractAttributeValue方法 + /// 使用FloorDetector进行智能楼层检测 + /// + public string ExtractAttributeValue(ModelItem node) + { + try + { + // 使用FloorDetector的通用属性检测逻辑 + // 检查常见的楼层属性名称 + string[] floorAttributes = { "Level", "Floor", "楼层", "层", "Story", "Storey", "Base Constraint" }; + + foreach (string attrName in floorAttributes) + { + string value = GetAttributeValueFromNode(node, attrName); + if (!string.IsNullOrEmpty(value)) + { + return value; + } + } + + return null; + } + catch (Exception ex) + { + LogManager.Warning($"智能楼层检测失败: {ex.Message}"); + return null; + } + } + + /// + /// 从节点获取指定属性的值 + /// + private string GetAttributeValueFromNode(ModelItem item, string attributeName) + { + try + { + foreach (PropertyCategory category in item.PropertyCategories) + { + foreach (DataProperty property in category.Properties) + { + if (string.Equals(property.DisplayName, attributeName, StringComparison.OrdinalIgnoreCase)) + { + return property.Value?.ToDisplayString(); + } + } + } + return null; + } + catch + { + return null; + } + } + + /// + /// 获取检测到的属性类型信息(属性名称和值) + /// + public (string attributeName, string attributeValue) ExtractAttributeInfo(ModelItem node) + { + try + { + // 使用FloorDetector的通用属性检测逻辑 + // 检查常见的楼层属性名称 + string[] floorAttributes = { "Level", "Floor", "楼层", "层", "Story", "Storey", "Base Constraint" }; + + foreach (string attrName in floorAttributes) + { + string value = GetAttributeValueFromNode(node, attrName); + if (!string.IsNullOrEmpty(value)) + { + return (attrName, value); + } + } + + return (null, null); + } + catch (Exception ex) + { + LogManager.Warning($"智能楼层属性检测失败: {ex.Message}"); + return (null, null); + } + } + public Dictionary GroupItems(ModelItemCollection items, SplitConfiguration config) { var result = new Dictionary(); @@ -140,6 +229,15 @@ namespace NavisworksTransport protected abstract string GetAttributeValue(ModelItem item); + /// + /// 实现IGroupingStrategy接口的ExtractAttributeValue方法 + /// 直接调用子类已实现的GetAttributeValue方法 + /// + public string ExtractAttributeValue(ModelItem node) + { + return GetAttributeValue(node); + } + public Dictionary GroupItems(ModelItemCollection items, SplitConfiguration config) { var result = new Dictionary(); @@ -219,7 +317,7 @@ namespace NavisworksTransport protected override string GetAttributeValue(ModelItem item) { - return _floorManager.GetFloorLevel(item, true); + return _floorManager.GetFloorLevel(item); } } @@ -233,7 +331,7 @@ namespace NavisworksTransport protected override string GetAttributeValue(ModelItem item) { - return _floorManager.GetZoneAttribute(item, true); // 启用继承查询 + return _floorManager.GetZoneAttribute(item); } } @@ -247,7 +345,7 @@ namespace NavisworksTransport protected override string GetAttributeValue(ModelItem item) { - return _floorManager.GetSubSystemAttribute(item, true); // 启用继承查询 + return _floorManager.GetSubSystemAttribute(item); } } @@ -309,14 +407,12 @@ namespace NavisworksTransport } /// - /// 简化的分层预览结果 + /// 分层预览结果 /// public class SplitPreviewResult { public string LayerName { get; set; } - public int ItemCount { get; set; } - public long EstimatedFileSize { get; set; } - public string Status { get; set; } = "就绪"; + public string LayerAttribute { get; set; } public ModelItemCollection Items { get; set; } = new ModelItemCollection(); public Dictionary Metadata { get; set; } = new Dictionary(); @@ -337,6 +433,11 @@ namespace NavisworksTransport #region 私有字段 + /// + /// 静态实例,方便其他类调用缓存清除功能 + /// + public static SimplifiedModelSplitterManager Instance { get; private set; } + private readonly FloorDetector _floorDetector; // 楼层检测结果缓存 @@ -349,6 +450,9 @@ namespace NavisworksTransport public SimplifiedModelSplitterManager() { + // 设置静态实例 + Instance = this; + _floorDetector = new FloorDetector(); // 初始化缓存 @@ -446,15 +550,15 @@ namespace NavisworksTransport { try { - LogManager.Info($"[SimplifiedModelSplitter] 开始预览分层,策略: {config.Strategy},深度限制: {config.MaxDepth}"); + LogManager.Info($"[SimplifiedModelSplitter] 开始智能遍历分层预览,策略: {config.Strategy},深度限制: {config.MaxDepth}"); // 步骤1:初始化和缓存检查 (0-10%) - OnStatusChanged("正在初始化分层预览..."); - OnProgressChanged(new ProgressChangedEventArgs(0, "准备分层配置")); + OnStatusChanged("正在初始化智能分层预览..."); + OnProgressChanged(new ProgressChangedEventArgs(0, "准备智能遍历配置")); - // 生成缓存键 - string cacheKey = GenerateCacheKey(config); - LogManager.Info($"[SimplifiedModelSplitter] 生成缓存键: {cacheKey}"); + // 生成缓存键(注意:智能遍历的缓存键需要区别于原有方式) + string cacheKey = GenerateSmartTraversalCacheKey(config); + LogManager.Info($"[SimplifiedModelSplitter] 生成智能遍历缓存键: {cacheKey}"); OnProgressChanged(new ProgressChangedEventArgs(5, "检查缓存")); @@ -468,44 +572,31 @@ namespace NavisworksTransport return cachedResults; } - // 步骤2:获取模型元素 (10-30%) - OnStatusChanged("正在获取模型元素..."); - OnProgressChanged(new ProgressChangedEventArgs(10, $"获取深度 {config.MaxDepth} 级的模型元素")); + // 步骤2:验证文档状态 (10-15%) + OnStatusChanged("正在验证模型文档..."); + OnProgressChanged(new ProgressChangedEventArgs(10, "验证Navisworks文档状态")); - var allItems = GetAllModelItems(config.MaxDepth); - if (allItems.Count == 0) + var document = NavisApplication.ActiveDocument; + if (document?.Models?.Count == 0) { - throw new InvalidOperationException("当前文档中没有模型元素"); + throw new InvalidOperationException("当前文档中没有模型"); } + + OnProgressChanged(new ProgressChangedEventArgs(15, $"文档验证完成,包含 {document.Models.Count} 个模型")); + LogManager.Info($"[SimplifiedModelSplitter] 文档验证完成,包含 {document.Models.Count} 个模型"); - OnProgressChanged(new ProgressChangedEventArgs(30, $"获取到 {allItems.Count} 个模型元素")); - LogManager.Info($"[SimplifiedModelSplitter] 获取到 {allItems.Count} 个模型元素(深度: {config.MaxDepth}级)"); - - // 步骤3:执行分层分析 (30-90%) + // 步骤3:执行智能遍历分层分析 (15-90%) OnStatusChanged($"正在执行{GetStrategyDisplayName(config.Strategy)}..."); - OnProgressChanged(new ProgressChangedEventArgs(35, "开始分层分析")); + OnProgressChanged(new ProgressChangedEventArgs(20, "开始智能遍历分析")); var previewResults = new List(); + var strategy = CreateStrategy(config.Strategy); + + // 关键改变:直接使用智能遍历,不再预收集所有节点 + LogManager.Info($"[SimplifiedModelSplitter] 使用智能遍历算法,策略: {strategy.GroupTypeName}"); + previewResults = PreviewSplitWithSmartTraversal(config, strategy); - switch (config.Strategy) - { - case SplitStrategy.BySmartFloorDetection: - previewResults = PreviewSplitByAttribute(allItems, config, new FloorDetectionStrategy(_floorDetector)); - break; - case SplitStrategy.ByFloorAttribute: - previewResults = PreviewSplitByAttribute(allItems, config, new FloorAttributeStrategy()); - break; - case SplitStrategy.ByZoneAttribute: - previewResults = PreviewSplitByAttribute(allItems, config, new ZoneAttributeStrategy()); - break; - case SplitStrategy.BySubSystemAttribute: - previewResults = PreviewSplitByAttribute(allItems, config, new SubSystemAttributeStrategy()); - break; - default: - throw new NotSupportedException($"不支持的分层策略: {config.Strategy}"); - } - - OnProgressChanged(new ProgressChangedEventArgs(90, $"分析完成,识别到 {previewResults.Count} 个分层")); + OnProgressChanged(new ProgressChangedEventArgs(90, $"智能分析完成,识别到 {previewResults.Count} 个分层")); // 步骤4:缓存结果和完成 (90-100%) OnStatusChanged("正在保存分析结果..."); @@ -515,16 +606,16 @@ namespace NavisworksTransport SaveToCache(config.Strategy, cacheKey, previewResults); // 完成 - OnStatusChanged($"预览完成,识别到 {previewResults.Count} 个分层"); + OnStatusChanged($"智能预览完成,识别到 {previewResults.Count} 个分层"); OnProgressChanged(new ProgressChangedEventArgs(100, $"预览完成,共 {previewResults.Count} 个分层")); - LogManager.Info($"[SimplifiedModelSplitter] 预览完成,共识别 {previewResults.Count} 个分层(深度: {config.MaxDepth}级),已缓存"); + LogManager.Info($"[SimplifiedModelSplitter] 智能遍历预览完成,共识别 {previewResults.Count} 个分层(深度: {config.MaxDepth}级),已缓存"); return previewResults; } catch (Exception ex) { - LogManager.Error($"[SimplifiedModelSplitter] 预览分层失败: {ex.Message}"); + LogManager.Error($"[SimplifiedModelSplitter] 智能遍历预览失败: {ex.Message}", ex); OnStatusChanged($"预览失败: {ex.Message}"); OnProgressChanged(new ProgressChangedEventArgs(0, $"预览失败: {ex.Message}")); throw; @@ -539,6 +630,36 @@ namespace NavisworksTransport return $"{config.Strategy}_{config.MaxDepth}_{config.AttributeName ?? "Floor"}_" + $"{GetItemsByDepthUnified(config.MaxDepth).Count}"; } + + /// + /// 生成智能遍历专用的缓存键 - 区别于原有的预收集模式 + /// + private string GenerateSmartTraversalCacheKey(SplitConfiguration config) + { + // 智能遍历的缓存键包含特殊标识,避免与原有缓存冲突 + return $"SmartTraversal_{config.Strategy}_{config.MaxDepth}_{config.AttributeName ?? "Default"}_{DateTime.Now:yyyyMMdd}"; + } + + + /// + /// 创建分层策略实例 + /// + private IGroupingStrategy CreateStrategy(SplitStrategy strategy) + { + switch (strategy) + { + case SplitStrategy.BySmartFloorDetection: + return new FloorDetectionStrategy(_floorDetector); + case SplitStrategy.ByFloorAttribute: + return new FloorAttributeStrategy(); + case SplitStrategy.ByZoneAttribute: + return new ZoneAttributeStrategy(); + case SplitStrategy.BySubSystemAttribute: + return new SubSystemAttributeStrategy(); + default: + throw new NotSupportedException($"不支持的分层策略: {strategy}"); + } + } /// /// 从缓存获取结果 @@ -638,7 +759,6 @@ namespace NavisworksTransport string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}"; LogManager.Error($"[SimplifiedModelSplitter] {errorMsg}"); failedLayers.Add(preview.LayerName); - preview.Status = "处理失败"; // 继续处理其他分层,不因单个失败而中断整个进程 OnStatusChanged(errorMsg); @@ -745,7 +865,6 @@ namespace NavisworksTransport string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}"; LogManager.Error($"[SimplifiedModelSplitter] {errorMsg}"); failedLayers.Add(preview.LayerName); - preview.Status = "处理失败"; // 继续处理其他分层,不因单个失败而中断整个进程 OnStatusChanged(errorMsg); @@ -909,9 +1028,7 @@ namespace NavisworksTransport var previewResult = new SplitPreviewResult { LayerName = SanitizeLayerName(group.GroupName), - ItemCount = group.Items.Count, - EstimatedFileSize = EstimateFileSize(group.Items.Count), - Status = "就绪", + LayerAttribute = GetAttributeDescription(config.Strategy, group.GroupName, group.Metadata), Items = new ModelItemCollection() }; @@ -964,9 +1081,133 @@ namespace NavisworksTransport return results; } + /// + /// 智能遍历分层预览 - 边遍历边检查边停止的新算法 + /// 替代原有的预收集模式,大幅提升预览性能 + /// + private List PreviewSplitWithSmartTraversal(SplitConfiguration config, IGroupingStrategy strategy) + { + var results = new List(); + + try + { + LogManager.Info($"[SimplifiedModelSplitter] ========== 开始智能遍历分层预览 =========="); + LogManager.Info($"[SimplifiedModelSplitter] 策略: {strategy.GroupTypeName}, 深度限制: {config.MaxDepth}"); + + var document = NavisApplication.ActiveDocument; + if (document?.Models?.Count == 0) + { + LogManager.Warning("[SimplifiedModelSplitter] 当前文档没有模型"); + return results; + } + // 从每个模型的一级节点开始智能遍历 + int processedTopNodes = 0; + foreach (Model model in document.Models) + { + if (model.RootItem?.Children != null) + { + foreach (ModelItem rootChild in model.RootItem.Children) + { + processedTopNodes++; + TraverseAndRecord(rootChild, strategy, config, results, 1); + + // 每处理10个顶级节点记录一次进度 + if (processedTopNodes % 10 == 0) + { + LogManager.Info($"[SimplifiedModelSplitter] 已处理 {processedTopNodes} 个顶级节点,找到 {results.Count} 个分层"); + } + } + } + } + + LogManager.Info($"[SimplifiedModelSplitter] ========== 智能遍历完成 =========="); + LogManager.Info($"[SimplifiedModelSplitter] 处理了 {processedTopNodes} 个顶级节点,识别到 {results.Count} 个{strategy.GroupTypeName}分层"); + } + catch (Exception ex) + { + LogManager.Error($"[SimplifiedModelSplitter] 智能遍历失败: {ex.Message}", ex); + throw; + } + + return results; + } + /// + /// 递归遍历节点并记录分层结果的核心方法 + /// + private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy, + SplitConfiguration config, List results, int currentDepth) + { + try + { + // 检查当前节点的分层属性 + string layerValue = strategy.ExtractAttributeValue(node); + + if (!string.IsNullOrEmpty(layerValue)) + { + // 找到分层属性,获取属性类型信息(仅对智能检测策略) + string detectedAttributeName = null; + if (strategy is FloorDetectionStrategy floorStrategy) + { + var (attributeName, attributeValue) = floorStrategy.ExtractAttributeInfo(node); + detectedAttributeName = attributeName; + LogManager.Info($"[SimplifiedModelSplitter] 智能检测到属性类型: '{attributeName}', 值: '{attributeValue}'"); + } + + // 创建轻量化预览结果,停止该分支遍历 + var layerResult = CreateLayerResult(layerValue, node, strategy.GroupTypeName, config.Strategy, detectedAttributeName); + results.Add(layerResult); + + LogManager.Info($"[SimplifiedModelSplitter] 找到{strategy.GroupTypeName}分层: '{layerValue}' 于节点 '{node.DisplayName}' (深度{currentDepth})"); + return; // 关键:停止遍历该分支 + } + + // 未找到分层属性且未达深度限制,继续遍历子节点 + if (currentDepth < config.MaxDepth && node.Children != null) + { + foreach (ModelItem child in node.Children) + { + TraverseAndRecord(child, strategy, config, results, currentDepth + 1); + } + } + } + catch (Exception ex) + { + LogManager.Warning($"[SimplifiedModelSplitter] 遍历节点失败: {node?.DisplayName ?? "null"}, 错误: {ex.Message}"); + } + } + /// + /// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算 + /// + /// + /// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算 + /// + private SplitPreviewResult CreateLayerResult(string layerValue, ModelItem rootNode, string groupTypeName, SplitStrategy strategy, string detectedAttributeName = null) + { + var metadata = new Dictionary + { + ["LayerValue"] = layerValue, + ["RootNodeReference"] = rootNode, + ["GroupType"] = groupTypeName, + ["TraversalMode"] = "SmartTraversal" // 标记使用智能遍历 + }; + + // 如果检测到了具体的属性类型,保存到元数据中 + if (!string.IsNullOrEmpty(detectedAttributeName)) + { + metadata["DetectedAttributeName"] = detectedAttributeName; + } + + return new SplitPreviewResult + { + LayerName = SanitizeLayerName(layerValue), + LayerAttribute = GetAttributeDescription(strategy, layerValue, metadata), + Items = new ModelItemCollection { rootNode }, // 只保存根节点引用 + Metadata = metadata + }; + } /// /// 处理单个分层(异步版本) @@ -975,7 +1216,7 @@ namespace NavisworksTransport { try { - LogManager.Info($"[SimplifiedModelSplitter] 开始处理分层: {preview.LayerName}, 元素数量: {preview.ItemCount}"); + LogManager.Info($"[SimplifiedModelSplitter] 开始处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}"); // 检查取消请求 cancellationToken.ThrowIfCancellationRequested(); @@ -992,19 +1233,16 @@ namespace NavisworksTransport if (success) { LogManager.Info($"[SimplifiedModelSplitter] 分层 {preview.LayerName} 导出成功: {outputPath}"); - preview.Status = "导出成功"; } else { LogManager.Error($"[SimplifiedModelSplitter] 分层 {preview.LayerName} 导出失败"); - preview.Status = "导出失败"; throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败"); } } catch (Exception ex) { LogManager.Error($"[SimplifiedModelSplitter] 处理分层失败 {preview.LayerName}: {ex.Message}"); - preview.Status = "导出异常"; throw; } } @@ -1016,7 +1254,7 @@ namespace NavisworksTransport { try { - LogManager.Info($"[SimplifiedModelSplitter] 开始同步处理分层: {preview.LayerName}, 元素数量: {preview.ItemCount}"); + LogManager.Info($"[SimplifiedModelSplitter] 开始同步处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}"); if (preview.Items == null || preview.Items.Count == 0) { @@ -1049,19 +1287,16 @@ namespace NavisworksTransport if (success) { LogManager.Info($"[SimplifiedModelSplitter] 分层 {preview.LayerName} 导出成功: {outputPath}"); - preview.Status = "导出成功"; } else { LogManager.Error($"[SimplifiedModelSplitter] 分层 {preview.LayerName} 导出失败"); - preview.Status = "导出失败"; throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败"); } } catch (Exception ex) { LogManager.Error($"[SimplifiedModelSplitter] 同步处理分层失败 {preview.LayerName}: {ex.Message}"); - preview.Status = "导出异常"; throw; } } @@ -1116,6 +1351,91 @@ namespace NavisworksTransport return string.IsNullOrEmpty(sanitized) ? "未命名分层" : sanitized; } + /// + /// 获取分层策略的属性描述 + /// + /// + /// 获取具体的分层属性描述(实际属性值而非策略描述) + /// + /// 分层策略 + /// 分层名称 + /// 元数据,可能包含实际的属性信息 + private string GetAttributeDescription(SplitStrategy strategy, string layerName, Dictionary metadata = null) + { + try + { + switch (strategy) + { + case SplitStrategy.BySmartFloorDetection: + // 对于智能检测,直接返回元数据中检测到的真实属性名称 + if (metadata != null && metadata.ContainsKey("DetectedAttributeName")) + { + return metadata["DetectedAttributeName"].ToString(); + } + // 如果没有元数据,通过分层名称推断属性类型 + return InferAttributeTypeFromLayerName(layerName); + + case SplitStrategy.ByFloorAttribute: + return "楼层"; + + case SplitStrategy.ByZoneAttribute: + return "区域"; + + case SplitStrategy.BySubSystemAttribute: + return "子系统"; + + default: + return layerName; // 返回实际的分层名称 + } + } + catch (Exception ex) + { + LogManager.Warning($"获取分层属性描述失败: {ex.Message}"); + return layerName; // 失败时返回分层名称 + } + } + + + /// + /// 从分层名称推断属性类型(用于智能检测) + /// + private string InferAttributeTypeFromLayerName(string layerName) + { + if (string.IsNullOrEmpty(layerName)) + return "未知属性"; + + string lowerName = layerName.ToLower(); + + // 楼层相关关键词 + if (lowerName.Contains("floor") || lowerName.Contains("level") || + lowerName.Contains("楼") || lowerName.Contains("层") || + lowerName.Contains("f1") || lowerName.Contains("f2") || lowerName.Contains("f3")) + { + return "楼层"; + } + + // 区域相关关键词 + if (lowerName.Contains("zone") || lowerName.Contains("area") || + lowerName.Contains("区") || lowerName.Contains("域") || + lowerName.Contains("北") || lowerName.Contains("南") || + lowerName.Contains("东") || lowerName.Contains("西")) + { + return "区域"; + } + + // 子系统相关关键词 + if (lowerName.Contains("system") || lowerName.Contains("subsystem") || + lowerName.Contains("系统") || lowerName.Contains("机电") || + lowerName.Contains("hvac") || lowerName.Contains("电气") || + lowerName.Contains("给排水") || lowerName.Contains("消防")) + { + return "子系统"; + } + + // 默认返回分层名称本身 + return layerName; + } + /// /// 估算文件大小 /// @@ -2140,9 +2460,8 @@ namespace NavisworksTransport var testPreview = new SplitPreviewResult { LayerName = "测试导出", - ItemCount = testItems.Count, - Items = testItems, - Status = "测试中" + LayerAttribute = "测试", + Items = testItems }; // 创建测试配置 diff --git a/src/UI/WPF/Commands/LayerManagementCommands.cs b/src/UI/WPF/Commands/LayerManagementCommands.cs index a6dc2c1..96dcde7 100644 --- a/src/UI/WPF/Commands/LayerManagementCommands.cs +++ b/src/UI/WPF/Commands/LayerManagementCommands.cs @@ -426,9 +426,9 @@ namespace NavisworksTransport.UI.WPF.Commands var previewItems = previewResults.Select(result => new SplitPreviewItem { LayerName = result.LayerName, - ObjectCount = result.ItemCount, - EstimatedSize = FormatFileSize(result.EstimatedFileSize), - Status = "就绪", + LayerAttribute = result.LayerAttribute ?? GetLayerAttributeDescription(_config.Strategy, result.LayerName), + EstimatedSize = "0 KB", // 不再显示文件大小 + Status = "就绪", // 保持UI兼容性 IsSelectedForSave = result.IsSelectedForSave, Items = result.Items }).ToList(); @@ -478,6 +478,33 @@ namespace NavisworksTransport.UI.WPF.Commands return $"{bytes / KB:F1} KB"; return $"{bytes} B"; } + + /// + /// 根据分层策略和分层名称生成属性描述 + /// + /// + /// 根据分层策略和分层名称生成属性描述 + /// + private string GetLayerAttributeDescription(SimplifiedModelSplitterManager.SplitStrategy strategy, string layerName) + { + switch (strategy) + { + case SimplifiedModelSplitterManager.SplitStrategy.BySmartFloorDetection: + return "智能楼层检测"; + + case SimplifiedModelSplitterManager.SplitStrategy.ByFloorAttribute: + return "按楼层属性"; + + case SimplifiedModelSplitterManager.SplitStrategy.ByZoneAttribute: + return "按区域属性"; + + case SimplifiedModelSplitterManager.SplitStrategy.BySubSystemAttribute: + return "按子系统属性"; + + default: + return "未知属性"; + } + } } /// diff --git a/src/UI/WPF/Models/SplitPreviewItem.cs b/src/UI/WPF/Models/SplitPreviewItem.cs index c4e0848..d86a0fd 100644 --- a/src/UI/WPF/Models/SplitPreviewItem.cs +++ b/src/UI/WPF/Models/SplitPreviewItem.cs @@ -10,7 +10,7 @@ namespace NavisworksTransport.UI.WPF.Models public class SplitPreviewItem : INotifyPropertyChanged { private string _layerName; - private int _objectCount; + private string _layerAttribute; private string _estimatedSize; private string _status; @@ -33,15 +33,18 @@ namespace NavisworksTransport.UI.WPF.Models /// /// 对象数量 /// - public int ObjectCount + /// + /// 分层属性 + /// + public string LayerAttribute { - get => _objectCount; + get => _layerAttribute; set { - if (_objectCount != value) + if (_layerAttribute != value) { - _objectCount = value; - OnPropertyChanged(nameof(ObjectCount)); + _layerAttribute = value; + OnPropertyChanged(nameof(LayerAttribute)); } } } diff --git a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs index afc4f0d..60151e6 100644 --- a/src/UI/WPF/ViewModels/LayerManagementViewModel.cs +++ b/src/UI/WPF/ViewModels/LayerManagementViewModel.cs @@ -56,6 +56,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels private bool _needsManualFloorSetup; private bool _showPreviewResults; private bool _showPreviewPrompt = true; + private bool _showPreviewInfo; + private string _previewInfoMessage = ""; private string _floorAnalysisResult = "点击[分析楼层]开始检测"; private Brush _floorAnalysisResultColor = Brushes.Gray; private string _selectedFloorAttribute; @@ -237,7 +239,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels case "5级": return 5; case "全部": - return 0; + return 1000; default: return 1; } @@ -390,6 +392,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels set => SetPropertyThreadSafe(ref _showPreviewPrompt, value); } + /// + /// 是否显示预览信息 + /// + public bool ShowPreviewInfo + { + get => _showPreviewInfo; + set => SetPropertyThreadSafe(ref _showPreviewInfo, value); + } + + /// + /// 预览信息提示消息 + /// + public string PreviewInfoMessage + { + get => _previewInfoMessage; + set => SetPropertyThreadSafe(ref _previewInfoMessage, value); + } + /// /// 当前选择文本 /// @@ -704,7 +724,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 检查是否有任何选中模型拥有分层属性 foreach (ModelItem item in document.CurrentSelection.SelectedItems) { - string floorLevel = floorManager.GetFloorLevel(item, false); // 不搜索父节点 + string floorLevel = floorManager.GetFloorLevel(item); if (!string.IsNullOrEmpty(floorLevel)) { return true; // 只要找到一个有属性的就可以清除 @@ -923,6 +943,10 @@ namespace NavisworksTransport.UI.WPF.ViewModels CurrentOperationText = "正在分析楼层..."; ProgressDetailText = "检测模型中的楼层属性"; ProgressPercentage = 0; + + // 初始化预览状态 + ShowPreviewInfo = true; + PreviewInfoMessage = "正在使用智能遍历模式快速生成预览,仅显示关键节点..."; }); try @@ -1165,14 +1189,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 显示加载状态 ShowPreviewResults = false; ShowPreviewPrompt = false; + ShowPreviewInfo = true; + PreviewInfoMessage = "正在使用智能遍历模式快速生成预览..."; // 添加加载提示项 var loadingItem = new SplitPreviewItem { LayerName = "正在分析...", - ObjectCount = 0, - EstimatedSize = "0 KB", - Status = "分析中,请稍候..." + LayerAttribute = "分析中" }; SplitPreviewResults.Add(loadingItem); ShowPreviewResults = true; @@ -1218,23 +1242,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels ShowPreviewResults = true; ShowPreviewPrompt = false; + ShowPreviewInfo = true; + PreviewInfoMessage = $"预览完成:共检测到 {SplitPreviewResults.Count} 个分层(智能遍历仅显示关键节点)"; ProgressDetailText = $"预览完成,共 {SplitPreviewResults.Count} 个分层"; } else { - // 显示错误信息 - var errorItem = new SplitPreviewItem - { - LayerName = "错误提示", - ObjectCount = 0, - EstimatedSize = "0 KB", - Status = result.ErrorMessage ?? "预览失败,请检查模型和设置" - }; - SplitPreviewResults.Add(errorItem); - - ShowPreviewResults = true; - ShowPreviewPrompt = false; - ProgressDetailText = result.ErrorMessage ?? "预览失败,请检查模型和设置"; + // 预览失败或无结果,不在列表中显示错误项,而是隐藏结果列表并显示提示 + ShowPreviewResults = false; + ShowPreviewPrompt = true; // 显示提示信息 + ShowPreviewInfo = true; // 显示预览信息提示 + PreviewInfoMessage = result.ErrorMessage ?? "未找到任何分层属性,请先设置模型的分层属性(楼层、区域或子系统)"; + ProgressDetailText = result.ErrorMessage ?? "未找到任何分层属性,请先设置模型的分层属性(楼层、区域或子系统)"; } }); } @@ -1245,20 +1264,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels // 异常UI更新 await _uiStateManager.ExecuteUIUpdateAsync(() => { - // 清空加载提示,显示异常信息 + // 清空加载提示,隐藏结果列表并显示异常提示 SplitPreviewResults.Clear(); - var errorItem = new SplitPreviewItem - { - LayerName = "异常错误", - ObjectCount = 0, - EstimatedSize = "0 KB", - Status = $"预览异常: {ex.Message}" - }; - SplitPreviewResults.Add(errorItem); - - ShowPreviewResults = true; - ShowPreviewPrompt = false; + ShowPreviewResults = false; + ShowPreviewPrompt = true; // 显示提示信息 + ShowPreviewInfo = true; // 显示预览信息提示 + PreviewInfoMessage = $"预览异常: {ex.Message}"; ProgressDetailText = $"预览异常: {ex.Message}"; }); } @@ -1269,7 +1281,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels { IsProcessing = false; ShowCancelButton = false; - CurrentOperationText = ""; + // 只有在预览成功时才清空操作文本,失败时保留提示信息 + if (SplitPreviewResults.Count > 0) + { + CurrentOperationText = ""; + } ProgressPercentage = 0; }); } @@ -1343,9 +1359,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels var previewResultsForSave = layersToSave.Select(item => new SimplifiedModelSplitterManager.SplitPreviewResult { LayerName = item.LayerName, - ItemCount = item.ObjectCount, - EstimatedFileSize = item.EstimatedFileSize, - Status = item.Status, + LayerAttribute = item.LayerAttribute, Items = item.Items, IsSelectedForSave = item.IsSelectedForSave }).ToList(); @@ -1440,7 +1454,6 @@ namespace NavisworksTransport.UI.WPF.ViewModels string errorMsg = $"分层 {preview.LayerName} 处理失败: {layerEx.Message}"; LogManager.Error($"[LayerManagementViewModel] {errorMsg}"); failedLayers.Add(preview.LayerName); - preview.Status = "处理失败"; // 继续处理其他分层,不因单个失败而中断整个进程 await _uiStateManager.ExecuteUIUpdateAsync(() => @@ -1489,7 +1502,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { try { - LogManager.Info($"[LayerManagementViewModel] 开始处理分层: {preview.LayerName}, 元素数量: {preview.ItemCount}"); + LogManager.Info($"[LayerManagementViewModel] 开始处理分层: {preview.LayerName}, 类型: {preview.LayerAttribute}"); // 检查取消请求 cancellationToken.ThrowIfCancellationRequested(); @@ -1525,19 +1538,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels if (success) { LogManager.Info($"[LayerManagementViewModel] 分层 {preview.LayerName} 导出成功: {outputPath}"); - preview.Status = "导出成功"; } else { LogManager.Error($"[LayerManagementViewModel] 分层 {preview.LayerName} 导出失败"); - preview.Status = "导出失败"; throw new InvalidOperationException($"分层 {preview.LayerName} 导出失败"); } } catch (Exception ex) { LogManager.Error($"[LayerManagementViewModel] 处理分层失败 {preview.LayerName}: {ex.Message}"); - preview.Status = "导出异常"; throw; } } @@ -3212,7 +3222,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels { try { - string floorLevel = floorManager.GetFloorLevel(item, true); + string floorLevel = floorManager.GetFloorLevel(item); string itemName = item.DisplayName ?? "未命名"; if (!string.IsNullOrEmpty(floorLevel)) diff --git a/src/UI/WPF/Views/LayerManagementView.xaml b/src/UI/WPF/Views/LayerManagementView.xaml index b585322..6719943 100644 --- a/src/UI/WPF/Views/LayerManagementView.xaml +++ b/src/UI/WPF/Views/LayerManagementView.xaml @@ -234,7 +234,7 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本 - +