1489 lines
53 KiB
C#
1489 lines
53 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using TellmePdmsPluging.Models;
|
||
using Aveva.ApplicationFramework;
|
||
using Aveva.Pdms.Database;
|
||
using Aveva.ApplicationFramework.Presentation;
|
||
using System.Linq;
|
||
|
||
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,
|
||
FileSize = -1,
|
||
PolygonCount = -1,
|
||
FeatureCount = -1,
|
||
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 = "项目已打开";
|
||
PopulateOpenProjectMetrics(result);
|
||
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)";
|
||
if (opened)
|
||
{
|
||
PopulateOpenProjectMetrics(result);
|
||
}
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "项目打开异常: " + ex.Message;
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public CloseProjectResult CloseProject(CloseProjectRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new CloseProjectRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new CloseProjectResult
|
||
{
|
||
CompletedAt = DateTime.Now
|
||
};
|
||
|
||
try
|
||
{
|
||
var currentProject = Project.CurrentProject;
|
||
if (currentProject == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "Project.CurrentProject 为空,无法关闭项目";
|
||
return result;
|
||
}
|
||
|
||
bool isOpen = false;
|
||
try
|
||
{
|
||
isOpen = currentProject.IsOpen();
|
||
}
|
||
catch
|
||
{
|
||
isOpen = false;
|
||
}
|
||
|
||
result.WasOpen = isOpen;
|
||
result.ProjectName = GetProjectNameSafe(currentProject);
|
||
|
||
if (!isOpen)
|
||
{
|
||
result.Success = true;
|
||
result.Message = "当前没有已打开项目";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
|
||
var projectType = currentProject.GetType();
|
||
|
||
var closeWithForce = projectType.GetMethod("Close", new[] { typeof(bool) });
|
||
if (closeWithForce != null)
|
||
{
|
||
closeWithForce.Invoke(currentProject, new object[] { effectiveRequest.Force });
|
||
}
|
||
else
|
||
{
|
||
var closeNoArg = projectType.GetMethod("Close", Type.EmptyTypes);
|
||
if (closeNoArg == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "未找到可用的 Project.Close 方法";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
|
||
closeNoArg.Invoke(currentProject, null);
|
||
}
|
||
|
||
result.Success = true;
|
||
result.Message = "项目关闭成功";
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "关闭项目异常: " + ex.Message;
|
||
result.CompletedAt = DateTime.Now;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
public ExportIfcResult ExportIfc(ExportIfcRequest request)
|
||
{
|
||
var effectiveRequest = request ?? new ExportIfcRequest();
|
||
effectiveRequest.ApplyDefaults();
|
||
|
||
var result = new ExportIfcResult
|
||
{
|
||
StartedAt = DateTime.Now,
|
||
ExportPath = effectiveRequest.ExportPath,
|
||
FileName = effectiveRequest.FileName,
|
||
FullPath = effectiveRequest.GetFullPath()
|
||
};
|
||
|
||
try
|
||
{
|
||
if (!IsPdmsConnected())
|
||
{
|
||
result.Success = false;
|
||
result.Message = "PDMS 未连接";
|
||
result.CompletedAt = DateTime.Now;
|
||
result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds;
|
||
return result;
|
||
}
|
||
|
||
var currentMdb = MDB.CurrentMDB;
|
||
var designDb = currentMdb?.GetFirstDB(DbType.Design);
|
||
if (designDb == null || designDb.World == null)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "未找到有效的设计数据库";
|
||
result.CompletedAt = DateTime.Now;
|
||
result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds;
|
||
return result;
|
||
}
|
||
|
||
// 确保导出目录存在
|
||
if (!System.IO.Directory.Exists(effectiveRequest.ExportPath))
|
||
{
|
||
System.IO.Directory.CreateDirectory(effectiveRequest.ExportPath);
|
||
}
|
||
|
||
// 构建PML导出命令
|
||
string pmlCommand = BuildIfcExportCommand(effectiveRequest);
|
||
|
||
// 执行PML命令 - 通过MacroCommand类型反射构造并执行
|
||
var macroType = CommandManager.Instance.MacroCommand;
|
||
if (macroType == null)
|
||
throw new InvalidOperationException("MacroCommand类型未注册");
|
||
var ctors = macroType.GetConstructors(
|
||
System.Reflection.BindingFlags.Public |
|
||
System.Reflection.BindingFlags.NonPublic |
|
||
System.Reflection.BindingFlags.Instance);
|
||
var ctorInfo = new System.Text.StringBuilder();
|
||
foreach (var c in ctors)
|
||
{
|
||
ctorInfo.Append(c.ToString()).Append(" | ");
|
||
}
|
||
throw new InvalidOperationException("MacroCommand类型: " + macroType.FullName + " 构造函数: " + ctorInfo);
|
||
|
||
// 检查文件是否生成
|
||
if (System.IO.File.Exists(result.FullPath))
|
||
{
|
||
var fileInfo = new System.IO.FileInfo(result.FullPath);
|
||
result.FileSizeBytes = fileInfo.Length;
|
||
result.Success = true;
|
||
result.Message = "IFC导出成功";
|
||
}
|
||
else
|
||
{
|
||
result.Success = false;
|
||
result.Message = "IFC导出失败,未生成文件";
|
||
}
|
||
|
||
result.CompletedAt = DateTime.Now;
|
||
result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds;
|
||
return result;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
result.Success = false;
|
||
result.Message = "IFC导出异常: " + ex.Message;
|
||
result.CompletedAt = DateTime.Now;
|
||
result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds;
|
||
return result;
|
||
}
|
||
}
|
||
|
||
private string BuildIfcExportCommand(ExportIfcRequest request)
|
||
{
|
||
// 构建PDMS IFC导出的PML命令
|
||
// 根据PDMS版本和配置,命令可能有所不同
|
||
string fullPath = request.GetFullPath().Replace("\\", "/");
|
||
|
||
// 基本的IFC导出命令格式
|
||
return string.Format("EXPORT IFC FILE '{0}'", fullPath);
|
||
}
|
||
|
||
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,
|
||
FileSize = -1,
|
||
PolygonCount = -1,
|
||
FeatureCount = -1,
|
||
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 已经打开";
|
||
PopulateOpenMdbMetrics(result);
|
||
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 (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 打开成功";
|
||
PopulateOpenMdbMetrics(result);
|
||
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 static string GetProjectNameSafe(Project project)
|
||
{
|
||
if (project == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
try
|
||
{
|
||
var nameProperty = project.GetType().GetProperty("Name");
|
||
if (nameProperty != null)
|
||
{
|
||
var value = nameProperty.GetValue(project, null) as string;
|
||
if (!string.IsNullOrEmpty(value))
|
||
{
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
// ignore and fallback
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
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,
|
||
FileSize = TryGetCurrentModelFileSize(),
|
||
PolygonCount = -1,
|
||
FeatureCount = totalElements
|
||
};
|
||
}
|
||
}
|
||
|
||
return new ModelStatistics
|
||
{
|
||
TotalElements = 0,
|
||
ElementCounts = new Dictionary<string, int>(),
|
||
ZoneCount = 0,
|
||
ActiveZones = new List<string>(),
|
||
FileSize = -1,
|
||
PolygonCount = -1,
|
||
FeatureCount = 0
|
||
};
|
||
}
|
||
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>(),
|
||
FileSize = -1,
|
||
PolygonCount = -1,
|
||
FeatureCount = 0
|
||
};
|
||
}
|
||
}
|
||
|
||
private void PopulateOpenProjectMetrics(OpenProjectResult result)
|
||
{
|
||
var statistics = GetModelStatistics();
|
||
if (statistics == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
result.FileSize = statistics.FileSize;
|
||
result.PolygonCount = statistics.PolygonCount;
|
||
result.FeatureCount = statistics.FeatureCount;
|
||
}
|
||
|
||
private void PopulateOpenMdbMetrics(OpenMdbResult result)
|
||
{
|
||
var statistics = GetModelStatistics();
|
||
if (statistics == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
result.FileSize = statistics.FileSize;
|
||
result.PolygonCount = statistics.PolygonCount;
|
||
result.FeatureCount = statistics.FeatureCount;
|
||
}
|
||
|
||
private long TryGetCurrentModelFileSize()
|
||
{
|
||
try
|
||
{
|
||
var currentMdb = MDB.CurrentMDB;
|
||
if (currentMdb == null)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
var designDb = currentMdb.GetFirstDB(DbType.Design);
|
||
string path = TryGetCandidatePath(designDb) ?? TryGetCandidatePath(currentMdb);
|
||
if (string.IsNullOrEmpty(path) || !System.IO.File.Exists(path))
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
return new System.IO.FileInfo(path).Length;
|
||
}
|
||
catch
|
||
{
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
private static string TryGetCandidatePath(object source)
|
||
{
|
||
if (source == null)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var type = source.GetType();
|
||
var names = new[] { "FileName", "Filename", "FullName", "Path" };
|
||
foreach (var name in names)
|
||
{
|
||
var prop = type.GetProperty(name);
|
||
if (prop != null && prop.PropertyType == typeof(string))
|
||
{
|
||
var value = prop.GetValue(source, null) as string;
|
||
if (LooksLikeFilePath(value))
|
||
{
|
||
return value;
|
||
}
|
||
}
|
||
|
||
var field = type.GetField(name);
|
||
if (field != null && field.FieldType == typeof(string))
|
||
{
|
||
var value = field.GetValue(source) as string;
|
||
if (LooksLikeFilePath(value))
|
||
{
|
||
return value;
|
||
}
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
private static bool LooksLikeFilePath(string value)
|
||
{
|
||
if (string.IsNullOrEmpty(value))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
var trimmed = value.Trim();
|
||
if (trimmed.Length < 3)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
return trimmed.IndexOf('\\') >= 0 || trimmed.IndexOf('/') >= 0;
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
}
|