f4f3e17e3e
Un-darken the address-space composition for the v3 dual namespace. Composer (AddressSpaceComposer): - Raw subtree: RawContainerNode (Folder/Driver/Device/TagGroup as Object/Folder nodes, keyed s=<RawPath>, parent-before-child) + RawTagPlan (raw tag Variables keyed (realm=Raw, s=<RawPath>) carrying DataType / writable / historize+historian tagname / array shape). Native-alarm intent attaches at the RAW tag (ConditionId = RawPath) with the list of referencing equipment UNS paths (one alarm at the raw tag, not one per equipment). - UNS subtree: UnsReferenceVariable per UnsTagReference, keyed (realm=Uns, s=<Area>/<Line>/<Equipment>/<EffectiveName>), carrying its backing RawPath (Organizes UNS->Raw target + fan-out) and inheriting DataType/AccessLevel from the backing raw tag. Effective name = DisplayNameOverride else backing raw Tag.Name. - All RawPaths flow through one shared RawTopology (RawPathResolver + memoised container paths), byte-parity with EquipmentReferenceMap. - Every new node carries an AddressSpaceRealm (so WP3's applier picks the namespace at the call site). Additive/defaulted model changes only — the un-migrated AddressSpaceApplier still compiles. Planner (AddressSpacePlanner.Compute) + AddressSpacePlan: - Per-realm diff sets: RawContainers + RawTags keyed by RawPath (NodeId) so a rename = remove+add; UnsReferenceVariables keyed by the stable UnsTagReferenceId so a backing-tag rename re-points (BackingRawPath moves, NodeId stable) and a display-name-override edit is UNS-only. - PINNED: raw-tag rename = remove+add in Raw AND re-point in UNS. Classifier folds the new sets in (adds->PureAdd, removes->PureRemove, changed->Rebuild safe default). Tests: AddressSpaceComposerDualNamespaceTests (7), AddressSpacePlannerDualNamespaceTests (8, incl. the rename pin), AddressSpaceChangeClassifierDualNamespaceTests (8); reactivated EquipmentNamespaceMaterializationTests' Batch-4-pending case, re-authored to drive the composer over the seeded config (the artifact-decode seam is WP3). Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox
104 lines
4.1 KiB
C#
104 lines
4.1 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
|
|
|
|
/// <summary>
|
|
/// v3 Batch 4 (B4-WP1) — the classifier folds the Raw + UNS subtree diff sets into its routing policy:
|
|
/// raw/uns adds count as adds (PureAdd), removes as removes (PureRemove), and any Changed raw/uns delta
|
|
/// routes to Rebuild (the safe default — WP3's applier owns surgical refinement).
|
|
/// </summary>
|
|
public sealed class AddressSpaceChangeClassifierDualNamespaceTests
|
|
{
|
|
private static readonly RawContainerNode Folder = new("Plant", null, "Plant", RawNodeKind.Folder);
|
|
|
|
private static RawTagPlan RawTag(string nodeId) =>
|
|
new("t-1", nodeId, "Plant/Modbus1/PLC-A", "drv-1", "Speed", "Float",
|
|
Writable: false, Alarm: null, ReferencingEquipmentPaths: Array.Empty<string>());
|
|
|
|
private static UnsReferenceVariable UnsRef(string nodeId) =>
|
|
new("ref-1", "eq-1", nodeId, "Speed", "Plant/Modbus1/PLC-A/Speed", "Float", Writable: false);
|
|
|
|
private static AddressSpacePlan Empty() => new(
|
|
Array.Empty<EquipmentNode>(), Array.Empty<EquipmentNode>(), Array.Empty<AddressSpacePlan.EquipmentDelta>(),
|
|
Array.Empty<DriverInstancePlan>(), Array.Empty<DriverInstancePlan>(), Array.Empty<AddressSpacePlan.DriverDelta>(),
|
|
Array.Empty<ScriptedAlarmPlan>(), Array.Empty<ScriptedAlarmPlan>(), Array.Empty<AddressSpacePlan.AlarmDelta>());
|
|
|
|
[Fact]
|
|
public void Added_raw_tag_only_is_PureAdd()
|
|
{
|
|
var plan = Empty() with { AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } };
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
|
|
}
|
|
|
|
[Fact]
|
|
public void Added_raw_container_only_is_PureAdd()
|
|
{
|
|
var plan = Empty() with { AddedRawContainers = new[] { Folder } };
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
|
|
}
|
|
|
|
[Fact]
|
|
public void Added_uns_reference_only_is_PureAdd()
|
|
{
|
|
var plan = Empty() with { AddedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } };
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd);
|
|
}
|
|
|
|
[Fact]
|
|
public void Removed_raw_tag_only_is_PureRemove()
|
|
{
|
|
var plan = Empty() with { RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } };
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove);
|
|
}
|
|
|
|
[Fact]
|
|
public void Removed_uns_reference_only_is_PureRemove()
|
|
{
|
|
var plan = Empty() with { RemovedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } };
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove);
|
|
}
|
|
|
|
[Fact]
|
|
public void Raw_rename_add_and_remove_is_AddRemoveMix()
|
|
{
|
|
var plan = Empty() with
|
|
{
|
|
RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") },
|
|
AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Velocity") },
|
|
};
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.AddRemoveMix);
|
|
}
|
|
|
|
[Fact]
|
|
public void Changed_raw_tag_is_Rebuild()
|
|
{
|
|
var plan = Empty() with
|
|
{
|
|
ChangedRawTags = new[]
|
|
{
|
|
new AddressSpacePlan.RawTagDelta(
|
|
RawTag("Plant/Modbus1/PLC-A/Speed"),
|
|
RawTag("Plant/Modbus1/PLC-A/Speed") with { IsHistorized = true }),
|
|
},
|
|
};
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild);
|
|
}
|
|
|
|
[Fact]
|
|
public void Uns_reference_repoint_is_Rebuild()
|
|
{
|
|
var plan = Empty() with
|
|
{
|
|
ChangedUnsReferenceVariables = new[]
|
|
{
|
|
new AddressSpacePlan.UnsReferenceDelta(
|
|
UnsRef("filling/line-1/station-1/Speed"),
|
|
UnsRef("filling/line-1/station-1/Speed") with { BackingRawPath = "Plant/Modbus1/PLC-A/Velocity" }),
|
|
},
|
|
};
|
|
AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild);
|
|
}
|
|
}
|