test(ab): hw4 evidence - mixed MSP read (#582), listing order (#572) on the press; AbLegacy bit-write test; gate-results + docs
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.PlcFamilies;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.IntegrationTests;
|
||||
|
||||
/// <summary>
|
||||
/// Bit-within-word write (<c>B3:0/5</c>) against <c>ab_server</c> on both transports: only the addressed
|
||||
/// bit may change, its neighbours must survive. Writes B3:0 (simulator-only, gated by
|
||||
/// <see cref="IntegrationWriteGate"/>). PLC-5 on the native transport refuses writes (#571), which the test pins instead.
|
||||
/// </summary>
|
||||
[Collection(AbLegacyServerCollection.Name)]
|
||||
[Trait("Category", "Integration")]
|
||||
[Trait("Simulator", "ab_server-PCCC")]
|
||||
public sealed class AbLegacyBitWriteTests(AbLegacyServerFixture sim)
|
||||
{
|
||||
/// <summary>Seeds B3:0 = 0x0A0A, sets bit 5, clears bit 3, and checks neighbours are untouched.</summary>
|
||||
/// <param name="transport">The transport stack the driver runs on.</param>
|
||||
[AbLegacyTheory]
|
||||
[MemberData(nameof(TransportMatrix.Profiles), MemberType = typeof(TransportMatrix))]
|
||||
public async Task Bit_write_changes_only_the_addressed_bit(AbLegacyServerProfile profile, AbLegacyTransport transport)
|
||||
{
|
||||
if (sim.SkipReasonFor(profile) is { } skip) Assert.Skip(skip);
|
||||
IntegrationWriteGate.SkipUnlessWritesAllowed();
|
||||
|
||||
var deviceUri = $"ab://{sim.Host}:{sim.PortFor(profile)}/{sim.CipPath}";
|
||||
await using var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions(deviceUri, profile.Family)],
|
||||
RawTags = AbLegacyRawTags.Entries(
|
||||
new AbLegacyTagDefinition("Word", deviceUri, "B3:0", AbLegacyDataType.Int, Writable: true),
|
||||
new AbLegacyTagDefinition("Bit5", deviceUri, "B3:0/5", AbLegacyDataType.Bit, Writable: true),
|
||||
new AbLegacyTagDefinition("Bit3", deviceUri, "B3:0/3", AbLegacyDataType.Bit, Writable: true)),
|
||||
Timeout = TimeSpan.FromSeconds(5),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
Transport = transport,
|
||||
}, driverInstanceId: $"ablegacy-bitwrite-{profile.Family}-{transport}");
|
||||
var ct = TestContext.Current.CancellationToken;
|
||||
await drv.InitializeAsync("{}", ct);
|
||||
|
||||
var refusedNatively = profile.Family == AbLegacyPlcFamily.Plc5 && transport == AbLegacyTransport.Native;
|
||||
|
||||
// 0x0A0A: bits 1,3,9,11 set; bit 5 clear.
|
||||
if (!refusedNatively)
|
||||
(await drv.WriteAsync([new WriteRequest("Word", (short)0x0A0A)], ct)).Single()
|
||||
.StatusCode.ShouldBe(AbLegacyStatusMapper.Good, "seeding the whole word must succeed");
|
||||
|
||||
if (refusedNatively)
|
||||
{
|
||||
// Native PLC-5 writes are refused before the wire (#571): BadNotSupported (0x803D0000), not an ab_server gap.
|
||||
(await drv.WriteAsync([new WriteRequest("Bit5", true)], ct)).Single()
|
||||
.StatusCode.ShouldBe(AbLegacyStatusMapper.BadNotSupported);
|
||||
return;
|
||||
}
|
||||
|
||||
(await drv.WriteAsync([new WriteRequest("Bit5", true)], ct)).Single()
|
||||
.StatusCode.ShouldBe(AbLegacyStatusMapper.Good, "B3:0/5 bit write must succeed");
|
||||
Convert.ToInt32((await drv.ReadAsync(["Word"], ct)).Single().Value).ShouldBe(0x0A0A | 0x20);
|
||||
|
||||
(await drv.WriteAsync([new WriteRequest("Bit3", false)], ct)).Single()
|
||||
.StatusCode.ShouldBe(AbLegacyStatusMapper.Good, "clearing B3:0/3 must succeed");
|
||||
Convert.ToInt32((await drv.ReadAsync(["Word"], ct)).Single().Value).ShouldBe((0x0A0A | 0x20) & ~0x08);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user