Merge pull request #1351 from PepperDash/stream-debugging

fix: centralize debug printing into extension methods
This commit is contained in:
Neil Dorin
2025-11-03 12:19:05 -05:00
committed by GitHub
14 changed files with 688 additions and 546 deletions

View File

@@ -0,0 +1,43 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace PepperDash.Core
{
/// <summary>
/// Helper class for formatting communication text and byte data for debugging purposes.
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <param name="bytes"></param>
/// <returns>string with all bytes escaped</returns>
public static string GetEscapedText(byte[] bytes)
{
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all bytes escaped</returns>
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());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all non-printable characters escaped</returns>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
}

View File

@@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -37,14 +36,14 @@ namespace PepperDash.Core
{ {
get get
{ {
return _DebugTimeoutInMs/60000; return _DebugTimeoutInMs / 60000;
} }
} }
/// <summary> /// <summary>
/// Gets or sets the RxStreamDebuggingIsEnabled /// Gets or sets the RxStreamDebuggingIsEnabled
/// </summary> /// </summary>
public bool RxStreamDebuggingIsEnabled{ get; private set; } public bool RxStreamDebuggingIsEnabled { get; private set; }
/// <summary> /// <summary>
/// Indicates that transmit stream debugging is enabled /// Indicates that transmit stream debugging is enabled
@@ -136,51 +135,4 @@ namespace PepperDash.Core
DebugExpiryPeriod = null; DebugExpiryPeriod = null;
} }
} }
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
/// <summary>
/// Enumeration of eStreamDebuggingSetting values
/// </summary>
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
/// <summary>
/// The available settings for stream debugging response types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text,
}
} }

View File

@@ -1,4 +1,5 @@
using System; using System;
using System.Collections.Generic;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using Crestron.SimplSharp; using Crestron.SimplSharp;
@@ -11,11 +12,12 @@ using Renci.SshNet.Common;
namespace PepperDash.Core namespace PepperDash.Core
{ {
/// <summary> /// <summary>
/// /// SSH Client
/// </summary> /// </summary>
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{ {
private const string SPlusKey = "Uninitialized SshClient"; private const string SPlusKey = "Uninitialized SshClient";
/// <summary> /// <summary>
/// Object to enable stream debugging /// Object to enable stream debugging
/// </summary> /// </summary>
@@ -36,11 +38,6 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange; public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
/// <summary>
/////
///// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
/// <summary> /// <summary>
/// Gets or sets the Hostname /// Gets or sets the Hostname
/// </summary> /// </summary>
@@ -67,7 +64,7 @@ namespace PepperDash.Core
public bool IsConnected public bool IsConnected
{ {
// returns false if no client or not connected // 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; }
} }
/// <summary> /// <summary>
@@ -83,16 +80,26 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public SocketStatus ClientStatus public SocketStatus ClientStatus
{ {
get { return _ClientStatus; } get { lock (_stateLock) { return _ClientStatus; } }
private set private set
{ {
if (_ClientStatus == value) bool shouldFireEvent = false;
return; lock (_stateLock)
_ClientStatus = value; {
OnConnectionChange(); 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;
/// <summary> /// <summary>
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
@@ -100,7 +107,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public ushort UStatus public ushort UStatus
{ {
get { return (ushort)_ClientStatus; } get { lock (_stateLock) { return (ushort)_ClientStatus; } }
} }
/// <summary> /// <summary>
@@ -111,7 +118,11 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Will be set and unset by connect and disconnect only /// Will be set and unset by connect and disconnect only
/// </summary> /// </summary>
public bool ConnectEnabled { get; private set; } public bool ConnectEnabled
{
get { lock (_stateLock) { return _ConnectEnabled; } }
private set { lock (_stateLock) { _ConnectEnabled = value; } }
}
/// <summary> /// <summary>
/// S+ helper for AutoReconnect /// S+ helper for AutoReconnect
@@ -127,17 +138,25 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public int AutoReconnectIntervalMs { get; set; } 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 //Lock object to prevent simulatneous connect/disconnect operations
//private CCriticalSection connectLock = new CCriticalSection(); //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;
/// <summary>
/// When true, turns off echo for the SSH session
/// </summary>
public bool DisableEcho { get; set; }
/// <summary> /// <summary>
/// Typical constructor. /// Typical constructor.
@@ -154,13 +173,13 @@ namespace PepperDash.Core
Password = password; Password = password;
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
ReconnectTimer = new CTimer(o => reconnectTimer = new Timer(o =>
{ {
if (ConnectEnabled) if (ConnectEnabled) // Now thread-safe property access
{ {
Connect(); Connect();
} }
}, System.Threading.Timeout.Infinite); }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
} }
/// <summary> /// <summary>
@@ -172,23 +191,23 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
ReconnectTimer = new CTimer(o => reconnectTimer = new Timer(o =>
{ {
if (ConnectEnabled) if (ConnectEnabled) // Now thread-safe property access
{ {
Connect(); Connect();
} }
}, System.Threading.Timeout.Infinite); }, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
} }
/// <summary> /// <summary>
/// Handles closing this up when the program shuts down /// Handles closing this up when the program shuts down
/// </summary> /// </summary>
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{ {
if (programEventType == eProgramStatusEventType.Stopping) if (programEventType == eProgramStatusEventType.Stopping)
{ {
if (Client != null) if (client != null)
{ {
this.LogDebug("Program stopping. Closing connection"); this.LogDebug("Program stopping. Closing connection");
Disconnect(); Disconnect();
@@ -223,10 +242,10 @@ namespace PepperDash.Core
this.LogDebug("Attempting connect"); this.LogDebug("Attempting connect");
// Cancel reconnect if running. // Cancel reconnect if running.
ReconnectTimer?.Stop(); StopReconnectTimer();
// Cleanup the old client if it already exists // Cleanup the old client if it already exists
if (Client != null) if (client != null)
{ {
this.LogDebug("Cleaning up disconnected client"); this.LogDebug("Cleaning up disconnected client");
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY); KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
@@ -239,29 +258,36 @@ namespace PepperDash.Core
this.LogDebug("Creating new SshClient"); this.LogDebug("Creating new SshClient");
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth); ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
Client = new SshClient(connectionInfo); client = new SshClient(connectionInfo);
Client.ErrorOccurred += Client_ErrorOccurred; client.ErrorOccurred += Client_ErrorOccurred;
//Attempt to connect //Attempt to connect
ClientStatus = SocketStatus.SOCKET_STATUS_WAITING; ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
try try
{ {
Client.Connect(); client.Connect();
TheStream = Client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534);
if (TheStream.DataAvailable) var modes = new Dictionary<TerminalModes, uint>();
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 // empty the buffer if there is data
string str = TheStream.Read(); shellStream.Read();
} }
TheStream.DataReceived += Stream_DataReceived; shellStream.DataReceived += Stream_DataReceived;
this.LogInformation("Connected"); this.LogInformation("Connected");
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED; ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
DisconnectLogged = false; disconnectLogged = false;
} }
catch (SshConnectionException e) catch (SshConnectionException e)
{ {
var ie = e.InnerException; // The details are inside!! var ie = e.InnerException; // The details are inside!!
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
if (ie is SocketException) if (ie is SocketException)
{ {
@@ -286,37 +312,36 @@ namespace PepperDash.Core
this.LogVerbose(ie, "Exception details: "); this.LogVerbose(ie, "Exception details: ");
} }
DisconnectLogged = true; disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect) if (AutoReconnect)
{ {
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs); this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
ReconnectTimer.Reset(AutoReconnectIntervalMs); StartReconnectTimer();
} }
} }
catch (SshOperationTimeoutException ex) catch (SshOperationTimeoutException ex)
{ {
this.LogWarning("Connection attempt timed out: {message}", ex.Message); this.LogWarning("Connection attempt timed out: {message}", ex.Message);
DisconnectLogged = true; disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect) if (AutoReconnect)
{ {
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
ReconnectTimer.Reset(AutoReconnectIntervalMs); StartReconnectTimer();
} }
} }
catch (Exception e) catch (Exception e)
{ {
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
this.LogError("Unhandled exception on connect: {error}", e.Message); this.LogError("Unhandled exception on connect: {error}", e.Message);
this.LogVerbose(e, "Exception details: "); this.LogVerbose(e, "Exception details: ");
DisconnectLogged = true; disconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect) if (AutoReconnect)
{ {
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
ReconnectTimer.Reset(AutoReconnectIntervalMs); StartReconnectTimer();
} }
} }
} }
@@ -334,11 +359,7 @@ namespace PepperDash.Core
{ {
ConnectEnabled = false; ConnectEnabled = false;
// Stop trying reconnects, if we are // Stop trying reconnects, if we are
if (ReconnectTimer != null) StopReconnectTimer();
{
ReconnectTimer.Stop();
// ReconnectTimer = null;
}
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY); KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
} }
@@ -352,12 +373,12 @@ namespace PepperDash.Core
try try
{ {
if (Client != null) if (client != null)
{ {
Client.ErrorOccurred -= Client_ErrorOccurred; client.ErrorOccurred -= Client_ErrorOccurred;
Client.Disconnect(); client.Disconnect();
Client.Dispose(); client.Dispose();
Client = null; client = null;
ClientStatus = status; ClientStatus = status;
this.LogDebug("Disconnected"); this.LogDebug("Disconnected");
} }
@@ -371,16 +392,16 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Kills the stream /// Kills the stream
/// </summary> /// </summary>
void KillStream() private void KillStream()
{ {
try try
{ {
if (TheStream != null) if (shellStream != null)
{ {
TheStream.DataReceived -= Stream_DataReceived; shellStream.DataReceived -= Stream_DataReceived;
TheStream.Close(); shellStream.Close();
TheStream.Dispose(); shellStream.Dispose();
TheStream = null; shellStream = null;
this.LogDebug("Disconnected stream"); this.LogDebug("Disconnected stream");
} }
} }
@@ -393,7 +414,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Handles the keyboard interactive authentication, should it be required. /// Handles the keyboard interactive authentication, should it be required.
/// </summary> /// </summary>
void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e) private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{ {
foreach (AuthenticationPrompt prompt in e.Prompts) foreach (AuthenticationPrompt prompt in e.Prompts)
if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1) if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
@@ -403,7 +424,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing. /// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
/// </summary> /// </summary>
void Stream_DataReceived(object sender, ShellDataEventArgs e) private void Stream_DataReceived(object sender, ShellDataEventArgs e)
{ {
if (((ShellStream)sender).Length <= 0L) if (((ShellStream)sender).Length <= 0L)
{ {
@@ -416,18 +437,14 @@ namespace PepperDash.Core
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.UTF8.GetBytes(response); var bytes = Encoding.UTF8.GetBytes(response);
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedBytes(bytes);
{
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedText(response);
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
textHandler(this, new GenericCommMethodReceiveTextArgs(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 /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
/// event /// event
/// </summary> /// </summary>
void Client_ErrorOccurred(object sender, ExceptionEventArgs e) private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
{ {
CrestronInvoke.BeginInvoke(o => CrestronInvoke.BeginInvoke(o =>
{ {
@@ -459,7 +476,7 @@ namespace PepperDash.Core
if (AutoReconnect && ConnectEnabled) if (AutoReconnect && ConnectEnabled)
{ {
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs); this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
ReconnectTimer.Reset(AutoReconnectIntervalMs); StartReconnectTimer();
} }
}); });
} }
@@ -467,7 +484,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Helper for ConnectionChange event /// Helper for ConnectionChange event
/// </summary> /// </summary>
void OnConnectionChange() private void OnConnectionChange()
{ {
ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this)); ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
} }
@@ -482,16 +499,12 @@ namespace PepperDash.Core
{ {
try try
{ {
if (Client != null && TheStream != null && IsConnected) if (client != null && shellStream != null && IsConnected)
{ {
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentText(text);
this.LogInformation(
"Sending {length} characters of text: '{text}'",
text.Length,
ComTextHelper.GetDebugText(text));
TheStream.Write(text); shellStream.Write(text);
TheStream.Flush(); shellStream.Flush();
} }
else else
{ {
@@ -503,7 +516,7 @@ namespace PepperDash.Core
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim()); this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
ReconnectTimer.Reset(); StartReconnectTimer();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -519,13 +532,12 @@ namespace PepperDash.Core
{ {
try try
{ {
if (Client != null && TheStream != null && IsConnected) if (client != null && shellStream != null && IsConnected)
{ {
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentBytes(bytes);
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
TheStream.Write(bytes, 0, bytes.Length); shellStream.Write(bytes, 0, bytes.Length);
TheStream.Flush(); shellStream.Flush();
} }
else else
{ {
@@ -537,7 +549,7 @@ namespace PepperDash.Core
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes)); this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED); KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
ReconnectTimer.Reset(); StartReconnectTimer();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -546,6 +558,83 @@ namespace PepperDash.Core
} }
#endregion #endregion
/// <summary>
/// Safely starts the reconnect timer with exception handling
/// </summary>
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");
}
}
/// <summary>
/// Safely stops the reconnect timer with exception handling
/// </summary>
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");
}
}
/// <summary>
/// Deactivate method - properly dispose of resources
/// </summary>
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;
}
}
} }
//***************************************************************************************************** //*****************************************************************************************************

View File

@@ -19,44 +19,44 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; } public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary> /// <summary>
/// Fires when data is received from the server and returns it as a Byte array /// Fires when data is received from the server and returns it as a Byte array
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary> /// <summary>
/// Fires when data is received from the server and returns it as text /// Fires when data is received from the server and returns it as text
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange; //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange; public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
private string _hostname; private string _hostname;
/// <summary> /// <summary>
/// Address of server /// Address of server
/// </summary> /// </summary>
public string Hostname public string Hostname
{ {
get get
{ {
return _hostname; return _hostname;
} }
set set
{ {
_hostname = value; _hostname = value;
if (_client != null) if (_client != null)
{ {
_client.AddressClientConnectedTo = _hostname; _client.AddressClientConnectedTo = _hostname;
} }
} }
} }
/// <summary> /// <summary>
/// Gets or sets the Port /// Gets or sets the Port
@@ -78,15 +78,15 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public int BufferSize { get; set; } public int BufferSize { get; set; }
/// <summary> /// <summary>
/// The actual client class /// The actual client class
/// </summary> /// </summary>
private TCPClient _client; private TCPClient _client;
/// <summary> /// <summary>
/// Bool showing if socket is connected /// Bool showing if socket is connected
/// </summary> /// </summary>
public bool IsConnected public bool IsConnected
{ {
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
} }
@@ -99,10 +99,10 @@ namespace PepperDash.Core
get { return (ushort)(IsConnected ? 1 : 0); } get { return (ushort)(IsConnected ? 1 : 0); }
} }
/// <summary> /// <summary>
/// _client socket status Read only /// _client socket status Read only
/// </summary> /// </summary>
public SocketStatus ClientStatus public SocketStatus ClientStatus
{ {
get get
{ {
@@ -119,26 +119,26 @@ namespace PepperDash.Core
get { return (ushort)ClientStatus; } get { return (ushort)ClientStatus; }
} }
/// <summary> /// <summary>
/// Status text shows the message associated with socket status /// Status text shows the message associated with socket status
/// </summary> /// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } } public string ClientStatusText { get { return ClientStatus.ToString(); } }
/// <summary> /// <summary>
/// Ushort representation of client status /// Ushort representation of client status
/// </summary> /// </summary>
[Obsolete] [Obsolete]
public ushort UClientStatus { get { return (ushort)ClientStatus; } } public ushort UClientStatus { get { return (ushort)ClientStatus; } }
/// <summary> /// <summary>
/// Connection failure reason /// Connection failure reason
/// </summary> /// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } } public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary> /// <summary>
/// Gets or sets the AutoReconnect /// Gets or sets the AutoReconnect
/// </summary> /// </summary>
public bool AutoReconnect { get; set; } public bool AutoReconnect { get; set; }
/// <summary> /// <summary>
/// S+ helper for AutoReconnect /// S+ helper for AutoReconnect
@@ -149,29 +149,29 @@ namespace PepperDash.Core
set { AutoReconnect = value == 1; } set { AutoReconnect = value == 1; }
} }
/// <summary> /// <summary>
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000 /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
/// </summary> /// </summary>
public int AutoReconnectIntervalMs { get; set; } public int AutoReconnectIntervalMs { get; set; }
/// <summary> /// <summary>
/// Set only when the disconnect method is called /// Set only when the disconnect method is called
/// </summary> /// </summary>
bool DisconnectCalledByUser; bool DisconnectCalledByUser;
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public bool Connected public bool Connected
{ {
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; } get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
} }
//Lock object to prevent simulatneous connect/disconnect operations //Lock object to prevent simulatneous connect/disconnect operations
private CCriticalSection connectLock = new CCriticalSection(); private CCriticalSection connectLock = new CCriticalSection();
// private Timer for auto reconnect // private Timer for auto reconnect
private CTimer RetryTimer; private CTimer RetryTimer;
/// <summary> /// <summary>
/// Constructor /// Constructor
@@ -181,8 +181,8 @@ namespace PepperDash.Core
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="bufferSize"></param> /// <param name="bufferSize"></param>
public GenericTcpIpClient(string key, string address, int port, int bufferSize) public GenericTcpIpClient(string key, string address, int port, int bufferSize)
: base(key) : base(key)
{ {
StreamDebugging = new CommunicationStreamDebugging(key); StreamDebugging = new CommunicationStreamDebugging(key);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
@@ -218,18 +218,18 @@ namespace PepperDash.Core
/// Default constructor for S+ /// Default constructor for S+
/// </summary> /// </summary>
public GenericTcpIpClient() public GenericTcpIpClient()
: base(SplusKey) : base(SplusKey)
{ {
StreamDebugging = new CommunicationStreamDebugging(SplusKey); StreamDebugging = new CommunicationStreamDebugging(SplusKey);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
BufferSize = 2000; BufferSize = 2000;
RetryTimer = new CTimer(o => RetryTimer = new CTimer(o =>
{ {
Reconnect(); Reconnect();
}, Timeout.Infinite); }, Timeout.Infinite);
} }
/// <summary> /// <summary>
/// Initialize method /// Initialize method
@@ -255,26 +255,26 @@ namespace PepperDash.Core
/// ///
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
/// <summary> /// <summary>
/// Deactivate method /// Deactivate method
/// </summary> /// </summary>
public override bool Deactivate() public override bool Deactivate()
{ {
RetryTimer.Stop(); RetryTimer.Stop();
RetryTimer.Dispose(); RetryTimer.Dispose();
if (_client != null) if (_client != null)
{ {
_client.SocketStatusChange -= this.Client_SocketStatusChange; _client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient(); DisconnectClient();
} }
return true; return true;
} }
/// <summary> /// <summary>
/// Connect method /// Connect method
/// </summary> /// </summary>
public void Connect() public void Connect()
{ {
if (string.IsNullOrEmpty(Hostname)) if (string.IsNullOrEmpty(Hostname))
{ {
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key); Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
@@ -310,7 +310,7 @@ namespace PepperDash.Core
{ {
connectLock.Leave(); connectLock.Leave();
} }
} }
private void Reconnect() private void Reconnect()
{ {
@@ -337,11 +337,11 @@ namespace PepperDash.Core
} }
} }
/// <summary> /// <summary>
/// Disconnect method /// Disconnect method
/// </summary> /// </summary>
public void Disconnect() public void Disconnect()
{ {
try try
{ {
connectLock.Enter(); connectLock.Enter();
@@ -355,7 +355,7 @@ namespace PepperDash.Core
{ {
connectLock.Leave(); connectLock.Leave();
} }
} }
/// <summary> /// <summary>
/// DisconnectClient method /// DisconnectClient method
@@ -375,7 +375,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
/// <param name="c"></param> /// <param name="c"></param>
void ConnectToServerCallback(TCPClient c) void ConnectToServerCallback(TCPClient c)
{ {
if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED) if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{ {
Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus); 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); Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
} }
} }
/// <summary> /// <summary>
/// Disconnects, waits and attemtps to connect again /// Disconnects, waits and attemtps to connect again
/// </summary> /// </summary>
void WaitAndTryReconnect() void WaitAndTryReconnect()
{ {
CrestronInvoke.BeginInvoke(o => CrestronInvoke.BeginInvoke(o =>
{ {
try try
@@ -409,7 +409,7 @@ namespace PepperDash.Core
connectLock.Leave(); connectLock.Leave();
} }
}); });
} }
/// <summary> /// <summary>
/// Recieves incoming data /// Recieves incoming data
@@ -417,7 +417,7 @@ namespace PepperDash.Core
/// <param name="client"></param> /// <param name="client"></param>
/// <param name="numBytes"></param> /// <param name="numBytes"></param>
void Receive(TCPClient client, int numBytes) void Receive(TCPClient client, int numBytes)
{ {
if (client != null) if (client != null)
{ {
if (numBytes > 0) if (numBytes > 0)
@@ -426,10 +426,7 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) if (bytesHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedBytes(bytes);
{
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
@@ -437,58 +434,53 @@ namespace PepperDash.Core
{ {
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedText(str);
{
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
}
textHandler(this, new GenericCommMethodReceiveTextArgs(str)); textHandler(this, new GenericCommMethodReceiveTextArgs(str));
} }
} }
client.ReceiveDataAsync(Receive); client.ReceiveDataAsync(Receive);
} }
} }
/// <summary> /// <summary>
/// SendText method /// SendText method
/// </summary> /// </summary>
public void SendText(string text) public void SendText(string text)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(text); var bytes = Encoding.GetEncoding(28591).GetBytes(text);
// Check debug level before processing byte array // Check debug level before processing byte array
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentText(text);
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
if (_client != null) if (_client != null)
_client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }
/// <summary> /// <summary>
/// SendEscapedText method /// SendEscapedText method
/// </summary> /// </summary>
public void SendEscapedText(string text) public void SendEscapedText(string text)
{ {
var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s => var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
{ {
var hex = s.Groups[1].Value; var hex = s.Groups[1].Value;
return ((char)Convert.ToByte(hex, 16)).ToString(); return ((char)Convert.ToByte(hex, 16)).ToString();
}); });
SendText(unescapedText); SendText(unescapedText);
} }
/// <summary> /// <summary>
/// Sends Bytes to the server /// Sends Bytes to the server
/// </summary> /// </summary>
/// <param name="bytes"></param> /// <param name="bytes"></param>
/// <summary> /// <summary>
/// SendBytes method /// SendBytes method
/// </summary> /// </summary>
public void SendBytes(byte[] bytes) public void SendBytes(byte[] bytes)
{ {
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentBytes(bytes);
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (_client != null) if (_client != null)
_client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }
/// <summary> /// <summary>
/// Socket Status Change Handler /// Socket Status Change Handler
@@ -496,7 +488,7 @@ namespace PepperDash.Core
/// <param name="client"></param> /// <param name="client"></param>
/// <param name="clientSocketStatus"></param> /// <param name="clientSocketStatus"></param>
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus) void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
{ {
if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED) if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{ {
Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText); Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
@@ -505,68 +497,73 @@ namespace PepperDash.Core
else else
{ {
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText); Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
_client.ReceiveDataAsync(Receive); _client.ReceiveDataAsync(Receive);
} }
var handler = ConnectionChange; var handler = ConnectionChange;
if (handler != null) if (handler != null)
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this)); ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
} }
} }
/// <summary> /// <summary>
/// Represents a TcpSshPropertiesConfig /// Represents a TcpSshPropertiesConfig
/// </summary> /// </summary>
public class TcpSshPropertiesConfig public class TcpSshPropertiesConfig
{ {
/// <summary> /// <summary>
/// Address to connect to /// Address to connect to
/// </summary> /// </summary>
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public string Address { get; set; } public string Address { get; set; }
/// <summary> /// <summary>
/// Port to connect to /// Port to connect to
/// </summary> /// </summary>
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public int Port { get; set; } public int Port { get; set; }
/// <summary> /// <summary>
/// Username credential /// Username credential
/// </summary> /// </summary>
public string Username { get; set; } public string Username { get; set; }
/// <summary> /// <summary>
/// Gets or sets the Password /// Gets or sets the Password
/// </summary> /// </summary>
public string Password { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
/// Defaults to 32768 /// Defaults to 32768
/// </summary> /// </summary>
public int BufferSize { get; set; } public int BufferSize { get; set; }
/// <summary> /// <summary>
/// Gets or sets the AutoReconnect /// Gets or sets the AutoReconnect
/// </summary> /// </summary>
public bool AutoReconnect { get; set; } public bool AutoReconnect { get; set; }
/// <summary> /// <summary>
/// Gets or sets the AutoReconnectIntervalMs /// Gets or sets the AutoReconnectIntervalMs
/// </summary> /// </summary>
public int AutoReconnectIntervalMs { get; set; } public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// When true, turns off echo for the SSH session
/// </summary>
[JsonProperty("disableSshEcho")]
public bool DisableSshEcho { get; set; }
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
public TcpSshPropertiesConfig() public TcpSshPropertiesConfig()
{ {
BufferSize = 32768; BufferSize = 32768;
AutoReconnect = true; AutoReconnect = true;
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
Username = ""; Username = "";
Password = ""; Password = "";
} DisableSshEcho = false;
}
} }
} }

View File

@@ -243,7 +243,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void Disconnect() public void Disconnect()
{ {
if(Server != null) if (Server != null)
Server.DisableUDPServer(); Server.DisableUDPServer();
IsConnected = false; IsConnected = false;
@@ -281,17 +281,13 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) if (bytesHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedBytes(bytes);
{
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedText(str);
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
textHandler(this, new GenericCommMethodReceiveTextArgs(str)); textHandler(this, new GenericCommMethodReceiveTextArgs(str));
} }
} }
@@ -318,8 +314,7 @@ namespace PepperDash.Core
if (IsConnected && Server != null) if (IsConnected && Server != null)
{ {
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentText(text);
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
Server.SendData(bytes, bytes.Length); Server.SendData(bytes, bytes.Length);
} }
@@ -334,8 +329,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void SendBytes(byte[] bytes) public void SendBytes(byte[] bytes)
{ {
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentBytes(bytes);
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (IsConnected && Server != null) if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length); Server.SendData(bytes, bytes.Length);
@@ -343,11 +337,11 @@ namespace PepperDash.Core
} }
/// <summary> /// <summary>
/// Represents a GenericUdpReceiveTextExtraArgs /// Represents a GenericUdpReceiveTextExtraArgs
/// </summary> /// </summary>
public class GenericUdpReceiveTextExtraArgs : EventArgs public class GenericUdpReceiveTextExtraArgs : EventArgs
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@@ -359,7 +353,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public int Port { get; private set; } public int Port { get; private set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@@ -373,18 +367,18 @@ namespace PepperDash.Core
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="bytes"></param> /// <param name="bytes"></param>
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes) public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
{ {
Text = text; Text = text;
IpAddress = ipAddress; IpAddress = ipAddress;
Port = port; Port = port;
Bytes = bytes; Bytes = bytes;
} }
/// <summary> /// <summary>
/// Stupid S+ Constructor /// Stupid S+ Constructor
/// </summary> /// </summary>
public GenericUdpReceiveTextExtraArgs() { } public GenericUdpReceiveTextExtraArgs() { }
} }
/// <summary> /// <summary>
/// ///

View File

@@ -0,0 +1,69 @@
using System;
using Crestron.SimplSharp;
namespace PepperDash.Core
{
/// <summary>
/// Extension methods for stream debugging
/// </summary>
public static class StreamDebuggingExtensions
{
private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $"App {InitialParametersClass.ApplicationNumber}" : $"{InitialParametersClass.RoomId}";
/// <summary>
/// Print the sent bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
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)}'");
}
/// <summary>
/// Print the received bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
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)}'");
}
/// <summary>
/// Print the sent text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
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)}'");
}
/// <summary>
/// Print the received text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
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)}'");
}
}
}

View File

@@ -0,0 +1,24 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging data format types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
}

View File

@@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets; using Crestron.SimplSharp.CrestronSockets;
using System.Text.RegularExpressions;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace PepperDash.Core namespace PepperDash.Core
@@ -42,7 +39,7 @@ namespace PepperDash.Core
/// Defines the contract for IBasicCommunication /// Defines the contract for IBasicCommunication
/// </summary> /// </summary>
public interface IBasicCommunication : ICommunicationReceiver public interface IBasicCommunication : ICommunicationReceiver
{ {
/// <summary> /// <summary>
/// Send text to the device /// Send text to the device
/// </summary> /// </summary>
@@ -54,7 +51,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
/// <param name="bytes"></param> /// <param name="bytes"></param>
void SendBytes(byte[] bytes); void SendBytes(byte[] bytes);
} }
/// <summary> /// <summary>
/// Represents a device that implements IBasicCommunication and IStreamDebugging /// Represents a device that implements IBasicCommunication and IStreamDebugging
@@ -67,7 +64,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Represents a device with stream debugging capablities /// Represents a device with stream debugging capablities
/// </summary> /// </summary>
public interface IStreamDebugging public interface IStreamDebugging : IKeyed
{ {
/// <summary> /// <summary>
/// Object to enable stream debugging /// Object to enable stream debugging
@@ -76,12 +73,12 @@ namespace PepperDash.Core
CommunicationStreamDebugging StreamDebugging { get; } CommunicationStreamDebugging StreamDebugging { get; }
} }
/// <summary> /// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient, /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient /// GenericTcpIpClient
/// </summary> /// </summary>
public interface ISocketStatus : IBasicCommunication public interface ISocketStatus : IBasicCommunication
{ {
/// <summary> /// <summary>
/// Notifies of socket status changes /// Notifies of socket status changes
/// </summary> /// </summary>
@@ -93,7 +90,7 @@ namespace PepperDash.Core
[JsonProperty("clientStatus")] [JsonProperty("clientStatus")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] [JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
SocketStatus ClientStatus { get; } SocketStatus ClientStatus { get; }
} }
/// <summary> /// <summary>
/// Describes a device that implements ISocketStatus and IStreamDebugging /// Describes a device that implements ISocketStatus and IStreamDebugging
@@ -107,24 +104,24 @@ namespace PepperDash.Core
/// Describes a device that can automatically attempt to reconnect /// Describes a device that can automatically attempt to reconnect
/// </summary> /// </summary>
public interface IAutoReconnect public interface IAutoReconnect
{ {
/// <summary> /// <summary>
/// Enable automatic recconnect /// Enable automatic recconnect
/// </summary> /// </summary>
[JsonProperty("autoReconnect")] [JsonProperty("autoReconnect")]
bool AutoReconnect { get; set; } bool AutoReconnect { get; set; }
/// <summary> /// <summary>
/// Interval in ms to attempt automatic recconnections /// Interval in ms to attempt automatic recconnections
/// </summary> /// </summary>
[JsonProperty("autoReconnectIntervalMs")] [JsonProperty("autoReconnectIntervalMs")]
int AutoReconnectIntervalMs { get; set; } int AutoReconnectIntervalMs { get; set; }
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public enum eGenericCommMethodStatusChangeType public enum eGenericCommMethodStatusChangeType
{ {
/// <summary> /// <summary>
/// Connected /// Connected
/// </summary> /// </summary>
@@ -133,45 +130,45 @@ namespace PepperDash.Core
/// Disconnected /// Disconnected
/// </summary> /// </summary>
Disconnected Disconnected
} }
/// <summary> /// <summary>
/// This delegate defines handler for IBasicCommunication status changes /// This delegate defines handler for IBasicCommunication status changes
/// </summary> /// </summary>
/// <param name="comm">Device firing the status change</param> /// <param name="comm">Device firing the status change</param>
/// <param name="status"></param> /// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status); public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs public class GenericCommMethodReceiveBytesArgs : EventArgs
{ {
/// <summary> /// <summary>
/// Gets or sets the Bytes /// Gets or sets the Bytes
/// </summary> /// </summary>
public byte[] Bytes { get; private set; } public byte[] Bytes { get; private set; }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <param name="bytes"></param> /// <param name="bytes"></param>
public GenericCommMethodReceiveBytesArgs(byte[] bytes) public GenericCommMethodReceiveBytesArgs(byte[] bytes)
{ {
Bytes = bytes; Bytes = bytes;
} }
/// <summary> /// <summary>
/// S+ Constructor /// S+ Constructor
/// </summary> /// </summary>
public GenericCommMethodReceiveBytesArgs() { } public GenericCommMethodReceiveBytesArgs() { }
} }
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs public class GenericCommMethodReceiveTextArgs : EventArgs
{ {
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
@@ -185,9 +182,9 @@ namespace PepperDash.Core
/// </summary> /// </summary>
/// <param name="text"></param> /// <param name="text"></param>
public GenericCommMethodReceiveTextArgs(string text) public GenericCommMethodReceiveTextArgs(string text)
{ {
Text = text; Text = text;
} }
/// <summary> /// <summary>
/// ///
@@ -195,59 +192,14 @@ namespace PepperDash.Core
/// <param name="text"></param> /// <param name="text"></param>
/// <param name="delimiter"></param> /// <param name="delimiter"></param>
public GenericCommMethodReceiveTextArgs(string text, string delimiter) public GenericCommMethodReceiveTextArgs(string text, string delimiter)
:this(text) : this(text)
{ {
Delimiter = delimiter; Delimiter = delimiter;
} }
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveTextArgs() { }
}
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary> /// <summary>
/// Gets escaped text for a byte array /// S+ Constructor
/// </summary> /// </summary>
/// <param name="bytes"></param> public GenericCommMethodReceiveTextArgs() { }
/// <returns></returns> }
public static string GetEscapedText(byte[] bytes)
{
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetEscapedText method
/// </summary>
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());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetDebugText method
/// </summary>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
} }

View File

@@ -168,7 +168,7 @@ namespace PepperDash.Core
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug, restrictedToMinimumLevel: LogEventLevel.Debug,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60, retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,
levelSwitch: _fileLogLevelSwitch levelSwitch: _fileLogLevelSwitch
); );
@@ -1081,9 +1081,6 @@ namespace PepperDash.Core
/// Logs to Console when at-level, and all messages to error log /// Logs to Console when at-level, and all messages to error log
/// </summary> /// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
/// <summary>
/// Console method
/// </summary>
public static void Console(uint level, ErrorLogLevel errorLogLevel, public static void Console(uint level, ErrorLogLevel errorLogLevel,
string format, params object[] items) string format, params object[] items)
{ {
@@ -1096,9 +1093,6 @@ namespace PepperDash.Core
/// it will only be written to the log. /// it will only be written to the log.
/// </summary> /// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] [Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
/// <summary>
/// ConsoleWithLog method
/// </summary>
public static void ConsoleWithLog(uint level, string format, params object[] items) public static void ConsoleWithLog(uint level, string format, params object[] items)
{ {
LogMessage(level, format, items); LogMessage(level, format, items);

View File

@@ -12,15 +12,15 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core namespace PepperDash.Essentials.Core
{ {
/// <summary> /// <summary>
/// Represents a CecPortController /// Represents a CecPortController
/// </summary> /// </summary>
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
{ {
/// <summary> /// <summary>
/// Gets or sets the StreamDebugging /// Gets or sets the StreamDebugging
/// </summary> /// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; } public CommunicationStreamDebugging StreamDebugging { get; private set; }
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
@@ -33,9 +33,9 @@ namespace PepperDash.Essentials.Core
ICec Port; ICec Port;
public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc, public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc,
EssentialsControlPropertiesConfig config):base(key) EssentialsControlPropertiesConfig config) : base(key)
{ {
StreamDebugging = new CommunicationStreamDebugging(key); StreamDebugging = new CommunicationStreamDebugging(key);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
@@ -58,27 +58,25 @@ namespace PepperDash.Essentials.Core
if (args.EventId == CecEventIds.CecMessageReceivedEventId) if (args.EventId == CecEventIds.CecMessageReceivedEventId)
OnDataReceived(cecDevice.Received.StringValue); OnDataReceived(cecDevice.Received.StringValue);
else if (args.EventId == CecEventIds.ErrorFeedbackEventId) else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
if(cecDevice.ErrorFeedback.BoolValue) if (cecDevice.ErrorFeedback.BoolValue)
Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error"); Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
} }
void OnDataReceived(string s) void OnDataReceived(string s)
{ {
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(s); var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedText(s);
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); }
}
} }
#region IBasicCommunication Members #region IBasicCommunication Members
@@ -90,8 +88,7 @@ namespace PepperDash.Essentials.Core
{ {
if (Port == null) if (Port == null)
return; return;
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentText(text);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.StreamCec.Send.StringValue = text; Port.StreamCec.Send.StringValue = text;
} }
@@ -103,8 +100,8 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.StreamCec.Send.StringValue = text; Port.StreamCec.Send.StringValue = text;
} }

View File

@@ -25,8 +25,8 @@ namespace PepperDash.Essentials.Core
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary> /// <summary>
/// Event fired when text is received /// Event fired when text is received
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary> /// <summary>
@@ -38,12 +38,12 @@ namespace PepperDash.Essentials.Core
ComPort.ComPortSpec Spec; ComPort.ComPortSpec Spec;
/// <summary> /// <summary>
/// Constructor /// Constructor
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="postActivationFunc"></param> /// <param name="postActivationFunc"></param>
/// <param name="spec"></param> /// <param name="spec"></param>
/// <param name="config"></param> /// <param name="config"></param>
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc, public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key) ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{ {
@@ -165,16 +165,14 @@ namespace PepperDash.Essentials.Core
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(s); var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true; eventSubscribed = true;
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
if (StreamDebugging.RxStreamDebuggingIsEnabled) this.PrintReceivedText(s);
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true; eventSubscribed = true;
} }
@@ -201,8 +199,7 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentText(text);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.Send(text); Port.Send(text);
} }
@@ -214,8 +211,7 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
if (StreamDebugging.TxStreamDebuggingIsEnabled) this.PrintSentBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.Send(text); Port.Send(text);
} }

View File

@@ -64,8 +64,11 @@ namespace PepperDash.Essentials.Core
break; break;
case eControlMethod.Ssh: case eControlMethod.Ssh:
{ {
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password); var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password)
ssh.AutoReconnect = c.AutoReconnect; {
AutoReconnect = c.AutoReconnect,
DisableEcho = c.DisableSshEcho
};
if (ssh.AutoReconnect) if (ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh; comm = ssh;
@@ -73,8 +76,10 @@ namespace PepperDash.Essentials.Core
} }
case eControlMethod.Tcpip: case eControlMethod.Tcpip:
{ {
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize); var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize)
tcp.AutoReconnect = c.AutoReconnect; {
AutoReconnect = c.AutoReconnect
};
if (tcp.AutoReconnect) if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp; comm = tcp;
@@ -90,8 +95,10 @@ namespace PepperDash.Essentials.Core
break; break;
case eControlMethod.SecureTcpIp: case eControlMethod.SecureTcpIp:
{ {
var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize); var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize)
secureTcp.AutoReconnect = c.AutoReconnect; {
AutoReconnect = c.AutoReconnect
};
if (secureTcp.AutoReconnect) if (secureTcp.AutoReconnect)
secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs; secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = secureTcp; comm = secureTcp;

View File

@@ -6,9 +6,9 @@ using PepperDash.Core.Web.RequestHandlers;
namespace PepperDash.Essentials.Core.Web.RequestHandlers namespace PepperDash.Essentials.Core.Web.RequestHandlers
{ {
/// <summary> /// <summary>
/// Represents a SetDeviceStreamDebugRequestHandler /// Represents a SetDeviceStreamDebugRequestHandler
/// </summary> /// </summary>
public class SetDeviceStreamDebugRequestHandler : WebApiBaseRequestHandler public class SetDeviceStreamDebugRequestHandler : WebApiBaseRequestHandler
{ {
/// <summary> /// <summary>
@@ -122,23 +122,23 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return; return;
} }
if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device)) if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))
{ {
context.Response.StatusCode = 404; context.Response.StatusCode = 404;
context.Response.StatusDescription = "Not Found"; context.Response.StatusDescription = "Not Found";
context.Response.End(); context.Response.End();
return; return;
} }
eStreamDebuggingSetting debugSetting; eStreamDebuggingSetting debugSetting;
try try
{ {
debugSetting = (eStreamDebuggingSetting) Enum.Parse(typeof (eStreamDebuggingSetting), body.Setting, true); debugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), body.Setting, true);
} }
catch (Exception ex) 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.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error"; context.Response.StatusDescription = "Internal Server Error";
context.Response.End(); context.Response.End();
@@ -164,7 +164,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
} }
catch (Exception ex) 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.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error"; context.Response.StatusDescription = "Internal Server Error";
context.Response.End(); context.Response.End();
@@ -198,21 +198,21 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
public class SetDeviceStreamDebugConfig public class SetDeviceStreamDebugConfig
{ {
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Include)] [JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Include)]
/// <summary> /// <summary>
/// Gets or sets the DeviceKey /// Gets or sets the DeviceKey
/// </summary> /// </summary>
public string DeviceKey { get; set; } public string DeviceKey { get; set; }
[JsonProperty("setting", NullValueHandling = NullValueHandling.Include)] [JsonProperty("setting", NullValueHandling = NullValueHandling.Include)]
/// <summary> /// <summary>
/// Gets or sets the Setting /// Gets or sets the Setting
/// </summary> /// </summary>
public string Setting { get; set; } public string Setting { get; set; }
[JsonProperty("timeout")] [JsonProperty("timeout")]
/// <summary> /// <summary>
/// Gets or sets the Timeout /// Gets or sets the Timeout
/// </summary> /// </summary>
public int Timeout { get; set; } public int Timeout { get; set; }
public SetDeviceStreamDebugConfig() public SetDeviceStreamDebugConfig()