From 9242d2bb083341e4c54e3fe1cf938d55326224a1 Mon Sep 17 00:00:00 2001 From: codex Date: Mon, 10 Aug 2026 15:11:40 +0300 Subject: [PATCH] feat: record p-rec-annot selected-view automation evidence --- .../PRecAnnot.Plant3D2026/AutoAnnotateViewCommand.cs | 2 ++ .../ScriptDrivenFirstApplyAutomation.cs | 10 +++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/AutoAnnotateViewCommand.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/AutoAnnotateViewCommand.cs index a58440e..fb5cc8f 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/AutoAnnotateViewCommand.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/AutoAnnotateViewCommand.cs @@ -41,6 +41,7 @@ internal static class AutoAnnotateViewCommand var bubbles = classifications.Count(x => x.Representation == OrthoAnnotationRepresentation.BubbleBlock); document.Editor.WriteMessage($"\nPXP-AAV preview: examined={plan.Summary.ObjectsExamined}; create={plan.Summary.CreateCount}; update={plan.Summary.UpdateCount}; leaders={leaders}; bubbles={bubbles}; skip={plan.Summary.SkipCount}; conflicts={plan.Summary.ConflictCount}; PlanId={plan.PlanId}. DWG не изменён."); RuntimeDiagnostics.Write("AutoAnnotateViewPreviewCompleted", commandName: "PXP-AAV", resultStatus: "PreviewCompleted", document: document, details: new Dictionary { ["PlanId"] = plan.PlanId, ["ObjectsExamined"] = plan.Summary.ObjectsExamined, ["MultiLeaderCandidates"] = leaders, ["BubbleCandidates"] = bubbles }); + ScriptDrivenFirstApplyAutomation.RecordPxpAav(plan.PlanId, objects.Select(value => value.SnapshotId).OrderBy(value => value, StringComparer.Ordinal).ToArray(), viewport.Id, plan.Summary.CreateCount, plan.Summary.SkipCount, permit is not null, plan.Summary.ObjectsExamined, objects.Count(value => value.MembershipStatus == ViewMembershipStatus.Resolved)); if (permit is null) return; if (!string.Equals(permit.ExpectedPlanId, plan.PlanId, StringComparison.Ordinal)) throw new InvalidOperationException("APPLY_PLAN_ID_MISMATCH"); var selectedSource = objects.SingleOrDefault(x => string.Equals(x.SnapshotId, permit.TargetSnapshotId, StringComparison.Ordinal)) ?? throw new InvalidOperationException("APPLY_TARGET_NOT_FOUND"); @@ -60,6 +61,7 @@ internal static class AutoAnnotateViewCommand var outcome = new FirstLimitedPipeAnnotationWriter().Write(document, preflight.Operation, permit.VisibilityState, permit.AnnotationTemplateDwgPath); document.Editor.WriteMessage("\nPXP-AAV Apply: " + outcome + "; AutoCAD UNDO is available."); RuntimeDiagnostics.Write("AutoAnnotateViewApplyCompleted", commandName: "PXP-AAV", resultStatus: outcome.ToString(), document: document, details: new Dictionary { ["PlanId"] = plan.PlanId }); + ScriptDrivenFirstApplyAutomation.RecordApplyOutcome(outcome); } catch (Exception exception) { RuntimeDiagnostics.Write("AutoAnnotateViewFailed", "ERROR", commandName: "PXP-AAV", resultStatus: exception.Message, exception: exception, document: document); document.Editor.WriteMessage("\nPXP-AAV: " + exception.Message); } } diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/ScriptDrivenFirstApplyAutomation.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/ScriptDrivenFirstApplyAutomation.cs index ba9ec34..29ef131 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/ScriptDrivenFirstApplyAutomation.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/ScriptDrivenFirstApplyAutomation.cs @@ -14,7 +14,7 @@ namespace PRecAnnot.Plant3D2026; // Test-only observer. The AutoCAD /b processor, never this type, drives PXP-OA. internal static class ScriptDrivenFirstApplyAutomation { - private sealed record Config(string Phase, string OrthoDrawingPath, string EvidencePath, string FixtureMarkerPath, string ExpectedEntryAssemblySha256, bool Diagnostic); + private sealed record Config(string Phase, string OrthoDrawingPath, string EvidencePath, string FixtureMarkerPath, string ExpectedEntryAssemblySha256, string TargetCommand, bool Diagnostic); private sealed record Run(int Sequence, string? PlanId, IReadOnlyList Candidates, string? View, int Create, int Skip, bool? PermitAccepted, string? ApplyOutcome, int? DbmodBefore, int? DbmodAfter, AutomationEntityCounts? Before, AutomationEntityCounts? After, bool WillStart, bool Ended, bool Failed, bool Cancelled, int? SourceRows, int? MembershipResolved); private static Config? config; private static Document? fixture; @@ -93,6 +93,8 @@ internal static class ScriptDrivenFirstApplyAutomation if (!last.WillStart || last.PlanId is not null) return; runs[^1] = last with { PlanId = planId, Candidates = candidates, View = view, Create = create, Skip = skip, PermitAccepted = permitAccepted, SourceRows = sourceRows, MembershipResolved = membershipResolved }; } + internal static void RecordPxpAav(string? planId, IReadOnlyList candidates, string? view, int create, int skip, bool? permitAccepted, int sourceRows, int membershipResolved) => + RecordPxpOa(planId, candidates, view, create, skip, permitAccepted, sourceRows, membershipResolved); internal static void RecordApplyOutcome(PipeAnnotationWriteOutcome outcome) { @@ -184,7 +186,7 @@ internal static class ScriptDrivenFirstApplyAutomation private static AutomationCommandCorrelationEvidence Correlate(string kind, object sender, string rawName, bool sequenceAccepted) { var normalized = rawName.Trim(); - var command = string.Equals(normalized, "PXP-OA", StringComparison.OrdinalIgnoreCase); + var command = config is not null && string.Equals(normalized, config.TargetCommand, StringComparison.OrdinalIgnoreCase); var state = prepared && !terminal && config is not null && fixture is not null && database is not null; var senderDocument = sender as Document; var senderPath = senderDocument?.Name; @@ -218,7 +220,9 @@ internal static class ScriptDrivenFirstApplyAutomation { var path = Environment.GetEnvironmentVariable("P_REC_ANNOT_AUTOMATION_CONFIG") ?? throw new InvalidOperationException("AUTOMATION_CONFIG_MISSING"); using var json = JsonDocument.Parse(File.ReadAllText(path)); var root = json.RootElement; - return new(root.GetProperty("phase").GetString() ?? "Preview", root.GetProperty("orthoDrawingPath").GetString() ?? throw new InvalidOperationException("AUTOMATION_ORTHO_PATH_REQUIRED"), root.GetProperty("evidencePath").GetString() ?? throw new InvalidOperationException("AUTOMATION_EVIDENCE_PATH_REQUIRED"), root.GetProperty("fixtureMarkerPath").GetString() ?? throw new InvalidOperationException("AUTOMATION_MARKER_PATH_REQUIRED"), root.GetProperty("entryAssemblySha256").GetString() ?? throw new InvalidOperationException("AUTOMATION_ENTRY_ASSEMBLY_HASH_REQUIRED"), root.TryGetProperty("diagnostic", out var diagnostic) && diagnostic.ValueKind == JsonValueKind.True); + var target = root.TryGetProperty("targetCommand", out var command) ? command.GetString() : "PXP-OA"; + if (string.IsNullOrWhiteSpace(target) || !target.All(value => char.IsAsciiLetterOrDigit(value) || value is '-' or '_')) throw new InvalidOperationException("AUTOMATION_TARGET_COMMAND_INVALID"); + return new(root.GetProperty("phase").GetString() ?? "Preview", root.GetProperty("orthoDrawingPath").GetString() ?? throw new InvalidOperationException("AUTOMATION_ORTHO_PATH_REQUIRED"), root.GetProperty("evidencePath").GetString() ?? throw new InvalidOperationException("AUTOMATION_EVIDENCE_PATH_REQUIRED"), root.GetProperty("fixtureMarkerPath").GetString() ?? throw new InvalidOperationException("AUTOMATION_MARKER_PATH_REQUIRED"), root.GetProperty("entryAssemblySha256").GetString() ?? throw new InvalidOperationException("AUTOMATION_ENTRY_ASSEMBLY_HASH_REQUIRED"), target, root.TryGetProperty("diagnostic", out var diagnostic) && diagnostic.ValueKind == JsonValueKind.True); } private static void RequireGate(Config value) {