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";
/// Verifies that per-device AllowPacking override is forwarded to tag creation parameters.
[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();
}
/// Verifies that AllowPacking defaults inherit from the family profile when not overridden.
[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();
}
/// Verifies that Micro800 devices have AllowPacking defaulting to false from the family profile.
[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();
}
/// Verifies that per-device ConnectionSize override is forwarded to tag creation parameters.
[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);
}
/// Verifies that ConnectionSize defaults inherit from the family profile when not overridden.
[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);
}
/// Verifies that AllowPacking and ConnectionSize round-trip correctly through JSON parsing.
[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);
}
}