diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index df44ab51..546a2a67 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -151,6 +151,8 @@ namespace PepperDash.Core // Thread-safety lock for state changes private readonly object _stateLock = new object(); + private volatile bool _isProgramStopping; + private bool disconnectLogged = false; /// @@ -207,11 +209,9 @@ namespace PepperDash.Core { if (programEventType == eProgramStatusEventType.Stopping) { - if (client != null) - { - this.LogDebug("Program stopping. Closing connection"); - Disconnect(); - } + _isProgramStopping = true; + this.LogDebug("Program stopping. Closing connection"); + Disconnect(); } } @@ -228,6 +228,12 @@ namespace PepperDash.Core return; } + if (_isProgramStopping) + { + this.LogDebug("Skipping connect because program is stopping"); + return; + } + ConnectEnabled = true; try @@ -287,13 +293,7 @@ namespace PepperDash.Core } catch (SshConnectionException e) { - var ie = e.InnerException; // The details are inside!! - - if (ie is SocketException) - { - this.LogError("CONNECTION failure: Cannot reach host"); - this.LogVerbose(ie, "Exception details: "); - } + var ie = e.InnerException; // The details are inside, when present - remote can close the connection with no inner exception at all if (ie is System.Net.Sockets.SocketException socketException) { @@ -301,20 +301,20 @@ namespace PepperDash.Core Hostname, Port); this.LogVerbose(socketException, "SocketException details: "); } - if (ie is SshAuthenticationException) + else if (ie is SshAuthenticationException) { this.LogError("Authentication failure for username {userName}", Username); this.LogVerbose(ie, "AuthenticationException details: "); } else { - this.LogError("Error on connect: {error}", ie.Message); - this.LogVerbose(ie, "Exception details: "); + this.LogError("Error on connect: {error}", ie?.Message ?? e.Message); + this.LogVerbose(ie ?? e, "Exception details: "); } disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -326,7 +326,7 @@ namespace PepperDash.Core disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -338,7 +338,7 @@ namespace PepperDash.Core this.LogVerbose(e, "Exception details: "); disconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - if (AutoReconnect) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -473,7 +473,7 @@ namespace PepperDash.Core { connectLock.Release(); } - if (AutoReconnect && ConnectEnabled) + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) { this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); StartReconnectTimer(); @@ -516,7 +516,10 @@ namespace PepperDash.Core this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { @@ -549,7 +552,10 @@ namespace PepperDash.Core this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); - StartReconnectTimer(); + if (AutoReconnect && ConnectEnabled && !_isProgramStopping) + { + StartReconnectTimer(); + } } catch (Exception ex) { diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs index 45453bc1..283ef0c8 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/ITechPasswordMessenger.cs @@ -11,15 +11,6 @@ namespace PepperDash.Essentials.AppServer.Messengers { private readonly ITechPassword _room; - // Captures the id of the client whose /validateTechPassword request is in flight, so the - // TechPasswordValidateResult handler below can reply to only that client instead of - // broadcasting to every connected panel. Relies on ValidateTechPassword firing the event - // synchronously (true for all known ITechPassword implementations); if a future - // implementation validates asynchronously, _pendingClientId will already be null when the - // handler runs and this degrades to the previous broadcast behavior. - private readonly object _pendingLock = new object(); - private string _pendingClientId; - public ITechPasswordMessenger(string key, string messagePath, ITechPassword room) : base(key, messagePath, room as IKeyName) { @@ -37,12 +28,7 @@ namespace PepperDash.Essentials.AppServer.Messengers { var password = content.Value("password"); - lock (_pendingLock) - { - _pendingClientId = id; - _room.ValidateTechPassword(password); - _pendingClientId = null; - } + _room.ValidateTechPassword(password); }); AddAction("/setTechPassword", (id, content) => @@ -59,18 +45,12 @@ namespace PepperDash.Essentials.AppServer.Messengers _room.TechPasswordValidateResult += (sender, args) => { - string clientId; - lock (_pendingLock) - { - clientId = _pendingClientId; - } - var evt = new ITechPasswordEventMessage { IsValid = args.IsValid }; - PostEventMessage(evt, "passwordValidationResult", clientId); + PostEventMessage(evt, "passwordValidationResult"); }; } diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs index cd59db6e..eb3afec3 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs @@ -405,28 +405,5 @@ namespace PepperDash.Essentials.AppServer.Messengers }); } - /// - /// Helper for posting an event message to a single client. A null/empty clientId falls back - /// to the existing broadcast behavior of the other PostEventMessage overloads. - /// - /// - /// - /// Client id that will direct the message back to only that client - protected void PostEventMessage(DeviceEventMessageBase message, string eventType, string clientId) - { - message.Key = _device.Key; - - message.Name = _device.Name; - - message.EventType = eventType; - - AppServerController?.SendMessageObject(new MobileControlMessage - { - Type = $"/event{MessagePath}/{eventType}", - ClientId = clientId, - Content = JToken.FromObject(message), - }); - } - } }