feat(abcip): resolve equipment-tag refs (read + write) via EquipmentTagRefResolver
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.AbCip;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public class AbCipEquipmentTagTests
|
||||
{
|
||||
[Fact]
|
||||
public void Parses_equipment_tagconfig_into_a_transient_definition()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","tagPath":"Motor1.Speed","dataType":"Real"}""";
|
||||
AbCipEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
def!.Name.ShouldBe(json);
|
||||
def.TagPath.ShouldBe("Motor1.Speed");
|
||||
def.DeviceHostAddress.ShouldBe("ab://10.0.0.5/1,0");
|
||||
def.DataType.ShouldBe(AbCipDataType.Real);
|
||||
def.Writable.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Defaults_optional_fields_when_only_the_mandatory_tagPath_is_present()
|
||||
{
|
||||
var json = """{"tagPath":"PT_101"}""";
|
||||
AbCipEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
def!.Name.ShouldBe(json);
|
||||
def.TagPath.ShouldBe("PT_101");
|
||||
def.DeviceHostAddress.ShouldBe(""); // absent → empty, matching AbCipTagConfigModel default
|
||||
def.DataType.ShouldBe(AbCipDataType.DInt); // AbCipTagConfigModel's default atomic type
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_a_non_abcip_blob()
|
||||
=> AbCipEquipmentTagParser.TryParse("""{"region":"x"}""", out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_a_blob_with_an_empty_tagPath()
|
||||
=> AbCipEquipmentTagParser.TryParse("""{"tagPath":" ","dataType":"DInt"}""", out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_tagPath_given_as_a_non_string()
|
||||
=> AbCipEquipmentTagParser.TryParse("""{"tagPath":42}""", out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_garbage()
|
||||
=> AbCipEquipmentTagParser.TryParse("not json", out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_a_plain_authored_name_without_a_leading_brace()
|
||||
=> AbCipEquipmentTagParser.TryParse("Motor1.Speed", out _).ShouldBeFalse();
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end driver-level proof: an AbCip driver with NO authored tags can still read an
|
||||
/// equipment-tag ref (the raw TagConfig JSON) — the resolver parses it into a transient
|
||||
/// definition (whose DeviceHostAddress names a configured device) and the read goes to the
|
||||
/// wire instead of returning BadNodeIdUnknown.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Driver_resolves_an_equipment_ref_and_reads_instead_of_BadNodeIdUnknown()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","tagPath":"Motor1.Speed","dataType":"DInt"}""";
|
||||
var factory = new FakeAbCipTagFactory();
|
||||
var opts = new AbCipDriverOptions
|
||||
{
|
||||
Devices = [new AbCipDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [],
|
||||
};
|
||||
var drv = new AbCipDriver(opts, "abcip-eq", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
factory.Customise = p => new FakeAbCipTag(p) { Value = 4242 };
|
||||
|
||||
var snapshots = await drv.ReadAsync([json], CancellationToken.None);
|
||||
|
||||
snapshots.Single().StatusCode.ShouldBe(AbCipStatusMapper.Good);
|
||||
snapshots.Single().StatusCode.ShouldNotBe(AbCipStatusMapper.BadNodeIdUnknown);
|
||||
snapshots.Single().Value.ShouldBe(4242);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user