using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.ProcessPower.PlantInstance; using PRecAnnot.Abstractions; using PRecAnnot.Core; using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application; namespace PRecAnnot.Plant3D2026; // PXP-ULN refreshes the source-backed text of already-owned annotations. The // legacy command drives the same operation across selected Ortho drawings; the // modern command keeps the mutation pipeline scoped to the active controlled // drawing until the Ortho drawing enumerator is ported. internal static class LineNumberRefreshCommand { public static void Run() { var document = AcadApplication.DocumentManager.MdiActiveDocument; if (document is null) return; try { var configuration = LineNumberRuntimeConfigurationReader.Read(); var definition = configuration.Annotations.Annotations.OrderBy(value => value.Nickname, StringComparer.Ordinal).First(); var manager = PlantApplication.ProjectPart("Piping")?.DataLinksManager ?? throw new InvalidOperationException("LINE_NUMBER_PIPING_DATALINKS_UNAVAILABLE"); var owned = new LineNumberOwnedAnnotationReader().ReadMarkers(document); if (owned.Count == 0) throw new InvalidOperationException("LINE_NUMBER_OWNED_MARKERS_NOT_FOUND"); var candidates = owned.Select(row => ReadCandidate(document.Database, row, manager, configuration.Assets)).ToArray(); var preview = new LineNumberCommandPreviewService( new LineNumberCommandPlanner(new LineNumberAnnotationFormatter()), new LineNumberFieldResolver(new PlantDataLinksPropertyReader(manager))) .Preview(definition, candidates, owned.Select(row => new ExistingLineNumberAnnotation(row.SourceStableId, row.SourceStableId, row.Text))); if (!preview.Success) throw new InvalidOperationException(preview.FailureCode); var rows = owned.ToDictionary(row => row.SourceStableId, StringComparer.OrdinalIgnoreCase); var apply = preview.Plan!.Operations.Where(operation => operation.Kind is AnnotationOperationKind.Create or AnnotationOperationKind.Update) .Select(operation => new LineNumberApplyPreflight(operation.StableId, rows[operation.StableId].MarkerNumber, operation.Text!, operation.Position, operation.Rotation, operation.Layer, operation.TextStyle)) .OrderBy(operation => operation.MarkerNumber).ToArray(); var writer = new LineNumberAnnotationWriter(); var markerResult = writer.Apply(document, apply, configuration.Assets); var mutations = apply.Select(item => new LineNumberRowMutation(item.StableId, rows[item.StableId].MarkerNumber, item.MarkerNumber, item.Text, null)).ToArray(); var listResult = writer.ApplyEdits(document, mutations); document.Editor.WriteMessage($"\nPXP-ULN: updated markers={markerResult.Updated}; lists={listResult.Updated}; already-current={markerResult.Unchanged}."); RuntimeDiagnostics.Write("LineNumberRefreshCompleted", commandName: "PXP-ULN", resultStatus: markerResult.Updated > 0 ? "Updated" : "AlreadyCurrent", document: document, details: new Dictionary { ["UpdatedMarkers"] = markerResult.Updated, ["UpdatedLists"] = listResult.Updated, ["AlreadyCurrent"] = markerResult.Unchanged, ["Scope"] = "ActiveDrawing" }); } catch (System.Exception exception) { RuntimeDiagnostics.Write("LineNumberRefreshFailed", "ERROR", commandName: "PXP-ULN", resultStatus: exception.Message, exception: exception, document: document); document.Editor.WriteMessage($"\nPXP-ULN: {exception.Message}"); } } private static LineNumberCandidate ReadCandidate(Database database, LineNumberOwnedRow row, Autodesk.ProcessPower.DataLinks.DataLinksManager manager, AnnotateLineNumbersAssets assets) { const string prefix = "acad:"; if (!row.SourceStableId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("LINE_NUMBER_SOURCE_ID_UNSUPPORTED"); if (!long.TryParse(row.SourceStableId[prefix.Length..], System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out var handle)) throw new InvalidOperationException("LINE_NUMBER_SOURCE_HANDLE_INVALID"); var id = database.GetObjectId(false, new Handle(handle), 0); var read = new PlantLineNumberCandidateReader(manager).Read(id, assets.Marker.Layer, assets.Marker.Texts[0].TextStyle); if (!read.Success) throw new InvalidOperationException(read.FailureCode); return read.Candidate!; } }