diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs index c686c8b..e984845 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/EntryPoint.cs @@ -200,7 +200,7 @@ public sealed class EntryPoint : IExtensionApplication new RuntimeWriteGuard(mode).EnsureWriteAllowed(preflight.Operation.OperationId); RuntimeDiagnostics.Write("ApplyPreflightCompleted", operationId: operationId, commandName: commandName, resultStatus: "Accepted", document: document); RuntimeDiagnostics.Write("AnnotationTransactionStarted", operationId: operationId, commandName: commandName, document: document); - var outcome = new FirstLimitedPipeAnnotationWriter().Write(document!, preflight.Operation, applyPermit.VisibilityState); + var outcome = new FirstLimitedPipeAnnotationWriter().Write(document!, preflight.Operation, applyPermit.VisibilityState, applyPermit.AnnotationTemplateDwgPath); ScriptDrivenFirstApplyAutomation.RecordApplyOutcome(outcome); FirstLimitedApplyAutomationCoordinator.RecordApplyOutcome(outcome); details["ApplyOutcome"] = outcome.ToString(); diff --git a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/FirstLimitedApplyRuntime.cs b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/FirstLimitedApplyRuntime.cs index 855982a..a3e693f 100644 --- a/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/FirstLimitedApplyRuntime.cs +++ b/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/FirstLimitedApplyRuntime.cs @@ -16,6 +16,9 @@ internal sealed record FirstLimitedApplyRuntimePermit public required string TargetSnapshotId { get; init; } public required string ExpectedPlanId { get; init; } public required string VisibilityState { get; init; } + // Test-only local copy of the legacy v1.0.4 Template.dwg. The package + // deliberately carries no DWG assets; the per-run permit binds this source. + public required string AnnotationTemplateDwgPath { get; init; } } internal static class FirstLimitedApplyPermitReader @@ -41,6 +44,8 @@ internal static class FirstLimitedApplyPermitReader var drawing = Path.GetFullPath(document.Name); if (!drawing.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("APPLY_TEST_FIXTURE_OR_PERMIT_REQUIRED"); + if (string.IsNullOrWhiteSpace(permit.AnnotationTemplateDwgPath) || !Path.IsPathFullyQualified(permit.AnnotationTemplateDwgPath) || permit.AnnotationTemplateDwgPath.StartsWith("\\\\", StringComparison.Ordinal) || !File.Exists(permit.AnnotationTemplateDwgPath)) + throw new InvalidOperationException("ANNOTATION_TEMPLATE_ASSET_REQUIRED"); return permit; } } @@ -52,12 +57,12 @@ internal sealed class FirstLimitedPipeAnnotationWriter private const string BlockName = "PIPE"; private const string XdataApplication = "PXP-OA"; - public PipeAnnotationWriteOutcome Write(Document document, CreatePipeAnnotationOperation operation, string visibilityState) + public PipeAnnotationWriteOutcome Write(Document document, CreatePipeAnnotationOperation operation, string visibilityState, string annotationTemplateDwgPath) { var database = document.Database; using var transaction = database.TransactionManager.StartTransaction(); var table = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead); - if (!table.Has(BlockName)) throw new InvalidOperationException("ANNOTATION_TEMPLATE_BLOCK_MISSING"); + var blockId = EnsureLegacyPipeBlock(transaction, database, table, annotationTemplateDwgPath); var paper = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.PaperSpace], OpenMode.ForWrite); if (HasOwnedAnnotation(transaction, paper, operation.DuplicateKey)) return PipeAnnotationWriteOutcome.AlreadyExists; @@ -65,7 +70,7 @@ internal sealed class FirstLimitedPipeAnnotationWriter using var annotation = new BlockReference( new Point3d(operation.Placement.X, operation.Placement.Y, operation.Placement.Z), - table[BlockName]); + blockId); paper.AppendEntity(annotation); transaction.AddNewlyCreatedDBObject(annotation, true); SetConfirmedVisibility(annotation, visibilityState); @@ -77,6 +82,27 @@ internal sealed class FirstLimitedPipeAnnotationWriter return PipeAnnotationWriteOutcome.Created; } + private static ObjectId EnsureLegacyPipeBlock(Transaction destinationTransaction, Database destination, BlockTable destinationTable, string templatePath) + { + if (destinationTable.Has(BlockName)) return destinationTable[BlockName]; + if (!File.Exists(templatePath)) throw new InvalidOperationException("ANNOTATION_TEMPLATE_ASSET_MISSING"); + + using var source = new Database(false, true); + source.ReadDwgFile(templatePath, FileShare.Read, true, string.Empty); + source.CloseInput(true); + using var sourceTransaction = source.TransactionManager.StartTransaction(); + var sourceTable = (BlockTable)sourceTransaction.GetObject(source.BlockTableId, OpenMode.ForRead); + if (!sourceTable.Has(BlockName)) throw new InvalidOperationException("ANNOTATION_TEMPLATE_PIPE_BLOCK_MISSING"); + using var sourceIds = new ObjectIdCollection { sourceTable[BlockName] }; + using var mapping = new IdMapping(); + destination.WblockCloneObjects(sourceIds, destination.BlockTableId, mapping, DuplicateRecordCloning.Ignore, false); + sourceTransaction.Commit(); + + var refreshed = (BlockTable)destinationTransaction.GetObject(destination.BlockTableId, OpenMode.ForRead); + if (!refreshed.Has(BlockName)) throw new InvalidOperationException("ANNOTATION_TEMPLATE_BLOCK_IMPORT_FAILED"); + return refreshed[BlockName]; + } + private static bool HasOwnedAnnotation(Transaction transaction, BlockTableRecord paper, string duplicateKey) { foreach (ObjectId id in paper)