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). When /// is non-null + non-empty, the symbol is treated /// as a whole-array read and the boxed value is a flat 1-D CLR /// sized to product(arrayDimensions). /// Task<(object? value, uint status)> ReadValueAsync( string symbolPath, TwinCATDataType type, int? bitIndex, int[]? arrayDimensions, CancellationToken cancellationToken); /// /// Write a symbolic value. Returns the mapped OPC UA status for the operation /// (0 = Good, non-zero = error mapped via ). /// mirrors ; PR-1.4 /// ships read-only whole-array support so writers may surface BadNotSupported. /// Task WriteValueAsync( string symbolPath, TwinCATDataType type, int? bitIndex, int[]? arrayDimensions, object? value, CancellationToken cancellationToken); /// /// Bulk-read N scalar symbols in a single AMS request via Beckhoff's ADS Sum-command /// family (IndexGroup 0xF080..0xF084). The result is a parallel array preserving /// ordering — element i's outcome maps to request i. /// Empty input returns an empty result without a wire round-trip. /// /// /// This is the throughput-optimised path used by /// to replace the per-tag loop — one ADS sum-read for N /// symbols beats N individual round-trips by ~10× on the typical PLC link. /// /// Whole-array reads + bit-extracted BOOL reads stay on the per-tag path because /// the Sum-command surface only marshals scalars + bitIndex needs CLR-side post-processing. /// Callers should pre-filter or fall back as appropriate. /// Task> ReadValuesAsync( IReadOnlyList reads, CancellationToken cancellationToken); /// /// Bulk-write N scalar symbols in a single AMS request via Beckhoff's /// SumWriteBySymbolPath. Result is a parallel status array preserving /// ordering. Empty input returns an empty result. /// /// /// Whole-array writes + bit-RMW writes are not in scope for the bulk path — those continue /// through the per-tag path. The driver layer pre-filters. /// Task> WriteValuesAsync( IReadOnlyList writes, 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). /// /// Per-tag MaxDelay in milliseconds — the upper bound on how long the runtime can /// buffer / coalesce change events before dispatching them (PR 3.1 / issue #313). /// 0 means "fire ASAP, no coalescing" (the pre-PR-3.1 default behaviour); larger /// values let the runtime coalesce bursty high-frequency changes so the OPC UA queue /// downstream doesn't flood. Plumbs straight into NotificationSettings(mode, cycleTime, maxDelay). /// /// 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, int maxDelayMs, 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); /// /// PR 2.2 — wipe process-scoped optional caches (today: the ADS variable-handle /// cache backing per-tag reads / writes). Surfaces so operators + the future PR /// 2.3 Symbol-Version invalidation listener can flush stale handles after a known /// online change without forcing a full reconnect. Safe to call mid-traffic — in-flight /// reads continue to use the handles they already hold; the next read for a symbol /// will re-resolve. Best-effort wire-side delete; failures are swallowed. /// Task FlushOptionalCachesAsync(); } /// 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(); } /// One element of an request — the symbol path /// + the IEC type for marshalling. Strings carry an explicit for /// fixed-size STRING(n) declarations (defaults to 80 matching IEC 61131-3). public sealed record TwinCATBulkReadItem( string SymbolPath, TwinCATDataType Type, int StringLength = 80); /// One element of an request. /// Mirror of with the value to push. public sealed record TwinCATBulkWriteItem( string SymbolPath, TwinCATDataType Type, object? Value, int StringLength = 80);