using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests;
///
/// Review 07 H2: the deadband baseline is keyed on (value, status). A Good-after-Bad recovery carrying
/// the IDENTICAL value must publish — the value-only dedup left the node Bad until the value drifted.
///
[Trait("Category", "Unit")]
public sealed class ModbusDeadbandRecoveryTests
{
private const uint Good = 0u;
private const uint Uncertain = 0x40000000u;
private const uint BadComm = 0x80050000u;
private static async Task NewDriverAsync()
{
var tag = new ModbusTagDefinition("Temp", ModbusRegion.HoldingRegisters, 0, ModbusDataType.Int16, Deadband: 5.0);
var opts = new ModbusDriverOptions
{
Host = "f", RawTags = ModbusRawTags.Entries([tag]), Probe = new ModbusProbeOptions { Enabled = false },
};
var drv = new ModbusDriver(opts, "m1", _ => new ModbusDriverTests.FakeTransport());
await drv.InitializeAsync("{}", TestContext.Current.CancellationToken);
return drv;
}
private static DataValueSnapshot Snap(object? v, uint status) => new(v, status, DateTime.UtcNow, DateTime.UtcNow);
[Fact]
public async Task Good_after_Bad_with_the_same_value_publishes()
{
await using var drv = await NewDriverAsync();
drv.ShouldPublish("Temp", Snap((short)10, Good)).ShouldBeTrue(); // baseline
drv.ShouldPublish("Temp", Snap(null, BadComm)).ShouldBeTrue(); // comms drop
drv.ShouldPublish("Temp", Snap((short)10, Good)).ShouldBeTrue("the recovery publish must not be swallowed");
// Positive control: the deadband is still active after the recovery re-seeded the baseline.
drv.ShouldPublish("Temp", Snap((short)11, Good)).ShouldBeFalse();
drv.ShouldPublish("Temp", Snap((short)16, Good)).ShouldBeTrue();
}
[Fact]
public async Task A_status_change_with_the_same_value_publishes()
{
await using var drv = await NewDriverAsync();
drv.ShouldPublish("Temp", Snap((short)10, Good)).ShouldBeTrue();
drv.ShouldPublish("Temp", Snap((short)10, Uncertain)).ShouldBeTrue("a quality change must publish");
drv.ShouldPublish("Temp", Snap((short)12, Uncertain)).ShouldBeFalse("within deadband at the same quality");
drv.ShouldPublish("Temp", Snap((short)10, Good)).ShouldBeTrue("back to Good must publish");
}
}