fix(abcip): explicit IsArray flag so 1-element arrays read as arrays (review I-1)

This commit is contained in:
Joseph Doherty
2026-06-16 22:14:41 -04:00
parent ce5d46be08
commit 94e8c55b5c
6 changed files with 172 additions and 42 deletions
@@ -135,10 +135,14 @@ public sealed record AbCipDeviceOptions(
/// GuardLogix controller; non-safety writes violate the safety-partition isolation and are
/// rejected by the PLC anyway. Surfaces the intent explicitly instead of relying on the
/// write attempt failing at runtime.</param>
/// <param name="ElementCount">Phase 4c — number of array elements for a 1-D array tag. Defaults
/// to 1 (scalar). When greater than 1 the tag discovers as an OPC UA array node
/// (<c>IsArray</c> + <c>ArrayDim</c>) and reads via libplctag's <c>elem_count</c> into an
/// element-typed CLR array. Ignored for <see cref="AbCipDataType.Structure"/>.</param>
/// <param name="ElementCount">Phase 4c — number of array elements for a 1-D array tag. Reads via
/// libplctag's <c>elem_count</c> into an element-typed CLR array when <paramref name="IsArray"/>
/// is set; <c>1</c> for a scalar. Ignored for <see cref="AbCipDataType.Structure"/>.</param>
/// <param name="IsArray">Review I-1 — the EXPLICIT array signal. <c>true</c> ⟺ the source TagConfig
/// had <c>isArray:true</c> (with <c>arrayLength &gt;= 1</c>); the tag discovers as an OPC UA
/// array node (<c>IsArray</c> + <c>ArrayDim</c>) and reads as a typed CLR array — even when
/// <paramref name="ElementCount"/> is 1 (a valid 1-element array). <c>ElementCount</c> alone
/// cannot carry this because a scalar and a 1-element array both have a count of 1.</param>
public sealed record AbCipTagDefinition(
string Name,
string DeviceHostAddress,
@@ -148,7 +152,8 @@ public sealed record AbCipTagDefinition(
bool WriteIdempotent = false,
IReadOnlyList<AbCipStructureMember>? Members = null,
bool SafetyTag = false,
int ElementCount = 1);
int ElementCount = 1,
bool IsArray = false);
/// <summary>
/// One declared member of a UDT tag. Name is the member identifier on the PLC (e.g. <c>Speed</c>,
@@ -156,12 +161,20 @@ public sealed record AbCipTagDefinition(
/// <see cref="AbCipTagDefinition"/>. Declaration-driven — the real CIP Template Object reader
/// (class 0x6C) that would auto-discover member layouts lands as a follow-up PR.
/// </summary>
/// <param name="Name">The member identifier on the PLC.</param>
/// <param name="DataType">The atomic Logix type of the member.</param>
/// <param name="Writable">Whether the member is writable.</param>
/// <param name="WriteIdempotent">Whether writes to the member are idempotent.</param>
/// <param name="ElementCount">Number of array elements for a 1-D array member; <c>1</c> for scalar.</param>
/// <param name="IsArray">Review I-1 — the EXPLICIT array signal for a member: <c>true</c> ⟺ the
/// member is a 1-D array (even of length 1). Discovers as an OPC UA array node when set.</param>
public sealed record AbCipStructureMember(
string Name,
AbCipDataType DataType,
bool Writable = true,
bool WriteIdempotent = false,
int ElementCount = 1);
int ElementCount = 1,
bool IsArray = false);
/// <summary>Which AB PLC family the device is — selects the profile applied to connection params.</summary>
public enum AbCipPlcFamily