Merge B1-ablegacy: v3 RawPath read-path
This commit is contained in:
@@ -94,9 +94,13 @@ public sealed class BuildOptionsTests
|
||||
options.Devices[0].DeviceName.ShouldBe("cli-MicroLogix");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that tag list is forwarded verbatim to the options.</summary>
|
||||
/// <summary>
|
||||
/// Verifies that the typed tag list is delivered as v3 <see cref="RawTagEntry"/> records: RawPath =
|
||||
/// the def's Name, DeviceName = the def's DeviceHostAddress, and the TagConfig blob round-trips the
|
||||
/// def's address / dataType / writable through the real mapper.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void BuildOptions_forwards_tag_list_verbatim()
|
||||
public void BuildOptions_delivers_tags_as_rawtag_entries()
|
||||
{
|
||||
var cmd = new TestCommand
|
||||
{
|
||||
@@ -107,7 +111,18 @@ public sealed class BuildOptionsTests
|
||||
|
||||
var options = cmd.Build(SampleTags);
|
||||
|
||||
options.Tags.ShouldBe(SampleTags);
|
||||
options.RawTags.Count.ShouldBe(SampleTags.Count);
|
||||
for (var i = 0; i < SampleTags.Count; i++)
|
||||
{
|
||||
var def = SampleTags[i];
|
||||
var entry = options.RawTags[i];
|
||||
entry.RawPath.ShouldBe(def.Name);
|
||||
entry.DeviceName.ShouldBe(def.DeviceHostAddress);
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(entry.TagConfig, entry.RawPath, out var round).ShouldBeTrue();
|
||||
round.Address.ShouldBe(def.Address);
|
||||
round.DataType.ShouldBe(def.DataType);
|
||||
round.Writable.ShouldBe(def.Writable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that timeout-ms option is propagated to the driver options.</summary>
|
||||
@@ -139,7 +154,7 @@ public sealed class BuildOptionsTests
|
||||
|
||||
var options = cmd.Build([]);
|
||||
|
||||
options.Tags.ShouldBeEmpty();
|
||||
options.RawTags.ShouldBeEmpty();
|
||||
options.Devices.Count.ShouldBe(1);
|
||||
options.Devices[0].PlcFamily.ShouldBe(AbLegacyPlcFamily.Plc5);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public sealed class AbLegacyArrayTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = tags,
|
||||
RawTags = AbLegacyRawTags.Entries(tags),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "ablegacy-array", factory);
|
||||
return (drv, factory);
|
||||
@@ -169,7 +169,7 @@ public sealed class AbLegacyArrayTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Vector", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int, ArrayLength: 8)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Vector", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int, ArrayLength: 8)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "ablegacy-array", new FakeAbLegacyTagFactory());
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -231,30 +231,30 @@ public sealed class AbLegacyArrayTests
|
||||
|
||||
// ---- Equipment-tag resolver threads arrayLength ----
|
||||
|
||||
/// <summary>The equipment-tag parser threads <c>arrayLength</c> into the transient definition.</summary>
|
||||
/// <summary>The tag-definition factory threads <c>arrayLength</c> into the definition.</summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_threads_arrayLength()
|
||||
public void TagDefinitionFactory_threads_arrayLength()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":true,"arrayLength":10}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":10}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBe(10);
|
||||
}
|
||||
|
||||
/// <summary>The parser caps <c>arrayLength</c> at the PCCC 256-word file maximum (isArray:true).</summary>
|
||||
/// <summary>The factory caps <c>arrayLength</c> at the PCCC 256-word file maximum (isArray:true).</summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_caps_arrayLength_at_256()
|
||||
public void TagDefinitionFactory_caps_arrayLength_at_256()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":true,"arrayLength":99999}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":99999}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBe(256);
|
||||
}
|
||||
|
||||
/// <summary>A scalar equipment tag (no arrayLength) leaves ArrayLength null.</summary>
|
||||
/// <summary>A scalar tag (no arrayLength) leaves ArrayLength null.</summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_scalar_leaves_arrayLength_null()
|
||||
public void TagDefinitionFactory_scalar_leaves_arrayLength_null()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int"}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int"}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
@@ -264,19 +264,19 @@ public sealed class AbLegacyArrayTests
|
||||
/// cleared / absent isArray must leave ArrayLength null and never produce an orphan array.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_arrayLength_without_isArray_is_scalar()
|
||||
public void TagDefinitionFactory_arrayLength_without_isArray_is_scalar()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","arrayLength":8}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int","arrayLength":8}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>C-2 regression: <c>isArray:false</c> with a positive arrayLength is a SCALAR.</summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_isArray_false_with_arrayLength_is_scalar()
|
||||
public void TagDefinitionFactory_isArray_false_with_arrayLength_is_scalar()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":false,"arrayLength":8}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":false,"arrayLength":8}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
@@ -286,32 +286,33 @@ public sealed class AbLegacyArrayTests
|
||||
/// as a 1-element array.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void EquipmentTagParser_isArray_true_arrayLength_one_is_one_element_array()
|
||||
public void TagDefinitionFactory_isArray_true_arrayLength_one_is_one_element_array()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":true,"arrayLength":1}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":1}""";
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, "eq/a", out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBe(1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end: an AbLegacy driver with NO authored tags reads an equipment-tag ref whose
|
||||
/// TagConfig carries <c>isArray:true</c> + <c>arrayLength</c> — the resolver threads the
|
||||
/// count and the read surfaces a typed array, capping the libplctag element count at 256.
|
||||
/// End-to-end: an AbLegacy driver reads a raw tag authored as a <see cref="RawTagEntry"/> whose
|
||||
/// TagConfig carries <c>isArray:true</c> + <c>arrayLength</c> — the mapper threads the count and
|
||||
/// the read surfaces a typed array, capping the libplctag element count at 256.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Driver_reads_equipment_array_ref_as_typed_array()
|
||||
public async Task Driver_reads_array_rawtag_as_typed_array()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":true,"arrayLength":3}""";
|
||||
const string rawPath = "cell/ablegacy/dev1/Vec";
|
||||
var tagConfig = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":3}""";
|
||||
var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { ArrayValue = new short[] { 11, 22, 33 } } };
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [],
|
||||
RawTags = [new RawTagEntry(rawPath, tagConfig, WriteIdempotent: false, DeviceName: "ab://10.0.0.5/1,0")],
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "ablegacy-eq-array", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
var r = await drv.ReadAsync([json], CancellationToken.None);
|
||||
var r = await drv.ReadAsync([rawPath], CancellationToken.None);
|
||||
|
||||
r[0].StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
|
||||
var arr = r[0].Value.ShouldBeOfType<short[]>();
|
||||
@@ -320,24 +321,25 @@ public sealed class AbLegacyArrayTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C-2 end-to-end: an equipment-tag ref carrying <c>isArray:false, arrayLength:8</c> reads
|
||||
/// a single SCALAR value (the parser drops the orphan length), so the read never decodes a
|
||||
/// C-2 end-to-end: a raw tag whose TagConfig carries <c>isArray:false, arrayLength:8</c> reads
|
||||
/// a single SCALAR value (the mapper drops the orphan length), so the read never decodes a
|
||||
/// phantom 8-element array against a scalar node. ElementCount stays 1.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Driver_reads_isArray_false_with_arrayLength_as_scalar()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int","isArray":false,"arrayLength":8}""";
|
||||
const string rawPath = "cell/ablegacy/dev1/Scl";
|
||||
var tagConfig = """{"address":"N7:0","dataType":"Int","isArray":false,"arrayLength":8}""";
|
||||
var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = 99 } };
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [],
|
||||
RawTags = [new RawTagEntry(rawPath, tagConfig, WriteIdempotent: false, DeviceName: "ab://10.0.0.5/1,0")],
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "ablegacy-eq-scalar", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
var r = await drv.ReadAsync([json], CancellationToken.None);
|
||||
var r = await drv.ReadAsync([rawPath], CancellationToken.None);
|
||||
|
||||
r[0].StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
|
||||
r[0].Value.ShouldBe(99);
|
||||
|
||||
@@ -80,7 +80,7 @@ public sealed class AbLegacyBitIndexRangeTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("LBit20", "ab://10.0.0.5/1,0", "L9:0/20", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("LBit20", "ab://10.0.0.5/1,0", "L9:0/20", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -105,7 +105,7 @@ public sealed class AbLegacyBitIndexRangeTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Bit0", "ab://10.0.0.5/1,0", "N7:0/0", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Bit0", "ab://10.0.0.5/1,0", "N7:0/0", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Flag3", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Flag3", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -43,7 +43,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
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", "N7:0/3", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -67,7 +67,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = tags,
|
||||
RawTags = AbLegacyRawTags.Entries(tags),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -89,11 +89,11 @@ public sealed class AbLegacyBitRmwTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags =
|
||||
RawTags = AbLegacyRawTags.Entries(
|
||||
[
|
||||
new AbLegacyTagDefinition("Bit0", "ab://10.0.0.5/1,0", "N7:0/0", AbLegacyDataType.Bit),
|
||||
new AbLegacyTagDefinition("Bit5", "ab://10.0.0.5/1,0", "N7:0/5", AbLegacyDataType.Bit),
|
||||
],
|
||||
]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -120,7 +120,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
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", bitAddr, AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", bitAddr, AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -143,7 +143,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
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", "B3:0/3", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("F", "ab://10.0.0.5/1,0", "B3:0/3", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -167,7 +167,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = tags,
|
||||
RawTags = AbLegacyRawTags.Entries(tags),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -195,7 +195,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
var opts = new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Bit2", "ab://10.0.0.5/1,0", "N7:0/2", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Bit2", "ab://10.0.0.5/1,0", "N7:0/2", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
};
|
||||
|
||||
@@ -241,7 +241,7 @@ public sealed class AbLegacyBitRmwTests
|
||||
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)],
|
||||
RawTags = AbLegacyRawTags.Entries([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);
|
||||
|
||||
@@ -19,11 +19,11 @@ public sealed class AbLegacyCapabilityTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0", DeviceName: "Press-SLC-1")],
|
||||
Tags =
|
||||
RawTags = AbLegacyRawTags.Entries(
|
||||
[
|
||||
new AbLegacyTagDefinition("Speed", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int),
|
||||
new AbLegacyTagDefinition("Temperature", "ab://10.0.0.5/1,0", "F8:0", AbLegacyDataType.Float, Writable: false),
|
||||
],
|
||||
]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1");
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -50,7 +50,7 @@ public sealed class AbLegacyCapabilityTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -75,7 +75,7 @@ public sealed class AbLegacyCapabilityTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -190,11 +190,11 @@ public sealed class AbLegacyCapabilityTests
|
||||
new AbLegacyDeviceOptions("ab://10.0.0.5/1,0"),
|
||||
new AbLegacyDeviceOptions("ab://10.0.0.6/1,0"),
|
||||
],
|
||||
Tags =
|
||||
RawTags = AbLegacyRawTags.Entries(
|
||||
[
|
||||
new AbLegacyTagDefinition("A", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int),
|
||||
new AbLegacyTagDefinition("B", "ab://10.0.0.6/1,0", "N7:0", AbLegacyDataType.Int),
|
||||
],
|
||||
]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1");
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
+3
-3
@@ -29,7 +29,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0", AbLegacyPlcFamily.Slc500)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-dispose", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -72,7 +72,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -135,7 +135,7 @@ public sealed class AbLegacyDisposeAndResolveHostTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
}, "drv-1");
|
||||
drv.ResolveHost("X").ShouldBe("ab://10.0.0.5/1,0");
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public sealed class AbLegacyDriverTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/", AbLegacyPlcFamily.Slc500)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -154,7 +154,7 @@ public sealed class AbLegacyDriverTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,2", AbLegacyPlcFamily.Slc500)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,2", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,2", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -171,7 +171,7 @@ public sealed class AbLegacyDriverTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/", AbLegacyPlcFamily.MicroLogix)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/", "L9:0", AbLegacyDataType.Long)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/", "L9:0", AbLegacyDataType.Long)]),
|
||||
}, "drv-1");
|
||||
|
||||
var ex = await Should.ThrowAsync<InvalidOperationException>(
|
||||
@@ -188,7 +188,7 @@ public sealed class AbLegacyDriverTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0", AbLegacyPlcFamily.Slc500)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "L9:0", AbLegacyDataType.Long)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "L9:0", AbLegacyDataType.Long)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1");
|
||||
|
||||
@@ -209,7 +209,7 @@ public sealed class AbLegacyDriverTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0", AbLegacyPlcFamily.Plc5)],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "L9:0", AbLegacyDataType.Long)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "L9:0", AbLegacyDataType.Long)]),
|
||||
}, "drv-1");
|
||||
|
||||
var ex = await Should.ThrowAsync<InvalidOperationException>(
|
||||
|
||||
+13
-9
@@ -4,30 +4,34 @@ using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
|
||||
/// <summary>R2-11 Phase C strictness surface for the AbLegacy equipment-tag parser: the runtime is now STRICT —
|
||||
/// a typo'd <c>dataType</c> rejects the tag (<c>TryParse</c> ⇒ <see langword="false"/> ⇒ <c>BadNodeIdUnknown</c>),
|
||||
/// <c>Inspect</c> reports the typo, and the <c>writable</c> key is honoured (default true).</summary>
|
||||
/// <summary>R2-11 Phase C strictness surface for the AbLegacy tag-definition factory: the runtime is now STRICT —
|
||||
/// a typo'd <c>dataType</c> rejects the tag (<c>FromTagConfig</c> ⇒ <see langword="false"/> ⇒ <c>BadNodeIdUnknown</c>),
|
||||
/// <c>Inspect</c> reports the typo, and the <c>writable</c> key is honoured (default true). Under v3 the mapped
|
||||
/// definition's <c>Name</c> is the RawPath the factory was handed, not the blob.</summary>
|
||||
public sealed class AbLegacyEquipmentTagParserStrictnessTests
|
||||
{
|
||||
private const string RawPath = "cell/ablegacy/dev1/T1";
|
||||
|
||||
[Fact]
|
||||
public void Typo_dataType_rejects_the_tag()
|
||||
{
|
||||
AbLegacyEquipmentTagParser.TryParse("{\"address\":\"N7:0\",\"dataType\":\"Intt\"}", out _)
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig("{\"address\":\"N7:0\",\"dataType\":\"Intt\"}", RawPath, out _)
|
||||
.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Valid_dataType_still_parses()
|
||||
{
|
||||
AbLegacyEquipmentTagParser.TryParse("{\"address\":\"N7:0\",\"dataType\":\"Int\"}", out var def)
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig("{\"address\":\"N7:0\",\"dataType\":\"Int\"}", RawPath, out var def)
|
||||
.ShouldBeTrue();
|
||||
def.DataType.ShouldBe(AbLegacyDataType.Int);
|
||||
def.Name.ShouldBe(RawPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Inspect_reports_typo_dataType()
|
||||
{
|
||||
var warnings = AbLegacyEquipmentTagParser.Inspect("{\"address\":\"N7:0\",\"dataType\":\"Intt\"}");
|
||||
var warnings = AbLegacyTagDefinitionFactory.Inspect("{\"address\":\"N7:0\",\"dataType\":\"Intt\"}");
|
||||
warnings.ShouldHaveSingleItem();
|
||||
warnings[0].ShouldContain("Intt");
|
||||
warnings[0].ShouldContain("dataType");
|
||||
@@ -36,13 +40,13 @@ public sealed class AbLegacyEquipmentTagParserStrictnessTests
|
||||
[Fact]
|
||||
public void Inspect_clean_config_has_no_warnings()
|
||||
{
|
||||
AbLegacyEquipmentTagParser.Inspect("{\"address\":\"N7:0\",\"dataType\":\"Int\"}").ShouldBeEmpty();
|
||||
AbLegacyTagDefinitionFactory.Inspect("{\"address\":\"N7:0\",\"dataType\":\"Int\"}").ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Writable_false_is_honoured()
|
||||
{
|
||||
AbLegacyEquipmentTagParser.TryParse("{\"address\":\"N7:0\",\"dataType\":\"Int\",\"writable\":false}", out var def)
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig("{\"address\":\"N7:0\",\"dataType\":\"Int\",\"writable\":false}", RawPath, out var def)
|
||||
.ShouldBeTrue();
|
||||
def.Writable.ShouldBeFalse();
|
||||
}
|
||||
@@ -50,7 +54,7 @@ public sealed class AbLegacyEquipmentTagParserStrictnessTests
|
||||
[Fact]
|
||||
public void Writable_absent_defaults_to_true()
|
||||
{
|
||||
AbLegacyEquipmentTagParser.TryParse("{\"address\":\"N7:0\",\"dataType\":\"Int\"}", out var def)
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig("{\"address\":\"N7:0\",\"dataType\":\"Int\"}", RawPath, out var def)
|
||||
.ShouldBeTrue();
|
||||
def.Writable.ShouldBeTrue();
|
||||
}
|
||||
|
||||
+26
-20
@@ -8,34 +8,38 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class AbLegacyEquipmentTagTests
|
||||
{
|
||||
private const string RawPath = "cell/ablegacy/dev1/Counter";
|
||||
|
||||
[Fact]
|
||||
public void Parses_equipment_tagconfig_into_a_transient_definition()
|
||||
public void Maps_tagconfig_into_a_definition_keyed_by_rawpath()
|
||||
{
|
||||
// v3: the deviceHostAddress key is dropped by the mapper (device routing travels on the
|
||||
// RawTagEntry's DeviceName). The mapped definition is keyed by the RawPath, not the blob.
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int"}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
def!.Name.ShouldBe(json);
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
|
||||
def!.Name.ShouldBe(RawPath);
|
||||
def.Address.ShouldBe("N7:0");
|
||||
def.DataType.ShouldBe(AbLegacyDataType.Int);
|
||||
def.DeviceHostAddress.ShouldBe("ab://10.0.0.5/1,0");
|
||||
def.DeviceHostAddress.ShouldBe(""); // dropped — resolved from RawTagEntry.DeviceName at table-build time
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Rejects_a_non_address_blob()
|
||||
=> AbLegacyEquipmentTagParser.TryParse("""{"FullName":"x"}""", out _).ShouldBeFalse();
|
||||
=> AbLegacyTagDefinitionFactory.FromTagConfig("""{"FullName":"x"}""", RawPath, out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_garbage()
|
||||
=> AbLegacyEquipmentTagParser.TryParse("not json", out _).ShouldBeFalse();
|
||||
=> AbLegacyTagDefinitionFactory.FromTagConfig("not json", RawPath, out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_address_as_a_json_number()
|
||||
=> AbLegacyEquipmentTagParser.TryParse(
|
||||
"""{"address":7,"dataType":"Int"}""", out _).ShouldBeFalse();
|
||||
=> AbLegacyTagDefinitionFactory.FromTagConfig(
|
||||
"""{"address":7,"dataType":"Int"}""", RawPath, out _).ShouldBeFalse();
|
||||
|
||||
[Fact]
|
||||
public void Rejects_empty_address()
|
||||
=> AbLegacyEquipmentTagParser.TryParse(
|
||||
"""{"address":"","dataType":"Int"}""", out _).ShouldBeFalse();
|
||||
=> AbLegacyTagDefinitionFactory.FromTagConfig(
|
||||
"""{"address":"","dataType":"Int"}""", RawPath, out _).ShouldBeFalse();
|
||||
|
||||
// -002 regression: isArray:true without a valid positive arrayLength → scalar (null ArrayLength).
|
||||
|
||||
@@ -43,7 +47,7 @@ public sealed class AbLegacyEquipmentTagTests
|
||||
public void IsArray_true_with_arrayLength_zero_produces_scalar()
|
||||
{
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":0}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
@@ -51,7 +55,7 @@ public sealed class AbLegacyEquipmentTagTests
|
||||
public void IsArray_true_with_no_arrayLength_produces_scalar()
|
||||
{
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
@@ -59,28 +63,30 @@ public sealed class AbLegacyEquipmentTagTests
|
||||
public void IsArray_true_with_negative_arrayLength_produces_scalar()
|
||||
{
|
||||
var json = """{"address":"N7:0","dataType":"Int","isArray":true,"arrayLength":-5}""";
|
||||
AbLegacyEquipmentTagParser.TryParse(json, out var def).ShouldBeTrue();
|
||||
AbLegacyTagDefinitionFactory.FromTagConfig(json, RawPath, out var def).ShouldBeTrue();
|
||||
def!.ArrayLength.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// End-to-end driver-level proof: an AbLegacy driver with NO authored tags can still read an
|
||||
/// equipment-tag ref (the raw TagConfig JSON) — the resolver parses it into a transient
|
||||
/// definition and the read goes to the fake runtime instead of returning BadNodeIdUnknown.
|
||||
/// End-to-end driver-level proof: a raw tag authored as a <see cref="RawTagEntry"/> (RawPath
|
||||
/// identity + TagConfig blob) is resolved by the driver's RawPath → def table and read against the
|
||||
/// fake runtime, returning Good instead of BadNodeIdUnknown. Under v3 the read reference is always
|
||||
/// a RawPath — the driver no longer parses a raw TagConfig blob handed as the reference.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Driver_resolves_an_equipment_ref_and_reads_instead_of_BadNodeIdUnknown()
|
||||
public async Task Driver_resolves_a_rawpath_ref_and_reads_instead_of_BadNodeIdUnknown()
|
||||
{
|
||||
var json = """{"deviceHostAddress":"ab://10.0.0.5/1,0","address":"N7:0","dataType":"Int"}""";
|
||||
var tagConfig = """{"address":"N7:0","dataType":"Int"}""";
|
||||
var factory = new FakeAbLegacyTagFactory { Customise = p => new FakeAbLegacyTag(p) { Value = 4242 } };
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [],
|
||||
RawTags = [new RawTagEntry(RawPath, tagConfig, WriteIdempotent: false, DeviceName: "ab://10.0.0.5/1,0")],
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "ablegacy-eq", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
var r = await drv.ReadAsync([json], CancellationToken.None);
|
||||
var r = await drv.ReadAsync([RawPath], CancellationToken.None);
|
||||
|
||||
r[0].StatusCode.ShouldBe(AbLegacyStatusMapper.Good);
|
||||
r[0].StatusCode.ShouldNotBe(AbLegacyStatusMapper.BadNodeIdUnknown);
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ public sealed class AbLegacyEvictOnFailureTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions(Device)],
|
||||
Tags = tags,
|
||||
RawTags = AbLegacyRawTags.Entries(tags),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
return (drv, factory);
|
||||
|
||||
+1
-1
@@ -79,7 +79,7 @@ public sealed class AbLegacyLoggerInjectionTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-logged", factory, logger);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Test helper bridging the v2 authoring shape (a typed <see cref="AbLegacyTagDefinition"/>) to the v3
|
||||
/// delivery shape (<see cref="RawTagEntry"/>). Under v3 the driver no longer takes pre-declared
|
||||
/// definitions — the deploy artifact hands it authored raw tags (RawPath + TagConfig blob + owning
|
||||
/// DeviceName), and the driver rebuilds the typed definitions via
|
||||
/// <see cref="AbLegacyTagDefinitionFactory"/>. These tests keep expressing their intent as typed
|
||||
/// definitions; this helper serialises each back to its TagConfig blob (via
|
||||
/// <see cref="AbLegacyTagDefinitionFactory.ToTagConfig"/>) and packages it as a <see cref="RawTagEntry"/>
|
||||
/// whose RawPath is the def's <c>Name</c> and whose <see cref="RawTagEntry.DeviceName"/> is the def's
|
||||
/// <c>DeviceHostAddress</c> — so the round-trip through the real mapper reproduces the same definition
|
||||
/// the test authored, keyed by the same name the read/write/subscribe call sites use and routed to the
|
||||
/// same device (the driver matches DeviceName against a configured device's Name or HostAddress).
|
||||
/// </summary>
|
||||
internal static class AbLegacyRawTags
|
||||
{
|
||||
/// <summary>Serialises one typed definition into its <see cref="RawTagEntry"/> (RawPath = def.Name, DeviceName = def.DeviceHostAddress).</summary>
|
||||
/// <param name="def">The typed definition to convert.</param>
|
||||
/// <returns>The equivalent <see cref="RawTagEntry"/>.</returns>
|
||||
public static RawTagEntry Entry(AbLegacyTagDefinition def)
|
||||
=> new(def.Name, AbLegacyTagDefinitionFactory.ToTagConfig(def), def.WriteIdempotent, DeviceName: def.DeviceHostAddress);
|
||||
|
||||
/// <summary>Serialises typed definitions into <see cref="RawTagEntry"/> records.</summary>
|
||||
/// <param name="defs">The typed definitions to convert (array, params, or a collection expression).</param>
|
||||
/// <returns>The equivalent raw-tag entries.</returns>
|
||||
public static IReadOnlyList<RawTagEntry> Entries(params AbLegacyTagDefinition[] defs)
|
||||
=> [.. defs.Select(Entry)];
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public sealed class AbLegacyReadWriteTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = tags,
|
||||
RawTags = AbLegacyRawTags.Entries(tags),
|
||||
}, "drv-1", factory);
|
||||
return (drv, factory);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ public sealed class AbLegacyReadWriteTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Bit3", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Bit3", "ab://10.0.0.5/1,0", "N7:0/3", AbLegacyDataType.Bit)]),
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
@@ -206,7 +206,7 @@ public sealed class AbLegacyReadWriteTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("Flag", "ab://10.0.0.5/1,0", "B3:0", AbLegacyDataType.Bit)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("Flag", "ab://10.0.0.5/1,0", "B3:0", AbLegacyDataType.Bit)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -245,11 +245,11 @@ public sealed class AbLegacyReadWriteTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags =
|
||||
RawTags = AbLegacyRawTags.Entries(
|
||||
[
|
||||
new AbLegacyTagDefinition("A", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int),
|
||||
new AbLegacyTagDefinition("B", "ab://10.0.0.5/1,0", "N7:1", AbLegacyDataType.Int, Writable: false),
|
||||
],
|
||||
]),
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.AbLegacy;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// CONV-4 — <see cref="AbLegacyDriver.ResolveHost"/> must resolve an equipment-tag reference
|
||||
/// (raw TagConfig JSON) to its OWN device host so per-host breaker keys are correct for
|
||||
/// equipment tags, not the first-device fallback.
|
||||
/// CONV-4 — <see cref="AbLegacyDriver.ResolveHost"/> must resolve a RawPath reference to its OWN
|
||||
/// device host so per-host breaker keys are correct for multi-device tags, not the first-device
|
||||
/// fallback. v3: the tag's device travels on the <see cref="RawTagEntry.DeviceName"/> the deploy
|
||||
/// artifact delivers; the driver resolves it to that device's host at table-build time.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class AbLegacyResolveHostTests
|
||||
@@ -15,21 +17,25 @@ public sealed class AbLegacyResolveHostTests
|
||||
private const string DeviceA = "ab://10.0.0.5/1,0";
|
||||
private const string DeviceB = "ab://10.0.0.6/1,0";
|
||||
|
||||
private static AbLegacyDriver NewDriver() => new(
|
||||
private static AbLegacyDriver NewDriver(params RawTagEntry[] rawTags) => new(
|
||||
new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions(DeviceA), new AbLegacyDeviceOptions(DeviceB)],
|
||||
RawTags = rawTags,
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
},
|
||||
"ablegacy-1", new FakeAbLegacyTagFactory());
|
||||
|
||||
/// <summary>An equipment-tag ref naming device B resolves to device B's host, not the first device.</summary>
|
||||
/// <summary>A raw tag owned by device B resolves to device B's host, not the first device.</summary>
|
||||
[Fact]
|
||||
public void ResolveHost_EquipmentTagRef_ReturnsItsOwnDeviceHost()
|
||||
public async Task ResolveHost_RawTagRef_ReturnsItsOwnDeviceHost()
|
||||
{
|
||||
var drv = NewDriver();
|
||||
var json = $$"""{"deviceHostAddress":"{{DeviceB}}","address":"N7:0","dataType":"Int"}""";
|
||||
drv.ResolveHost(json).ShouldBe(DeviceB);
|
||||
const string rawPath = "cell/ablegacy/devB/T1";
|
||||
var drv = NewDriver(new RawTagEntry(
|
||||
rawPath, """{"address":"N7:0","dataType":"Int"}""", WriteIdempotent: false, DeviceName: DeviceB));
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
drv.ResolveHost(rawPath).ShouldBe(DeviceB);
|
||||
}
|
||||
|
||||
/// <summary>An unresolvable ref keeps the current first-device fallback.</summary>
|
||||
|
||||
+2
-2
@@ -84,7 +84,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
@@ -117,7 +117,7 @@ public sealed class AbLegacyRuntimeConcurrencyTests
|
||||
var drv = new AbLegacyDriver(new AbLegacyDriverOptions
|
||||
{
|
||||
Devices = [new AbLegacyDeviceOptions("ab://10.0.0.5/1,0")],
|
||||
Tags = [new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)],
|
||||
RawTags = AbLegacyRawTags.Entries([new AbLegacyTagDefinition("X", "ab://10.0.0.5/1,0", "N7:0", AbLegacyDataType.Int)]),
|
||||
Probe = new AbLegacyProbeOptions { Enabled = false },
|
||||
}, "drv-1", factory);
|
||||
await drv.InitializeAsync("{}", CancellationToken.None);
|
||||
|
||||
Reference in New Issue
Block a user