using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies; namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests; /// /// #646 against ab_server: N7:[N7:0] reads the index word, then the word it names, on both /// transports. Writes N7:0 and N7:5 (simulator-only, gated by ). /// [Collection(AbLegacyServerCollection.Name)] [Trait("Category", "Integration")] [Trait("Simulator", "ab_server-PCCC")] public sealed class AbLegacyIndirectAddressingTests(AbLegacyServerFixture sim) { /// Seeds N7:0=5 and N7:5=42, then reads and writes through N7:[N7:0]. /// The transport stack the driver runs on. [AbLegacyTheory] [MemberData(nameof(TransportMatrix.Transports), MemberType = typeof(TransportMatrix))] public async Task Slc500_indirect_word_read_and_write_resolve_through_the_index(AbLegacyTransport transport) { if (sim.SkipReasonFor(KnownProfiles.Slc500) is { } skip) Assert.Skip(skip); IntegrationWriteGate.SkipUnlessWritesAllowed(); var deviceUri = $"ab://{sim.Host}:{sim.PortFor(KnownProfiles.Slc500)}/{sim.CipPath}"; await using var drv = new AbLegacyDriver(new AbLegacyDriverOptions { Devices = [new AbLegacyDeviceOptions(deviceUri, AbLegacyPlcFamily.Slc500)], RawTags = AbLegacyRawTags.Entries( new AbLegacyTagDefinition("Index", deviceUri, "N7:0", AbLegacyDataType.Int, Writable: true), new AbLegacyTagDefinition("Target", deviceUri, "N7:5", AbLegacyDataType.Int, Writable: true), new AbLegacyTagDefinition("Indirect", deviceUri, "N7:[N7:0]", AbLegacyDataType.Int, Writable: true)), Timeout = TimeSpan.FromSeconds(5), Probe = new AbLegacyProbeOptions { Enabled = false }, Transport = transport, }, driverInstanceId: $"ablegacy-indirect-{transport}"); var ct = TestContext.Current.CancellationToken; await drv.InitializeAsync("{}", ct); var seeded = await drv.WriteAsync( [new WriteRequest("Index", (short)5), new WriteRequest("Target", (short)42)], ct); seeded.ShouldAllBe(r => r.StatusCode == AbLegacyStatusMapper.Good); var read = (await drv.ReadAsync(["Indirect"], ct)).Single(); read.StatusCode.ShouldBe(AbLegacyStatusMapper.Good); Convert.ToInt32(read.Value).ShouldBe(42); // Write through the indirection lands on N7:5. (await drv.WriteAsync([new WriteRequest("Indirect", (short)77)], ct)).Single() .StatusCode.ShouldBe(AbLegacyStatusMapper.Good); var direct = (await drv.ReadAsync(["Target"], ct)).Single(); Convert.ToInt32(direct.Value).ShouldBe(77); } }