perf(s7): span-grouped reads — one PDU per contiguous block instead of one per tag
This commit is contained in:
@@ -79,6 +79,56 @@ public sealed class S7DriverOptions
|
||||
[Display(Name = "Probe timeout (seconds)", Description = "Connection test timeout. Default 5s.", GroupName = "Diagnostics")]
|
||||
[Range(1, 60)]
|
||||
public int ProbeTimeoutSeconds { get; init; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Largest hole, in bytes, the read planner will read THROUGH when merging two neighbouring
|
||||
/// tags into one block request. It is a budget for deliberately wasted bytes: raising it
|
||||
/// merges more aggressively (fewer wire requests, more bytes nobody asked for), lowering it
|
||||
/// to <c>0</c> merges only tags that are strictly adjacent or overlapping.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Any value <c><= -1</c> disables span grouping entirely</b> — every tag is read
|
||||
/// on its own, exactly as the driver behaved before grouping existed. That is the
|
||||
/// escape hatch for a CPU that dislikes wide block reads, and the control arm the
|
||||
/// parity tests read through.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Grouping cannot make a read fail where the per-tag reads would have succeeded: a
|
||||
/// span never extends past its LAST member's end, so every byte it requests lies inside
|
||||
/// a range some tag was already going to read. Timer and Counter tags are never grouped
|
||||
/// — their offset is a device number, not a byte address.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Display(Name = "Span read max gap (bytes)",
|
||||
Description = "Largest hole read through when merging neighbouring tags into one block request. -1 disables grouping.",
|
||||
GroupName = "Performance")]
|
||||
public int SpanReadMaxGapBytes { get; init; } = DefaultSpanReadMaxGapBytes;
|
||||
|
||||
/// <summary>Default for <see cref="SpanReadMaxGapBytes"/>, shared with the config DTO so the two can't drift.</summary>
|
||||
public const int DefaultSpanReadMaxGapBytes = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Largest single planned span, in bytes. A run of tags that would exceed it is broken into
|
||||
/// several spans; a single tag wider than the cap still gets its own span, because one tag
|
||||
/// is never split across requests.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is <b>not</b> a correctness bound: S7.Net transparently chunks a <c>ReadBytes</c>
|
||||
/// call larger than the negotiated PDU into several wire requests, so an oversized span
|
||||
/// reads correctly either way. The cap exists to bound wasted-gap amplification — without
|
||||
/// it, a long sparse run of tags each separated by an under-budget hole would coalesce into
|
||||
/// one enormous request that is mostly padding. The 220-byte default sits just under the
|
||||
/// usable payload of the common 240-byte S7-300/400 PDU, so a typical span is one request
|
||||
/// on the wire.
|
||||
/// </remarks>
|
||||
[Display(Name = "Span read max span (bytes)",
|
||||
Description = "Largest single planned block read. Bounds wasted-gap amplification, not correctness.",
|
||||
GroupName = "Performance")]
|
||||
public int SpanReadMaxSpanBytes { get; init; } = DefaultSpanReadMaxSpanBytes;
|
||||
|
||||
/// <summary>Default for <see cref="SpanReadMaxSpanBytes"/>, shared with the config DTO so the two can't drift.</summary>
|
||||
public const int DefaultSpanReadMaxSpanBytes = 220;
|
||||
}
|
||||
|
||||
public sealed class S7ProbeOptions
|
||||
|
||||
Reference in New Issue
Block a user