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 4469cc7..09ba5f0 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DriverInstanceActor.cs @@ -45,15 +45,48 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers public ITimerScheduler Timers { get; set; } = null!; - public static Props Props(IDriver driver, TimeSpan? reconnectInterval = null) => - Akka.Actor.Props.Create(() => new DriverInstanceActor(driver, reconnectInterval ?? DefaultReconnectInterval)); + public static Props Props(IDriver driver, TimeSpan? reconnectInterval = null, bool startStubbed = false) => + Akka.Actor.Props.Create(() => new DriverInstanceActor(driver, reconnectInterval ?? DefaultReconnectInterval, startStubbed)); - public DriverInstanceActor(IDriver driver, TimeSpan reconnectInterval) + /// + /// Returns true when the driver should boot in DEV-STUB mode based on host platform and + /// configured roles. Mirrors plan §Task 55: Windows-only driver types (Galaxy, Wonderware + /// Historian) are stubbed when running on non-Windows OR when the host carries the + /// dev role. + /// + public static bool ShouldStub(string driverType, IEnumerable roles) + { + var isWindowsOnly = driverType is "Galaxy" or "Historian.Wonderware"; + if (!OperatingSystem.IsWindows() && isWindowsOnly) return true; + if (roles.Contains("dev") && isWindowsOnly) return true; + return false; + } + + public DriverInstanceActor(IDriver driver, TimeSpan reconnectInterval, bool startStubbed = false) { _driver = driver; _driverInstanceId = driver.DriverInstanceId; _reconnectInterval = reconnectInterval; - Become(Connecting); + if (startStubbed) + { + Context.GetLogger().Info("[DEV-STUB] driver={Name} type={Type}", + _driverInstanceId, driver.DriverType); + Become(Stubbed); + } + else + { + Become(Connecting); + } + } + + private void Stubbed() + { + // Stubbed drivers accept the standard message contracts but return deterministic + // success without touching real hardware. Read returns null; Write succeeds. + Receive(_ => { /* no-op */ }); + Receive(msg => Sender.Tell(new ApplyResult(true, "stubbed", msg.Correlation))); + Receive(_ => Sender.Tell(new WriteAttributeResult(true, "stubbed"))); + Receive(_ => { /* stubbed drivers don't disconnect */ }); } private void Connecting()