112 lines
5.1 KiB
C#
112 lines
5.1 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
|
|
|
|
/// <summary>
|
|
/// Covers follow-up E projection: <see cref="EquipmentNode"/> carries the equipment's
|
|
/// <c>DriverInstanceId</c> / <c>DeviceId</c> bindings and the resolved <c>DeviceHost</c> (parsed from
|
|
/// the bound <see cref="Device"/>'s schemaless <c>DeviceConfig</c> JSON via the shared
|
|
/// <see cref="AddressSpaceComposer.TryExtractDeviceHost"/>). A later task grafts a driver's discovered
|
|
/// FixedTree onto a zero-tag equipment and partitions a multi-device driver by host using these.
|
|
/// </summary>
|
|
public sealed class AddressSpaceComposerDeviceHostTests
|
|
{
|
|
/// <summary>An equipment bound to a driver + a device whose config carries a top-level
|
|
/// <c>HostAddress</c> resolves all three fields, with the host trimmed + lower-cased.</summary>
|
|
[Fact]
|
|
public void Compose_resolves_driver_device_and_device_host()
|
|
{
|
|
var equipment = new[] { NewEquipment("eq-1", driver: "d1", device: "dev1") };
|
|
var devices = new[] { NewDevice("dev1", "d1", "{\"HostAddress\":\"10.0.0.5:8193\"}") };
|
|
|
|
var node = AddressSpaceComposer.Compose(
|
|
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
|
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: devices)
|
|
.EquipmentNodes.ShouldHaveSingleItem();
|
|
|
|
node.EquipmentId.ShouldBe("eq-1");
|
|
node.DriverInstanceId.ShouldBe("d1");
|
|
node.DeviceId.ShouldBe("dev1");
|
|
node.DeviceHost.ShouldBe("10.0.0.5:8193");
|
|
}
|
|
|
|
/// <summary>An equipment with no driver and no device → all three new fields null (driver-less,
|
|
/// no device to resolve a host from).</summary>
|
|
[Fact]
|
|
public void Compose_equipment_without_driver_or_device_yields_null_bindings()
|
|
{
|
|
var equipment = new[] { NewEquipment("eq-1", driver: null, device: null) };
|
|
|
|
var node = AddressSpaceComposer.Compose(
|
|
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
|
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: Array.Empty<Device>())
|
|
.EquipmentNodes.ShouldHaveSingleItem();
|
|
|
|
node.DriverInstanceId.ShouldBeNull();
|
|
node.DeviceId.ShouldBeNull();
|
|
node.DeviceHost.ShouldBeNull();
|
|
}
|
|
|
|
/// <summary>A bound DeviceId with no matching device row, or a device whose config has no
|
|
/// <c>HostAddress</c>, resolves DeviceHost to null while DeviceId is still carried.</summary>
|
|
[Fact]
|
|
public void Compose_device_host_is_null_when_unresolvable()
|
|
{
|
|
var equipment = new[]
|
|
{
|
|
NewEquipment("eq-missing", driver: "d1", device: "dev-missing"),
|
|
NewEquipment("eq-nohost", driver: "d1", device: "dev-nohost"),
|
|
};
|
|
var devices = new[] { NewDevice("dev-nohost", "d1", "{\"Port\":502}") };
|
|
|
|
var nodes = AddressSpaceComposer.Compose(
|
|
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
|
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: devices)
|
|
.EquipmentNodes;
|
|
|
|
var missing = nodes.Single(n => n.EquipmentId == "eq-missing");
|
|
missing.DeviceId.ShouldBe("dev-missing");
|
|
missing.DeviceHost.ShouldBeNull();
|
|
|
|
var noHost = nodes.Single(n => n.EquipmentId == "eq-nohost");
|
|
noHost.DeviceId.ShouldBe("dev-nohost");
|
|
noHost.DeviceHost.ShouldBeNull();
|
|
}
|
|
|
|
/// <summary>The shared host extractor normalizes (trim + lower-case) and tolerates every malformed
|
|
/// shape (blank / non-object / no string HostAddress / blank value / non-JSON) by returning null.</summary>
|
|
[Theory]
|
|
[InlineData("{\"HostAddress\":\"10.201.31.5:8193\"}", "10.201.31.5:8193")]
|
|
[InlineData("{\"HostAddress\":\" HOST-A:8193 \"}", "host-a:8193")] // trimmed + lower-cased
|
|
[InlineData("{\"HostAddress\":\"\"}", null)] // blank value
|
|
[InlineData("{\"HostAddress\":1234}", null)] // non-string
|
|
[InlineData("{\"Port\":502}", null)] // absent
|
|
[InlineData("[]", null)] // non-object root
|
|
[InlineData("not json", null)] // malformed
|
|
[InlineData("", null)] // blank
|
|
public void TryExtractDeviceHost_normalizes_and_tolerates(string? deviceConfig, string? expected)
|
|
{
|
|
AddressSpaceComposer.TryExtractDeviceHost(deviceConfig).ShouldBe(expected);
|
|
}
|
|
|
|
private static Equipment NewEquipment(string id, string? driver, string? device) => new()
|
|
{
|
|
EquipmentId = id,
|
|
DriverInstanceId = driver,
|
|
DeviceId = device,
|
|
UnsLineId = "line-1",
|
|
Name = id,
|
|
MachineCode = id.ToUpperInvariant(),
|
|
};
|
|
|
|
private static Device NewDevice(string deviceId, string driverInstanceId, string deviceConfig) => new()
|
|
{
|
|
DeviceId = deviceId,
|
|
DriverInstanceId = driverInstanceId,
|
|
Name = deviceId,
|
|
DeviceConfig = deviceConfig,
|
|
};
|
|
}
|