Merge pull request #1313 from PepperDash/temp-to-dev

Temp to dev
This commit is contained in:
Neil Dorin
2025-08-15 09:34:41 -06:00
committed by GitHub
18 changed files with 649 additions and 303 deletions

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>2.4.0-local</Version>
<Version>2.12.1-local</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<Authors>PepperDash Technology</Authors>
<Company>PepperDash Technology</Company>

View File

@@ -74,6 +74,10 @@ namespace PepperDash.Core
/// <summary>
/// Secure TCP/IP
/// </summary>
SecureTcpIp
SecureTcpIp,
/// <summary>
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
/// </summary>
ComBridge
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
@@ -19,7 +20,6 @@ using Serilog.Templates;
namespace PepperDash.Core
{
/// <summary>
/// Contains debug commands for use in various situations
/// </summary>
public static class Debug
{
@@ -272,6 +272,9 @@ namespace PepperDash.Core
if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
{
CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, (int)LogEventLevel.Information);
return LogEventLevel.Information;
}
@@ -346,13 +349,13 @@ namespace PepperDash.Core
if (levelString.Trim() == "?")
{
CrestronConsole.ConsoleCommandResponse(
$@"Used to set the minimum level of debug messages to be printed to the console:
{_logLevels[0]} = 0
{_logLevels[1]} = 1
{_logLevels[2]} = 2
{_logLevels[3]} = 3
{_logLevels[4]} = 4
{_logLevels[5]} = 5");
"Used to set the minimum level of debug messages to be printed to the console:\r\n" +
$"{_logLevels[0]} = 0\r\n" +
$"{_logLevels[1]} = 1\r\n" +
$"{_logLevels[2]} = 2\r\n" +
$"{_logLevels[3]} = 3\r\n" +
$"{_logLevels[4]} = 4\r\n" +
$"{_logLevels[5]} = 5");
return;
}
@@ -373,7 +376,7 @@ namespace PepperDash.Core
return;
}
if (Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
if (Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum))
{
SetDebugLevel(levelEnum);
return;

View File

@@ -1,5 +1,5 @@
using Serilog.Events;
using System;
using System;
using Serilog.Events;
using Log = PepperDash.Core.Debug;
namespace PepperDash.Core.Logging
@@ -11,7 +11,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogException(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(ex, message, device, args);
Log.LogMessage(ex, message, device: device, args);
}
/// <summary>
@@ -19,7 +19,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogVerbose(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Verbose, ex, message, device, args);
Log.LogVerbose(ex, device, message, args);
}
/// <summary>
@@ -27,7 +27,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogVerbose(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Verbose, device, message, args);
Log.LogVerbose(device, message, args);
}
/// <summary>
@@ -35,7 +35,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogDebug(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Debug, ex, message, device, args);
Log.LogDebug(ex, device, message, args);
}
/// <summary>
@@ -43,7 +43,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogDebug(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Debug, device, message, args);
Log.LogDebug(device, message, args);
}
/// <summary>
@@ -51,7 +51,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogInformation(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Information, ex, message, device, args);
Log.LogInformation(ex, device, message, args);
}
/// <summary>
@@ -59,7 +59,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogInformation(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Information, device, message, args);
Log.LogInformation(device, message, args);
}
/// <summary>
@@ -67,7 +67,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogWarning(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Warning, ex, message, device, args);
Log.LogWarning(ex, device, message, args);
}
/// <summary>
@@ -75,7 +75,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogWarning(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Warning, device, message, args);
Log.LogWarning(device, message, args);
}
/// <summary>
@@ -83,7 +83,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogError(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Error, ex, message, device, args);
Log.LogError(ex, device, message, args);
}
/// <summary>
@@ -91,7 +91,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogError(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Error, device, message, args);
Log.LogError(device, message, args);
}
/// <summary>
@@ -99,7 +99,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogFatal(this IKeyed device, Exception ex, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Fatal, ex, message, device, args);
Log.LogFatal(ex, device, message, args);
}
/// <summary>
@@ -107,7 +107,7 @@ namespace PepperDash.Core.Logging
/// </summary>
public static void LogFatal(this IKeyed device, string message, params object[] args)
{
Log.LogMessage(LogEventLevel.Fatal, device, message, args);
Log.LogFatal(device, message, args);
}
}
}

View File

@@ -7,6 +7,13 @@ namespace PepperDash.Essentials.Core.Bridges
/// </summary>
public interface IBridgeAdvanced
{
/// <summary>
/// Links the bridge to the API using the provided trilist, join start, join map key, and bridge.
/// </summary>
/// <param name="trilist">The trilist to link to.</param>
/// <param name="joinStart">The starting join number.</param>
/// <param name="joinMapKey">The key for the join map.</param>
/// <param name="bridge">The EISC API bridge.</param>
void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Collections.Generic;
using System.Text;
using Crestron.SimplSharp.CrestronSockets;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Implements IBasicCommunication and sends all communication through an EISC
/// </summary>
[Description("Generic communication wrapper class for any IBasicCommunication type")]
public class CommBridge : EssentialsBridgeableDevice, IBasicCommunication
{
private EiscApiAdvanced eisc;
private IBasicCommunicationJoinMap joinMap;
/// <summary>
/// Event triggered when text is received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Event triggered when bytes are received through the communication bridge.
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Indicates whether the communication bridge is currently connected.
/// </summary>
public bool IsConnected { get; private set; }
/// <summary>
/// Initializes a new instance of the <see cref="CommBridge"/> class.
/// </summary>
/// <param name="key">The unique key for the communication bridge.</param>
/// <param name="name">The display name for the communication bridge.</param>
public CommBridge(string key, string name)
: base(key, name)
{
}
/// <summary>
/// Sends a byte array through the communication bridge.
/// </summary>
/// <param name="bytes">The byte array to send.</param>
public void SendBytes(byte[] bytes)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send bytes.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, Encoding.ASCII.GetString(bytes, 0, bytes.Length));
}
/// <summary>
/// Sends a text string through the communication bridge.
/// </summary>
/// <param name="text">The text string to send.</param>
public void SendText(string text)
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot send text.");
return;
}
eisc.Eisc.SetString(joinMap.SendText.JoinNumber, text);
}
/// <summary>
/// Initiates a connection through the communication bridge.
/// </summary>
public void Connect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot connect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, true);
}
/// <summary>
/// Terminates the connection through the communication bridge.
/// </summary>
public void Disconnect()
{
if (eisc == null)
{
this.LogWarning("EISC is null, cannot disconnect.");
return;
}
eisc.Eisc.SetBool(joinMap.Connect.JoinNumber, false);
}
/// <inheritdoc />
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{
joinMap = new IBasicCommunicationJoinMap(joinStart);
var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey);
if (!string.IsNullOrEmpty(joinMapSerialized))
joinMap = JsonConvert.DeserializeObject<IBasicCommunicationJoinMap>(joinMapSerialized);
if (bridge != null)
{
bridge.AddJoinMap(Key, joinMap);
}
else
{
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
}
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X"));
eisc = bridge;
trilist.SetBoolSigAction(joinMap.Connected.JoinNumber, (b) => IsConnected = b);
trilist.SetStringSigAction(joinMap.TextReceived.JoinNumber, (s) =>
{
TextReceived?.Invoke(this, new GenericCommMethodReceiveTextArgs(s));
BytesReceived?.Invoke(this, new GenericCommMethodReceiveBytesArgs(Encoding.ASCII.GetBytes(s)));
});
}
}
}

View File

@@ -1,11 +1,9 @@
using System;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DM;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
@@ -38,9 +36,9 @@ namespace PepperDash.Essentials.Core
/// Returns a comm method of either com port, TCP, SSH, and puts this into the DeviceManager
/// </summary>
/// <param name="deviceConfig">The Device config object</param>
/// <summary>
/// CreateCommForDevice method
/// </summary>
/// <summary>
/// CreateCommForDevice method
/// </summary>
public static IBasicCommunication CreateCommForDevice(DeviceConfig deviceConfig)
{
EssentialsControlPropertiesConfig controlConfig = GetControlPropertiesConfig(deviceConfig);
@@ -56,35 +54,38 @@ namespace PepperDash.Essentials.Core
case eControlMethod.Com:
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break;
case eControlMethod.ComBridge:
comm = new CommBridge(deviceConfig.Key + "-simpl", deviceConfig.Name + " Simpl");
break;
case eControlMethod.Cec:
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
break;
case eControlMethod.IR:
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if(ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Udp:
{
var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
comm = udp;
break;
}
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if (ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
break;
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
break;
}
case eControlMethod.Udp:
{
var udp = new GenericUdpServer(deviceConfig.Key + "-udp", c.Address, c.Port, c.BufferSize);
comm = udp;
break;
}
case eControlMethod.Telnet:
break;
case eControlMethod.SecureTcpIp:
@@ -98,7 +99,7 @@ namespace PepperDash.Essentials.Core
}
default:
break;
}
}
}
catch (Exception e)
{
@@ -107,15 +108,14 @@ namespace PepperDash.Essentials.Core
}
// put it in the device manager if it's the right flavor
var comDev = comm as Device;
if (comDev != null)
if (comm is Device comDev)
DeviceManager.AddDevice(comDev);
return comm;
}
/// <summary>
/// GetComPort method
/// </summary>
/// <summary>
/// GetComPort method
/// </summary>
public static ComPort GetComPort(EssentialsControlPropertiesConfig config)
{
var comPar = config.ComParams;
@@ -126,74 +126,74 @@ namespace PepperDash.Essentials.Core
return null;
}
/// <summary>
/// Gets an ICec port from a RoutingInput or RoutingOutput on a device
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
/// <summary>
/// GetCecPort method
/// </summary>
public static ICec GetCecPort(ControlPropertiesConfig config)
{
try
{
var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
/// <summary>
/// Gets an ICec port from a RoutingInput or RoutingOutput on a device
/// </summary>
/// <param name="config"></param>
/// <returns></returns>
/// <summary>
/// GetCecPort method
/// </summary>
public static ICec GetCecPort(ControlPropertiesConfig config)
{
try
{
var dev = DeviceManager.GetDeviceForKey(config.ControlPortDevKey);
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port"
: "found in device manager, attempting to get cec port");
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: device '{0}' {1}", config.ControlPortDevKey, dev == null
? "is not valid, failed to get cec port"
: "found in device manager, attempting to get cec port");
if (dev == null)
return null;
if (dev == null)
return null;
if (String.IsNullOrEmpty(config.ControlPortName))
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey);
return null;
}
if (String.IsNullOrEmpty(config.ControlPortName))
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: '{0}' - Configuration missing 'ControlPortName'", config.ControlPortDevKey);
return null;
}
var inputsOutputs = dev as IRoutingInputsOutputs;
if (inputsOutputs == null)
{
var inputsOutputs = dev as IRoutingInputsOutputs;
if (inputsOutputs == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not support IRoutingInputsOutputs, failed to get CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
return null;
}
var inputPort = inputsOutputs.InputPorts[config.ControlPortName];
if (inputPort != null && inputPort.Port is ICec)
if (inputPort != null && inputPort.Port is ICec)
return inputPort.Port as ICec;
var outputPort = inputsOutputs.OutputPorts[config.ControlPortName];
if (outputPort != null && outputPort.Port is ICec)
return outputPort.Port as ICec;
}
catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace);
if (ex.InnerException != null)
Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException);
}
var outputPort = inputsOutputs.OutputPorts[config.ControlPortName];
if (outputPort != null && outputPort.Port is ICec)
return outputPort.Port as ICec;
}
catch (Exception ex)
{
Debug.LogMessage(LogEventLevel.Debug, "GetCecPort Exception Message: {0}", ex.Message);
Debug.LogMessage(LogEventLevel.Verbose, "GetCecPort Exception StackTrace: {0}", ex.StackTrace);
if (ex.InnerException != null)
Debug.LogMessage(LogEventLevel.Information, "GetCecPort Exception InnerException: {0}", ex.InnerException);
}
Debug.LogMessage(LogEventLevel.Information, "GetCecPort: Device '{0}' does not have a CEC port called '{1}'",
config.ControlPortDevKey, config.ControlPortName);
return null;
}
}
/// <summary>
/// Helper to grab the IComPorts device for this PortDeviceKey. Key "controlSystem" will
/// return the ControlSystem object from the Global class.
/// </summary>
/// <returns>IComPorts device or null if the device is not found or does not implement IComPorts</returns>
/// <summary>
/// GetIComPortsDeviceFromManagedDevice method
/// </summary>
/// <summary>
/// GetIComPortsDeviceFromManagedDevice method
/// </summary>
public static IComPorts GetIComPortsDeviceFromManagedDevice(string ComPortDevKey)
{
if ((ComPortDevKey.Equals("controlSystem", System.StringComparison.OrdinalIgnoreCase)
@@ -210,81 +210,81 @@ namespace PepperDash.Essentials.Core
}
}
/// <summary>
/// Represents a EssentialsControlPropertiesConfig
/// </summary>
public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig
{
/// <summary>
/// Represents a EssentialsControlPropertiesConfig
/// </summary>
public class EssentialsControlPropertiesConfig :
ControlPropertiesConfig
{
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; }
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(ComSpecJsonConverter))]
public ComPort.ComPortSpec? ComParams { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; }
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
public string CresnetId { get; set; }
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
[JsonIgnore]
public uint CresnetIdInt
{
get
{
try
{
return Convert.ToUInt32(CresnetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
}
}
}
/// <summary>
/// Attempts to provide uint conversion of string CresnetId
/// </summary>
[JsonIgnore]
public uint CresnetIdInt
{
get
{
try
{
return Convert.ToUInt32(CresnetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to convert Cresnet ID: {0} to hex. Error:\n{1}", CresnetId));
}
}
}
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the InfinetId
/// </summary>
public string InfinetId { get; set; }
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the InfinetId
/// </summary>
public string InfinetId { get; set; }
/// <summary>
/// Attepmts to provide uiont conversion of string InifinetId
/// </summary>
[JsonIgnore]
public uint InfinetIdInt
{
get
{
try
{
return Convert.ToUInt32(InfinetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
}
}
}
}
/// <summary>
/// Attepmts to provide uiont conversion of string InifinetId
/// </summary>
[JsonIgnore]
public uint InfinetIdInt
{
get
{
try
{
return Convert.ToUInt32(InfinetId, 16);
}
catch (Exception)
{
throw new FormatException(string.Format("ERROR:Unable to conver Infinet ID: {0} to hex. Error:\n{1}", InfinetId));
}
}
}
}
/// <summary>
/// Represents a IrControlSpec
/// </summary>
public class IrControlSpec
{
/// <summary>
/// Gets or sets the PortDeviceKey
/// </summary>
public string PortDeviceKey { get; set; }
/// <summary>
/// Gets or sets the PortNumber
/// </summary>
public uint PortNumber { get; set; }
/// <summary>
/// Gets or sets the File
/// </summary>
public string File { get; set; }
}
/// <summary>
/// Represents a IrControlSpec
/// </summary>
public class IrControlSpec
{
/// <summary>
/// Gets or sets the PortDeviceKey
/// </summary>
public string PortDeviceKey { get; set; }
/// <summary>
/// Gets or sets the PortNumber
/// </summary>
public uint PortNumber { get; set; }
/// <summary>
/// Gets or sets the File
/// </summary>
public string File { get; set; }
}
}

View File

@@ -0,0 +1,23 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Represents the base properties for a streaming device.
/// </summary>
public class BaseStreamingDeviceProperties
{
/// <summary>
/// The multicast video address for the streaming device.
/// </summary>
[JsonProperty("multicastVideoAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastVideoAddress { get; set; }
/// <summary>
/// The multicast audio address for the streaming device.
/// </summary>
[JsonProperty("multicastAudioAddress", NullValueHandling = NullValueHandling.Ignore)]
public string MulticastAudioAddress { get; set; }
}
}

View File

@@ -475,9 +475,9 @@ namespace PepperDash.Essentials.Core
if (String.IsNullOrEmpty(s) || s.Contains("?"))
{
CrestronConsole.ConsoleCommandResponse(
@"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]
{deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use
timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes");
"SETDEVICESTREAMDEBUG [{deviceKey}] [OFF |TX | RX | BOTH] [timeOutInMinutes]\r\n" +
" {deviceKey} [OFF | TX | RX | BOTH] - Device to set stream debugging on, and which setting to use\r\n" +
" timeOutInMinutes - Set timeout for stream debugging. Default is 30 minutes");
return;
}

View File

@@ -3,16 +3,29 @@ using PepperDash.Essentials.Core.Bridges;
namespace PepperDash.Essentials.Core
{
public abstract class EssentialsBridgeableDevice:EssentialsDevice, IBridgeAdvanced
/// <summary>
/// Base class for devices that can be bridged to an EISC API.
/// </summary>
public abstract class EssentialsBridgeableDevice : EssentialsDevice, IBridgeAdvanced
{
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsBridgeableDevice"/> class with the specified key.
/// </summary>
/// <param name="key">The unique key for the device.</param>
protected EssentialsBridgeableDevice(string key) : base(key)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsBridgeableDevice"/> class with the specified key and name.
/// </summary>
/// <param name="key">The unique key for the device.</param>
/// <param name="name">The display name for the device.</param>
protected EssentialsBridgeableDevice(string key, string name) : base(key, name)
{
}
/// <inheritdoc />
public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge);
}
}

View File

@@ -244,6 +244,20 @@ namespace PepperDash.Essentials.Core
/// </summary>
[JsonProperty("type")]
public eRoutingSignalType Type { get; set; }
/// <summary>
/// Key for a destination list item. If BOTH SourceListItemKey AND DestinationListItemKey are defined,
/// then the direct route method should be used.
/// </summary>
[JsonProperty("destinationListItemKey", NullValueHandling = NullValueHandling.Ignore)]
public string DestinationListItemKey { get; set; }
/// <summary>
/// Key for a source list item. If BOTH SourceListItemKey AND DestinationListItemKey are defined,
/// then the direct route method should be used.
/// </summary>
[JsonProperty("sourceListItemKey", NullValueHandling = NullValueHandling.Ignore)]
public string SourceListItemKey { get; set; }
}
/// <summary>

View File

@@ -135,7 +135,7 @@ namespace PepperDash.Essentials.Core
{
if (FactoryMethods.ContainsKey(typeName))
{
Debug.LogInformation("Unable to add type: '{typeName}'. Already exists in DeviceFactory", typeName);
Debug.LogInformation("Unable to add type: '{typeName}'. Already exists in DeviceFactory", typeName);
return;
}
@@ -217,7 +217,8 @@ namespace PepperDash.Essentials.Core
}
catch (Exception ex)
{
Debug.LogError(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message);
Debug.LogError(ex, "Exception occurred while creating device {key}: {message}", dc.Key, ex.Message);
Debug.LogDebug(ex, "Exception details: {stackTrace}", ex.StackTrace);
return null;
}
}
@@ -249,9 +250,9 @@ namespace PepperDash.Essentials.Core
}
CrestronConsole.ConsoleCommandResponse(
@"Type: '{0}'
Type: '{1}'
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
"Type: '{0}'\r\n" +
" Type: '{1}'\r\n" +
" Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
}
}

View File

@@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Core.Routing
{
@@ -25,5 +25,19 @@ namespace PepperDash.Essentials.Core.Routing
/// </summary>
Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; }
/// <summary>
/// Event raised when the current sources change.
/// </summary>
event EventHandler CurrentSourcesChanged;
/// <summary>
/// Sets the current source for a specific signal type.
/// This method updates the current source for the specified signal type and notifies any subscribers of the change.
/// </summary>
/// <param name="signalType">The signal type to update.</param>
/// <param name="sourceListKey">The key for the source list.</param>
/// <param name="sourceListItem">The source list item to set as the current source.</param>
void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem);
}
}

View File

@@ -22,38 +22,38 @@ namespace PepperDash.Essentials.Core.Config
/// The key of the source device.
/// </summary>
public string SourceKey { get; set; }
/// <summary>
/// The key of the source card (if applicable, e.g., in a modular chassis).
/// </summary>
/// <summary>
/// The key of the source card (if applicable, e.g., in a modular chassis).
/// </summary>
public string SourceCard { get; set; }
/// <summary>
/// The key of the source output port, used for routing configurations.
/// </summary>
/// <summary>
/// The key of the source output port, used for routing configurations.
/// </summary>
public string SourcePort { get; set; }
/// <summary>
/// Gets or sets the DestinationKey
/// </summary>
/// <summary>
/// Gets or sets the DestinationKey
/// </summary>
public string DestinationKey { get; set; }
/// <summary>
/// Gets or sets the DestinationCard
/// </summary>
/// <summary>
/// Gets or sets the DestinationCard
/// </summary>
public string DestinationCard { get; set; }
/// <summary>
/// Gets or sets the DestinationPort
/// </summary>
/// <summary>
/// Gets or sets the DestinationPort
/// </summary>
public string DestinationPort { get; set; }
/// <summary>
/// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations.
/// </summary>
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eRoutingSignalType? OverrideType { get; set; }
/// <summary>
/// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations.
/// </summary>
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eRoutingSignalType? OverrideType { get; set; }
/// <summary>
/// Returns the appropriate tie line for either a card-based device or
@@ -62,40 +62,39 @@ namespace PepperDash.Essentials.Core.Config
/// <returns>null if config data does not match ports, cards or devices</returns>
public TieLine GetTieLine()
{
Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, this);
Debug.LogInformation("Build TieLine: {config}", ToString());
// Get the source device
var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs;
if (sourceDev == null)
if (!(DeviceManager.GetDeviceForKey(SourceKey) is IRoutingOutputs sourceDev))
{
LogError("Routable source not found");
return null;
}
// Get the destination device
var destDev = DeviceManager.GetDeviceForKey(DestinationKey) as IRoutingInputs;
if (destDev == null)
if (!(DeviceManager.GetDeviceForKey(DestinationKey) is IRoutingInputs destDev))
{
LogError("Routable destination not found");
return null;
}
//Get the source port
var sourceOutputPort = sourceDev.OutputPorts[SourcePort];
//Get the source port
var sourceOutputPort = sourceDev.OutputPorts[SourcePort];
if (sourceOutputPort == null)
if (sourceOutputPort == null)
{
LogError("Source does not contain port");
return null;
}
//Get the Destination port
var destinationInputPort = destDev.InputPorts[DestinationPort];
//Get the Destination port
var destinationInputPort = destDev.InputPorts[DestinationPort];
if (destinationInputPort == null)
{
LogError("Destination does not contain port");
return null;
}
if (destinationInputPort == null)
{
LogError("Destination does not contain port");
return null;
}
return new TieLine(sourceOutputPort, destinationInputPort, OverrideType);
}
@@ -104,9 +103,9 @@ namespace PepperDash.Essentials.Core.Config
/// Logs an error message related to creating this tie line configuration.
/// </summary>
/// <param name="msg">The specific error message.</param>
void LogError(string msg)
private void LogError(string msg)
{
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
Debug.LogError("Cannot create tie line: {message}", msg);
}
/// <summary>
@@ -115,8 +114,7 @@ namespace PepperDash.Essentials.Core.Config
/// <returns>A string describing the source and destination of the configured tie line.</returns>
public override string ToString()
{
return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort,
DestinationKey, DestinationCard, DestinationPort);
return $"{SourceKey}.{SourceCard}.{SourcePort} --> {DestinationKey}.{DestinationCard}.{DestinationPort}";
}
}
}

View File

@@ -5,6 +5,7 @@ using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
@@ -52,9 +53,9 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// </summary>
public event SourceInfoChangeHandler CurrentSourceChange;
/// <summary>
/// Gets or sets the CurrentSourceInfoKey
/// </summary>
/// <summary>
/// Gets or sets the CurrentSourceInfoKey
/// </summary>
public string CurrentSourceInfoKey { get; set; }
/// <summary>
@@ -89,29 +90,32 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// <inheritdoc/>
public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; private set; }
/// <inheritdoc />
public event EventHandler CurrentSourcesChanged;
/// <summary>
/// Gets feedback indicating whether the display is currently cooling down after being powered off.
/// </summary>
public BoolFeedback IsCoolingDownFeedback { get; protected set; }
/// <summary>
/// Gets or sets the IsWarmingUpFeedback
/// </summary>
/// <summary>
/// Gets or sets the IsWarmingUpFeedback
/// </summary>
public BoolFeedback IsWarmingUpFeedback { get; private set; }
/// <summary>
/// Gets or sets the UsageTracker
/// </summary>
/// <summary>
/// Gets or sets the UsageTracker
/// </summary>
public UsageTracking UsageTracker { get; set; }
/// <summary>
/// Gets or sets the WarmupTime
/// </summary>
/// <summary>
/// Gets or sets the WarmupTime
/// </summary>
public uint WarmupTime { get; set; }
/// <summary>
/// Gets or sets the CooldownTime
/// </summary>
/// <summary>
/// Gets or sets the CooldownTime
/// </summary>
public uint CooldownTime { get; set; }
/// <summary>
@@ -189,7 +193,7 @@ namespace PepperDash.Essentials.Devices.Common.Displays
/// <summary>
/// Gets the collection of feedback objects for this display device.
/// </summary>
/// <inheritdoc />
/// <inheritdoc />
public virtual FeedbackCollection<Feedback> Feedbacks
{
get
@@ -378,6 +382,53 @@ namespace PepperDash.Essentials.Devices.Common.Displays
volumeDisplayWithFeedback.MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]);
}
/// <inheritdoc />
public virtual void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
{
foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType)))
{
var flagValue = Convert.ToInt32(type);
// Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag).
// (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set.
if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0)
{
this.LogDebug("Skipping {type}", type);
continue;
}
this.LogDebug("setting {type}", type);
if (signalType.HasFlag(type))
{
UpdateCurrentSources(type, sourceListKey, sourceListItem);
}
}
// Raise the CurrentSourcesChanged event
CurrentSourcesChanged?.Invoke(this, EventArgs.Empty);
}
private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
{
if (CurrentSources.ContainsKey(signalType))
{
CurrentSources[signalType] = sourceListItem;
}
else
{
CurrentSources.Add(signalType, sourceListItem);
}
// Update the current source key for the specified signal type
if (CurrentSourceKeys.ContainsKey(signalType))
{
CurrentSourceKeys[signalType] = sourceListKey;
}
else
{
CurrentSourceKeys.Add(signalType, sourceListKey);
}
}
}
/// <summary>

View File

@@ -0,0 +1,77 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Routing;
namespace PepperDash.Essentials.AppServer.Messengers
{
/// <summary>
/// Represents a IHasCurrentSourceInfoMessenger
/// </summary>
public class CurrentSourcesMessenger : MessengerBase
{
private readonly ICurrentSources sourceDevice;
/// <summary>
/// Initializes a new instance of the <see cref="CurrentSourcesMessenger"/> class.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="messagePath">The message path.</param>
/// <param name="device">The device.</param>
public CurrentSourcesMessenger(string key, string messagePath, ICurrentSources device) : base(key, messagePath, device as IKeyName)
{
sourceDevice = device;
}
/// <summary>
/// Registers the actions for the messenger.
/// </summary>
protected override void RegisterActions()
{
base.RegisterActions();
AddAction("/fullStatus", (id, content) =>
{
var message = new CurrentSourcesStateMessage
{
CurrentSourceKeys = sourceDevice.CurrentSourceKeys,
CurrentSources = sourceDevice.CurrentSources
};
PostStatusMessage(message);
});
sourceDevice.CurrentSourcesChanged += (sender, e) =>
{
PostStatusMessage(JToken.FromObject(new
{
currentSourceKeys = sourceDevice.CurrentSourceKeys,
currentSources = sourceDevice.CurrentSources
}));
};
}
}
/// <summary>
/// Represents a CurrentSourcesStateMessage
/// </summary>
public class CurrentSourcesStateMessage : DeviceStateMessageBase
{
/// <summary>
/// Gets or sets the CurrentSourceKey
/// </summary>
[JsonProperty("currentSourceKeys", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; set; }
/// <summary>
/// Gets or sets the CurrentSource
/// </summary>
[JsonProperty("currentSources")]
public Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; set; }
}
}

View File

@@ -54,16 +54,18 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// </summary>
public class CurrentSourceStateMessage : DeviceStateMessageBase
{
[JsonProperty("currentSourceKey", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the CurrentSourceKey
/// </summary>
[JsonProperty("currentSourceKey", NullValueHandling = NullValueHandling.Ignore)]
public string CurrentSourceKey { get; set; }
[JsonProperty("currentSource")]
/// <summary>
/// Gets or sets the CurrentSource
/// </summary>
[JsonProperty("currentSource")]
public SourceListItem CurrentSource { get; set; }
}
}

View File

@@ -1,4 +1,10 @@
using Crestron.SimplSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.Net.Http;
using Crestron.SimplSharp.WebScripting;
@@ -30,12 +36,6 @@ using PepperDash.Essentials.RoomBridges;
using PepperDash.Essentials.Services;
using PepperDash.Essentials.WebApiHandlers;
using PepperDash.Essentials.WebSocketServer;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WebSocketSharp;
namespace PepperDash.Essentials
@@ -582,7 +582,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding ISetTopBoxControlMessenger for {deviceKey}"
);
);
var messenger = new ISetTopBoxControlsMessenger(
$"{device.Key}-stb-{Key}",
@@ -599,7 +599,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding IChannelMessenger for {deviceKey}", device.Key
);
);
var messenger = new IChannelMessenger(
$"{device.Key}-channel-{Key}",
@@ -614,7 +614,7 @@ namespace PepperDash.Essentials
if (device is IColor colorDevice)
{
this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key);
var messenger = new IColorMessenger(
$"{device.Key}-color-{Key}",
@@ -629,7 +629,7 @@ namespace PepperDash.Essentials
if (device is IDPad dPadDevice)
{
this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key);
var messenger = new IDPadMessenger(
$"{device.Key}-dPad-{Key}",
@@ -644,7 +644,7 @@ namespace PepperDash.Essentials
if (device is INumericKeypad nkDevice)
{
this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key);
var messenger = new INumericKeypadMessenger(
$"{device.Key}-numericKeypad-{Key}",
@@ -659,7 +659,7 @@ namespace PepperDash.Essentials
if (device is IHasPowerControl pcDevice)
{
this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key);
this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key);
var messenger = new IHasPowerMessenger(
$"{device.Key}-powerControl-{Key}",
@@ -693,7 +693,7 @@ namespace PepperDash.Essentials
{
this.LogVerbose(
"Adding ITransportMessenger for {deviceKey}", device.Key
);
);
var messenger = new ITransportMessenger(
$"{device.Key}-transport-{Key}",
@@ -721,6 +721,17 @@ namespace PepperDash.Essentials
messengerAdded = true;
}
if (device is ICurrentSources currentSources)
{
this.LogVerbose("Adding CurrentSourcesMessenger for {deviceKey}", device.Key);
var messenger = new CurrentSourcesMessenger($"{device.Key}-currentSources-{Key}", $"/device/{device.Key}", currentSources);
AddDefaultDeviceMessenger(messenger);
messengerAdded = true;
}
if (device is ISwitchedOutput switchedDevice)
{
this.LogVerbose(
@@ -1625,15 +1636,14 @@ namespace PepperDash.Essentials
if (Config.EnableApiServer)
{
CrestronConsole.ConsoleCommandResponse(
@"Mobile Control Edge Server API Information:
Server address: {0}
System Name: {1}
System URL: {2}
System UUID: {3}
System User code: {4}
Connected?: {5}
Seconds Since Last Ack: {6}",
"Mobile Control Edge Server API Information:\r\n\r\n" +
"\tServer address: {0}\r\n" +
"\tSystem Name: {1}\r\n" +
"\tSystem URL: {2}\r\n" +
"\tSystem UUID: {3}\r\n" +
"\tSystem User code: {4}\r\n" +
"\tConnected?: {5}\r\n" +
"\tSeconds Since Last Ack: {6}\r\n",
url,
name,
ConfigReader.ConfigObject.SystemUrl,
@@ -1646,10 +1656,8 @@ namespace PepperDash.Essentials
else
{
CrestronConsole.ConsoleCommandResponse(
@"
Mobile Control Edge Server API Information:
Not Enabled in Config.
"
"\r\nMobile Control Edge Server API Information:\r\n" +
" Not Enabled in Config.\r\n"
);
}
@@ -1660,21 +1668,17 @@ Mobile Control Edge Server API Information:
)
{
CrestronConsole.ConsoleCommandResponse(
@"
Mobile Control Direct Server Information:
User App URL: {0}
Server port: {1}
",
"\r\nMobile Control Direct Server Information:\r\n" +
" User App URL: {0}\r\n" +
" Server port: {1}\r\n",
string.Format("{0}[insert_client_token]", _directServer.UserAppUrlPrefix),
_directServer.Port
);
CrestronConsole.ConsoleCommandResponse(
@"
UI Client Info:
Tokens Defined: {0}
Clients Connected: {1}
",
"\r\n UI Client Info:\r\n" +
" Tokens Defined: {0}\r\n" +
" Clients Connected: {1}\r\n",
_directServer.UiClients.Count,
_directServer.ConnectedUiClientsCount
);
@@ -1692,15 +1696,13 @@ Mobile Control Direct Server Information:
}
CrestronConsole.ConsoleCommandResponse(
@"
Client {0}:
Room Key: {1}
Touchpanel Key: {6}
Token: {2}
Client URL: {3}
Connected: {4}
Duration: {5}
",
"\r\nClient {0}:\r\n" +
"Room Key: {1}\r\n" +
"Touchpanel Key: {6}\r\n" +
"Token: {2}\r\n" +
"Client URL: {3}\r\n" +
"Connected: {4}\r\n" +
"Duration: {5}\r\n",
clientNo,
clientContext.Value.Token.RoomKey,
clientContext.Key,
@@ -1715,9 +1717,8 @@ Duration: {5}
else
{
CrestronConsole.ConsoleCommandResponse(
@"
Mobile Control Direct Server Infromation:
Not Enabled in Config."
"\r\nMobile Control Direct Server Information:\r\n" +
" Not Enabled in Config.\r\n"
);
}
}
@@ -2309,7 +2310,7 @@ Mobile Control Direct Server Infromation:
{
this.LogInformation("-- Warning: Incoming message has no registered handler {type}", message.Type);
break;
}
}
foreach (var handler in handlers)
{