Auto: ablegacy-3 — sub-element bit semantics

Closes #246
This commit is contained in:
Joseph Doherty
2026-04-25 13:41:52 -04:00
parent 07235d3b66
commit c89f5bb3b9
6 changed files with 350 additions and 5 deletions

View File

@@ -256,4 +256,113 @@ public sealed class AbLegacyReadWriteTests
Value = value;
}
}
// ---- Timer / Counter / Control sub-element bit semantics (issue #246) ----
[Theory]
[InlineData("T4:0.DN", 13)]
[InlineData("T4:0.TT", 14)]
[InlineData("T4:0.EN", 15)]
public async Task Timer_status_bit_decodes_correct_position(string address, int bitPos)
{
var (drv, factory) = NewDriver(
new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", address, AbLegacyDataType.TimerElement));
await drv.InitializeAsync("{}", CancellationToken.None);
// Seed a parent-word with only the target bit set.
factory.Customise = p => new FakeAbLegacyTag(p) { Value = 1 << bitPos };
var snapshots = await drv.ReadAsync(["X"], CancellationToken.None);
snapshots.Single().Value.ShouldBe(true);
// The driver must have asked the runtime for the right bit position.
factory.Tags[address].LastDecodeBitIndex.ShouldBe(bitPos);
}
[Fact]
public async Task Timer_PRE_subelement_decodes_as_int_word()
{
var (drv, factory) = NewDriver(
new AbLegacyTagDefinition("Pre", "ab://10.0.0.5/1,0", "T4:0.PRE", AbLegacyDataType.TimerElement));
await drv.InitializeAsync("{}", CancellationToken.None);
factory.Customise = p => new FakeAbLegacyTag(p) { Value = 5000 };
var snapshots = await drv.ReadAsync(["Pre"], CancellationToken.None);
snapshots.Single().Value.ShouldBe(5000);
factory.Tags["T4:0.PRE"].LastDecodeBitIndex.ShouldBeNull();
}
[Theory]
[InlineData("C5:0.UN", 10)]
[InlineData("C5:0.OV", 11)]
[InlineData("C5:0.DN", 12)]
[InlineData("C5:0.CD", 13)]
[InlineData("C5:0.CU", 14)]
public async Task Counter_status_bit_decodes_correct_position(string address, int bitPos)
{
var (drv, factory) = NewDriver(
new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", address, AbLegacyDataType.CounterElement));
await drv.InitializeAsync("{}", CancellationToken.None);
factory.Customise = p => new FakeAbLegacyTag(p) { Value = 1 << bitPos };
var snapshots = await drv.ReadAsync(["X"], CancellationToken.None);
snapshots.Single().Value.ShouldBe(true);
factory.Tags[address].LastDecodeBitIndex.ShouldBe(bitPos);
}
[Theory]
[InlineData("R6:0.FD", 8)]
[InlineData("R6:0.IN", 9)]
[InlineData("R6:0.UL", 10)]
[InlineData("R6:0.ER", 11)]
[InlineData("R6:0.EM", 12)]
[InlineData("R6:0.DN", 13)]
[InlineData("R6:0.EU", 14)]
[InlineData("R6:0.EN", 15)]
public async Task Control_status_bit_decodes_correct_position(string address, int bitPos)
{
var (drv, factory) = NewDriver(
new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", address, AbLegacyDataType.ControlElement));
await drv.InitializeAsync("{}", CancellationToken.None);
factory.Customise = p => new FakeAbLegacyTag(p) { Value = 1 << bitPos };
var snapshots = await drv.ReadAsync(["X"], CancellationToken.None);
snapshots.Single().Value.ShouldBe(true);
factory.Tags[address].LastDecodeBitIndex.ShouldBe(bitPos);
}
[Fact]
public async Task Status_bit_returns_false_when_parent_word_bit_is_clear()
{
var (drv, factory) = NewDriver(
new AbLegacyTagDefinition("Done", "ab://10.0.0.5/1,0", "T4:0.DN", AbLegacyDataType.TimerElement));
await drv.InitializeAsync("{}", CancellationToken.None);
// Bit 14 (TT) set, bit 13 (DN) clear.
factory.Customise = p => new FakeAbLegacyTag(p) { Value = 1 << 14 };
var snapshots = await drv.ReadAsync(["Done"], CancellationToken.None);
snapshots.Single().Value.ShouldBe(false);
}
[Theory]
[InlineData("T4:0.DN", AbLegacyDataType.TimerElement)]
[InlineData("T4:0.TT", AbLegacyDataType.TimerElement)]
[InlineData("C5:0.DN", AbLegacyDataType.CounterElement)]
[InlineData("C5:0.OV", AbLegacyDataType.CounterElement)]
[InlineData("C5:0.UN", AbLegacyDataType.CounterElement)]
[InlineData("R6:0.ER", AbLegacyDataType.ControlElement)]
[InlineData("R6:0.EM", AbLegacyDataType.ControlElement)]
[InlineData("R6:0.DN", AbLegacyDataType.ControlElement)]
[InlineData("R6:0.FD", AbLegacyDataType.ControlElement)]
public async Task Writes_to_PLC_set_status_bits_return_BadNotWritable(
string address, AbLegacyDataType dataType)
{
var (drv, _) = NewDriver(
new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", address, dataType));
await drv.InitializeAsync("{}", CancellationToken.None);
var results = await drv.WriteAsync(
[new WriteRequest("X", true)], CancellationToken.None);
results.Single().StatusCode.ShouldBe(AbLegacyStatusMapper.BadNotWritable);
}
}