64 lines
2.7 KiB
C#
64 lines
2.7 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Modbus.Tests;
|
|
|
|
/// <summary>
|
|
/// CONV-4 (Modbus leg) — Modbus keys per-host resilience per SLAVE UNIT
|
|
/// (<c>host:port/unitN</c>). An equipment tag must carry its own <c>unitId</c> so multi-unit
|
|
/// equipment tags key their own breaker; <see cref="ModbusDriver.ResolveHost"/> must resolve
|
|
/// the equipment ref through the resolver rather than only matching authored names. Scope:
|
|
/// <c>unitId</c> ONLY — the parser's other strictness gaps are R2-11's.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class ModbusUnitIdResolveHostTests
|
|
{
|
|
private sealed class NoopTransport : IModbusTransport
|
|
{
|
|
public Task ConnectAsync(CancellationToken ct) => Task.CompletedTask;
|
|
public Task<byte[]> SendAsync(byte unitId, byte[] pdu, CancellationToken ct)
|
|
=> Task.FromResult(new byte[] { 0x03, 0x00 });
|
|
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
|
}
|
|
|
|
private static ModbusDriver NewDriver() => new(
|
|
new ModbusDriverOptions { Host = "10.0.0.1", Port = 502, UnitId = 1, Probe = new ModbusProbeOptions { Enabled = false } },
|
|
"modbus-1", _ => new NoopTransport());
|
|
|
|
/// <summary>The parser reads an optional unitId into the transient definition.</summary>
|
|
[Fact]
|
|
public void Parser_reads_optional_unitId()
|
|
{
|
|
var json = """{"region":"HoldingRegisters","address":0,"dataType":"Int16","unitId":7}""";
|
|
ModbusEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
|
def.UnitId.ShouldBe((byte)7);
|
|
}
|
|
|
|
/// <summary>An absent unitId leaves the definition on the driver-level default (null override).</summary>
|
|
[Fact]
|
|
public void Parser_absent_unitId_is_null()
|
|
{
|
|
var json = """{"region":"HoldingRegisters","address":0,"dataType":"Int16"}""";
|
|
ModbusEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
|
def.UnitId.ShouldBeNull();
|
|
}
|
|
|
|
/// <summary>An equipment ref carrying a unitId keys its own per-unit host name.</summary>
|
|
[Fact]
|
|
public void EquipmentTag_WithUnitId_ResolvesPerUnitHostName()
|
|
{
|
|
var drv = NewDriver();
|
|
var json = """{"region":"HoldingRegisters","address":0,"dataType":"Int16","unitId":7}""";
|
|
drv.ResolveHost(json).ShouldBe("10.0.0.1:502/unit7");
|
|
}
|
|
|
|
/// <summary>An equipment ref without a unitId keys the driver-level unit host name.</summary>
|
|
[Fact]
|
|
public void EquipmentTag_WithoutUnitId_KeysDriverDefaultUnit()
|
|
{
|
|
var drv = NewDriver();
|
|
var json = """{"region":"HoldingRegisters","address":0,"dataType":"Int16"}""";
|
|
drv.ResolveHost(json).ShouldBe("10.0.0.1:502/unit1");
|
|
}
|
|
}
|