review(Driver.AbLegacy): fix Bit write 1-byte/2-byte encode-decode mismatch (Medium)
Re-review at 7286d320. -014 (Medium): Bit EncodeValue (no bitIndex) wrote SetInt8 while
DecodeValue read GetInt16 on a 16-bit B-file element, so a false write could round-trip
as true (stale high byte). Fix: SetInt16 + TDD. -015: tests pass CancellationToken.
This commit is contained in:
@@ -89,7 +89,7 @@ public sealed class AbLegacyCapabilityTests
|
||||
|
||||
var afterUnsub = events.Count;
|
||||
tagRef.Value = 999;
|
||||
await Task.Delay(300);
|
||||
await Task.Delay(300, TestContext.Current.CancellationToken);
|
||||
events.Count.ShouldBe(afterUnsub);
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public sealed class AbLegacyCapabilityTests
|
||||
Probe = new AbLegacyProbeOptions { Enabled = true, ProbeAddress = null },
|
||||
}, "drv-1");
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
await Task.Delay(200);
|
||||
await Task.Delay(200, TestContext.Current.CancellationToken);
|
||||
|
||||
drv.GetHostStatuses().Single().State.ShouldBe(HostState.Unknown);
|
||||
await drv.ShutdownAsync(CancellationToken.None);
|
||||
|
||||
@@ -189,6 +189,40 @@ public sealed class AbLegacyReadWriteTests
|
||||
results.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Driver.AbLegacy-014 — a Bit-typed tag with no bit suffix (e.g. B3:0, DataType=Bit)
|
||||
/// takes the EncodeValue(Bit, bitIndex:null, …) path (not RMW). The encode must be
|
||||
/// symmetric with the DecodeValue path, which reads the full 16-bit word via GetInt16.
|
||||
/// Through the fake factory this verifies the driver dispatches through EncodeValue with
|
||||
/// the right arguments; the SetInt16/SetInt8 delta is exercised against a live PLC.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Bit_tag_without_suffix_writes_via_EncodeValue_not_RMW()
|
||||
{
|
||||
// A Bit-typed tag with NO /N bit suffix — bitIndex is null, so write must NOT
|
||||
// route through WriteBitInWordAsync (which requires bitIndex). Instead it goes
|
||||
// through EncodeValue(Bit, null, value) on the tag's own runtime, not a parent runtime.
|
||||
var factory = new FakeAbLegacyTagFactory();
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Flag", "ab://10.0.0.5/1,0", "B3:0", AbLegacyDataType.Bit)],
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
var results = await drv.WriteAsync(
|
||||
[new WriteRequest("Flag", true)], CancellationToken.None);
|
||||
|
||||
// Must succeed (Good) and route through the tag's own runtime, NOT a parent runtime.
|
||||
results.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
|
||||
factory.Tags.ShouldContainKey("B3:0");
|
||||
factory.Tags.ShouldNotContainKey("B3"); // no parent-word runtime created
|
||||
factory.Tags["B3:0"].WriteCount.ShouldBe(1);
|
||||
// FakeAbLegacyTag.EncodeValue stores the raw value; verify it received true.
|
||||
factory.Tags["B3:0"].Value.ShouldBe(true);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that write exceptions surface as BadCommunicationError.</summary>
|
||||
[Fact]
|
||||
public async Task Write_exception_surfaces_BadCommunicationError()
|
||||
|
||||
Reference in New Issue
Block a user