Compare commits

..

14 Commits

Author SHA1 Message Date
Neil Dorin
c55de61da9 fix: enhance COM port registration logging and update GenericSink class for input switching 2025-11-05 14:43:42 -07:00
Andrew Welker
42444ede0a Merge pull request #1354 from PepperDash/comport-controller-updates
fix: improve logging for COM port registration and configuration
2025-11-04 10:55:44 -05:00
jkdevito
3d50f5f5ac fix: improve logging for COM port registration and configuration 2025-11-04 09:37:14 -06:00
Nick Genovese
11d62aebe1 Merge pull request #1353 from PepperDash/mc-subscription-concurrency
fix: make subscriber functionality thread-safe
2025-11-03 17:26:31 -04:00
Andrew Welker
edc10a9c2a fix: make subscriberIds private & check for add failure 2025-11-03 15:19:17 -06:00
Andrew Welker
9be5823956 Merge branch 'main' into mc-subscription-concurrency 2025-11-03 15:18:45 -06:00
Andrew Welker
35371dde22 fix: make subscriber functionality thread-safe 2025-11-03 15:13:48 -06:00
Jason DeVito
d3ceb4d7e7 Merge pull request #1352 from PepperDash/theme-saving
fix: use correct overload for PostStatusMessage
2025-11-03 15:08:28 -06:00
Andrew Welker
a782b57100 fix: use correct overload for PostStatusMessage 2025-11-03 14:00:51 -06:00
Neil Dorin
1360de599f Merge pull request #1351 from PepperDash/stream-debugging
fix: centralize debug printing into extension methods
2025-11-03 12:19:05 -05:00
Andrew Welker
fd95f5fed1 docs: update XML docs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-03 11:11:00 -06:00
Andrew Welker
9426dff5df fix: copilot suggestions from PR review 2025-11-03 11:02:39 -06:00
Andrew Welker
0d083e63c6 Merge commit '314570d6c3d78c7a92a362f3ec3a4a06bdbebd28' into stream-debugging 2025-11-03 10:56:53 -06:00
Andrew Welker
6ed7c96ec7 fix: centralize debug printing into extension methods
Stream debugging now uses CrestronConsole instead of debug methods, so that the debug statements will be printed regardless of console debug level. This also means that comm debug statements will NOT be in the Crestron Error log or in the files created by the logging system
2025-11-03 10:53:21 -06:00
16 changed files with 375 additions and 339 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

@@ -278,7 +278,7 @@ namespace PepperDash.Core
if (shellStream.DataAvailable) if (shellStream.DataAvailable)
{ {
// empty the buffer if there is data // empty the buffer if there is data
string str = shellStream.Read(); shellStream.Read();
} }
shellStream.DataReceived += Stream_DataReceived; shellStream.DataReceived += Stream_DataReceived;
this.LogInformation("Connected"); this.LogInformation("Connected");
@@ -288,7 +288,6 @@ namespace PepperDash.Core
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)
{ {
@@ -335,7 +334,6 @@ namespace PepperDash.Core
} }
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;
@@ -439,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));
} }
@@ -507,11 +501,7 @@ namespace PepperDash.Core
{ {
if (client != null && shellStream != 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));
shellStream.Write(text); shellStream.Write(text);
shellStream.Flush(); shellStream.Flush();
@@ -526,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);
StopReconnectTimer(); StartReconnectTimer();
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -544,8 +534,7 @@ namespace PepperDash.Core
{ {
if (client != null && shellStream != 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));
shellStream.Write(bytes, 0, bytes.Length); shellStream.Write(bytes, 0, bytes.Length);
shellStream.Flush(); shellStream.Flush();
@@ -560,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);
StopReconnectTimer(); StartReconnectTimer();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -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,10 +434,7 @@ 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));
} }
@@ -456,8 +450,7 @@ namespace PepperDash.Core
{ {
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);
} }
@@ -484,8 +477,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 (_client != null) if (_client != null)
_client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }

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);

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
@@ -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
@@ -195,7 +192,7 @@ 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;
} }
@@ -205,49 +202,4 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public GenericCommMethodReceiveTextArgs() { } public GenericCommMethodReceiveTextArgs() { }
} }
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <param name="bytes"></param>
/// <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

@@ -33,7 +33,7 @@ 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);
@@ -58,7 +58,7 @@ 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");
} }
@@ -68,15 +68,13 @@ 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));
} }
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));
} }
} }
@@ -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,7 +100,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)); 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

@@ -1,7 +1,9 @@
using System; using System;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging; using PepperDash.Core.Logging;
using Serilog.Events; using Serilog.Events;
@@ -85,61 +87,29 @@ namespace PepperDash.Essentials.Core
{ {
if (Port == null) if (Port == null)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist."); this.LogInformation($"Configured {Port.Parent.GetType().Name}-comport-{Port.ID} for {Key} does not exist.");
return; return;
} }
// TODO [ ] - Remove commented out code once verified working
//if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
if (Port.Parent is GenericBase genericDevice && genericDevice.Registerable) if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
{ {
//this.LogInformation($"INFO: Attempting to register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID}"); var result = Port.Register();
var result = genericDevice.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success) if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{ {
this.LogError($"ERROR: Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})"); this.LogError($"Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
return; // false return;
} }
this.LogInformation($"Successfully registered {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
} }
var specResult = Port.SetComPortSpec(Spec); var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0) if (specResult != 0)
{ {
this.LogError($"ERROR: Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})"); this.LogError($"Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
return; return;
} }
//this.LogInformation($"INFO: Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})"); this.LogInformation($"Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
// TODO [ ] - Remove debug logging once verified working
// if (Port.Parent is CenIoCom102)
// {
// Port.PropertyChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: PropertyChanged Fired >>
// comPort-'{Port.ID}',
// Property Changed-'{e.Property}',
// Value Changed-'{e.Value}',
// deviceName-'{Port.DeviceName}',
// parentDevice-'{Port.ParentDevice}',
// parent-`{Port.Parent}`,
// online-`{Port.IsOnline}`,
// present-`{Port.Present}`,
// supportedBaudRates-'{Port.SupportedBaudRates}'");
// };
// Port.ExtendedInformationChanged += (s, e) =>
// {
// this.LogInformation($@"RegisterAndConfigureComPort: ExtendedInformationChanged Fired >>
// comPort-'{Port.ID}',
// {e.Protocol},
// {e.BaudRate},
// {e.Parity},
// {e.DataBits},
// {e.StopBits},
// HW Handshake-'{e.HardwareHandshakeSetting}',
// SW Handshake-'{e.SoftwareHandshakeSetting}'");
// };
// }
Port.SerialDataReceived += Port_SerialDataReceived; Port.SerialDataReceived += Port_SerialDataReceived;
} }
@@ -165,16 +135,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 +169,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 +181,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

@@ -98,7 +98,7 @@ namespace PepperDash.Essentials.Core.Routing
/// <param name="inputPort">The currently selected input port on the destination device.</param> /// <param name="inputPort">The currently selected input port on the destination device.</param>
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort) private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
{ {
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key); Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this, destination?.Key, inputPort?.Key);
if(inputPort == null) if(inputPort == null)
{ {

View File

@@ -134,7 +134,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
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)
{ {

View File

@@ -9,7 +9,7 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// <summary> /// <summary>
/// Represents a GenericSink /// Represents a GenericSink
/// </summary> /// </summary>
public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the GenericSink class /// Initializes a new instance of the GenericSink class
@@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Devices.Common.Generic
{ {
InputPorts = new RoutingPortCollection<RoutingInputPort>(); InputPorts = new RoutingPortCollection<RoutingInputPort>();
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this); var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this);
InputPorts.Add(inputPort); InputPorts.Add(inputPort);
} }
@@ -66,6 +66,15 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// Event fired when the current source changes /// Event fired when the current source changes
/// </summary> /// </summary>
public event SourceInfoChangeHandler CurrentSourceChange; public event SourceInfoChangeHandler CurrentSourceChange;
/// <inheritdoc />
public event InputChangedEventHandler InputChanged;
/// <inheritdoc />
public void ExecuteSwitch(object inputSelector)
{
throw new System.NotImplementedException();
}
} }
/// <summary> /// <summary>

View File

@@ -31,7 +31,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <remarks> /// <remarks>
/// Unsoliciited feedback from a device in a messenger will ONLY be sent to devices in this subscription list. When a client disconnects, it's ID will be removed from the collection. /// Unsoliciited feedback from a device in a messenger will ONLY be sent to devices in this subscription list. When a client disconnects, it's ID will be removed from the collection.
/// </remarks> /// </remarks>
protected HashSet<string> SubscriberIds = new HashSet<string>(); private readonly HashSet<string> subscriberIds = new HashSet<string>();
/// <summary>
/// Lock object for thread-safe access to SubscriberIds
/// </summary>
private readonly object _subscriberLock = new object();
private readonly List<string> _deviceInterfaces; private readonly List<string> _deviceInterfaces;
@@ -193,13 +198,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
return; return;
} }
if (SubscriberIds.Any(id => id == clientId)) lock (_subscriberLock)
{
if (!subscriberIds.Add(clientId))
{ {
this.LogVerbose("Client {clientId} already subscribed", clientId); this.LogVerbose("Client {clientId} already subscribed", clientId);
return; return;
} }
}
SubscriberIds.Add(clientId);
this.LogDebug("Client {clientId} subscribed", clientId); this.LogDebug("Client {clientId} subscribed", clientId);
} }
@@ -216,14 +222,22 @@ namespace PepperDash.Essentials.AppServer.Messengers
return; return;
} }
if (!SubscriberIds.Any(i => i == clientId)) bool wasSubscribed;
lock (_subscriberLock)
{
wasSubscribed = subscriberIds.Contains(clientId);
if (wasSubscribed)
{
subscriberIds.Remove(clientId);
}
}
if (!wasSubscribed)
{ {
this.LogVerbose("Client with ID {clientId} is not subscribed", clientId); this.LogVerbose("Client with ID {clientId} is not subscribed", clientId);
return; return;
} }
SubscriberIds.RemoveWhere((i) => i == clientId);
this.LogInformation("Client with ID {clientId} unsubscribed", clientId); this.LogInformation("Client with ID {clientId} unsubscribed", clientId);
} }
@@ -312,7 +326,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
// If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties // If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties
if (string.IsNullOrEmpty(clientId)) if (string.IsNullOrEmpty(clientId))
{ {
foreach (var client in SubscriberIds) // Create a snapshot of subscribers to avoid collection modification during iteration
List<string> subscriberSnapshot;
lock (_subscriberLock)
{
subscriberSnapshot = new List<string>(subscriberIds);
}
foreach (var client in subscriberSnapshot)
{ {
AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content }); AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content });
} }

View File

@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Touchpanel
this.LogInformation("Setting theme to {theme}", theme.Value); this.LogInformation("Setting theme to {theme}", theme.Value);
_tpDevice.UpdateTheme(theme.Value); _tpDevice.UpdateTheme(theme.Value);
PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), id); PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), clientId: id);
}); });
} }
} }