using System.Diagnostics; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.Runtime; using Autodesk.Windows; using PRecAnnot.Abstractions; using PRecAnnot.Core; using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application; namespace PRecAnnot.Plant3D2026; public sealed class EntryPoint : IExtensionApplication { public void Initialize() { RuntimeDiagnostics.Write("AssemblyInitializeStarted"); RibbonIntegration.Register(); RuntimeDiagnostics.Write("AssemblyInitializeCompleted"); } public void Terminate() { RuntimeDiagnostics.Write("AssemblyTerminateStarted"); RibbonIntegration.Terminate(); RuntimeDiagnostics.Write("AssemblyTerminateCompleted"); } [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) { var operationId = Guid.NewGuid().ToString("N"); var stopwatch = Stopwatch.StartNew(); Document? document = null; RuntimeDiagnostics.Write("CommandStarted", operationId: operationId, commandName: commandName); try { document = AcadApplication.DocumentManager.MdiActiveDocument; var mode = ParseMode(RuntimeDiagnostics.RuntimeMode); if (mode == PRecAnnotExecutionMode.Apply) throw new InvalidOperationException("APPLY_MODE_NOT_IMPLEMENTED"); var context = new PlantRuntimeContextReader().ReadCurrentContext(); var details = new Dictionary { ["ExecutionMode"] = mode.ToString(), ["IsPlantProjectDetected"] = context.HasPlantProject, ["IsPlantProjectOpen"] = context.HasPlantProject, ["IsOrthoDrawing"] = context.IsOrthoDrawing, ["ViewportCount"] = context.Viewports.Count, ["DataLinksAvailable"] = DataLinksAvailable(context.HasPlantProject) }; if (document is null || !context.HasPlantProject) { RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: "PreflightRejected", document: document, details: details); WriteEditor(document, "\nP_Rec_Annot: Активный Plant 3D-проект не найден; DWG не изменён."); WriteUsage(operationId, commandName, "PreflightRejected", document, details, stopwatch.ElapsedMilliseconds); RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: "PreflightRejected", document: document, details: details); return; } if (mode == PRecAnnotExecutionMode.Smoke || !context.IsOrthoDrawing) { var status = context.IsOrthoDrawing ? "PreflightAccepted" : "NotOrtho"; RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: status, document: document, details: details); WriteEditor(document, $"\nP_Rec_Annot {commandName}: проект '{context.ProjectDisplayName}' найден; тип чертежа: {(context.IsOrthoDrawing ? "Ortho" : "NotOrtho")}; DWG не изменён."); WriteUsage(operationId, commandName, status, document, details, stopwatch.ElapsedMilliseconds); RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: status, document: document, details: details); return; } var builder = new AnnotationPlanBuilder(); var plans = new List(); foreach (var viewport in context.Viewports) { var objects = new PlantObjectReader().ReadObjects(viewport, CancellationToken.None); plans.Add(builder.Build(new AnnotationPlanningRequest { CommandName = commandName, Viewport = viewport, Objects = objects })); } var summary = new { ObjectsExamined = plans.Sum(x => x.Summary.ObjectsExamined), CreateCount = plans.Sum(x => x.Summary.CreateCount), UpdateCount = plans.Sum(x => x.Summary.UpdateCount), SkipCount = plans.Sum(x => x.Summary.SkipCount), ConflictCount = plans.Sum(x => x.Summary.ConflictCount), UnsupportedCount = plans.Sum(x => x.Summary.UnsupportedCount), WarningCount = plans.Sum(x => x.Summary.WarningCount), ErrorCount = plans.Sum(x => x.Summary.ErrorCount) }; details["ObjectsExamined"] = summary.ObjectsExamined; details["PlanCreateCount"] = summary.CreateCount; details["PlanUpdateCount"] = summary.UpdateCount; details["PlanSkipCount"] = summary.SkipCount; details["PlanConflictCount"] = summary.ConflictCount; details["PlanUnsupportedCount"] = summary.UnsupportedCount; details["ErrorCount"] = summary.ErrorCount; RuntimeDiagnostics.Write("CommandPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: "DryRunCompleted", document: document, details: details); WriteEditor(document, $"\nP_Rec_Annot — диагностический план\nПроект: {context.ProjectDisplayName}\nТип чертежа: Ortho\nViewport: {context.Viewports.Count}\nОбъектов найдено: {summary.ObjectsExamined}\nБудет создано: {summary.CreateCount}\nБудет обновлено: {summary.UpdateCount}\nБудет пропущено: {summary.SkipCount}\nКонфликтов: {summary.ConflictCount}\nОшибок: {summary.ErrorCount}\nDWG не изменён."); WriteUsage(operationId, commandName, "DryRunCompleted", document, details, stopwatch.ElapsedMilliseconds); RuntimeDiagnostics.Write("CommandCompleted", operationId: operationId, commandName: commandName, resultStatus: "DryRunCompleted", document: document, details: details); } catch (System.Exception exception) { RuntimeDiagnostics.Write("CommandFailed", "ERROR", operationId, commandName, exception.Message, exception, document); WriteUsage(operationId, commandName, exception.Message, document, null, stopwatch.ElapsedMilliseconds, exception); WriteEditor(document, $"\nP_Rec_Annot: безопасная диагностика завершилась ошибкой: {exception.Message}"); } } private static PRecAnnotExecutionMode ParseMode(string value) => value.Equals("Smoke", StringComparison.OrdinalIgnoreCase) ? PRecAnnotExecutionMode.Smoke : value.Equals("Apply", StringComparison.OrdinalIgnoreCase) ? PRecAnnotExecutionMode.Apply : PRecAnnotExecutionMode.DryRun; private static bool DataLinksAvailable(bool hasPlantProject) { try { return hasPlantProject && Autodesk.ProcessPower.PlantInstance.PlantApplication.ProjectPart("Piping")?.DataLinksManager is not null; } catch { return false; } } private static void WriteUsage(string operationId, string commandName, string status, Document? document, IReadOnlyDictionary? details, long duration, System.Exception? exception = null) { var eventDetails = new Dictionary { ["ObjectsExamined"] = 0, ["AnnotationsCreated"] = 0, ["AnnotationsUpdated"] = 0, ["AnnotationsDeleted"] = 0, ["AnnotationsSkipped"] = 0, ["WarningCount"] = 0, ["ErrorCount"] = exception is null ? 0 : 1, ["DurationMilliseconds"] = duration }; if (details is not null) foreach (var item in details) eventDetails[item.Key] = item.Value; RuntimeDiagnostics.Write("CommandUsageEvent", exception is null ? "INFO" : "ERROR", operationId, commandName, status, exception, document, eventDetails); } private static void WriteEditor(Document? document, string message) { try { document?.Editor.WriteMessage(message); } catch { } } } internal static class RibbonIntegration { private const string TabId = "PRecAnnot.Plant3D2026"; private const string PanelId = "PRecAnnot.Plant3D2026.Annotations"; private const string ButtonId = "PRecAnnot.Plant3D2026.PHP_REC_ANNOT"; private static bool deferred; public static void Register() { RuntimeDiagnostics.Write("RibbonInitializationStarted"); try { if (ComponentManager.Ribbon is null) { if (!deferred) { ComponentManager.ItemInitialized += OnItemInitialized; deferred = true; } RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "Deferred"); return; } RegisterNow(); } catch (System.Exception exception) { RuntimeDiagnostics.Write("RibbonInitializationCompleted", "ERROR", exception: exception, resultStatus: "Failed"); } } private static void OnItemInitialized(object? sender, EventArgs args) { if (ComponentManager.Ribbon is null) return; ComponentManager.ItemInitialized -= OnItemInitialized; deferred = false; Register(); } private static void RegisterNow() { var ribbon = ComponentManager.Ribbon!; var tab = ribbon.Tabs.FirstOrDefault(x => string.Equals(x.Id, TabId, StringComparison.OrdinalIgnoreCase)); if (tab is null) { tab = new RibbonTab { Id = TabId, Title = "Промхимпроект" }; ribbon.Tabs.Add(tab); } var panel = tab.Panels.FirstOrDefault(x => string.Equals(x.Source?.Id, PanelId, StringComparison.OrdinalIgnoreCase)); if (panel is not null) { RuntimeDiagnostics.Write("RibbonAlreadyExists", resultStatus: "AlreadyExists"); RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "AlreadyExists"); return; } var source = new RibbonPanelSource { Id = PanelId, Title = "Аннотации" }; source.Items.Add(new RibbonButton { Id = ButtonId, Text = "Аннотировать", ShowText = true, CommandParameter = "PHP_REC_ANNOT " }); tab.Panels.Add(new RibbonPanel { Source = source }); RuntimeDiagnostics.Write("RibbonInitializationCompleted", resultStatus: "Created"); } public static void Terminate() { if (!deferred) return; ComponentManager.ItemInitialized -= OnItemInitialized; deferred = false; } }