Surface int[]? ArrayDimensions on TwinCATTagDefinition + thread it through ITwinCATClient.ReadValueAsync / WriteValueAsync. When non-null + non-empty, AdsTwinCATClient issues a single ADS read against the symbol with clrType.MakeArrayType() and returns the flat 1-D CLR Array; for IEC TIME / DATE / DT / TOD element types we project per-element to the native TimeSpan / DateTime so consumers see consistent types regardless of rank. DiscoverAsync surfaces IsArray=true + ArrayDim=product(dims) onto DriverAttributeInfo via a new ResolveArrayShape helper. Multi-dim shapes flatten to the product on the wire — DriverAttributeInfo.ArrayDim is single-uint today and the OPC UA layer reflects rank via its own metadata. Native ADS notification subscriptions skip whole-array tags so the OPC UA layer falls through to a polled snapshot — the per-element AdsNotificationEx callback shape doesn't fit a flat array. Whole-array WRITES are out of scope for this PR — AdsTwinCATClient.WriteValueAsync returns BadNotSupported when ArrayDimensions is set. Tests: TwinCATArrayReadTests covers ResolveArrayShape (null / empty / single-dim / multi-dim flatten / non-positive defensive), DiscoverAsync emitting IsArray + ArrayDim for declared array tags, single-dim + multi-dim fake-client read fan-out, and the BadNotSupported gate on whole-array writes. Existing 137 unit tests still pass — total now 143. Closes #308
68 lines
3.3 KiB
C#
68 lines
3.3 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
|
|
|
|
/// <summary>
|
|
/// TwinCAT ADS driver configuration. One instance supports N targets (each identified by
|
|
/// an AMS Net ID + port). Compiles + runs without a local AMS router but every wire call
|
|
/// fails with <c>BadCommunicationError</c> until a router is reachable.
|
|
/// </summary>
|
|
public sealed class TwinCATDriverOptions
|
|
{
|
|
public IReadOnlyList<TwinCATDeviceOptions> Devices { get; init; } = [];
|
|
public IReadOnlyList<TwinCATTagDefinition> Tags { get; init; } = [];
|
|
public TwinCATProbeOptions Probe { get; init; } = new();
|
|
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
|
|
|
|
/// <summary>
|
|
/// When <c>true</c> (default), <c>SubscribeAsync</c> registers native ADS notifications
|
|
/// via <c>AddDeviceNotificationExAsync</c> — the PLC pushes changes on its own cycle
|
|
/// rather than the driver polling. Strictly better for latency + CPU when the target
|
|
/// supports it (TC2 + TC3 PLC runtimes always do; some soft-PLC / third-party ADS
|
|
/// implementations may not). When <c>false</c>, the driver falls through to the shared
|
|
/// <see cref="Core.Abstractions.PollGroupEngine"/> — same semantics as the other
|
|
/// libplctag-backed drivers. Set <c>false</c> for deployments where the AMS router has
|
|
/// notification limits you can't raise.
|
|
/// </summary>
|
|
public bool UseNativeNotifications { get; init; } = true;
|
|
|
|
/// <summary>
|
|
/// When <c>true</c>, <c>DiscoverAsync</c> walks each device's symbol table via the
|
|
/// TwinCAT <c>SymbolLoaderFactory</c> (flat mode) + surfaces controller-resident
|
|
/// globals / program locals under a <c>Discovered/</c> sub-folder. Pre-declared tags
|
|
/// from <see cref="Tags"/> always emit regardless. Default <c>false</c> to preserve
|
|
/// the strict-config path for deployments where only declared tags should appear.
|
|
/// </summary>
|
|
public bool EnableControllerBrowse { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// One TwinCAT target. <paramref name="HostAddress"/> must parse via
|
|
/// <see cref="TwinCATAmsAddress.TryParse"/>; misconfigured devices fail driver initialisation.
|
|
/// </summary>
|
|
public sealed record TwinCATDeviceOptions(
|
|
string HostAddress,
|
|
string? DeviceName = null);
|
|
|
|
/// <summary>
|
|
/// One TwinCAT-backed OPC UA variable. <c>SymbolPath</c> is the full TwinCAT symbolic name
|
|
/// (e.g. <c>MAIN.bStart</c>, <c>GVL.Counter</c>, <c>Motor1.Status.Running</c>). When
|
|
/// <c>ArrayDimensions</c> is non-null + non-empty the symbol is treated as a whole-array
|
|
/// read of <c>product(dims)</c> elements rather than a single scalar — PR-1.4 ships read-
|
|
/// only whole-array support; multi-dim shapes flatten to the product on the wire and the
|
|
/// OPC UA layer reflects the rank via its own <c>ArrayDimensions</c> metadata.
|
|
/// </summary>
|
|
public sealed record TwinCATTagDefinition(
|
|
string Name,
|
|
string DeviceHostAddress,
|
|
string SymbolPath,
|
|
TwinCATDataType DataType,
|
|
bool Writable = true,
|
|
bool WriteIdempotent = false,
|
|
int[]? ArrayDimensions = null);
|
|
|
|
public sealed class TwinCATProbeOptions
|
|
{
|
|
public bool Enabled { get; init; } = true;
|
|
public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(5);
|
|
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
|
|
}
|