using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.AbCip; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; /// /// Regression coverage for Driver.AbCip-013 โ€” driver-specs.md ยง3 lists per-device /// AllowPacking and ConnectionSize as configurable connection settings, but /// the implementation previously hard-coded both from the family profile and never wired /// them through to the libplctag Tag. The fix exposes them as per-device options on /// , plumbs them through , /// and applies AllowPacking to the live Tag. ConnectionSize is captured /// for forward-compat (libplctag 1.5.2 does not expose a direct property; the value is /// plumbed through the create-params so future wrappers / a custom tag-attribute path can /// consume it without a config-shape change). /// [Trait("Category", "Unit")] public sealed class AbCipPerDeviceConnectionOptionsTests { private const string Device = "ab://10.0.0.5/1,0"; [Fact] public async Task Device_AllowPacking_override_is_forwarded_to_tag_create_params() { // Driver.AbCip-013 โ€” operator opts out of request packing on a specific device. var factory = new FakeAbCipTagFactory(); var drv = new AbCipDriver(new AbCipDriverOptions { Devices = [new AbCipDeviceOptions(Device, AllowPacking: false)], Tags = [new AbCipTagDefinition("Speed", Device, "Speed", AbCipDataType.DInt)], Probe = new AbCipProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.ReadAsync(["Speed"], CancellationToken.None); factory.Tags["Speed"].CreationParams.AllowPacking.ShouldBeFalse(); } [Fact] public async Task Device_AllowPacking_default_inherits_from_family_profile() { // No per-device override โ€” default falls back to the family profile's value (true for // ControlLogix, false for Micro800). var factory = new FakeAbCipTagFactory(); var drv = new AbCipDriver(new AbCipDriverOptions { Devices = [new AbCipDeviceOptions(Device)], Tags = [new AbCipTagDefinition("Speed", Device, "Speed", AbCipDataType.DInt)], Probe = new AbCipProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.ReadAsync(["Speed"], CancellationToken.None); // ControlLogix profile has SupportsRequestPacking = true. factory.Tags["Speed"].CreationParams.AllowPacking.ShouldBeTrue(); } [Fact] public async Task Micro800_default_AllowPacking_is_false_from_family_profile() { const string micro = "ab://10.0.0.6/"; var factory = new FakeAbCipTagFactory(); var drv = new AbCipDriver(new AbCipDriverOptions { Devices = [new AbCipDeviceOptions(micro, AbCipPlcFamily.Micro800)], Tags = [new AbCipTagDefinition("X", micro, "X", AbCipDataType.DInt)], Probe = new AbCipProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.ReadAsync(["X"], CancellationToken.None); // Micro800 profile defaults SupportsRequestPacking = false. factory.Tags["X"].CreationParams.AllowPacking.ShouldBeFalse(); } [Fact] public async Task Device_ConnectionSize_override_is_forwarded_to_tag_create_params() { var factory = new FakeAbCipTagFactory(); var drv = new AbCipDriver(new AbCipDriverOptions { Devices = [new AbCipDeviceOptions(Device, ConnectionSize: 504)], Tags = [new AbCipTagDefinition("Speed", Device, "Speed", AbCipDataType.DInt)], Probe = new AbCipProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.ReadAsync(["Speed"], CancellationToken.None); factory.Tags["Speed"].CreationParams.ConnectionSize.ShouldBe(504); } [Fact] public async Task Device_ConnectionSize_default_inherits_from_family_profile() { var factory = new FakeAbCipTagFactory(); var drv = new AbCipDriver(new AbCipDriverOptions { Devices = [new AbCipDeviceOptions(Device)], Tags = [new AbCipTagDefinition("Speed", Device, "Speed", AbCipDataType.DInt)], Probe = new AbCipProbeOptions { Enabled = false }, }, "drv-1", factory); await drv.InitializeAsync("{}", CancellationToken.None); await drv.ReadAsync(["Speed"], CancellationToken.None); // ControlLogix family default ConnectionSize is 4002 (Large Forward Open). factory.Tags["Speed"].CreationParams.ConnectionSize.ShouldBe(4002); } [Fact] public void AbCipDriverFactoryExtensions_ParseOptions_round_trips_AllowPacking_and_ConnectionSize() { const string json = """ { "Devices": [ { "HostAddress": "ab://10.0.0.5/1,0", "PlcFamily": "ControlLogix", "AllowPacking": false, "ConnectionSize": 504 } ] } """; var opts = AbCipDriverFactoryExtensions.ParseOptions("drv-1", json); opts.Devices.Single().AllowPacking.ShouldBe(false); opts.Devices.Single().ConnectionSize.ShouldBe(504); } }