namespace ZB.MOM.WW.OtOpcUa.Driver.TwinCAT;
///
/// Wire-layer abstraction over one connection to a TwinCAT AMS target. One instance per
/// ; reused across reads / writes / probes for the device.
/// Tests swap in a fake via .
///
///
/// Unlike libplctag-backed drivers where one native handle exists per tag, TwinCAT's
/// AdsClient is one connection per target with symbolic reads / writes issued against it.
/// The abstraction reflects that — single , many
/// / calls.
///
public interface ITwinCATClient : IDisposable
{
/// Establish the AMS connection. Idempotent — subsequent calls are no-ops when already connected.
/// The target AMS address.
/// The connection timeout.
/// Cancellation token for the connection attempt.
/// A task that represents the asynchronous operation.
Task ConnectAsync(TwinCATAmsAddress address, TimeSpan timeout, CancellationToken cancellationToken);
/// True when the AMS router + target both accept commands.
bool IsConnected { get; }
///
/// Raised when the client observes the ADS symbol-version-changed code
/// ( — DeviceSymbolVersionInvalid
/// 1809 / 0x0711) on any read / write / notification — the signal that a PLC program
/// re-download has invalidated every symbol + notification handle. The driver forwards
/// this to so Core
/// rebuilds the address space subtree (docs/v2/driver-specs.md §6).
///
event EventHandler? OnSymbolVersionChanged;
///
/// Read a symbolic value. Returns a boxed .NET value matching the requested
/// , or null when the read produced no data; the
/// status tuple member carries the mapped OPC UA status (0 = Good).
///
/// The ADS symbol path.
/// The target data type.
/// Optional bit index for bit extraction within a word.
/// When non-null, read a 1-D array of this many
/// elements; the boxed value is the element-typed CLR array (int[] / float[] /
/// bool[] / string[] / …). When null, read a scalar (Phase 4c).
/// Cancellation token for the read operation.
/// The boxed value read (or null) together with the mapped OPC UA status.
Task<(object? value, uint status)> ReadValueAsync(
string symbolPath,
TwinCATDataType type,
int? bitIndex,
int? arrayCount,
CancellationToken cancellationToken);
///
/// Write a symbolic value. Returns the mapped OPC UA status for the operation
/// (0 = Good, non-zero = error mapped via ).
///
/// The ADS symbol path.
/// The data type.
/// Optional bit index for bit manipulation within a word.
/// The value to write.
/// Cancellation token for the write operation.
/// The mapped OPC UA status for the write (0 = Good).
Task WriteValueAsync(
string symbolPath,
TwinCATDataType type,
int? bitIndex,
object? value,
CancellationToken cancellationToken);
///
/// Cheap health probe — returns true when the target's AMS state is reachable.
/// Used by 's probe loop.
///
/// Cancellation token for the probe operation.
/// true when the target is reachable; otherwise false.
Task ProbeAsync(CancellationToken cancellationToken);
///
/// Register a cyclic / on-change ADS notification for a symbol. Returns a handle whose
/// tears the notification down. Callback fires on the
/// thread libplctag / AdsClient uses for notifications — consumers should marshal to
/// their own scheduler before doing work of any size.
///
/// ADS symbol path (e.g. MAIN.bStart).
/// Declared type; drives the native layout + callback value boxing.
/// For BOOL-within-word tags — the bit to extract from the parent word.
/// Minimum interval between change notifications (native-floor depends on target).
/// Maximum batching delay in milliseconds — TwinCAT may coalesce
/// notifications up to this delay before pushing them. 0 = no batching, push
/// immediately.
/// Invoked with (symbolPath, boxedValue) per notification.
/// Cancels the initial registration; does not tear down an established notification.
/// A handle whose disposal tears the registered notification down.
Task AddNotificationAsync(
string symbolPath,
TwinCATDataType type,
int? bitIndex,
TimeSpan cycleTime,
int maxDelayMs,
Action onChange,
CancellationToken cancellationToken);
///
/// Walk the target's symbol table via the TwinCAT SymbolLoaderFactory (flat mode)
/// and expand all struct / UDT / function-block symbols to their atomic member leaves.
/// The implementation uses to recursively
/// descend each struct/UDT/FB-instance symbol into its addressable atomic members,
/// yielding only atomic leaves (scalars or 1-D arrays with a non-null
/// ) and dropping struct/UDT/FB containers.
/// Callers receive fully-qualified InstancePaths (e.g. MAIN.Motor1.Speed)
/// and never see struct-typed entries — expansion is performed here, not in the caller.
///
/// Cancellation token for the enumeration operation.
/// An asynchronous stream of the target's atomic-leaf symbols.
IAsyncEnumerable BrowseSymbolsAsync(CancellationToken cancellationToken);
}
/// Opaque handle for a registered ADS notification. tears it down.
public interface ITwinCATNotificationHandle : IDisposable { }
///
/// One symbol yielded by — full instance
/// path + detected + read-only flag.
///
/// Full dotted symbol path (e.g. MAIN.bStart, GVL.Counter).
/// Mapped of the (element) type; null
/// when the symbol's type doesn't map onto our supported atomic surface (UDTs, pointers,
/// function blocks). For an array symbol this is the ELEMENT type, with
/// carrying the dimension.
/// true when the symbol's AccessRights flag forbids writes.
/// When non-null, the symbol is a 1-D array of this many
/// elements. null = scalar. Multi-dimensional arrays are
/// reported as null (treated as scalar/unsupported) — only 1-D is in scope for Phase 4c.
public sealed record TwinCATDiscoveredSymbol(
string InstancePath,
TwinCATDataType? DataType,
bool ReadOnly,
int? ArrayLength = null);
/// Factory for s. One client per device.
public interface ITwinCATClientFactory
{
/// Creates a new TwinCAT client instance.
/// A new, not-yet-connected .
ITwinCATClient Create();
}