using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
///
/// AB Legacy (PCCC) driver configuration. One instance supports N devices (SLC 500 /
/// MicroLogix / PLC-5 / LogixPccc). Per plan decision #41 AbLegacy ships separately from
/// AbCip because PCCC's file-based addressing (N7:0) and Logix's symbolic addressing
/// (Motor1.Speed) pull the abstraction in different directions.
///
public sealed class AbLegacyDriverOptions
{
public IReadOnlyList Devices { get; init; } = [];
public IReadOnlyList Tags { get; init; } = [];
public AbLegacyProbeOptions Probe { get; init; } = new();
///
/// Driver-wide default per-operation timeout. Applies to every device unless that device
/// overrides it via (PR 9).
///
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
///
/// PR 9 — driver-wide default retry count for transient
/// BadCommunicationError reads. null ≡ 0 (single attempt). Applies
/// to every device unless that device overrides it via
/// .
///
public int? Retries { get; init; }
}
///
/// Per-device options for the AB Legacy driver. PR 9 added optional
/// and overrides — chassis families have very different per-operation
/// latency floors (SLC 5/01 RS-232 ~5 s; SLC 5/05 ~2 s; ML1100 ~3 s) so a single driver-wide
/// timeout always misfires on at least one device. Both fields are optional and fall back
/// to the driver-wide default on .
///
public sealed record AbLegacyDeviceOptions(
string HostAddress,
AbLegacyPlcFamily PlcFamily = AbLegacyPlcFamily.Slc500,
string? DeviceName = null,
TimeSpan? Timeout = null,
int? Retries = null,
AbLegacyDemoteOptions? Demote = null);
///
/// PR ablegacy-12 / #255 — auto-demote knobs. After
/// consecutive read / probe failures the driver
/// marks the device Demoted for ; reads against
/// a demoted device short-circuit with BadCommunicationError instead
/// of dispatching through libplctag, so one slow PLC can't starve faster
/// peers sharing the same driver. A successful probe clears the demotion
/// early; a successful read just resets the consecutive-failure counter
/// without leaving the demoted window.
///
/// Consecutive read or probe failures that trip
/// the demotion. Default 3.
/// Cool-down window before reads are dispatched again
/// without a successful probe in between. Default 30s.
/// When false the failure tally still ticks but the
/// driver never sets the demoted window — useful when an operator wants the
/// diagnostic counters without the throttling behaviour.
public sealed record AbLegacyDemoteOptions(
int FailureThreshold = 3,
TimeSpan? DemoteFor = null,
bool Enabled = true)
{
///
/// Effective demote window. Records can't have TimeSpan defaults
/// because TimeSpan.FromSeconds(30) isn't a compile-time constant;
/// callers that pass null get the documented 30-second default
/// here.
///
public TimeSpan EffectiveDemoteFor => DemoteFor ?? TimeSpan.FromSeconds(30);
}
///
/// One PCCC-backed OPC UA variable. Address is the canonical PCCC file-address
/// string that parses via .
///
///
/// PR 8 deadband fields:
///
/// - AbsoluteDeadband — when set, suppresses OnDataChange for numeric
/// tags unless |new - prev| >= AbsoluteDeadband.
/// - PercentDeadband — when set, suppresses unless
/// |new - prev| >= |prev * Percent / 100|; prev == 0 always publishes.
///
/// Booleans bypass deadband entirely (every transition publishes); strings + status
/// changes always publish; first-seen always publishes; both set → logical-OR (Kepware
/// semantics).
///
public sealed record AbLegacyTagDefinition(
string Name,
string DeviceHostAddress,
string Address,
AbLegacyDataType DataType,
bool Writable = true,
bool WriteIdempotent = false,
int? ArrayLength = null,
double? AbsoluteDeadband = null,
double? PercentDeadband = null);
public sealed class AbLegacyProbeOptions
{
public bool Enabled { get; init; } = true;
public TimeSpan Interval { get; init; } = TimeSpan.FromSeconds(5);
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
/// Probe address — defaults to S:0 (status file, first word) when null.
public string? ProbeAddress { get; init; } = "S:0";
}