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.
Task ConnectAsync(TwinCATAmsAddress address, TimeSpan timeout, CancellationToken cancellationToken);
/// True when the AMS router + target both accept commands.
bool IsConnected { get; }
///
/// 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).
///
Task<(object? value, uint status)> ReadValueAsync(
string symbolPath,
TwinCATDataType type,
int? bitIndex,
CancellationToken cancellationToken);
///
/// Write a symbolic value. Returns the mapped OPC UA status for the operation
/// (0 = Good, non-zero = error mapped via ).
///
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.
///
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).
/// Invoked with (symbolPath, boxedValue) per notification.
/// Cancels the initial registration; does not tear down an established notification.
Task AddNotificationAsync(
string symbolPath,
TwinCATDataType type,
int? bitIndex,
TimeSpan cycleTime,
Action onChange,
CancellationToken cancellationToken);
///
/// Walk the target's symbol table via the TwinCAT SymbolLoaderFactory (flat mode).
/// Yields each top-level symbol the PLC exposes — global variables, program-scope locals,
/// function-block instance fields. Filters for our atomic type surface; structured /
/// UDT / function-block typed symbols surface with DataType = null so callers can
/// decide whether to drill in via their own walker.
///
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 ; null when the symbol's type
/// doesn't map onto our supported atomic surface (UDTs, pointers, function blocks).
/// true when the symbol's AccessRights flag forbids writes.
public sealed record TwinCATDiscoveredSymbol(
string InstancePath,
TwinCATDataType? DataType,
bool ReadOnly);
/// Factory for s. One client per device.
public interface ITwinCATClientFactory
{
ITwinCATClient Create();
}