From da1accceff66654365896a226cc771ae8f4b87e7 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 13 Jun 2026 06:27:52 -0400 Subject: [PATCH] feat(runtime): carry DriverInstanceId on AttributeValuePublished (live-value routing key) --- .../Drivers/DriverInstanceActor.cs | 4 ++-- .../VirtualTags/DependencyMuxActorTests.cs | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs index 1255b4f8..8485e9ba 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs @@ -62,7 +62,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers public sealed record ForceReconnect; /// Published to the actor's parent whenever the subscribed IDriver fires /// . The parent forwards to OpcUaPublishActor. - public sealed record AttributeValuePublished(string FullReference, object? Value, OpcUaQuality Quality, DateTime TimestampUtc); + public sealed record AttributeValuePublished(string DriverInstanceId, string FullReference, object? Value, OpcUaQuality Quality, DateTime TimestampUtc); private sealed record DataChangeForward(string FullReference, DataValueSnapshot Snapshot); public sealed class RetryConnect { @@ -446,7 +446,7 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers { var quality = QualityFromStatus(msg.Snapshot.StatusCode); var ts = msg.Snapshot.SourceTimestampUtc ?? msg.Snapshot.ServerTimestampUtc; - Context.Parent.Tell(new AttributeValuePublished(msg.FullReference, msg.Snapshot.Value, quality, ts)); + Context.Parent.Tell(new AttributeValuePublished(_driverInstanceId, msg.FullReference, msg.Snapshot.Value, quality, ts)); } /// Translate an OPC UA status code to the 3-state projection diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs index 2c237ab6..c026cee1 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/DependencyMuxActorTests.cs @@ -21,9 +21,9 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-1", "tag-2" }, subA.Ref)); mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-2", "tag-3" }, subB.Ref)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-3", 30, OpcUaQuality.Good, DateTime.UtcNow)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-2", 20, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-3", 30, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-2", 20, OpcUaQuality.Good, DateTime.UtcNow)); // subA hears tag-1 + tag-2. var aMsgs = new[] @@ -53,7 +53,7 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase var sub = CreateTestProbe(); mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-1" }, sub.Ref)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("nobody-cares", 99, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "nobody-cares", 99, OpcUaQuality.Good, DateTime.UtcNow)); sub.ExpectNoMsg(TimeSpan.FromMilliseconds(200)); } @@ -66,11 +66,11 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase var sub = CreateTestProbe(); mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-1" }, sub.Ref)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); sub.ExpectMsg(); mux.Tell(new DependencyMuxActor.UnregisterInterest(sub.Ref)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-1", 20, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-1", 20, OpcUaQuality.Good, DateTime.UtcNow)); sub.ExpectNoMsg(TimeSpan.FromMilliseconds(200)); } @@ -84,10 +84,10 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-1" }, sub.Ref)); mux.Tell(new DependencyMuxActor.RegisterInterest(new[] { "tag-2" }, sub.Ref)); // replaces tag-1 - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-1", 10, OpcUaQuality.Good, DateTime.UtcNow)); sub.ExpectNoMsg(TimeSpan.FromMilliseconds(200)); - mux.Tell(new DriverInstanceActor.AttributeValuePublished("tag-2", 20, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "tag-2", 20, OpcUaQuality.Good, DateTime.UtcNow)); sub.ExpectMsg().TagId.ShouldBe("tag-2"); } @@ -110,17 +110,17 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase AwaitAssert(() => { mux.Tell(new DriverInstanceActor.AttributeValuePublished( - "ref-a", 10, OpcUaQuality.Good, DateTime.UtcNow)); + "driver-1", "ref-a", 10, OpcUaQuality.Good, DateTime.UtcNow)); parent.ExpectMsg(TimeSpan.FromMilliseconds(200)) .Value.ShouldBe(10); }, duration: TimeSpan.FromSeconds(3)); // From here the actor is wired — second publish drives the sum. - mux.Tell(new DriverInstanceActor.AttributeValuePublished("ref-b", 32, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "ref-b", 32, OpcUaQuality.Good, DateTime.UtcNow)); parent.ExpectMsg(TimeSpan.FromSeconds(2)).Value.ShouldBe(42); // Unrelated ref shouldn't fire eval. - mux.Tell(new DriverInstanceActor.AttributeValuePublished("ref-unrelated", 99, OpcUaQuality.Good, DateTime.UtcNow)); + mux.Tell(new DriverInstanceActor.AttributeValuePublished("driver-1", "ref-unrelated", 99, OpcUaQuality.Good, DateTime.UtcNow)); parent.ExpectNoMsg(TimeSpan.FromMilliseconds(200)); } @@ -144,7 +144,7 @@ public sealed class DependencyMuxActorTests : RuntimeActorTestBase // Tell the host an AttributeValuePublished — it should fan out to the mux + subscriber. hostActor.Tell(new DriverInstanceActor.AttributeValuePublished( - "ref-1", 42, OpcUaQuality.Good, DateTime.UtcNow)); + "driver-1", "ref-1", 42, OpcUaQuality.Good, DateTime.UtcNow)); subscriber.ExpectMsg().TagId.ShouldBe("ref-1"); }