Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests/AbCipResolveHostTests.cs
T
Joseph Doherty c0379742bc v3(b1-abcip): RawPath read-path for AbCip (multi-device)
Apply the reviewed Modbus exemplar to the AbCip multi-device driver:
- Mapper AbCipEquipmentTagParser -> AbCipTagDefinitionFactory; TryParse -> FromTagConfig(tagConfig, rawPath, out def); drop leading-{ heuristic + the deviceHostAddress key; add inverse ToTagConfig; round-trip arrays/members/safety for coverage.
- Options: Tags (typed) -> RawTags (RawTagEntry list); Devices list kept.
- Driver: byName-only resolver over _tagsByRawPath (Ordinal); table built from RawTags via FromTagConfig, threading entry.DeviceName (device routing key) + entry.WriteIdempotent onto the def. Multi-device routing keys on DeviceName-or-host via a _devicesByRoutingKey index; discovery groups _declaredTags by RoutesToDevice.
- Factory: retire the pre-declared Tags DTO/BuildTag; bind List<RawTagEntry> RawTags.
- Probe: derive probe tag path from RawTags via the mapper.
- Cli BuildOptions projects typed tags -> RawTagEntry (ToTagConfig).
- Tests: AbCipRawTags helper (typed def -> RawTagEntry); parser tests -> FromTagConfig; blob-fallback driver tests rewritten to author via RawTags + read by RawPath. Driver.AbCip.Tests 342/342, Cli.Tests 42/42.

TODO(v3 WaveC): DeviceName becomes a pure logical name once the device connection host moves to the Device row's DeviceConfig.
2026-07-15 20:18:28 -04:00

51 lines
1.9 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.Tests;
/// <summary>
/// CONV-4 — <see cref="AbCipDriver.ResolveHost"/> keys per-host resilience (bulkhead / circuit
/// breaker). It must resolve an authored tag (by its RawPath) to its OWN device host, not fall back
/// to the first device — otherwise a broken device B trips device A's breaker. Unknown refs keep the
/// current first-device fallback.
/// </summary>
[Trait("Category", "Unit")]
public sealed class AbCipResolveHostTests
{
private const string DeviceA = "ab://10.0.0.5/1,0";
private const string DeviceB = "ab://10.0.0.6/1,0";
private static AbCipDriver NewDriver(params RawTagEntry[] rawTags) => new(
new AbCipDriverOptions
{
Devices = [new AbCipDeviceOptions(DeviceA), new AbCipDeviceOptions(DeviceB)],
RawTags = rawTags,
Probe = new AbCipProbeOptions { Enabled = false },
},
"abcip-1", new FakeAbCipTagFactory());
/// <summary>An authored tag routed to device B resolves to device B's host, not the first device.</summary>
[Fact]
public async Task ResolveHost_TagRoutedToDeviceB_ReturnsItsOwnDeviceHost()
{
var drv = NewDriver(new RawTagEntry(
RawPath: "line1/motorB/speed",
TagConfig: """{"tagPath":"Motor1.Speed","dataType":"DInt"}""",
WriteIdempotent: false,
DeviceName: DeviceB));
await drv.InitializeAsync("{}", CancellationToken.None);
drv.ResolveHost("line1/motorB/speed").ShouldBe(DeviceB);
}
/// <summary>An unresolvable ref keeps the current first-device fallback.</summary>
[Fact]
public void ResolveHost_UnknownRef_KeepsCurrentFallback()
{
var drv = NewDriver();
drv.ResolveHost("totally-unknown").ShouldBe(DeviceA);
}
}