- Driver.AbCip-007: inject an optional ILogger<AbCipDriver> / ILogger<AbCipAlarmProjection> (default NullLogger) and log around every read / write / template-fetch / probe / alarm-poll failure path. - Driver.AbCip-011: LogWarning when InitializeAsync is configured with Probe.Enabled=true but ProbeTagPath is blank — operators now see why GetHostStatuses keeps reporting Unknown. - Driver.AbCip-012: documented the LibplctagTemplateReader per-call Tag cost as accepted given libplctag's own connection pool and the low-frequency discovery use-case. - Driver.AbCip-013: per-device AllowPacking + ConnectionSize overrides on AbCipDeviceOptions, threaded through AbCipTagCreateParams; central BuildCreateParams helper replaces five ad-hoc clones; AllowPacking now reaches Tag.AllowPacking at runtime. - Driver.AbCip-015: stale-comment sweep — every PR-N forward-reference is rewritten to describe present behaviour. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
135 lines
5.5 KiB
C#
135 lines
5.5 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests;
|
|
|
|
/// <summary>
|
|
/// Regression coverage for Driver.AbCip-013 — driver-specs.md §3 lists per-device
|
|
/// <c>AllowPacking</c> and <c>ConnectionSize</c> as configurable connection settings, but
|
|
/// the implementation previously hard-coded both from the family profile and never wired
|
|
/// them through to the libplctag <c>Tag</c>. The fix exposes them as per-device options on
|
|
/// <see cref="AbCipDeviceOptions"/>, plumbs them through <see cref="AbCipTagCreateParams"/>,
|
|
/// and applies <c>AllowPacking</c> to the live <c>Tag</c>. <c>ConnectionSize</c> 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).
|
|
/// </summary>
|
|
[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);
|
|
}
|
|
}
|