From c733a27cbbd9f4c5eb5f48d3770186d13beb74ee Mon Sep 17 00:00:00 2001 From: codex Date: Fri, 31 Jul 2026 11:37:31 +0300 Subject: [PATCH] feat: register real Plant 3D 2026 commands and ribbon --- .../P_Rec_Annot/docs/migration-dashboard.md | 15 +++- .../P_Rec_Annot/docs/runtime-test-plan.md | 13 ++- .../src/PRecAnnot.Plant3D2026/EntryPoint.cs | 80 +++++++++++++++++++ 3 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs diff --git a/Plant3D2026/P_Rec_Annot/docs/migration-dashboard.md b/Plant3D2026/P_Rec_Annot/docs/migration-dashboard.md index 48bbdca..05a97b1 100644 --- a/Plant3D2026/P_Rec_Annot/docs/migration-dashboard.md +++ b/Plant3D2026/P_Rec_Annot/docs/migration-dashboard.md @@ -1,3 +1,16 @@ # Migration dashboard -The active command set is mapped in `command-migration-matrix.md`; all currently published target descriptors are `BLOCKED_WITH_REASON` because Plant 3D 2026 assemblies/runtime are unavailable. Coverage is not reported as a percentage until every active use case has a runtime result. Current status: mapped 3 foundation descriptors, implemented Core planning/registry/telemetry contracts, runtime verified 0, native replacements approved 0, retirements approved 0, blockers 3. +The first real Plant 3D 2026 build is complete against the workstation assemblies (AutoCAD managed 25.1.0.0; Plant managed 17.0.60.1; AdWindows 5.2.1.1). Core remains Autodesk-free. + +| Area | Status | +|---|---| +| `PHP_REC_ANNOT` | Registered real `CommandMethod`; primary diagnostic entry point | +| `PXP-AAV` | Registered real `CommandMethod`; diagnostic alias | +| `PXP-OA` | Registered real `CommandMethod`; diagnostic alias | +| Ribbon | Implemented with `Autodesk.Windows`; idempotent tab/panel guard | +| Adapter | Compiled against real 2026 assemblies | +| Bundle | Local smoke package built and checked for Autodesk DLL exclusion | +| Runtime | Not executed in AutoCAD/Plant process | +| Production deploy | NOT PERFORMED | + +Fixed API assumptions: foundation-only lifecycle became `IExtensionApplication`; command descriptors became actual `CommandMethod` registrations; Plant project detection uses `PlantApplication.CurrentProject`; Ribbon uses `ComponentManager.Ribbon`, `RibbonTab`, `RibbonPanelSource`, and `RibbonButton`. Remaining blockers are runtime loading, Ortho API/data-link mapping, safe DWG transaction implementation, and golden fixture validation. diff --git a/Plant3D2026/P_Rec_Annot/docs/runtime-test-plan.md b/Plant3D2026/P_Rec_Annot/docs/runtime-test-plan.md index b8a19f5..4a701ab 100644 --- a/Plant3D2026/P_Rec_Annot/docs/runtime-test-plan.md +++ b/Plant3D2026/P_Rec_Annot/docs/runtime-test-plan.md @@ -1,3 +1,14 @@ # Runtime test plan -On a controlled Plant 3D 2026 test machine: install the bundle outside production, start Plant, verify exactly one Ribbon tab/panel, open a test project and Ortho DWG, run `PHP_REC_ANNOT`, inspect dry-run counts, confirm one transaction, save/reopen/audit, repeat command, test no duplicates and Undo, then compare entities/text/layers/positions/links with the 2019 golden sample. This plan is prepared but not executed. +Runtime smoke is ready but has not been executed in AutoCAD. The local package is `Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle`. + +Next-step installation command (intentionally not executed): + +```powershell +Copy-Item -Recurse -Force .\Plant3D2026\P_Rec_Annot\artifacts\P_Rec_Annot.bundle ` + "$env:APPDATA\Autodesk\ApplicationPlugins\P_Rec_Annot.bundle" +``` + +In a controlled Plant 3D 2026 session, verify that the entry assembly loads, the `Промхимпроект` tab and `Аннотации` panel occur once, and the `Аннотировать` button invokes `PHP_REC_ANNOT`. Open a Plant project and active DWG, run all three commands, verify the project guard and diagnostic-only behavior, and confirm no unhandled exception. Repeat initialization and confirm no Ribbon duplicate. DWG mutation, Ortho viewport/data-link behavior, undo, repeat-run idempotence, and golden-DWG comparison remain runtime blockers for the implementation wave. + +Deploy status: NOT PERFORMED. diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs new file mode 100644 index 0000000..03a03d8 --- /dev/null +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs @@ -0,0 +1,80 @@ +using Autodesk.AutoCAD.ApplicationServices; +using Autodesk.AutoCAD.EditorInput; +using Autodesk.AutoCAD.Runtime; +using Autodesk.ProcessPower.PlantInstance; +using Autodesk.Windows; +using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application; + +namespace PRecAnnot.Plant3D2026; + +public sealed class EntryPoint : IExtensionApplication +{ + public void Initialize() => RibbonIntegration.Register(); + + public void Terminate() { } + + [CommandMethod("PHP_REC_ANNOT")] + public void PrimaryCommand() => RunDiagnostic("PHP_REC_ANNOT"); + + [CommandMethod("PXP-AAV")] + public void AutoAnnotateViewCommand() => RunDiagnostic("PXP-AAV"); + + [CommandMethod("PXP-OA")] + public void OrthoAnnotateCommand() => RunDiagnostic("PXP-OA"); + + private static void RunDiagnostic(string commandName) + { + try + { + Document? document = AcadApplication.DocumentManager.MdiActiveDocument; + if (document is null) + return; + + Editor editor = document.Editor; + if (PlantApplication.CurrentProject is null) + { + editor.WriteMessage("\nP_Rec_Annot: активный Plant 3D project не найден; DWG не изменён."); + return; + } + + editor.WriteMessage($"\nP_Rec_Annot {commandName}: Plant 3D project '{PlantApplication.CurrentProject.Name}' найден; diagnostic-only, DWG не изменён."); + } + catch (System.Exception exception) + { + try { AcadApplication.DocumentManager.MdiActiveDocument?.Editor.WriteMessage($"\nP_Rec_Annot: безопасная диагностика завершилась ошибкой: {exception.Message}"); } + catch { } + } + } +} + +internal static class RibbonIntegration +{ + private const string TabId = "PRecAnnot.Plant3D2026"; + private const string Command = "PHP_REC_ANNOT "; + + public static void Register() + { + try + { + RibbonControl? ribbon = ComponentManager.Ribbon; + if (ribbon is null) return; + + RibbonTab? tab = ribbon.Tabs.FirstOrDefault(item => string.Equals(item.Id, TabId, StringComparison.OrdinalIgnoreCase)); + if (tab is null) + { + tab = new RibbonTab { Id = TabId, Title = "Промхимпроект" }; + ribbon.Tabs.Add(tab); + } + + if (tab.Panels.Any(panel => string.Equals(panel.Source?.Title, "Аннотации", StringComparison.OrdinalIgnoreCase))) return; + + RibbonPanelSource source = new() { Title = "Аннотации" }; + source.Items.Add(new RibbonButton { Text = "Аннотировать", ShowText = true, CommandParameter = Command }); + tab.Panels.Add(new RibbonPanel { Source = source }); + } + catch + { + // Ribbon is optional; command-line registration remains available. + } + } +}