From 6dc5af7aa25caadbc04317b7b8ca0027476165e5 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 16 Jul 2026 05:53:52 -0400 Subject: [PATCH] v3 Batch 3 contracts: IEffectiveNameGuard authoring-time uniqueness seam Wave-A shared contract. WP2 implements + registers the guard; WP1 consumes it in UnsTreeService reference/VirtualTag/ScriptedAlarm mutations. Ordinal, mirrors the deploy-time DraftValidator UnsEffectiveNameCollision rule. Claude-Session: https://claude.ai/code/session_01LVneM3eh1UtJxEisFXgmox --- .../Uns/IEffectiveNameGuard.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs new file mode 100644 index 00000000..959d8acd --- /dev/null +++ b/src/Server/ZB.MOM.WW.OtOpcUa.AdminUI/Uns/IEffectiveNameGuard.cs @@ -0,0 +1,61 @@ +namespace ZB.MOM.WW.OtOpcUa.AdminUI.Uns; + +/// +/// Which authoring surface a proposed effective name comes from. Used to word the collision +/// error and to exclude the correct same-kind self-row on an edit (rename / override change). +/// +public enum EffectiveNameSourceKind +{ + /// A (effective name = + /// DisplayNameOverride else the backing raw tag's Name). + Reference, + + /// An equipment-bound (effective name = Name). + VirtualTag, + + /// An equipment-bound (effective name = Name). + ScriptedAlarm, +} + +/// +/// v3 Batch 3 authoring-time guard for the UNS effective-name uniqueness rule. Within an +/// equipment the EFFECTIVE leaf name must be unique across its UnsTagReferences +/// (DisplayNameOverride else the backing raw tag's Name), its VirtualTags +/// (Name), and its ScriptedAlarms (Name) — the three share the equipment's +/// UNS NodeId space ({EquipmentId}/{EffectiveName}). This is the authoring mirror of the +/// deploy-time DraftValidator UnsEffectiveNameCollision rule; comparison is +/// to match NodeId semantics and the validator. +/// +/// +/// Injectable (registered Scoped, its own pooled context per call — the same pattern +/// UnsTreeService uses). Consumed by UnsTreeService's reference / virtual-tag / +/// scripted-alarm mutations, which call before persisting and surface a +/// non-null result as the mutation's readable failure. The deploy gate remains the backstop for +/// rename-induced collisions the authoring check never saw. +/// +public interface IEffectiveNameGuard +{ + /// + /// Tests whether would collide with an existing + /// effective name within . + /// + /// The owning equipment's logical id. + /// The effective name to be authored (override else raw name for a reference; Name for a VT/alarm). + /// Which surface the proposed name is for (words the error; identifies the self-row kind to exclude). + /// + /// On an edit, the logical id of the row being changed (its UnsTagReferenceId / + /// VirtualTagId / ScriptedAlarmId) so it is excluded from the collision set; + /// on a create. + /// + /// Cancellation token. + /// + /// when the name is free; otherwise a readable error naming the + /// colliding existing source and the equipment. + /// + Task CheckAsync( + string equipmentId, + string proposedEffectiveName, + EffectiveNameSourceKind kind, + string? excludeSourceId, + CancellationToken ct = default); +}