feat(modbus): block-read coalescing on by default; coils/DI/BitInRegister/String fold

U2: MaxReadGap defaults to 8 and 0 now means adjacent-only rather than
off; a new DisableReadCoalescing switch (factory DTO + AdminUI form) turns it
off entirely. The planner also merges Coils/DiscreteInputs (FC01/FC02, gap in
bits), String scalars and BitInRegister tags - sixteen bit tags on one status
word are one FC03 instead of sixteen. Auto-prohibition and its re-probe /
bisection use the region's own function code (a coil range was re-probed
with FC04).

Refs #741
Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
Joseph Doherty
2026-10-22 15:24:38 -04:00
parent f827b3a134
commit 2ef3cd0df2
7 changed files with 269 additions and 56 deletions
@@ -86,7 +86,8 @@ public static class ModbusDriverFactoryExtensions
UseFC16ForSingleRegisterWrites = dto.UseFC16ForSingleRegisterWrites ?? false,
DisableFC23 = dto.DisableFC23 ?? false,
WriteOnChangeOnly = dto.WriteOnChangeOnly ?? false,
MaxReadGap = dto.MaxReadGap ?? 0,
MaxReadGap = dto.MaxReadGap ?? ModbusDriverOptions.DefaultMaxReadGap,
DisableReadCoalescing = dto.DisableReadCoalescing ?? false,
Family = dto.Family is null ? ModbusFamily.Generic
: ParseEnum<ModbusFamily>(dto.Family, "<driver-level>", driverInstanceId, "Family"),
MelsecSubFamily = dto.MelsecSubFamily is null ? MelsecFamily.Q_L_iQR
@@ -171,6 +172,8 @@ public static class ModbusDriverFactoryExtensions
public bool? WriteOnChangeOnly { get; init; }
/// <summary>Gets or sets the maximum gap between register addresses for coalescing.</summary>
public ushort? MaxReadGap { get; init; }
/// <summary>Gets or sets a value indicating whether block-read coalescing is turned off entirely.</summary>
public bool? DisableReadCoalescing { get; init; }
/// <summary>Gets or sets the Modbus family type.</summary>
public string? Family { get; init; }
/// <summary>Gets or sets the Melsec subfamily.</summary>