diff --git a/tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs b/tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs new file mode 100644 index 0000000..0d04148 --- /dev/null +++ b/tests/ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests/DL205/DL205FloatCdabQuirkTests.cs @@ -0,0 +1,64 @@ +using Shouldly; +using Xunit; + +namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; + +/// +/// Verifies DL205/DL260 CDAB word ordering for 32-bit floats against the +/// dl205.json pymodbus profile. DirectLOGIC stores IEEE-754 singles with the low +/// word at the lower register address (CDAB) rather than the high word (ABCD). Reading +/// HR[1056..1057] with produces a tiny +/// denormal (~5.74e-41) instead of the intended 1.5f — a silent "value is 0" bug in the +/// field unless the caller opts into . +/// +[Collection(ModbusSimulatorCollection.Name)] +[Trait("Category", "Integration")] +[Trait("Device", "DL205")] +public sealed class DL205FloatCdabQuirkTests(ModbusSimulatorFixture sim) +{ + [Fact] + public async Task DL205_Float32_CDAB_decodes_1_5f_from_HR1056() + { + if (sim.SkipReason is not null) Assert.Skip(sim.SkipReason); + if (!string.Equals(Environment.GetEnvironmentVariable("MODBUS_SIM_PROFILE"), "dl205", + StringComparison.OrdinalIgnoreCase)) + { + Assert.Skip("MODBUS_SIM_PROFILE != dl205 — skipping (standard profile does not seed HR[1056..1057])."); + } + + var options = new ModbusDriverOptions + { + Host = sim.Host, + Port = sim.Port, + UnitId = 1, + Timeout = TimeSpan.FromSeconds(2), + Tags = + [ + new ModbusTagDefinition("DL205_Float_CDAB", + ModbusRegion.HoldingRegisters, Address: 1056, + DataType: ModbusDataType.Float32, Writable: false, + ByteOrder: ModbusByteOrder.WordSwap), + // Control: same address, BigEndian — proves the default decode produces garbage. + new ModbusTagDefinition("DL205_Float_ABCD", + ModbusRegion.HoldingRegisters, Address: 1056, + DataType: ModbusDataType.Float32, Writable: false, + ByteOrder: ModbusByteOrder.BigEndian), + ], + Probe = new ModbusProbeOptions { Enabled = false }, + }; + await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-cdab"); + await driver.InitializeAsync("{}", TestContext.Current.CancellationToken); + + var results = await driver.ReadAsync(["DL205_Float_CDAB", "DL205_Float_ABCD"], + TestContext.Current.CancellationToken); + + results[0].StatusCode.ShouldBe(0u); + results[0].Value.ShouldBe(1.5f, "DL205 Float32 with WordSwap (CDAB) must decode HR[1056..1057] as 1.5f"); + + // The BigEndian read of the same wire bytes should differ — not asserting the exact + // denormal value (that couples the test to IEEE-754 bit math) but the two decodes MUST + // disagree, otherwise the word-order flag is a no-op. + results[1].StatusCode.ShouldBe(0u); + results[1].Value.ShouldNotBe(1.5f); + } +}