826312befe
Clears CommentChecker findings in src/ (3,280 -> 209, the remainder being false positives like PLC-5/IEEE-754/M0.0) by adding missing summaries/params/returns, normalising inheritdoc usage, and removing Gitea/review tracking IDs while keeping the rationale they annotated. Comment-only: comment-stripped sources are identical to the parent commit.
103 lines
5.0 KiB
C#
103 lines
5.0 KiB
C#
namespace ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.Cip;
|
||
|
||
/// <summary>How CIP requests reach the target's message router (review finding B3; the captures decide
|
||
/// each family's mode).</summary>
|
||
public enum CipRouting
|
||
{
|
||
/// <summary>Forward Open a class-3 connection, then SendUnitData. Logix and Micro800 (empty route).</summary>
|
||
Connected,
|
||
|
||
/// <summary>SendRRData wrapping each request in Unconnected Send (0x52) carrying the route.</summary>
|
||
UnconnectedSend,
|
||
|
||
/// <summary>SendRRData carrying the request bare; any configured CIP path is ignored (PCCC families).</summary>
|
||
Direct,
|
||
}
|
||
|
||
/// <summary>Per-device connection settings. Validated on assignment so an unusable value fails at the
|
||
/// configuration seam rather than on the wire.</summary>
|
||
public sealed record ConnectionOptions
|
||
{
|
||
/// <summary>Press safety floor: no setting may let Forward Opens to one device start closer together.</summary>
|
||
public static readonly TimeSpan MinimumForwardOpenInterval = TimeSpan.FromSeconds(1);
|
||
|
||
/// <summary>Originator serial shared by every connection this process opens.</summary>
|
||
private static readonly uint ProcessOriginatorSerial = (uint)Random.Shared.Next(1, int.MaxValue);
|
||
|
||
private readonly TimeSpan _timeout = TimeSpan.FromSeconds(5);
|
||
private readonly int _connectionSize = 4002;
|
||
private readonly TimeSpan _minForwardOpenInterval = MinimumForwardOpenInterval;
|
||
private readonly uint _rpiMicros = 2_000_000;
|
||
private readonly byte _timeoutMultiplier = 1;
|
||
private readonly TimeSpan? _keepAliveInterval;
|
||
|
||
/// <summary>How requests reach the target's message router.</summary>
|
||
public CipRouting Routing { get; init; } = CipRouting.Connected;
|
||
|
||
/// <summary>Pack queued requests into Multiple Service Packets (the scheduler's switch).</summary>
|
||
public bool AllowPacking { get; init; }
|
||
|
||
/// <summary>Bound on each single exchange (TCP connect, RegisterSession, Forward Open, request).
|
||
/// Must be finite and positive: an infinite timeout would let an abandoned exchange
|
||
/// hold the session's single-flight gate forever.</summary>
|
||
public TimeSpan Timeout
|
||
{
|
||
get => _timeout;
|
||
init => _timeout = value > TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan
|
||
? value
|
||
: throw new ArgumentOutOfRangeException(nameof(Timeout), value, "Timeout must be finite and positive.");
|
||
}
|
||
|
||
/// <summary>Requested connection size in bytes. Above 511 the open is a Large Forward Open (0x5B).</summary>
|
||
public int ConnectionSize
|
||
{
|
||
get => _connectionSize;
|
||
init => _connectionSize = value is >= 1 and <= ushort.MaxValue
|
||
? value
|
||
: throw new ArgumentOutOfRangeException(nameof(ConnectionSize), value, "Connection size must be 1..65535.");
|
||
}
|
||
|
||
/// <summary>Minimum time between Forward Open attempt starts for one device (≥ 1 s, press safety).</summary>
|
||
public TimeSpan MinForwardOpenInterval
|
||
{
|
||
get => _minForwardOpenInterval;
|
||
init => _minForwardOpenInterval = value >= MinimumForwardOpenInterval && value != System.Threading.Timeout.InfiniteTimeSpan
|
||
? value
|
||
: throw new ArgumentOutOfRangeException(nameof(MinForwardOpenInterval), value, "Must be finite and at least 1 s (press safety).");
|
||
}
|
||
|
||
/// <summary>Requested packet interval for both directions, in microseconds.</summary>
|
||
public uint RpiMicros
|
||
{
|
||
get => _rpiMicros;
|
||
init => _rpiMicros = value > 0 ? value : throw new ArgumentOutOfRangeException(nameof(RpiMicros), value, "RPI must be positive.");
|
||
}
|
||
|
||
/// <summary>Encoded connection timeout multiplier: the connection expires after RPI × (4 << value).
|
||
/// The default 1 means ×8, so 2 s × 8 = 16 s.</summary>
|
||
public byte TimeoutMultiplier
|
||
{
|
||
get => _timeoutMultiplier;
|
||
init => _timeoutMultiplier = value <= 7 ? value : throw new ArgumentOutOfRangeException(nameof(TimeoutMultiplier), value, "Multiplier must be 0..7.");
|
||
}
|
||
|
||
/// <summary>How long an idle connected session waits before the target would drop it.</summary>
|
||
public TimeSpan ConnectionExpiry => TimeSpan.FromTicks(RpiMicros * 10L * (4L << TimeoutMultiplier));
|
||
|
||
/// <summary>Idle time after which a keep-alive is sent (Connected only). Defaults to half the
|
||
/// connection expiry: RPI × 8 / 2 = 8 s.</summary>
|
||
public TimeSpan KeepAliveInterval
|
||
{
|
||
get => _keepAliveInterval ?? ConnectionExpiry / 2;
|
||
init => _keepAliveInterval = value > TimeSpan.Zero && value != System.Threading.Timeout.InfiniteTimeSpan
|
||
? value
|
||
: throw new ArgumentOutOfRangeException(nameof(KeepAliveInterval), value, "Keep-alive interval must be finite and positive.");
|
||
}
|
||
|
||
/// <summary>The vendor id sent in the Forward Open originator identity.</summary>
|
||
public ushort VendorId { get; init; } = 0x1337;
|
||
|
||
/// <summary>Random per process; together with the vendor id and connection serial it names a connection.</summary>
|
||
public uint OriginatorSerial { get; init; } = ProcessOriginatorSerial;
|
||
}
|