feat(opcua): add EquipmentVirtualTagPlan to Phase7 composition
Adds the EquipmentVirtualTagPlan sealed record (VirtualTagId, EquipmentId, FolderPath, Name, DataType, Expression, DependencyRefs) and the EquipmentVirtualTags init-only member on Phase7CompositionResult, mirroring the existing EquipmentTagPlan / EquipmentTags pattern. Type-only: no producer logic yet. Two new tests cover the default-empty guarantee and the record shape.
This commit is contained in:
@@ -56,6 +56,10 @@ public sealed record Phase7CompositionResult(
|
||||
/// constructor + call site keeps compiling unchanged; new producers set it via initializer.
|
||||
/// </summary>
|
||||
public IReadOnlyList<EquipmentTagPlan> EquipmentTags { get; init; } = Array.Empty<EquipmentTagPlan>();
|
||||
|
||||
/// <summary>Equipment-namespace VirtualTags. See <see cref="EquipmentVirtualTagPlan"/>. Init-only,
|
||||
/// defaults empty so every existing constructor + call site keeps compiling.</summary>
|
||||
public IReadOnlyList<EquipmentVirtualTagPlan> EquipmentVirtualTags { get; init; } = Array.Empty<EquipmentVirtualTagPlan>();
|
||||
}
|
||||
|
||||
public sealed record UnsAreaProjection(string UnsAreaId, string DisplayName);
|
||||
@@ -100,6 +104,26 @@ public sealed record EquipmentTagPlan(
|
||||
string DataType,
|
||||
string FullName);
|
||||
|
||||
/// <summary>
|
||||
/// One Equipment-namespace VirtualTag from a <see cref="VirtualTag"/> row (joined to its
|
||||
/// <see cref="Script"/> for the expression). The VirtualTag value analogue of
|
||||
/// <see cref="EquipmentTagPlan"/>: <c>Phase7Applier.MaterialiseEquipmentVirtualTags</c>
|
||||
/// materialises each as a Variable under its equipment folder with a folder-scoped NodeId
|
||||
/// (<c>EquipmentId/Name</c>, or <c>EquipmentId/FolderPath/Name</c> when a sub-folder is set),
|
||||
/// and <c>VirtualTagHostActor</c> spawns a <c>VirtualTagActor</c> per plan that evaluates
|
||||
/// <see cref="Expression"/> over <see cref="DependencyRefs"/> and publishes the value back to
|
||||
/// that NodeId. <see cref="DependencyRefs"/> = the distinct <c>ctx.GetTag("…")</c> literals in
|
||||
/// the script source.
|
||||
/// </summary>
|
||||
public sealed record EquipmentVirtualTagPlan(
|
||||
string VirtualTagId,
|
||||
string EquipmentId,
|
||||
string FolderPath,
|
||||
string Name,
|
||||
string DataType,
|
||||
string Expression,
|
||||
IReadOnlyList<string> DependencyRefs);
|
||||
|
||||
/// <summary>
|
||||
/// Pure composer that flattens the live-edit DB tables into the address-space build plan a
|
||||
/// driver-role host needs. Same inputs → same outputs, no logging, no DB writes. The driver-role
|
||||
|
||||
@@ -91,6 +91,26 @@ public sealed class Phase7ComposerPurityTests
|
||||
node.DisplayName.ShouldNotBe("FILLING-EQ"); // not the colloquial MachineCode
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Composition_carries_empty_equipment_virtualtags_by_default()
|
||||
{
|
||||
var r = new Phase7CompositionResult(
|
||||
Array.Empty<EquipmentNode>(), Array.Empty<DriverInstancePlan>(), Array.Empty<ScriptedAlarmPlan>());
|
||||
r.EquipmentVirtualTags.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EquipmentVirtualTagPlan_holds_id_equipment_name_datatype_expression_and_deps()
|
||||
{
|
||||
var p = new EquipmentVirtualTagPlan("vt-1", "eq-1", "", "speed-rpm", "Float64",
|
||||
"return ctx.GetTag(\"TestMachine_001.TestDouble\").Value;",
|
||||
new[] { "TestMachine_001.TestDouble" });
|
||||
p.VirtualTagId.ShouldBe("vt-1");
|
||||
p.EquipmentId.ShouldBe("eq-1");
|
||||
p.Name.ShouldBe("speed-rpm");
|
||||
p.DependencyRefs.ShouldHaveSingleItem();
|
||||
}
|
||||
|
||||
private static Equipment NewEquipment(string id) => new()
|
||||
{
|
||||
EquipmentId = id,
|
||||
|
||||
Reference in New Issue
Block a user