From 8f075fbed73b66b83bf5804b10cd02d3ed732117 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 9 Jun 2020 14:05:27 -0600 Subject: [PATCH 1/2] Adds IStreamDebugging and associated logic to implment on TCP, SSH and UDP methods --- .../Comm/CommunicationStreamDebugging.cs | 127 ++++++++++++++++++ .../Pepperdash Core/Comm/GenericSshClient.cs | 18 ++- .../Comm/GenericTcpIpClient.cs | 24 +++- .../Pepperdash Core/Comm/GenericUdpServer.cs | 18 ++- .../Pepperdash Core/CommunicationExtras.cs | 23 ++++ .../Pepperdash Core/Logging/Debug.cs | 22 +++ .../Pepperdash Core/Logging/DebugMemory.cs | 45 ++++++- .../Pepperdash Core/PepperDash_Core.csproj | 1 + 8 files changed, 263 insertions(+), 15 deletions(-) create mode 100644 Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs diff --git a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs new file mode 100644 index 0000000..3050e6f --- /dev/null +++ b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash.Core +{ + /// + /// Controls the ability to disable/enable debugging of TX/RX data sent to/from a device with a built in timer to disable + /// + public class CommunicationStreamDebugging + { + public string ParentDeviceKey { get; private set; } + + /// + /// Timer to disable automatically if not manually disabled + /// + private CTimer DebugExpiryPeriod; + + public eStreamDebuggingSetting DebugSetting { get; private set; } + + private uint _DebugTimeoutMin; + private const uint _DefaultDebugTimeoutMin = 30; + + /// + /// Timeout in Minutes + /// + public uint DebugTimeoutMinutes + { + get + { + return _DebugTimeoutMin; + } + } + + + private long _DebugTimeoutInMs + { + get + { + return DebugTimeoutMinutes * 60000; + } + } + + public bool RxStreamDebuggingIsEnabled{ get; private set; } + + public bool TxStreamDebuggingIsEnabled { get; private set; } + + + public CommunicationStreamDebugging(string parentDeviceKey) + { + ParentDeviceKey = parentDeviceKey; + } + + + /// + /// Sets the debugging setting and if not setting to off, assumes the default of 30 mintues + /// + /// + public void SetDebuggingWithDefaultTimeout(eStreamDebuggingSetting setting) + { + if (setting == eStreamDebuggingSetting.Off) + { + DisableDebugging(); + } + else + { + SetDebuggingWithSpecificTimeout(setting, _DefaultDebugTimeoutMin); + } + } + + /// + /// Sets the debugging setting for the specified number of minutes + /// + /// + /// + public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes) + { + _DebugTimeoutMin = minutes; + + if (DebugExpiryPeriod != null) + { + DisableDebugging(); + } + + DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs); + + if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx) + RxStreamDebuggingIsEnabled = true; + + if ((setting & eStreamDebuggingSetting.Tx) == eStreamDebuggingSetting.Tx) + TxStreamDebuggingIsEnabled = true; + + Debug.SetDeviceDebugSettings(ParentDeviceKey, setting); + + } + + /// + /// Disabled debugging + /// + public void DisableDebugging() + { + DebugExpiryPeriod.Stop(); + DebugExpiryPeriod.Dispose(); + DebugExpiryPeriod = null; + + RxStreamDebuggingIsEnabled = false; + TxStreamDebuggingIsEnabled = false; + + Debug.SetDeviceDebugSettings(ParentDeviceKey, eStreamDebuggingSetting.Off); + } + } + + /// + /// The available settings for stream debugging + /// + [Flags] + public enum eStreamDebuggingSetting + { + Off = 0, + Rx = 1, + Tx = 2, + Both = Rx | Tx + } +} \ No newline at end of file diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs index ef57ae0..69fa3be 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs @@ -11,8 +11,10 @@ namespace PepperDash.Core /// /// /// - public class GenericSshClient : Device, ISocketStatus, IAutoReconnect + public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect { + public CommunicationStreamDebugging StreamDebugging { get; private set; } + /// /// Event that fires when data is received. Delivers args with byte array /// @@ -139,6 +141,7 @@ namespace PepperDash.Core public GenericSshClient(string key, string hostname, int port, string username, string password) : base(key) { + StreamDebugging = new CommunicationStreamDebugging(key); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); Key = key; Hostname = hostname; @@ -240,7 +243,7 @@ namespace PepperDash.Core try { Client.Connect(); - TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534); + TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 100000); TheStream.DataReceived += Stream_DataReceived; //TheStream.ErrorOccurred += TheStream_ErrorOccurred; Debug.Console(1, this, "Connected"); @@ -381,6 +384,10 @@ namespace PepperDash.Core { var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); textHandler(this, new GenericCommMethodReceiveTextArgs(str)); + + if (StreamDebugging.RxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Recevied: '{0}'", str); + } } } @@ -422,8 +429,12 @@ namespace PepperDash.Core { if (Client != null) { + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, text); + TheStream.Write(text); TheStream.Flush(); + } } catch @@ -444,6 +455,9 @@ namespace PepperDash.Core { if (Client != null) { + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); + TheStream.Write(bytes, 0, bytes.Length); TheStream.Flush(); } diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs index c1384f9..233926c 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs @@ -14,8 +14,10 @@ namespace PepperDash.Core /// /// A class to handle basic TCP/IP communications with a server /// - public class GenericTcpIpClient : Device, ISocketStatus, IAutoReconnect + public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect { + public CommunicationStreamDebugging StreamDebugging { get; private set; } + /// /// Fires when data is received from the server and returns it as a Byte array /// @@ -176,7 +178,7 @@ namespace PepperDash.Core public GenericTcpIpClient(string key, string address, int port, int bufferSize) : base(key) { - + StreamDebugging = new CommunicationStreamDebugging(key); Hostname = address; Port = port; BufferSize = bufferSize; @@ -192,6 +194,7 @@ namespace PepperDash.Core public GenericTcpIpClient(string key) : base(key) { + StreamDebugging = new CommunicationStreamDebugging(key); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); AutoReconnectIntervalMs = 5000; BufferSize = 2000; @@ -348,8 +351,15 @@ namespace PepperDash.Core if (textHandler != null) { var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); + + if (StreamDebugging.RxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Recevied: '{0}'", str); + textHandler(this, new GenericCommMethodReceiveTextArgs(str)); + } + + } client.ReceiveDataAsync(Receive); @@ -363,10 +373,12 @@ namespace PepperDash.Core { var bytes = Encoding.GetEncoding(28591).GetBytes(text); // Check debug level before processing byte array - //if (Debug.Level == 2) - // Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); if(Client != null) Client.SendData(bytes, bytes.Length); + + } /// @@ -388,8 +400,8 @@ namespace PepperDash.Core /// public void SendBytes(byte[] bytes) { - //if (Debug.Level == 2) - // Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); if(Client != null) Client.SendData(bytes, bytes.Length); } diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs index d675646..1e9dddd 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs @@ -15,8 +15,9 @@ using Newtonsoft.Json.Linq; namespace PepperDash.Core { - public class GenericUdpServer : Device, IBasicCommunication, ISocketStatus + public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging { + public CommunicationStreamDebugging StreamDebugging { get; private set; } /// /// /// @@ -138,6 +139,7 @@ namespace PepperDash.Core public GenericUdpServer(string key, string address, int port, int buffefSize) : base(key) { + StreamDebugging = new CommunicationStreamDebugging(key); Hostname = address; Port = port; BufferSize = buffefSize; @@ -268,8 +270,9 @@ namespace PepperDash.Core var textHandler = TextReceived; if (textHandler != null) { - - Debug.Console(2, this, "RX: {0}", str); + if (StreamDebugging.RxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Recevied: '{0}'", str); + textHandler(this, new GenericCommMethodReceiveTextArgs(str)); } else @@ -323,7 +326,9 @@ namespace PepperDash.Core if (IsConnected && Server != null) { - Debug.Console(2, this, "TX: {0}", text); + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, text); + Server.SendData(bytes, bytes.Length); } } @@ -334,8 +339,9 @@ namespace PepperDash.Core /// public void SendBytes(byte[] bytes) { - //if (Debug.Level == 2) - // Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); + if (StreamDebugging.TxStreamDebuggingIsEnabled) + Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); + if (IsConnected && Server != null) Server.SendData(bytes, bytes.Length); } diff --git a/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs b/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs index 2c8c0bc..c5892a4 100644 --- a/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs +++ b/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs @@ -32,6 +32,22 @@ namespace PepperDash.Core void SendBytes(byte[] bytes); } + /// + /// Represents a device that implements IBasicCommunication and IStreamDebugging + /// + public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging + { + + } + + /// + /// Represents a device with stream debugging capablities + /// + public interface IStreamDebugging + { + CommunicationStreamDebugging StreamDebugging { get; } + } + /// /// For IBasicCommunication classes that have SocketStatus. GenericSshClient, /// GenericTcpIpClient @@ -42,6 +58,13 @@ namespace PepperDash.Core SocketStatus ClientStatus { get; } } + /// + /// Represents a device that implements ISocketStatus and IStreamDebugging + /// + public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging + { + + } public interface IAutoReconnect { diff --git a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs b/Pepperdash Core/Pepperdash Core/Logging/Debug.cs index cf4d8df..5d245d2 100644 --- a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs +++ b/Pepperdash Core/Pepperdash Core/Logging/Debug.cs @@ -274,6 +274,28 @@ namespace PepperDash.Core } } + /// + /// sets the settings for a device or creates a new entry + /// + /// + /// + /// + public static void SetDeviceDebugSettings(string deviceKey, object settings) + { + Contexts.SetDebugSettingsForKey(deviceKey, settings); + SaveMemoryOnTimeout(); + } + + /// + /// Gets the device settings for a device by key or returns null + /// + /// + /// + public static object GetDeviceDebugSettingsForKey(string deviceKey) + { + return Contexts.GetDebugSettingsForKey(deviceKey); + } + /// /// Sets the flag to prevent application starting on next boot /// diff --git a/Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs b/Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs index 6dcb65f..1b0fea1 100644 --- a/Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs +++ b/Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs @@ -5,16 +5,31 @@ using System.Text; using Crestron.SimplSharp; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace PepperDash.Core.DebugThings { public class DebugContextCollection { + /// + /// To prevent threading issues with the DeviceDebugSettings collection + /// + private CCriticalSection DeviceDebugSettingsLock; + [JsonProperty("items")] Dictionary Items; + /// + /// Collection of the debug settings for each device where the dictionary key is the device key + /// + [JsonProperty("deviceDebugSettings")] + private Dictionary DeviceDebugSettings { get; set; } + + public DebugContextCollection() { + DeviceDebugSettingsLock = new CCriticalSection(); + DeviceDebugSettings = new Dictionary(); Items = new Dictionary(); } @@ -43,6 +58,35 @@ namespace PepperDash.Core.DebugThings Items[contextKey] = new DebugContextItem(this) { Level = 0 }; return Items[contextKey]; } + + + /// + /// sets the settings for a device or creates a new entry + /// + /// + /// + /// + public void SetDebugSettingsForKey(string deviceKey, object settings) + { + var existingSettings = DeviceDebugSettings[deviceKey]; + + if (existingSettings != null) + { + existingSettings = settings; + } + else + DeviceDebugSettings.Add(deviceKey, settings); + } + + /// + /// Gets the device settings for a device by key or returns null + /// + /// + /// + public object GetDebugSettingsForKey(string deviceKey) + { + return DeviceDebugSettings[deviceKey]; + } } public class DebugContextItem @@ -59,7 +103,6 @@ namespace PepperDash.Core.DebugThings [JsonProperty("doNotLoadOnNextBoot")] public bool DoNotLoadOnNextBoot { get; set; } - public DebugContextItem(DebugContextCollection parent) { diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj index 92ce8c8..d4fbf18 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -69,6 +69,7 @@ + From a7bacd26dd1eeecb6f3b498bd09304ae4dcdd5d1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 9 Jun 2020 15:16:08 -0600 Subject: [PATCH 2/2] change private field to store ms instead of minutes --- .../Comm/CommunicationStreamDebugging.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs index 3050e6f..3f1624a 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs @@ -12,6 +12,9 @@ namespace PepperDash.Core /// public class CommunicationStreamDebugging { + /// + /// Device Key that this instance configures + /// public string ParentDeviceKey { get; private set; } /// @@ -21,7 +24,7 @@ namespace PepperDash.Core public eStreamDebuggingSetting DebugSetting { get; private set; } - private uint _DebugTimeoutMin; + private uint _DebugTimeoutInMs; private const uint _DefaultDebugTimeoutMin = 30; /// @@ -31,16 +34,7 @@ namespace PepperDash.Core { get { - return _DebugTimeoutMin; - } - } - - - private long _DebugTimeoutInMs - { - get - { - return DebugTimeoutMinutes * 60000; + return _DebugTimeoutInMs/60000; } } @@ -78,7 +72,7 @@ namespace PepperDash.Core /// public void SetDebuggingWithSpecificTimeout(eStreamDebuggingSetting setting, uint minutes) { - _DebugTimeoutMin = minutes; + _DebugTimeoutInMs = minutes * 60000; if (DebugExpiryPeriod != null) {