using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.IntegrationTests.DL205; /// /// Verifies the DL205/DL260 low-byte-first ASCII string packing quirk against the /// dl205.json pymodbus profile. Standard Modbus packs the first char of each pair /// in the high byte of the register; DirectLOGIC packs it in the low byte instead. Without /// the driver decodes "eHllo" garbage /// even though the bytes on the wire are identical. /// /// /// /// Requires the dl205 profile (Pymodbus\serve.ps1 -Profile dl205). The standard /// profile does not seed HR[1040..1042] with string bytes, so running this against the /// standard profile returns "\0\0\0\0\0" and the test fails. Skip when the env /// var MODBUS_SIM_PROFILE is not set to dl205. /// /// [Collection(ModbusSimulatorCollection.Name)] [Trait("Category", "Integration")] [Trait("Device", "DL205")] public sealed class DL205StringQuirkTests(ModbusSimulatorFixture sim) { [Fact] public async Task DL205_string_low_byte_first_decodes_Hello_from_HR1040() { 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[1040..1042])."); } var options = new ModbusDriverOptions { Host = sim.Host, Port = sim.Port, UnitId = 1, Timeout = TimeSpan.FromSeconds(2), Tags = [ new ModbusTagDefinition( Name: "DL205_Hello_Low", Region: ModbusRegion.HoldingRegisters, Address: 1040, DataType: ModbusDataType.String, Writable: false, StringLength: 5, StringByteOrder: ModbusStringByteOrder.LowByteFirst), // Control: same address, HighByteFirst, to prove the driver would have decoded // garbage without the quirk flag. new ModbusTagDefinition( Name: "DL205_Hello_High", Region: ModbusRegion.HoldingRegisters, Address: 1040, DataType: ModbusDataType.String, Writable: false, StringLength: 5, StringByteOrder: ModbusStringByteOrder.HighByteFirst), ], Probe = new ModbusProbeOptions { Enabled = false }, }; await using var driver = new ModbusDriver(options, driverInstanceId: "dl205-string"); await driver.InitializeAsync(driverConfigJson: "{}", TestContext.Current.CancellationToken); var results = await driver.ReadAsync(["DL205_Hello_Low", "DL205_Hello_High"], TestContext.Current.CancellationToken); results.Count.ShouldBe(2); results[0].StatusCode.ShouldBe(0u); results[0].Value.ShouldBe("Hello", "DL205 low-byte-first ordering must produce 'Hello' from HR[1040..1042]"); // The high-byte-first read of the same wire bytes should differ — not asserting the // exact garbage string (that would couple the test to the ASCII byte math) but the two // decodes MUST disagree, otherwise the quirk flag is a no-op. results[1].StatusCode.ShouldBe(0u); results[1].Value.ShouldNotBe("Hello"); } }