114 lines
5.2 KiB
C#
114 lines
5.2 KiB
C#
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
|
|
|
/// <summary>
|
|
/// 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 (<c>N7:0</c>) and Logix's symbolic addressing
|
|
/// (<c>Motor1.Speed</c>) pull the abstraction in different directions.
|
|
/// </summary>
|
|
public sealed class AbLegacyDriverOptions
|
|
{
|
|
public IReadOnlyList<AbLegacyDeviceOptions> Devices { get; init; } = [];
|
|
public IReadOnlyList<AbLegacyTagDefinition> Tags { get; init; } = [];
|
|
public AbLegacyProbeOptions Probe { get; init; } = new();
|
|
|
|
/// <summary>
|
|
/// Driver-wide default per-operation timeout. Applies to every device unless that device
|
|
/// overrides it via <see cref="AbLegacyDeviceOptions.Timeout"/> (PR 9).
|
|
/// </summary>
|
|
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(2);
|
|
|
|
/// <summary>
|
|
/// PR 9 — driver-wide default retry count for transient
|
|
/// <c>BadCommunicationError</c> reads. <c>null</c> ≡ <c>0</c> (single attempt). Applies
|
|
/// to every device unless that device overrides it via
|
|
/// <see cref="AbLegacyDeviceOptions.Retries"/>.
|
|
/// </summary>
|
|
public int? Retries { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Per-device options for the AB Legacy driver. PR 9 added optional <see cref="Timeout"/>
|
|
/// and <see cref="Retries"/> 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 <see cref="AbLegacyDriverOptions"/>.
|
|
/// </summary>
|
|
public sealed record AbLegacyDeviceOptions(
|
|
string HostAddress,
|
|
AbLegacyPlcFamily PlcFamily = AbLegacyPlcFamily.Slc500,
|
|
string? DeviceName = null,
|
|
TimeSpan? Timeout = null,
|
|
int? Retries = null,
|
|
AbLegacyDemoteOptions? Demote = null);
|
|
|
|
/// <summary>
|
|
/// PR ablegacy-12 / #255 — auto-demote knobs. After
|
|
/// <see cref="FailureThreshold"/> consecutive read / probe failures the driver
|
|
/// marks the device <c>Demoted</c> for <see cref="DemoteFor"/>; reads against
|
|
/// a demoted device short-circuit with <c>BadCommunicationError</c> 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.
|
|
/// </summary>
|
|
/// <param name="FailureThreshold">Consecutive read or probe failures that trip
|
|
/// the demotion. Default <c>3</c>.</param>
|
|
/// <param name="DemoteFor">Cool-down window before reads are dispatched again
|
|
/// without a successful probe in between. Default <c>30s</c>.</param>
|
|
/// <param name="Enabled">When <c>false</c> 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.</param>
|
|
public sealed record AbLegacyDemoteOptions(
|
|
int FailureThreshold = 3,
|
|
TimeSpan? DemoteFor = null,
|
|
bool Enabled = true)
|
|
{
|
|
/// <summary>
|
|
/// Effective demote window. Records can't have <c>TimeSpan</c> defaults
|
|
/// because <c>TimeSpan.FromSeconds(30)</c> isn't a compile-time constant;
|
|
/// callers that pass <c>null</c> get the documented 30-second default
|
|
/// here.
|
|
/// </summary>
|
|
public TimeSpan EffectiveDemoteFor => DemoteFor ?? TimeSpan.FromSeconds(30);
|
|
}
|
|
|
|
/// <summary>
|
|
/// One PCCC-backed OPC UA variable. <c>Address</c> is the canonical PCCC file-address
|
|
/// string that parses via <see cref="AbLegacyAddress.TryParse(string?)"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// PR 8 deadband fields:
|
|
/// <list type="bullet">
|
|
/// <item><c>AbsoluteDeadband</c> — when set, suppresses <c>OnDataChange</c> for numeric
|
|
/// tags unless <c>|new - prev| >= AbsoluteDeadband</c>.</item>
|
|
/// <item><c>PercentDeadband</c> — when set, suppresses unless
|
|
/// <c>|new - prev| >= |prev * Percent / 100|</c>; <c>prev == 0</c> always publishes.</item>
|
|
/// </list>
|
|
/// Booleans bypass deadband entirely (every transition publishes); strings + status
|
|
/// changes always publish; first-seen always publishes; both set → logical-OR (Kepware
|
|
/// semantics).
|
|
/// </remarks>
|
|
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);
|
|
|
|
/// <summary>Probe address — defaults to <c>S:0</c> (status file, first word) when null.</summary>
|
|
public string? ProbeAddress { get; init; } = "S:0";
|
|
}
|