namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip;
///
/// Thin wire-layer abstraction over a single CIP tag. The driver holds one instance per
/// (device, tag path) pair; the default implementation delegates to
/// . Tests swap in a fake via
/// so the driver's read / write / status-mapping logic can
/// be exercised without a running PLC or the native libplctag binary.
///
public interface IAbCipTagRuntime : IDisposable
{
/// Create the underlying native tag (equivalent to libplctag's plc_tag_create).
Task InitializeAsync(CancellationToken cancellationToken);
/// Issue a read; on completion the local buffer holds the current PLC value.
Task ReadAsync(CancellationToken cancellationToken);
/// Flush the local buffer to the PLC.
Task WriteAsync(CancellationToken cancellationToken);
///
/// Raw libplctag status code — mapped to an OPC UA StatusCode via
/// . Zero on success, negative on error.
///
int GetStatus();
///
/// Decode the local buffer into a boxed .NET value per the tag's configured type.
/// is non-null only for BOOL-within-DINT tags captured in
/// the .N syntax at parse time.
///
object? DecodeValue(AbCipDataType type, int? bitIndex);
///
/// Decode a value at an arbitrary byte offset in the local buffer. Task #194 —
/// whole-UDT reads perform one on the parent UDT tag then
/// call this per declared member with its computed offset, avoiding one libplctag
/// round-trip per member. Implementations that do not support offset-aware decoding
/// may fall back to when is zero;
/// offsets greater than zero against an unsupporting runtime should return null
/// so the planner can skip grouping.
///
object? DecodeValueAt(AbCipDataType type, int offset, int? bitIndex);
///
/// Encode into the local buffer per the tag's type. Callers
/// pair this with .
///
void EncodeValue(AbCipDataType type, int? bitIndex, object? value);
}
///
/// Factory for per-tag runtime handles. Instantiated once per driver, consumed per
/// (device, tag path) pair at the first read/write.
///
public interface IAbCipTagFactory
{
IAbCipTagRuntime Create(AbCipTagCreateParams createParams);
}
/// Everything libplctag needs to materialise a tag handle.
/// Gateway IP / hostname parsed from .
/// EtherNet/IP TCP port — default 44818.
/// CIP route path, e.g. 1,0. Empty for Micro800.
/// libplctag plc=... attribute, per family profile.
/// Logix symbolic tag name as emitted by .
/// libplctag operation timeout (applies to Initialize / Read / Write).
/// Optional Logix STRINGnn DATA-array capacity (e.g. 20 / 40 / 80
/// for STRING_20 / STRING_40 / STRING_80 UDTs). Threads through libplctag's
/// str_max_capacity attribute. null keeps libplctag's default 82-byte STRING
/// behaviour for back-compat.
/// Optional libplctag ElementCount override — set to N
/// to issue a Rockwell array read covering N consecutive elements starting at the
/// subscripted index in . Drives PR abcip-1.3 array-slice support;
/// null leaves libplctag's default scalar-element behaviour for back-compat.
/// PR abcip-3.1 — CIP Forward Open buffer size in bytes. Threads
/// through to libplctag's connection_size attribute. The driver always supplies a
/// value here — either the per-device
/// override or the family profile's .
/// Bigger packets fit more tags per RTT (higher throughput); smaller packets stay compatible
/// with legacy firmware (v19-and-earlier ControlLogix caps at 504, Micro800 hard-caps at
/// 488).
/// PR abcip-3.2 — concrete addressing mode the runtime should
/// activate for this tag handle. Always either or
/// at this layer (the driver resolves Auto +
/// family-incompatibility before building the create-params). Symbolic is the libplctag
/// default and needs no extra attribute. Logical adds the libplctag use_connected_msg=1
/// attribute + (when an instance ID is known via ) reaches
/// into NativeTagWrapper.SetAttributeString by reflection because the .NET wrapper
/// does not expose a public knob for instance-ID addressing.
/// PR abcip-3.2 — Symbol Object instance ID the controller
/// assigned to this tag, populated by the driver after a one-time @tags walk for
/// Logical-mode devices. null for Symbolic mode + for the very first read on a
/// Logical device when the symbol-table walk has not yet completed; the runtime falls back
/// to Symbolic addressing in either case so the read still completes.
public sealed record AbCipTagCreateParams(
string Gateway,
int Port,
string CipPath,
string LibplctagPlcAttribute,
string TagName,
TimeSpan Timeout,
int? StringMaxCapacity = null,
int? ElementCount = null,
int ConnectionSize = 4002,
AddressingMode AddressingMode = AddressingMode.Symbolic,
uint? LogicalInstanceId = null);