From 990d057ae3045add78c985afbffb897dca23fe8c Mon Sep 17 00:00:00 2001 From: codex Date: Mon, 10 Aug 2026 16:20:52 +0300 Subject: [PATCH] feat: bridge settings assets and define fixture matrix --- .../evidence/controlled-fixture-matrix.json | 73 +++++++++++++++++++ .../ProductionSettingsContract.cs | 23 +++++- .../ProductionSettingsContractTests.cs | 11 +++ 3 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 Plant3D2026/P_Rec_Annot/docs/evidence/controlled-fixture-matrix.json diff --git a/Plant3D2026/P_Rec_Annot/docs/evidence/controlled-fixture-matrix.json b/Plant3D2026/P_Rec_Annot/docs/evidence/controlled-fixture-matrix.json new file mode 100644 index 0000000..a57cf71 --- /dev/null +++ b/Plant3D2026/P_Rec_Annot/docs/evidence/controlled-fixture-matrix.json @@ -0,0 +1,73 @@ +{ + "schemaVersion": 1, + "policy": "controlled-local-only; fail-closed on ambiguous identity; no production DWG/project writes", + "fixtures": [ + { + "id": "LINE_NUMBER_ORTHO_OWNED", + "requiredDocument": "controlled Plant Ortho DWG copy", + "requiredObjects": ["Pipe with DataLinks line number", "owned line-number block", "legacy layers/text styles"], + "selection": "exact owned block handle discovered from drawing", + "preState": "owned annotation baseline and unchanged Plant source", + "mutation": "create/update/delete line annotation", + "repeat": "update or AlreadyExists according to command", + "undo": "native AutoCAD UNDO restores entity and ownership baseline", + "workflows": ["PXP-LN", "PXP-ULN", "PXP-ESN", "PXP-ELN", "PXP-RMP"] + }, + { + "id": "CONNECTOR_TOPOLOGY", + "requiredDocument": "controlled Plant project and Ortho copy", + "requiredObjects": ["two connected Plant parts", "connector/port snapshot", "P3dPartConnection", "line-group relationships"], + "selection": "exact connector source identity; no arbitrary entity fallback", + "preState": "no owned connection annotation", + "mutation": "orthogonal connection leader and ownership XData", + "repeat": "recreate/update without duplicate ownership", + "undo": "native AutoCAD UNDO restores pre-annotation state", + "workflows": ["PXP-ALC"] + }, + { + "id": "ELEVATION_GOLDEN", + "requiredDocument": "controlled Plant model plus paper/model context", + "requiredObjects": ["positive, negative and zero elevations", "BLOCK_ELEVATION definition", "attribute definitions"], + "selection": "exact Plant source object handle", + "preState": "source object and annotation baseline", + "mutation": "legacy /5, AwayFromZero, *5 elevation annotation", + "repeat": "update existing owned annotation", + "undo": "native AutoCAD UNDO restores baseline", + "workflows": ["PXP-AE", "PXP-IE"] + }, + { + "id": "ORTHO_VIEWPORT_EXACT", + "requiredDocument": "controlled Plant Ortho DWG with one-to-one source view mapping", + "requiredObjects": ["exact viewport handle/ObjectId", "layout membership", "Ortho view identity", "visible Plant candidates"], + "selection": "validated AcDbViewport handle and expected Ortho mapping", + "preState": "viewport and annotation baseline", + "mutation": "view annotation/viewport maintenance operation", + "repeat": "deterministic update/no duplicate", + "undo": "native AutoCAD UNDO restores viewport/entity baseline", + "workflows": ["PXP-AAV", "PXP-COV", "PXP-UOB", "PXP-DJ"], + "status": "EXACT_VIEWPORT_FIXTURE_BLOCKED" + }, + { + "id": "MANUAL_TEMPLATE_EDIT", + "requiredDocument": "controlled local Template-derived drawing", + "requiredObjects": ["manual annotation blocks", "attributes", "dynamic properties", "required layers/styles", "owned P_REC_ANNOT_MANUAL XData"], + "selection": "exact owned annotation handle for edit/reopen", + "preState": "palette state and owned annotation baseline", + "mutation": "manual insert/update/attribute persistence", + "repeat": "restore and update existing owned annotation", + "undo": "native AutoCAD UNDO restores entity and manual-state baseline", + "workflows": ["mto-ma", "PXP-MA", "CAC", "CAC1", "BJ", "PXP-MAB", "PXP-AC"] + }, + { + "id": "ORTHO_BATCH_SAME_NAME", + "requiredDocument": "controlled project copy with two relative drawing paths", + "requiredObjects": ["same filename in different project-relative directories", "one failing controlled drawing"], + "selection": "project-relative identity, never filename-only", + "preState": "unchanged controlled copies", + "mutation": "per-drawing update with continue-on-error", + "repeat": "deterministic ordering and idempotent result", + "undo": "per-drawing recovery/cleanup", + "workflows": ["PXP-IOD"] + } + ] +} diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs index 1e5d8b6..46f4037 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Core/ProductionSettingsContract.cs @@ -1,5 +1,6 @@ using System.Globalization; using System.Xml.Linq; +using PRecAnnot.Abstractions; namespace PRecAnnot.Core; @@ -21,6 +22,18 @@ public sealed record ProductionSettingsContract( LineNumberMarkerSettings? LineNumberMarker, SupportTopViewSettings? SupportTopView) { + public LineNumberMarkerAsset? ToLineNumberMarkerAsset() + { + if (LineNumberMarker is null) return null; + var texts = LineNumberMarker.Text is { } text + ? new[] { new LineNumberTextAsset(text.Layer ?? string.Empty, text.TextStyle ?? string.Empty, text.Size ?? 0, text.TextValue ?? string.Empty, new(text.PositionX ?? 0, text.PositionY ?? 0)) } + : Array.Empty(); + var lines = LineNumberMarker.Line is { } line + ? new[] { new LineNumberLineAsset(line.Layer ?? string.Empty, new(line.StartX ?? 0, line.StartY ?? 0), new(line.EndX ?? 0, line.EndY ?? 0)) } + : Array.Empty(); + return new(LineNumberMarker.Layer, LineNumberMarker.MaskScaleFactor ?? 0, texts, lines); + } + public static ProductionSettingsContract Parse(string xml) { var root = XElement.Parse(xml, LoadOptions.PreserveWhitespace); @@ -104,5 +117,13 @@ public sealed record LayerSetupRule(string FilterType, string FilterValue, int? public sealed record LineNumberMarkerSettings(string Layer, double? MaskScaleFactor, LineGeometrySettings? Line, TextAnnotationSettings? Text); public sealed record LineGeometrySettings(double? StartX, double? StartY, double? EndX, double? EndY, string? Layer); public sealed record TextAnnotationSettings(string? Layer, string? TextStyle, double? Size, string? TextValue, double? PositionX, double? PositionY); -public sealed record SupportTopViewSettings(string? UndefinedSize, IReadOnlyList Rules); +public sealed record SupportTopViewSettings(string? UndefinedSize, IReadOnlyList Rules) +{ + public SupportRule? Resolve(double nominalDiameter, string nominalUnit, string? selectionValue = null) => Rules + .Where(rule => rule.NominalDiameter is not null && Math.Abs(rule.NominalDiameter.Value - nominalDiameter) < 0.0001) + .Where(rule => string.Equals(rule.NominalUnit, nominalUnit, StringComparison.OrdinalIgnoreCase)) + .Where(rule => selectionValue is null || string.Equals(rule.SelectionValue, selectionValue, StringComparison.Ordinal)) + .OrderBy(rule => rule.LastDigit ?? int.MaxValue) + .FirstOrDefault(); +} public sealed record SupportRule(string ScriptName, double? NominalDiameter, string? NominalUnit, int? LastDigit, double? Scale, string? SelectionValue); diff --git a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs index 4c002c4..28296cb 100644 --- a/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs +++ b/Plant3D2026/P_Rec_Annot/tests/PRecAnnot.Core.Tests/ProductionSettingsContractTests.cs @@ -65,5 +65,16 @@ public sealed class ProductionSettingsContractTests Assert.Equal(50.0, rule.NominalDiameter); Assert.Equal(0.04, rule.Scale); Assert.Equal("DN50", rule.SelectionValue); + Assert.Same(rule, result.SupportTopView.Resolve(50, "mm", "DN50")); + } + + [Fact] + public void BridgesTypedMarkerIntoProductionAsset() + { + const string xml = "
"; + var asset = ProductionSettingsContract.Parse(xml).ToLineNumberMarkerAsset(); + Assert.NotNull(asset); + Assert.Equal("DIM", asset!.Layer); + Assert.Equal(-1.0607, asset.Lines[0].Start.X); } }