mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-15 21:44:54 +00:00
Compare commits
10 Commits
appdebug-c
...
logging-im
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed2b90d8e7 | ||
|
|
2368f0c8cc | ||
|
|
be58a0bc29 | ||
|
|
8406f69e0d | ||
|
|
116d83394a | ||
|
|
9b8e452eb4 | ||
|
|
c9d86bd5dd | ||
|
|
b2b257020f | ||
|
|
6f58e18d14 | ||
|
|
60fc0298ec |
@@ -3,6 +3,7 @@ using Crestron.SimplSharp.CrestronDataStore;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.CrestronLogger;
|
||||
using Newtonsoft.Json;
|
||||
using Org.BouncyCastle.Crypto.Prng;
|
||||
using PepperDash.Core.Logging;
|
||||
using Serilog;
|
||||
using Serilog.Context;
|
||||
@@ -13,7 +14,9 @@ using Serilog.Formatting.Json;
|
||||
using Serilog.Templates;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace PepperDash.Core
|
||||
@@ -25,8 +28,9 @@ namespace PepperDash.Core
|
||||
{
|
||||
private static readonly string LevelStoreKey = "ConsoleDebugLevel";
|
||||
private static readonly string WebSocketLevelStoreKey = "WebsocketDebugLevel";
|
||||
private static readonly string ErrorLogLevelStoreKey = "ErrorLogDebugLevel";
|
||||
private static readonly string Error_logLevelstoreKey = "ErrorLogDebugLevel";
|
||||
private static readonly string FileLevelStoreKey = "FileDebugLevel";
|
||||
private static readonly string DeviceKeysStoreKey = "DeviceKeys";
|
||||
|
||||
private static readonly Dictionary<uint, LogEventLevel> _logLevels = new Dictionary<uint, LogEventLevel>()
|
||||
{
|
||||
@@ -44,10 +48,12 @@ namespace PepperDash.Core
|
||||
|
||||
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch _error_logLevelswitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _fileLevelSwitch;
|
||||
|
||||
private static List<string> deviceKeysFilterList = new List<string>();
|
||||
|
||||
public static LogEventLevel WebsocketMinimumLogLevel
|
||||
{
|
||||
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
|
||||
@@ -124,19 +130,21 @@ namespace PepperDash.Core
|
||||
|
||||
var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);
|
||||
|
||||
var defaultErrorLogLevel = GetStoredLogEventLevel(ErrorLogLevelStoreKey);
|
||||
var defaultErrorLogLevel = GetStoredLogEventLevel(Error_logLevelstoreKey);
|
||||
|
||||
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
|
||||
|
||||
deviceKeysFilterList = GetStoredDeviceKeys();
|
||||
|
||||
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
|
||||
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
|
||||
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
_error_logLevelswitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
|
||||
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||
|
||||
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||
|
||||
var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?
|
||||
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
|
||||
@@ -152,9 +160,9 @@ namespace PepperDash.Core
|
||||
.MinimumLevel.Verbose()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With(new CrestronEnricher())
|
||||
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
|
||||
.WriteTo.Conditional(ConsoleFilter,c => c.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
|
||||
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch)
|
||||
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
|
||||
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _error_logLevelswitch))
|
||||
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
|
||||
rollingInterval: RollingInterval.Day,
|
||||
restrictedToMinimumLevel: LogEventLevel.Debug,
|
||||
@@ -206,12 +214,15 @@ namespace PepperDash.Core
|
||||
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
|
||||
"appdebug:P [0-5]: Sets the application's console debug message level",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
||||
"appdebuglog:P [all] Use \"all\" for full log.",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
|
||||
"appdebugclear:P Clears the current custom log",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
|
||||
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
|
||||
}
|
||||
@@ -233,6 +244,23 @@ namespace PepperDash.Core
|
||||
};
|
||||
}
|
||||
|
||||
private static bool ConsoleFilter(LogEvent evt)
|
||||
{
|
||||
// print error and fatal messages no matter what
|
||||
if(evt.Level >= LogEventLevel.Error)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// message is a keyed message
|
||||
if(evt.Properties.TryGetValue("Key", out var value) && value is ScalarValue sv && sv.Value is string rawValue)
|
||||
{
|
||||
return deviceKeysFilterList.Count == 0 || deviceKeysFilterList.Contains(rawValue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void UpdateLoggerConfiguration(LoggerConfiguration config)
|
||||
{
|
||||
_loggerConfiguration = config;
|
||||
@@ -273,6 +301,39 @@ namespace PepperDash.Core
|
||||
}
|
||||
}
|
||||
|
||||
private static List<string> GetStoredDeviceKeys()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = CrestronDataStoreStatic.GetLocalStringValue(DeviceKeysStoreKey, out string devicesString);
|
||||
|
||||
if (result != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
{
|
||||
CrestronConsole.Print($"Unable to retrieve stored log level for DEVICE_KEYS.\r\nError: {result}.\r\nShowing Log statements from all devices\r\n");
|
||||
return new List<string>();
|
||||
}
|
||||
|
||||
var devices = devicesString.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(d => d.Trim())
|
||||
.ToList();
|
||||
|
||||
if (devices.Count == 0)
|
||||
{
|
||||
CrestronConsole.PrintLine($"Showing log statements from all devices");
|
||||
return devices;
|
||||
}
|
||||
|
||||
CrestronConsole.PrintLine($"Showing log statements from devices: {string.Join(", ", devices)}");
|
||||
|
||||
return devices;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CrestronConsole.PrintLine($"Exception retrieving log level for DEVICE_KEYS: {ex.Message}");
|
||||
return new List<string>();
|
||||
}
|
||||
}
|
||||
|
||||
private static void GetVersion()
|
||||
{
|
||||
var assembly = Assembly.GetExecutingAssembly();
|
||||
@@ -342,29 +403,58 @@ namespace PepperDash.Core
|
||||
return;
|
||||
}
|
||||
|
||||
if(int.TryParse(levelString, out var levelInt))
|
||||
if (int.TryParse(levelString, out var levelInt))
|
||||
{
|
||||
if(levelInt < 0 || levelInt > 5)
|
||||
if (levelInt < 0 || levelInt > 5)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5");
|
||||
return;
|
||||
}
|
||||
SetDebugLevel((uint) levelInt);
|
||||
SetDebugLevel((uint)levelInt);
|
||||
return;
|
||||
}
|
||||
|
||||
if(Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum))
|
||||
if (Enum.TryParse<LogEventLevel>(levelString, out var levelEnum))
|
||||
{
|
||||
SetDebugLevel(levelEnum);
|
||||
return;
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the device filter for the debug messages
|
||||
/// </summary>
|
||||
/// <param name="deviceKeys">Array of keys to filter. Empty array is all devices</param>
|
||||
public static void SetDeviceFilter(string[] deviceKeys)
|
||||
{
|
||||
var newKeys = deviceKeys.ToList();
|
||||
|
||||
if (newKeys.Count == 0)
|
||||
{
|
||||
deviceKeysFilterList.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
deviceKeysFilterList = deviceKeysFilterList.Union(newKeys).ToList();
|
||||
}
|
||||
|
||||
var joinedList = string.Join(", ", deviceKeysFilterList);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"[Application {InitialParametersClass.ApplicationNumber}], Device filter set to {(deviceKeysFilterList.Count > 0 ? joinedList : "all devices")}\r\n");
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalStringValue(DeviceKeysStoreKey, joinedList);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Store result: {err}");
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
CrestronConsole.PrintLine($"Error saving console debug level setting: {err}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -380,6 +470,8 @@ namespace PepperDash.Core
|
||||
CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
|
||||
|
||||
SetDebugLevel(logLevel);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SetDebugLevel(logLevel);
|
||||
@@ -416,9 +508,9 @@ namespace PepperDash.Core
|
||||
|
||||
public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
_error_logLevelswitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(Error_logLevelstoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err);
|
||||
@@ -428,9 +520,9 @@ namespace PepperDash.Core
|
||||
|
||||
public static void SetFileMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
_error_logLevelswitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(Error_logLevelstoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
|
||||
@@ -466,78 +558,34 @@ namespace PepperDash.Core
|
||||
/// <param name="items"></param>
|
||||
public static void SetDebugFilterFromConsole(string items)
|
||||
{
|
||||
var str = items.Trim();
|
||||
if (str == "?")
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
||||
"+all: at beginning puts filter into 'default include' mode\r" +
|
||||
" All keys that follow will be excluded from output.\r" +
|
||||
"-all: at beginning puts filter into 'default exclude all' mode.\r" +
|
||||
" All keys that follow will be the only keys that are shown\r" +
|
||||
"+nokey: Enables messages with no key (default)\r" +
|
||||
"-nokey: Disables messages with no key.\r" +
|
||||
"(nokey settings are independent of all other settings)");
|
||||
var arguments = items.Trim();
|
||||
if (arguments == "?")
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage:\r\n APPDEBUGFILTER [all] [key1 key2 key3...]\r\n " +
|
||||
"all: clears filters and shows all device messages\r\n" +
|
||||
"key1 key2 key3...: adds keys to the filter list\r\n" +
|
||||
"non-keyed messages will ALWAYS be shown based on their level\r\n"
|
||||
);
|
||||
return;
|
||||
}
|
||||
var keys = Regex.Split(str, @"\s*");
|
||||
foreach (var keyToken in keys)
|
||||
{
|
||||
var lkey = keyToken.ToLower();
|
||||
if (lkey == "+all")
|
||||
{
|
||||
IncludedExcludedKeys.Clear();
|
||||
_excludeAllMode = false;
|
||||
}
|
||||
else if (lkey == "-all")
|
||||
{
|
||||
IncludedExcludedKeys.Clear();
|
||||
_excludeAllMode = true;
|
||||
}
|
||||
//else if (lkey == "+nokey")
|
||||
//{
|
||||
// ExcludeNoKeyMessages = false;
|
||||
//}
|
||||
//else if (lkey == "-nokey")
|
||||
//{
|
||||
// ExcludeNoKeyMessages = true;
|
||||
//}
|
||||
else
|
||||
{
|
||||
string key;
|
||||
if (lkey.StartsWith("-"))
|
||||
{
|
||||
key = lkey.Substring(1);
|
||||
// if in exclude all mode, we need to remove this from the inclusions
|
||||
if (_excludeAllMode)
|
||||
{
|
||||
if (IncludedExcludedKeys.ContainsKey(key))
|
||||
IncludedExcludedKeys.Remove(key);
|
||||
}
|
||||
// otherwise include all mode, add to the exclusions
|
||||
else
|
||||
{
|
||||
IncludedExcludedKeys[key] = new object();
|
||||
}
|
||||
}
|
||||
else if (lkey.StartsWith("+"))
|
||||
{
|
||||
key = lkey.Substring(1);
|
||||
// if in exclude all mode, we need to add this as inclusion
|
||||
if (_excludeAllMode)
|
||||
{
|
||||
if (string.IsNullOrEmpty(arguments))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("APPDEBUGFILTER: No arguments provided");
|
||||
return;
|
||||
}
|
||||
|
||||
IncludedExcludedKeys[key] = new object();
|
||||
}
|
||||
// otherwise include all mode, remove this from exclusions
|
||||
else
|
||||
{
|
||||
if (IncludedExcludedKeys.ContainsKey(key))
|
||||
IncludedExcludedKeys.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var keys = arguments.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (keys[0].ToLower() == "all")
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Clearing all filters");
|
||||
|
||||
SetDeviceFilter(new string[] { });
|
||||
return;
|
||||
}
|
||||
|
||||
SetDeviceFilter(keys);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -18,15 +18,8 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores pending route requests, keyed by the destination device key.
|
||||
/// Used primarily to handle routing requests while a device is cooling down.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// A queue to process route requests and releases sequentially.
|
||||
/// </summary>
|
||||
private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue");
|
||||
|
||||
/// <summary>
|
||||
@@ -45,49 +38,16 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set
|
||||
/// </summary>
|
||||
/// <param name="destination">destination to clear</param>
|
||||
public static void ReleaseRoute(this IRoutingInputs destination)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, false));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set
|
||||
/// </summary>
|
||||
/// <param name="destination">destination to clear</param>
|
||||
/// <param name="inputPortKey">Input to use to find existing route</param>
|
||||
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, false));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the route on the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destination">Destination</param>
|
||||
public static void ClearRoute(this IRoutingInputs destination)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the route on the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destination">destination</param>
|
||||
/// <param name="inputPortKey">input to use to find existing route</param>
|
||||
public static void ClearRoute(this IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the route request for the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destinationKey">destination device key</param>
|
||||
public static void RemoveRouteRequestForDestination(string destinationKey)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey);
|
||||
@@ -170,15 +130,6 @@ namespace PepperDash.Essentials.Core
|
||||
return (audioRouteDescriptor, videoRouteDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle the logic for releasing an existing route and making a new one.
|
||||
/// Handles devices with cooling states by queueing the request.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="signalType">The type of signal to route.</param>
|
||||
/// <param name="destinationPort">The specific destination input port (optional).</param>
|
||||
/// <param name="sourcePort">The specific source output port (optional).</param>
|
||||
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
@@ -233,16 +184,11 @@ namespace PepperDash.Essentials.Core
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||
}
|
||||
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty));
|
||||
|
||||
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the actual routing based on a <see cref="RouteRequest"/>.
|
||||
/// Finds the route path, adds it to the collection, and executes the switches.
|
||||
/// </summary>
|
||||
/// <param name="request">The route request details.</param>
|
||||
private static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
try
|
||||
@@ -270,15 +216,14 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in RouteDescriptorCollection.DefaultCollection
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="inputPortKey"> The input port key to use to find the route. If empty, will use the first available input port</param>
|
||||
/// <param name="clearRoute"> If true, will clear the route on the destination. This will remove any routes that are currently in use</param>
|
||||
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey, bool clearRoute)
|
||||
/// <param name="destination"></param>
|
||||
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -297,7 +242,7 @@ namespace PepperDash.Essentials.Core
|
||||
if (current != null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
||||
current.ReleaseRoutes(clearRoute);
|
||||
current.ReleaseRoutes();
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Marker interface to identify a device that acts as the origin of a signal path (<see cref="IRoutingOutputs"/>).
|
||||
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||
/// </summary>
|
||||
public interface IRoutingSource : IRoutingOutputs
|
||||
{
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a routing device (<see cref="IRouting"/>) that supports explicitly clearing a route on an output.
|
||||
/// </summary>
|
||||
public interface IRoutingWithClear : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -3,25 +3,14 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for handling route change events on devices implementing <see cref="IRoutingWithFeedback"/>.
|
||||
/// </summary>
|
||||
/// <param name="midpoint">The routing device where the change occurred.</param>
|
||||
/// <param name="newRoute">A descriptor of the new route that was established.</param>
|
||||
public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
|
||||
/// <summary>
|
||||
/// Defines a routing device (<see cref="IRouting"/>) that provides feedback about its current routes.
|
||||
/// Defines an IRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface IRoutingWithFeedback : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list describing the currently active routes on this device.
|
||||
/// </summary>
|
||||
List<RouteSwitchDescriptor> CurrentRoutes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a route changes on this device.
|
||||
/// </summary>
|
||||
event RouteChangedEventHandler RouteChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,8 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a routing device (typically a transmitter or source) that provides numeric feedback for its current route.
|
||||
/// Extends <see cref="IRoutingNumeric"/>.
|
||||
/// </summary>
|
||||
public interface ITxRouting : IRoutingNumeric
|
||||
{
|
||||
/// <summary>
|
||||
/// Feedback indicating the currently routed video source by its numeric identifier.
|
||||
/// </summary>
|
||||
IntFeedback VideoSourceNumericFeedback { get; }
|
||||
/// <summary>
|
||||
/// Feedback indicating the currently routed audio source by its numeric identifier.
|
||||
/// </summary>
|
||||
IntFeedback AudioSourceNumericFeedback { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharpPro;
|
||||
|
||||
@@ -10,63 +9,35 @@ using Serilog.Events;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection of individual route steps between a Source and a Destination device for a specific signal type.
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The destination device (sink or midpoint) for the route.
|
||||
/// </summary>
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The specific input port on the destination device used for this route. Can be null if not specified or applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source device for the route.
|
||||
/// </summary>
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal being routed (e.g., Audio, Video). This descriptor represents a single signal type.
|
||||
/// </summary>
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of individual switching steps required to establish the route.
|
||||
/// </summary>
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteDescriptor"/> class for a route without a specific destination input port.
|
||||
/// </summary>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="signalType">The type of signal being routed.</param>
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteDescriptor"/> class for a route with a specific destination input port.
|
||||
/// </summary>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="inputPort">The destination input port (optional).</param>
|
||||
/// <param name="signalType">The signal type for this route.</param>
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
InputPort = inputPort;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
InputPort = inputPort;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all the switching steps defined in the <see cref="Routes"/> list.
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
@@ -92,27 +63,15 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases the usage tracking for the route and optionally clears the route on the switching devices.
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
/// <param name="clearRoute">If true, attempts to clear the route on the switching devices (e.g., set input to null/0).</param>
|
||||
public void ReleaseRoutes(bool clearRoute = false)
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||
{
|
||||
if(clearRoute)
|
||||
{
|
||||
try
|
||||
{
|
||||
switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Error executing switch: {exception}", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (route.OutputPort == null)
|
||||
{
|
||||
continue;
|
||||
@@ -131,10 +90,6 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route descriptor, including source, destination, and individual route steps.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the route.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
|
||||
@@ -4,42 +4,15 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a request to establish a route between a source and a destination device.
|
||||
/// </summary>
|
||||
public class RouteRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The specific input port on the destination device to use for the route. Can be null if the port should be automatically determined or is not applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort DestinationPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The specific output port on the source device to use for the route. Can be null if the port should be automatically determined or is not applicable.
|
||||
/// </summary>
|
||||
public RoutingOutputPort SourcePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The destination device (sink or midpoint) for the route.
|
||||
/// </summary>
|
||||
public IRoutingInputs Destination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source device for the route.
|
||||
/// </summary>
|
||||
public IRoutingOutputs Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal being routed (e.g., Audio, Video, AudioVideo).
|
||||
/// </summary>
|
||||
public eRoutingSignalType SignalType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handles the route request after a device's cooldown period has finished.
|
||||
/// This method is typically subscribed to the IsCoolingDownFeedback.OutputChange event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object that triggered the event (usually the cooling device).</param>
|
||||
/// <param name="args">Event arguments indicating the cooldown state change.</param>
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
try
|
||||
@@ -66,10 +39,6 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route request.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the source and destination of the route request.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Route {Source?.Key ?? "No Source Device"}:{SourcePort?.Key ?? "auto"} to {Destination?.Key ?? "No Destination Device"}:{DestinationPort?.Key ?? "auto"}";
|
||||
|
||||
@@ -5,34 +5,17 @@ using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an item in the route request queue.
|
||||
/// </summary>
|
||||
public class RouteRequestQueueItem : IQueueMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to perform for the route request.
|
||||
/// </summary>
|
||||
private readonly Action<RouteRequest> action;
|
||||
/// <summary>
|
||||
/// The route request data.
|
||||
/// </summary>
|
||||
private readonly RouteRequest routeRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteRequestQueueItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="routeAction">The action to perform.</param>
|
||||
/// <param name="request">The route request data.</param>
|
||||
public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)
|
||||
{
|
||||
action = routeAction;
|
||||
routeRequest = request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the route request action.
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest);
|
||||
@@ -40,50 +23,23 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an item in the queue for releasing a route.
|
||||
/// </summary>
|
||||
public class ReleaseRouteQueueItem : IQueueMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to perform for releasing the route.
|
||||
/// </summary>
|
||||
private readonly Action<IRoutingInputs, string, bool> action;
|
||||
/// <summary>
|
||||
/// The destination device whose route is being released.
|
||||
/// </summary>
|
||||
private readonly Action<IRoutingInputs, string> action;
|
||||
private readonly IRoutingInputs destination;
|
||||
/// <summary>
|
||||
/// The specific input port key on the destination to release, or null/empty for any/default.
|
||||
/// </summary>
|
||||
private readonly string inputPortKey;
|
||||
/// <summary>
|
||||
/// Indicates whether to clear the route (send null) or just release the usage tracking.
|
||||
/// </summary>
|
||||
private readonly bool clearRoute;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReleaseRouteQueueItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="inputPortKey">The input port key.</param>
|
||||
/// <param name="clearRoute">True to clear the route, false to just release.</param>
|
||||
public ReleaseRouteQueueItem(Action<IRoutingInputs, string, bool> action, IRoutingInputs destination, string inputPortKey, bool clearRoute)
|
||||
public ReleaseRouteQueueItem(Action<IRoutingInputs, string> action, IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
this.action = action;
|
||||
this.destination = destination;
|
||||
this.inputPortKey = inputPortKey;
|
||||
this.clearRoute = clearRoute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the release route action.
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||
action(destination, inputPortKey, clearRoute);
|
||||
action(destination, inputPortKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,25 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single switching step within a larger route, detailing the switching device, input port, and optionally the output port.
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The device performing the switch (derived from the InputPort's parent).
|
||||
/// </summary>
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }
|
||||
/// <summary>
|
||||
/// The output port being switched from (relevant for matrix switchers). Null for sink devices.
|
||||
/// </summary>
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
/// <summary>
|
||||
/// The input port being switched to.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteSwitchDescriptor"/> class for sink devices (no output port).
|
||||
/// </summary>
|
||||
/// <param name="inputPort">The input port being switched to.</param>
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteSwitchDescriptor"/> class for matrix switchers.
|
||||
/// </summary>
|
||||
/// <param name="outputPort">The output port being switched from.</param>
|
||||
/// <param name="inputPort">The input port being switched to.</param>
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route switch descriptor.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the switch operation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (SwitchingDevice is IRouting)
|
||||
|
||||
@@ -5,17 +5,8 @@ using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages routing feedback by subscribing to route changes on midpoint and sink devices,
|
||||
/// tracing the route back to the original source, and updating the CurrentSourceInfo on sink devices.
|
||||
/// </summary>
|
||||
public class RoutingFeedbackManager:EssentialsDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingFeedbackManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this manager device.</param>
|
||||
/// <param name="name">The name of this manager device.</param>
|
||||
public RoutingFeedbackManager(string key, string name): base(key, name)
|
||||
{
|
||||
AddPreActivationAction(SubscribeForMidpointFeedback);
|
||||
@@ -23,9 +14,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the RouteChanged event on all devices implementing <see cref="IRoutingWithFeedback"/>.
|
||||
/// </summary>
|
||||
private void SubscribeForMidpointFeedback()
|
||||
{
|
||||
var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();
|
||||
@@ -36,9 +24,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the InputChanged event on all devices implementing <see cref="IRoutingSinkWithSwitchingWithInputPort"/>.
|
||||
/// </summary>
|
||||
private void SubscribeForSinkFeedback()
|
||||
{
|
||||
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||
@@ -49,12 +34,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the RouteChanged event from a midpoint device.
|
||||
/// Triggers an update for all sink devices.
|
||||
/// </summary>
|
||||
/// <param name="midpoint">The midpoint device that reported a route change.</param>
|
||||
/// <param name="newRoute">The descriptor of the new route.</param>
|
||||
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
|
||||
{
|
||||
try
|
||||
@@ -72,12 +51,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the InputChanged event from a sink device.
|
||||
/// Triggers an update for the specific sink device.
|
||||
/// </summary>
|
||||
/// <param name="sender">The sink device that reported an input change.</param>
|
||||
/// <param name="currentInputPort">The new input port selected on the sink device.</param>
|
||||
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
|
||||
{
|
||||
try
|
||||
@@ -90,12 +63,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CurrentSourceInfo and CurrentSourceInfoKey properties on a destination (sink) device
|
||||
/// based on its currently selected input port by tracing the route back through tie lines.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination sink device to update.</param>
|
||||
/// <param name="inputPort">The currently selected input port on the destination device.</param>
|
||||
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
|
||||
{
|
||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
|
||||
@@ -232,12 +199,6 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively traces a route back from a given tie line to find the root source tie line.
|
||||
/// It navigates through midpoint devices (<see cref="IRoutingWithFeedback"/>) by checking their current routes.
|
||||
/// </summary>
|
||||
/// <param name="tieLine">The starting tie line (typically connected to a sink or midpoint).</param>
|
||||
/// <returns>The <see cref="TieLine"/> connected to the original source device, or null if the source cannot be determined.</returns>
|
||||
private TieLine GetRootTieLine(TieLine tieLine)
|
||||
{
|
||||
TieLine nextTieLine = null;
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic routing input port on a device.
|
||||
/// Basic RoutingInput with no statuses.
|
||||
/// </summary>
|
||||
public class RoutingInputPort : RoutingPort
|
||||
{
|
||||
@@ -41,10 +41,6 @@ namespace PepperDash.Essentials.Core
|
||||
ParentDevice = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the input port.
|
||||
/// </summary>
|
||||
/// <returns>A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType".</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a routing input port that provides video status feedback (e.g., sync, resolution).
|
||||
/// Suitable for devices like DM transmitters or DM input cards.
|
||||
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
||||
/// Will provide video statistics on connected signals
|
||||
/// </summary>
|
||||
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides feedback outputs for video statuses associated with this port.
|
||||
/// Video statuses attached to this port
|
||||
/// </summary>
|
||||
public VideoStatusOutputs VideoStatus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingInputPortWithVideoStatuses"/> class.
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingInputs"/> device this port belongs to.</param>
|
||||
/// <param name="funcs">A <see cref="VideoStatusFuncsWrapper"/> containing delegates to retrieve video status values.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
|
||||
/// May be string, number, whatever</param>
|
||||
/// <param name="parent">The IRoutingInputs object this lives on</param>
|
||||
/// <param name="funcs">A VideoStatusFuncsWrapper used to assign the callback funcs that will get
|
||||
/// the values for the various stats</param>
|
||||
public RoutingInputPortWithVideoStatuses(string key,
|
||||
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
|
||||
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
|
||||
|
||||
@@ -3,72 +3,32 @@
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides event arguments for routing changes, potentially including numeric or port object references.
|
||||
/// </summary>
|
||||
public class RoutingNumericEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The numeric representation of the output, if applicable.
|
||||
/// </summary>
|
||||
|
||||
public uint? Output { get; set; }
|
||||
/// <summary>
|
||||
/// The numeric representation of the input, if applicable.
|
||||
/// </summary>
|
||||
public uint? Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal involved in the routing change.
|
||||
/// </summary>
|
||||
public eRoutingSignalType SigType { get; set; }
|
||||
/// <summary>
|
||||
/// The input port involved in the routing change, if applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
/// <summary>
|
||||
/// The output port involved in the routing change, if applicable.
|
||||
/// </summary>
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class using numeric identifiers.
|
||||
/// </summary>
|
||||
/// <param name="output">The numeric output identifier.</param>
|
||||
/// <param name="input">The numeric input identifier.</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class using port objects.
|
||||
/// </summary>
|
||||
/// <param name="outputPort">The output port object.</param>
|
||||
/// <param name="inputPort">The input port object.</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
||||
eRoutingSignalType sigType)
|
||||
: this(null, null, outputPort, inputPort, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class with default values.
|
||||
/// </summary>
|
||||
public RoutingNumericEventArgs()
|
||||
: this(null, null, null, null, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class with potentially mixed identifiers.
|
||||
/// </summary>
|
||||
/// <param name="output">The numeric output identifier (optional).</param>
|
||||
/// <param name="input">The numeric input identifier (optional).</param>
|
||||
/// <param name="outputPort">The output port object (optional).</param>
|
||||
/// <param name="inputPort">The input port object (optional).</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
||||
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
||||
{
|
||||
|
||||
@@ -4,46 +4,29 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic routing output port on a device.
|
||||
/// </summary>
|
||||
public class RoutingOutputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingOutputs object this port lives on.
|
||||
/// The IRoutingOutputs object this port lives on
|
||||
/// </summary>
|
||||
///
|
||||
[JsonIgnore]
|
||||
public IRoutingOutputs ParentDevice { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tracks which destinations are currently using this output port.
|
||||
/// </summary>
|
||||
public InUseTracking InUseTracker { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingOutputPort"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingOutputs"/> device this port belongs to.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
|
||||
/// May be string, number, whatever</param>
|
||||
/// <param name="parent">The IRoutingOutputs object this port lives on</param>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingOutputPort"/> class, potentially marking it as internal.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingOutputs"/> device this port belongs to.</param>
|
||||
/// <param name="isInternal">True if this port represents an internal connection within a device (e.g., card to backplane).</param>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
@@ -52,10 +35,6 @@ namespace PepperDash.Essentials.Core
|
||||
InUseTracker = new InUseTracking();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the output port.
|
||||
/// </summary>
|
||||
/// <returns>A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType".</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
|
||||
@@ -4,47 +4,18 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for <see cref="RoutingInputPort"/> and <see cref="RoutingOutputPort"/>.
|
||||
/// Base class for RoutingInput and Output ports
|
||||
/// </summary>
|
||||
public abstract class RoutingPort : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique key identifying this port within its parent device.
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
/// <summary>
|
||||
/// The type of signal this port handles (e.g., Audio, Video, AudioVideo).
|
||||
/// </summary>
|
||||
public eRoutingSignalType Type { get; private set; }
|
||||
/// <summary>
|
||||
/// The physical connection type of this port (e.g., Hdmi, Rca, Dm).
|
||||
/// </summary>
|
||||
public eRoutingPortConnectionType ConnectionType { get; private set; }
|
||||
/// <summary>
|
||||
/// An object (often a number or string) used by the parent routing device to select this port during switching.
|
||||
/// </summary>
|
||||
public readonly object Selector;
|
||||
/// <summary>
|
||||
/// Indicates if this port represents an internal connection within a device (e.g., card to backplane).
|
||||
/// </summary>
|
||||
public bool IsInternal { get; private set; }
|
||||
/// <summary>
|
||||
/// An object used to match feedback values to this port, if applicable.
|
||||
/// </summary>
|
||||
public object FeedbackMatchObject { get; set; }
|
||||
/// <summary>
|
||||
/// A reference to the underlying hardware port object (e.g., SimplSharpPro Port), if applicable.
|
||||
/// </summary>
|
||||
public object Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingPort"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">The selector object for switching.</param>
|
||||
/// <param name="isInternal">True if this port is internal.</param>
|
||||
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal)
|
||||
{
|
||||
Key = key;
|
||||
|
||||
@@ -7,8 +7,7 @@ using Crestron.SimplSharp;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines constant string values for common routing port keys.
|
||||
/// These should correspond directly with the portNames var in the config tool.
|
||||
/// These should correspond directly with the portNames var in the config tool.
|
||||
/// </summary>
|
||||
public class RoutingPortNames
|
||||
{
|
||||
|
||||
@@ -4,23 +4,14 @@ using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a connection (tie line) between a <see cref="RoutingOutputPort"/> and a <see cref="RoutingInputPort"/>.
|
||||
/// </summary>
|
||||
public class TieLine
|
||||
{
|
||||
/// <summary>
|
||||
/// The source output port of the tie line.
|
||||
/// </summary>
|
||||
public RoutingOutputPort SourcePort { get; private set; }
|
||||
/// <summary>
|
||||
/// The destination input port of the tie line.
|
||||
/// </summary>
|
||||
public RoutingInputPort DestinationPort { get; private set; }
|
||||
//public int InUseCount { get { return DestinationUsingThis.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this tie line. Will either be the type of the destination port
|
||||
/// Gets the type of this tie line. Will either be the type of the desination port
|
||||
/// or the type of OverrideType when it is set.
|
||||
/// </summary>
|
||||
public eRoutingSignalType Type
|
||||
@@ -44,27 +35,20 @@ namespace PepperDash.Essentials.Core
|
||||
//List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this tie line represents an internal connection within a device (both source and destination ports are internal).
|
||||
/// For tie lines that represent internal links, like from cards to the matrix in a DM.
|
||||
/// This property is true if SourcePort and DestinationPort IsInternal
|
||||
/// property are both true
|
||||
/// </summary>
|
||||
public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the signal types of the source and destination ports differ.
|
||||
/// </summary>
|
||||
public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connection types of the source and destination ports differ.
|
||||
/// </summary>
|
||||
public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }
|
||||
/// <summary>
|
||||
/// A descriptive note about any type mismatch, if applicable.
|
||||
/// </summary>
|
||||
public string TypeMismatchNote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TieLine"/> class.
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
/// <param name="sourcePort"></param>
|
||||
/// <param name="destinationPort"></param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)
|
||||
{
|
||||
if (sourcePort == null || destinationPort == null)
|
||||
@@ -74,11 +58,9 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info.
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
|
||||
/// </summary>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType? overrideType) :
|
||||
this(sourcePort, destinationPort)
|
||||
{
|
||||
@@ -86,11 +68,9 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info.
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
|
||||
/// </summary>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :
|
||||
this(sourcePort, destinationPort)
|
||||
{
|
||||
@@ -98,25 +78,18 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will link up video status from supporting inputs to connected outputs.
|
||||
/// Will link up video status from supporting inputs to connected outputs
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
// Now does nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the tie line.
|
||||
/// </summary>
|
||||
public void Deactivate()
|
||||
{
|
||||
// Now does nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the tie line.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the source, destination, and type of the tie line.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
||||
@@ -126,14 +99,8 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
//********************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of <see cref="TieLine"/> objects.
|
||||
/// </summary>
|
||||
public class TieLineCollection : List<TieLine>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default singleton instance of the <see cref="TieLineCollection"/>.
|
||||
/// </summary>
|
||||
public static TieLineCollection Default
|
||||
{
|
||||
get
|
||||
@@ -144,9 +111,6 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backing field for the singleton instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
private static TieLineCollection _Default;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
@@ -13,44 +15,15 @@ using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the configuration data for a single tie line between two routing ports.
|
||||
/// </summary>
|
||||
public class TieLineConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public string SourceCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the source output port.
|
||||
/// </summary>
|
||||
public string SourcePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination device.
|
||||
/// </summary>
|
||||
public string DestinationKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination card (if applicable).
|
||||
/// </summary>
|
||||
public string DestinationCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination input port.
|
||||
/// </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; }
|
||||
@@ -100,19 +73,11 @@ namespace PepperDash.Essentials.Core.Config
|
||||
return new TieLine(sourceOutputPort, destinationInputPort, OverrideType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message related to creating this tie line configuration.
|
||||
/// </summary>
|
||||
/// <param name="msg">The specific error message.</param>
|
||||
void LogError(string msg)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the tie line configuration.
|
||||
/// </summary>
|
||||
/// <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,
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.Generic
|
||||
{
|
||||
public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort
|
||||
public class GenericSink : EssentialsDevice, IRoutingSink
|
||||
{
|
||||
public GenericSink(string key, string name) : base(key, name)
|
||||
{
|
||||
|
||||
@@ -8,10 +8,9 @@ using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.SoftCodec
|
||||
{
|
||||
public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingSinkWithSwitchingWithInputPort
|
||||
public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingOutputs, IRoutingSinkWithSwitching
|
||||
{
|
||||
private RoutingInputPort _currentInputPort;
|
||||
|
||||
public RoutingInputPort CurrentInputPort {
|
||||
get => _currentInputPort;
|
||||
set
|
||||
|
||||
Reference in New Issue
Block a user