diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs
index 8f0b07a0..f5a196c3 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/AbLegacyBitRmwTests.cs
@@ -177,4 +177,38 @@ public sealed class AbLegacyBitRmwTests
Convert.ToInt32(factory.Tags["B3:0"].Value).ShouldBe(0xFF);
}
+
+ ///
+ /// A non-zero libplctag status returned by the parent-word write is surfaced as a non-Good
+ /// OPC UA StatusCode — the PCCC device rejection propagates to the caller.
+ ///
+ [Fact]
+ 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;
+ var factory = new FakeAbLegacyTagFactory
+ {
+ Customise = p => new FakeAbLegacyTag(p)
+ {
+ Value = (short)0b0001,
+ WriteStatusOverride = errorTimeout,
+ },
+ };
+ var drv = new AbLegacyDriver(new AbLegacyDriverOptions
+ {
+ Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
+ Tags = [new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", "I:0/3", AbLegacyDataType.Bit)],
+ Probe = new AbLegacyProbeOptions { Enabled = false },
+ }, "drv-1", factory);
+ await drv.InitializeAsync("{}", CancellationToken.None);
+
+ // Act
+ var results = await drv.WriteAsync([new WriteRequest("F", true)], CancellationToken.None);
+
+ // 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));
+ }
}
diff --git a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs
index 68dfb4b6..4730777a 100644
--- a/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs
+++ b/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests/FakeAbLegacyTag.cs
@@ -17,9 +17,16 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
///
public object? ArrayValue { get; set; }
- /// Gets or sets the tag status code.
+ /// Gets or sets the tag status code (returned by for reads).
public int Status { get; set; }
+ ///
+ /// Gets or sets an optional status code that overrides once a write
+ /// has been performed (i.e. after becomes > 0). Lets tests seed
+ /// a write-rejection status without also failing the preceding read.
+ ///
+ public int? WriteStatusOverride { get; set; }
+
/// Gets or sets a value indicating whether to throw on initialization.
public bool ThrowOnInitialize { get; set; }
@@ -80,7 +87,8 @@ internal class FakeAbLegacyTag : IAbLegacyTagRuntime
/// Gets the current tag status.
/// The status code.
- public virtual int GetStatus() => Status;
+ public virtual int GetStatus() =>
+ WriteStatusOverride.HasValue && WriteCount > 0 ? WriteStatusOverride.Value : Status;
/// Decodes the tag value based on the specified data type and bit index.
/// The AbLegacy data type.