fix(driver-modbus-addressing): resolve High code-review finding (Driver.Modbus.Addressing-001)

The DL205 family-native branch routed every V-prefixed address through
DirectLogicAddress.UserVMemoryToPdu, a plain octal-to-decimal decode.
DL205/DL260 system V-memory (V40400 and up) is not a simple octal decode:
the CPU relocates the system bank to Modbus PDU 0x2100. Octal-decoding
V40400 produced 16640 (0x4100), the wrong register, so any tag addressing
a system register through the grammar string silently read/wrote the
wrong PLC memory.

- Add DirectLogicAddress.VMemoryToPdu, which decodes the octal V-address,
  detects the system bank (octal >= V40400 == SystemVMemoryOctalBase) and
  relocates it through SystemVMemoryToPdu to PDU 0x2100; user-bank
  addresses keep the plain octal decode.
- ModbusAddressParser's DL205 V branch now calls VMemoryToPdu instead of
  UserVMemoryToPdu. UserVMemoryToPdu is retained for user-bank-only callers.
- Correct the ModbusFamilyParserTests V40400 assertion (16640 -> 0x2100)
  and add system-bank regression cases plus direct helper coverage.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 06:53:59 -04:00
parent 532b961cf2
commit 1837b5a828
4 changed files with 88 additions and 7 deletions

View File

@@ -17,8 +17,8 @@ public sealed class ModbusFamilyParserTests
[Theory]
[InlineData("V0", 0)]
[InlineData("V2000", 1024)] // octal 2000 = decimal 1024
[InlineData("V40400", 16640)] // octal 40400 = decimal 16640 (system bank in user mapping per the helper)
public void DL205_VMemory_To_HoldingRegisters(string addr, int expectedOffset)
[InlineData("V7777", 4095)] // octal 7777 = decimal 4095, top of the user bank example
public void DL205_UserVMemory_To_HoldingRegisters(string addr, int expectedOffset)
{
var p = ModbusAddressParser.Parse(addr, ModbusFamily.DL205);
p.Region.ShouldBe(ModbusRegion.HoldingRegisters);
@@ -26,6 +26,32 @@ public sealed class ModbusFamilyParserTests
p.DataType.ShouldBe(ModbusDataType.Int16);
}
// Regression: Driver.Modbus.Addressing-001. DL205/DL260 system V-memory (V40400+) is NOT a
// plain octal decode — the CPU relocates the system bank to Modbus PDU 0x2100. Octal-decoding
// V40400 yields 16640 (0x4100), the WRONG register. Per docs/v2/dl205.md §V-Memory Addressing,
// V40400 must map to PDU 0x2100 (decimal 8448) and the bank is contiguous from there.
[Theory]
[InlineData("V40400", 0x2100)] // system base
[InlineData("V40401", 0x2101)] // next register — contiguous, +1 decimal
[InlineData("V40410", 0x2108)] // octal 40410 = base + octal(10) = base + 8 decimal
public void DL205_SystemVMemory_To_HoldingRegisters_SystemBank(string addr, int expectedOffset)
{
var p = ModbusAddressParser.Parse(addr, ModbusFamily.DL205);
p.Region.ShouldBe(ModbusRegion.HoldingRegisters);
p.Offset.ShouldBe((ushort)expectedOffset);
p.DataType.ShouldBe(ModbusDataType.Int16);
}
[Fact]
public void DL205_SystemVMemory_Helper_Routes_Through_SystemBank()
{
// Direct helper coverage: VMemoryToPdu routes user vs system; the legacy
// UserVMemoryToPdu is the plain octal decoder used only for the user bank.
DirectLogicAddress.VMemoryToPdu("V40400").ShouldBe((ushort)DirectLogicAddress.SystemVMemoryBasePdu);
DirectLogicAddress.VMemoryToPdu("V2000").ShouldBe((ushort)1024);
DirectLogicAddress.UserVMemoryToPdu("V40400").ShouldBe((ushort)16640); // plain octal decode — user-bank only
}
[Fact]
public void DL205_Y_Output_Maps_To_Coils_Bank()
{