mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-09 09:45:06 +00:00
feat: map routes/tielines at startup and new console commands
* visualizeroutes allows visualizing configured routes based on tielines and signal type * can be filtered by source key, destination key, and type, along with partial matches for source & destination keys * visualizecurrentroutes visualizes what Essentials says is currently routed by type * uses same filtering as visualizeroutes * improvements to how the routing algorithm works
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -92,12 +93,16 @@ namespace PepperDash.Essentials
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(s => Debug.LogMessage(LogEventLevel.Information, "CONSOLE MESSAGE: {0}", s), "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(s =>
|
||||
{
|
||||
foreach (var tl in TieLineCollection.Default)
|
||||
CrestronConsole.ConsoleCommandResponse(" {0}{1}", tl, CrestronEnvironment.NewLine);
|
||||
},
|
||||
"listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator);
|
||||
CrestronConsole.AddNewConsoleCommand(ListTieLines,
|
||||
"listtielines", "Prints out all tie lines. Usage: listtielines [signaltype]", ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(VisualizeRoutes, "visualizeroutes",
|
||||
"Visualizes routes by signal type",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(VisualizeCurrentRoutes, "visualizecurrentroutes",
|
||||
"Visualizes current active routes from DefaultCollection",
|
||||
ConsoleAccessLevelEnum.AccessOperator);
|
||||
|
||||
CrestronConsole.AddNewConsoleCommand(s =>
|
||||
{
|
||||
@@ -443,6 +448,282 @@ namespace PepperDash.Essentials
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "All Tie Lines Loaded.");
|
||||
|
||||
Extensions.MapDestinationsToSources();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "All Routes Mapped.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Visualizes routes in a tree format for better understanding of signal paths
|
||||
/// </summary>
|
||||
private void ListTieLines(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Contains("?"))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: listtielines [signaltype]\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("Signal types: Audio, Video, SecondaryAudio, AudioVideo, UsbInput, UsbOutput\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
eRoutingSignalType? signalTypeFilter = null;
|
||||
if (!string.IsNullOrEmpty(args))
|
||||
{
|
||||
eRoutingSignalType parsedType;
|
||||
if (Enum.TryParse(args.Trim(), true, out parsedType))
|
||||
{
|
||||
signalTypeFilter = parsedType;
|
||||
}
|
||||
else
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Invalid signal type: {0}\r\n", args.Trim());
|
||||
CrestronConsole.ConsoleCommandResponse("Valid types: Audio, Video, SecondaryAudio, AudioVideo, UsbInput, UsbOutput\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var tielines = signalTypeFilter.HasValue
|
||||
? TieLineCollection.Default.Where(tl => tl.Type.HasFlag(signalTypeFilter.Value))
|
||||
: TieLineCollection.Default;
|
||||
|
||||
var count = 0;
|
||||
foreach (var tl in tielines)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(" {0}{1}", tl, CrestronEnvironment.NewLine);
|
||||
count++;
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\nTotal: {0} tieline{1}{2}", count, count == 1 ? "" : "s", CrestronEnvironment.NewLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Error listing tielines: {0}\r\n", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void VisualizeRoutes(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Contains("?"))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: visualizeroutes [signaltype] [-s source] [-d destination]\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" signaltype: Audio, Video, AudioVideo, etc.\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" -s: Filter by source key (partial match)\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" -d: Filter by destination key (partial match)\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ParseRouteFilters(args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n+===========================================================================+\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("| ROUTE VISUALIZATION |\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("+===========================================================================+\r\n\r\n");
|
||||
|
||||
foreach (var descriptorCollection in Extensions.RouteDescriptors.Where(kv => kv.Value.Descriptors.Count() > 0))
|
||||
{
|
||||
// Filter by signal type if specified
|
||||
if (signalTypeFilter.HasValue && descriptorCollection.Key != signalTypeFilter.Value)
|
||||
continue;
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n+--- Signal Type: {0} ({1} routes) ---\r\n",
|
||||
descriptorCollection.Key,
|
||||
descriptorCollection.Value.Descriptors.Count());
|
||||
|
||||
foreach (var descriptor in descriptorCollection.Value.Descriptors)
|
||||
{
|
||||
// Filter by source/dest if specified
|
||||
if (sourceFilter != null && !descriptor.Source.Key.ToLower().Contains(sourceFilter))
|
||||
continue;
|
||||
if (destFilter != null && !descriptor.Destination.Key.ToLower().Contains(destFilter))
|
||||
continue;
|
||||
|
||||
VisualizeRouteDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Error visualizing routes: {0}\r\n", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void VisualizeCurrentRoutes(string args)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (args.Contains("?"))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Usage: visualizecurrentroutes [signaltype] [-s source] [-d destination]\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" signaltype: Audio, Video, AudioVideo, etc.\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" -s: Filter by source key (partial match)\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse(" -d: Filter by destination key (partial match)\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ParseRouteFilters(args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n+===========================================================================+\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("| CURRENT ROUTES VISUALIZATION |\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("+===========================================================================+\r\n\r\n");
|
||||
|
||||
var hasRoutes = false;
|
||||
|
||||
// Get all descriptors from DefaultCollection
|
||||
var allDescriptors = RouteDescriptorCollection.DefaultCollection.Descriptors;
|
||||
|
||||
// Group by signal type
|
||||
var groupedByType = allDescriptors.GroupBy(d => d.SignalType);
|
||||
|
||||
foreach (var group in groupedByType)
|
||||
{
|
||||
var signalType = group.Key;
|
||||
|
||||
// Filter by signal type if specified
|
||||
if (signalTypeFilter.HasValue && signalType != signalTypeFilter.Value)
|
||||
continue;
|
||||
|
||||
var filteredDescriptors = group.Where(d =>
|
||||
{
|
||||
if (sourceFilter != null && !d.Source.Key.ToLower().Contains(sourceFilter))
|
||||
return false;
|
||||
if (destFilter != null && !d.Destination.Key.ToLower().Contains(destFilter))
|
||||
return false;
|
||||
return true;
|
||||
}).ToList();
|
||||
|
||||
if (filteredDescriptors.Count == 0)
|
||||
continue;
|
||||
|
||||
hasRoutes = true;
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n+--- Signal Type: {0} ({1} routes) ---\r\n",
|
||||
signalType,
|
||||
filteredDescriptors.Count);
|
||||
|
||||
foreach (var descriptor in filteredDescriptors)
|
||||
{
|
||||
VisualizeRouteDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasRoutes)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("\r\nNo active routes found in current state.\r\n");
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("\r\n");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("Error visualizing current state: {0}\r\n", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses route filter arguments from command line
|
||||
/// </summary>
|
||||
/// <param name="args">Command line arguments</param>
|
||||
/// <param name="signalTypeFilter">Parsed signal type filter (if any)</param>
|
||||
/// <param name="sourceFilter">Parsed source filter (if any)</param>
|
||||
/// <param name="destFilter">Parsed destination filter (if any)</param>
|
||||
private void ParseRouteFilters(string args, out eRoutingSignalType? signalTypeFilter, out string sourceFilter, out string destFilter)
|
||||
{
|
||||
signalTypeFilter = null;
|
||||
sourceFilter = null;
|
||||
destFilter = null;
|
||||
|
||||
if (string.IsNullOrEmpty(args))
|
||||
return;
|
||||
|
||||
var parts = args.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
for (int i = 0; i < parts.Length; i++)
|
||||
{
|
||||
var part = parts[i];
|
||||
|
||||
// Check for flags
|
||||
if (part == "-s" && i + 1 < parts.Length)
|
||||
{
|
||||
sourceFilter = parts[++i].ToLower();
|
||||
}
|
||||
else if (part == "-d" && i + 1 < parts.Length)
|
||||
{
|
||||
destFilter = parts[++i].ToLower();
|
||||
}
|
||||
// Try to parse as signal type if not a flag and no signal type set yet
|
||||
else if (!part.StartsWith("-") && !signalTypeFilter.HasValue)
|
||||
{
|
||||
if (Enum.TryParse(part, true, out eRoutingSignalType parsedType))
|
||||
{
|
||||
signalTypeFilter = parsedType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Visualizes a single route descriptor in a tree format
|
||||
/// </summary>
|
||||
private void VisualizeRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("|\r\n");
|
||||
CrestronConsole.ConsoleCommandResponse("|-- {0} --> {1}\r\n",
|
||||
descriptor.Source.Key,
|
||||
descriptor.Destination.Key);
|
||||
|
||||
if (descriptor.Routes == null || descriptor.Routes.Count == 0)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("| +-- (No switching steps)\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < descriptor.Routes.Count; i++)
|
||||
{
|
||||
var route = descriptor.Routes[i];
|
||||
var isLast = i == descriptor.Routes.Count - 1;
|
||||
var prefix = isLast ? "+" : "|";
|
||||
var continuation = isLast ? " " : "|";
|
||||
|
||||
if (route.SwitchingDevice != null)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("| {0}-- [{1}] {2}\r\n",
|
||||
prefix,
|
||||
route.SwitchingDevice.Key,
|
||||
GetSwitchDescription(route));
|
||||
|
||||
// Add visual connection line for non-last items
|
||||
if (!isLast)
|
||||
CrestronConsole.ConsoleCommandResponse("| {0} |\r\n", continuation);
|
||||
}
|
||||
else
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("| {0}-- {1}\r\n", prefix, route.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a readable description of the switching operation
|
||||
/// </summary>
|
||||
private string GetSwitchDescription(RouteSwitchDescriptor route)
|
||||
{
|
||||
if (route.OutputPort != null && route.InputPort != null)
|
||||
{
|
||||
return string.Format("{0} -> {1}", route.OutputPort.Key, route.InputPort.Key);
|
||||
}
|
||||
else if (route.InputPort != null)
|
||||
{
|
||||
return string.Format("-> {0}", route.InputPort.Key);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(passthrough)";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user