196 lines
11 KiB
C#
196 lines
11 KiB
C#
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<string, object?>
|
|
{
|
|
["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<AnnotationPlan>();
|
|
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<string, object?>? details, long duration, System.Exception? exception = null)
|
|
{
|
|
var eventDetails = new Dictionary<string, object?>
|
|
{
|
|
["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;
|
|
}
|
|
}
|