diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs index be81c509..4d8954f7 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverHostActor.cs @@ -524,6 +524,11 @@ public sealed class DriverHostActor : ReceiveActor, IWithTimers // VirtualTagActor per plan and streams their evaluated values back onto the just-rebuilt // address space. Runs on BOTH the fresh-apply path (ApplyAndAck) and the bootstrap-restore // path (RestoreApplied) because both call this method, so one send covers both. + // NOTE: the Stale-recovery path (TryRecoverFromStale) does NOT call PushDesiredSubscriptions, + // so — like drivers — VirtualTags remain empty after a Stale recovery until the next + // deployment dispatch. This is intentional and consistent with driver recovery: the Stale + // path only restores the revision marker + NodeDeploymentState; a subsequent dispatch + // (or a redeploy from AdminUI) triggers the full apply + subscribe pass. _virtualTagHost?.Tell(new VirtualTagHostActor.ApplyVirtualTags(composition.EquipmentVirtualTags)); if (composition.EquipmentVirtualTags.Count > 0) { diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs index f1ec86f6..d17adae3 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/ServiceCollectionExtensions.cs @@ -93,7 +93,13 @@ public static class ServiceCollectionExtensions // Production evaluator is the Host's RoslynVirtualTagEvaluator (registered as // IVirtualTagEvaluator); fall back to the null evaluator for test harnesses that don't // register one (VirtualTagActor children then evaluate to nothing). - var virtualTagEvaluator = resolver.GetService() ?? NullVirtualTagEvaluator.Instance; + var virtualTagEvaluator = resolver.GetService(); + if (virtualTagEvaluator is null) + { + loggerFactory.CreateLogger("ZB.MOM.WW.OtOpcUa.Runtime.ServiceCollectionExtensions") + .LogWarning("IVirtualTagEvaluator not registered; Equipment VirtualTags will evaluate to NoChange (no live values). Expected only in test harnesses — driver-role nodes should register RoslynVirtualTagEvaluator."); + virtualTagEvaluator = NullVirtualTagEvaluator.Instance; + } var dbHealth = system.ActorOf( DbHealthProbeActor.Props(dbFactory),