优化了分层预览的遍历机制,提高性能

This commit is contained in:
tian 2025-08-28 18:31:57 +08:00
parent 2b92e783bb
commit 4dc989926e
8 changed files with 1244 additions and 289 deletions

View File

@ -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<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config);
}
```
**接口局限性**
- 只能处理预收集的节点集合
- 无法在遍历过程中动态决策是否继续遍历
- 不支持单节点的属性检查和分支控制
### 3. 属性检查机制的缺陷
**现有策略类的问题**
- 如`FloorAttributeStrategy`仅检查特定的固定属性
- 没有通用的"分层信息"属性类别检查机制
- 不支持候选属性组和优先级评分
- 缺乏多属性竞争选择逻辑
## 架构改造方案
### 1. 核心遍历算法重设计
**新的智能遍历算法**
```csharp
public List<SplitPreviewResult> PreviewSplitWithSmartTraversal(
SplitConfiguration config, ISmartGroupingStrategy strategy)
{
var results = new List<SplitPreviewResult>();
// 从每个模型的一级节点开始遍历
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<SplitPreviewResult> 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; }
/// <summary>
/// 核心方法:检查单个节点的分层信息
/// </summary>
LayerInfo ExtractLayerInfo(ModelItem node);
/// <summary>
/// 支持候选属性组的智能检测
/// </summary>
LayerInfo ExtractBestLayerInfo(ModelItem node, List<AttributeCandidate> 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<string, string> ValueProcessor { get; set; } // 值处理函数
}
```
### 3. 智能检测策略实现
**SmartLayerDetectionStrategy类**
```csharp
public class SmartLayerDetectionStrategy : ISmartGroupingStrategy
{
private readonly List<AttributeCandidate> _candidates = new List<AttributeCandidate>
{
// 按优先级排序的候选属性
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<AttributeCandidate> 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<AttributeCandidate> 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<string, object>
{
["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<SplitPreviewResult> 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. **性能监控**:关注新算法的性能表现,必要时进行优化
**预期收益**
- **更精确的分层**:基于实际属性内容而非固定深度
- **更高的效率**:避免不必要的深度遍历
- **更灵活的配置**:支持多种属性检查策略
- **更好的用户体验**:提供智能化的分层建议

View File

@ -0,0 +1,260 @@
# 轻量化智能遍历预览实施方案
## 目标
实现基于属性的智能遍历算法,替代现有的固定深度收集方式,专注于预览性能提升。保存逻辑暂时不修改。
## 核心修改点
### 1. 新增智能遍历算法SimplifiedModelSplitterManager.cs
**PreviewSplitWithSmartTraversal方法**
```csharp
private List<SplitPreviewResult> PreviewSplitWithSmartTraversal(
SplitConfiguration config, IGroupingStrategy strategy)
{
var results = new List<SplitPreviewResult>();
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<SplitPreviewResult> 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<string, object>
{
["LayerValue"] = layerValue,
["RootNodeReference"] = rootNode,
["GroupType"] = groupTypeName
}
};
}
```
**说明**
- 不设置 ItemCount、EstimatedFileSize、Status 等字段
- 只包含预览必需的信息LayerName、根节点引用、元数据
- 避免任何不必要的计算和统计
### 3. 策略接口扩展
**IGroupingStrategy接口新增方法**
```csharp
public interface IGroupingStrategy
{
string GroupTypeName { get; }
// 现有方法保持不变
Dictionary<string, GroupItem> 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<SplitPreviewResult> 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%以上的预览响应时间提升**
- **完全兼容现有功能**,风险极低
- **为后续智能检测功能奠定基础**
通过这次重构,分层预览将从当前的性能瓶颈转变为系统的性能优势点。

View File

@ -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;
}
/// <summary>
/// 获取模型项的区域属性值,支持继承查询
/// 直接获取对象的楼层属性
/// </summary>
/// <param name="item">目标模型项</param>
/// <param name="searchParents">是否向上查找父节点的区域属性</param>
/// <returns>区域属性值未找到返回null</returns>
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;
}
}
/// <summary>
/// 直接获取对象的区域属性(不进行继承查找)
/// </summary>
private string GetZoneAttributeDirect(ModelItem item)
/// <returns>楼层标识未找到返回null</returns>
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;
}
}
/// <summary>
/// 直接获取对象的区域属性
/// </summary>
/// <param name="item">目标模型项</param>
/// <returns>区域属性值未找到返回null</returns>
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
}
/// <summary>
/// 获取模型项的子系统属性值,支持继承查询
/// 直接获取对象的子系统属性
/// </summary>
/// <param name="item">目标模型项</param>
/// <param name="searchParents">是否向上查找父节点的子系统属性</param>
/// <returns>子系统属性值未找到返回null</returns>
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;
}
}
/// <summary>
/// 直接获取对象的子系统属性(不进行继承查找)
/// </summary>
private string GetSubSystemAttributeDirect(ModelItem item)
public string GetSubSystemAttribute(ModelItem item)
{
if (item == null) return null;
@ -480,50 +433,6 @@ namespace NavisworksTransport.Core
}
}
/// <summary>
/// 获取对象的楼层标识,支持继承查询
/// </summary>
/// <param name="item">目标模型项</param>
/// <param name="searchParents">是否向上查找父节点的楼层属性</param>
/// <returns>楼层标识未找到返回null</returns>
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;
}
}
/// <summary>
/// 批量设置楼层属性
/// </summary>
@ -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
/// <summary>
/// 直接获取对象的楼层属性(不进行继承查找)
/// </summary>
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;
}
}
/// <summary>
/// 添加楼层属性到属性分类
/// </summary>

View File

@ -38,6 +38,11 @@ namespace NavisworksTransport
string GroupTypeName { get; }
string UnclassifiedLabel { get; }
Dictionary<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config);
/// <summary>
/// 提取单个节点的属性值,用于智能遍历
/// </summary>
string ExtractAttributeValue(ModelItem node);
}
/// <summary>
@ -66,6 +71,90 @@ namespace NavisworksTransport
_floorDetector = floorDetector;
}
/// <summary>
/// 实现IGroupingStrategy接口的ExtractAttributeValue方法
/// 使用FloorDetector进行智能楼层检测
/// </summary>
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;
}
}
/// <summary>
/// 从节点获取指定属性的值
/// </summary>
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;
}
}
/// <summary>
/// 获取检测到的属性类型信息(属性名称和值)
/// </summary>
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<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config)
{
var result = new Dictionary<string, GroupItem>();
@ -140,6 +229,15 @@ namespace NavisworksTransport
protected abstract string GetAttributeValue(ModelItem item);
/// <summary>
/// 实现IGroupingStrategy接口的ExtractAttributeValue方法
/// 直接调用子类已实现的GetAttributeValue方法
/// </summary>
public string ExtractAttributeValue(ModelItem node)
{
return GetAttributeValue(node);
}
public Dictionary<string, GroupItem> GroupItems(ModelItemCollection items, SplitConfiguration config)
{
var result = new Dictionary<string, GroupItem>();
@ -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
}
/// <summary>
/// 简化的分层预览结果
/// 分层预览结果
/// </summary>
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<string, object> Metadata { get; set; } = new Dictionary<string, object>();
@ -337,6 +433,11 @@ namespace NavisworksTransport
#region
/// <summary>
/// 静态实例,方便其他类调用缓存清除功能
/// </summary>
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<SplitPreviewResult>();
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}";
}
/// <summary>
/// 生成智能遍历专用的缓存键 - 区别于原有的预收集模式
/// </summary>
private string GenerateSmartTraversalCacheKey(SplitConfiguration config)
{
// 智能遍历的缓存键包含特殊标识,避免与原有缓存冲突
return $"SmartTraversal_{config.Strategy}_{config.MaxDepth}_{config.AttributeName ?? "Default"}_{DateTime.Now:yyyyMMdd}";
}
/// <summary>
/// 创建分层策略实例
/// </summary>
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}");
}
}
/// <summary>
/// 从缓存获取结果
@ -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;
}
/// <summary>
/// 智能遍历分层预览 - 边遍历边检查边停止的新算法
/// 替代原有的预收集模式,大幅提升预览性能
/// </summary>
private List<SplitPreviewResult> PreviewSplitWithSmartTraversal(SplitConfiguration config, IGroupingStrategy strategy)
{
var results = new List<SplitPreviewResult>();
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;
}
/// <summary>
/// 递归遍历节点并记录分层结果的核心方法
/// </summary>
private void TraverseAndRecord(ModelItem node, IGroupingStrategy strategy,
SplitConfiguration config, List<SplitPreviewResult> 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}");
}
}
/// <summary>
/// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算
/// </summary>
/// <summary>
/// 创建轻量化分层预览结果 - 只包含必要信息,不做统计计算
/// </summary>
private SplitPreviewResult CreateLayerResult(string layerValue, ModelItem rootNode, string groupTypeName, SplitStrategy strategy, string detectedAttributeName = null)
{
var metadata = new Dictionary<string, object>
{
["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
};
}
/// <summary>
/// 处理单个分层(异步版本)
@ -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;
}
/// <summary>
/// 获取分层策略的属性描述
/// </summary>
/// <summary>
/// 获取具体的分层属性描述(实际属性值而非策略描述)
/// </summary>
/// <param name="strategy">分层策略</param>
/// <param name="layerName">分层名称</param>
/// <param name="metadata">元数据,可能包含实际的属性信息</param>
private string GetAttributeDescription(SplitStrategy strategy, string layerName, Dictionary<string, object> 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; // 失败时返回分层名称
}
}
/// <summary>
/// 从分层名称推断属性类型(用于智能检测)
/// </summary>
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;
}
/// <summary>
/// 估算文件大小
/// </summary>
@ -2140,9 +2460,8 @@ namespace NavisworksTransport
var testPreview = new SplitPreviewResult
{
LayerName = "测试导出",
ItemCount = testItems.Count,
Items = testItems,
Status = "测试中"
LayerAttribute = "测试",
Items = testItems
};
// 创建测试配置

View File

@ -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";
}
/// <summary>
/// 根据分层策略和分层名称生成属性描述
/// </summary>
/// <summary>
/// 根据分层策略和分层名称生成属性描述
/// </summary>
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 "未知属性";
}
}
}
/// <summary>

View File

@ -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
/// <summary>
/// 对象数量
/// </summary>
public int ObjectCount
/// <summary>
/// 分层属性
/// </summary>
public string LayerAttribute
{
get => _objectCount;
get => _layerAttribute;
set
{
if (_objectCount != value)
if (_layerAttribute != value)
{
_objectCount = value;
OnPropertyChanged(nameof(ObjectCount));
_layerAttribute = value;
OnPropertyChanged(nameof(LayerAttribute));
}
}
}

View File

@ -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);
}
/// <summary>
/// 是否显示预览信息
/// </summary>
public bool ShowPreviewInfo
{
get => _showPreviewInfo;
set => SetPropertyThreadSafe(ref _showPreviewInfo, value);
}
/// <summary>
/// 预览信息提示消息
/// </summary>
public string PreviewInfoMessage
{
get => _previewInfoMessage;
set => SetPropertyThreadSafe(ref _previewInfoMessage, value);
}
/// <summary>
/// 当前选择文本
/// </summary>
@ -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))

View File

@ -234,7 +234,7 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="分层名称" DisplayMemberBinding="{Binding LayerName}" Width="120"/>
<GridViewColumn Header="对象数量" DisplayMemberBinding="{Binding ItemCount}" Width="80"/>
<GridViewColumn Header="分层属性" DisplayMemberBinding="{Binding LayerAttribute}" Width="80"/>
<GridViewColumn Header="是否保存" Width="80">
<GridViewColumn.HeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
@ -286,6 +286,16 @@ NavisworksTransport 分层管理页签视图 - 重构优化版本
</StackPanel>
</Grid>
</GroupBox>
<!-- 预览信息提示区域 -->
<StackPanel Margin="0,5,0,0"
Visibility="{Binding ShowPreviewInfo, Converter={StaticResource BoolToVisConverter}}">
<TextBlock Text="{Binding PreviewInfoMessage}"
Style="{StaticResource StatusTextStyle}"
TextWrapping="Wrap"
HorizontalAlignment="Left"
Margin="5,0,5,0"/>
</StackPanel>
</StackPanel>
</Border>