feat: add p-rec-annot datalinks line-number read path

This commit is contained in:
codex
2026-08-07 08:18:55 +03:00
parent 8e197cdcde
commit b6a2cd06af
4 changed files with 57 additions and 0 deletions
@@ -52,6 +52,10 @@ public interface ILineNumberAnnotationFormatter
LineNumberFormattingResult Format(LineNumberAnnotationDefinition definition, IReadOnlyDictionary<string, string?> fields); LineNumberFormattingResult Format(LineNumberAnnotationDefinition definition, IReadOnlyDictionary<string, string?> fields);
} }
public sealed record DataLinksPropertyReadResult(bool Success, IReadOnlyDictionary<string, string?> Properties, string? FailureCode = null);
public interface IDataLinksPropertyReader { DataLinksPropertyReadResult ReadProperties(long rowId); }
public sealed record LineNumberFieldResolution(bool Success, IReadOnlyDictionary<string, string?> Values, string? FailureCode = null);
public sealed record GeometrySnapshot public sealed record GeometrySnapshot
{ {
public Point3Dto? WorldPosition { get; init; } public Point3Dto? WorldPosition { get; init; }
@@ -0,0 +1,23 @@
using PRecAnnot.Abstractions;
namespace PRecAnnot.Core;
public sealed class LineNumberFieldResolver(IDataLinksPropertyReader reader)
{
// Source-backed Convert_field_to_value port. The adapter supplies a
// deterministic, read-only flattened key set: Table.Column and Column.
public LineNumberFieldResolution Resolve(long rowId, IEnumerable<string> fields)
{
var read = reader.ReadProperties(rowId);
if (!read.Success) return new(false, new Dictionary<string, string?>(), read.FailureCode ?? "DATALINKS_READ_FAILED");
var values = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
foreach (var field in fields.Distinct(StringComparer.OrdinalIgnoreCase))
{
if (string.IsNullOrWhiteSpace(field)) return new(false, values, "LINE_NUMBER_FIELD_EMPTY");
var dot = field.IndexOf('.', StringComparison.Ordinal);
if (dot <= 0 || dot == field.Length - 1) return new(false, values, "LINE_NUMBER_FIELD_INVALID:" + field);
if (!read.Properties.TryGetValue(field, out var value) && !read.Properties.TryGetValue(field[(dot + 1)..], out value)) return new(false, values, "LINE_NUMBER_COLUMN_MISSING:" + field);
values[field] = value ?? string.Empty;
}
return new(true, values);
}
}
@@ -0,0 +1,20 @@
using Autodesk.ProcessPower.DataLinks;
using PRecAnnot.Abstractions;
namespace PRecAnnot.Plant3D2026;
internal sealed class PlantDataLinksPropertyReader(DataLinksManager dataLinks) : IDataLinksPropertyReader
{
public DataLinksPropertyReadResult ReadProperties(long rowId)
{
if (rowId <= 0) return new(false, new Dictionary<string, string?>(), "DATALINKS_ROW_ID_INVALID");
try
{
var values = dataLinks.GetAllProperties((int)rowId, true)
.GroupBy(x => x.Key, StringComparer.OrdinalIgnoreCase)
.ToDictionary(x => x.Key, x => (string?)x.Last().Value, StringComparer.OrdinalIgnoreCase);
return new(true, values);
}
catch (DLException) { return new(false, new Dictionary<string, string?>(), "DATALINKS_PROPERTY_READ_FAILED"); }
catch (Exception) { return new(false, new Dictionary<string, string?>(), "DATALINKS_PROPERTY_READ_FAILED"); }
}
}
@@ -0,0 +1,10 @@
using PRecAnnot.Abstractions;
using PRecAnnot.Core;
namespace PRecAnnot.Core.Tests;
public sealed class LineNumberFieldResolverTests
{
[Fact] public void ResolvesNullAsLegacyEmptyString() => Assert.Equal(string.Empty, new LineNumberFieldResolver(new Reader(true, new Dictionary<string,string?> { ["EngineeringItems.LineNumber"] = null })).Resolve(5, ["EngineeringItems.LineNumber"]).Values["EngineeringItems.LineNumber"]);
[Fact] public void RejectsInvalidField() => Assert.Equal("LINE_NUMBER_FIELD_INVALID:LineNumber", new LineNumberFieldResolver(new Reader(true, new Dictionary<string, string?>())).Resolve(5, ["LineNumber"]).FailureCode);
[Fact] public void PropagatesAdapterFailure() => Assert.Equal("DATALINKS_PROPERTY_READ_FAILED", new LineNumberFieldResolver(new Reader(false, new Dictionary<string, string?>())).Resolve(5, ["EngineeringItems.LineNumber"]).FailureCode);
private sealed record Reader(bool Ok, IReadOnlyDictionary<string,string?> Values) : IDataLinksPropertyReader { public DataLinksPropertyReadResult ReadProperties(long _) => new(Ok, Values, Ok ? null : "DATALINKS_PROPERTY_READ_FAILED"); }
}