95 lines
4.1 KiB
C#
95 lines
4.1 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
|
|
|
/// <summary>
|
|
/// PR abcip-1.3 — issues one libplctag tag-create with <c>ElementCount=N</c> per Rockwell
|
|
/// array-slice tag (<c>Tag[0..N]</c> in <see cref="AbCipTagPath"/>), then decodes the
|
|
/// contiguous buffer at element stride into <c>N</c> typed values. Mirrors the whole-UDT
|
|
/// planner pattern (<see cref="AbCipUdtReadPlanner"/>): pure shape — the planner never
|
|
/// touches the runtime + never reads the PLC, the driver wires the runtime in.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// <para>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).</para>
|
|
///
|
|
/// <para>Output is a single <c>object[]</c> snapshot value containing the N decoded
|
|
/// elements at indices 0..Count-1. Pairing with one slice tag = one snapshot keeps the
|
|
/// <c>ReadAsync</c> 1:1 contract (one fullReference -> one snapshot) intact.</para>
|
|
/// </remarks>
|
|
public static class AbCipArrayReadPlanner
|
|
{
|
|
/// <summary>
|
|
/// Build the libplctag create-params + decode descriptor for a slice tag. Returns
|
|
/// <c>null</c> 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.
|
|
/// </summary>
|
|
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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decode <paramref name="plan"/>.Count elements from <paramref name="runtime"/> at
|
|
/// element stride. Caller has already invoked <see cref="IAbCipTagRuntime.ReadAsync"/>
|
|
/// and confirmed <see cref="IAbCipTagRuntime.GetStatus"/> == 0.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Plan output: the libplctag create-params for the single array-read tag plus the
|
|
/// element-type / stride / slice metadata the decoder needs.
|
|
/// </summary>
|
|
public sealed record AbCipArrayReadPlan(
|
|
AbCipDataType ElementType,
|
|
AbCipTagPathSlice Slice,
|
|
int Stride,
|
|
AbCipTagCreateParams CreateParams);
|