feat: implement first limited Pipe annotation Apply
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
using System.Text.Json;
|
||||
using Autodesk.AutoCAD.ApplicationServices;
|
||||
using Autodesk.AutoCAD.DatabaseServices;
|
||||
using Autodesk.AutoCAD.Geometry;
|
||||
using PRecAnnot.Abstractions;
|
||||
|
||||
namespace PRecAnnot.Plant3D2026;
|
||||
|
||||
// This file is deliberately runtime-only. Core receives only the anonymised
|
||||
// permit and canonical operation; no AutoCAD handle, database or project path
|
||||
// crosses the adapter boundary.
|
||||
internal sealed record FirstLimitedApplyRuntimePermit
|
||||
{
|
||||
public required bool AllowApply { get; init; }
|
||||
public required string FixtureMarkerPath { get; init; }
|
||||
public required string TargetSnapshotId { get; init; }
|
||||
public required string ExpectedPlanId { get; init; }
|
||||
public required string VisibilityState { get; init; }
|
||||
}
|
||||
|
||||
internal static class FirstLimitedApplyPermitReader
|
||||
{
|
||||
private const string MarkerContent = "P_REC_ANNOT_TEST_FIXTURE=FIRST_LIMITED_APPLY";
|
||||
|
||||
public static FirstLimitedApplyRuntimePermit ReadFor(Document document)
|
||||
{
|
||||
var path = Environment.GetEnvironmentVariable("P_REC_ANNOT_APPLY_PERMIT_PATH");
|
||||
if (string.IsNullOrWhiteSpace(path) || !Path.IsPathFullyQualified(path) || path.StartsWith("\\\\", StringComparison.Ordinal))
|
||||
throw new InvalidOperationException("APPLY_PERMIT_PATH_REQUIRED");
|
||||
if (!File.Exists(path)) throw new InvalidOperationException("APPLY_PERMIT_MISSING");
|
||||
|
||||
var permit = JsonSerializer.Deserialize<FirstLimitedApplyRuntimePermit>(File.ReadAllText(path), new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
|
||||
if (permit is null || !permit.AllowApply || string.IsNullOrWhiteSpace(permit.TargetSnapshotId) || string.IsNullOrWhiteSpace(permit.ExpectedPlanId) || string.IsNullOrWhiteSpace(permit.VisibilityState))
|
||||
throw new InvalidOperationException("APPLY_TEST_FIXTURE_OR_PERMIT_REQUIRED");
|
||||
if (!Path.IsPathFullyQualified(permit.FixtureMarkerPath) || permit.FixtureMarkerPath.StartsWith("\\\\", StringComparison.Ordinal) || !File.Exists(permit.FixtureMarkerPath))
|
||||
throw new InvalidOperationException("APPLY_TEST_FIXTURE_OR_PERMIT_REQUIRED");
|
||||
if (!string.Equals(File.ReadAllText(permit.FixtureMarkerPath).Trim(), MarkerContent, StringComparison.Ordinal))
|
||||
throw new InvalidOperationException("APPLY_TEST_FIXTURE_OR_PERMIT_REQUIRED");
|
||||
|
||||
var root = Path.GetDirectoryName(Path.GetFullPath(permit.FixtureMarkerPath))!;
|
||||
var drawing = Path.GetFullPath(document.Name);
|
||||
if (!drawing.StartsWith(root + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("APPLY_TEST_FIXTURE_OR_PERMIT_REQUIRED");
|
||||
return permit;
|
||||
}
|
||||
}
|
||||
|
||||
internal enum PipeAnnotationWriteOutcome { Created, AlreadyExists }
|
||||
|
||||
internal sealed class FirstLimitedPipeAnnotationWriter
|
||||
{
|
||||
private const string BlockName = "PIPE";
|
||||
private const string XdataApplication = "PXP-OA";
|
||||
|
||||
public PipeAnnotationWriteOutcome Write(Document document, CreatePipeAnnotationOperation operation, string visibilityState)
|
||||
{
|
||||
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 paper = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.PaperSpace], OpenMode.ForWrite);
|
||||
if (HasOwnedAnnotation(transaction, paper, operation.DuplicateKey)) return PipeAnnotationWriteOutcome.AlreadyExists;
|
||||
EnsureRegApp(transaction, database);
|
||||
|
||||
using var annotation = new BlockReference(
|
||||
new Point3d(operation.Placement.X, operation.Placement.Y, operation.Placement.Z),
|
||||
table[BlockName]);
|
||||
paper.AppendEntity(annotation);
|
||||
transaction.AddNewlyCreatedDBObject(annotation, true);
|
||||
SetConfirmedVisibility(annotation, visibilityState);
|
||||
annotation.XData = new ResultBuffer(
|
||||
new TypedValue((int)DxfCode.ExtendedDataRegAppName, XdataApplication),
|
||||
new TypedValue((int)DxfCode.ExtendedDataAsciiString, operation.DuplicateKey),
|
||||
new TypedValue((int)DxfCode.ExtendedDataAsciiString, operation.RuleId));
|
||||
transaction.Commit();
|
||||
return PipeAnnotationWriteOutcome.Created;
|
||||
}
|
||||
|
||||
private static bool HasOwnedAnnotation(Transaction transaction, BlockTableRecord paper, string duplicateKey)
|
||||
{
|
||||
foreach (ObjectId id in paper)
|
||||
{
|
||||
if (transaction.GetObject(id, OpenMode.ForRead) is not BlockReference reference) continue;
|
||||
using var xdata = reference.GetXDataForApplication(XdataApplication);
|
||||
if (xdata?.AsArray().Any(value => value.TypeCode == (int)DxfCode.ExtendedDataAsciiString && string.Equals(value.Value as string, duplicateKey, StringComparison.Ordinal)) == true)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void EnsureRegApp(Transaction transaction, Database database)
|
||||
{
|
||||
var apps = (RegAppTable)transaction.GetObject(database.RegAppTableId, OpenMode.ForRead);
|
||||
if (apps.Has(XdataApplication)) return;
|
||||
apps.UpgradeOpen();
|
||||
using var record = new RegAppTableRecord { Name = XdataApplication };
|
||||
apps.Add(record);
|
||||
transaction.AddNewlyCreatedDBObject(record, true);
|
||||
}
|
||||
|
||||
// The "V" property and allowed values are the confirmed legacy PXP-OA
|
||||
// dynamic-block contract. Failure is pre-commit and leaves no entity.
|
||||
private static void SetConfirmedVisibility(BlockReference annotation, string visibilityState)
|
||||
{
|
||||
var property = annotation.DynamicBlockReferencePropertyCollection
|
||||
.Cast<DynamicBlockReferenceProperty>()
|
||||
.SingleOrDefault(value => string.Equals(value.PropertyName, "V", StringComparison.Ordinal));
|
||||
if (property is null || !property.GetAllowedValues().OfType<string>().Contains(visibilityState, StringComparer.Ordinal))
|
||||
throw new InvalidOperationException("ANNOTATION_VISIBILITY_STATE_UNSUPPORTED");
|
||||
property.Value = visibilityState;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user