From 210a83f1312ae1e05632089558a794a4114d9168 Mon Sep 17 00:00:00 2001 From: sladro Date: Wed, 4 Mar 2026 17:07:25 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BF=AE=E5=A4=8D=E4=BA=86?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3bug=EF=BC=8C=E5=AF=BC=E5=87=BArvm=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 46 + Core/MainThreadInvoker.cs | 34 + Core/PdmsManager.cs | 1689 ++++++++++++----- Documentation/creo操控参考文档.md | 641 ------- Models/ExportIfcRequest.cs | 52 +- Models/ShrinkwrapModelRequest.cs | 7 +- Models/SimplifyModelRequest.cs | 7 +- .../Aveva.Pdms.BeforeFinishEventHandler.html | 47 + .../Aveva.Pdms.FinishEventHandler.html | 47 + .../Aveva.Pdms/Aveva.Pdms.ModuleType.html | 100 + ...a.Pdms.PdmsApplication.BeforeFinished.html | 39 + .../Aveva.Pdms.PdmsApplication.Exit.html | 43 + .../Aveva.Pdms.PdmsApplication.Finish.html | 36 + .../Aveva.Pdms.PdmsApplication.Finished.html | 39 + ...eva.Pdms.PdmsApplication.GraphicsMode.html | 38 + ...s.PdmsApplication.RaiseBeforeFinished.html | 47 + ...va.Pdms.PdmsApplication.RaiseFinished.html | 47 + .../Aveva.Pdms.PdmsApplication.Start.html | 60 + ...a.Pdms.PdmsApplication.StartEventLoop.html | 36 + ...dms.PdmsApplication.StartViaCommsFile.html | 51 + ....Pdms.PdmsApplication.getLicenseError.html | 39 + ...dms.PdmsApplication.getLicenseFeature.html | 44 + ...dms.PdmsApplication.hasLicenseFeature.html | 44 + .../Aveva.Pdms.PdmsApplication.html | 54 + .../Aveva.Pdms.PdmsApplicationEvents.html | 41 + .../Aveva.Pdms.PdmsApplicationMembers.html | 79 + .../Aveva.Pdms.PdmsApplicationMethods.html | 65 + .../Aveva.Pdms.PdmsApplicationProperties.html | 40 + ...Aveva.Pdms.PdmsVersion.GetCompanyName.html | 38 + .../Aveva.Pdms.PdmsVersion.GetCopyright.html | 38 + .../Aveva.Pdms.PdmsVersion.GetModuleName.html | 45 + ...eva.Pdms.PdmsVersion.GetModuleVersion.html | 45 + .../Aveva.Pdms.PdmsVersion.GetName.html | 38 + ...Aveva.Pdms.PdmsVersion.GetShortBanner.html | 38 + .../Aveva.Pdms.PdmsVersion.GetStatus.html | 38 + .../Aveva.Pdms.PdmsVersion.GetVersion.html | 38 + .../Aveva.Pdms/Aveva.Pdms.PdmsVersion.html | 54 + .../Aveva.Pdms.PdmsVersionMembers.html | 66 + .../Aveva.Pdms.PdmsVersionMethods.html | 63 + .../Aveva.Pdms/Aveva.Pdms.hhc | 1 + .../Aveva.Pdms/Aveva.Pdms.hhk | 1 + .../Aveva.Pdms/Aveva.Pdms.html | 90 + .../Aveva.Pdms/Aveva.PdmsHierarchy.html | 53 + .../Aveva.Pdms/MSDN.css | 405 ++++ .../Aveva.Pdms/pubevent.gif | Bin 0 -> 869 bytes .../Aveva.Pdms/pubmethod.gif | Bin 0 -> 889 bytes .../Aveva.Pdms/pubproperty.gif | Bin 0 -> 893 bytes .../Aveva.Pdms/static.gif | Bin 0 -> 909 bytes Network/HttpServer.cs | 93 +- 命令.md | 5 +- 日志排查指南.md | 119 ++ 51 files changed, 3695 insertions(+), 1085 deletions(-) create mode 100644 AGENTS.md delete mode 100644 Documentation/creo操控参考文档.md create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.BeforeFinishEventHandler.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.FinishEventHandler.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.ModuleType.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.BeforeFinished.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Exit.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finish.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finished.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.GraphicsMode.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseBeforeFinished.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseFinished.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Start.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartEventLoop.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartViaCommsFile.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseError.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseFeature.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.hasLicenseFeature.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationEvents.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMembers.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMethods.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationProperties.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCompanyName.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCopyright.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleName.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleVersion.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetName.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetShortBanner.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetStatus.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetVersion.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMembers.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMethods.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhc create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhk create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.PdmsHierarchy.html create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/MSDN.css create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/pubevent.gif create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/pubmethod.gif create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/pubproperty.gif create mode 100644 NetInterfaceReferenceFiles/Aveva.Pdms/static.gif create mode 100644 日志排查指南.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6b6861b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,46 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- `TellmePdmsPluging.sln` contains a single plugin project targeting .NET Framework 3.5. +- `Class1.cs` is the PDMS addin entry (`IAddin`) and lifecycle hook. +- `Commands/` contains command objects implementing `Core/ICommand.cs` (e.g., `OpenProjectCommand`, `ExportIfcCommand`). +- `Core/` holds shared runtime infrastructure (queueing, PDMS manager, main-thread invocation, API response models). +- `Network/` contains the local HTTP server and callback integration used by external clients. +- `Models/` stores request/response DTOs for API endpoints. +- `Documentation/` and `NetInterfaceReferenceFiles/` provide design notes and AVEVA reference docs. + +## Build, Test, and Development Commands +Run commands in Windows terminal with UTF-8 code page: +```powershell +chcp 65001 +msbuild TellmePdmsPluging.sln /p:Configuration=Debug /p:Platform=x86 +msbuild TellmePdmsPluging.sln /p:Configuration=Release /p:Platform=x86 +``` +- `Debug|x86`: local validation and troubleshooting. +- `Release|x86`: deployment artifact for PDMS host. + +Smoke-check after loading the plugin in PDMS: +```powershell +Invoke-WebRequest http://localhost:9001/health +``` + +## Coding Style & Naming Conventions +- Use 4-space indentation and K&R-style braces as in existing C# files. +- Use `PascalCase` for public types/methods/properties, `camelCase` for locals/parameters, and `_camelCase` for private fields. +- Keep command classes focused: parse/validate input in `Network/`, execute PDMS logic via `Core/PdmsManager`. +- Preserve compatibility with .NET 3.5 and PDMS x86 runtime (avoid newer language/runtime features). + +## Testing Guidelines +- There is currently no dedicated test project in this repository. +- Validate changes with targeted endpoint checks (`/health`, `/api/...`) and PDMS integration scenarios. +- If adding tests, place them in a separate `*.Tests` project and keep test names descriptive (`MethodName_State_ExpectedResult`). + +## Commit & Pull Request Guidelines +- Follow existing commit style: concise, action-oriented summaries; current history mainly uses Chinese imperative verbs (implement/fix/add/improve equivalents). +- Keep each commit focused on one functional change. +- PRs should include: change summary, affected endpoints/modules, manual validation steps, and sample request/response payloads when APIs change. + +## Encoding & Environment Requirements +- All text/code files must be UTF-8 **without BOM**. +- Always run `chcp 65001` before Windows command-line operations. +- Do not save source files in GBK/GB2312/GB18030. diff --git a/Core/MainThreadInvoker.cs b/Core/MainThreadInvoker.cs index d4dda25..228e548 100644 --- a/Core/MainThreadInvoker.cs +++ b/Core/MainThreadInvoker.cs @@ -29,15 +29,37 @@ namespace TellmePdmsPluging.Core return new InvokeResult(true, wrapper.Result, null, null); } + public static void InvokeAsync(ICommand command, Action onCompleted) + { + if (command == null) + { + if (onCompleted != null) + { + onCompleted(new InvokeResult(false, null, new ArgumentNullException("command"), "command为空")); + } + return; + } + + var wrapper = new ResultCommandWrapper(command, onCompleted); + SafeQueue.Enqueue(wrapper); + } + private class ResultCommandWrapper : IResultCommand { private readonly ICommand _inner; private readonly ManualResetEvent _done; + private readonly Action _onCompleted; public ResultCommandWrapper(ICommand inner) + : this(inner, null) + { + } + + public ResultCommandWrapper(ICommand inner, Action onCompleted) { _inner = inner; _done = new ManualResetEvent(false); + _onCompleted = onCompleted; CommandId = inner.CommandId; } @@ -75,6 +97,18 @@ namespace TellmePdmsPluging.Core { IsCompleted = true; _done.Set(); + + if (_onCompleted != null) + { + try + { + _onCompleted(new InvokeResult(Error == null, Result, Error, Error == null ? null : Error.Message)); + } + catch + { + // ignore callback errors + } + } } } diff --git a/Core/PdmsManager.cs b/Core/PdmsManager.cs index 51bf5d7..d1d0698 100644 --- a/Core/PdmsManager.cs +++ b/Core/PdmsManager.cs @@ -8,10 +8,17 @@ using System.Linq; namespace TellmePdmsPluging.Core { - public class PdmsManager - { - private static PdmsManager _instance; - private static readonly object _lock = new object(); + public class PdmsManager + { + private static PdmsManager _instance; + private static readonly object _lock = new object(); + private static readonly object _attributeCacheLock = new object(); + private static readonly Dictionary _attributeCache = new Dictionary(StringComparer.OrdinalIgnoreCase); + private static readonly string[] LinkedAttributeCandidates = new[] + { + "ATCONN", "BRCON", "DEPREF", "ECRFA", "CONNE", "CONNS", "ATTC", + "CTYA", "CTYE", "CTYO", "CTYS", "ENDPOI", "CLNK", "CONN", "DCON", "DPCO" + }; public static PdmsManager Instance { @@ -33,16 +40,17 @@ namespace TellmePdmsPluging.Core { } - public SimplifyModelResult SimplifyModel(SimplifyModelRequest request) - { - var effectiveRequest = request ?? new SimplifyModelRequest(); - effectiveRequest.ApplyDefaults(); - - var result = new SimplifyModelResult - { - DryRun = effectiveRequest.DryRun, - StartedAt = DateTime.Now - }; + public SimplifyModelResult SimplifyModel(SimplifyModelRequest request) + { + var effectiveRequest = request ?? new SimplifyModelRequest(); + effectiveRequest.ApplyDefaults(); + + var result = new SimplifyModelResult + { + DryRun = effectiveRequest.DryRun, + SkipLinkedElements = effectiveRequest.SkipLinkedElements, + StartedAt = DateTime.Now + }; try { @@ -57,23 +65,32 @@ namespace TellmePdmsPluging.Core 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; - } + if (designDb == null || designDb.World == null) + { + result.Success = false; + result.CompletedAt = DateTime.Now; + result.Message = "未找到有效的设计数据库"; + result.Errors.Add("未找到有效的设计数据库"); + return result; + } + + if (!effectiveRequest.DryRun && IsDbReadOnly(designDb)) + { + result.Success = false; + result.CompletedAt = DateTime.Now; + result.Message = "当前Design DB为只读,无法执行真实删除"; + result.Errors.Add("当前Design DB为只读,无法执行真实删除"); + 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 (sites != null) + { + foreach (var site in sites) + { if (!IsElementValid(site) || !string.Equals(GetElementTypeName(site), "SITE", StringComparison.OrdinalIgnoreCase)) { continue; @@ -100,16 +117,42 @@ namespace TellmePdmsPluging.Core 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; + } + } + } + + if (!effectiveRequest.DryRun && processed && result.RemovedCount > 0) + { + string persistError; + if (!TryPersistCurrentMdbChanges("TellmePdms simplify model", out persistError)) + { + result.Errors.Add("删除已执行,但保存更改失败: " + persistError); + } + } + + result.Success = processed && result.Errors.Count == 0; + if (!processed) + { + result.Message = "未找到符合过滤条件的Zone"; + } + else if (result.Success) + { + result.Message = result.DryRun ? "模型轻量化干跑完成" : "模型轻量化完成"; + } + else + { + var topErrors = result.Errors.Take(3).ToArray(); + var errorSummary = topErrors.Length == 0 ? "UNKNOWN_ERROR" : string.Join(" | ", topErrors); + result.Message = (result.DryRun ? "模型轻量化干跑完成,但存在错误: " : "模型轻量化完成,但存在错误: ") + errorSummary; + } + + if (effectiveRequest.SkipLinkedElements && result.SkippedLinkedCount > 0) + { + result.Message += ";已跳过连接元素 " + result.SkippedLinkedCount + " 个"; + } + + result.CompletedAt = DateTime.Now; + return result; } catch (Exception ex) { @@ -121,17 +164,18 @@ namespace TellmePdmsPluging.Core } } - 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 - }; + public ShrinkwrapModelResult ShrinkwrapModel(ShrinkwrapModelRequest request) + { + var effectiveRequest = request ?? new ShrinkwrapModelRequest(); + effectiveRequest.ApplyDefaults(); + + var result = new ShrinkwrapModelResult + { + DryRun = effectiveRequest.DryRun, + SkipLinkedElements = effectiveRequest.SkipLinkedElements, + Padding = effectiveRequest.Padding, + StartedAt = DateTime.Now + }; try { @@ -146,23 +190,32 @@ namespace TellmePdmsPluging.Core 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; - } + if (designDb == null || designDb.World == null) + { + result.Success = false; + result.CompletedAt = DateTime.Now; + result.Message = "未找到有效的设计数据库"; + result.Errors.Add("未找到有效的设计数据库"); + return result; + } + + if (!effectiveRequest.DryRun && IsDbReadOnly(designDb)) + { + result.Success = false; + result.CompletedAt = DateTime.Now; + result.Message = "当前Design DB为只读,无法执行真实删除"; + result.Errors.Add("当前Design DB为只读,无法执行真实删除"); + 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 (sites != null) + { + foreach (var site in sites) + { if (!IsElementValid(site) || !string.Equals(GetElementTypeName(site), "SITE", StringComparison.OrdinalIgnoreCase)) { continue; @@ -189,16 +242,42 @@ namespace TellmePdmsPluging.Core 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; + } + } + } + + if (!effectiveRequest.DryRun && processed && result.RemovedCount > 0) + { + string persistError; + if (!TryPersistCurrentMdbChanges("TellmePdms shrinkwrap model", out persistError)) + { + result.Errors.Add("删除已执行,但保存更改失败: " + persistError); + } + } + + result.Success = processed && result.Errors.Count == 0; + if (!processed) + { + result.Message = "未找到符合过滤条件的Zone"; + } + else if (result.Success) + { + result.Message = result.DryRun ? "外壳保留干跑完成" : "外壳保留完成"; + } + else + { + var topErrors = result.Errors.Take(3).ToArray(); + var errorSummary = topErrors.Length == 0 ? "UNKNOWN_ERROR" : string.Join(" | ", topErrors); + result.Message = (result.DryRun ? "外壳保留干跑完成,但存在错误: " : "外壳保留完成,但存在错误: ") + errorSummary; + } + + if (effectiveRequest.SkipLinkedElements && result.SkippedLinkedCount > 0) + { + result.Message += ";已跳过连接元素 " + result.SkippedLinkedCount + " 个"; + } + + result.CompletedAt = DateTime.Now; + return result; } catch (Exception ex) { @@ -434,37 +513,64 @@ namespace TellmePdmsPluging.Core 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); + // 执行PML命令 - 通过MacroCommand类型反射构造并执行 + var macroType = CommandManager.Instance.MacroCommand; + if (macroType == null) + throw new InvalidOperationException("MacroCommand类型未注册"); + var ctor = macroType.GetConstructor( + System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Instance, + null, + new[] { typeof(string), typeof(string), typeof(string) }, + null); + if (ctor == null) + { + 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未找到(string,string,string)构造函数,类型: " + macroType.FullName + " 构造函数: " + ctorInfo); + } + + var exportCommands = BuildExportCommands(effectiveRequest); + for (int i = 0; i < exportCommands.Length; i++) + { + var macroCommand = ctor.Invoke(new object[] + { + "TellmePdmsPluging.ExportIfc." + (i + 1), + exportCommands[i], + string.Empty + }) as Command; + + if (macroCommand == null) + { + throw new InvalidOperationException("MacroCommand实例创建失败,类型: " + macroType.FullName); + } + + macroCommand.Execute(); + } // 检查文件是否生成 - 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导出失败,未生成文件"; - } + if (System.IO.File.Exists(result.FullPath)) + { + var fileInfo = new System.IO.FileInfo(result.FullPath); + result.FileSizeBytes = fileInfo.Length; + result.Success = true; + result.Message = "RVM导出成功"; + } + else + { + result.Success = false; + result.Message = "RVM导出失败,未生成文件"; + } result.CompletedAt = DateTime.Now; result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds; @@ -472,23 +578,41 @@ namespace TellmePdmsPluging.Core } 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); - } + result.Success = false; + result.Message = "RVM导出异常: " + ex.Message; + result.CompletedAt = DateTime.Now; + result.DurationSeconds = (result.CompletedAt - result.StartedAt).TotalSeconds; + return result; + } + } + + private string[] BuildExportCommands(ExportIfcRequest request) + { + // 按文档流程构建命令: + // EXPORT SYSTEM (可选) + // EXPORT FILE OVERWRITE + // EXPORT + // EXPORT FINISH + string fullPath = request.GetFullPath().Replace("\\", "/"); + var commands = new List(); + + if (!string.IsNullOrEmpty(request.ExportSystem)) + { + commands.Add("EXPORT SYSTEM " + request.ExportSystem); + } + + commands.Add(string.Format("EXPORT FILE {0} {1}", fullPath, request.Overwrite.GetValueOrDefault(true) ? "OVERWRITE" : "READ")); + + var selections = request.GetEffectiveSelections(); + foreach (var selectionCommand in selections) + { + commands.Add(selectionCommand); + } + + commands.Add("EXPORT FINISH"); + + return commands.ToArray(); + } public OpenMdbResult OpenMdb(OpenMdbRequest request) { @@ -696,11 +820,140 @@ namespace TellmePdmsPluging.Core return null; } - - private bool IsPdmsConnected() - { - try - { + + private static bool IsDbReadOnly(Db db) + { + if (db == null) + { + return true; + } + + try + { + var property = db.GetType().GetProperty("IsReadOnly"); + if (property == null) + { + return false; + } + + var value = property.GetValue(db, null); + if (value is bool) + { + return (bool)value; + } + } + catch + { + // ignore and treat as writable + } + + return false; + } + + private static bool TryPersistCurrentMdbChanges(string comment, out string error) + { + error = null; + try + { + var mdb = MDB.CurrentMDB; + if (mdb == null) + { + error = "当前MDB为空"; + return false; + } + + bool saveResult; + if (TryInvokeSaveWorkByComment(mdb, comment, out saveResult)) + { + if (saveResult) + { + return true; + } + + error = "SaveWork(string) 返回 false"; + return false; + } + + var designDb = mdb.GetFirstDB(DbType.Design); + if (designDb == null) + { + error = "未找到Design DB"; + return false; + } + + if (IsDbReadOnly(designDb)) + { + error = "Design DB为只读"; + return false; + } + + if (TryInvokeSaveWorkByDbs(mdb, new[] { designDb }, comment, out saveResult)) + { + if (saveResult) + { + return true; + } + + error = "SaveWork(Db[], string) 返回 false"; + return false; + } + + error = "未找到可用的SaveWork重载"; + return false; + } + catch (Exception ex) + { + error = ex.Message; + return false; + } + } + + private static bool TryInvokeSaveWorkByComment(MDB mdb, string comment, out bool saveResult) + { + saveResult = false; + try + { + var method = mdb.GetType().GetMethod("SaveWork", new[] { typeof(string) }); + if (method == null) + { + return false; + } + + var result = method.Invoke(mdb, new object[] { comment ?? string.Empty }); + saveResult = !(result is bool) || (bool)result; + return true; + } + catch + { + return false; + } + } + + private static bool TryInvokeSaveWorkByDbs(MDB mdb, Db[] dbs, string comment, out bool saveResult) + { + saveResult = false; + try + { + var method = mdb.GetType().GetMethod("SaveWork", new[] { typeof(Db[]), typeof(string) }); + if (method == null) + { + return false; + } + + var result = method.Invoke(mdb, new object[] { dbs, comment ?? string.Empty }); + saveResult = !(result is bool) || (bool)result; + return true; + } + catch + { + return false; + } + } + + private bool IsPdmsConnected() + { + try + { // 使用MDB.CurrentMDB检查PDMS连接状态 var currentMdb = MDB.CurrentMDB; return currentMdb != null; @@ -1084,11 +1337,11 @@ namespace TellmePdmsPluging.Core } } - private static string BuildElementPath(DbElement element) - { - var segments = new List(); - var current = element; - int guard = 0; + private static string BuildElementPath(DbElement element) + { + var segments = new List(); + var current = element; + int guard = 0; while (IsElementValid(current) && guard < 128) { @@ -1110,13 +1363,263 @@ namespace TellmePdmsPluging.Core guard++; } - segments.Reverse(); - return "/" + string.Join("/", segments.ToArray()); - } - - private class SimplifyContext - { - private const int MaxRemovedSnapshots = 200; + segments.Reverse(); + return "/" + string.Join("/", segments.ToArray()); + } + + private static bool ShouldSkipLinkedElement(DbElement element, bool skipLinkedElements, out string reason) + { + reason = null; + + if (!skipLinkedElements || !IsElementValid(element)) + { + return false; + } + + try + { + if (!element.IsDeleteable) + { + reason = "IsDeleteable=false"; + return true; + } + } + catch + { + // ignore + } + + string attributeName; + string attributeValue; + if (TryFindLinkedAttributeValue(element, out attributeName, out attributeValue)) + { + reason = string.IsNullOrEmpty(attributeValue) + ? attributeName + : (attributeName + "=" + attributeValue); + return true; + } + + return false; + } + + private static bool TryFindLinkedAttributeValue(DbElement element, out string attributeName, out string attributeValue) + { + attributeName = null; + attributeValue = null; + + if (!IsElementValid(element)) + { + return false; + } + + foreach (var candidate in LinkedAttributeCandidates) + { + DbAttribute attribute; + if (!TryGetDbAttributeByName(candidate, out attribute) || attribute == null) + { + continue; + } + + bool isValid; + try + { + isValid = element.IsAttributeValid(attribute); + } + catch + { + continue; + } + + if (!isValid) + { + continue; + } + + string textValue = string.Empty; + try + { + if (element.GetValidAsString(attribute, ref textValue) && HasMeaningfulLinkedValue(textValue)) + { + attributeName = candidate; + attributeValue = SummarizeValue(textValue); + return true; + } + } + catch + { + // ignore and fallback to array check + } + + try + { + var refs = element.GetElementArray(attribute); + if (refs != null) + { + for (int i = 0; i < refs.Length; i++) + { + if (IsElementValid(refs[i])) + { + attributeName = candidate; + attributeValue = "REFS>0"; + return true; + } + } + } + } + catch + { + // ignore + } + } + + return false; + } + + private static bool TryGetDbAttributeByName(string name, out DbAttribute attribute) + { + attribute = null; + if (string.IsNullOrEmpty(name)) + { + return false; + } + + lock (_attributeCacheLock) + { + if (_attributeCache.TryGetValue(name, out attribute)) + { + return attribute != null; + } + } + + DbAttribute resolved = null; + try + { + var flags = System.Reflection.BindingFlags.Public | + System.Reflection.BindingFlags.Static | + System.Reflection.BindingFlags.IgnoreCase; + + var field = typeof(DbAttributeInstance).GetField(name, flags); + if (field != null) + { + resolved = field.GetValue(null) as DbAttribute; + } + + if (resolved == null) + { + var property = typeof(DbAttributeInstance).GetProperty(name, flags); + if (property != null) + { + resolved = property.GetValue(null, null) as DbAttribute; + } + } + } + catch + { + resolved = null; + } + + lock (_attributeCacheLock) + { + _attributeCache[name] = resolved; + } + + attribute = resolved; + return attribute != null; + } + + private static bool HasMeaningfulLinkedValue(string value) + { + if (IsNullOrWhiteSpace(value)) + { + return false; + } + + var normalized = value.Trim().ToUpperInvariant(); + switch (normalized) + { + case "0": + case "FALSE": + case "NONE": + case "NO": + case "NULREF": + case "NULL": + case "UNSET": + case "UNDEFINED": + case "[]": + case "{}": + return false; + } + + if (normalized.IndexOf("NULREF", StringComparison.Ordinal) >= 0 || + normalized.IndexOf("UNSET", StringComparison.Ordinal) >= 0 || + normalized.IndexOf("UNDEFINED", StringComparison.Ordinal) >= 0) + { + return false; + } + + bool hasLetter = false; + bool hasSlash = false; + for (int i = 0; i < normalized.Length; i++) + { + char ch = normalized[i]; + if (char.IsLetter(ch)) + { + hasLetter = true; + break; + } + if (ch == '/') + { + hasSlash = true; + } + } + + if (!hasLetter && !hasSlash) + { + return false; + } + + return true; + } + + private static string SummarizeValue(string value) + { + if (string.IsNullOrEmpty(value)) + { + return value; + } + + var trimmed = value.Trim(); + const int maxLength = 64; + if (trimmed.Length <= maxLength) + { + return trimmed; + } + + return trimmed.Substring(0, maxLength) + "..."; + } + + private static bool IsNullOrWhiteSpace(string value) + { + if (value == null) + { + return true; + } + + for (int i = 0; i < value.Length; i++) + { + if (!char.IsWhiteSpace(value[i])) + { + return false; + } + } + + return true; + } + + private class SimplifyContext + { + private const int MaxRemovedSnapshots = 200; + private const int MaxSkippedSnapshots = 200; public SimplifyContext(SimplifyModelRequest request, SimplifyModelResult result) { @@ -1191,298 +1694,636 @@ namespace TellmePdmsPluging.Core ProcessChildren(element); } - private void RemoveElement(DbElement element, string typeName) - { - var elementPath = BuildElementPath(element); + private void RemoveElement(DbElement element, string typeName) + { + var elementPath = BuildElementPath(element); + + try + { + string linkedReason; + if (ShouldSkipLinkedElement(element, Request.SkipLinkedElements, out linkedReason)) + { + Result.KeptCount++; + SnapshotSkipped(elementPath, typeName, linkedReason); + return; + } + + if (Request.DryRun) + { + SnapshotRemoval(elementPath, typeName, true); + return; + } + + 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) + { + Result.RemovedElements.Add((dryRun ? "[DRYRUN-DEL] " : "[DEL] ") + elementPath + " (" + typeName + ")"); + } + } + + private void SnapshotSkipped(string elementPath, string typeName, string reason) + { + Result.SkippedLinkedCount++; + + if (Result.SkippedLinkedElements.Count < MaxSkippedSnapshots) + { + var suffix = string.IsNullOrEmpty(reason) ? string.Empty : (" [" + reason + "]"); + Result.SkippedLinkedElements.Add("[SKIP-LINKED] " + elementPath + " (" + typeName + ")" + suffix); + } + } + } - 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(request.KeepTypes ?? new List()); - } - - public ShrinkwrapModelRequest Request { get; } - public ShrinkwrapModelResult Result { get; } - public string CurrentZoneName { get; private set; } - - private readonly HashSet _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); - } - } + private class ShrinkwrapContext + { + private const int MaxRemovedSnapshots = 200; + private const int MaxSkippedSnapshots = 200; + private const float MinBoxEdge = 0.001f; + + public ShrinkwrapContext(ShrinkwrapModelRequest request, ShrinkwrapModelResult result) + { + Request = request; + Result = result; + _keepTypes = new HashSet(request.KeepTypes ?? new List()); + _shellBoxes = new List(); + } + + public ShrinkwrapModelRequest Request { get; } + public ShrinkwrapModelResult Result { get; } + public string CurrentZoneName { get; private set; } + + private readonly HashSet _keepTypes; + private float[] _zoneBox; + private float[] _innerBox; + private float[] _innerDeleteBox; + private List _shellBoxes; + + 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) + { + if (!TryGetLimitsBox(zone, out _zoneBox)) + { + Result.Errors.Add($"Zone包围盒计算失败: {BuildElementPath(zone)}"); + return; + } + + _innerBox = BuildInnerBox(_zoneBox, (float)Math.Max(0d, Request.Padding)); + _innerDeleteBox = BuildDeleteCoreBox(_innerBox, (float)Math.Max(0d, Request.TouchTolerance)); + _shellBoxes = BuildShellBoxes(_zoneBox, _innerDeleteBox); + + if (!ProcessZoneWithExactSpatial(zone)) + { + ProcessChildren(zone); + } + } + + private bool ProcessZoneWithExactSpatial(DbElement zone) + { + if (_shellBoxes == null || _shellBoxes.Count == 0 || _innerDeleteBox == null) + { + return false; + } + + var spatial = Spatial.Instance; + if (spatial == null) + { + return false; + } + + DbElement[] candidates; + try + { + candidates = spatial.ElementsInElementExact(zone, true); + } + catch + { + return false; + } + + if (candidates == null || candidates.Length == 0) + { + return true; + } + + Array.Sort(candidates, delegate (DbElement a, DbElement b) + { + return GetOwnerDepth(b).CompareTo(GetOwnerDepth(a)); + }); + + foreach (var element in candidates) + { + ProcessExactCandidate(element); + } + + return true; + } + + private void ProcessExactCandidate(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++; + return; + } + + if (OverlapsAnyShellBox(element)) + { + Result.ShellKeptCount++; + Result.KeptCount++; + return; + } + + bool overlapsDeleteCore; + if (!TryElementInBox(element, _innerDeleteBox, out overlapsDeleteCore)) + { + float[] box; + if (TryGetLimitsBox(element, out box) && IsInsideInnerBox(box, _innerDeleteBox, 0f)) + { + RemoveElement(element, normalizedType); + return; + } + + Result.KeptCount++; + return; + } + + if (overlapsDeleteCore) + { + RemoveElement(element, normalizedType); + return; + } + + Result.KeptCount++; + } + + private bool OverlapsAnyShellBox(DbElement element) + { + if (_shellBoxes == null || _shellBoxes.Count == 0) + { + return false; + } + + foreach (var shellBox in _shellBoxes) + { + bool overlaps; + if (TryElementInBox(element, shellBox, out overlaps) && overlaps) + { + return true; + } + } + + return false; + } + + 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; + } + + if (OverlapsAnyShellBox(element)) + { + Result.ShellKeptCount++; + Result.KeptCount++; + ProcessChildren(element); + return; + } + + float[] box; + if (!TryGetLimitsBox(element, out box)) + { + Result.KeptCount++; + return; + } + + if (IsInsideInnerBox(box, _innerDeleteBox, 0f)) + { + RemoveElement(element, normalizedType); + return; + } + + Result.KeptCount++; + ProcessChildren(element); + } + + private void RemoveElement(DbElement element, string typeName) + { + var elementPath = BuildElementPath(element); + + try + { + string linkedReason; + if (ShouldSkipLinkedElement(element, Request.SkipLinkedElements, out linkedReason)) + { + Result.KeptCount++; + SnapshotSkipped(elementPath, typeName, linkedReason); + return; + } + + if (Request.DryRun) + { + SnapshotRemoval(elementPath, typeName, true); + return; + } + + 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) + { + Result.RemovedElements.Add((dryRun ? "[DRYRUN-DEL] " : "[DEL] ") + elementPath + " (" + typeName + ")"); + } + } + + private void SnapshotSkipped(string elementPath, string typeName, string reason) + { + Result.SkippedLinkedCount++; + + if (Result.SkippedLinkedElements.Count < MaxSkippedSnapshots) + { + var suffix = string.IsNullOrEmpty(reason) ? string.Empty : (" [" + reason + "]"); + Result.SkippedLinkedElements.Add("[SKIP-LINKED] " + elementPath + " (" + typeName + ")" + suffix); + } + } + + private static int GetOwnerDepth(DbElement element) + { + var current = element; + int depth = 0; + int guard = 0; + + while (IsElementValid(current) && guard < 512) + { + depth++; + current = current.Owner; + guard++; + } + + return depth; + } + + private static bool TryElementInBox(DbElement element, float[] box, out bool overlaps) + { + overlaps = false; + if (!IsElementValid(element) || box == null || box.Length < 6) + { + return false; + } + + try + { + float[] elementBox; + if (!TryGetLimitsBox(element, out elementBox)) + { + return false; + } + + overlaps = BoxesOverlap(elementBox, box); + return true; + } + catch + { + return false; + } + } + + private static bool TryGetLimitsBox(DbElement element, out float[] box) + { + box = null; + if (!IsElementValid(element)) + { + return false; + } + + try + { + var spatial = Spatial.Instance; + if (spatial == null) + { + return false; + } + + var spatialType = spatial.GetType(); + + var arrayMethod = spatialType.GetMethod("LimitsBox", new[] { typeof(DbElement), typeof(float[]) }); + if (arrayMethod != null) + { + var args = new object[] { element, new float[6] }; + var okObj = arrayMethod.Invoke(spatial, args); + if (okObj is bool && (bool)okObj) + { + if (TryNormalizeBox(args[1] as float[], out box)) + { + return true; + } + } + } + + var methods = spatialType.GetMethods().Where(m => m.Name == "LimitsBox").ToArray(); + foreach (var method in methods) + { + var parameters = method.GetParameters(); + if (parameters.Length != 2 || parameters[0].ParameterType != typeof(DbElement) || !parameters[1].ParameterType.IsByRef) + { + continue; + } + + object boxValue; + try + { + boxValue = Activator.CreateInstance(parameters[1].ParameterType.GetElementType()); + } + catch + { + continue; + } + + var args = new object[] { element, boxValue }; + var okObj = method.Invoke(spatial, args); + if (!(okObj is bool) || !(bool)okObj) + { + continue; + } + + var converted = ConvertLimitsBoxObject(args[1]); + if (TryNormalizeBox(converted, out box)) + { + return true; + } + } + } + catch + { + // ignore + } + + return false; + } + + private static float[] ConvertLimitsBoxObject(object limits) + { + if (limits == null) + { + return null; + } + + var type = limits.GetType(); + + try + { + float xmin = ReadFloat(limits, type, new[] { "XMIN", "XMin", "MinX", "Xmin" }); + float ymin = ReadFloat(limits, type, new[] { "YMIN", "YMin", "MinY", "Ymin" }); + float zmin = ReadFloat(limits, type, new[] { "ZMIN", "ZMin", "MinZ", "Zmin" }); + float xmax = ReadFloat(limits, type, new[] { "XMAX", "XMax", "MaxX", "Xmax" }); + float ymax = ReadFloat(limits, type, new[] { "YMAX", "YMax", "MaxY", "Ymax" }); + float zmax = ReadFloat(limits, type, new[] { "ZMAX", "ZMax", "MaxZ", "Zmax" }); + return new[] { xmin, ymin, zmin, xmax, ymax, zmax }; + } + catch + { + return null; + } + } + + private static float ReadFloat(object boxed, Type type, string[] names) + { + foreach (var name in names) + { + var prop = type.GetProperty(name); + if (prop != null) + { + var value = prop.GetValue(boxed, null); + if (value != null) + { + return Convert.ToSingle(value); + } + } + + var field = type.GetField(name); + if (field != null) + { + var value = field.GetValue(boxed); + if (value != null) + { + return Convert.ToSingle(value); + } + } + } + + throw new InvalidOperationException("LimitsBox缺少必要字段/属性"); + } + + private static bool TryNormalizeBox(float[] source, out float[] normalized) + { + normalized = null; + if (source == null || source.Length < 6) + { + return false; + } + + float xmin = Math.Min(source[0], source[3]); + float ymin = Math.Min(source[1], source[4]); + float zmin = Math.Min(source[2], source[5]); + float xmax = Math.Max(source[0], source[3]); + float ymax = Math.Max(source[1], source[4]); + float zmax = Math.Max(source[2], source[5]); + + if ((xmax - xmin) < MinBoxEdge || (ymax - ymin) < MinBoxEdge || (zmax - zmin) < MinBoxEdge) + { + return false; + } + + normalized = new[] { xmin, ymin, zmin, xmax, ymax, zmax }; + return true; + } + + private static float[] BuildInnerBox(float[] outerBox, float padding) + { + if (outerBox == null || outerBox.Length < 6) + { + return null; + } + + 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 float[] BuildDeleteCoreBox(float[] innerBox, float tolerance) + { + if (innerBox == null || innerBox.Length < 6) + { + return null; + } + + var core = new float[6]; + var offset = Math.Max(0f, tolerance); + core[0] = innerBox[0] + offset; + core[1] = innerBox[1] + offset; + core[2] = innerBox[2] + offset; + core[3] = innerBox[3] - offset; + core[4] = innerBox[4] - offset; + core[5] = innerBox[5] - offset; + + float[] normalized; + return TryNormalizeBox(core, out normalized) ? normalized : null; + } + + private static List BuildShellBoxes(float[] outerBox, float[] innerDeleteBox) + { + var boxes = new List(); + if (outerBox == null || innerDeleteBox == null) + { + return boxes; + } + + AddShellBox(boxes, outerBox[0], outerBox[1], outerBox[2], innerDeleteBox[0], outerBox[4], outerBox[5]); // Left + AddShellBox(boxes, innerDeleteBox[3], outerBox[1], outerBox[2], outerBox[3], outerBox[4], outerBox[5]); // Right + AddShellBox(boxes, innerDeleteBox[0], outerBox[1], outerBox[2], innerDeleteBox[3], innerDeleteBox[1], outerBox[5]); // Front + AddShellBox(boxes, innerDeleteBox[0], innerDeleteBox[4], outerBox[2], innerDeleteBox[3], outerBox[4], outerBox[5]); // Back + AddShellBox(boxes, innerDeleteBox[0], innerDeleteBox[1], outerBox[2], innerDeleteBox[3], innerDeleteBox[4], innerDeleteBox[2]); // Bottom + AddShellBox(boxes, innerDeleteBox[0], innerDeleteBox[1], innerDeleteBox[5], innerDeleteBox[3], innerDeleteBox[4], outerBox[5]); // Top + + return boxes; + } + + private static void AddShellBox(List boxes, float xmin, float ymin, float zmin, float xmax, float ymax, float zmax) + { + var candidate = new[] { xmin, ymin, zmin, xmax, ymax, zmax }; + float[] normalized; + if (TryNormalizeBox(candidate, out normalized)) + { + boxes.Add(normalized); + } + } + + private static bool IsInsideInnerBox(float[] box, float[] innerBox, float tolerance) + { + if (box == null || innerBox == null || box.Length < 6 || innerBox.Length < 6) + { + return false; + } + + 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); + } + + private static bool BoxesOverlap(float[] a, float[] b) + { + if (a == null || b == null || a.Length < 6 || b.Length < 6) + { + return false; + } + + return (a[0] <= b[3]) && (a[3] >= b[0]) && + (a[1] <= b[4]) && (a[4] >= b[1]) && + (a[2] <= b[5]) && (a[5] >= b[2]); + } + } } } diff --git a/Documentation/creo操控参考文档.md b/Documentation/creo操控参考文档.md deleted file mode 100644 index efd7b83..0000000 --- a/Documentation/creo操控参考文档.md +++ /dev/null @@ -1,641 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## 项目架构 - -这是一个 MFC 动态链接库 (DLL) 项目,作为 Creo CAD 软件的插件运行。项目整合了以下技术栈: - -- **MFC框架**: 微软基础类库,用于 Windows 应用程序开发 -- **OTK/ProToolkit**: PTC Creo 的官方开发工具包,用于与 Creo 交互 -- **Windows Socket**: 原生网络通信,替代第三方HTTP库 -- **WebSocket**: 实时双向通信支持 - -## 当前功能 - -目前实现了基础的 HTTP 服务器,允许外部应用通过 HTTP 请求与 Creo 软件进行交互: -- HTTP 服务器监听端口 12345 -- 提供 `/show_message` 端点接收文本消息并在 Creo 中显示 -- 使用定时器机制解决跨线程 OTK 调用问题 -- 支持跨主机部署 - -## 项目升级计划 - Web API + WebSocket 架构 - -### 项目目标 -实现Web前端通过API控制Creo进行模型分析、特征删除和格式导出的MVP系统。 - -### 接口设计 -**HTTP API (快速查询)**: -- GET /api/status/creo - 检测Creo运行状态 -- GET /api/status/model - 检查模型加载状态 -- GET /api/model/features - 获取特征列表 -- POST /api/auth/login - 用户认证 - -**WebSocket (长操作)**: -- load_model - 加载模型 -- delete_features - 删除特征 -- export_model - 导出模型 -- analyze_structure - 分析结构 - -### 技术架构 -``` -Web前端 -> HTTP API (状态查询) -> Creo状态管理器 - -> WebSocket (操作执行) -> Creo操作管理器 -> 实时日志推送 -``` - -### 目标文件结构 -``` -MFCCreoDll/ -├── src/ -│ ├── core/ # 核心服务器管理 -│ ├── http/ # HTTP API处理 -│ ├── websocket/ # WebSocket服务 -│ ├── creo/ # Creo操作封装 -│ ├── utils/ # 工具类 -│ └── auth/ # 认证管理 -``` - -### 开发计划 -- 第1周:基础框架 (HTTP改造 + WebSocket基础) -- 第2周:核心功能 (Creo集成 + 操作实现) -- 第3周:测试优化 (联调 + 性能优化) - -### 关键特性 -- 双端口服务:12345(HTTP) + 12346(WebSocket) -- 实时日志推送和进度反馈 -- 大模型支持(>2GB)一次性加载 -- 简单用户识别机制 -- 跨主机部署支持 - -## 开发进度记录 - 模块化实现 - -### ✅ 已完成模块 - -#### 模块1: 基础HTTP服务器 (完成) -**功能:** HTTP服务器框架,支持路由注册和请求处理 -**文件:** HttpServer.h, HttpServer.cpp, Config.h -**测试状态:** ✅ 编译成功,功能测试通过 -**API端点:** -- `/test` - 服务器连通性测试 -- `/show_message?text=消息` - 在Creo中显示消息 - -#### 模块2: Creo状态检测 (完成) -**功能:** Creo连接状态和模型状态实时检测 -**文件:** CreoManager.h, CreoManager.cpp -**测试状态:** ✅ 编译成功,功能测试通过,已优化增强 -**API端点:** -- `/api/status/creo` - Creo连接状态(包含版本、工作目录、会话ID) -- `/api/status/model` - 当前模型状态(包含名称、路径、修改状态等) - -**技术细节:** -- 使用OTK API进行Creo交互 -- 采用单例模式管理Creo会话 -- 完善的异常处理机制 -- 详细的状态信息返回 - -#### 模块3: STEP导出功能 (完成) -**功能:** 支持将Creo模型导出为STEP格式 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 编译成功,功能测试通过,已解决崩溃问题 -**API端点:** -- `POST /api/export/model` - 模型导出接口 - -**技术细节:** -- 使用 `pfcSTEPExportInstructions` 接口进行STEP导出 -- 支持装配体和零件的导出 -- 包含文件大小计算和时间戳功能 -- 安全的JSON解析机制(避免正则表达式崩溃) -- 完善的错误处理和异常管理 - -**API请求格式:** -```json -{ - "software_type": "creo", - "format_type": "step", - "export_path": "D:\\model.stp", - "options": { - "geom_flags": "solids", - "advanced": false - } -} -``` - -**API响应格式:** -```json -{ - "success": true, - "data": { - "exportPath": "D:\\model.stp", - "fileSize": "15.2MB", - "format": "step", - "exportTime": "2024-01-15T10:30:00Z", - "software": "Creo Parametric", - "originalFile": "assembly.asm", - "details": { - "dirname": "D:\\", - "filename": "model.stp", - "creoson_response": { - "dirname": "D:\\", - "filename": "model.stp", - "full_path": "D:\\model.stp" - } - } - }, - "error": null -} -``` - -#### 模块4: 层级分析功能 (完成) -**功能:** 装配体层级结构分析,支持无限深度遍历 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 编译成功,已修复循环调用问题 -**API端点:** -- `POST /api/creo/analysis/hierarchy` - 装配体层级分析 - -**技术细节:** -- 使用SOTA算法基于ListFeaturesByType进行组件遍历 -- 支持装配体和零件的完整层级分析 -- 优化了API调用链,避免重复的LoadComponentModel和ListFeaturesByType调用 -- 实现了组件信息提取、文件大小计算和删除安全评估 -- 支持无层级限制的递归分析 - -**API请求格式:** -```json -{ - "software_type": "creo", - "project_name": "Assembly Analysis", - "max_depth": 0, - "include_geometry": false -} -``` - -**API响应格式:** -```json -{ - "success": true, - "message": "Hierarchy analysis completed", - "data": { - "project_name": "ASM0001.asm", - "total_levels": 9, - "total_components": 223, - "hierarchy": [ - { - "level": 0, - "name": "Main Assembly", - "components": [ - { - "id": "ASM0001.asm", - "name": "Asm0001", - "type": "assembly", - "level": 0, - "children_count": 5, - "path": "ASM0001.asm", - "file_size": "2.5MB", - "deletion_safety": "forbidden" - } - ] - } - ], - "deletion_recommendations": { - "safe_deletions": [], - "risky_deletions": [] - } - }, - "error": null -} -``` - -**已解决的技术问题:** -1. **重复API调用优化** - 修复了LoadComponentModel和ListFeaturesByType的重复调用 -2. **递归调用优化** - 优化了组件加载流程,避免不必要的重复操作 -3. **内存管理改进** - 使用预加载模型参数传递,减少内存分配 - -#### 模块5: 层级删除功能 (完成) -**功能:** 装配体层级组件删除,支持安全的组件移除 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 编译成功,功能测试通过,已解决崩溃问题 -**API端点:** -- `POST /api/creo/hierarchy/delete` - 层级组件删除接口 - -**技术细节:** -- 使用 `SuppressFeatures` 替代 `DeleteFeatures` 实现安全删除 -- 保持特征引用关系完整,避免装配体结构破坏 -- 支持任意层级的组件删除,包括根层级组件 -- 使用重生成指令确保模型状态一致性 -- 完善的异常处理和错误恢复机制 - -**API请求格式:** -```json -{ - "software_type": "creo", - "project_name": "Assembly Delete", - "target_level": 2 -} -``` - -**API响应格式:** -```json -{ - "success": true, - "message": "All components suppressed successfully (safer than deletion)", - "data": { - "original_levels": 5, - "target_level": 2, - "final_levels": 3, - "deleted_components": { - "level_2": [ - "component1.prt", - "component2.asm" - ] - }, - "deletion_summary": { - "total_deleted": 15, - "successful": 15, - "failed": 0 - } - }, - "error": null -} -``` - -**已解决的技术问题:** -1. **Creo崩溃问题** - 使用SuppressFeatures替代DeleteFeatures避免引用丢失导致的崩溃 -2. **层级安全性** - 支持删除任意层级的组件,包括根层级的关键组件 -3. **引用完整性** - 抑制特征保持引用关系,避免外部依赖丢失 -4. **模型重生成** - 使用重生成指令确保删除后模型状态正确 -5. **异常处理** - 完善的错误处理机制,操作失败时提供详细信息 - -**关键技术突破:** -- 发现并解决了OTK DeleteFeatures在删除装配体核心组件时的崩溃问题 -- 采用Suppression策略实现视觉删除效果,同时保持模型结构完整性 -- 实现了从根层级到任意深度的安全组件删除 - -#### 模块6: 薄壳化分析功能 (完成) -**功能:** CAD模型薄壳化分析,基于几何边界识别内部可删除特征 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 编译成功,功能测试通过,API格式已优化 -**API端点:** -- `POST /api/creo/analysis/shell` - 薄壳化分析接口 - -#### 模块7: 关闭模型功能 (完成) -**功能:** 安全关闭当前Creo模型,支持修改状态检查和强制关闭 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 开发完成,待编译测试 -**API端点:** -- `POST /api/model/close` - 关闭模型接口 - -**技术细节:** -- 使用OTK `pfcModel::Erase()` API执行模型关闭操作 -- 使用 `pfcModel::GetIsModified()` 检查模型修改状态 -- 支持安全关闭(检查修改)和强制关闭(忽略修改)两种模式 -- 完善的异常处理和错误反馈机制 -- 返回详细的关闭结果信息(模型名称、修改状态、关闭时间) - -**API请求格式:** -```json -{ - "software_type": "creo", - "force_close": false -} -``` - -**API响应格式:** -```json -{ - "success": true, - "data": { - "model_name": "assembly.asm", - "was_modified": false, - "close_time": "2024-01-15T10:30:00Z" - }, - "error": null -} -``` - -**已解决的技术问题:** -1. **OTK API正确性** - 使用正确的 `GetIsModified()` 方法检查模型修改状态 -2. **安全关闭逻辑** - 实现修改状态检查,防止意外的数据丢失 -3. **强制关闭选项** - 提供 `force_close` 参数允许忽略未保存修改 -4. **详细错误信息** - 当模型有未保存修改时提供清晰的错误提示 -5. **状态反馈完整性** - 返回关闭操作的完整状态信息 - -**关键技术突破:** -- 实现了安全的模型关闭机制,平衡了用户便利性和数据安全性 -- 提供了灵活的关闭选项,适应不同的使用场景 -- 建立了标准的模型生命周期管理API模式 - -#### 模块8: 打开模型功能 (完成) -**功能:** 根据文件路径打开Creo模型,支持装配体、零件和工程图 -**文件:** CreoManager.h, CreoManager.cpp, MFCCreoDll.cpp -**测试状态:** ✅ 开发完成,待编译测试 -**API端点:** -- `POST /api/model/open` - 模型打开接口 - -**技术细节:** -- 使用OTK `pfcSession::RetrieveModel()` API打开模型文件 -- 优先从内存检索已加载模型,避免重复加载 -- 支持主动激活(active)和静默打开(silent)两种模式 -- 自动识别模型类型(装配体、零件、工程图) -- 对装配体自动统计组件数量 -- 完善的文件路径验证和错误处理 - -**API请求格式:** -```json -{ - "software_type": "creo", - "file_path": "D:\\models\\assembly.asm", - "open_mode": "active" -} -``` - -**API响应格式:** -```json -{ - "success": true, - "data": { - "model_name": "assembly.asm", - "model_type": "assembly", - "file_path": "D:\\models\\assembly.asm", - "file_size": "15.2MB", - "open_time": "2024-01-15T10:30:00Z", - "is_assembly": true, - "total_parts": 25 - }, - "error": null -} -``` - -**已解决的技术问题:** -1. **智能模型检索** - 优先从内存检索,避免重复加载导致的性能问题 -2. **文件路径转换** - 正确处理Windows文件路径到xstring的转换 -3. **模型类型识别** - 基于OTK API准确识别装配体、零件、工程图类型 -4. **装配体统计** - 使用ListFeaturesByType安全统计装配体组件数量 -5. **异常分类处理** - 区分文件不存在、权限问题、OTK错误等不同异常类型 - -**关键技术突破:** -- 实现了完整的模型打开流程,支持所有Creo模型格式 -- 建立了内存优先的智能加载策略 -- 完善了模型生命周期管理的开放环节 - -**API请求格式:** -```json -{ - "software_type": "creo", - "project_name": "Shell Analysis", - "preserve_external_surfaces": true, - "analysis_mode": "aggressive" -} -``` - -**API响应格式:** -```json -{ - "success": true, - "message": "Shell analysis completed", - "data": { - "safeDeletions": [ - { - "id": 123, - "name": "EXTRUDE_1", - "type": "EXTRUDE", - "reason": "Internal feature, safe for removal", - "confidence": 0.85, - "volumeReduction": 15, - "partFile": "part1.prt", - "partPath": "assembly.asm/part1.prt", - "componentType": "FEATURE" - } - ], - "suggestedDeletions": [], - "preserveList": [], - "analysisParameters": { - "totalFeatures": 45, - "deletableFeatures": 12, - "preservedFeatures": 33, - "shellSurfaces": 8, - "internalSurfaces": 37 - } - } -} -``` - -**已解决的技术问题:** -1. **假数据问题** - 移除所有模拟包络盒数据,使用真实OTK API计算 -2. **几何分析** - 实现基于pfcOutline3D的真实边界识别算法 -3. **特征名称格式** - 生成"EXTRUDE_1"等标准格式名称 -4. **文件路径显示** - 正确显示partFile和partPath信息,包含文件扩展名 -5. **置信度算法** - 实现基于几何位置和特征类型的标准置信度计算 -6. **编译错误** - 修复中文字符串导致的编译问题 - -**关键技术突破:** -- 发现并实现了基于OTK几何API的真实薄壳化算法 -- 解决了特征边界判定的核心技术难题 -- 实现了无假数据、无猜测的纯几何分析方法 - -#### 模块9: 无锁线程安全方案 (完成) -**功能:** 解决HTTP线程与主线程的跨线程通信安全问题 -**文件:** MFCCreoDll.cpp, CreoManager.cpp -**测试状态:** ✅ 完全解决,系统稳定运行 -**关键问题:** 模型打开接口超时、线程崩溃、窗口激活失败 - -**技术细节:** -- **根本问题发现**:HTTP服务器运行在独立Windows线程中,无法使用C++标准库mutex -- **核心解决方案**:实现完全无锁的跨线程通信机制 -- **消息队列设计**:使用`MessageItem`结构统一处理所有HTTP请求类型 -- **原子操作**:基于`std::atomic`和`volatile`指针实现线程同步 -- **窗口激活集成**:将窗口激活直接集成到主线程OpenModel流程中 - -**无锁架构设计:** -```cpp -// 消息结构 -struct MessageItem { - std::string type; // "SHOW_MESSAGE" 或 "OPEN_MODEL_REQUEST" - std::string data; // 消息内容或文件路径 - std::string extra; // 额外参数(如open_mode) - volatile bool completed; // 完成标志 - void* result_ptr; // 结果指针 -}; - -// 全局状态(无锁) -static volatile MessageItem* pending_message_item = nullptr; -static std::atomic has_pending_item{false}; -``` - -**线程通信流程:** -``` -HTTP线程 -> 设置MessageItem -> 原子标志 -> 等待完成 - ↓ -主线程(TimerProc) -> 检测标志 -> 执行OTK操作 -> 设置完成标志 - ↓ - 窗口激活(如果需要) -> 返回结果 -``` - -**已解决的技术问题:** -1. **HTTP线程mutex崩溃** - 完全移除C++标准库同步原语 -2. **跨线程OTK调用** - 所有OTK操作都在主线程执行 -3. **嵌套消息冲突** - 窗口激活直接集成到OpenModel流程 -4. **内存管理** - 动态分配结果对象,HTTP线程负责清理 -5. **超时处理** - 保持10秒超时机制,但现在能正常完成 - -**性能优化:** -- **零锁开销**:完全避免锁竞争和上下文切换 -- **高效轮询**:50ms间隔轮询,平衡响应速度和CPU占用 -- **内存最小化**:栈上分配消息项,堆上仅分配结果对象 - -**API兼容性:** -- ✅ **所有现有API保持100%兼容** -- ✅ **窗口激活功能正常工作** -- ✅ **错误处理机制完整保留** -- ✅ **调试信息完整记录执行过程** - -**关键技术突破:** -- 发现并解决了MFC DLL环境下HTTP线程无法使用C++标准库mutex的根本问题 -- 实现了完全无锁的跨线程通信方案,保持高性能和线程安全 -- 成功集成窗口激活功能,实现了完整的模型打开体验 -- 建立了可扩展的消息机制,为未来功能提供稳定基础 - -### 🔄 待实现模块(按优先级排序) - -#### 模块8: WebSocket服务 (高优先级) -**目标:** 实现双向实时通信,支持长时间操作 -**预计文件:** WebSocketServer.h, WebSocketServer.cpp -**主要功能:** -- WebSocket服务器(端口12346) -- 实时日志推送 -- 长操作进度反馈 -- 客户端连接管理 - -#### 模块9: Creo长操作接口 (中优先级) -**目标:** 实现模型加载、特征删除、多格式导出等操作 -**预计文件:** ModelAnalyzer.h, ModelAnalyzer.cpp -**主要功能:** -- 模型文件加载操作 -- 特征分析和删除 -- 多格式导出(STL、IGES等) -- 操作进度监控 - -#### 模块10: 日志系统 (低优先级) -**目标:** 统一的日志记录和错误追踪 -**预计文件:** Logger.h, Logger.cpp -**主要功能:** -- 结构化日志输出 -- 多级别日志支持 -- 文件和控制台输出 -- 调试信息记录 - -#### 模块11: 用户认证 (最低优先级) -**目标:** 简单的用户识别和访问控制 -**预计文件:** AuthManager.h, AuthManager.cpp -**主要功能:** -- 简单token认证 -- 会话管理 -- 权限控制 - -### 技术架构总结 - -**当前架构状态:** -``` -Web前端 -> HTTP API (12345端口) -> 无锁MessageItem -> TimerProc主线程 -> OTK -> Creo - ↓ (已实现完整功能) ↓ - 状态查询接口 -> CreoManager -> 实时状态反馈 - 导出接口 -> ExportModelToSTEP -> STEP文件导出 - 层级分析接口 -> HierarchyAnalysis -> 装配体结构分析 - 层级删除接口 -> HierarchyDelete -> 组件安全删除 - 薄壳化分析接口 -> ShellAnalysis -> 几何边界特征识别 - 关闭模型接口 -> CloseModel -> 安全模型关闭 - 打开模型接口 -> OpenModel + 窗口激活 -> 完整模型加载体验 -``` - -**目标架构:** -``` -Web前端 -> HTTP API (快速查询) -> CreoManager -> Creo - -> WebSocket (长操作) -> WebSocketServer -> ModelAnalyzer -> Creo - ↓ - 实时日志推送 -``` - -### 开发原则 - -1. **模块化开发** - 每个模块独立测试通过后再进行下一个 -2. **最小化修改** - 保持现有代码稳定,只添加新功能 -3. **向后兼容** - 新API不影响已有接口 -4. **错误处理** - 每个模块都有完善的异常处理 -5. **简化实现** - 优先实现MVP功能,避免过度工程化 - -### 已解决的技术问题 - -1. **OTK API兼容性** - 使用pfcSession而非ProToolkit -2. **跨线程通信** - 保持定时器机制处理主线程OTK调用 -3. **内存管理** - 使用智能指针和RAII模式,避免内存泄漏 -4. **编码问题** - UTF-8编码保存文件解决中文注释警告 -5. **错误处理** - 分层异常处理确保系统稳定性 -6. **STEP导出崩溃** - 修复了废弃API和正则表达式导致的崩溃问题 -7. **JSON解析问题** - 实现了安全的JSON解析机制 -8. **线程安全问题** - 使用mutex和atomic保证多线程安全 -9. **API参数验证** - 完善了输入参数的验证机制 -10. **DeleteFeatures崩溃问题** - 使用SuppressFeatures替代DeleteFeatures避免装配体结构破坏 -11. **层级删除安全性** - 实现了任意层级的安全组件删除,包括根层级组件 -12. **特征引用完整性** - 采用抑制策略保持特征引用关系,避免外部依赖丢失 -13. **薄壳化分析算法** - 实现了基于真实几何边界的薄壳化特征识别 -14. **假数据彻底清除** - 移除所有模拟计算,实现纯OTK API的几何分析 -15. **特征置信度算法** - 实现了标准的特征删除安全性评估机制 -16. **模型关闭API设计** - 实现了安全的模型关闭机制,平衡用户便利性和数据安全性 -17. **OTK模型状态检查** - 使用正确的`GetIsModified()`API检查模型修改状态 -18. **强制关闭选项** - 提供灵活的关闭选项,适应不同使用场景 -19. **模型生命周期管理** - 建立了标准的模型生命周期管理API模式 -20. **HTTP线程mutex崩溃** - 发现并解决MFC DLL环境下HTTP线程无法使用C++标准库mutex的根本问题 -21. **无锁跨线程通信** - 实现完全无锁的MessageItem机制,彻底解决线程安全问题 -22. **嵌套消息冲突** - 将窗口激活直接集成到OpenModel流程,避免嵌套消息导致的死锁 -23. **模型打开超时** - 解决10秒超时问题,实现稳定的模型打开和窗口激活功能 - -### 下一步计划 - -建议继续实现模块8(WebSocket服务),为后续长操作和实时通信奠定基础。核心分析功能(层级分析、层级删除、薄壳化分析、模型关闭)已经完成,现在可以专注于实时通信和用户体验优化。 - -## 测试记录 - -### 测试原则与特点 -- **所有的测试由我进行,因为我在IDE,visual studio中开发的** -- **不能用任何假数据、模拟数据** -- **不能限制层级和数量** - -## 构建和编译 - -使用 Visual Studio 2022 (v143 工具集) 构建: - -```bash -# 使用 Visual Studio 构建 -msbuild MFCCreoDll.sln /p:Configuration=Debug /p:Platform=x64 -``` - -或在 Visual Studio IDE 中: -- 打开 `MFCCreoDll.sln` -- 选择 Debug|x64 配置 -- 构建解决方案 (Ctrl+Shift+B) - -## 依赖环境 - -项目依赖 Creo 5.0.0.0 安装: -- OTK C++ 库路径: `C:\Program Files\PTC\Creo 5.0.0.0\Common Files\otk\otk_cpp\` -- ProToolkit 库路径: `C:\Program Files\PTC\Creo 5.0.0.0\Common Files\protoolkit\` - -## 关键文件说明 - -- `MFCCreoDll.cpp`: 主要逻辑实现,包含服务器和 OTK 交互代码 -- `MFCCreoDll.h`: MFC 应用程序类声明 -- `MFCCreoDll.def`: DLL 导出定义文件 -- `MFCCreoDll.vcxproj`: Visual Studio 项目配置文件 - -## 重要架构注意事项 - -- 项目使用动态 MFC 链接 (`UseOfMfc=Dynamic`) -- 需要 Unicode 字符集支持 -- 使用 Windows 原生线程和定时器机制 -- 插件生命周期由 `user_initialize()` 和 `user_terminate()` 函数管理 -- OTK API 必须在主线程中调用,使用定时器机制处理跨线程通信 - -## 调试 - -编译后的 DLL 位于 `x64/Debug/MFCCreoDll.dll`,需要注册到 Creo 中作为插件使用。 - -这个项目是在visual studio 2022中进行编译生成,所以不要用其它的编译环境,调试报错也是从2022中进行。项目开发于win11环境,运行于win7环境,这个要特别关注。otk文档在文件夹otk_cpp_doc下,请自己查找相关api \ No newline at end of file diff --git a/Models/ExportIfcRequest.cs b/Models/ExportIfcRequest.cs index f7a5f9b..9accb2b 100644 --- a/Models/ExportIfcRequest.cs +++ b/Models/ExportIfcRequest.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Linq; namespace TellmePdmsPluging.Models { @@ -9,6 +11,10 @@ namespace TellmePdmsPluging.Models public string ExportPath { get; set; } public string FileName { get; set; } + public string ExportSystem { get; set; } + public bool? Overwrite { get; set; } + public List Selections { get; set; } + public List SelectionCommands { get; set; } public void ApplyDefaults() { @@ -19,7 +25,12 @@ namespace TellmePdmsPluging.Models if (string.IsNullOrEmpty(FileName)) { - FileName = string.Format("pdms_export_{0:yyyyMMdd_HHmmss}.ifc", DateTime.Now); + FileName = string.Format("pdms_export_{0:yyyyMMdd_HHmmss}.rvm", DateTime.Now); + } + + if (!Overwrite.HasValue) + { + Overwrite = true; } } @@ -27,6 +38,45 @@ namespace TellmePdmsPluging.Models { return System.IO.Path.Combine(ExportPath, FileName); } + + public List GetEffectiveSelections() + { + var raw = new List(); + if (Selections != null && Selections.Count > 0) + { + raw.AddRange(Selections); + } + if (SelectionCommands != null && SelectionCommands.Count > 0) + { + raw.AddRange(SelectionCommands); + } + + if (raw.Count == 0) + { + // 采用文档示例中的常见选择语句作为默认项 + raw.Add("/EQUIP"); + raw.Add("/PIPES"); + } + + var normalized = new List(); + foreach (var item in raw) + { + var text = (item ?? string.Empty).Trim(); + if (string.IsNullOrEmpty(text)) + { + continue; + } + + if (!text.StartsWith("EXPORT ", StringComparison.OrdinalIgnoreCase)) + { + text = "EXPORT " + text; + } + + normalized.Add(text); + } + + return normalized.Distinct(StringComparer.OrdinalIgnoreCase).ToList(); + } } public class ExportIfcResult diff --git a/Models/ShrinkwrapModelRequest.cs b/Models/ShrinkwrapModelRequest.cs index 6bee819..19c3039 100644 --- a/Models/ShrinkwrapModelRequest.cs +++ b/Models/ShrinkwrapModelRequest.cs @@ -7,7 +7,9 @@ namespace TellmePdmsPluging.Models public string ExecutionId { get; set; } public string execution_id { get { return ExecutionId; } set { ExecutionId = value; } } - public bool DryRun { get; set; } = true; + public bool DryRun { get; set; } = false; + + public bool SkipLinkedElements { get; set; } = false; public double Padding { get; set; } = 500.0; @@ -90,12 +92,15 @@ namespace TellmePdmsPluging.Models { public bool Success { get; set; } public bool DryRun { get; set; } + public bool SkipLinkedElements { get; set; } public double Padding { get; set; } public int TotalVisited { get; set; } public int RemovedCount { get; set; } public int KeptCount { get; set; } public int ShellKeptCount { get; set; } + public int SkippedLinkedCount { get; set; } public List RemovedElements { get; set; } = new List(); + public List SkippedLinkedElements { get; set; } = new List(); public List Errors { get; set; } = new List(); public List ZoneSummaries { get; set; } = new List(); public System.DateTime StartedAt { get; set; } diff --git a/Models/SimplifyModelRequest.cs b/Models/SimplifyModelRequest.cs index 61e00a5..0a1c084 100644 --- a/Models/SimplifyModelRequest.cs +++ b/Models/SimplifyModelRequest.cs @@ -8,7 +8,9 @@ namespace TellmePdmsPluging.Models public string ExecutionId { get; set; } public string execution_id { get { return ExecutionId; } set { ExecutionId = value; } } - public bool DryRun { get; set; } = true; + public bool DryRun { get; set; } = false; + + public bool SkipLinkedElements { get; set; } = false; public List ZoneFilters { @@ -111,10 +113,13 @@ namespace TellmePdmsPluging.Models { public bool Success { get; set; } public bool DryRun { get; set; } + public bool SkipLinkedElements { get; set; } public int TotalVisited { get; set; } public int RemovedCount { get; set; } public int KeptCount { get; set; } + public int SkippedLinkedCount { get; set; } public List RemovedElements { get; set; } = new List(); + public List SkippedLinkedElements { get; set; } = new List(); public List Errors { get; set; } = new List(); public DateTime StartedAt { get; set; } public DateTime CompletedAt { get; set; } diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.BeforeFinishEventHandler.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.BeforeFinishEventHandler.html new file mode 100644 index 0000000..6fed8fd --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.BeforeFinishEventHandler.html @@ -0,0 +1,47 @@ + + + + + BeforeFinishEventHandler Delegate + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

BeforeFinishEventHandler Delegate

+
+
+
+

BeforeFinish event handler delegate.

+
+
publicdelegatevoidBeforeFinishEventHandler(
objectsender,
EventArgse
);
+
+

Requirements

+

+ Namespace: + Aveva.Pdms +

+

+ Assembly: Aveva.Pdms (in Aveva.Pdms.dll) +

+

See Also

+

+ Aveva.Pdms Namespace +

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.FinishEventHandler.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.FinishEventHandler.html new file mode 100644 index 0000000..4393038 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.FinishEventHandler.html @@ -0,0 +1,47 @@ + + + + + FinishEventHandler Delegate + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

FinishEventHandler Delegate

+
+
+
+

Finish event handler delegate.

+
+
publicdelegatevoidFinishEventHandler(
objectsender,
EventArgse
);
+
+

Requirements

+

+ Namespace: + Aveva.Pdms +

+

+ Assembly: Aveva.Pdms (in Aveva.Pdms.dll) +

+

See Also

+

+ Aveva.Pdms Namespace +

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.ModuleType.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.ModuleType.html new file mode 100644 index 0000000..20f55a7 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.ModuleType.html @@ -0,0 +1,100 @@ + + + + + ModuleType Enumeration + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

ModuleType Enumeration

+
+
+
+

PDMS Module type code.

+
+
publicenumModuleType
+
+

Members

+
+ + + + + + + + + + + + + + + + + + + +
Member NameDescription
Dabacon + +
Gml + +
Gtx + +
CommandProcessor + +
Flayer + +
Splash + +
FlexLM + +
PML + +
NxLib + +
FormsLib + +
InteractionManager + +
Druid + +
Sgl + +
AttributeDataFile + +
Global + +
+
+

Requirements

+

+ Namespace: + Aveva.Pdms +

+

+ Assembly: Aveva.Pdms (in Aveva.Pdms.dll) +

+

See Also

+

+ Aveva.Pdms Namespace +

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.BeforeFinished.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.BeforeFinished.html new file mode 100644 index 0000000..f11e52a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.BeforeFinished.html @@ -0,0 +1,39 @@ + + + + + PdmsApplication.BeforeFinished Event + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.BeforeFinished Event +

+
+
+
+

BeforeFinished event handler.

+
publicstaticeventBeforeFinishEventHandlerBeforeFinished;
+

+

+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Exit.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Exit.html new file mode 100644 index 0000000..0a3bd5a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Exit.html @@ -0,0 +1,43 @@ + + + + + PdmsApplication.Exit Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.ExitMethod

+
+
+
+

Exit application.

+
publicstaticvoidExit(
stringmessage
);
+

Parameters

+
+
+ message +
+
Exit message
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finish.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finish.html new file mode 100644 index 0000000..2cffd1a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finish.html @@ -0,0 +1,36 @@ + + + + + PdmsApplication.Finish Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.FinishMethod

+
+
+
+

Finish application.

+
publicstaticvoidFinish();
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finished.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finished.html new file mode 100644 index 0000000..7bfb4f8 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Finished.html @@ -0,0 +1,39 @@ + + + + + PdmsApplication.Finished Event + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.Finished Event +

+
+
+
+

Finished event handler.

+
publicstaticeventFinishEventHandlerFinished;
+

+

+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.GraphicsMode.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.GraphicsMode.html new file mode 100644 index 0000000..2ebbffe --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.GraphicsMode.html @@ -0,0 +1,38 @@ + + + + + GraphicsMode Property + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.GraphicsMode Property

+
+
+
+

True if application is in graphics mode

+
publicstaticboolGraphicsMode{get;}
+

+

+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseBeforeFinished.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseBeforeFinished.html new file mode 100644 index 0000000..42deda3 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseBeforeFinished.html @@ -0,0 +1,47 @@ + + + + + PdmsApplication.RaiseBeforeFinished Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.RaiseBeforeFinishedMethod

+
+
+
+

Raise Finished event.

+
publicstaticvoidRaiseBeforeFinished(
objectsender,
EventArgse
);
+

Parameters

+
+
+ sender +
+
Object raising the event
+
+ e +
+
Event arguments
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseFinished.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseFinished.html new file mode 100644 index 0000000..64a2641 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.RaiseFinished.html @@ -0,0 +1,47 @@ + + + + + PdmsApplication.RaiseFinished Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.RaiseFinishedMethod

+
+
+
+

Raise Finished event.

+
publicstaticvoidRaiseFinished(
objectsender,
EventArgse
);
+

Parameters

+
+
+ sender +
+
Object raising the event
+
+ e +
+
Event arguments
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Start.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Start.html new file mode 100644 index 0000000..5940765 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.Start.html @@ -0,0 +1,60 @@ + + + + + PdmsApplication.Start Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.StartMethod

+
+
+
+

Start the application.

+
publicstaticvoidStart(
boolisGraphics,
intmoduleNumber,
boolisBatch,
boolisNoConsole,
stringlogfile
);
+

Parameters

+
+
+ isGraphics +
+
True if to be started in graphics mode
+
+ moduleNumber +
+
+
+
+ isBatch +
+
True is to be started in batch mode
+
+ isNoConsole +
+
True if to be started with no console
+
+ logfile +
+
Filename for application log file
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartEventLoop.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartEventLoop.html new file mode 100644 index 0000000..cd5986b --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartEventLoop.html @@ -0,0 +1,36 @@ + + + + + PdmsApplication.StartEventLoop Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.StartEventLoopMethod

+
+
+
+

Start application activity.

+
publicstaticvoidStartEventLoop();
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartViaCommsFile.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartViaCommsFile.html new file mode 100644 index 0000000..d1f01b8 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.StartViaCommsFile.html @@ -0,0 +1,51 @@ + + + + + PdmsApplication.StartViaCommsFile Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.StartViaCommsFileMethod

+
+
+
+

Start the application using a Comms file.

+
publicstaticvoidStartViaCommsFile(
boolisGraphics,
intmoduleNumber,
stringmoduleName
);
+

Parameters

+
+
+ isGraphics +
+
True if to be started in graphics mode
+
+ moduleNumber +
+
Module number
+
+ moduleName +
+
Module name
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseError.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseError.html new file mode 100644 index 0000000..f6ba103 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseError.html @@ -0,0 +1,39 @@ + + + + + PdmsApplication.getLicenseError Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.getLicenseErrorMethod

+
+
+
+

get license error

+
publicstaticstringgetLicenseError();
+

Parameters

+
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseFeature.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseFeature.html new file mode 100644 index 0000000..5346ec8 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.getLicenseFeature.html @@ -0,0 +1,44 @@ + + + + + PdmsApplication.getLicenseFeature Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.getLicenseFeatureMethod

+
+
+
+

get license feature

+
publicstaticboolgetLicenseFeature(
stringfeature
);
+

Parameters

+
+
+ feature +
+
+
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.hasLicenseFeature.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.hasLicenseFeature.html new file mode 100644 index 0000000..3456d14 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.hasLicenseFeature.html @@ -0,0 +1,44 @@ + + + + + PdmsApplication.hasLicenseFeature Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication.hasLicenseFeatureMethod

+
+
+
+

has license feature

+
publicstaticboolhasLicenseFeature(
stringfeature
);
+

Parameters

+
+
+ feature +
+
+
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.html new file mode 100644 index 0000000..0264db6 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplication.html @@ -0,0 +1,54 @@ + + + + + PdmsApplication Class + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication Class

+
+
+
+

Entry point for PDMS application framework

+

For a list of all members of this type, see PdmsApplication Members.

+

+ System.Object +
Aveva.Pdms.PdmsApplication

+
+
publicabstractclassPdmsApplication
+
+

Thread Safety

+

Public static (Shared in Visual Basic) members of this type are + safe for multithreaded operations. Instance members are not guaranteed to be + thread-safe.

+

Requirements

+

+ Namespace: + Aveva.Pdms +

+

+ Assembly: Aveva.Pdms (in Aveva.Pdms.dll) +

+

See Also

+

+ PdmsApplication Members | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationEvents.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationEvents.html new file mode 100644 index 0000000..5bc609a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationEvents.html @@ -0,0 +1,41 @@ + + + + + PdmsApplication Events + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplicationEvents

+
+
+
+

The events of the PdmsApplication class are listed below. For a complete list of PdmsApplication class members, see the PdmsApplication Members topic.

+

Public Static Events

+
+ + +
BeforeFinished BeforeFinished event handler.
Finished Finished event handler.
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMembers.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMembers.html new file mode 100644 index 0000000..6037418 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMembers.html @@ -0,0 +1,79 @@ + + + + + PdmsApplication Members + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplication Members +

+
+
+
+

+ PdmsApplication overview +

+

Public Static Properties

+
+ +
GraphicsMode True if application is in graphics mode
+
+

Public Static Methods

+
+ + + + + + + + + + +
Exit Exit application.
Finish Finish application.
getLicenseError get license error
getLicenseFeature get license feature
hasLicenseFeature has license feature
RaiseBeforeFinished Raise Finished event.
RaiseFinished Raise Finished event.
Start Start the application.
StartEventLoop Start application activity.
StartViaCommsFile Start the application using a Comms file.
+
+

Public Static Events

+
+ + +
BeforeFinished BeforeFinished event handler.
Finished Finished event handler.
+
+

Public Instance Methods

+
+ + + + +
Equals (inherited from Object) + Determines whether the specified Object is equal to the current Object. +
GetHashCode (inherited from Object) + Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. +
GetType (inherited from Object) + Gets the Type of the current instance. +
ToString (inherited from Object) + Returns a String that represents the current Object. +
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMethods.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMethods.html new file mode 100644 index 0000000..dded68b --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationMethods.html @@ -0,0 +1,65 @@ + + + + + PdmsApplication Methods + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplicationMethods

+
+
+
+

The methods of the PdmsApplication class are listed below. For a complete list of PdmsApplication class members, see the PdmsApplication Members topic.

+

Public Static Methods

+
+ + + + + + + + + + +
Exit Exit application.
Finish Finish application.
getLicenseError get license error
getLicenseFeature get license feature
hasLicenseFeature has license feature
RaiseBeforeFinished Raise Finished event.
RaiseFinished Raise Finished event.
Start Start the application.
StartEventLoop Start application activity.
StartViaCommsFile Start the application using a Comms file.
+
+

Public Instance Methods

+
+ + + + +
Equals (inherited from Object) + Determines whether the specified Object is equal to the current Object. +
GetHashCode (inherited from Object) + Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. +
GetType (inherited from Object) + Gets the Type of the current instance. +
ToString (inherited from Object) + Returns a String that represents the current Object. +
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationProperties.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationProperties.html new file mode 100644 index 0000000..2f5d426 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsApplicationProperties.html @@ -0,0 +1,40 @@ + + + + + PdmsApplication Properties + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsApplicationProperties

+
+
+
+

The properties of the PdmsApplication class are listed below. For a complete list of PdmsApplication class members, see the PdmsApplication Members topic.

+

Public Static Properties

+
+ +
GraphicsMode True if application is in graphics mode
+
+

See Also

+

+ PdmsApplication Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCompanyName.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCompanyName.html new file mode 100644 index 0000000..aa7d480 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCompanyName.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetCompanyName Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetCompanyNameMethod

+
+
+
+

Retrieve company name PDMS is licensed to

+
publicstaticstringGetCompanyName();
+

Return Value

+

Licenced company name

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCopyright.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCopyright.html new file mode 100644 index 0000000..008b41a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetCopyright.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetCopyright Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetCopyrightMethod

+
+
+
+

Retrieve copyright information for PDMS

+
publicstaticstringGetCopyright();
+

Return Value

+

Copyright text

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleName.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleName.html new file mode 100644 index 0000000..7ac5366 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleName.html @@ -0,0 +1,45 @@ + + + + + PdmsVersion.GetModuleName Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetModuleNameMethod

+
+
+
+

Retrieve the name of a PDMS module

+
publicstaticstringGetModuleName(
ModuleTypemodule
);
+

Parameters

+
+
+ module +
+
Module identifier
+
+

Return Value

+

Module name

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleVersion.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleVersion.html new file mode 100644 index 0000000..3e5fa77 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetModuleVersion.html @@ -0,0 +1,45 @@ + + + + + PdmsVersion.GetModuleVersion Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetModuleVersionMethod

+
+
+
+

Retrieve the version of a PDMS module

+
publicstaticstringGetModuleVersion(
ModuleTypemodule
);
+

Parameters

+
+
+ module +
+
Module identifier
+
+

Return Value

+

Module version identifier

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetName.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetName.html new file mode 100644 index 0000000..dbf3224 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetName.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetName Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetNameMethod

+
+
+
+

Retrieve name of current module (i.e. VANTAGE PDMS Design)

+
publicstaticstringGetName();
+

Return Value

+

Current module name

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetShortBanner.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetShortBanner.html new file mode 100644 index 0000000..0552c04 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetShortBanner.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetShortBanner Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetShortBannerMethod

+
+
+
+

Retrieve short banner string (i.e. qbann)

+
publicstaticstringGetShortBanner();
+

Return Value

+

Banner text

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetStatus.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetStatus.html new file mode 100644 index 0000000..b67b007 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetStatus.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetStatus Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetStatusMethod

+
+
+
+

Retrieve any special status instructions (i.e. for pre-release builds)

+
publicstaticstringGetStatus();
+

Return Value

+

Status text

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetVersion.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetVersion.html new file mode 100644 index 0000000..b11f590 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.GetVersion.html @@ -0,0 +1,38 @@ + + + + + PdmsVersion.GetVersion Method + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion.GetVersionMethod

+
+
+
+

Retrieve version number of PDMS excluding Mk (i.e. 11.6)

+
publicstaticstringGetVersion();
+

Return Value

+

PDMS version identifier

+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.html new file mode 100644 index 0000000..7c3dc07 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersion.html @@ -0,0 +1,54 @@ + + + + + PdmsVersion Class + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion Class

+
+
+
+

PDMS Version query.

+

For a list of all members of this type, see PdmsVersion Members.

+

+ System.Object +
Aveva.Pdms.PdmsVersion

+
+
publicabstractclassPdmsVersion
+
+

Thread Safety

+

Public static (Shared in Visual Basic) members of this type are + safe for multithreaded operations. Instance members are not guaranteed to be + thread-safe.

+

Requirements

+

+ Namespace: + Aveva.Pdms +

+

+ Assembly: Aveva.Pdms (in Aveva.Pdms.dll) +

+

See Also

+

+ PdmsVersion Members | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMembers.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMembers.html new file mode 100644 index 0000000..2feb582 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMembers.html @@ -0,0 +1,66 @@ + + + + + PdmsVersion Members + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersion Members +

+
+
+
+

+ PdmsVersion overview +

+

Public Static Methods

+
+ + + + + + + + +
GetCompanyName Retrieve company name PDMS is licensed to
GetCopyright Retrieve copyright information for PDMS
GetModuleName Retrieve the name of a PDMS module
GetModuleVersion Retrieve the version of a PDMS module
GetName Retrieve name of current module (i.e. VANTAGE PDMS Design)
GetShortBanner Retrieve short banner string (i.e. qbann)
GetStatus Retrieve any special status instructions (i.e. for pre-release builds)
GetVersion Retrieve version number of PDMS excluding Mk (i.e. 11.6)
+
+

Public Instance Methods

+
+ + + + +
Equals (inherited from Object) + Determines whether the specified Object is equal to the current Object. +
GetHashCode (inherited from Object) + Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. +
GetType (inherited from Object) + Gets the Type of the current instance. +
ToString (inherited from Object) + Returns a String that represents the current Object. +
+
+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMethods.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMethods.html new file mode 100644 index 0000000..e99d5a7 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.PdmsVersionMethods.html @@ -0,0 +1,63 @@ + + + + + PdmsVersion Methods + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

PdmsVersionMethods

+
+
+
+

The methods of the PdmsVersion class are listed below. For a complete list of PdmsVersion class members, see the PdmsVersion Members topic.

+

Public Static Methods

+
+ + + + + + + + +
GetCompanyName Retrieve company name PDMS is licensed to
GetCopyright Retrieve copyright information for PDMS
GetModuleName Retrieve the name of a PDMS module
GetModuleVersion Retrieve the version of a PDMS module
GetName Retrieve name of current module (i.e. VANTAGE PDMS Design)
GetShortBanner Retrieve short banner string (i.e. qbann)
GetStatus Retrieve any special status instructions (i.e. for pre-release builds)
GetVersion Retrieve version number of PDMS excluding Mk (i.e. 11.6)
+
+

Public Instance Methods

+
+ + + + +
Equals (inherited from Object) + Determines whether the specified Object is equal to the current Object. +
GetHashCode (inherited from Object) + Serves as a hash function for a particular type, suitable for use in hashing algorithms and data structures like a hash table. +
GetType (inherited from Object) + Gets the Type of the current instance. +
ToString (inherited from Object) + Returns a String that represents the current Object. +
+
+

See Also

+

+ PdmsVersion Class | Aveva.Pdms Namespace

+ +
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhc b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhc new file mode 100644 index 0000000..26f8c3c --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhc @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhk b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhk new file mode 100644 index 0000000..fc02d78 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.hhk @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.html new file mode 100644 index 0000000..9f7d4a3 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.Pdms.html @@ -0,0 +1,90 @@ + + + + + Aveva.Pdms + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

Aveva.Pdms Namespace

+
+
+
+

+ Namespace hierarchy +

+

Classes

+
+ + + + + + + + + + + + + +
ClassDescription
+ PdmsApplication + Entry point for PDMS application framework
+ PdmsVersion + PDMS Version query.
+
+

Delegates

+
+ + + + + + + + + + + + + +
DelegateDescription
+ BeforeFinishEventHandler + BeforeFinish event handler delegate.
+ FinishEventHandler + Finish event handler delegate.
+
+

Enumerations

+
+ + + + + + + + + +
EnumerationDescription
+ ModuleType + PDMS Module type code.
+
+
+ +
+ + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.PdmsHierarchy.html b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.PdmsHierarchy.html new file mode 100644 index 0000000..8b9a952 --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/Aveva.PdmsHierarchy.html @@ -0,0 +1,53 @@ + + + + + Aveva.PdmsHierarchy + + + + + + +
+
+ + + + + +
AVEVA Application .NET Public Interface +
+
+
+

Aveva.Pdms Hierarchy

+
+
+ + + \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/MSDN.css b/NetInterfaceReferenceFiles/Aveva.Pdms/MSDN.css new file mode 100644 index 0000000..11a962a --- /dev/null +++ b/NetInterfaceReferenceFiles/Aveva.Pdms/MSDN.css @@ -0,0 +1,405 @@ +body /* This body tag requires the use of one of the sets of banner and/or text div ids */ + { + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; + background: #ffffff; + color: #000000; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 70%; + width: 100%; + /*overflow: expression('hidden');*/ + } +div#scrollyes /* Allows topic to scroll with correct margins. Cannot be used with running head banner */ + { /* Must immediately follow . */ + padding: 2px 15px 2px 22px; + width: 100%; + } +div#nsbanner /* Creates Nonscrolling banner region */ + { + position: relative; + left: 0px; + padding: 0px 0px 0px 0px; + border-bottom: 1px solid #999999; + /*width: expression(document.body.clientWidth);*/ + background-color: #99ccff; + } +div#nstext /* Creates the scrolling text area for Nonscrolling region topic */ + { + top: 0px; + padding: 5px 20px 0px 22px; + /*overflow: expression('auto'); + width: expression(document.body.clientWidth); + height: expression(document.body.clientHeight - nsbanner.offsetHeight);*/ + } +div#scrbanner /* Creates the running head bar in a full-scroll topic */ + { /* Allows topic to scroll. */ + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 0px; + border-bottom: 1px solid #999999; + } +div#scrtext /* Creates the text area in a full-scroll topic */ + { /* Allows topic to scroll. */ + padding: 0px 10px 0px 22px; + } +div#bannerrow1 /* provides full-width color to top row in running head (requires script) */ + { + } +div#titlerow /* provides non-scroll topic title area (requires script) */ + { + padding: 0px 10px 0px 22px; + } + +h1, h2, h3, h4 + { + font-family: Verdana, Arial, Helvetica, sans-serif; + margin-bottom: .4em; + margin-top: 1em; + font-weight: bold; + } +h1 + { + font-size: 120%; + margin-top: 0em; + } +div#scrollyes h1 /* Changes font size for full-scrolling topic */ + { + font-size: 150%; + } +h2 + { + font-size: 130%; + } +h3 + { + font-size: 115%; + } +h4 + { + font-size: 100%; + } +.dtH1, .dtH2, .dtH3, .dtH4 + { + margin-left: -18px; + } +div#titlerow h1 + { + margin-bottom: .2em + } + +table.bannerparthead, table.bannertitle /* General values for the Running Head tables */ + { + position: relative; + left: 0px; + top: 0px; + padding: 0px 0px 0px 0px; + margin: 0px 0px 0px 0px; + width: 100%; + height: 21px; + border-collapse: collapse; + border-style: solid; + border-width: 0px; + background-color: #99ccff; + font-size: 100%; + } +table.bannerparthead td /* General Values for cells in the top row of running head */ + { + margin: 0px 0px 0px 0px; + padding: 2px 0px 0px 4px; + vertical-align: middle; + border-width: 0px; + border-style: solid; + border-color: #999999; + background: transparent; + font-style: italic; + font-weight: normal; + } +table.bannerparthead td.product /* Values for top right cell in running head */ + { /* Allows for a second text block in the running head */ + text-align: right; + padding: 2px 5px 0px 5px; + } +table.bannertitle td /* General Values for cells in the bottom row of running head */ + { + margin: 0px 0px 0px 0px; + padding: 0px 0px 0px 3px; + vertical-align: middle; + border-width: 0px 0px 1px 0px; + border-style: solid; + border-color: #999999; + background: transparent; + font-weight: bold; + } +td.button1 /* Values for button cells */ + { + width: 14px; + cursor: hand; + } + +p + { + margin: .5em 0em .5em 0em; + } +blockquote.dtBlock + { + margin: .5em 1.5em .5em 1.5em; + } +div#dtHoverText + { + color: #000066; + } +.normal + { + margin: .5em 0em .5em 0em; + } +.fineprint + { + font-size: 90%; /* 90% of 70% */ + } +.indent + { + margin: .5em 1.5em .5em 1.5em; + } +.topicstatus /* Topic Status Boilerplate class */ + { + display: block; + color: red; + } +p.label + { + margin-top: 1em; + } +p.labelproc + { + margin-top: 1em; + color: #000066; + } + +div.tablediv + { + width: 100%; /* Forces tables to have correct right margins and top spacing */ + margin-top: -.4em; + } +ol div.tablediv, ul div.tablediv, ol div.HxLinkTable, ul div.HxLinkTable + { + margin-top: 0em; /* Forces tables to have correct right margins and top spacing */ + } +table.dtTABLE + { + width: 100%; /* Forces tables to have correct right margin */ + margin-top: .6em; + margin-bottom: .3em; + border-width: 1px 1px 0px 0px; + border-style: solid; + border-color: #999999; + background-color: #999999; + font-size: 100%; /* Text in Table is same size as text outside table */ + } +table.dtTABLE th, table.dtTABLE td + { + border-style: solid; /* Creates the cell border and color */ + border-width: 0px 0px 1px 1px; + border-style: solid; + border-color: #999999; + padding: 4px 6px; + text-align: left; + } +table.dtTABLE th + { + background: #cccccc; /* Creates the shaded table header row */ + vertical-align: bottom; + } +table.dtTABLE td + { + background: #ffffff; + vertical-align: top; + } + +MSHelp\:ktable + { + disambiguator: span; + separator:  | + prefix: | + postfix:   + filterString: ; + } +div.HxLinkTable + { + width: auto; /* Forces tables to have correct right margins and top spacing */ + margin-top: -.4em; + visibility: visible; + } +ol div.HxLinkTable, ul div.HxLinkTable + { + margin-top: 0em; /* Forces tables to have correct right margins and top spacing */ + } +table.HxLinkTable /* Keep in sync with general table settings below */ + { + width: auto; + margin-top: 1.5em; + margin-bottom: .3em; + margin-left: -1em; + border-width: 1px 1px 0px 0px; + border-style: solid; + border-color: #999999; + background-color: #999999; + font-size: 100%; /* Text in Table is same size as text outside table */ + behavior:url(hxlinktable.htc); /* Attach the behavior to link elements. */ + } +table.HxLinkTable th, table.HxLinkTable td /* Keep in sync with general table settings below */ + { + border-style: solid; /* Creates the cell border and color */ + border-width: 0px 0px 1px 1px; + border-style: solid; + border-color: #999999; + padding: 4px 6px; + text-align: left; + } +table.HxLinkTable th /* Keep in sync with general table settings below */ + { + background: #cccccc; /* Creates the shaded table header row */ + vertical-align: bottom; + } +table.HxLinkTable td /* Keep in sync with general table settings below */ + { + background: #ffffff; + vertical-align: top; + } +pre.code + { + background-color: #eeeeee; + padding: 4px 6px 4px 6px; + } +pre, div.syntax + { + margin-top: .5em; + margin-bottom: .5em; + } +pre, code, .code, div.syntax + { + font: 100% Monospace, Courier New, Courier; /* This is 100% of 70% */ + color: #000066; + } +pre b, code b + { + letter-spacing: .1em; /* opens kerning on bold in Syntax/Code */ + } +pre.syntax, div.syntax + { + background: #cccccc; + padding: 4px 8px; + cursor: text; + margin-top: 1em; + margin-bottom: 1em; + color: #000000; + border-width: 1px; + border-style: solid; + border-color: #999999; +/* ------------------------------------- */ +/* BEGIN changes to dtue.css conventions */ + font-weight: bolder; + letter-spacing: .1em; + } +.syntax span.lang + { + margin: 0; + font-weight: normal; + } +.syntax span.meta + { + margin: 0; + font-weight: normal; + font-style: italic; + } +.syntax a + { + margin: 0; + font-weight: normal; + } +/* END changes to dtue.css conventions */ +/* ----------------------------------- */ + +.syntax div + { + padding-left: 24px; + text-indent: -24px; + } + +.syntax .attribute + { + font-weight: normal; + } +div.footer + { + font-style: italic; + } +div.footer hr + { + color: #999999; + height: 1px; + } + +ol, ul + { + margin: .5em 0em 0em 4em; + } +li + { + margin-bottom: .5em; + } +ul p, ol p, dl p + { + margin-left: 0em; + } +ul p.label, ol p.label + { + margin-top: .5em; + } + +dl + { + margin-top: 0em; + padding-left: 1px; /* Prevents italic-letter descenders from being cut off */ + } +dd + { + margin-bottom: 0em; + margin-left: 1.5em; + } +dt + { + margin-top: .5em; + } + +a:link + { + color: #0000ff; + } +a:visited + { + color: #0000ff; + } +a:hover + { + color: #3366ff; + } + +img + { + border: none; + } +table.dtTABLE td img + { + border: none; + vertical-align: top; + margin-right: 2px; + } +/* Not in dtue.css. Used by NDoc's "ShowMissing..." options. */ +.missing + { + color: Red; + font-weight: bold; + } +div.Hierarchy +{ + margin: 0.5em,0.0em,0.5em,1.0em; +} \ No newline at end of file diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/pubevent.gif b/NetInterfaceReferenceFiles/Aveva.Pdms/pubevent.gif new file mode 100644 index 0000000000000000000000000000000000000000..f1821fca720c44dc83954b03fdf254c4f9ca87c0 GIT binary patch literal 869 zcmZ?wbhEHb6krfw_|5&2xXb23G5K#Qd!pOkD$e;uA4=7JKa5ynAbI5pXSa6Vmk4vJUV8a4tW-dvy z5`~Qq9oZR-!c2Z_aA52b6V&7J+_c2AMb16S=f}h+Og)0mb|E__Jv%vDjG2kS8UW;` BHt_%e literal 0 HcmV?d00001 diff --git a/NetInterfaceReferenceFiles/Aveva.Pdms/pubmethod.gif b/NetInterfaceReferenceFiles/Aveva.Pdms/pubmethod.gif new file mode 100644 index 0000000000000000000000000000000000000000..b0c11814a9abcc9bc05ddabf6a13263e0663fe07 GIT binary patch literal 889 zcmZ?wbhEHb6krfw_|501_JoqaiTdLqPE-3nK#q zBZCgeqo6$Dz!Ap4%pv2kVSxh^tAH3sK|%uq13$ZrfyIXghIV01sWU4kD!4bYinWPU zFf4NJVHR$SIZ?pC&?%dIYR<`k1MXd7{3Xi2O1h06ciM|B4D{uVl)JXbqFZ_WMO1r zU}Vq%`4W^T95|vFm^oxTHY{*p2FgfqG#qehVdLk^@K~V4$jHPbapT0s$HzKYY}pR1 z2uwIUnU9mlz(Hu?p%$)qCl=4m%X~YQSFaW{-|3LH~#2N*oAuz;4K=CIFBLjmFgAT|apgiHgk;S0OA>*+j z!I7C+Nu$Ssp~5(payload) ?? new SimplifyModelRequest(); executionId = simplifyRequest.ExecutionId; - var command = new SimplifyModelCommand(simplifyRequest); - var result = command.Execute() as SimplifyModelResult; + var command = new SimplifyModelCommand(simplifyRequest); + var invokeResult = MainThreadInvoker.Invoke(command, 600000); + + if (!invokeResult.Success) + { + var msg = string.IsNullOrEmpty(invokeResult.Message) ? "模型轻量化失败" : invokeResult.Message; + NotifyBatchTaskResult(simplifyRequest.ExecutionId, false, msg, null); + return CreateErrorResponse(500, msg); + } + + var result = invokeResult.Result as SimplifyModelResult; if (result == null) { @@ -281,19 +290,71 @@ namespace TellmePdmsPluging.Network string executionId = null; try { + if (!string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase)) + { + return CreateErrorResponse(405, "仅支持POST"); + } + var payload = ReadRequestBody(request); if (string.IsNullOrEmpty(payload)) { - return CreateErrorResponse(400, "请求体不能为空"); - } + return CreateErrorResponse(400, "请求体不能为空"); + } var serializer = new JavaScriptSerializer(); var shrinkwrapRequest = serializer.Deserialize(payload) ?? new ShrinkwrapModelRequest(); executionId = shrinkwrapRequest.ExecutionId; - - var command = new ShrinkwrapModelCommand(shrinkwrapRequest); - var invokeResult = MainThreadInvoker.Invoke(command, 600000); - + + var command = new ShrinkwrapModelCommand(shrinkwrapRequest); + + // executionId 场景采用异步提交,避免长时间阻塞HTTP连接。 + if (!string.IsNullOrEmpty(shrinkwrapRequest.ExecutionId)) + { + MainThreadInvoker.InvokeAsync(command, asyncInvokeResult => + { + try + { + if (!asyncInvokeResult.Success) + { + var msg = string.IsNullOrEmpty(asyncInvokeResult.Message) ? "外壳保留失败" : asyncInvokeResult.Message; + NotifyBatchTaskResult(shrinkwrapRequest.ExecutionId, false, msg, null); + return; + } + + var asyncResult = asyncInvokeResult.Result as ShrinkwrapModelResult; + if (asyncResult == null) + { + NotifyBatchTaskResult(shrinkwrapRequest.ExecutionId, false, "外壳保留结果为空", null); + return; + } + + if (!asyncResult.Success) + { + var message = string.IsNullOrEmpty(asyncResult.Message) ? "外壳保留失败" : asyncResult.Message; + NotifyBatchTaskResult(shrinkwrapRequest.ExecutionId, false, message, null); + return; + } + + NotifyBatchTaskResult(shrinkwrapRequest.ExecutionId, true, null, asyncResult); + } + catch (Exception callbackEx) + { + NotifyBatchTaskResult(shrinkwrapRequest.ExecutionId, false, callbackEx.Message, null); + } + }); + + var acceptedData = new + { + accepted = true, + executionId = shrinkwrapRequest.ExecutionId, + status = "QUEUED", + message = "外壳保留任务已提交" + }; + return CreateSuccessResponse(acceptedData); + } + + var invokeResult = MainThreadInvoker.Invoke(command, 600000); + if (!invokeResult.Success) { var msg = string.IsNullOrEmpty(invokeResult.Message) ? "外壳保留失败" : invokeResult.Message; @@ -505,20 +566,20 @@ namespace TellmePdmsPluging.Network if (!invokeResult.Success) { - var msg = string.IsNullOrEmpty(invokeResult.Message) ? "IFC导出失败" : invokeResult.Message; + var msg = string.IsNullOrEmpty(invokeResult.Message) ? "RVM导出失败" : invokeResult.Message; NotifyBatchTaskResult(exportRequest.ExecutionId, false, msg, null); return CreateErrorResponse(500, msg); } var result = invokeResult.Result as ExportIfcResult; - if (result == null) - { - return CreateErrorResponse(500, "IFC导出结果为空"); - } - + if (result == null) + { + return CreateErrorResponse(500, "RVM导出结果为空"); + } + if (!result.Success) { - var message = string.IsNullOrEmpty(result.Message) ? "IFC导出失败" : result.Message; + var message = string.IsNullOrEmpty(result.Message) ? "RVM导出失败" : result.Message; NotifyBatchTaskResult(exportRequest.ExecutionId, false, message, null); return CreateErrorResponse(500, message); } @@ -529,7 +590,7 @@ namespace TellmePdmsPluging.Network catch (Exception ex) { NotifyBatchTaskResult(executionId, false, ex.Message, null); - return CreateErrorResponse(500, $"IFC导出失败: {ex.Message}"); + return CreateErrorResponse(500, $"RVM导出失败: {ex.Message}"); } } diff --git a/命令.md b/命令.md index 80fe33c..762c5c5 100644 --- a/命令.md +++ b/命令.md @@ -19,4 +19,7 @@ 4. 启动PDMS - PDMS启动时会自动加载插件,成功后会弹出提示框,显示HTTP服务已在9001端口启动。 \ No newline at end of file + PDMS启动时会自动加载插件,成功后会弹出提示框,显示HTTP服务已在9001端口启动。 + + ### 转换chm到html + hh.exe -decompile ./Aveva.Pdms Aveva.Pdms.chm \ No newline at end of file diff --git a/日志排查指南.md b/日志排查指南.md new file mode 100644 index 0000000..f3db8ad --- /dev/null +++ b/日志排查指南.md @@ -0,0 +1,119 @@ +# 日志排查指南(PDMS 插件) + +本文给后续开发使用,目标是快速定位导出/接口问题。 + +## 1. 先看哪些日志 + +### 1.1 PDMS Alert Summary(最关键) +- 典型内容: + - `Message: (...) CP: Syntax error` + - `Message: (...) Security error ... feature has not been checked out` + - `Command: EXPORT ...` +- 这是最直接的命令执行结果,优先级最高。 + +### 1.2 接口返回 JSON +- 本插件接口统一返回: + - `code` + - `message` + - `data` +- 导出失败时重点看 `message`,例如: + - `RVM导出失败,未生成文件` + - `RVM导出异常: ...` + +### 1.3 插件源码中的错误包装点 +- 导出主逻辑:`Core/PdmsManager.cs` +- HTTP 包装:`Network/HttpServer.cs` + +--- + +## 2. 日志/文档常见位置 + +### 2.1 PDMS 安装目录 +- `C:\AVEVA\Plant\PDMS12.1.SP4\` + +### 2.2 文档目录(命令语法依据) +- `C:\AVEVA\Plant\PDMS12.1.SP4\Documentation\` +- 常用已解包文档(若存在): + - `...\_decompiled\DRMPU\`(EXPORT 命令完整语法) + - `...\_decompiled\EXPLA\`(ExPLANT-A 示例) + +### 2.3 项目目录 +- 当前仓库根目录(本文件所在目录) + +--- + +## 3. 常用检索命令(Windows) + +先执行: + +```powershell +chcp 65001 +``` + +### 3.1 在项目里搜导出相关代码 + +```powershell +rg -n "export|EXPORT|RVM|IFC|ExportSystem|Selections|EXPORT FINISH" -S Core Network Models +``` + +### 3.2 在文档里搜命令语法 + +```powershell +rg -n -i "export command - full syntax|copying model data from pdms to review|export system|export file|export finish" "C:\AVEVA\Plant\PDMS12.1.SP4\Documentation" -S --glob "*.htm" --glob "*.html" +``` + +### 3.3 搜许可证/安全错误关键词 + +```powershell +rg -n -i "security error|feature has not been checked out|license|licen[cs]e" -S . +``` + +> 注:Alert Summary 通常是人工“Save Alert Summary to File”保存的文本,路径不固定。拿到文件后可直接用 `rg` 搜 `Message:` / `Command:`。 + +--- + +## 4. 一次标准排查流程 + +1. 看接口返回 `message`(先判断是语法错误、许可证错误,还是文件未生成)。 +2. 打开 PDMS Alert Summary,记录两行: + - `Message: ...` + - `Command: ...` +3. 若 `Message` 包含: + - `CP: Syntax error`:命令格式问题,去查 `DRMPU` 语法页。 + - `feature has not been checked out`:许可证问题,找管理员处理 feature。 +4. 对照 `Core/PdmsManager.cs` 的导出命令构建逻辑,确认实际发送命令序列。 +5. 最后再看落盘路径: + - `ExportPath` + - `FileName` + - `FullPath` + - 文件是否真实存在。 + +--- + +## 5. 典型问题与判断 + +### 5.1 `CP: Syntax error` +- 说明 PDMS 不接受该命令写法。 +- 优先根据文档语法修正,不要靠猜。 + +### 5.2 `Security error ... feature has not been checked out` +- 许可证未授权对应导出驱动(例如 `/EXPLANTA`)。 +- 这是环境/授权问题,不是代码语法问题。 + +### 5.3 `导出失败,未生成文件` +- 命令可能执行但未产出文件,常见原因: + - 选择对象为空或不匹配; + - 导出驱动/许可问题; + - 输出目录权限或路径异常。 + +--- + +## 6. 开发约定建议 + +1. 提交 issue 时必须附: + - 接口请求体 + - 接口响应 JSON + - Alert Summary 里的 `Message` + `Command` +2. 优先用文档佐证命令,不要直接改命令字符串“试错”。 +3. 涉及导出驱动(`EXPORT SYSTEM ...`)时,先确认许可证 feature。 +