afe6850f3d
The libplctag adapters assigned Tag.Path straight from the device's parsed CipPath. A device address with no route (ab://host:44818/ - the normal Micro800 form, and a MicroLogix with no route) parses to "", and libplctag rejects an empty path attribute with ErrorBadData; omitting the attribute (null) is what it accepts. So Micro800 could not connect on the default libplctag transport. This predates the native transport work and exists on master. Add an internal LibplctagPath.Normalize helper per driver project (blank -> null) and route all four Tag constructions through it (AbCip tag runtime, symbol-list enumerator, template reader; AbLegacy tag runtime). The runtime tag construction is extracted to an internal BuildTag so tests can inspect Tag.Path without connecting. Tests fail with Normalize reverted to identity. Update the Micro800 smoke-test comment: both transports are expected to pass.
62 lines
3.1 KiB
C#
62 lines
3.1 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// End-to-end smoke tests that exercise the real transport stack (libplctag and native, via <see cref="TransportMatrix"/>) against a running
|
|
/// <c>ab_server</c>. Skipped when the binary isn't on PATH (<see cref="AbServerFactAttribute"/>).
|
|
/// Parametrized over <see cref="KnownProfiles.All"/> so one test file covers every family
|
|
/// (ControlLogix / CompactLogix / Micro800 / GuardLogix).
|
|
/// </summary>
|
|
[Trait("Category", "Integration")]
|
|
[Trait("Requires", "AbServer")]
|
|
public sealed class AbCipReadSmokeTests
|
|
{
|
|
/// <summary>Verifies that the driver can read a seeded DInt value from an AB server.</summary>
|
|
/// <param name="profile">The AB server profile to test against.</param>
|
|
/// <param name="transport">The transport stack the driver runs on.</param>
|
|
[AbServerTheory]
|
|
[MemberData(nameof(TransportMatrix.Profiles), MemberType = typeof(TransportMatrix))]
|
|
public async Task Driver_reads_seeded_DInt_from_ab_server(AbServerProfile profile, AbCipTransport transport)
|
|
{
|
|
var fixture = new AbServerFixture(profile);
|
|
await fixture.InitializeAsync();
|
|
try
|
|
{
|
|
// Use fixture.Host (not hardcoded 127.0.0.1) so the docker-host migration
|
|
// (10.100.0.35) and AB_SERVER_ENDPOINT overrides reach this test too. The
|
|
// sibling Emulate tests already use the fixture's resolved endpoint; this
|
|
// smoke test was missed.
|
|
// Micro800 has no backplane: its cip-path is empty (the compose service runs
|
|
// ab_server --plc=Micro800 with no --path, which refuses a routed request). Every
|
|
// other family reaches slot 0 through `1,0`. Both transports are expected to pass
|
|
// for Micro800: the libplctag adapters map the empty route to a null `path` attribute
|
|
// (LibplctagPath.Normalize) because libplctag rejects "" with ErrorBadData.
|
|
var cipPath = profile.Family == AbCipPlcFamily.Micro800 ? "" : "1,0";
|
|
var deviceUri = $"ab://{fixture.Host}:{fixture.Port}/{cipPath}";
|
|
var drv = new AbCipDriver(new AbCipDriverOptions
|
|
{
|
|
Devices = [new AbCipDeviceOptions(deviceUri, profile.Family)],
|
|
RawTags = AbCipRawTags.From(new AbCipTagDefinition("Counter", deviceUri, "TestDINT", AbCipDataType.DInt)),
|
|
Timeout = TimeSpan.FromSeconds(5),
|
|
Transport = transport,
|
|
}, $"drv-smoke-{profile.Family}-{transport}");
|
|
|
|
await drv.InitializeAsync("{}", CancellationToken.None);
|
|
var snapshots = await drv.ReadAsync(["Counter"], CancellationToken.None);
|
|
|
|
snapshots.Single().StatusCode.ShouldBe(AbCipStatusMapper.Good);
|
|
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
|
|
|
|
await drv.ShutdownAsync(CancellationToken.None);
|
|
}
|
|
finally
|
|
{
|
|
await fixture.DisposeAsync();
|
|
}
|
|
}
|
|
}
|