using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests; [Trait("Category", "Unit")] public sealed class DirectLogicAddressTests { [Theory] [InlineData("V0", (ushort)0x0000)] [InlineData("V1", (ushort)0x0001)] [InlineData("V7", (ushort)0x0007)] [InlineData("V10", (ushort)0x0008)] [InlineData("V2000", (ushort)0x0400)] // canonical DL205/DL260 user-memory start [InlineData("V7777", (ushort)0x0FFF)] [InlineData("V10000", (ushort)0x1000)] [InlineData("V17777", (ushort)0x1FFF)] public void UserVMemoryToPdu_converts_octal_V_prefix(string v, ushort expected) => DirectLogicAddress.UserVMemoryToPdu(v).ShouldBe(expected); [Theory] [InlineData("0", (ushort)0)] [InlineData("2000", (ushort)0x0400)] [InlineData("v2000", (ushort)0x0400)] // lowercase v [InlineData(" V2000 ", (ushort)0x0400)] // surrounding whitespace public void UserVMemoryToPdu_accepts_bare_or_prefixed_or_padded(string v, ushort expected) => DirectLogicAddress.UserVMemoryToPdu(v).ShouldBe(expected); [Theory] [InlineData("V8")] // 8 is not a valid octal digit [InlineData("V19")] [InlineData("V2009")] public void UserVMemoryToPdu_rejects_non_octal_digits(string v) { Should.Throw(() => DirectLogicAddress.UserVMemoryToPdu(v)) .Message.ShouldContain("octal"); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] [InlineData("V")] public void UserVMemoryToPdu_rejects_empty_input(string? v) => Should.Throw(() => DirectLogicAddress.UserVMemoryToPdu(v!)); [Fact] public void UserVMemoryToPdu_overflow_rejected() { // 200000 octal = 0x10000 — one past ushort range. Should.Throw(() => DirectLogicAddress.UserVMemoryToPdu("V200000")); } [Fact] public void SystemVMemoryBasePdu_is_0x2100_for_V40400() { // V40400 on DL260 / H2-ECOM100 absolute mode → PDU 0x2100 (decimal 8448), NOT 0x4100 // which a naive octal-to-decimal of 40400 octal would give (= 16640). DirectLogicAddress.SystemVMemoryBasePdu.ShouldBe((ushort)0x2100); DirectLogicAddress.SystemVMemoryToPdu(0).ShouldBe((ushort)0x2100); } [Fact] public void SystemVMemoryToPdu_offsets_within_bank() { DirectLogicAddress.SystemVMemoryToPdu(1).ShouldBe((ushort)0x2101); DirectLogicAddress.SystemVMemoryToPdu(0x100).ShouldBe((ushort)0x2200); } [Fact] public void SystemVMemoryToPdu_rejects_overflow() { // ushort wrap: 0xFFFF - 0x2100 = 0xDEFF; anything above should throw. Should.NotThrow(() => DirectLogicAddress.SystemVMemoryToPdu(0xDEFF)); Should.Throw(() => DirectLogicAddress.SystemVMemoryToPdu(0xDF00)); } }