From c6a543d1b61f188a18645cf3d8f6e05102687d91 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 15 Jun 2026 10:50:08 -0400 Subject: [PATCH] docs(vtags): note rename-respawn transient + write-side-only historize (integration review) --- src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs | 4 ++++ .../VirtualTags/VirtualTagHostActor.cs | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs index d922ed72..72b9d1c4 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/Phase7Applier.cs @@ -253,6 +253,10 @@ public sealed class Phase7Applier // Variables: NodeId is FOLDER-SCOPED ("/"), mirroring the equipment-tag pass. // Parent is the FolderPath sub-folder when set, else the equipment folder directly. + // NOTE (H5): a VirtualTag's Historize flag is honoured on the WRITE side only — VirtualTagHostActor + // forwards historized results to IHistoryWriter. It is intentionally NOT materialised as an SDK + // Historizing/HistoryRead variable here (no server-side OPC UA HistoryRead for vtags), so these + // stay plain read-only computed-output nodes. foreach (var v in composition.EquipmentVirtualTags) { var parent = string.IsNullOrWhiteSpace(v.FolderPath) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagHostActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagHostActor.cs index 6bc2cb7c..61571288 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagHostActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/VirtualTags/VirtualTagHostActor.cs @@ -113,6 +113,11 @@ public sealed class VirtualTagHostActor : ReceiveActor // Rebuild the NodeId map every apply so renames (Name/FolderPath/EquipmentId changes) are // picked up. The map only contains currently-desired vtags, so a result for a removed vtag // finds no entry and is dropped. + // NOTE: if a respawn (above) was triggered by a Name/FolderPath/EquipmentId change, the + // stopping old child may still deliver ONE in-flight result keyed by the same vtagId, which + // OnResult would publish to the NEW NodeId. This is a transient phantom that the new child's + // next evaluation overwrites — acceptable (and strictly better than the pre-H1b behaviour, + // where a renamed vtag was a silent no-op). _nodeIdByVtag.Clear(); foreach (var p in msg.Plans) {