using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy; namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests; [Trait("Category", "Unit")] public sealed class AbLegacyBitRmwTests { [Fact] public async Task Bit_set_reads_parent_word_ORs_bit_writes_back() { var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = (short)0b0001 }, }; var drv = new AbLegacyDriver(new AbLegacyDriverOptions { Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")], Tags = [new AbLegacyTagDefinition("Flag3", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)], Probe = new AbLegacyProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); var results = await drv.WriteAsync( [new WriteRequest("Flag3", true)], CancellationToken.None); results.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.Good); factory.Tags.ShouldContainKey("N7:0"); // parent word runtime created Convert.ToInt32(factory.Tags["N7:0"].Value).ShouldBe(0b1001); } [Fact] public async Task Bit_clear_preserves_other_bits_in_N_file_word() { var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = unchecked((short)0xFFFF) }, }; var drv = new AbLegacyDriver(new AbLegacyDriverOptions { Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")], Tags = [new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)], Probe = new AbLegacyProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.WriteAsync([new WriteRequest("F", false)], CancellationToken.None); Convert.ToInt32(factory.Tags["N7:0"].Value).ShouldBe(unchecked((short)0xFFF7)); } [Fact] public async Task Concurrent_bit_writes_to_same_word_compose_correctly() { var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = (short)0 }, }; var tags = Enumerable.Range(0, 8) .Select(b => new AbLegacyTagDefinition($"Bit{b}", "ab://10.0.0.5/1,0", $"N7:0/{b}", AbLegacyDataType.Bit)) .ToArray(); var drv = new AbLegacyDriver(new AbLegacyDriverOptions { Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")], Tags = tags, Probe = new AbLegacyProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await Task.WhenAll(Enumerable.Range(0, 8).Select(b => drv.WriteAsync([new WriteRequest($"Bit{b}", true)], CancellationToken.None))); Convert.ToInt32(factory.Tags["N7:0"].Value).ShouldBe(0xFF); } [Fact] public async Task Repeat_bit_writes_reuse_parent_runtime() { var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = (short)0 }, }; var drv = new AbLegacyDriver(new AbLegacyDriverOptions { Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")], Tags = [ new AbLegacyTagDefinition("Bit0", "ab://10.0.0.5/1,0", "N7:0/0", AbLegacyDataType.Bit), new AbLegacyTagDefinition("Bit5", "ab://10.0.0.5/1,0", "N7:0/5", AbLegacyDataType.Bit), ], Probe = new AbLegacyProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.WriteAsync([new WriteRequest("Bit0", true)], CancellationToken.None); await drv.WriteAsync([new WriteRequest("Bit5", true)], CancellationToken.None); factory.Tags["N7:0"].InitializeCount.ShouldBe(1); factory.Tags["N7:0"].WriteCount.ShouldBe(2); Convert.ToInt32(factory.Tags["N7:0"].Value).ShouldBe(0x21); // bits 0 + 5 } }