merge: Task 3 — AbLegacy seam decoupled from libplctag types
This commit is contained in:
@@ -112,7 +112,7 @@ public sealed class AbLegacyArrayTests
|
||||
var (drv, factory) = NewDriver(
|
||||
new AbLegacyTagDefinition("Levels", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int, ArrayLength: 5));
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
factory.Customise = p => new FakeAbLegacyTag(p) { Status = (int)libplctag.Status.ErrorTimeout };
|
||||
factory.Customise = p => new FakeAbLegacyTag(p) { Status = (int)ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.AbStatus.Timeout };
|
||||
|
||||
var snapshots = await drv.ReadAsync(["Levels"], CancellationToken.None);
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
public async Task Bit_write_surfaces_device_rejection_status()
|
||||
{
|
||||
// Arrange: the parent tag reads OK (Status = 0) but write returns a timeout error.
|
||||
const int errorTimeout = (int)libplctag.Status.ErrorTimeout;
|
||||
const int errorTimeout = (int)ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport.AbStatus.Timeout;
|
||||
var factory = new FakeAbLegacyTagFactory
|
||||
{
|
||||
Customise = p => new FakeAbLegacyTag(p)
|
||||
@@ -252,6 +252,6 @@ public sealed class AbLegacyBitRmwTests
|
||||
// Assert: the write rejection must NOT be silently swallowed as Good.
|
||||
var statusCode = results.Single().StatusCode;
|
||||
statusCode.ShouldNotBe(AbLegacyStatusMapper.Good);
|
||||
statusCode.ShouldBe(AbLegacyStatusMapper.MapLibplctagStatus(errorTimeout));
|
||||
statusCode.ShouldBe(AbLegacyStatusMapper.MapStatus(errorTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
using libplctag;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
@@ -42,7 +42,7 @@ public sealed class AbLegacyEvictOnFailureTests
|
||||
{
|
||||
callCount++;
|
||||
return callCount == 1
|
||||
? new FakeAbLegacyTag(p) { Status = (int)Status.ErrorBadConnection }
|
||||
? new FakeAbLegacyTag(p) { Status = (int)AbStatus.BadConnection }
|
||||
: new FakeAbLegacyTag(p) { Status = 0, Value = 42 };
|
||||
};
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -94,7 +94,7 @@ public sealed class AbLegacyEvictOnFailureTests
|
||||
{
|
||||
callCount++;
|
||||
return callCount == 1
|
||||
? new FakeAbLegacyTag(p) { Status = (int)Status.ErrorBadConnection }
|
||||
? new FakeAbLegacyTag(p) { Status = (int)AbStatus.BadConnection }
|
||||
: new FakeAbLegacyTag(p) { Status = 0 };
|
||||
};
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
+62
-28
@@ -1,6 +1,7 @@
|
||||
using libplctag;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
@@ -65,42 +66,75 @@ public sealed class AbLegacyHostAndStatusTests
|
||||
AbLegacyStatusMapper.MapPcccStatus(sts).ShouldBe(expected);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that libplctag Status enum members are mapped to OPC UA status codes correctly.</summary>
|
||||
/// <param name="status">The libplctag Status enum value to map.</param>
|
||||
/// <summary>Verifies that AbStatus members map to OPC UA status codes correctly.</summary>
|
||||
/// <param name="status">The AbStatus value to map.</param>
|
||||
/// <param name="expected">The expected OPC UA status code.</param>
|
||||
// Driver.AbLegacy-010 — tests use the libplctag.NET Status enum members (what
|
||||
// (int)Tag.GetStatus() actually returns) rather than the unverified magic integers
|
||||
// that predated this fix (-5/-7/-14/-16/-17 matched neither native PLCTAG_ERR_*
|
||||
// constants nor the .NET wrapper enum ordinals reliably).
|
||||
[Theory]
|
||||
[InlineData(Status.Ok, AbLegacyStatusMapper.Good)]
|
||||
[InlineData(Status.Pending, AbLegacyStatusMapper.GoodMoreData)]
|
||||
[InlineData(Status.ErrorTimeout, AbLegacyStatusMapper.BadTimeout)]
|
||||
[InlineData(Status.ErrorNotFound, AbLegacyStatusMapper.BadNodeIdUnknown)]
|
||||
[InlineData(Status.ErrorNoMatch, AbLegacyStatusMapper.BadNodeIdUnknown)]
|
||||
[InlineData(Status.ErrorNotAllowed, AbLegacyStatusMapper.BadNotWritable)]
|
||||
[InlineData(Status.ErrorOutOfBounds, AbLegacyStatusMapper.BadOutOfRange)]
|
||||
[InlineData(Status.ErrorTooLarge, AbLegacyStatusMapper.BadOutOfRange)]
|
||||
[InlineData(Status.ErrorBadConnection, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
[InlineData(Status.ErrorBadGateway, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
[InlineData(Status.ErrorUnsupported, AbLegacyStatusMapper.BadNotSupported)]
|
||||
[InlineData(Status.ErrorNoMem, AbLegacyStatusMapper.BadCommunicationError)] // unmapped → generic comms
|
||||
public void LibplctagStatus_maps_real_enum_members(Status status, uint expected)
|
||||
[InlineData(AbStatus.Ok, AbLegacyStatusMapper.Good)]
|
||||
[InlineData(AbStatus.Pending, AbLegacyStatusMapper.GoodMoreData)]
|
||||
[InlineData(AbStatus.Timeout, AbLegacyStatusMapper.BadTimeout)]
|
||||
[InlineData(AbStatus.NotFound, AbLegacyStatusMapper.BadNodeIdUnknown)]
|
||||
[InlineData(AbStatus.NotAllowed, AbLegacyStatusMapper.BadNotWritable)]
|
||||
[InlineData(AbStatus.OutOfBounds, AbLegacyStatusMapper.BadOutOfRange)]
|
||||
[InlineData(AbStatus.TooLarge, AbLegacyStatusMapper.BadOutOfRange)]
|
||||
[InlineData(AbStatus.TooSmall, AbLegacyStatusMapper.BadOutOfRange)]
|
||||
[InlineData(AbStatus.Unsupported, AbLegacyStatusMapper.BadNotSupported)]
|
||||
[InlineData(AbStatus.TypeMismatch, AbLegacyStatusMapper.BadTypeMismatch)]
|
||||
[InlineData(AbStatus.DeviceStateConflict, AbLegacyStatusMapper.BadDeviceFailure)]
|
||||
[InlineData(AbStatus.BadConnection, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
[InlineData(AbStatus.BadReply, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
[InlineData(AbStatus.Partial, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
[InlineData(AbStatus.Abort, AbLegacyStatusMapper.BadCommunicationError)]
|
||||
public void AbStatus_maps_to_opcua_codes(AbStatus status, uint expected)
|
||||
{
|
||||
AbLegacyStatusMapper.MapLibplctagStatus(status).ShouldBe(expected);
|
||||
AbLegacyStatusMapper.MapStatus(status).ShouldBe(expected);
|
||||
// The int overload must agree — it is the seam IAbLegacyTagRuntime.GetStatus() drives.
|
||||
AbLegacyStatusMapper.MapLibplctagStatus((int)status).ShouldBe(expected);
|
||||
AbLegacyStatusMapper.MapStatus((int)status).ShouldBe(expected);
|
||||
}
|
||||
|
||||
/// <summary>MapException reads the status off an AbTransportException, else falls back to comms error.</summary>
|
||||
[Fact]
|
||||
public void MapException_uses_transport_status()
|
||||
{
|
||||
AbLegacyStatusMapper.MapException(new AbTransportException(AbStatus.Timeout, "t"))
|
||||
.ShouldBe(AbLegacyStatusMapper.BadTimeout);
|
||||
AbLegacyStatusMapper.MapException(new InvalidOperationException("x"))
|
||||
.ShouldBe(AbLegacyStatusMapper.BadCommunicationError);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The regression oracle: the OPC UA code the pre-seam <c>MapLibplctagStatus(Status)</c> returned,
|
||||
/// copied verbatim from the deleted switch. For every libplctag member the new
|
||||
/// translation + <see cref="AbLegacyStatusMapper.MapStatus(AbStatus)"/> must reproduce it.
|
||||
/// </summary>
|
||||
private static uint OldMapLibplctagStatus(Status status) => status switch
|
||||
{
|
||||
Status.Ok => AbLegacyStatusMapper.Good,
|
||||
Status.Pending => AbLegacyStatusMapper.GoodMoreData,
|
||||
Status.ErrorTimeout => AbLegacyStatusMapper.BadTimeout,
|
||||
Status.ErrorNotFound or Status.ErrorNoMatch or Status.ErrorBadDevice => AbLegacyStatusMapper.BadNodeIdUnknown,
|
||||
Status.ErrorNotAllowed => AbLegacyStatusMapper.BadNotWritable,
|
||||
Status.ErrorOutOfBounds or Status.ErrorTooLarge or Status.ErrorTooSmall => AbLegacyStatusMapper.BadOutOfRange,
|
||||
Status.ErrorUnsupported or Status.ErrorNotImplemented => AbLegacyStatusMapper.BadNotSupported,
|
||||
_ => AbLegacyStatusMapper.BadCommunicationError,
|
||||
};
|
||||
|
||||
/// <summary>Every libplctag Status member maps to the same OPC UA code as before the seam.</summary>
|
||||
[Fact]
|
||||
public void Translation_plus_mapper_matches_the_old_libplctag_switch_for_every_member()
|
||||
{
|
||||
foreach (var s in Enum.GetValues<Status>())
|
||||
{
|
||||
AbLegacyStatusMapper.MapStatus(LibplctagLegacyStatusTranslation.ToAbStatus(s))
|
||||
.ShouldBe(OldMapLibplctagStatus(s), $"libplctag Status.{s}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that timeout errors are distinguished from generic communication errors.</summary>
|
||||
[Fact]
|
||||
public void MapLibplctagStatus_distinguishes_timeout_from_generic_comms_error()
|
||||
public void MapStatus_distinguishes_timeout_from_generic_comms_error()
|
||||
{
|
||||
// Regression for Driver.AbLegacy-010: timeout must not fall through to
|
||||
// BadCommunicationError the way the old magic-integer switch did.
|
||||
AbLegacyStatusMapper.MapLibplctagStatus((int)Status.ErrorTimeout)
|
||||
.ShouldBe(AbLegacyStatusMapper.BadTimeout);
|
||||
AbLegacyStatusMapper.MapLibplctagStatus((int)Status.ErrorNotFound)
|
||||
.ShouldBe(AbLegacyStatusMapper.BadNodeIdUnknown);
|
||||
AbLegacyStatusMapper.MapStatus((int)AbStatus.Timeout).ShouldBe(AbLegacyStatusMapper.BadTimeout);
|
||||
AbLegacyStatusMapper.MapStatus((int)AbStatus.NotFound).ShouldBe(AbLegacyStatusMapper.BadNodeIdUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using libplctag;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
@@ -70,12 +70,12 @@ public sealed class AbLegacyReadWriteTests
|
||||
[Fact]
|
||||
public async Task NonZero_libplctag_status_maps_via_AbLegacyStatusMapper()
|
||||
{
|
||||
// Use the real libplctag.Status enum value rather than a raw integer so the test
|
||||
// Use the real AbStatus enum value rather than a raw integer so the test
|
||||
// stays correct if the wrapper renumbers its ordinals (Driver.AbLegacy-010).
|
||||
var (drv, factory) = NewDriver(
|
||||
new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int));
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
factory.Customise = p => new FakeAbLegacyTag(p) { Status = (int)Status.ErrorNotFound };
|
||||
factory.Customise = p => new FakeAbLegacyTag(p) { Status = (int)AbStatus.NotFound };
|
||||
|
||||
var snapshots = await drv.ReadAsync(["X"], CancellationToken.None);
|
||||
snapshots.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.BadNodeIdUnknown);
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using libplctag;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
|
||||
public sealed class LibplctagLegacyStatusTranslationTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(Status.Ok, AbStatus.Ok)]
|
||||
[InlineData(Status.Pending, AbStatus.Pending)]
|
||||
[InlineData(Status.ErrorTimeout, AbStatus.Timeout)]
|
||||
[InlineData(Status.ErrorNotFound, AbStatus.NotFound)]
|
||||
[InlineData(Status.ErrorNoMatch, AbStatus.NotFound)]
|
||||
[InlineData(Status.ErrorBadDevice, AbStatus.NotFound)]
|
||||
[InlineData(Status.ErrorNotAllowed, AbStatus.NotAllowed)]
|
||||
[InlineData(Status.ErrorOutOfBounds, AbStatus.OutOfBounds)]
|
||||
[InlineData(Status.ErrorTooLarge, AbStatus.TooLarge)]
|
||||
[InlineData(Status.ErrorTooSmall, AbStatus.TooSmall)]
|
||||
[InlineData(Status.ErrorUnsupported, AbStatus.Unsupported)]
|
||||
[InlineData(Status.ErrorNotImplemented, AbStatus.Unsupported)]
|
||||
[InlineData(Status.ErrorPartial, AbStatus.Partial)]
|
||||
[InlineData(Status.ErrorAbort, AbStatus.Abort)]
|
||||
[InlineData(Status.ErrorBadReply, AbStatus.BadReply)]
|
||||
[InlineData(Status.ErrorBadGateway, AbStatus.BadConnection)]
|
||||
[InlineData(Status.ErrorWinsock, AbStatus.BadConnection)]
|
||||
public void Translates(Status from, AbStatus to) => LibplctagLegacyStatusTranslation.ToAbStatus(from).ShouldBe(to);
|
||||
|
||||
[Fact]
|
||||
public void Exception_message_is_recovered_as_the_enum_name()
|
||||
{
|
||||
LibplctagLegacyStatusTranslation.ToAbStatus(new LibPlcTagException("ErrorNotFound")).ShouldBe(AbStatus.NotFound);
|
||||
LibplctagLegacyStatusTranslation.ToAbStatus(new LibPlcTagException("gibberish")).ShouldBe(AbStatus.BadConnection);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user