- 在HttpServer中实现了新的API端点:/api/project/open和/api/model/shrinkwrap - 添加了ShrinkwrapModel和OpenProject命令的处理逻辑 - 在PdmsManager中实现了ShrinkwrapModel和OpenProject方法,支持相应请求的处理 - 更新了项目文件以包含新的命令和模型请求类 此更新增强了插件的功能,允许用户通过API进行模型缩减和项目打开操作。
1169 lines
42 KiB
C#
1169 lines
42 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using TellmePdmsPluging.Models;
|
||
using Aveva.ApplicationFramework;
|
||
using Aveva.Pdms.Database;
|
||
using System.Linq;
|
||
using System;
|
||
|
||
namespace TellmePdmsPluging.Core
|
||
{
|
||
public class PdmsManager
|
||
{
|
||
private static PdmsManager _instance;
|
||
private static readonly object _lock = new object();
|
||
|
||
public static PdmsManager Instance
|
||
{
|
||
get
|
||
{
|
||
if (_instance == null)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (_instance == null)
|
||
_instance = new PdmsManager();
|
||
}
|
||
}
|
||
return _instance;
|
||
}
|
||
}
|
||
|
||
private PdmsManager()
|
||
{
|
||
}
|
||
|
||
public SimplifyModelResult SimplifyModel(SimplifyModelRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new SimplifyModelRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new SimplifyModelResult
|
||
{
|
||
DryRun = effectiveRequest.DryRun,
|
||
StartedAt = DateTime.Now
|
||
};
|
||
|
||
try
|
||
{
|
||
if (!IsPdmsConnected())
|
||
{
|
||
result.Success = false;
|
||
result.CompletedAt = DateTime.Now;
|
||
result.Message = "PDMS 未连接";
|
||
result.Errors.Add("PDMS 未连接");
|
||
return result;
|
||
}
|
||
|
||
var currentMdb = MDB.CurrentMDB;
|
||
var designDb = currentMdb?.GetFirstDB(DbType.Design);
|
||
if (designDb == null || designDb.World == null)
|
||
{
|
||
result.Success = false;
|
||
result.CompletedAt = DateTime.Now;
|
||
result.Message = "未找到有效的设计数据库";
|
||
result.Errors.Add("未找到有效的设计数据库");
|
||
return result;
|
||
}
|
||
|
||
var context = new SimplifyContext(effectiveRequest, result);
|
||
var sites = designDb.World.Members();
|
||
bool processed = false;
|
||
|
||
if (sites != null)
|
||
{
|
||
foreach (var site in sites)
|
||
{
|
||
if (!IsElementValid(site) || !string.Equals(GetElementTypeName(site), "SITE", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var zones = site.Members();
|
||
if (zones == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
foreach (var zone in zones)
|
||
{
|
||
if (!IsElementValid(zone) || !string.Equals(GetElementTypeName(zone), "ZONE", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (!context.ShouldProcessZone(zone))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
processed = true;
|
||
context.EnterZone(zone);
|
||
context.ProcessChildren(zone);
|
||
}
|
||
}
|
||
}
|
||
|
||
result.Success = processed && result.Errors.Count == 0;
|
||
result.Message = processed
|
||
? (result.DryRun ? "模型轻量化干跑完成" : "模型轻量化完成")
|
||
: "未找到符合过滤条件的Zone";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Errors.Add(ex.Message);
|
||
result.Message = "模型轻量化失败";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public ShrinkwrapModelResult ShrinkwrapModel(ShrinkwrapModelRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new ShrinkwrapModelRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new ShrinkwrapModelResult
|
||
{
|
||
DryRun = effectiveRequest.DryRun,
|
||
Padding = effectiveRequest.Padding,
|
||
StartedAt = DateTime.Now
|
||
};
|
||
|
||
try
|
||
{
|
||
if (!IsPdmsConnected())
|
||
{
|
||
result.Success = false;
|
||
result.CompletedAt = DateTime.Now;
|
||
result.Message = "PDMS 未连接";
|
||
result.Errors.Add("PDMS 未连接");
|
||
return result;
|
||
}
|
||
|
||
var currentMdb = MDB.CurrentMDB;
|
||
var designDb = currentMdb?.GetFirstDB(DbType.Design);
|
||
if (designDb == null || designDb.World == null)
|
||
{
|
||
result.Success = false;
|
||
result.CompletedAt = DateTime.Now;
|
||
result.Message = "未找到有效的设计数据库";
|
||
result.Errors.Add("未找到有效的设计数据库");
|
||
return result;
|
||
}
|
||
|
||
var context = new ShrinkwrapContext(effectiveRequest, result);
|
||
var sites = designDb.World.Members();
|
||
bool processed = false;
|
||
|
||
if (sites != null)
|
||
{
|
||
foreach (var site in sites)
|
||
{
|
||
if (!IsElementValid(site) || !string.Equals(GetElementTypeName(site), "SITE", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var zones = site.Members();
|
||
if (zones == null)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
foreach (var zone in zones)
|
||
{
|
||
if (!IsElementValid(zone) || !string.Equals(GetElementTypeName(zone), "ZONE", StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (!context.ShouldProcessZone(zone))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
processed = true;
|
||
context.EnterZone(zone);
|
||
context.ProcessZone(zone);
|
||
}
|
||
}
|
||
}
|
||
|
||
result.Success = processed && result.Errors.Count == 0;
|
||
result.Message = processed
|
||
? (result.DryRun ? "外壳保留干跑完成" : "外壳保留完成")
|
||
: "未找到符合过滤条件的Zone";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Errors.Add(ex.Message);
|
||
result.Message = "外壳保留失败";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public ModelStatusResponse GetModelStatus()
|
||
{
|
||
try
|
||
{
|
||
// 检查PDMS是否连接
|
||
if (!IsPdmsConnected())
|
||
{
|
||
return new ModelStatusResponse
|
||
{
|
||
ModelLoaded = false
|
||
};
|
||
}
|
||
|
||
return new ModelStatusResponse
|
||
{
|
||
ModelLoaded = true,
|
||
ProjectInfo = GetProjectInfo(),
|
||
ModelStatistics = GetModelStatistics(),
|
||
SessionInfo = GetSessionInfo()
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
// 记录错误日志
|
||
System.Diagnostics.Debug.WriteLine($"获取PDMS模型状态失败: {ex.Message}");
|
||
return new ModelStatusResponse
|
||
{
|
||
ModelLoaded = false
|
||
};
|
||
}
|
||
}
|
||
|
||
public OpenProjectResult OpenProject(OpenProjectRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new OpenProjectRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new OpenProjectResult
|
||
{
|
||
ProjectName = effectiveRequest.ProjectName,
|
||
CompletedAt = DateTime.Now
|
||
};
|
||
|
||
try
|
||
{
|
||
var currentProject = Project.CurrentProject;
|
||
if (currentProject == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "Project.CurrentProject 为空,无法打开项目";
|
||
return result;
|
||
}
|
||
|
||
bool alreadyOpen = false;
|
||
try
|
||
{
|
||
alreadyOpen = currentProject.IsOpen();
|
||
}
|
||
catch
|
||
{
|
||
alreadyOpen = false;
|
||
}
|
||
|
||
result.WasAlreadyOpen = alreadyOpen;
|
||
|
||
// 如果已经打开且未指定目标项目名,则直接返回成功
|
||
if (alreadyOpen && string.IsNullOrEmpty(effectiveRequest.ProjectName))
|
||
{
|
||
result.Success = true;
|
||
result.Message = "项目已打开";
|
||
return result;
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(effectiveRequest.ProjectName))
|
||
{
|
||
result.Success = false;
|
||
result.Message = "ProjectName 不能为空";
|
||
return result;
|
||
}
|
||
|
||
bool opened = currentProject.Open(
|
||
effectiveRequest.ProjectName,
|
||
effectiveRequest.UserName ?? string.Empty,
|
||
effectiveRequest.Password ?? string.Empty);
|
||
|
||
result.Success = opened;
|
||
result.Message = opened ? "项目打开成功" : "项目打开失败(Open 返回 false)";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "项目打开异常: " + ex.Message;
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public OpenMdbResult OpenMdb(OpenMdbRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new OpenMdbRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new OpenMdbResult
|
||
{
|
||
MdbName = effectiveRequest.MdbName,
|
||
ReadOnly = effectiveRequest.ReadOnly,
|
||
DefaultType = effectiveRequest.DefaultType,
|
||
CompletedAt = DateTime.Now
|
||
};
|
||
|
||
try
|
||
{
|
||
if (string.IsNullOrEmpty(effectiveRequest.MdbName))
|
||
{
|
||
result.Success = false;
|
||
result.Message = "MdbName 不能为空";
|
||
return result;
|
||
}
|
||
|
||
// 如果当前MDB已经是目标,则直接返回成功
|
||
try
|
||
{
|
||
var current = MDB.CurrentMDB;
|
||
if (current != null && string.Equals(current.Name, effectiveRequest.MdbName, StringComparison.OrdinalIgnoreCase))
|
||
{
|
||
result.Success = true;
|
||
result.WasAlreadyOpen = true;
|
||
result.Message = "MDB 已经打开";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// ignore, continue to open
|
||
}
|
||
|
||
var setup = MDBSetup.CreateMDBSetup(effectiveRequest.MdbName);
|
||
if (setup == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "创建 MDBSetup 失败";
|
||
return result;
|
||
}
|
||
|
||
setup.ReadOnly = effectiveRequest.ReadOnly;
|
||
|
||
if (!string.IsNullOrEmpty(effectiveRequest.Stamp))
|
||
{
|
||
setup.Stamp = effectiveRequest.Stamp;
|
||
}
|
||
|
||
if (effectiveRequest.Subtype.HasValue)
|
||
{
|
||
setup.Subtype = effectiveRequest.Subtype.Value;
|
||
}
|
||
|
||
if (!string.IsNullOrEmpty(effectiveRequest.DefaultType))
|
||
{
|
||
DbType defaultType;
|
||
if (!TryParseDbType(effectiveRequest.DefaultType, out defaultType))
|
||
{
|
||
result.Success = false;
|
||
result.Message = "DefaultType 无效: " + effectiveRequest.DefaultType;
|
||
return result;
|
||
}
|
||
setup.DefaultType = defaultType;
|
||
}
|
||
|
||
if (effectiveRequest.ReadTypes != null && effectiveRequest.ReadTypes.Count > 0)
|
||
{
|
||
DbType[] readTypes;
|
||
string error;
|
||
if (!TryParseDbTypes(effectiveRequest.ReadTypes, out readTypes, out error))
|
||
{
|
||
result.Success = false;
|
||
result.Message = "ReadTypes 无效: " + error;
|
||
return result;
|
||
}
|
||
setup.ReadTypes = readTypes;
|
||
}
|
||
|
||
if (effectiveRequest.WriteTypes != null && effectiveRequest.WriteTypes.Count > 0)
|
||
{
|
||
DbType[] writeTypes;
|
||
string error;
|
||
if (!TryParseDbTypes(effectiveRequest.WriteTypes, out writeTypes, out error))
|
||
{
|
||
result.Success = false;
|
||
result.Message = "WriteTypes 无效: " + error;
|
||
return result;
|
||
}
|
||
setup.WriteTypes = writeTypes;
|
||
}
|
||
|
||
var openedMdb = Project.OpenMDB(setup);
|
||
if (openedMdb == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "打开 MDB 失败(OpenMDB 返回 null)";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
|
||
result.Success = true;
|
||
result.WasAlreadyOpen = false;
|
||
result.MdbName = openedMdb.Name;
|
||
result.Message = "MDB 打开成功";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "打开 MDB 异常: " + ex.Message;
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
private static bool TryParseDbTypes(IEnumerable<string> values, out DbType[] types, out string error)
|
||
{
|
||
var list = new List<DbType>();
|
||
var invalid = new List<string>();
|
||
|
||
foreach (var raw in values)
|
||
{
|
||
if (string.IsNullOrEmpty(raw))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
DbType parsed;
|
||
if (TryParseDbType(raw, out parsed))
|
||
{
|
||
if (!list.Contains(parsed))
|
||
{
|
||
list.Add(parsed);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
invalid.Add(raw);
|
||
}
|
||
}
|
||
|
||
if (invalid.Count > 0)
|
||
{
|
||
types = null;
|
||
error = string.Join(",", invalid.ToArray());
|
||
return false;
|
||
}
|
||
|
||
types = list.ToArray();
|
||
error = null;
|
||
return true;
|
||
}
|
||
|
||
private static bool TryParseDbType(string value, out DbType dbType)
|
||
{
|
||
dbType = DbType.Design;
|
||
if (string.IsNullOrEmpty(value))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
try
|
||
{
|
||
dbType = (DbType)Enum.Parse(typeof(DbType), value.Trim(), true);
|
||
return true;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private bool IsPdmsConnected()
|
||
{
|
||
try
|
||
{
|
||
// 使用MDB.CurrentMDB检查PDMS连接状态
|
||
var currentMdb = MDB.CurrentMDB;
|
||
return currentMdb != null;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private ProjectInfo GetProjectInfo()
|
||
{
|
||
try
|
||
{
|
||
// 使用MDB.CurrentMDB和Project.CurrentProject获取真实项目信息
|
||
var currentMdb = MDB.CurrentMDB;
|
||
var currentProject = Project.CurrentProject;
|
||
|
||
if (currentMdb != null)
|
||
{
|
||
var designDb = currentMdb.GetFirstDB(DbType.Design);
|
||
if (designDb != null)
|
||
{
|
||
return new ProjectInfo
|
||
{
|
||
ProjectName = designDb.Name ?? "Unknown",
|
||
MdsName = currentMdb.Name ?? "Unknown",
|
||
PdmsVersion = "12.1.SP4"
|
||
};
|
||
}
|
||
}
|
||
|
||
return new ProjectInfo
|
||
{
|
||
ProjectName = "Unknown",
|
||
MdsName = "Unknown",
|
||
PdmsVersion = "12.1.SP4"
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"获取项目信息失败: {ex.Message}");
|
||
return new ProjectInfo
|
||
{
|
||
ProjectName = "Unknown",
|
||
MdsName = "Unknown",
|
||
PdmsVersion = "12.1.SP4"
|
||
};
|
||
}
|
||
}
|
||
|
||
|
||
|
||
private ModelStatistics GetModelStatistics()
|
||
{
|
||
try
|
||
{
|
||
// 使用MDB.CurrentMDB获取真实模型统计信息
|
||
var currentMdb = MDB.CurrentMDB;
|
||
if (currentMdb != null)
|
||
{
|
||
var designDb = currentMdb.GetFirstDB(DbType.Design);
|
||
if (designDb?.World != null)
|
||
{
|
||
// 初始化统计计数器
|
||
var elementCounts = new Dictionary<string, int>
|
||
{
|
||
{"SITE", 0}, {"ZONE", 0}, {"PIPE", 0}, {"EQUI", 0},
|
||
{"STRU", 0}, {"VALVE", 0}, {"FITT", 0}, {"NOZZ", 0}
|
||
};
|
||
|
||
int totalElements = 0;
|
||
var activeZones = new List<string>();
|
||
int zoneCount = 0;
|
||
|
||
// 递归统计所有元素
|
||
CountElementsByType(designDb.World, elementCounts, ref totalElements);
|
||
|
||
// 专门统计Zone信息
|
||
CountZones(designDb.World, ref zoneCount, activeZones);
|
||
|
||
return new ModelStatistics
|
||
{
|
||
TotalElements = totalElements,
|
||
ElementCounts = elementCounts,
|
||
ZoneCount = zoneCount,
|
||
ActiveZones = activeZones
|
||
};
|
||
}
|
||
}
|
||
|
||
return new ModelStatistics
|
||
{
|
||
TotalElements = 0,
|
||
ElementCounts = new Dictionary<string, int>(),
|
||
ZoneCount = 0,
|
||
ActiveZones = new List<string>()
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"获取模型统计信息失败: {ex.Message}");
|
||
return new ModelStatistics
|
||
{
|
||
TotalElements = 0,
|
||
ElementCounts = new Dictionary<string, int>(),
|
||
ZoneCount = 0,
|
||
ActiveZones = new List<string>()
|
||
};
|
||
}
|
||
}
|
||
|
||
private void CountElementsByType(DbElement parentElement, Dictionary<string, int> elementCounts, ref int totalElements)
|
||
{
|
||
try
|
||
{
|
||
if (parentElement == null || parentElement.IsNull || !parentElement.IsValid)
|
||
return;
|
||
|
||
// 获取所有子元素
|
||
var members = parentElement.Members();
|
||
if (members != null)
|
||
{
|
||
foreach (var element in members)
|
||
{
|
||
if (element != null && !element.IsNull && element.IsValid)
|
||
{
|
||
totalElements++;
|
||
|
||
// 获取元素的实际类型
|
||
var elementType = element.GetActualType();
|
||
if (elementType != null)
|
||
{
|
||
string typeName = elementType.Name;
|
||
if (elementCounts.ContainsKey(typeName))
|
||
{
|
||
elementCounts[typeName]++;
|
||
}
|
||
}
|
||
|
||
// 递归统计子元素
|
||
CountElementsByType(element, elementCounts, ref totalElements);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"统计元素类型时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void CountZones(DbElement worldElement, ref int zoneCount, List<string> activeZones)
|
||
{
|
||
try
|
||
{
|
||
if (worldElement == null || worldElement.IsNull || !worldElement.IsValid)
|
||
return;
|
||
|
||
// 查找SITE元素
|
||
var sites = worldElement.Members();
|
||
if (sites != null)
|
||
{
|
||
foreach (var site in sites)
|
||
{
|
||
if (site != null && !site.IsNull && site.IsValid)
|
||
{
|
||
var siteType = site.GetActualType();
|
||
if (siteType != null && siteType.Name == "SITE")
|
||
{
|
||
// 在SITE下查找ZONE元素
|
||
var zones = site.Members();
|
||
if (zones != null)
|
||
{
|
||
foreach (var zone in zones)
|
||
{
|
||
if (zone != null && !zone.IsNull && zone.IsValid)
|
||
{
|
||
var zoneType = zone.GetActualType();
|
||
if (zoneType != null && zoneType.Name == "ZONE")
|
||
{
|
||
zoneCount++;
|
||
|
||
// 获取Zone名称
|
||
try
|
||
{
|
||
string zoneName = "";
|
||
if (zone.GetValidString(DbAttributeInstance.NAME, ref zoneName))
|
||
{
|
||
if (!string.IsNullOrEmpty(zoneName))
|
||
{
|
||
activeZones.Add(zoneName);
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// 如果无法获取名称,跳过
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"统计Zone信息时出错: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private SessionInfo GetSessionInfo()
|
||
{
|
||
try
|
||
{
|
||
// 使用MDB.CurrentMDB获取真实会话信息
|
||
var currentMdb = MDB.CurrentMDB;
|
||
if (currentMdb != null)
|
||
{
|
||
var designDb = currentMdb.GetFirstDB(DbType.Design);
|
||
if (designDb?.CurrentSession != null)
|
||
{
|
||
// 从真实的数据库会话获取信息
|
||
var session = designDb.CurrentSession;
|
||
return new SessionInfo
|
||
{
|
||
UserName = session.User ?? Environment.UserName, // 从DbSession获取用户名
|
||
StartTime = session.Date, // 从DbSession获取会话创建时间
|
||
DurationMinutes = (int)(DateTime.Now - session.Date).TotalMinutes // 计算会话持续时间
|
||
};
|
||
}
|
||
}
|
||
|
||
return new SessionInfo
|
||
{
|
||
UserName = Environment.UserName,
|
||
StartTime = DateTime.Now,
|
||
DurationMinutes = 0
|
||
};
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
System.Diagnostics.Debug.WriteLine($"获取会话信息失败: {ex.Message}");
|
||
return new SessionInfo
|
||
{
|
||
UserName = Environment.UserName,
|
||
StartTime = DateTime.Now,
|
||
DurationMinutes = 0
|
||
};
|
||
}
|
||
}
|
||
private static bool IsElementValid(DbElement element)
|
||
{
|
||
return element != null && !element.IsNull && element.IsValid;
|
||
}
|
||
|
||
private static string GetElementTypeName(DbElement element)
|
||
{
|
||
try
|
||
{
|
||
var type = element?.GetActualType();
|
||
return type?.Name ?? string.Empty;
|
||
}
|
||
catch
|
||
{
|
||
return string.Empty;
|
||
}
|
||
}
|
||
|
||
private static string BuildElementPath(DbElement element)
|
||
{
|
||
var segments = new List<string>();
|
||
var current = element;
|
||
int guard = 0;
|
||
|
||
while (IsElementValid(current) && guard < 128)
|
||
{
|
||
string name = string.Empty;
|
||
try
|
||
{
|
||
if (!current.GetValidString(DbAttributeInstance.NAME, ref name) || string.IsNullOrEmpty(name))
|
||
{
|
||
name = current.ToString();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
name = current.ToString();
|
||
}
|
||
|
||
segments.Add(name.Trim());
|
||
current = current.Owner;
|
||
guard++;
|
||
}
|
||
|
||
segments.Reverse();
|
||
return "/" + string.Join("/", segments.ToArray());
|
||
}
|
||
|
||
private class SimplifyContext
|
||
{
|
||
private const int MaxRemovedSnapshots = 200;
|
||
|
||
public SimplifyContext(SimplifyModelRequest request, SimplifyModelResult result)
|
||
{
|
||
Request = request;
|
||
Result = result;
|
||
_keepTypes = new HashSet<string>(request.KeepTypes ?? new List<string>());
|
||
_removeTypes = new HashSet<string>(request.RemoveTypes ?? new List<string>());
|
||
}
|
||
|
||
public SimplifyModelRequest Request { get; }
|
||
public SimplifyModelResult Result { get; }
|
||
public string CurrentZoneName { get; private set; }
|
||
|
||
private readonly HashSet<string> _keepTypes;
|
||
private readonly HashSet<string> _removeTypes;
|
||
|
||
public bool ShouldProcessZone(DbElement zone)
|
||
{
|
||
if (Request.ZoneFilters == null || Request.ZoneFilters.Count == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
var zonePath = BuildElementPath(zone).ToUpperInvariant();
|
||
return Request.ZoneFilters.Any(filter => zonePath.Contains(filter));
|
||
}
|
||
|
||
public void EnterZone(DbElement zone)
|
||
{
|
||
CurrentZoneName = BuildElementPath(zone);
|
||
if (!Result.ZoneSummaries.Contains(CurrentZoneName))
|
||
{
|
||
Result.ZoneSummaries.Add(CurrentZoneName);
|
||
}
|
||
}
|
||
|
||
public void ProcessChildren(DbElement parent)
|
||
{
|
||
var members = parent.Members();
|
||
if (members == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (var child in members)
|
||
{
|
||
ProcessElement(child);
|
||
}
|
||
}
|
||
|
||
private void ProcessElement(DbElement element)
|
||
{
|
||
if (!IsElementValid(element))
|
||
{
|
||
return;
|
||
}
|
||
|
||
Result.TotalVisited++;
|
||
var typeName = GetElementTypeName(element);
|
||
var normalizedType = string.IsNullOrEmpty(typeName) ? string.Empty : typeName.ToUpperInvariant();
|
||
|
||
bool keep = _keepTypes.Contains(normalizedType);
|
||
bool remove = _removeTypes.Contains(normalizedType) && !keep;
|
||
|
||
if (remove)
|
||
{
|
||
RemoveElement(element, normalizedType);
|
||
return;
|
||
}
|
||
|
||
Result.KeptCount++;
|
||
ProcessChildren(element);
|
||
}
|
||
|
||
private void RemoveElement(DbElement element, string typeName)
|
||
{
|
||
var elementPath = BuildElementPath(element);
|
||
|
||
if (Request.DryRun)
|
||
{
|
||
SnapshotRemoval(elementPath, typeName, true);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
element.Delete();
|
||
SnapshotRemoval(elementPath, typeName, false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Result.Errors.Add($"删除元素 {elementPath} 失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void SnapshotRemoval(string elementPath, string typeName, bool dryRun)
|
||
{
|
||
Result.RemovedCount++;
|
||
|
||
if (Result.RemovedElements.Count < MaxRemovedSnapshots)
|
||
{
|
||
var marker = dryRun ? "DRY" : "DEL";
|
||
Result.RemovedElements.Add($"[{marker}] {elementPath} ({typeName})");
|
||
}
|
||
}
|
||
}
|
||
|
||
private class ShrinkwrapContext
|
||
{
|
||
private const int MaxRemovedSnapshots = 200;
|
||
|
||
public ShrinkwrapContext(ShrinkwrapModelRequest request, ShrinkwrapModelResult result)
|
||
{
|
||
Request = request;
|
||
Result = result;
|
||
_keepTypes = new HashSet<string>(request.KeepTypes ?? new List<string>());
|
||
}
|
||
|
||
public ShrinkwrapModelRequest Request { get; }
|
||
public ShrinkwrapModelResult Result { get; }
|
||
public string CurrentZoneName { get; private set; }
|
||
|
||
private readonly HashSet<string> _keepTypes;
|
||
private float[] _innerBox;
|
||
|
||
public bool ShouldProcessZone(DbElement zone)
|
||
{
|
||
if (Request.ZoneFilters == null || Request.ZoneFilters.Count == 0)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
var zonePath = BuildElementPath(zone).ToUpperInvariant();
|
||
return Request.ZoneFilters.Any(filter => zonePath.Contains(filter));
|
||
}
|
||
|
||
public void EnterZone(DbElement zone)
|
||
{
|
||
CurrentZoneName = BuildElementPath(zone);
|
||
if (!Result.ZoneSummaries.Contains(CurrentZoneName))
|
||
{
|
||
Result.ZoneSummaries.Add(CurrentZoneName);
|
||
}
|
||
}
|
||
|
||
public void ProcessZone(DbElement zone)
|
||
{
|
||
float[] zoneBox;
|
||
if (!TryGetLimitsBox(zone, out zoneBox))
|
||
{
|
||
Result.Errors.Add($"Zone包围盒计算失败: {BuildElementPath(zone)}");
|
||
return;
|
||
}
|
||
|
||
_innerBox = BuildInnerBox(zoneBox, (float)Request.Padding);
|
||
ProcessChildren(zone);
|
||
}
|
||
|
||
private void ProcessChildren(DbElement parent)
|
||
{
|
||
var members = parent.Members();
|
||
if (members == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
foreach (var child in members)
|
||
{
|
||
ProcessElement(child);
|
||
}
|
||
}
|
||
|
||
private void ProcessElement(DbElement element)
|
||
{
|
||
if (!IsElementValid(element))
|
||
{
|
||
return;
|
||
}
|
||
|
||
Result.TotalVisited++;
|
||
|
||
var typeName = GetElementTypeName(element);
|
||
var normalizedType = string.IsNullOrEmpty(typeName) ? string.Empty : typeName.ToUpperInvariant();
|
||
|
||
if (_keepTypes.Contains(normalizedType))
|
||
{
|
||
Result.KeptCount++;
|
||
ProcessChildren(element);
|
||
return;
|
||
}
|
||
|
||
float[] box;
|
||
if (!TryGetLimitsBox(element, out box))
|
||
{
|
||
Result.KeptCount++;
|
||
return;
|
||
}
|
||
|
||
if (IsInsideInnerBox(box, _innerBox, (float)Request.TouchTolerance))
|
||
{
|
||
RemoveElement(element, normalizedType);
|
||
return;
|
||
}
|
||
|
||
Result.ShellKeptCount++;
|
||
Result.KeptCount++;
|
||
ProcessChildren(element);
|
||
}
|
||
|
||
private void RemoveElement(DbElement element, string typeName)
|
||
{
|
||
var elementPath = BuildElementPath(element);
|
||
|
||
if (Request.DryRun)
|
||
{
|
||
SnapshotRemoval(elementPath, typeName, true);
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
element.Delete();
|
||
SnapshotRemoval(elementPath, typeName, false);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Result.Errors.Add($"删除元素 {elementPath} 失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
private void SnapshotRemoval(string elementPath, string typeName, bool dryRun)
|
||
{
|
||
Result.RemovedCount++;
|
||
|
||
if (Result.RemovedElements.Count < MaxRemovedSnapshots)
|
||
{
|
||
var marker = dryRun ? "DRY" : "DEL";
|
||
Result.RemovedElements.Add($"[{marker}] {elementPath} ({typeName})");
|
||
}
|
||
}
|
||
|
||
private static bool TryGetLimitsBox(DbElement element, out float[] box)
|
||
{
|
||
box = null;
|
||
try
|
||
{
|
||
var spatial = Spatial.Instance;
|
||
if (spatial == null)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
Aveva.Pdms.Geometry.LimitsBox limits;
|
||
var ok = spatial.LimitsBox(element, out limits);
|
||
if (!ok)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
box = ConvertLimitsBox(limits);
|
||
return box != null && box.Length >= 6;
|
||
}
|
||
catch
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static float[] ConvertLimitsBox(Aveva.Pdms.Geometry.LimitsBox limits)
|
||
{
|
||
// 兼容不同版本 PDMS LimitsBox 字段/属性命名
|
||
// 期望输出: [xmin, ymin, zmin, xmax, ymax, zmax]
|
||
try
|
||
{
|
||
var type = typeof(Aveva.Pdms.Geometry.LimitsBox);
|
||
var boxed = (object)limits;
|
||
|
||
float xmin = ReadFloat(boxed, type, new[] { "XMIN", "XMin", "MinX", "Xmin" });
|
||
float ymin = ReadFloat(boxed, type, new[] { "YMIN", "YMin", "MinY", "Ymin" });
|
||
float zmin = ReadFloat(boxed, type, new[] { "ZMIN", "ZMin", "MinZ", "Zmin" });
|
||
float xmax = ReadFloat(boxed, type, new[] { "XMAX", "XMax", "MaxX", "Xmax" });
|
||
float ymax = ReadFloat(boxed, type, new[] { "YMAX", "YMax", "MaxY", "Ymax" });
|
||
float zmax = ReadFloat(boxed, type, new[] { "ZMAX", "ZMax", "MaxZ", "Zmax" });
|
||
|
||
return new[] { xmin, ymin, zmin, xmax, ymax, zmax };
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
private static float ReadFloat(object boxed, System.Type type, string[] names)
|
||
{
|
||
foreach (var name in names)
|
||
{
|
||
var prop = type.GetProperty(name);
|
||
if (prop != null)
|
||
{
|
||
var v = prop.GetValue(boxed, null);
|
||
if (v != null)
|
||
{
|
||
return Convert.ToSingle(v);
|
||
}
|
||
}
|
||
|
||
var field = type.GetField(name);
|
||
if (field != null)
|
||
{
|
||
var v = field.GetValue(boxed);
|
||
if (v != null)
|
||
{
|
||
return Convert.ToSingle(v);
|
||
}
|
||
}
|
||
}
|
||
|
||
throw new InvalidOperationException("LimitsBox缺少必要字段/属性");
|
||
}
|
||
|
||
private static float[] BuildInnerBox(float[] outerBox, float padding)
|
||
{
|
||
var inner = new float[6];
|
||
inner[0] = outerBox[0] + padding;
|
||
inner[1] = outerBox[1] + padding;
|
||
inner[2] = outerBox[2] + padding;
|
||
inner[3] = outerBox[3] - padding;
|
||
inner[4] = outerBox[4] - padding;
|
||
inner[5] = outerBox[5] - padding;
|
||
|
||
if (inner[3] < inner[0])
|
||
{
|
||
inner[0] = outerBox[0];
|
||
inner[3] = outerBox[3];
|
||
}
|
||
|
||
if (inner[4] < inner[1])
|
||
{
|
||
inner[1] = outerBox[1];
|
||
inner[4] = outerBox[4];
|
||
}
|
||
|
||
if (inner[5] < inner[2])
|
||
{
|
||
inner[2] = outerBox[2];
|
||
inner[5] = outerBox[5];
|
||
}
|
||
|
||
return inner;
|
||
}
|
||
|
||
private static bool IsInsideInnerBox(float[] box, float[] innerBox, float tolerance)
|
||
{
|
||
if (box == null || innerBox == null || box.Length < 6 || innerBox.Length < 6)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
// box完全在innerBox内部(留tolerance避免贴边误删)
|
||
return (box[0] > innerBox[0] + tolerance) &&
|
||
(box[1] > innerBox[1] + tolerance) &&
|
||
(box[2] > innerBox[2] + tolerance) &&
|
||
(box[3] < innerBox[3] - tolerance) &&
|
||
(box[4] < innerBox[4] - tolerance) &&
|
||
(box[5] < innerBox[5] - tolerance);
|
||
}
|
||
}
|
||
}
|
||
} |