feat(abcip): 1-D array read via libplctag + IsArray discovery
This commit is contained in:
@@ -135,6 +135,10 @@ 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>
|
||||
public sealed record AbCipTagDefinition(
|
||||
string Name,
|
||||
string DeviceHostAddress,
|
||||
@@ -143,7 +147,8 @@ public sealed record AbCipTagDefinition(
|
||||
bool Writable = true,
|
||||
bool WriteIdempotent = false,
|
||||
IReadOnlyList<AbCipStructureMember>? Members = null,
|
||||
bool SafetyTag = false);
|
||||
bool SafetyTag = false,
|
||||
int ElementCount = 1);
|
||||
|
||||
/// <summary>
|
||||
/// One declared member of a UDT tag. Name is the member identifier on the PLC (e.g. <c>Speed</c>,
|
||||
@@ -155,7 +160,8 @@ public sealed record AbCipStructureMember(
|
||||
string Name,
|
||||
AbCipDataType DataType,
|
||||
bool Writable = true,
|
||||
bool WriteIdempotent = false);
|
||||
bool WriteIdempotent = false,
|
||||
int ElementCount = 1);
|
||||
|
||||
/// <summary>Which AB PLC family the device is — selects the profile applied to connection params.</summary>
|
||||
public enum AbCipPlcFamily
|
||||
|
||||
@@ -31,9 +31,13 @@ public static class AbCipEquipmentTagParser
|
||||
|
||||
var deviceHostAddress = ReadString(root, "deviceHostAddress");
|
||||
var dataType = ReadEnum(root, "dataType", AbCipDataType.DInt);
|
||||
// Phase 4c — an isArray equipment tag carries arrayLength; thread it into the def's
|
||||
// ElementCount so the read pulls the whole array via libplctag elem_count. When
|
||||
// isArray is absent/false (or arrayLength is missing/<=1) the tag stays scalar.
|
||||
var elementCount = ReadArrayElementCount(root);
|
||||
def = new AbCipTagDefinition(
|
||||
Name: reference, DeviceHostAddress: deviceHostAddress, TagPath: tagPath,
|
||||
DataType: dataType, Writable: true);
|
||||
DataType: dataType, Writable: true, ElementCount: elementCount);
|
||||
return true;
|
||||
}
|
||||
catch (JsonException) { return false; }
|
||||
@@ -41,6 +45,23 @@ public static class AbCipEquipmentTagParser
|
||||
catch (InvalidOperationException) { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolve the 1-D array element count from an <c>isArray</c> / <c>arrayLength</c> pair.
|
||||
/// Returns 1 (scalar) unless <c>isArray</c> is truthy AND <c>arrayLength</c> is a number
|
||||
/// greater than 1; matches the sink's "isArray + arrayLength" carrier.
|
||||
/// </summary>
|
||||
private static int ReadArrayElementCount(JsonElement o)
|
||||
{
|
||||
var isArray = o.TryGetProperty("isArray", out var a) && a.ValueKind == JsonValueKind.True;
|
||||
if (!isArray) return 1;
|
||||
if (o.TryGetProperty("arrayLength", out var len)
|
||||
&& len.ValueKind == JsonValueKind.Number
|
||||
&& len.TryGetInt32(out var n)
|
||||
&& n > 1)
|
||||
return n;
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static TEnum ReadEnum<TEnum>(JsonElement o, string name, TEnum fallback) where TEnum : struct, Enum
|
||||
=> o.TryGetProperty(name, out var e) && e.ValueKind == JsonValueKind.String
|
||||
&& Enum.TryParse<TEnum>(e.GetString(), ignoreCase: true, out var v) ? v : fallback;
|
||||
|
||||
Reference in New Issue
Block a user