feat: activate p-rec-annot line-number edit commands
This commit is contained in:
@@ -41,9 +41,9 @@ public sealed class EntryPoint : IExtensionApplication
|
|||||||
|
|
||||||
[CommandMethod("PXP-LN")] public void AnnotateLineNumbersCommand() => RunLineNumberPreview();
|
[CommandMethod("PXP-LN")] public void AnnotateLineNumbersCommand() => RunLineNumberPreview();
|
||||||
[CommandMethod("PXP-ULN")] public void UpdateLineNumbersCommand() => RunDiagnostic("PXP-ULN");
|
[CommandMethod("PXP-ULN")] public void UpdateLineNumbersCommand() => RunDiagnostic("PXP-ULN");
|
||||||
[CommandMethod("PXP-ESN")] public void EditLineNumberStartCommand() => RunDiagnostic("PXP-ESN");
|
[CommandMethod("PXP-ESN")] public void EditLineNumberStartCommand() => LineNumberEditCommands.Renumber();
|
||||||
[CommandMethod("PXP-ELN")] public void EditLineNumbersCommand() => RunDiagnostic("PXP-ELN");
|
[CommandMethod("PXP-ELN")] public void EditLineNumbersCommand() => LineNumberEditCommands.EditText();
|
||||||
[CommandMethod("PXP-RMP")] public void ResetMarkerPositionCommand() => RunDiagnostic("PXP-RMP");
|
[CommandMethod("PXP-RMP")] public void ResetMarkerPositionCommand() => LineNumberEditCommands.ResetMarkerPosition();
|
||||||
|
|
||||||
[CommandMethod("PHP_REC_AUTOMATION_START", CommandFlags.Session)]
|
[CommandMethod("PHP_REC_AUTOMATION_START", CommandFlags.Session)]
|
||||||
public void AutomationStartCommand() => FirstLimitedApplyAutomationBootstrap.Arm();
|
public void AutomationStartCommand() => FirstLimitedApplyAutomationBootstrap.Arm();
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using Autodesk.AutoCAD.ApplicationServices;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using Autodesk.AutoCAD.EditorInput;
|
||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
using PRecAnnot.Core;
|
||||||
|
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Plant3D2026;
|
||||||
|
|
||||||
|
internal static class LineNumberEditCommands
|
||||||
|
{
|
||||||
|
public static void Renumber()
|
||||||
|
{
|
||||||
|
Run("PXP-ESN", (document, editor, selected) =>
|
||||||
|
{
|
||||||
|
var start = editor.GetInteger(new PromptIntegerOptions("\nВведите новое начальное значение маркера: ") { LowerLimit = 1 });
|
||||||
|
if (start.Status != PromptStatus.OK) return null;
|
||||||
|
var rows = new LineNumberOwnedAnnotationReader().ReadList(document, selected);
|
||||||
|
return new LineNumberAnnotationWriter().ApplyEdits(document, LineNumberEditPlanner.Renumber(rows, start.Value));
|
||||||
|
}, "Выберите блок списка номеров линий: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EditText()
|
||||||
|
{
|
||||||
|
Run("PXP-ELN", (document, editor, selected) =>
|
||||||
|
{
|
||||||
|
var rows = new LineNumberOwnedAnnotationReader().ReadList(document, selected);
|
||||||
|
var marker = editor.GetInteger(new PromptIntegerOptions("\nВведите номер редактируемого маркера: ") { LowerLimit = 1 });
|
||||||
|
if (marker.Status != PromptStatus.OK) return null;
|
||||||
|
var row = rows.SingleOrDefault(value => value.MarkerNumber == marker.Value) ?? throw new InvalidOperationException("LINE_NUMBER_ROW_NOT_FOUND");
|
||||||
|
var text = editor.GetString(new PromptStringOptions("\nВведите новый текст номера линии: ") { AllowSpaces = true });
|
||||||
|
if (text.Status != PromptStatus.OK) return null;
|
||||||
|
return new LineNumberAnnotationWriter().ApplyEdits(document, [LineNumberEditPlanner.EditText(row, text.StringResult)]);
|
||||||
|
}, "Выберите блок списка номеров линий: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ResetMarkerPosition()
|
||||||
|
{
|
||||||
|
Run("PXP-RMP", (document, _, selected) =>
|
||||||
|
{
|
||||||
|
var row = new LineNumberOwnedAnnotationReader().ReadMarker(document, selected);
|
||||||
|
var configured = LineNumberRuntimeConfigurationReader.Read().Assets.Marker.Texts[0].Position;
|
||||||
|
return new LineNumberAnnotationWriter().ApplyEdits(document, [LineNumberEditPlanner.ResetMarkerAttributePosition(row, configured)]);
|
||||||
|
}, "Выберите маркер номера линии: ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Run(string command, Func<Document, Editor, ObjectId, LineNumberApplyResult?> operation, string prompt)
|
||||||
|
{
|
||||||
|
var document = AcadApplication.DocumentManager.MdiActiveDocument;
|
||||||
|
if (document is null) return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var options = new PromptEntityOptions($"\n{prompt}");
|
||||||
|
options.SetRejectMessage("\nТребуется вхождение блока PXP-LN.");
|
||||||
|
options.AddAllowedClass(typeof(BlockReference), true);
|
||||||
|
var selected = document.Editor.GetEntity(options);
|
||||||
|
if (selected.Status != PromptStatus.OK) return;
|
||||||
|
var result = operation(document, document.Editor, selected.ObjectId);
|
||||||
|
if (result is null) return;
|
||||||
|
document.Editor.WriteMessage($"\n{command}: updated={result.Updated}; unchanged={result.Unchanged}.");
|
||||||
|
RuntimeDiagnostics.Write("LineNumberEditCompleted", commandName: command, resultStatus: result.Updated > 0 ? "Updated" : "AlreadyCurrent", document: document,
|
||||||
|
details: new Dictionary<string, object?> { ["Updated"] = result.Updated, ["AlreadyCurrent"] = result.Unchanged });
|
||||||
|
}
|
||||||
|
catch (System.Exception exception)
|
||||||
|
{
|
||||||
|
RuntimeDiagnostics.Write("LineNumberEditFailed", "ERROR", commandName: command, resultStatus: exception.Message, exception: exception, document: document);
|
||||||
|
document.Editor.WriteMessage($"\n{command}: {exception.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using Autodesk.AutoCAD.ApplicationServices;
|
||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using Autodesk.AutoCAD.Geometry;
|
||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Plant3D2026;
|
||||||
|
|
||||||
|
internal sealed class LineNumberOwnedAnnotationReader
|
||||||
|
{
|
||||||
|
private const string ApplicationName = "PXP-LN";
|
||||||
|
|
||||||
|
public IReadOnlyList<LineNumberOwnedRow> ReadList(Document document, ObjectId listId)
|
||||||
|
{
|
||||||
|
using var transaction = document.Database.TransactionManager.StartOpenCloseTransaction();
|
||||||
|
if (transaction.GetObject(listId, OpenMode.ForRead) is not BlockReference list || !HasRole(list, "LIST"))
|
||||||
|
throw new InvalidOperationException("LINE_NUMBER_LIST_NOT_SELECTED");
|
||||||
|
|
||||||
|
var rows = new Dictionary<int, (string? StableId, string? Text)>();
|
||||||
|
foreach (ObjectId id in list.AttributeCollection)
|
||||||
|
{
|
||||||
|
var attribute = (AttributeReference)transaction.GetObject(id, OpenMode.ForRead);
|
||||||
|
var ownership = ReadOwnership(attribute);
|
||||||
|
if (ownership is null) continue;
|
||||||
|
rows.TryGetValue(ownership.Value.MarkerNumber, out var row);
|
||||||
|
rows[ownership.Value.MarkerNumber] = ownership.Value.Role == "LINENUMBER"
|
||||||
|
? (ownership.Value.StableId, attribute.TextString.Replace("\\P", "\n", StringComparison.Ordinal))
|
||||||
|
: (ownership.Value.StableId ?? row.StableId, row.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = rows.OrderBy(value => value.Key)
|
||||||
|
.Select(value => new LineNumberOwnedRow(
|
||||||
|
value.Value.StableId ?? throw new InvalidOperationException("LINE_NUMBER_ROW_STABLE_ID_MISSING"),
|
||||||
|
value.Key,
|
||||||
|
value.Value.Text ?? throw new InvalidOperationException("LINE_NUMBER_ROW_TEXT_MISSING"),
|
||||||
|
default))
|
||||||
|
.ToArray();
|
||||||
|
if (result.Length == 0) throw new InvalidOperationException("LINE_NUMBER_LIST_EMPTY");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LineNumberOwnedRow ReadMarker(Document document, ObjectId markerId)
|
||||||
|
{
|
||||||
|
using var transaction = document.Database.TransactionManager.StartOpenCloseTransaction();
|
||||||
|
if (transaction.GetObject(markerId, OpenMode.ForRead) is not BlockReference marker)
|
||||||
|
throw new InvalidOperationException("LINE_NUMBER_MARKER_NOT_SELECTED");
|
||||||
|
var values = marker.GetXDataForApplication(ApplicationName)?.AsArray() ?? [];
|
||||||
|
var stableId = values.Where(value => value.TypeCode == 1000).Select(value => value.Value as string)
|
||||||
|
.FirstOrDefault(value => !string.Equals(value, "MARKER", StringComparison.OrdinalIgnoreCase));
|
||||||
|
var markerNumber = values.Where(value => value.TypeCode == 1071).Select(value => Convert.ToInt32(value.Value)).FirstOrDefault();
|
||||||
|
var text = values.Where(value => value.TypeCode == 1000).Select(value => value.Value as string).LastOrDefault() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(stableId) || markerNumber <= 0) throw new InvalidOperationException("LINE_NUMBER_MARKER_OWNERSHIP_MISSING");
|
||||||
|
var localPosition = Point3d.Origin;
|
||||||
|
foreach (ObjectId id in marker.AttributeCollection)
|
||||||
|
{
|
||||||
|
var attribute = (AttributeReference)transaction.GetObject(id, OpenMode.ForRead);
|
||||||
|
if (!string.Equals(attribute.Tag, "NUMBER", StringComparison.OrdinalIgnoreCase)) continue;
|
||||||
|
localPosition = attribute.Position.TransformBy(marker.BlockTransform.Inverse());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new(stableId, markerNumber, text, new(localPosition.X, localPosition.Y, localPosition.Z));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasRole(BlockReference reference, string role) =>
|
||||||
|
reference.GetXDataForApplication(ApplicationName)?.AsArray().Any(value =>
|
||||||
|
value.TypeCode == 1000 && string.Equals(value.Value as string, role, StringComparison.OrdinalIgnoreCase)) == true;
|
||||||
|
|
||||||
|
private static (string? StableId, string Role, int MarkerNumber)? ReadOwnership(AttributeReference attribute)
|
||||||
|
{
|
||||||
|
var values = attribute.GetXDataForApplication(ApplicationName)?.AsArray();
|
||||||
|
if (values is null) return null;
|
||||||
|
var strings = values.Where(value => value.TypeCode == 1000).Select(value => value.Value as string).ToArray();
|
||||||
|
var role = strings.FirstOrDefault(value => value is "MARKER" or "LINENUMBER");
|
||||||
|
var number = values.Where(value => value.TypeCode == 1071).Select(value => Convert.ToInt32(value.Value)).FirstOrDefault();
|
||||||
|
return role is null || number <= 0 ? null : (strings.FirstOrDefault(value => value != role), role, number);
|
||||||
|
}
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Core.Tests;
|
||||||
|
|
||||||
|
public sealed class LineNumberEditCommandRegistrationTests
|
||||||
|
{
|
||||||
|
private static string ProjectRoot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||||
|
while (directory is not null && !File.Exists(Path.Combine(directory.FullName, "PRecAnnot2026.sln"))) directory = directory.Parent;
|
||||||
|
return Path.Combine(directory!.FullName, "src", "PRecAnnot.Plant3D2026");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("PXP-ESN", "LineNumberEditCommands.Renumber")]
|
||||||
|
[InlineData("PXP-ELN", "LineNumberEditCommands.EditText")]
|
||||||
|
[InlineData("PXP-RMP", "LineNumberEditCommands.ResetMarkerPosition")]
|
||||||
|
public void EditCommand_UsesMutationPipeline(string command, string handler)
|
||||||
|
{
|
||||||
|
var entry = File.ReadAllText(Path.Combine(ProjectRoot, "EntryPoint.cs"));
|
||||||
|
var declaration = entry.Split('\n').Single(value => value.Contains($"CommandMethod(\"{command}\")", StringComparison.Ordinal));
|
||||||
|
Assert.Contains(handler, declaration);
|
||||||
|
Assert.DoesNotContain("RunDiagnostic", declaration);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OwnedReader_RequiresPxpLnIdentityForListsAndMarkers()
|
||||||
|
{
|
||||||
|
var reader = File.ReadAllText(Path.Combine(ProjectRoot, "LineNumberOwnedAnnotationReader.cs"));
|
||||||
|
Assert.Contains("GetXDataForApplication(ApplicationName)", reader);
|
||||||
|
Assert.Contains("LINE_NUMBER_LIST_NOT_SELECTED", reader);
|
||||||
|
Assert.Contains("LINE_NUMBER_MARKER_OWNERSHIP_MISSING", reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MutationCommands_UseOneTransactionalWriter()
|
||||||
|
{
|
||||||
|
var commands = File.ReadAllText(Path.Combine(ProjectRoot, "LineNumberEditCommands.cs"));
|
||||||
|
Assert.Equal(3, System.Text.RegularExpressions.Regex.Matches(commands, "ApplyEdits\\(document").Count);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user