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. null0 (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"; }