diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs index 3ba9059e..13192ed5 100644 --- a/src/PepperDash.Core/Comm/GenericSshClient.cs +++ b/src/PepperDash.Core/Comm/GenericSshClient.cs @@ -146,31 +146,31 @@ namespace PepperDash.Core base(key) { StreamDebugging = new CommunicationStreamDebugging(key); - CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); - Key = key; - Hostname = hostname; - Port = port; - Username = username; - Password = password; - AutoReconnectIntervalMs = 5000; + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + Key = key; + Hostname = hostname; + Port = port; + Username = username; + Password = password; + AutoReconnectIntervalMs = 5000; ReconnectTimer = new CTimer(o => - { + { if (ConnectEnabled) { Connect(); } - }, System.Threading.Timeout.Infinite); - } + }, System.Threading.Timeout.Infinite); + } - /// - /// S+ Constructor - Must set all properties before calling Connect - /// - public GenericSshClient() - : base(SPlusKey) - { - CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); - AutoReconnectIntervalMs = 5000; + /// + /// S+ Constructor - Must set all properties before calling Connect + /// + public GenericSshClient() + : base(SPlusKey) + { + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + AutoReconnectIntervalMs = 5000; ReconnectTimer = new CTimer(o => { @@ -179,18 +179,18 @@ namespace PepperDash.Core Connect(); } }, System.Threading.Timeout.Infinite); - } + } - /// - /// Handles closing this up when the program shuts down - /// - void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType == eProgramStatusEventType.Stopping) - { - if (Client != null) - { - this.LogDebug("Program stopping. Closing connection"); + /// + /// Handles closing this up when the program shuts down + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType == eProgramStatusEventType.Stopping) + { + if (Client != null) + { + this.LogDebug("Program stopping. Closing connection"); Disconnect(); } } @@ -223,10 +223,7 @@ namespace PepperDash.Core this.LogDebug("Attempting connect"); // Cancel reconnect if running. - if (ReconnectTimer != null) - { - ReconnectTimer.Stop(); - } + ReconnectTimer?.Stop(); // Cleanup the old client if it already exists if (Client != null) @@ -268,20 +265,26 @@ namespace PepperDash.Core if (ie is SocketException) { - this.LogException(ie, "CONNECTION failure: Cannot reach host"); + this.LogError("CONNECTION failure: Cannot reach host"); + this.LogVerbose(ie, "Exception details: "); } if (ie is System.Net.Sockets.SocketException socketException) { - this.LogException(ie, "Connection failure: Cannot reach {host} on {port}", + this.LogError("Connection failure: Cannot reach {host} on {port}", Hostname, Port); + this.LogVerbose(socketException, "SocketException details: "); } if (ie is SshAuthenticationException) { - this.LogException(ie, "Authentication failure for username {userName}", Username); + this.LogError("Authentication failure for username {userName}", Username); + this.LogVerbose(ie, "AuthenticationException details: "); } else - this.LogException(ie, "Error on connect"); + { + this.LogError("Error on connect: {error}", ie.Message); + this.LogVerbose(ie, "Exception details: "); + } DisconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); @@ -291,7 +294,7 @@ namespace PepperDash.Core ReconnectTimer.Reset(AutoReconnectIntervalMs); } } - catch(SshOperationTimeoutException ex) + catch (SshOperationTimeoutException ex) { this.LogWarning("Connection attempt timed out: {message}", ex.Message); @@ -306,7 +309,8 @@ namespace PepperDash.Core catch (Exception e) { var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error; - this.LogException(e, "Unhandled exception on connect"); + this.LogError("Unhandled exception on connect: {error}", e.Message); + this.LogVerbose(e, "Exception details: "); DisconnectLogged = true; KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); if (AutoReconnect) @@ -323,18 +327,18 @@ namespace PepperDash.Core } } - /// - /// Disconnect method - /// - public void Disconnect() - { - ConnectEnabled = false; - // Stop trying reconnects, if we are - if (ReconnectTimer != null) - { - ReconnectTimer.Stop(); - // ReconnectTimer = null; - } + /// + /// Disconnect method + /// + public void Disconnect() + { + ConnectEnabled = false; + // Stop trying reconnects, if we are + if (ReconnectTimer != null) + { + ReconnectTimer.Stop(); + // ReconnectTimer = null; + } KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY); } @@ -360,7 +364,7 @@ namespace PepperDash.Core } catch (Exception ex) { - this.LogException(ex,"Exception in Kill Client"); + this.LogException(ex, "Exception in Kill Client"); } } @@ -368,7 +372,7 @@ namespace PepperDash.Core /// Kills the stream /// void KillStream() - { + { try { if (TheStream != null) @@ -384,59 +388,59 @@ namespace PepperDash.Core { this.LogException(ex, "Exception in Kill Stream:{0}"); } - } + } - /// - /// Handles the keyboard interactive authentication, should it be required. - /// - void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) - { - foreach (AuthenticationPrompt prompt in e.Prompts) - if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1) - prompt.Response = Password; - } - - /// - /// Handler for data receive on ShellStream. Passes data across to queue for line parsing. - /// - void Stream_DataReceived(object sender, ShellDataEventArgs e) - { + /// + /// Handles the keyboard interactive authentication, should it be required. + /// + void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) + { + foreach (AuthenticationPrompt prompt in e.Prompts) + if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1) + prompt.Response = Password; + } + + /// + /// Handler for data receive on ShellStream. Passes data across to queue for line parsing. + /// + void Stream_DataReceived(object sender, ShellDataEventArgs e) + { if (((ShellStream)sender).Length <= 0L) { return; } - var response = ((ShellStream)sender).Read(); - - var bytesHandler = BytesReceived; - - if (bytesHandler != null) - { + var response = ((ShellStream)sender).Read(); + + var bytesHandler = BytesReceived; + + if (bytesHandler != null) + { var bytes = Encoding.UTF8.GetBytes(response); - if (StreamDebugging.RxStreamDebuggingIsEnabled) - { - this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length); - } + if (StreamDebugging.RxStreamDebuggingIsEnabled) + { + this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length); + } bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); - } - - var textHandler = TextReceived; - if (textHandler != null) - { + } + + var textHandler = TextReceived; + if (textHandler != null) + { if (StreamDebugging.RxStreamDebuggingIsEnabled) this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response)); textHandler(this, new GenericCommMethodReceiveTextArgs(response)); } - - } + + } - /// - /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange - /// event - /// - void Client_ErrorOccurred(object sender, ExceptionEventArgs e) - { + /// + /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange + /// event + /// + void Client_ErrorOccurred(object sender, ExceptionEventArgs e) + { CrestronInvoke.BeginInvoke(o => { if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException) @@ -465,61 +469,54 @@ namespace PepperDash.Core /// void OnConnectionChange() { - if (ConnectionChange != null) - ConnectionChange(this, new GenericSocketStatusChageEventArgs(this)); + ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this)); } #region IBasicCommunication Members - /// - /// Sends text to the server - /// - /// - /// - /// SendText method - /// - public void SendText(string text) - { - try - { - if (Client != null && TheStream != null && IsConnected) - { - if (StreamDebugging.TxStreamDebuggingIsEnabled) - this.LogInformation( - "Sending {length} characters of text: '{text}'", - text.Length, - ComTextHelper.GetDebugText(text)); - - TheStream.Write(text); - TheStream.Flush(); - } - else - { - this.LogDebug("Client is null or disconnected. Cannot Send Text"); - } - } - catch (ObjectDisposedException) + /// + /// Sends text to the server + /// + /// The text to send + public void SendText(string text) + { + try { - this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); + if (Client != null && TheStream != null && IsConnected) + { + if (StreamDebugging.TxStreamDebuggingIsEnabled) + this.LogInformation( + "Sending {length} characters of text: '{text}'", + text.Length, + ComTextHelper.GetDebugText(text)); + + TheStream.Write(text); + TheStream.Flush(); + } + else + { + this.LogDebug("Client is null or disconnected. Cannot Send Text"); + } + } + catch (ObjectDisposedException) + { + this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); ReconnectTimer.Reset(); - } - catch (Exception ex) - { + } + catch (Exception ex) + { this.LogException(ex, "Exception sending text: '{message}'", text); - } - } + } + } /// /// Sends Bytes to the server /// - /// - /// - /// SendBytes method - /// - public void SendBytes(byte[] bytes) - { + /// The bytes to send + public void SendBytes(byte[] bytes) + { try { if (Client != null && TheStream != null && IsConnected) @@ -545,38 +542,38 @@ namespace PepperDash.Core catch (Exception ex) { this.LogException(ex, "Exception sending {message}", ComTextHelper.GetEscapedText(bytes)); - } - } - #endregion + } + } + #endregion -} + } -//***************************************************************************************************** -//***************************************************************************************************** -/// -/// Represents a SshConnectionChangeEventArgs -/// -public class SshConnectionChangeEventArgs : EventArgs - { + //***************************************************************************************************** + //***************************************************************************************************** + /// + /// Represents a SshConnectionChangeEventArgs + /// + public class SshConnectionChangeEventArgs : EventArgs + { /// /// Connection State /// public bool IsConnected { get; private set; } - /// - /// Gets or sets the UIsConnected - /// - public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } } + /// + /// Gets or sets the UIsConnected + /// + public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } } - /// - /// Gets or sets the Client - /// - public GenericSshClient Client { get; private set; } + /// + /// Gets or sets the Client + /// + public GenericSshClient Client { get; private set; } - /// - /// Gets or sets the Status - /// - public ushort Status { get { return Client.UStatus; } } + /// + /// Gets or sets the Status + /// + public ushort Status { get { return Client.UStatus; } } /// /// S+ Constructor