v3 B1-WP6: migrate Core + Server test projects to greenfield schema
Fix TESTS only (production already green) so the four in-scope test projects compile + pass under the v3 dark address space: - Core.Abstractions.Tests: drop AllowedNamespaceKinds/NamespaceKindCompatibility from DriverTypeMetadata; rewrite EquipmentTagRefResolver tests to the single-func RawPath-lookup contract (no parseRef/transient cache). - Core.Tests: rewrite EquipmentNodeWalkerTests to EquipmentNamespaceContent(Areas, Lines,Equipment,VirtualTags?,ScriptedAlarms?) — folders + VirtualTags + ScriptedAlarms only; raw-tag variables dark (skipped Batch-4 placeholder). Drop retired Equipment.DriverInstanceId. - Runtime.Tests: rewrite golden corpus + TagConfigCorpusParityTests to the v3 RawPath/RawTagEntry round-trip (byte + TagConfigIntent parity; EquipmentTags dark). Salvage VirtualTag + ScriptedAlarm artifact parity to the new Compose signature. Retire the equipment-tag-materialization parity files (Array/Historize/Alias) and the equipment-device-binding DeviceHost parity to documented skipped placeholders. Skip 34 dark equipment-tag routing/value/alarm/write/discovery actor tests with a shared DarkAddressSpaceReasons constant. Re-key cluster-scoped tests to v3 UNS line->area attribution. - ControlPlane.Tests: ConfigComposer round-trips re-keyed to RawFolder/Device (no Namespaces); tag-config gate resolves driver via Device; deploy-gate collision test re-keyed to UnsEffectiveNameCollision; drop retired BadCrossClusterNamespaceBinding case.
This commit is contained in:
@@ -8,9 +8,8 @@ public sealed class DriverTypeRegistryTests
|
||||
{
|
||||
private static DriverTypeMetadata SampleMetadata(
|
||||
string typeName = "Modbus",
|
||||
NamespaceKindCompatibility allowed = NamespaceKindCompatibility.Equipment,
|
||||
DriverTier tier = DriverTier.B) =>
|
||||
new(typeName, allowed,
|
||||
new(typeName,
|
||||
DriverConfigJsonSchema: "{\"type\": \"object\"}",
|
||||
DeviceConfigJsonSchema: "{\"type\": \"object\"}",
|
||||
TagConfigJsonSchema: "{\"type\": \"object\"}",
|
||||
@@ -118,7 +117,7 @@ public sealed class DriverTypeRegistryTests
|
||||
var registry = new DriverTypeRegistry();
|
||||
registry.Register(SampleMetadata("Modbus"));
|
||||
registry.Register(SampleMetadata("S7"));
|
||||
registry.Register(SampleMetadata("Galaxy", NamespaceKindCompatibility.Equipment));
|
||||
registry.Register(SampleMetadata("Galaxy"));
|
||||
|
||||
var all = registry.All();
|
||||
|
||||
@@ -126,22 +125,6 @@ public sealed class DriverTypeRegistryTests
|
||||
all.Select(m => m.TypeName).ShouldBe(new[] { "Modbus", "S7", "Galaxy" }, ignoreOrder: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that NamespaceKindCompatibility flags are implemented as a bitmask.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void NamespaceKindCompatibility_FlagsAreBitmask()
|
||||
{
|
||||
// Per decision #111 — driver types may be valid for multiple namespace kinds.
|
||||
var both = NamespaceKindCompatibility.Equipment | NamespaceKindCompatibility.Simulated;
|
||||
|
||||
both.HasFlag(NamespaceKindCompatibility.Equipment).ShouldBeTrue();
|
||||
both.HasFlag(NamespaceKindCompatibility.Simulated).ShouldBeTrue();
|
||||
|
||||
// A single-flag value does not carry the other flag — proves these are real bitmask bits.
|
||||
NamespaceKindCompatibility.Equipment.HasFlag(NamespaceKindCompatibility.Simulated).ShouldBeFalse();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that Get rejects empty or null type names.
|
||||
/// </summary>
|
||||
|
||||
+39
-28
@@ -4,54 +4,65 @@ using Xunit;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// v3: <see cref="EquipmentTagRefResolver{TDef}"/> is now a thin wrapper over the driver's
|
||||
/// authored <c>RawPath → TDef</c> lookup. The pre-v3 two-arg ctor (byName + a JSON-blob parse
|
||||
/// fallback with a transient cache) is retired — a reference is always a RawPath, and a miss is a
|
||||
/// miss (no parse attempt). These tests pin the single-func RawPath-lookup contract.
|
||||
/// </summary>
|
||||
public class EquipmentTagRefResolverTests
|
||||
{
|
||||
private sealed record Def(string Id);
|
||||
|
||||
private static EquipmentTagRefResolver<Def> Make(
|
||||
Dictionary<string, Def> byName, Func<string, Def?> parse)
|
||||
=> new(r => byName.TryGetValue(r, out var d) ? d : null, parse);
|
||||
private static EquipmentTagRefResolver<Def> Make(Dictionary<string, Def> byRawPath)
|
||||
=> new(r => byRawPath.TryGetValue(r, out var d) ? d : null);
|
||||
|
||||
[Fact]
|
||||
public void Legacy_name_resolves_via_byName()
|
||||
public void RawPath_resolves_via_lookup()
|
||||
{
|
||||
var r = Make(new() { ["Temp"] = new Def("Temp") }, _ => null);
|
||||
r.TryResolve("Temp", out var def).ShouldBeTrue();
|
||||
var r = Make(new() { ["Area/Modbus/Dev/Temp"] = new Def("Temp") });
|
||||
r.TryResolve("Area/Modbus/Dev/Temp", out var def).ShouldBeTrue();
|
||||
def!.Id.ShouldBe("Temp");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Equipment_ref_resolves_via_parse_and_is_cached()
|
||||
public void Unknown_rawPath_returns_false()
|
||||
{
|
||||
var calls = 0;
|
||||
var r = Make(new(), reference => { calls++; return reference.StartsWith("{") ? new Def(reference) : null; });
|
||||
r.TryResolve("{\"a\":1}", out var d1).ShouldBeTrue();
|
||||
r.TryResolve("{\"a\":1}", out var d2).ShouldBeTrue();
|
||||
d1!.Id.ShouldBe("{\"a\":1}");
|
||||
calls.ShouldBe(1);
|
||||
var r = Make(new() { ["Area/Modbus/Dev/Temp"] = new Def("Temp") });
|
||||
r.TryResolve("Area/Modbus/Dev/Missing", out _).ShouldBeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Garbage_ref_returns_false_and_caches_negative()
|
||||
public void Lookup_is_consulted_each_call()
|
||||
{
|
||||
// v3 holds no transient parse cache — the resolver reads the driver's live table by closure,
|
||||
// so every TryResolve consults the underlying lookup (a table rebuild is immediately visible).
|
||||
var calls = 0;
|
||||
var r = Make(new(), _ => { calls++; return null; });
|
||||
r.TryResolve("nope", out _).ShouldBeFalse();
|
||||
r.TryResolve("nope", out _).ShouldBeFalse();
|
||||
calls.ShouldBe(1);
|
||||
}
|
||||
var table = new Dictionary<string, Def>();
|
||||
var r = new EquipmentTagRefResolver<Def>(reference =>
|
||||
{
|
||||
calls++;
|
||||
return table.TryGetValue(reference, out var d) ? d : null;
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public void Clear_drops_transient_cache()
|
||||
{
|
||||
var calls = 0;
|
||||
var r = Make(new(), reference => { calls++; return new Def(reference); });
|
||||
r.TryResolve("{\"a\":1}", out _).ShouldBeTrue();
|
||||
r.Clear();
|
||||
r.TryResolve("{\"a\":1}", out _).ShouldBeTrue();
|
||||
r.TryResolve("Area/Dev/Temp", out _).ShouldBeFalse();
|
||||
table["Area/Dev/Temp"] = new Def("Temp");
|
||||
r.TryResolve("Area/Dev/Temp", out var def).ShouldBeTrue();
|
||||
def!.Id.ShouldBe("Temp");
|
||||
calls.ShouldBe(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Clear_is_a_noop_and_lookup_still_works()
|
||||
{
|
||||
// Clear() is retained for call-site compatibility only — there is no transient cache to drop.
|
||||
var r = Make(new() { ["ref"] = new Def("ref") });
|
||||
r.TryResolve("ref", out _).ShouldBeTrue();
|
||||
r.Clear();
|
||||
r.TryResolve("ref", out var def).ShouldBeTrue();
|
||||
def!.Id.ShouldBe("ref");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core.Abstractions-009: <see cref="EquipmentTagRefResolver{TDef}.TryResolve"/> carries
|
||||
/// <c>[MaybeNullWhen(false)]</c> on its <c>out</c> parameter so NRT-enabled callers do
|
||||
@@ -62,7 +73,7 @@ public class EquipmentTagRefResolverTests
|
||||
[Fact]
|
||||
public void TryResolve_false_path_sets_def_to_default()
|
||||
{
|
||||
var r = Make(new(), _ => null);
|
||||
var r = Make(new());
|
||||
var returned = r.TryResolve("no-such-ref", out var def);
|
||||
returned.ShouldBeFalse();
|
||||
// The [MaybeNullWhen(false)] attribute means callers must treat def as potentially null
|
||||
|
||||
@@ -9,12 +9,23 @@ namespace ZB.MOM.WW.OtOpcUa.Core.Tests.OpcUa;
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class EquipmentNodeWalkerTests
|
||||
{
|
||||
/// <summary>
|
||||
/// v3: raw tags no longer flow into <see cref="EquipmentNamespaceContent"/> and the walker
|
||||
/// emits no raw-tag variable nodes. Raw tags reach the UNS namespace via
|
||||
/// <c>UnsTagReference</c> as the dual-namespace fan-out of their backing raw nodes, which lands
|
||||
/// with the Batch-4 address space. Tests that asserted the old equipment-tag variable
|
||||
/// materialization are skipped with this reason until that projection is built.
|
||||
/// </summary>
|
||||
internal const string DarkBatch4 =
|
||||
"v3 dark address space: raw-tag → UNS variable materialization lands in Batch 4 " +
|
||||
"(UnsTagReference fan-out), not in EquipmentNodeWalker.";
|
||||
|
||||
/// <summary>Verifies that walking empty content emits no nodes.</summary>
|
||||
[Fact]
|
||||
public void Walk_EmptyContent_EmitsNothing()
|
||||
{
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, new EquipmentNamespaceContent([], [], [], []));
|
||||
EquipmentNodeWalker.Walk(rec, new EquipmentNamespaceContent([], [], []));
|
||||
|
||||
rec.Children.ShouldBeEmpty();
|
||||
}
|
||||
@@ -26,8 +37,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
var content = new EquipmentNamespaceContent(
|
||||
Areas: [Area("area-1", "warsaw"), Area("area-2", "berlin")],
|
||||
Lines: [Line("line-1", "area-1", "oven-line"), Line("line-2", "area-2", "press-line")],
|
||||
Equipment: [Eq("eq-1", "line-1", "oven-3"), Eq("eq-2", "line-2", "press-7")],
|
||||
Tags: []);
|
||||
Equipment: [Eq("eq-1", "line-1", "oven-3"), Eq("eq-2", "line-2", "press-7")]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -49,7 +59,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
eq.ZTag = null;
|
||||
eq.SAPID = null;
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], []);
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -72,7 +82,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
eq.ZTag = "ZT-0042";
|
||||
eq.SAPID = "10000042";
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], []);
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -90,7 +100,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
eq.Manufacturer = "Trumpf";
|
||||
eq.Model = "TruLaser-3030";
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], []);
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -108,7 +118,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
{
|
||||
var eq = Eq("eq-1", "line-1", "oven-3"); // no identification fields
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], []);
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -117,42 +127,18 @@ public sealed class EquipmentNodeWalkerTests
|
||||
equipmentNode.Children.ShouldNotContain(c => c.BrowseName == "Identification");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that walking emits a variable for each bound tag under equipment.</summary>
|
||||
[Fact]
|
||||
public void Walk_Emits_Variable_Per_BoundTag_Under_Equipment()
|
||||
/// <summary>
|
||||
/// Batch-4 pending: the walker must emit a raw-tag variable per <c>UnsTagReference</c> under
|
||||
/// equipment (dual-namespace fan-out). Preserved as a skipped placeholder — the pre-v3 test
|
||||
/// asserted equipment-tag variable materialization directly off an equipment-bound <c>Tag</c>,
|
||||
/// which no longer exists (tags bind to Devices, not equipment). Intent retained; unskip when
|
||||
/// the Batch-4 UNS projection lands.
|
||||
/// </summary>
|
||||
[Fact(Skip = DarkBatch4)]
|
||||
public void Walk_Emits_RawTag_Variables_Under_Equipment_DARK_Batch4()
|
||||
{
|
||||
var eq = Eq("eq-1", "line-1", "oven-3");
|
||||
var tag1 = NewTag("tag-1", "Temperature", "Int32", "plcaddr-01", equipmentId: "eq-1");
|
||||
var tag2 = NewTag("tag-2", "Setpoint", "Float32", "plcaddr-02", equipmentId: "eq-1");
|
||||
var unboundTag = NewTag("tag-3", "Orphan", "Int32", "plcaddr-03", equipmentId: null); // SystemPlatform-style, walker skips
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [tag1, tag2, unboundTag]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
|
||||
var equipmentNode = rec.Children[0].Children[0].Children[0];
|
||||
equipmentNode.Variables.Count.ShouldBe(2);
|
||||
equipmentNode.Variables.Select(v => v.BrowseName).ShouldBe(["Setpoint", "Temperature"]);
|
||||
equipmentNode.Variables.First(v => v.BrowseName == "Temperature").AttributeInfo.FullName.ShouldBe("plcaddr-01");
|
||||
equipmentNode.Variables.First(v => v.BrowseName == "Setpoint").AttributeInfo.DriverDataType.ShouldBe(DriverDataType.Float32);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that walking falls back to String type for unparseable data types.</summary>
|
||||
[Fact]
|
||||
public void Walk_FallsBack_To_String_For_Unparseable_DataType()
|
||||
{
|
||||
var eq = Eq("eq-1", "line-1", "oven-3");
|
||||
var tag = NewTag("tag-1", "Mystery", "NotARealType", "plcaddr-42", equipmentId: "eq-1");
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], [tag]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
|
||||
var variable = rec.Children[0].Children[0].Children[0].Variables.Single();
|
||||
variable.AttributeInfo.DriverDataType.ShouldBe(DriverDataType.String);
|
||||
// When Batch 4 lands, EquipmentNamespaceContent carries UnsTagReference rows and the walker
|
||||
// emits one Variable per referenced raw node (correct DriverDataType, address, source kind).
|
||||
}
|
||||
|
||||
/// <summary>Verifies that walking emits virtual tag variables with Virtual source discriminator.</summary>
|
||||
@@ -168,7 +154,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
};
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [], VirtualTags: [vtag]);
|
||||
[eq], VirtualTags: [vtag]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -196,7 +182,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
};
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [], ScriptedAlarms: [alarm]);
|
||||
[eq], ScriptedAlarms: [alarm]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -229,7 +215,7 @@ public sealed class EquipmentNodeWalkerTests
|
||||
};
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [], VirtualTags: [vtag], ScriptedAlarms: [alarm]);
|
||||
[eq], VirtualTags: [vtag], ScriptedAlarms: [alarm]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
@@ -241,10 +227,10 @@ public sealed class EquipmentNodeWalkerTests
|
||||
[Fact]
|
||||
public void Walk_Null_VirtualTags_And_ScriptedAlarms_Is_Safe()
|
||||
{
|
||||
// Backwards-compat — callers that don't populate the new collections still work.
|
||||
// Backwards-compat — callers that don't populate the optional collections still work.
|
||||
var eq = Eq("eq-1", "line-1", "oven-3");
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq], []);
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")], [eq]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content); // must not throw
|
||||
@@ -252,81 +238,6 @@ public sealed class EquipmentNodeWalkerTests
|
||||
rec.Children[0].Children[0].Children[0].Variables.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that driver tag default NodeSourceKind is Driver.</summary>
|
||||
[Fact]
|
||||
public void Driver_tag_default_NodeSourceKind_is_Driver()
|
||||
{
|
||||
var eq = Eq("eq-1", "line-1", "oven-3");
|
||||
var tag = NewTag("t-1", "Temp", "Int32", "plc-01", "eq-1");
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [tag]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
|
||||
var v = rec.Children[0].Children[0].Children[0].Variables.Single();
|
||||
v.AttributeInfo.Source.ShouldBe(NodeSourceKind.Driver);
|
||||
v.AttributeInfo.VirtualTagId.ShouldBeNull();
|
||||
v.AttributeInfo.ScriptedAlarmId.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ExtractFullName unwraps a JSON object with FullName field.</summary>
|
||||
[Fact]
|
||||
public void ExtractFullName_unwraps_json_object_with_FullName_field()
|
||||
{
|
||||
EquipmentNodeWalker.ExtractFullName(
|
||||
"{\"FullName\":\"MESReceiver_001.MoveInBatchID\",\"DataType\":\"Int32\"}")
|
||||
.ShouldBe("MESReceiver_001.MoveInBatchID");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ExtractFullName handles S7-style extra fields.</summary>
|
||||
[Fact]
|
||||
public void ExtractFullName_handles_S7_style_extra_fields()
|
||||
{
|
||||
EquipmentNodeWalker.ExtractFullName(
|
||||
"{\"FullName\":\"DB1_DBW0\",\"Address\":\"DB1.DBW0\",\"DataType\":\"Int16\"}")
|
||||
.ShouldBe("DB1_DBW0");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ExtractFullName returns raw string when input is not JSON.</summary>
|
||||
[Fact]
|
||||
public void ExtractFullName_returns_raw_when_not_json()
|
||||
{
|
||||
// Drivers that opt out of JSON TagConfig still work — fallback preserves the literal
|
||||
// string so the driver's IReadable sees whatever the row author stored.
|
||||
EquipmentNodeWalker.ExtractFullName("raw-tag-ref").ShouldBe("raw-tag-ref");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ExtractFullName returns raw string when JSON is missing FullName field.</summary>
|
||||
[Fact]
|
||||
public void ExtractFullName_returns_raw_when_json_missing_FullName_field()
|
||||
{
|
||||
EquipmentNodeWalker.ExtractFullName("{\"Address\":\"DB1.DBW0\"}")
|
||||
.ShouldBe("{\"Address\":\"DB1.DBW0\"}");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that driver tag FullName passes through from TagConfig JSON.</summary>
|
||||
[Fact]
|
||||
public void Driver_tag_FullName_passes_through_from_TagConfig_json()
|
||||
{
|
||||
// The walker hands the driver the unwrapped FullName string so IReadable.ReadAsync
|
||||
// sees the plain address, not the raw TagConfig JSON. Verifies the dispatch contract
|
||||
// the path-based NodeId refactor relies on.
|
||||
var eq = Eq("eq-1", "line-1", "oven-3");
|
||||
var tag = NewTag("t-1", "Temp", "Int32", "plc-01", "eq-1",
|
||||
tagConfig: "{\"FullName\":\"plc-01/HR200\",\"DataType\":\"Int32\"}");
|
||||
var content = new EquipmentNamespaceContent(
|
||||
[Area("area-1", "warsaw")], [Line("line-1", "area-1", "line-a")],
|
||||
[eq], [tag]);
|
||||
|
||||
var rec = new RecordingBuilder("root");
|
||||
EquipmentNodeWalker.Walk(rec, content);
|
||||
|
||||
var v = rec.Children[0].Children[0].Children[0].Variables.Single();
|
||||
v.AttributeInfo.FullName.ShouldBe("plc-01/HR200");
|
||||
}
|
||||
|
||||
// ----- builders for test seed rows -----
|
||||
|
||||
private static UnsArea Area(string id, string name) => new()
|
||||
@@ -344,25 +255,11 @@ public sealed class EquipmentNodeWalkerTests
|
||||
EquipmentRowId = Guid.NewGuid(),
|
||||
EquipmentId = equipmentId,
|
||||
EquipmentUuid = Guid.NewGuid(),
|
||||
DriverInstanceId = "drv",
|
||||
UnsLineId = lineId,
|
||||
Name = name,
|
||||
MachineCode = "MC-" + name,
|
||||
};
|
||||
|
||||
private static Tag NewTag(string tagId, string name, string dataType, string address,
|
||||
string? equipmentId, string? tagConfig = null) => new()
|
||||
{
|
||||
TagRowId = Guid.NewGuid(),
|
||||
TagId = tagId,
|
||||
DriverInstanceId = "drv",
|
||||
EquipmentId = equipmentId,
|
||||
Name = name,
|
||||
DataType = dataType,
|
||||
AccessLevel = ZB.MOM.WW.OtOpcUa.Configuration.Enums.TagAccessLevel.ReadWrite,
|
||||
TagConfig = tagConfig ?? address,
|
||||
};
|
||||
|
||||
// ----- recording IAddressSpaceBuilder -----
|
||||
|
||||
/// <summary>Test implementation of IAddressSpaceBuilder that records calls.</summary>
|
||||
|
||||
@@ -45,7 +45,6 @@ public sealed class IdentificationFolderBuilderTests
|
||||
private static Equipment EmptyEquipment() => new()
|
||||
{
|
||||
EquipmentId = "EQ-000000000001",
|
||||
DriverInstanceId = "drv-1",
|
||||
UnsLineId = "line-1",
|
||||
Name = "eq-1",
|
||||
MachineCode = "machine_001",
|
||||
@@ -54,7 +53,6 @@ public sealed class IdentificationFolderBuilderTests
|
||||
private static Equipment FullyPopulatedEquipment() => new()
|
||||
{
|
||||
EquipmentId = "EQ-000000000001",
|
||||
DriverInstanceId = "drv-1",
|
||||
UnsLineId = "line-1",
|
||||
Name = "eq-1",
|
||||
MachineCode = "machine_001",
|
||||
|
||||
Reference in New Issue
Block a user