diff --git a/src/PepperDash.Core/ComTextHelper.cs b/src/PepperDash.Core/ComTextHelper.cs
new file mode 100644
index 00000000..28a76975
--- /dev/null
+++ b/src/PepperDash.Core/ComTextHelper.cs
@@ -0,0 +1,43 @@
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Helper class for formatting communication text and byte data for debugging purposes.
+ ///
+ public class ComTextHelper
+ {
+ ///
+ /// Gets escaped text for a byte array
+ ///
+ ///
+ /// string with all bytes escaped
+ public static string GetEscapedText(byte[] bytes)
+ {
+ return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets escaped text for a string
+ ///
+ ///
+ /// string with all bytes escaped
+ public static string GetEscapedText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets debug text for a string
+ ///
+ ///
+ /// string with all non-printable characters escaped
+ public static string GetDebugText(string text)
+ {
+ return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs b/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
index 33141f0c..780e65d0 100644
--- a/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
+++ b/src/PepperDash.Core/Comm/CommunicationStreamDebugging.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
@@ -37,14 +36,14 @@ namespace PepperDash.Core
{
get
{
- return _DebugTimeoutInMs/60000;
+ return _DebugTimeoutInMs / 60000;
}
}
///
/// Gets or sets the RxStreamDebuggingIsEnabled
///
- public bool RxStreamDebuggingIsEnabled{ get; private set; }
+ public bool RxStreamDebuggingIsEnabled { get; private set; }
///
/// Indicates that transmit stream debugging is enabled
@@ -108,7 +107,7 @@ namespace PepperDash.Core
TxStreamDebuggingIsEnabled = true;
Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
-
+
}
///
@@ -136,51 +135,4 @@ namespace PepperDash.Core
DebugExpiryPeriod = null;
}
}
-
- ///
- /// The available settings for stream debugging
- ///
- [Flags]
- ///
- /// Enumeration of eStreamDebuggingSetting values
- ///
- public enum eStreamDebuggingSetting
- {
- ///
- /// Debug off
- ///
- Off = 0,
- ///
- /// Debug received data
- ///
- Rx = 1,
- ///
- /// Debug transmitted data
- ///
- Tx = 2,
- ///
- /// Debug both received and transmitted data
- ///
- Both = Rx | Tx
- }
-
- ///
- /// The available settings for stream debugging response types
- ///
- [Flags]
- public enum eStreamDebuggingDataTypeSettings
- {
- ///
- /// Debug data in byte format
- ///
- Bytes = 0,
- ///
- /// Debug data in text format
- ///
- Text = 1,
- ///
- /// Debug data in both byte and text formats
- ///
- Both = Bytes | Text,
- }
}
diff --git a/src/PepperDash.Core/Comm/GenericSshClient.cs b/src/PepperDash.Core/Comm/GenericSshClient.cs
index 13192ed5..df44ab51 100644
--- a/src/PepperDash.Core/Comm/GenericSshClient.cs
+++ b/src/PepperDash.Core/Comm/GenericSshClient.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Text;
using System.Threading;
using Crestron.SimplSharp;
@@ -11,11 +12,12 @@ using Renci.SshNet.Common;
namespace PepperDash.Core
{
///
- ///
+ /// SSH Client
///
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{
private const string SPlusKey = "Uninitialized SshClient";
+
///
/// Object to enable stream debugging
///
@@ -36,11 +38,6 @@ namespace PepperDash.Core
///
public event EventHandler ConnectionChange;
- ///
- /////
- /////
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
-
///
/// Gets or sets the Hostname
///
@@ -67,7 +64,7 @@ namespace PepperDash.Core
public bool IsConnected
{
// returns false if no client or not connected
- get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ get { return client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
///
@@ -83,16 +80,26 @@ namespace PepperDash.Core
///
public SocketStatus ClientStatus
{
- get { return _ClientStatus; }
+ get { lock (_stateLock) { return _ClientStatus; } }
private set
{
- if (_ClientStatus == value)
- return;
- _ClientStatus = value;
- OnConnectionChange();
+ bool shouldFireEvent = false;
+ lock (_stateLock)
+ {
+ if (_ClientStatus != value)
+ {
+ _ClientStatus = value;
+ shouldFireEvent = true;
+ }
+ }
+ // Fire event outside lock to avoid deadlock
+ if (shouldFireEvent)
+ OnConnectionChange();
}
}
- SocketStatus _ClientStatus;
+
+ private SocketStatus _ClientStatus;
+ private bool _ConnectEnabled;
///
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
@@ -100,7 +107,7 @@ namespace PepperDash.Core
///
public ushort UStatus
{
- get { return (ushort)_ClientStatus; }
+ get { lock (_stateLock) { return (ushort)_ClientStatus; } }
}
///
@@ -111,7 +118,11 @@ namespace PepperDash.Core
///
/// Will be set and unset by connect and disconnect only
///
- public bool ConnectEnabled { get; private set; }
+ public bool ConnectEnabled
+ {
+ get { lock (_stateLock) { return _ConnectEnabled; } }
+ private set { lock (_stateLock) { _ConnectEnabled = value; } }
+ }
///
/// S+ helper for AutoReconnect
@@ -127,17 +138,25 @@ namespace PepperDash.Core
///
public int AutoReconnectIntervalMs { get; set; }
- SshClient Client;
+ private SshClient client;
- ShellStream TheStream;
+ private ShellStream shellStream;
- CTimer ReconnectTimer;
+ private readonly Timer reconnectTimer;
//Lock object to prevent simulatneous connect/disconnect operations
//private CCriticalSection connectLock = new CCriticalSection();
- private SemaphoreSlim connectLock = new SemaphoreSlim(1);
+ private readonly SemaphoreSlim connectLock = new SemaphoreSlim(1);
- private bool DisconnectLogged = false;
+ // Thread-safety lock for state changes
+ private readonly object _stateLock = new object();
+
+ private bool disconnectLogged = false;
+
+ ///
+ /// When true, turns off echo for the SSH session
+ ///
+ public bool DisableEcho { get; set; }
///
/// Typical constructor.
@@ -154,13 +173,13 @@ namespace PepperDash.Core
Password = password;
AutoReconnectIntervalMs = 5000;
- ReconnectTimer = new CTimer(o =>
+ reconnectTimer = new Timer(o =>
{
- if (ConnectEnabled)
+ if (ConnectEnabled) // Now thread-safe property access
{
Connect();
}
- }, System.Threading.Timeout.Infinite);
+ }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
///
@@ -172,23 +191,23 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
- ReconnectTimer = new CTimer(o =>
+ reconnectTimer = new Timer(o =>
{
- if (ConnectEnabled)
+ if (ConnectEnabled) // Now thread-safe property access
{
Connect();
}
- }, System.Threading.Timeout.Infinite);
+ }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
///
/// Handles closing this up when the program shuts down
///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
if (programEventType == eProgramStatusEventType.Stopping)
{
- if (Client != null)
+ if (client != null)
{
this.LogDebug("Program stopping. Closing connection");
Disconnect();
@@ -223,10 +242,10 @@ namespace PepperDash.Core
this.LogDebug("Attempting connect");
// Cancel reconnect if running.
- ReconnectTimer?.Stop();
+ StopReconnectTimer();
// Cleanup the old client if it already exists
- if (Client != null)
+ if (client != null)
{
this.LogDebug("Cleaning up disconnected client");
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
@@ -239,29 +258,36 @@ namespace PepperDash.Core
this.LogDebug("Creating new SshClient");
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
- Client = new SshClient(connectionInfo);
- Client.ErrorOccurred += Client_ErrorOccurred;
+ client = new SshClient(connectionInfo);
+ client.ErrorOccurred += Client_ErrorOccurred;
//Attempt to connect
ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
try
{
- Client.Connect();
- TheStream = Client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534);
- if (TheStream.DataAvailable)
+ client.Connect();
+
+ var modes = new Dictionary();
+
+ if (DisableEcho)
+ {
+ modes.Add(TerminalModes.ECHO, 0);
+ }
+
+ shellStream = client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534, modes);
+ if (shellStream.DataAvailable)
{
// empty the buffer if there is data
- string str = TheStream.Read();
+ shellStream.Read();
}
- TheStream.DataReceived += Stream_DataReceived;
+ shellStream.DataReceived += Stream_DataReceived;
this.LogInformation("Connected");
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
- DisconnectLogged = false;
+ disconnectLogged = false;
}
catch (SshConnectionException e)
{
var ie = e.InnerException; // The details are inside!!
- var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
if (ie is SocketException)
{
@@ -286,37 +312,36 @@ namespace PepperDash.Core
this.LogVerbose(ie, "Exception details: ");
}
- DisconnectLogged = true;
+ disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ StartReconnectTimer();
}
}
catch (SshOperationTimeoutException ex)
{
this.LogWarning("Connection attempt timed out: {message}", ex.Message);
- DisconnectLogged = true;
+ disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ StartReconnectTimer();
}
}
catch (Exception e)
{
- var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
this.LogError("Unhandled exception on connect: {error}", e.Message);
this.LogVerbose(e, "Exception details: ");
- DisconnectLogged = true;
+ disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ StartReconnectTimer();
}
}
}
@@ -334,11 +359,7 @@ namespace PepperDash.Core
{
ConnectEnabled = false;
// Stop trying reconnects, if we are
- if (ReconnectTimer != null)
- {
- ReconnectTimer.Stop();
- // ReconnectTimer = null;
- }
+ StopReconnectTimer();
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
}
@@ -352,12 +373,12 @@ namespace PepperDash.Core
try
{
- if (Client != null)
+ if (client != null)
{
- Client.ErrorOccurred -= Client_ErrorOccurred;
- Client.Disconnect();
- Client.Dispose();
- Client = null;
+ client.ErrorOccurred -= Client_ErrorOccurred;
+ client.Disconnect();
+ client.Dispose();
+ client = null;
ClientStatus = status;
this.LogDebug("Disconnected");
}
@@ -371,16 +392,16 @@ namespace PepperDash.Core
///
/// Kills the stream
///
- void KillStream()
+ private void KillStream()
{
try
{
- if (TheStream != null)
+ if (shellStream != null)
{
- TheStream.DataReceived -= Stream_DataReceived;
- TheStream.Close();
- TheStream.Dispose();
- TheStream = null;
+ shellStream.DataReceived -= Stream_DataReceived;
+ shellStream.Close();
+ shellStream.Dispose();
+ shellStream = null;
this.LogDebug("Disconnected stream");
}
}
@@ -393,7 +414,7 @@ namespace PepperDash.Core
///
/// Handles the keyboard interactive authentication, should it be required.
///
- void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
+ private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
foreach (AuthenticationPrompt prompt in e.Prompts)
if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
@@ -403,7 +424,7 @@ namespace PepperDash.Core
///
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
///
- void Stream_DataReceived(object sender, ShellDataEventArgs e)
+ private void Stream_DataReceived(object sender, ShellDataEventArgs e)
{
if (((ShellStream)sender).Length <= 0L)
{
@@ -416,18 +437,14 @@ namespace PepperDash.Core
if (bytesHandler != null)
{
var bytes = Encoding.UTF8.GetBytes(response);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
+ this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
+ this.PrintReceivedText(response);
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
}
@@ -439,7 +456,7 @@ namespace PepperDash.Core
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
/// event
///
- void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
+ private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
{
CrestronInvoke.BeginInvoke(o =>
{
@@ -459,7 +476,7 @@ namespace PepperDash.Core
if (AutoReconnect && ConnectEnabled)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ StartReconnectTimer();
}
});
}
@@ -467,7 +484,7 @@ namespace PepperDash.Core
///
/// Helper for ConnectionChange event
///
- void OnConnectionChange()
+ private void OnConnectionChange()
{
ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
}
@@ -482,16 +499,12 @@ namespace PepperDash.Core
{
try
{
- if (Client != null && TheStream != null && IsConnected)
+ if (client != null && shellStream != null && IsConnected)
{
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation(
- "Sending {length} characters of text: '{text}'",
- text.Length,
- ComTextHelper.GetDebugText(text));
+ this.PrintSentText(text);
- TheStream.Write(text);
- TheStream.Flush();
+ shellStream.Write(text);
+ shellStream.Flush();
}
else
{
@@ -503,7 +516,7 @@ namespace PepperDash.Core
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Reset();
+ StartReconnectTimer();
}
catch (Exception ex)
{
@@ -519,13 +532,12 @@ namespace PepperDash.Core
{
try
{
- if (Client != null && TheStream != null && IsConnected)
+ if (client != null && shellStream != null && IsConnected)
{
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ this.PrintSentBytes(bytes);
- TheStream.Write(bytes, 0, bytes.Length);
- TheStream.Flush();
+ shellStream.Write(bytes, 0, bytes.Length);
+ shellStream.Flush();
}
else
{
@@ -537,7 +549,7 @@ namespace PepperDash.Core
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Reset();
+ StartReconnectTimer();
}
catch (Exception ex)
{
@@ -546,6 +558,83 @@ namespace PepperDash.Core
}
#endregion
+ ///
+ /// Safely starts the reconnect timer with exception handling
+ ///
+ private void StartReconnectTimer()
+ {
+ try
+ {
+ reconnectTimer?.Change(AutoReconnectIntervalMs, System.Threading.Timeout.Infinite);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Timer was disposed, ignore
+ this.LogDebug("Attempted to start timer but it was already disposed");
+ }
+ }
+
+ ///
+ /// Safely stops the reconnect timer with exception handling
+ ///
+ private void StopReconnectTimer()
+ {
+ try
+ {
+ reconnectTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
+ }
+ catch (ObjectDisposedException)
+ {
+ // Timer was disposed, ignore
+ this.LogDebug("Attempted to stop timer but it was already disposed");
+ }
+ }
+
+ ///
+ /// Deactivate method - properly dispose of resources
+ ///
+ public override bool Deactivate()
+ {
+ try
+ {
+ this.LogDebug("Deactivating SSH client - disposing resources");
+
+ // Stop trying reconnects
+ ConnectEnabled = false;
+ StopReconnectTimer();
+
+ // Disconnect and cleanup client
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+
+ // Dispose timer
+ try
+ {
+ reconnectTimer?.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed, ignore
+ }
+
+ // Dispose semaphore
+ try
+ {
+ connectLock?.Dispose();
+ }
+ catch (ObjectDisposedException)
+ {
+ // Already disposed, ignore
+ }
+
+ return base.Deactivate();
+ }
+ catch (Exception ex)
+ {
+ this.LogException(ex, "Error during SSH client deactivation");
+ return false;
+ }
+ }
+
}
//*****************************************************************************************************
diff --git a/src/PepperDash.Core/Comm/GenericTcpIpClient.cs b/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
index c9235411..d13eed3f 100644
--- a/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
+++ b/src/PepperDash.Core/Comm/GenericTcpIpClient.cs
@@ -19,44 +19,44 @@ namespace PepperDash.Core
///
public CommunicationStreamDebugging StreamDebugging { get; private set; }
- ///
- /// Fires when data is received from the server and returns it as a Byte array
- ///
- public event EventHandler BytesReceived;
+ ///
+ /// Fires when data is received from the server and returns it as a Byte array
+ ///
+ public event EventHandler BytesReceived;
- ///
- /// Fires when data is received from the server and returns it as text
- ///
- public event EventHandler TextReceived;
+ ///
+ /// Fires when data is received from the server and returns it as text
+ ///
+ public event EventHandler TextReceived;
- ///
- ///
- ///
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
- public event EventHandler ConnectionChange;
+ ///
+ ///
+ ///
+ //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
+ public event EventHandler ConnectionChange;
- private string _hostname;
+ private string _hostname;
///
/// Address of server
///
public string Hostname
{
- get
- {
- return _hostname;
- }
+ get
+ {
+ return _hostname;
+ }
- set
- {
- _hostname = value;
- if (_client != null)
- {
- _client.AddressClientConnectedTo = _hostname;
- }
- }
- }
+ set
+ {
+ _hostname = value;
+ if (_client != null)
+ {
+ _client.AddressClientConnectedTo = _hostname;
+ }
+ }
+ }
///
/// Gets or sets the Port
@@ -78,19 +78,19 @@ namespace PepperDash.Core
///
public int BufferSize { get; set; }
- ///
- /// The actual client class
- ///
- private TCPClient _client;
+ ///
+ /// The actual client class
+ ///
+ private TCPClient _client;
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
-
+
///
/// S+ helper for IsConnected
///
@@ -99,15 +99,15 @@ namespace PepperDash.Core
get { return (ushort)(IsConnected ? 1 : 0); }
}
- ///
- /// _client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
+ ///
+ /// _client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
{
- return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
- }
+ return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
+ }
}
///
@@ -119,26 +119,26 @@ namespace PepperDash.Core
get { return (ushort)ClientStatus; }
}
- ///
+ ///
/// Status text shows the message associated with socket status
- ///
- public string ClientStatusText { get { return ClientStatus.ToString(); } }
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
- ///
- /// Ushort representation of client status
- ///
+ ///
+ /// Ushort representation of client status
+ ///
[Obsolete]
- public ushort UClientStatus { get { return (ushort)ClientStatus; } }
+ public ushort UClientStatus { get { return (ushort)ClientStatus; } }
- ///
- /// Connection failure reason
- ///
- public string ConnectionFailure { get { return ClientStatus.ToString(); } }
+ ///
+ /// Connection failure reason
+ ///
+ public string ConnectionFailure { get { return ClientStatus.ToString(); } }
- ///
- /// Gets or sets the AutoReconnect
- ///
- public bool AutoReconnect { get; set; }
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
///
/// S+ helper for AutoReconnect
@@ -149,29 +149,29 @@ namespace PepperDash.Core
set { AutoReconnect = value == 1; }
}
- ///
- /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
- ///
- public int AutoReconnectIntervalMs { get; set; }
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
- ///
- /// Set only when the disconnect method is called
- ///
- bool DisconnectCalledByUser;
+ ///
+ /// Set only when the disconnect method is called
+ ///
+ bool DisconnectCalledByUser;
- ///
- ///
- ///
- public bool Connected
- {
- get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
+ ///
+ ///
+ ///
+ public bool Connected
+ {
+ get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
//Lock object to prevent simulatneous connect/disconnect operations
private CCriticalSection connectLock = new CCriticalSection();
// private Timer for auto reconnect
- private CTimer RetryTimer;
+ private CTimer RetryTimer;
///
/// Constructor
@@ -181,8 +181,8 @@ namespace PepperDash.Core
///
///
public GenericTcpIpClient(string key, string address, int port, int bufferSize)
- : base(key)
- {
+ : base(key)
+ {
StreamDebugging = new CommunicationStreamDebugging(key);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
@@ -218,18 +218,18 @@ namespace PepperDash.Core
/// Default constructor for S+
///
public GenericTcpIpClient()
- : base(SplusKey)
+ : base(SplusKey)
{
StreamDebugging = new CommunicationStreamDebugging(SplusKey);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
BufferSize = 2000;
RetryTimer = new CTimer(o =>
{
Reconnect();
}, Timeout.Infinite);
- }
+ }
///
/// Initialize method
@@ -255,26 +255,26 @@ namespace PepperDash.Core
///
///
///
- ///
- /// Deactivate method
- ///
- public override bool Deactivate()
- {
+ ///
+ /// Deactivate method
+ ///
+ public override bool Deactivate()
+ {
RetryTimer.Stop();
RetryTimer.Dispose();
if (_client != null)
{
- _client.SocketStatusChange -= this.Client_SocketStatusChange;
+ _client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient();
}
- return true;
- }
+ return true;
+ }
- ///
- /// Connect method
- ///
- public void Connect()
- {
+ ///
+ /// Connect method
+ ///
+ public void Connect()
+ {
if (string.IsNullOrEmpty(Hostname))
{
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
@@ -310,7 +310,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
- }
+ }
private void Reconnect()
{
@@ -337,11 +337,11 @@ namespace PepperDash.Core
}
}
- ///
- /// Disconnect method
- ///
- public void Disconnect()
- {
+ ///
+ /// Disconnect method
+ ///
+ public void Disconnect()
+ {
try
{
connectLock.Enter();
@@ -355,7 +355,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
- }
+ }
///
/// DisconnectClient method
@@ -375,7 +375,7 @@ namespace PepperDash.Core
///
///
void ConnectToServerCallback(TCPClient c)
- {
+ {
if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
@@ -385,13 +385,13 @@ namespace PepperDash.Core
{
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
}
- }
+ }
///
/// Disconnects, waits and attemtps to connect again
///
void WaitAndTryReconnect()
- {
+ {
CrestronInvoke.BeginInvoke(o =>
{
try
@@ -409,7 +409,7 @@ namespace PepperDash.Core
connectLock.Leave();
}
});
- }
+ }
///
/// Recieves incoming data
@@ -417,7 +417,7 @@ namespace PepperDash.Core
///
///
void Receive(TCPClient client, int numBytes)
- {
+ {
if (client != null)
{
if (numBytes > 0)
@@ -426,10 +426,7 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
+ this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
@@ -437,58 +434,53 @@ namespace PepperDash.Core
{
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
- }
+ this.PrintReceivedText(str);
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
+ }
}
client.ReceiveDataAsync(Receive);
}
- }
+ }
- ///
- /// SendText method
- ///
- public void SendText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- // Check debug level before processing byte array
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
+ ///
+ /// SendText method
+ ///
+ public void SendText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ // Check debug level before processing byte array
+ this.PrintSentText(text);
if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
+ _client.SendData(bytes, bytes.Length);
+ }
- ///
- /// SendEscapedText method
- ///
- public void SendEscapedText(string text)
- {
- var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
- {
- var hex = s.Groups[1].Value;
- return ((char)Convert.ToByte(hex, 16)).ToString();
- });
- SendText(unescapedText);
- }
+ ///
+ /// SendEscapedText method
+ ///
+ public void SendEscapedText(string text)
+ {
+ var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
+ {
+ var hex = s.Groups[1].Value;
+ return ((char)Convert.ToByte(hex, 16)).ToString();
+ });
+ SendText(unescapedText);
+ }
///
/// Sends Bytes to the server
///
///
- ///
- /// SendBytes method
- ///
- public void SendBytes(byte[] bytes)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ ///
+ /// SendBytes method
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ this.PrintSentBytes(bytes);
if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
+ _client.SendData(bytes, bytes.Length);
+ }
///
/// Socket Status Change Handler
@@ -496,7 +488,7 @@ namespace PepperDash.Core
///
///
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
- {
+ {
if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
@@ -505,68 +497,73 @@ namespace PepperDash.Core
else
{
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
- _client.ReceiveDataAsync(Receive);
+ _client.ReceiveDataAsync(Receive);
}
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
- }
- }
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ }
+ }
- ///
- /// Represents a TcpSshPropertiesConfig
- ///
- public class TcpSshPropertiesConfig
- {
+ ///
+ /// Represents a TcpSshPropertiesConfig
+ ///
+ public class TcpSshPropertiesConfig
+ {
///
/// Address to connect to
///
[JsonProperty(Required = Required.Always)]
- public string Address { get; set; }
-
+ public string Address { get; set; }
+
///
/// Port to connect to
///
- [JsonProperty(Required = Required.Always)]
- public int Port { get; set; }
-
+ [JsonProperty(Required = Required.Always)]
+ public int Port { get; set; }
+
///
/// Username credential
///
- public string Username { get; set; }
- ///
- /// Gets or sets the Password
- ///
- public string Password { get; set; }
+ public string Username { get; set; }
+ ///
+ /// Gets or sets the Password
+ ///
+ public string Password { get; set; }
- ///
- /// Defaults to 32768
- ///
- public int BufferSize { get; set; }
+ ///
+ /// Defaults to 32768
+ ///
+ public int BufferSize { get; set; }
- ///
- /// Gets or sets the AutoReconnect
- ///
- public bool AutoReconnect { get; set; }
+ ///
+ /// Gets or sets the AutoReconnect
+ ///
+ public bool AutoReconnect { get; set; }
- ///
- /// Gets or sets the AutoReconnectIntervalMs
- ///
- public int AutoReconnectIntervalMs { get; set; }
+ ///
+ /// Gets or sets the AutoReconnectIntervalMs
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// When true, turns off echo for the SSH session
+ ///
+ [JsonProperty("disableSshEcho")]
+ public bool DisableSshEcho { get; set; }
///
/// Default constructor
///
public TcpSshPropertiesConfig()
- {
- BufferSize = 32768;
- AutoReconnect = true;
- AutoReconnectIntervalMs = 5000;
+ {
+ BufferSize = 32768;
+ AutoReconnect = true;
+ AutoReconnectIntervalMs = 5000;
Username = "";
Password = "";
- }
-
- }
-
+ DisableSshEcho = false;
+ }
+ }
}
diff --git a/src/PepperDash.Core/Comm/GenericUdpServer.cs b/src/PepperDash.Core/Comm/GenericUdpServer.cs
index 52ac627a..a713872a 100644
--- a/src/PepperDash.Core/Comm/GenericUdpServer.cs
+++ b/src/PepperDash.Core/Comm/GenericUdpServer.cs
@@ -124,7 +124,7 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
}
-
+
///
///
///
@@ -135,7 +135,7 @@ namespace PepperDash.Core
public GenericUdpServer(string key, string address, int port, int bufferSize)
: base(key)
{
- StreamDebugging = new CommunicationStreamDebugging(key);
+ StreamDebugging = new CommunicationStreamDebugging(key);
Hostname = address;
Port = port;
BufferSize = bufferSize;
@@ -180,7 +180,7 @@ namespace PepperDash.Core
///
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
- if (programEventType != eProgramStatusEventType.Stopping)
+ if (programEventType != eProgramStatusEventType.Stopping)
return;
Debug.Console(1, this, "Program stopping. Disabling Server");
@@ -243,7 +243,7 @@ namespace PepperDash.Core
///
public void Disconnect()
{
- if(Server != null)
+ if (Server != null)
Server.DisableUDPServer();
IsConnected = false;
@@ -265,7 +265,7 @@ namespace PepperDash.Core
try
{
- if (numBytes <= 0)
+ if (numBytes <= 0)
return;
var sourceIp = Server.IPAddressLastMessageReceivedFrom;
@@ -281,17 +281,13 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
+ this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
+ this.PrintReceivedText(str);
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
}
}
@@ -318,8 +314,7 @@ namespace PepperDash.Core
if (IsConnected && Server != null)
{
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
+ this.PrintSentText(text);
Server.SendData(bytes, bytes.Length);
}
@@ -334,8 +329,7 @@ namespace PepperDash.Core
///
public void SendBytes(byte[] bytes)
{
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ this.PrintSentBytes(bytes);
if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length);
@@ -343,11 +337,11 @@ namespace PepperDash.Core
}
- ///
- /// Represents a GenericUdpReceiveTextExtraArgs
- ///
- public class GenericUdpReceiveTextExtraArgs : EventArgs
- {
+ ///
+ /// Represents a GenericUdpReceiveTextExtraArgs
+ ///
+ public class GenericUdpReceiveTextExtraArgs : EventArgs
+ {
///
///
///
@@ -359,7 +353,7 @@ namespace PepperDash.Core
///
///
///
- public int Port { get; private set; }
+ public int Port { get; private set; }
///
///
///
@@ -373,18 +367,18 @@ namespace PepperDash.Core
///
///
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
- {
- Text = text;
- IpAddress = ipAddress;
- Port = port;
- Bytes = bytes;
- }
+ {
+ Text = text;
+ IpAddress = ipAddress;
+ Port = port;
+ Bytes = bytes;
+ }
- ///
- /// Stupid S+ Constructor
- ///
- public GenericUdpReceiveTextExtraArgs() { }
- }
+ ///
+ /// Stupid S+ Constructor
+ ///
+ public GenericUdpReceiveTextExtraArgs() { }
+ }
///
///
diff --git a/src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs b/src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs
new file mode 100644
index 00000000..9fd7544d
--- /dev/null
+++ b/src/PepperDash.Core/Comm/StreamDebuggingExtensions.cs
@@ -0,0 +1,69 @@
+using System;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Extension methods for stream debugging
+ ///
+ public static class StreamDebuggingExtensions
+ {
+ private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $"App {InitialParametersClass.ApplicationNumber}" : $"{InitialParametersClass.RoomId}";
+
+ ///
+ /// Print the sent bytes to the console
+ ///
+ /// comms device
+ /// bytes to print
+ public static void PrintSentBytes(this IStreamDebugging comms, byte[] bytes)
+ {
+ if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
+
+ var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
+
+ CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
+ }
+
+ ///
+ /// Print the received bytes to the console
+ ///
+ /// comms device
+ /// bytes to print
+ public static void PrintReceivedBytes(this IStreamDebugging comms, byte[] bytes)
+ {
+ if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
+
+ var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
+
+ CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
+ }
+
+ ///
+ /// Print the sent text to the console
+ ///
+ /// comms device
+ /// text to print
+ public static void PrintSentText(this IStreamDebugging comms, string text)
+ {
+ if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
+
+ var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
+
+ CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending Text: '{ComTextHelper.GetDebugText(text)}'");
+ }
+
+ ///
+ /// Print the received text to the console
+ ///
+ /// comms device
+ /// text to print
+ public static void PrintReceivedText(this IStreamDebugging comms, string text)
+ {
+ if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
+
+ var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
+
+ CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received Text: '{ComTextHelper.GetDebugText(text)}'");
+ }
+ }
+}
diff --git a/src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs b/src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs
new file mode 100644
index 00000000..f5fbfa2b
--- /dev/null
+++ b/src/PepperDash.Core/Comm/eStreamDebuggingDataTypeSettings.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace PepperDash.Core
+{
+ ///
+ /// The available settings for stream debugging data format types
+ ///
+ [Flags]
+ public enum eStreamDebuggingDataTypeSettings
+ {
+ ///
+ /// Debug data in byte format
+ ///
+ Bytes = 0,
+ ///
+ /// Debug data in text format
+ ///
+ Text = 1,
+ ///
+ /// Debug data in both byte and text formats
+ ///
+ Both = Bytes | Text
+ }
+}
diff --git a/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs b/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
new file mode 100644
index 00000000..f9f7eb3f
--- /dev/null
+++ b/src/PepperDash.Core/Comm/eStreamDebuggingSetting.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace PepperDash.Core
+{
+ ///
+ /// The available settings for stream debugging
+ ///
+ [Flags]
+ public enum eStreamDebuggingSetting
+ {
+ ///
+ /// Debug off
+ ///
+ Off = 0,
+ ///
+ /// Debug received data
+ ///
+ Rx = 1,
+ ///
+ /// Debug transmitted data
+ ///
+ Tx = 2,
+ ///
+ /// Debug both received and transmitted data
+ ///
+ Both = Rx | Tx
+ }
+}
diff --git a/src/PepperDash.Core/CommunicationExtras.cs b/src/PepperDash.Core/CommunicationExtras.cs
index d16ea761..a8bc57d3 100644
--- a/src/PepperDash.Core/CommunicationExtras.cs
+++ b/src/PepperDash.Core/CommunicationExtras.cs
@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
-using System.Linq;
-using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets;
-using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace PepperDash.Core
@@ -42,7 +39,7 @@ namespace PepperDash.Core
/// Defines the contract for IBasicCommunication
///
public interface IBasicCommunication : ICommunicationReceiver
- {
+ {
///
/// Send text to the device
///
@@ -54,7 +51,7 @@ namespace PepperDash.Core
///
///
void SendBytes(byte[] bytes);
- }
+ }
///
/// Represents a device that implements IBasicCommunication and IStreamDebugging
@@ -67,7 +64,7 @@ namespace PepperDash.Core
///
/// Represents a device with stream debugging capablities
///
- public interface IStreamDebugging
+ public interface IStreamDebugging : IKeyed
{
///
/// Object to enable stream debugging
@@ -76,12 +73,12 @@ namespace PepperDash.Core
CommunicationStreamDebugging StreamDebugging { get; }
}
- ///
- /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
- /// GenericTcpIpClient
- ///
- public interface ISocketStatus : IBasicCommunication
- {
+ ///
+ /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
+ /// GenericTcpIpClient
+ ///
+ public interface ISocketStatus : IBasicCommunication
+ {
///
/// Notifies of socket status changes
///
@@ -93,7 +90,7 @@ namespace PepperDash.Core
[JsonProperty("clientStatus")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
SocketStatus ClientStatus { get; }
- }
+ }
///
/// Describes a device that implements ISocketStatus and IStreamDebugging
@@ -107,24 +104,24 @@ namespace PepperDash.Core
/// Describes a device that can automatically attempt to reconnect
///
public interface IAutoReconnect
- {
+ {
///
/// Enable automatic recconnect
///
[JsonProperty("autoReconnect")]
- bool AutoReconnect { get; set; }
+ bool AutoReconnect { get; set; }
///
/// Interval in ms to attempt automatic recconnections
///
[JsonProperty("autoReconnectIntervalMs")]
- int AutoReconnectIntervalMs { get; set; }
- }
+ int AutoReconnectIntervalMs { get; set; }
+ }
- ///
- ///
- ///
- public enum eGenericCommMethodStatusChangeType
- {
+ ///
+ ///
+ ///
+ public enum eGenericCommMethodStatusChangeType
+ {
///
/// Connected
///
@@ -133,45 +130,45 @@ namespace PepperDash.Core
/// Disconnected
///
Disconnected
- }
+ }
- ///
- /// This delegate defines handler for IBasicCommunication status changes
- ///
- /// Device firing the status change
- ///
- public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
+ ///
+ /// This delegate defines handler for IBasicCommunication status changes
+ ///
+ /// Device firing the status change
+ ///
+ public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
- ///
- ///
- ///
- public class GenericCommMethodReceiveBytesArgs : EventArgs
- {
- ///
- /// Gets or sets the Bytes
- ///
- public byte[] Bytes { get; private set; }
+ ///
+ ///
+ ///
+ public class GenericCommMethodReceiveBytesArgs : EventArgs
+ {
+ ///
+ /// Gets or sets the Bytes
+ ///
+ public byte[] Bytes { get; private set; }
///
///
///
///
public GenericCommMethodReceiveBytesArgs(byte[] bytes)
- {
- Bytes = bytes;
- }
+ {
+ Bytes = bytes;
+ }
- ///
- /// S+ Constructor
- ///
- public GenericCommMethodReceiveBytesArgs() { }
- }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericCommMethodReceiveBytesArgs() { }
+ }
- ///
- ///
- ///
- public class GenericCommMethodReceiveTextArgs : EventArgs
- {
+ ///
+ ///
+ ///
+ public class GenericCommMethodReceiveTextArgs : EventArgs
+ {
///
///
///
@@ -185,9 +182,9 @@ namespace PepperDash.Core
///
///
public GenericCommMethodReceiveTextArgs(string text)
- {
- Text = text;
- }
+ {
+ Text = text;
+ }
///
///
@@ -195,59 +192,14 @@ namespace PepperDash.Core
///
///
public GenericCommMethodReceiveTextArgs(string text, string delimiter)
- :this(text)
+ : this(text)
{
Delimiter = delimiter;
}
- ///
- /// S+ Constructor
- ///
- public GenericCommMethodReceiveTextArgs() { }
- }
-
-
-
- ///
- ///
- ///
- public class ComTextHelper
- {
///
- /// Gets escaped text for a byte array
+ /// S+ Constructor
///
- ///
- ///
- public static string GetEscapedText(byte[] bytes)
- {
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets escaped text for a string
- ///
- ///
- ///
- ///
- /// GetEscapedText method
- ///
- public static string GetEscapedText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets debug text for a string
- ///
- ///
- ///
- ///
- /// GetDebugText method
- ///
- public static string GetDebugText(string text)
- {
- return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
- }
- }
+ public GenericCommMethodReceiveTextArgs() { }
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Core/Logging/Debug.cs b/src/PepperDash.Core/Logging/Debug.cs
index 62a95f2c..877d8997 100644
--- a/src/PepperDash.Core/Logging/Debug.cs
+++ b/src/PepperDash.Core/Logging/Debug.cs
@@ -168,7 +168,7 @@ namespace PepperDash.Core
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug,
- retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
+ retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,
levelSwitch: _fileLogLevelSwitch
);
@@ -1081,9 +1081,6 @@ namespace PepperDash.Core
/// Logs to Console when at-level, and all messages to error log
///
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
- ///
- /// Console method
- ///
public static void Console(uint level, ErrorLogLevel errorLogLevel,
string format, params object[] items)
{
@@ -1096,9 +1093,6 @@ namespace PepperDash.Core
/// it will only be written to the log.
///
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
- ///
- /// ConsoleWithLog method
- ///
public static void ConsoleWithLog(uint level, string format, params object[] items)
{
LogMessage(level, format, items);
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
index 7dacce5c..2dd4f2ba 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CecPortController.cs
@@ -12,15 +12,15 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core
{
- ///
- /// Represents a CecPortController
- ///
- public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
+ ///
+ /// Represents a CecPortController
+ ///
+ public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
{
- ///
- /// Gets or sets the StreamDebugging
- ///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
+ ///
+ /// Gets or sets the StreamDebugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
public event EventHandler BytesReceived;
public event EventHandler TextReceived;
@@ -33,16 +33,16 @@ namespace PepperDash.Essentials.Core
ICec Port;
public CecPortController(string key, Func postActivationFunc,
- EssentialsControlPropertiesConfig config):base(key)
+ EssentialsControlPropertiesConfig config) : base(key)
{
- StreamDebugging = new CommunicationStreamDebugging(key);
+ StreamDebugging = new CommunicationStreamDebugging(key);
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
Port.StreamCec.CecChange += StreamCec_CecChange;
- });
+ });
}
public CecPortController(string key, ICec port)
@@ -58,27 +58,25 @@ namespace PepperDash.Essentials.Core
if (args.EventId == CecEventIds.CecMessageReceivedEventId)
OnDataReceived(cecDevice.Received.StringValue);
else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
- if(cecDevice.ErrorFeedback.BoolValue)
+ if (cecDevice.ErrorFeedback.BoolValue)
Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
}
void OnDataReceived(string s)
{
- var bytesHandler = BytesReceived;
+ var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
+ this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
- if (textHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
- textHandler(this, new GenericCommMethodReceiveTextArgs(s));
- }
+ if (textHandler != null)
+ {
+ this.PrintReceivedText(s);
+ textHandler(this, new GenericCommMethodReceiveTextArgs(s));
+ }
}
#region IBasicCommunication Members
@@ -90,8 +88,7 @@ namespace PepperDash.Essentials.Core
{
if (Port == null)
return;
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
+ this.PrintSentText(text);
Port.StreamCec.Send.StringValue = text;
}
@@ -103,8 +100,8 @@ namespace PepperDash.Essentials.Core
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ this.PrintSentBytes(bytes);
+ Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.StreamCec.Send.StringValue = text;
}
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs b/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
index ba599ead..d3c46196 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/ComPortController.cs
@@ -23,10 +23,10 @@ namespace PepperDash.Essentials.Core
/// Event fired when bytes are received
///
public event EventHandler BytesReceived;
-
+
///
- /// Event fired when text is received
- ///
+ /// Event fired when text is received
+ ///
public event EventHandler TextReceived;
///
@@ -38,12 +38,12 @@ namespace PepperDash.Essentials.Core
ComPort.ComPortSpec Spec;
///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
public ComPortController(string key, Func postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
@@ -100,7 +100,7 @@ namespace PepperDash.Essentials.Core
return; // false
}
}
-
+
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
@@ -165,16 +165,14 @@ namespace PepperDash.Essentials.Core
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
+ this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
}
var textHandler = TextReceived;
if (textHandler != null)
{
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
+ this.PrintReceivedText(s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
}
@@ -201,8 +199,7 @@ namespace PepperDash.Essentials.Core
if (Port == null)
return;
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
+ this.PrintSentText(text);
Port.Send(text);
}
@@ -214,8 +211,7 @@ namespace PepperDash.Essentials.Core
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ this.PrintSentBytes(bytes);
Port.Send(text);
}
diff --git a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
index a0723c4b..fbc46579 100644
--- a/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
+++ b/src/PepperDash.Essentials.Core/Comm and IR/CommFactory.cs
@@ -64,8 +64,11 @@ namespace PepperDash.Essentials.Core
break;
case eControlMethod.Ssh:
{
- var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
- ssh.AutoReconnect = c.AutoReconnect;
+ var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password)
+ {
+ AutoReconnect = c.AutoReconnect,
+ DisableEcho = c.DisableSshEcho
+ };
if (ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
@@ -73,8 +76,10 @@ namespace PepperDash.Essentials.Core
}
case eControlMethod.Tcpip:
{
- var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
- tcp.AutoReconnect = c.AutoReconnect;
+ var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize)
+ {
+ AutoReconnect = c.AutoReconnect
+ };
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
@@ -90,8 +95,10 @@ namespace PepperDash.Essentials.Core
break;
case eControlMethod.SecureTcpIp:
{
- var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize);
- secureTcp.AutoReconnect = c.AutoReconnect;
+ var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize)
+ {
+ AutoReconnect = c.AutoReconnect
+ };
if (secureTcp.AutoReconnect)
secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = secureTcp;
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/SetDeviceStreamDebugRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/SetDeviceStreamDebugRequestHandler.cs
index 72758b0e..7c6aea26 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/SetDeviceStreamDebugRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/SetDeviceStreamDebugRequestHandler.cs
@@ -6,9 +6,9 @@ using PepperDash.Core.Web.RequestHandlers;
namespace PepperDash.Essentials.Core.Web.RequestHandlers
{
- ///
- /// Represents a SetDeviceStreamDebugRequestHandler
- ///
+ ///
+ /// Represents a SetDeviceStreamDebugRequestHandler
+ ///
public class SetDeviceStreamDebugRequestHandler : WebApiBaseRequestHandler
{
///
@@ -122,23 +122,23 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
- if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))
- {
- context.Response.StatusCode = 404;
- context.Response.StatusDescription = "Not Found";
- context.Response.End();
+ if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))
+ {
+ context.Response.StatusCode = 404;
+ context.Response.StatusDescription = "Not Found";
+ context.Response.End();
- return;
- }
+ return;
+ }
- eStreamDebuggingSetting debugSetting;
+ eStreamDebuggingSetting debugSetting;
try
{
- debugSetting = (eStreamDebuggingSetting) Enum.Parse(typeof (eStreamDebuggingSetting), body.Setting, true);
+ debugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), body.Setting, true);
}
catch (Exception ex)
{
- Debug.LogMessage(ex, "Exception handling set debug request");
+ Debug.LogMessage(ex, "Exception handling set debug request");
context.Response.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error";
context.Response.End();
@@ -164,7 +164,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
}
catch (Exception ex)
{
- Debug.LogMessage(ex, "Exception handling set debug request");
+ Debug.LogMessage(ex, "Exception handling set debug request");
context.Response.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error";
context.Response.End();
@@ -198,21 +198,21 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
public class SetDeviceStreamDebugConfig
{
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Include)]
- ///
- /// Gets or sets the DeviceKey
- ///
+ ///
+ /// Gets or sets the DeviceKey
+ ///
public string DeviceKey { get; set; }
[JsonProperty("setting", NullValueHandling = NullValueHandling.Include)]
- ///
- /// Gets or sets the Setting
- ///
+ ///
+ /// Gets or sets the Setting
+ ///
public string Setting { get; set; }
[JsonProperty("timeout")]
- ///
- /// Gets or sets the Timeout
- ///
+ ///
+ /// Gets or sets the Timeout
+ ///
public int Timeout { get; set; }
public SetDeviceStreamDebugConfig()