fix(modbus): multi-write PDUs carry no trailing byte past their byte count
The FC15, FC16, BitInRegister[] RMW FC16 and FC23 builders allocated one byte more than they filled (6 + 1 + n / 10 + 1 + n, data copied at offset 6 / 10), so every multi-write request ended in a stray zero byte beyond the length its byte-count field declares. A strict device or gateway that checks the frame length against the byte count refuses it. Each PDU is now exactly 6 + n (FC15/FC16) or 10 + n (FC23). Tests: ModbusWritePduFramingTests asserts the exact length and byte-count field for FC15 (1/8/9 coils), FC16 (array, Float64 array, scalar multi-register), the BitInRegister RMW FC16 and FC23 (all red before the fix); the parity suite's conformant device now refuses a frame-length mismatch on FC15/FC16/FC23. Live: Driver.Modbus.IntegrationTests against 10.100.0.35:5020 (standard profile) passes, including the FC16 and FC23 multi-register round trips. Refs #741 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
This commit is contained in:
@@ -1603,7 +1603,8 @@ public sealed class ModbusDriver
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
if (values[i]) bitmap[i / 8] |= (byte)(1 << (i % 8));
|
||||
var qty = (ushort)values.Length;
|
||||
var pdu15 = new byte[6 + 1 + byteCount];
|
||||
// [fc][addr:2][qty:2][byteCount] + data: exactly 6 + byteCount (no trailing byte past the declared data).
|
||||
var pdu15 = new byte[6 + byteCount];
|
||||
pdu15[0] = 0x0F;
|
||||
pdu15[1] = (byte)(tag.Address >> 8); pdu15[2] = (byte)(tag.Address & 0xFF);
|
||||
pdu15[3] = (byte)(qty >> 8); pdu15[4] = (byte)(qty & 0xFF);
|
||||
@@ -1638,7 +1639,8 @@ public sealed class ModbusDriver
|
||||
$"Write of {qty} registers to {tag.Name} exceeds MaxRegistersPerWrite={writeCap}. " +
|
||||
$"Split the tag (e.g. shorter StringLength or smaller ArrayCount) — partial FC16 chunks would lose atomicity.");
|
||||
if (await TryWriteViaFc23Async(transport, tag, bytes, qty, ct).ConfigureAwait(false)) return;
|
||||
var pdu = new byte[6 + 1 + bytes.Length];
|
||||
// [fc][addr:2][qty:2][byteCount] + data: exactly 6 + byteCount.
|
||||
var pdu = new byte[6 + bytes.Length];
|
||||
pdu[0] = 0x10;
|
||||
pdu[1] = (byte)(tag.Address >> 8); pdu[2] = (byte)(tag.Address & 0xFF);
|
||||
pdu[3] = (byte)(qty >> 8); pdu[4] = (byte)(qty & 0xFF);
|
||||
@@ -1685,7 +1687,8 @@ public sealed class ModbusDriver
|
||||
if (!_options.UseFC23ForMultiRegisterWrites || _options.DisableFC23
|
||||
|| qty < 2 || qty > Fc23MaxWriteRegisters || unsupported.ContainsKey(unit)) return false;
|
||||
|
||||
var pdu = new byte[10 + 1 + bytes.Length];
|
||||
// [fc][readAddr:2][readQty:2][writeAddr:2][writeQty:2][writeByteCount] + data: exactly 10 + byteCount.
|
||||
var pdu = new byte[10 + bytes.Length];
|
||||
pdu[0] = 0x17;
|
||||
pdu[1] = (byte)(tag.Address >> 8); pdu[2] = (byte)(tag.Address & 0xFF); // read start
|
||||
pdu[3] = (byte)(qty >> 8); pdu[4] = (byte)(qty & 0xFF); // read quantity
|
||||
@@ -1824,7 +1827,8 @@ public sealed class ModbusDriver
|
||||
payload[i * 2] = (byte)(reg >> 8);
|
||||
payload[i * 2 + 1] = (byte)(reg & 0xFF);
|
||||
}
|
||||
var pdu = new byte[6 + 1 + payload.Length];
|
||||
// [fc][addr:2][qty:2][byteCount] + data: exactly 6 + byteCount.
|
||||
var pdu = new byte[6 + payload.Length];
|
||||
pdu[0] = 0x10;
|
||||
pdu[1] = (byte)(tag.Address >> 8); pdu[2] = (byte)(tag.Address & 0xFF);
|
||||
pdu[3] = (byte)(count >> 8); pdu[4] = (byte)(count & 0xFF);
|
||||
|
||||
Reference in New Issue
Block a user