namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
///
/// PR abcip-1.3 — issues one libplctag tag-create with ElementCount=N per Rockwell
/// array-slice tag (Tag[0..N] in ), then decodes the
/// contiguous buffer at element stride into N typed values. Mirrors the whole-UDT
/// planner pattern (): pure shape — the planner never
/// touches the runtime + never reads the PLC, the driver wires the runtime in.
///
///
/// Stride is the natural Logix size of the element type (DInt = 4, Real = 4, LInt = 8).
/// Bool / String / Structure slices aren't supported here — Logix packs BOOLs into a host
/// byte (no fixed stride), STRING members carry a Length+DATA pair that's not a flat array,
/// and structure arrays need the CIP Template Object reader (PR-tracked separately).
///
/// Output is a single object[] snapshot value containing the N decoded
/// elements at indices 0..Count-1. Pairing with one slice tag = one snapshot keeps the
/// ReadAsync 1:1 contract (one fullReference -> one snapshot) intact.
///
public static class AbCipArrayReadPlanner
{
///
/// Build the libplctag create-params + decode descriptor for a slice tag. Returns
/// null when the slice element type isn't supported under this declaration-only
/// decoder (Bool / String / Structure / unrecognised) — the driver falls back to the
/// scalar read path so the operator gets a clean per-element result instead.
///
public static AbCipArrayReadPlan? TryBuild(
AbCipTagDefinition definition,
AbCipTagPath parsedPath,
AbCipTagCreateParams baseParams)
{
ArgumentNullException.ThrowIfNull(definition);
ArgumentNullException.ThrowIfNull(parsedPath);
ArgumentNullException.ThrowIfNull(baseParams);
if (parsedPath.Slice is null) return null;
if (!TryGetStride(definition.DataType, out var stride)) return null;
var slice = parsedPath.Slice;
var createParams = baseParams with
{
TagName = parsedPath.ToLibplctagSliceArrayName(),
ElementCount = slice.Count,
};
return new AbCipArrayReadPlan(definition.DataType, slice, stride, createParams);
}
///
/// Decode .Count elements from at
/// element stride. Caller has already invoked
/// and confirmed == 0.
///
public static object?[] Decode(AbCipArrayReadPlan plan, IAbCipTagRuntime runtime)
{
ArgumentNullException.ThrowIfNull(plan);
ArgumentNullException.ThrowIfNull(runtime);
var values = new object?[plan.Slice.Count];
for (var i = 0; i < plan.Slice.Count; i++)
values[i] = runtime.DecodeValueAt(plan.ElementType, i * plan.Stride, bitIndex: null);
return values;
}
private static bool TryGetStride(AbCipDataType type, out int stride)
{
switch (type)
{
case AbCipDataType.SInt: case AbCipDataType.USInt:
stride = 1; return true;
case AbCipDataType.Int: case AbCipDataType.UInt:
stride = 2; return true;
case AbCipDataType.DInt: case AbCipDataType.UDInt:
case AbCipDataType.Real: case AbCipDataType.Dt:
stride = 4; return true;
case AbCipDataType.LInt: case AbCipDataType.ULInt:
case AbCipDataType.LReal:
stride = 8; return true;
default:
stride = 0; return false;
}
}
}
///
/// Plan output: the libplctag create-params for the single array-read tag plus the
/// element-type / stride / slice metadata the decoder needs.
///
public sealed record AbCipArrayReadPlan(
AbCipDataType ElementType,
AbCipTagPathSlice Slice,
int Stride,
AbCipTagCreateParams CreateParams);