mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-15 20:54:55 +00:00
refactor: move routing interfaces into their own files
This commit is contained in:
@@ -21,6 +21,9 @@
|
|||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Remove="Routing\GenericExtensions.cs" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.42" />
|
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.42" />
|
||||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-402" />
|
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-402" />
|
||||||
|
|||||||
252
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
252
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||||
|
/// on those destinations.
|
||||||
|
/// </summary>
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||||
|
/// <summary>
|
||||||
|
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||||
|
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||||
|
/// in RouteDescriptorCollection.DefaultCollection
|
||||||
|
/// </summary>
|
||||||
|
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
var routeRequest = new RouteRequest {
|
||||||
|
Destination = destination,
|
||||||
|
Source = source,
|
||||||
|
SignalType = signalType
|
||||||
|
};
|
||||||
|
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
|
|
||||||
|
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||||
|
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
RouteRequests[destination.Key] = routeRequest;
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//New Request
|
||||||
|
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
RouteRequests.Add(destination.Key, routeRequest);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down. Storing route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||||
|
{
|
||||||
|
RouteRequests.Remove(destination.Key);
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.ReleaseRoute();
|
||||||
|
|
||||||
|
RunRouteRequest(routeRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void RunRouteRequest(RouteRequest request)
|
||||||
|
{
|
||||||
|
if (request.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
||||||
|
|
||||||
|
if (newRoute == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
|
||||||
|
|
||||||
|
newRoute.ExecuteRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will release the existing route on the destination, if it is found in
|
||||||
|
/// RouteDescriptorCollection.DefaultCollection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
public static void ReleaseRoute(this IRoutingSink destination)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
|
||||||
|
{
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
RouteRequests.Remove(destination.Key);
|
||||||
|
|
||||||
|
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||||
|
if (current != null)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
|
||||||
|
current.ReleaseRoutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||||
|
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||||
|
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||||
|
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||||
|
/// discovered, then null is returned
|
||||||
|
/// </summary>
|
||||||
|
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
var routeDescriptor = new RouteDescriptor(source, destination, signalType);
|
||||||
|
|
||||||
|
// if it's a single signal type, find the route
|
||||||
|
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {0}", null, source.Key);
|
||||||
|
|
||||||
|
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescriptor))
|
||||||
|
routeDescriptor = null;
|
||||||
|
|
||||||
|
return routeDescriptor;
|
||||||
|
}
|
||||||
|
// otherwise, audioVideo needs to be handled as two steps.
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build audio and video routes from {0}", destination, source.Key);
|
||||||
|
|
||||||
|
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescriptor);
|
||||||
|
|
||||||
|
if (!audioSuccess)
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
|
||||||
|
|
||||||
|
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescriptor);
|
||||||
|
|
||||||
|
if (!videoSuccess)
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
|
||||||
|
|
||||||
|
if (!audioSuccess && !videoSuccess)
|
||||||
|
routeDescriptor = null;
|
||||||
|
|
||||||
|
|
||||||
|
return routeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||||
|
/// desired source and if not found, invoke this function for the each input port
|
||||||
|
/// hoping to find the source.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
||||||
|
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||||
|
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||||
|
/// <param name="cycle">Just an informational counter</param>
|
||||||
|
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||||
|
/// <returns>true if source is hit</returns>
|
||||||
|
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
||||||
|
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
||||||
|
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
|
||||||
|
{
|
||||||
|
|
||||||
|
cycle++;
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
|
||||||
|
|
||||||
|
RoutingInputPort goodInputPort = null;
|
||||||
|
|
||||||
|
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
||||||
|
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
|
||||||
|
|
||||||
|
// find a direct tie
|
||||||
|
var directTie = destDevInputTies.FirstOrDefault(
|
||||||
|
t => t.DestinationPort.ParentDevice == destination
|
||||||
|
&& t.SourcePort.ParentDevice == source);
|
||||||
|
if (directTie != null) // Found a tie directly to the source
|
||||||
|
{
|
||||||
|
goodInputPort = directTie.DestinationPort;
|
||||||
|
}
|
||||||
|
else // no direct-connect. Walk back devices.
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {0}. Walking down tie lines", destination, source.Key);
|
||||||
|
|
||||||
|
// No direct tie? Run back out on the inputs' attached devices...
|
||||||
|
// Only the ones that are routing devices
|
||||||
|
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||||
|
|
||||||
|
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||||
|
if (alreadyCheckedDevices == null)
|
||||||
|
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
||||||
|
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
||||||
|
|
||||||
|
foreach (var inputTieToTry in attachedMidpoints)
|
||||||
|
{
|
||||||
|
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
||||||
|
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {0}", destination, upstreamRoutingDevice.Key);
|
||||||
|
|
||||||
|
// Check if this previous device has already been walked
|
||||||
|
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {0} on {1}, this was already checked", destination, upstreamRoutingDevice.Key, destination.Key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||||
|
// level to enable switching on success
|
||||||
|
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
||||||
|
alreadyCheckedDevices, signalType, cycle, routeTable);
|
||||||
|
if (upstreamRoutingSuccess)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
|
||||||
|
goodInputPort = inputTieToTry.DestinationPort;
|
||||||
|
break; // Stop looping the inputs in this cycle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (goodInputPort == null)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a route on corresponding inputPort. *** Do the route ***
|
||||||
|
|
||||||
|
if (outputPortToUse == null)
|
||||||
|
{
|
||||||
|
// it's a sink device
|
||||||
|
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
||||||
|
}
|
||||||
|
else if (destination is IRouting)
|
||||||
|
{
|
||||||
|
routeTable.Routes.Add(new RouteSwitchDescriptor(outputPortToUse, goodInputPort));
|
||||||
|
}
|
||||||
|
else // device is merely IRoutingInputOutputs
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
261
src/PepperDash.Essentials.Core/Routing/GenericExtensions.cs
Normal file
261
src/PepperDash.Essentials.Core/Routing/GenericExtensions.cs
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Crestron.SimplSharpPro;
|
||||||
|
using Crestron.SimplSharpPro.DM;
|
||||||
|
|
||||||
|
using PepperDash.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||||
|
/// on those destinations.
|
||||||
|
/// </summary>
|
||||||
|
public static class GenericExtensions
|
||||||
|
{
|
||||||
|
//private static readonly Dictionary<string, RouteRequest<TInputSelector, TOutputSelector>> RouteRequests = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||||
|
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||||
|
/// in RouteDescriptorCollection.DefaultCollection
|
||||||
|
/// </summary>
|
||||||
|
public static void ReleaseAndMakeRoute<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
var routeRequest = new RouteRequest<TInputSelector, TOutputSelector> {
|
||||||
|
Destination = destination,
|
||||||
|
Source = source,
|
||||||
|
SignalType = signalType
|
||||||
|
};
|
||||||
|
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
var existingRouteRequest = destination.GetRouteRequest<TOutputSelector>();
|
||||||
|
|
||||||
|
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||||
|
if (existingRouteRequest != null && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
destination.UpdateRouteRequest(routeRequest);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//New Request
|
||||||
|
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
|
{
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
|
destination.UpdateRouteRequest(routeRequest);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down. Storing route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingRouteRequest != null && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||||
|
{
|
||||||
|
destination.UpdateRouteRequest<TOutputSelector>(null);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.ReleaseRoute();
|
||||||
|
|
||||||
|
RunRouteRequest(routeRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RunRouteRequest<TInputSelector, TOutputSelector>(RouteRequest<TInputSelector, TOutputSelector> request)
|
||||||
|
{
|
||||||
|
if (request.Source == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
||||||
|
|
||||||
|
if (newRoute == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
|
||||||
|
|
||||||
|
newRoute.ExecuteRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will release the existing route on the destination, if it is found in
|
||||||
|
/// RouteDescriptorCollection.DefaultCollection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
public static void ReleaseRoute<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination)
|
||||||
|
{
|
||||||
|
var existingRouteRequest = destination.GetRouteRequest<TOutputSelector>();
|
||||||
|
|
||||||
|
if (existingRouteRequest != null && destination is IWarmingCooling)
|
||||||
|
{
|
||||||
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
destination.UpdateRouteRequest<TOutputSelector>(null);
|
||||||
|
|
||||||
|
var current = RouteDescriptorCollection<TInputSelector, TOutputSelector>.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||||
|
if (current != null)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
|
||||||
|
current.ReleaseRoutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||||
|
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||||
|
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||||
|
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||||
|
/// discovered, then null is returned
|
||||||
|
/// </summary>
|
||||||
|
public static RouteDescriptor<TInputSelector, TOutputSelector> GetRouteToSource<TInputSelector, TOutputSelector>(this IRoutingSink<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
var routeDescriptor = new RouteDescriptor<TInputSelector, TOutputSelector>(source, destination, signalType);
|
||||||
|
|
||||||
|
// if it's a single signal type, find the route
|
||||||
|
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {0}", null, source.Key);
|
||||||
|
|
||||||
|
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescriptor))
|
||||||
|
routeDescriptor = null;
|
||||||
|
|
||||||
|
return routeDescriptor;
|
||||||
|
}
|
||||||
|
// otherwise, audioVideo needs to be handled as two steps.
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build audio and video routes from {0}", destination, source.Key);
|
||||||
|
|
||||||
|
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescriptor);
|
||||||
|
|
||||||
|
if (!audioSuccess)
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
|
||||||
|
|
||||||
|
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescriptor);
|
||||||
|
|
||||||
|
if (!videoSuccess)
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
|
||||||
|
|
||||||
|
if (!audioSuccess && !videoSuccess)
|
||||||
|
routeDescriptor = null;
|
||||||
|
|
||||||
|
|
||||||
|
return routeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||||
|
/// desired source and if not found, invoke this function for the each input port
|
||||||
|
/// hoping to find the source.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="destination"></param>
|
||||||
|
/// <param name="source"></param>
|
||||||
|
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
||||||
|
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||||
|
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||||
|
/// <param name="cycle">Just an informational counter</param>
|
||||||
|
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||||
|
/// <returns>true if source is hit</returns>
|
||||||
|
static bool GetRouteToSource<TInputSelector, TOutputSelector>(this IRoutingInputs<TInputSelector> destination, IRoutingOutputs<TOutputSelector> source,
|
||||||
|
RoutingOutputPort<TOutputSelector> outputPortToUse, List<IRoutingInputsOutputs<TInputSelector, TOutputSelector>> alreadyCheckedDevices,
|
||||||
|
eRoutingSignalType signalType, int cycle, RouteDescriptor<TInputSelector, TOutputSelector> routeTable)
|
||||||
|
{
|
||||||
|
|
||||||
|
cycle++;
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
|
||||||
|
|
||||||
|
RoutingInputPort<TInputSelector> goodInputPort = null;
|
||||||
|
|
||||||
|
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
||||||
|
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
|
||||||
|
|
||||||
|
// find a direct tie
|
||||||
|
var directTie = destDevInputTies.FirstOrDefault(
|
||||||
|
t => t.DestinationPort.ParentDevice == destination
|
||||||
|
&& t.SourcePort.ParentDevice == source);
|
||||||
|
if (directTie != null) // Found a tie directly to the source
|
||||||
|
{
|
||||||
|
goodInputPort = directTie.DestinationPort;
|
||||||
|
}
|
||||||
|
else // no direct-connect. Walk back devices.
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {0}. Walking down tie lines", destination, source.Key);
|
||||||
|
|
||||||
|
// No direct tie? Run back out on the inputs' attached devices...
|
||||||
|
// Only the ones that are routing devices
|
||||||
|
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||||
|
|
||||||
|
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||||
|
if (alreadyCheckedDevices == null)
|
||||||
|
alreadyCheckedDevices = new List<IRoutingInputsOutputs<TInputSelector, TOutputSelector>>();
|
||||||
|
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs<TInputSelector, TOutputSelector>);
|
||||||
|
|
||||||
|
foreach (var inputTieToTry in attachedMidpoints)
|
||||||
|
{
|
||||||
|
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
||||||
|
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs<TInputSelector, TOutputSelector>;
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {0}", destination, upstreamRoutingDevice.Key);
|
||||||
|
|
||||||
|
// Check if this previous device has already been walked
|
||||||
|
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {0} on {1}, this was already checked", destination, upstreamRoutingDevice.Key, destination.Key);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||||
|
// level to enable switching on success
|
||||||
|
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
||||||
|
alreadyCheckedDevices, signalType, cycle, routeTable);
|
||||||
|
if (upstreamRoutingSuccess)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
|
||||||
|
goodInputPort = inputTieToTry.DestinationPort;
|
||||||
|
break; // Stop looping the inputs in this cycle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (goodInputPort == null)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a route on corresponding inputPort. *** Do the route ***
|
||||||
|
|
||||||
|
if (outputPortToUse == null)
|
||||||
|
{
|
||||||
|
// it's a sink device
|
||||||
|
routeTable.Routes.Add(new RouteSwitchDescriptor<TInputSelector, TOutputSelector>(goodInputPort));
|
||||||
|
}
|
||||||
|
else if (destination is IRouting)
|
||||||
|
{
|
||||||
|
routeTable.Routes.Add(new RouteSwitchDescriptor<TInputSelector, TOutputSelector>(outputPortToUse, goodInputPort));
|
||||||
|
}
|
||||||
|
else // device is merely IRoutingInputOutputs
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/* Unmerged change from project 'PepperDash.Essentials.Core (net6)'
|
||||||
|
Before:
|
||||||
|
namespace PepperDash.Essentials.Core.Routing.Interfaces
|
||||||
|
After:
|
||||||
|
using PepperDash;
|
||||||
|
using PepperDash.Essentials;
|
||||||
|
using PepperDash.Essentials.Core;
|
||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
using PepperDash.Essentials.Core.Routing.Interfaces
|
||||||
|
*/
|
||||||
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The handler type for a Room's SourceInfoChange
|
||||||
|
/// </summary>
|
||||||
|
public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);
|
||||||
|
//*******************************************************************************************
|
||||||
|
// Interfaces
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For rooms with a single presentation source, change event
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasCurrentSourceInfoChange
|
||||||
|
{
|
||||||
|
string CurrentSourceInfoKey { get; set; }
|
||||||
|
SourceListItem CurrentSourceInfo { get; set; }
|
||||||
|
event SourceInfoChangeHandler CurrentSourceChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Routing
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
{
|
{
|
||||||
public interface IVideoSync: IKeyed
|
public interface IVideoSync : IKeyed
|
||||||
{
|
{
|
||||||
bool VideoSyncDetected { get; }
|
bool VideoSyncDetected { get; }
|
||||||
|
|
||||||
|
|||||||
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
|
||||||
|
/// </summary>
|
||||||
|
public interface IRmcRouting : IRoutingNumeric
|
||||||
|
{
|
||||||
|
IntFeedback AudioVideoSourceNumericFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRmcRouting with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface IRmcRoutingWithFeedback : IRmcRouting
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
|
||||||
|
/// signal chain, that do switching, must implement this for routing to work otherwise
|
||||||
|
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
|
||||||
|
/// device.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRouting : IRoutingInputsOutputs
|
||||||
|
{
|
||||||
|
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public interface IRouting<TInputSelector,TOutputSelector> : IRoutingInputsOutputs
|
||||||
|
{
|
||||||
|
void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using PepperDash.Core;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an event structure for reporting output route data
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingFeedback : IKeyName
|
||||||
|
{
|
||||||
|
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
|
||||||
|
//void OnSwitchChange(RoutingNumericEventArgs e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using Crestron.SimplSharpPro;
|
||||||
|
using Crestron.SimplSharpPro.DM;
|
||||||
|
|
||||||
|
using PepperDash.Core;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
|
||||||
|
public interface IRoutingHasVideoInputSyncFeedbacks
|
||||||
|
{
|
||||||
|
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a class that has a collection of RoutingInputPorts
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingInputs : IKeyed
|
||||||
|
{
|
||||||
|
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public interface IRoutingInputs<TSelector> : IKeyed
|
||||||
|
{
|
||||||
|
RoutingPortCollection<RoutingInputPort<TSelector>, TSelector> InputPorts { get; }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -1,426 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharpPro;
|
|
||||||
using Crestron.SimplSharpPro.DM;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
using Serilog.Events;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
|
||||||
{
|
|
||||||
public class RouteRequest
|
|
||||||
{
|
|
||||||
public IRoutingSink Destination {get; set;}
|
|
||||||
public IRoutingOutputs Source {get; set;}
|
|
||||||
public eRoutingSignalType SignalType {get; set;}
|
|
||||||
|
|
||||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
|
||||||
{
|
|
||||||
var coolingDevice = sender as IWarmingCooling;
|
|
||||||
|
|
||||||
if(args.BoolValue == false)
|
|
||||||
{
|
|
||||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
|
||||||
|
|
||||||
if(sender == null) return;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
|
||||||
/// on those destinations.
|
|
||||||
/// </summary>
|
|
||||||
public static class IRoutingInputsExtensions
|
|
||||||
{
|
|
||||||
private static Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
|
||||||
/// <summary>
|
|
||||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
|
||||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
|
||||||
/// in RouteDescriptorCollection.DefaultCollection
|
|
||||||
/// </summary>
|
|
||||||
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
|
||||||
{
|
|
||||||
var routeRequest = new RouteRequest {
|
|
||||||
Destination = destination,
|
|
||||||
Source = source,
|
|
||||||
SignalType = signalType
|
|
||||||
};
|
|
||||||
|
|
||||||
var coolingDevice = destination as IWarmingCooling;
|
|
||||||
|
|
||||||
RouteRequest existingRouteRequest;
|
|
||||||
|
|
||||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
|
||||||
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
|
||||||
{
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
|
||||||
|
|
||||||
RouteRequests[destination.Key] = routeRequest;
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//New Request
|
|
||||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
|
||||||
{
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
|
||||||
|
|
||||||
RouteRequests.Add(destination.Key, routeRequest);
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
|
||||||
{
|
|
||||||
RouteRequests.Remove(destination.Key);
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", destination.Key, routeRequest.Source.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
destination.ReleaseRoute();
|
|
||||||
|
|
||||||
RunRouteRequest(routeRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RunRouteRequest(RouteRequest request)
|
|
||||||
{
|
|
||||||
if (request.Source == null) return;
|
|
||||||
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
|
||||||
if (newRoute == null) return;
|
|
||||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, request.Destination, "Executing full route");
|
|
||||||
newRoute.ExecuteRoutes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Will release the existing route on the destination, if it is found in
|
|
||||||
/// RouteDescriptorCollection.DefaultCollection
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="destination"></param>
|
|
||||||
public static void ReleaseRoute(this IRoutingSink destination)
|
|
||||||
{
|
|
||||||
RouteRequest existingRequest;
|
|
||||||
|
|
||||||
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
|
|
||||||
{
|
|
||||||
var coolingDevice = destination as IWarmingCooling;
|
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteRequests.Remove(destination.Key);
|
|
||||||
|
|
||||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
|
||||||
if (current != null)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Releasing current route: {0}", current.Source.Key);
|
|
||||||
current.ReleaseRoutes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
|
||||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
|
||||||
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
|
||||||
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
|
||||||
/// discovered, then null is returned
|
|
||||||
/// </summary>
|
|
||||||
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
|
||||||
{
|
|
||||||
var routeDescr = new RouteDescriptor(source, destination, signalType);
|
|
||||||
// if it's a single signal type, find the route
|
|
||||||
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build source route from {0}", source.Key);
|
|
||||||
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
|
|
||||||
routeDescr = null;
|
|
||||||
}
|
|
||||||
// otherwise, audioVideo needs to be handled as two steps.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build audio and video routes from {0}", source.Key);
|
|
||||||
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
|
|
||||||
if (!audioSuccess)
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find audio route to {0}", source.Key);
|
|
||||||
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
|
|
||||||
if (!videoSuccess)
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find video route to {0}", source.Key);
|
|
||||||
if (!audioSuccess && !videoSuccess)
|
|
||||||
routeDescr = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Debug.LogMessage(LogEventLevel.Debug, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
|
|
||||||
return routeDescr;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
|
||||||
/// desired source and if not found, invoke this function for the each input port
|
|
||||||
/// hoping to find the source.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="destination"></param>
|
|
||||||
/// <param name="source"></param>
|
|
||||||
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
|
||||||
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
|
||||||
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
|
||||||
/// <param name="cycle">Just an informational counter</param>
|
|
||||||
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
|
||||||
/// <returns>true if source is hit</returns>
|
|
||||||
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
|
||||||
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
|
||||||
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
|
|
||||||
{
|
|
||||||
cycle++;
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
|
|
||||||
|
|
||||||
RoutingInputPort goodInputPort = null;
|
|
||||||
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
|
||||||
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
|
|
||||||
|
|
||||||
// find a direct tie
|
|
||||||
var directTie = destDevInputTies.FirstOrDefault(
|
|
||||||
t => t.DestinationPort.ParentDevice == destination
|
|
||||||
&& t.SourcePort.ParentDevice == source);
|
|
||||||
if (directTie != null) // Found a tie directly to the source
|
|
||||||
{
|
|
||||||
goodInputPort = directTie.DestinationPort;
|
|
||||||
}
|
|
||||||
else // no direct-connect. Walk back devices.
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
|
|
||||||
|
|
||||||
// No direct tie? Run back out on the inputs' attached devices...
|
|
||||||
// Only the ones that are routing devices
|
|
||||||
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
|
||||||
|
|
||||||
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
|
||||||
if (alreadyCheckedDevices == null)
|
|
||||||
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
|
||||||
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
|
||||||
|
|
||||||
foreach (var inputTieToTry in attachedMidpoints)
|
|
||||||
{
|
|
||||||
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
|
||||||
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
|
|
||||||
|
|
||||||
// Check if this previous device has already been walked
|
|
||||||
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// haven't seen this device yet. Do it. Pass the output port to the next
|
|
||||||
// level to enable switching on success
|
|
||||||
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
|
||||||
alreadyCheckedDevices, signalType, cycle, routeTable);
|
|
||||||
if (upstreamRoutingSuccess)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Upstream device route found");
|
|
||||||
goodInputPort = inputTieToTry.DestinationPort;
|
|
||||||
break; // Stop looping the inputs in this cycle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have a route on corresponding inputPort. *** Do the route ***
|
|
||||||
if (goodInputPort != null)
|
|
||||||
{
|
|
||||||
//Debug.LogMessage(LogEventLevel.Verbose, destination, "adding RouteDescriptor");
|
|
||||||
if (outputPortToUse == null)
|
|
||||||
{
|
|
||||||
// it's a sink device
|
|
||||||
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
|
||||||
}
|
|
||||||
else if (destination is IRouting)
|
|
||||||
{
|
|
||||||
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
|
|
||||||
}
|
|
||||||
else // device is merely IRoutingInputOutputs
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, " No routing. Passthrough device");
|
|
||||||
//Debug.LogMessage(LogEventLevel.Verbose, destination, "Exiting cycle {0}", cycle);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "No route found to {0}", source.Key);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
|
||||||
/// </summary>
|
|
||||||
public class RouteDescriptorCollection
|
|
||||||
{
|
|
||||||
public static RouteDescriptorCollection DefaultCollection
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_DefaultCollection == null)
|
|
||||||
_DefaultCollection = new RouteDescriptorCollection();
|
|
||||||
return _DefaultCollection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static RouteDescriptorCollection _DefaultCollection;
|
|
||||||
|
|
||||||
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
|
||||||
/// destination exists already, it will not be added - in order to preserve
|
|
||||||
/// proper route releasing.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="descriptor"></param>
|
|
||||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
|
||||||
{
|
|
||||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
|
||||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
RouteDescriptors.Add(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the RouteDescriptor for a destination
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
|
||||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
|
||||||
{
|
|
||||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
|
||||||
/// Returns null if no route with the provided destination exists.
|
|
||||||
/// </summary>
|
|
||||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
|
||||||
{
|
|
||||||
var descr = GetRouteDescriptorForDestination(destination);
|
|
||||||
if (descr != null)
|
|
||||||
RouteDescriptors.Remove(descr);
|
|
||||||
return descr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an collection of individual route steps between Source and Destination
|
|
||||||
/// </summary>
|
|
||||||
public class RouteDescriptor
|
|
||||||
{
|
|
||||||
public IRoutingInputs Destination { get; private set; }
|
|
||||||
public IRoutingOutputs Source { get; private set; }
|
|
||||||
public eRoutingSignalType SignalType { get; private set; }
|
|
||||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
|
||||||
{
|
|
||||||
Destination = destination;
|
|
||||||
Source = source;
|
|
||||||
SignalType = signalType;
|
|
||||||
Routes = new List<RouteSwitchDescriptor>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Executes all routes described in this collection. Typically called via
|
|
||||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
|
||||||
/// </summary>
|
|
||||||
public void ExecuteRoutes()
|
|
||||||
{
|
|
||||||
foreach (var route in Routes)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", route.ToString());
|
|
||||||
if (route.SwitchingDevice is IRoutingSink)
|
|
||||||
{
|
|
||||||
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
|
|
||||||
if (device == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
device.ExecuteSwitch(route.InputPort.Selector);
|
|
||||||
}
|
|
||||||
else if (route.SwitchingDevice is IRouting)
|
|
||||||
{
|
|
||||||
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
|
||||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all routes in this collection. Typically called via
|
|
||||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
|
||||||
/// </summary>
|
|
||||||
public void ReleaseRoutes()
|
|
||||||
{
|
|
||||||
foreach (var route in Routes)
|
|
||||||
{
|
|
||||||
if (route.SwitchingDevice is IRouting)
|
|
||||||
{
|
|
||||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
|
||||||
// responsible for responding appropriately.
|
|
||||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
|
||||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents an individual link for a route
|
|
||||||
/// </summary>
|
|
||||||
public class RouteSwitchDescriptor
|
|
||||||
{
|
|
||||||
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
|
||||||
public RoutingOutputPort OutputPort { get; set; }
|
|
||||||
public RoutingInputPort InputPort { get; set; }
|
|
||||||
|
|
||||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
|
||||||
{
|
|
||||||
InputPort = inputPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
|
||||||
{
|
|
||||||
InputPort = inputPort;
|
|
||||||
OutputPort = outputPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if(SwitchingDevice is IRouting)
|
|
||||||
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
|
||||||
else
|
|
||||||
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For devices like RMCs, baluns, other devices with no switching.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /// <summary>
|
||||||
|
/// For devices like RMCs, baluns, other devices with no switching.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingInputsOutputs<TInputSelector, TOutputSelector> : IRoutingInputs<TInputSelector>, IRoutingOutputs<TOutputSelector>
|
||||||
|
{
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface IRoutingNumeric : IRouting
|
||||||
|
{
|
||||||
|
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRoutingNumeric with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a class that has a collection of RoutingOutputPorts
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
public interface IRoutingOutputs : IKeyed
|
||||||
|
{
|
||||||
|
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public interface IRoutingOutputs<TSelector> : IKeyed
|
||||||
|
{
|
||||||
|
RoutingPortCollection<RoutingOutputPort<TSelector>, TSelector> OutputPorts { get; }
|
||||||
|
}*/
|
||||||
|
}
|
||||||
22
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
22
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// For fixed-source endpoint devices
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// For fixed-source endpoint devices
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSink<TSelector> : IRoutingInputs<TSelector>, IHasCurrentSourceInfoChange
|
||||||
|
{
|
||||||
|
void UpdateRouteRequest<TOutputSelector>(RouteRequest<TSelector, TOutputSelector> request);
|
||||||
|
|
||||||
|
RouteRequest<TSelector, TOutputSelector> GetRouteRequest<TOutputSelector>();
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Endpoint device like a display, that selects inputs
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSinkWithSwitching : IRoutingSink
|
||||||
|
{
|
||||||
|
void ExecuteSwitch(object inputSelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /// <summary>
|
||||||
|
/// Endpoint device like a display, that selects inputs
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSinkWithSwitching<TSelector> : IRoutingSink<TSelector>
|
||||||
|
{
|
||||||
|
void ExecuteSwitch(TSelector inputSelector);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||||
|
/// </summary>
|
||||||
|
public interface IRoutingSource : IRoutingOutputs
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface IRoutingWithClear : IRouting
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Clears a route to an output, however a device needs to do that
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="outputSelector">Output to clear</param>
|
||||||
|
/// <param name="signalType">signal type to clear</param>
|
||||||
|
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public interface ITxRouting : IRoutingNumeric
|
||||||
|
{
|
||||||
|
IntFeedback VideoSourceNumericFeedback { get; }
|
||||||
|
IntFeedback AudioSourceNumericFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an IRmcRouting with a feedback event
|
||||||
|
/// </summary>
|
||||||
|
public interface ITxRoutingWithFeedback : ITxRouting
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
152
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
152
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Crestron.SimplSharpPro;
|
||||||
|
|
||||||
|
using PepperDash.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an collection of individual route steps between Source and Destination
|
||||||
|
/// </summary>
|
||||||
|
public class RouteDescriptor
|
||||||
|
{
|
||||||
|
public IRoutingInputs Destination { get; private set; }
|
||||||
|
public IRoutingOutputs Source { get; private set; }
|
||||||
|
public eRoutingSignalType SignalType { get; private set; }
|
||||||
|
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
Destination = destination;
|
||||||
|
Source = source;
|
||||||
|
SignalType = signalType;
|
||||||
|
Routes = new List<RouteSwitchDescriptor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all routes described in this collection. Typically called via
|
||||||
|
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||||
|
/// </summary>
|
||||||
|
public void ExecuteRoutes()
|
||||||
|
{
|
||||||
|
foreach (var route in Routes)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}",null, route.ToString());
|
||||||
|
|
||||||
|
if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)
|
||||||
|
{
|
||||||
|
sink.ExecuteSwitch(route.InputPort.Selector);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||||
|
{
|
||||||
|
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||||
|
|
||||||
|
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all routes in this collection. Typically called via
|
||||||
|
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||||
|
/// </summary>
|
||||||
|
public void ReleaseRoutes()
|
||||||
|
{
|
||||||
|
foreach (var route in Routes)
|
||||||
|
{
|
||||||
|
if (route.SwitchingDevice is IRouting)
|
||||||
|
{
|
||||||
|
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||||
|
// responsible for responding appropriately.
|
||||||
|
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}",null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||||
|
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// Represents an collection of individual route steps between Source and Destination
|
||||||
|
/// </summary>
|
||||||
|
public class RouteDescriptor<TInputSelector, TOutputSelector>
|
||||||
|
{
|
||||||
|
public IRoutingInputs<TInputSelector> Destination { get; private set; }
|
||||||
|
public IRoutingOutputs<TOutputSelector> Source { get; private set; }
|
||||||
|
public eRoutingSignalType SignalType { get; private set; }
|
||||||
|
public List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>> Routes { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
public RouteDescriptor(IRoutingOutputs<TOutputSelector> source, IRoutingInputs<TInputSelector> destination, eRoutingSignalType signalType)
|
||||||
|
{
|
||||||
|
Destination = destination;
|
||||||
|
Source = source;
|
||||||
|
SignalType = signalType;
|
||||||
|
Routes = new List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes all routes described in this collection. Typically called via
|
||||||
|
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||||
|
/// </summary>
|
||||||
|
public void ExecuteRoutes()
|
||||||
|
{
|
||||||
|
foreach (var route in Routes)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", null, route.ToString());
|
||||||
|
|
||||||
|
if (route.SwitchingDevice is IRoutingSinkWithSwitching<TInputSelector> sink)
|
||||||
|
{
|
||||||
|
sink.ExecuteSwitch(route.InputPort.Selector);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||||
|
{
|
||||||
|
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||||
|
|
||||||
|
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||||
|
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all routes in this collection. Typically called via
|
||||||
|
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||||
|
/// </summary>
|
||||||
|
public void ReleaseRoutes()
|
||||||
|
{
|
||||||
|
foreach (var route in Routes)
|
||||||
|
{
|
||||||
|
if (route.SwitchingDevice is IRouting<TInputSelector, TOutputSelector>)
|
||||||
|
{
|
||||||
|
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||||
|
// responsible for responding appropriately.
|
||||||
|
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||||
|
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||||
|
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using PepperDash.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||||
|
/// </summary>
|
||||||
|
public class RouteDescriptorCollection
|
||||||
|
{
|
||||||
|
public static RouteDescriptorCollection DefaultCollection
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_DefaultCollection == null)
|
||||||
|
_DefaultCollection = new RouteDescriptorCollection();
|
||||||
|
return _DefaultCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static RouteDescriptorCollection _DefaultCollection;
|
||||||
|
|
||||||
|
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||||
|
/// destination exists already, it will not be added - in order to preserve
|
||||||
|
/// proper route releasing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor"></param>
|
||||||
|
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
||||||
|
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RouteDescriptors.Add(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the RouteDescriptor for a destination
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||||
|
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
||||||
|
{
|
||||||
|
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||||
|
/// Returns null if no route with the provided destination exists.
|
||||||
|
/// </summary>
|
||||||
|
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
||||||
|
{
|
||||||
|
var descr = GetRouteDescriptorForDestination(destination);
|
||||||
|
if (descr != null)
|
||||||
|
RouteDescriptors.Remove(descr);
|
||||||
|
return descr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||||
|
/// </summary>
|
||||||
|
public class RouteDescriptorCollection<TInputSelector, TOutputSelector>
|
||||||
|
{
|
||||||
|
public static RouteDescriptorCollection<TInputSelector, TOutputSelector> DefaultCollection
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_DefaultCollection == null)
|
||||||
|
_DefaultCollection = new RouteDescriptorCollection<TInputSelector, TOutputSelector>();
|
||||||
|
return _DefaultCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static RouteDescriptorCollection<TInputSelector, TOutputSelector> _DefaultCollection;
|
||||||
|
|
||||||
|
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||||
|
/// destination exists already, it will not be added - in order to preserve
|
||||||
|
/// proper route releasing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptor"></param>
|
||||||
|
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||||
|
{
|
||||||
|
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
||||||
|
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RouteDescriptors.Add(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the RouteDescriptor for a destination
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||||
|
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs<TInputSelector> destination)
|
||||||
|
{
|
||||||
|
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||||
|
/// Returns null if no route with the provided destination exists.
|
||||||
|
/// </summary>
|
||||||
|
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs<TInputSelector> destination)
|
||||||
|
{
|
||||||
|
var descr = GetRouteDescriptorForDestination(destination);
|
||||||
|
if (descr != null)
|
||||||
|
RouteDescriptors.Remove(descr);
|
||||||
|
return descr;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
44
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
44
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public class RouteRequest
|
||||||
|
{
|
||||||
|
public IRoutingSink Destination {get; set;}
|
||||||
|
public IRoutingOutputs Source {get; set;}
|
||||||
|
public eRoutingSignalType SignalType {get; set;}
|
||||||
|
|
||||||
|
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||||
|
{
|
||||||
|
var coolingDevice = sender as IWarmingCooling;
|
||||||
|
|
||||||
|
if(args.BoolValue == false)
|
||||||
|
{
|
||||||
|
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||||
|
|
||||||
|
if(sender == null) return;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public class RouteRequest<TInputSelector, TOutputSelector>
|
||||||
|
{
|
||||||
|
public IRoutingSink<TInputSelector> Destination { get; set; }
|
||||||
|
public IRoutingOutputs<TOutputSelector> Source { get; set; }
|
||||||
|
public eRoutingSignalType SignalType { get; set; }
|
||||||
|
|
||||||
|
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||||
|
{
|
||||||
|
var coolingDevice = sender as IWarmingCooling;
|
||||||
|
|
||||||
|
if (args.BoolValue == false)
|
||||||
|
{
|
||||||
|
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||||
|
|
||||||
|
if (sender == null) return;
|
||||||
|
|
||||||
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an individual link for a route
|
||||||
|
/// </summary>
|
||||||
|
public class RouteSwitchDescriptor
|
||||||
|
{
|
||||||
|
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||||
|
public RoutingOutputPort OutputPort { get; set; }
|
||||||
|
public RoutingInputPort InputPort { get; set; }
|
||||||
|
|
||||||
|
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||||
|
{
|
||||||
|
InputPort = inputPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||||
|
{
|
||||||
|
InputPort = inputPort;
|
||||||
|
OutputPort = outputPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if(SwitchingDevice is IRouting)
|
||||||
|
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||||
|
else
|
||||||
|
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// Represents an individual link for a route
|
||||||
|
/// </summary>
|
||||||
|
public class RouteSwitchDescriptor<TInputSelector, TOutputSelector>
|
||||||
|
{
|
||||||
|
public IRoutingInputs<TInputSelector> SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||||
|
public RoutingOutputPort<TOutputSelector> OutputPort { get; set; }
|
||||||
|
public RoutingInputPort<TInputSelector> InputPort { get; set; }
|
||||||
|
|
||||||
|
public RouteSwitchDescriptor(RoutingInputPort<TInputSelector> inputPort)
|
||||||
|
{
|
||||||
|
InputPort = inputPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RouteSwitchDescriptor(RoutingOutputPort<TOutputSelector> outputPort, RoutingInputPort<TInputSelector> inputPort)
|
||||||
|
{
|
||||||
|
InputPort = inputPort;
|
||||||
|
OutputPort = outputPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (SwitchingDevice is IRouting)
|
||||||
|
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||||
|
else
|
||||||
|
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
77
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
77
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Basic RoutingInput with no statuses.
|
||||||
|
/// </summary>
|
||||||
|
public class RoutingInputPort : RoutingPort
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The IRoutingInputs object this lives on
|
||||||
|
/// </summary>
|
||||||
|
public IRoutingInputs ParentDevice { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for a basic RoutingInputPort
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
object selector, IRoutingInputs parent)
|
||||||
|
: this (key, type, connType, selector, parent, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for a virtual routing input port that lives inside a device. For example
|
||||||
|
/// the ports that link a DM card to a DM matrix bus
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isInternal">true for internal ports</param>
|
||||||
|
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
object selector, IRoutingInputs parent, bool isInternal)
|
||||||
|
: base(key, type, connType, selector, isInternal)
|
||||||
|
{
|
||||||
|
if (parent == null)
|
||||||
|
throw new ArgumentNullException(nameof(parent));
|
||||||
|
ParentDevice = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*/// <summary>
|
||||||
|
/// Basic RoutingInput with no statuses.
|
||||||
|
/// </summary>
|
||||||
|
public class RoutingInputPort<TSelector> : RoutingPort<TSelector>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The IRoutingInputs object this lives on
|
||||||
|
/// </summary>
|
||||||
|
public IRoutingInputs<TSelector> ParentDevice { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for a basic RoutingInputPort
|
||||||
|
/// </summary>
|
||||||
|
/// <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>
|
||||||
|
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
TSelector selector, IRoutingInputs<TSelector> parent)
|
||||||
|
: this(key, type, connType, selector, parent, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for a virtual routing input port that lives inside a device. For example
|
||||||
|
/// the ports that link a DM card to a DM matrix bus
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isInternal">true for internal ports</param>
|
||||||
|
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
TSelector selector, IRoutingInputs<TSelector> parent, bool isInternal)
|
||||||
|
: base(key, type, connType, selector, isInternal)
|
||||||
|
{
|
||||||
|
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
||||||
|
/// Will provide video statistics on connected signals
|
||||||
|
/// </summary>
|
||||||
|
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Video statuses attached to this port
|
||||||
|
/// </summary>
|
||||||
|
public VideoStatusOutputs VideoStatus { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <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) :
|
||||||
|
base(key, type, connType, selector, parent)
|
||||||
|
{
|
||||||
|
VideoStatus = new VideoStatusOutputs(funcs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Crestron.SimplSharpPro;
|
|
||||||
using Crestron.SimplSharpPro.DM;
|
|
||||||
|
|
||||||
using PepperDash.Core;
|
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The handler type for a Room's SourceInfoChange
|
|
||||||
/// </summary>
|
|
||||||
public delegate void SourceInfoChangeHandler(/*EssentialsRoomBase room,*/ SourceListItem info, ChangeType type);
|
|
||||||
|
|
||||||
|
|
||||||
//*******************************************************************************************
|
|
||||||
// Interfaces
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For rooms with a single presentation source, change event
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasCurrentSourceInfoChange
|
|
||||||
{
|
|
||||||
string CurrentSourceInfoKey { get; set; }
|
|
||||||
SourceListItem CurrentSourceInfo { get; set; }
|
|
||||||
event SourceInfoChangeHandler CurrentSourceChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a class that has a collection of RoutingInputPorts
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingInputs : IKeyed
|
|
||||||
{
|
|
||||||
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a class that has a collection of RoutingOutputPorts
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public interface IRoutingOutputs : IKeyed
|
|
||||||
{
|
|
||||||
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For fixed-source endpoint devices
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Endpoint device like a display, that selects inputs
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingSinkWithSwitching : IRoutingSink
|
|
||||||
{
|
|
||||||
//void ClearRoute();
|
|
||||||
void ExecuteSwitch(object inputSelector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For devices like RMCs, baluns, other devices with no switching.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
|
|
||||||
/// signal chain, that do switching, must implement this for routing to work otherwise
|
|
||||||
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
|
|
||||||
/// device.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRouting : IRoutingInputsOutputs
|
|
||||||
{
|
|
||||||
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IRoutingWithClear : IRouting
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Clears a route to an output, however a device needs to do that
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="outputSelector">Output to clear</param>
|
|
||||||
/// <param name="signalType">signal type to clear</param>
|
|
||||||
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IRoutingNumeric : IRouting
|
|
||||||
{
|
|
||||||
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITxRouting : IRoutingNumeric
|
|
||||||
{
|
|
||||||
IntFeedback VideoSourceNumericFeedback { get; }
|
|
||||||
IntFeedback AudioSourceNumericFeedback { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
|
|
||||||
/// </summary>
|
|
||||||
public interface IRmcRouting : IRoutingNumeric
|
|
||||||
{
|
|
||||||
IntFeedback AudioVideoSourceNumericFeedback { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an IRmcRouting with a feedback event
|
|
||||||
/// </summary>
|
|
||||||
public interface ITxRoutingWithFeedback : ITxRouting
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an IRmcRouting with a feedback event
|
|
||||||
/// </summary>
|
|
||||||
public interface IRmcRoutingWithFeedback : IRmcRouting
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingSource : IRoutingOutputs
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an event structure for reporting output route data
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingFeedback : IKeyName
|
|
||||||
{
|
|
||||||
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
|
|
||||||
//void OnSwitchChange(RoutingNumericEventArgs e);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an IRoutingNumeric with a feedback event
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines an IRouting with a feedback event
|
|
||||||
/// </summary>
|
|
||||||
public interface IRoutingWithFeedback : IRouting, IRoutingFeedback
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoutingNumericEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
|
|
||||||
public uint? Output { get; set; }
|
|
||||||
public uint? Input { get; set; }
|
|
||||||
|
|
||||||
public eRoutingSignalType SigType { get; set; }
|
|
||||||
public RoutingInputPort InputPort { get; set; }
|
|
||||||
public RoutingOutputPort OutputPort { get; set; }
|
|
||||||
|
|
||||||
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
|
||||||
eRoutingSignalType sigType)
|
|
||||||
: this(null, null, outputPort, inputPort, sigType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingNumericEventArgs()
|
|
||||||
: this(null, null, null, null, 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
|
||||||
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
|
||||||
{
|
|
||||||
OutputPort = outputPort;
|
|
||||||
InputPort = inputPort;
|
|
||||||
|
|
||||||
Output = output;
|
|
||||||
Input = input;
|
|
||||||
SigType = sigType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IRoutingHasVideoInputSyncFeedbacks
|
|
||||||
{
|
|
||||||
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public class RoutingNumericEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
|
||||||
|
public uint? Output { get; set; }
|
||||||
|
public uint? Input { get; set; }
|
||||||
|
|
||||||
|
public eRoutingSignalType SigType { get; set; }
|
||||||
|
public RoutingInputPort InputPort { get; set; }
|
||||||
|
public RoutingOutputPort OutputPort { get; set; }
|
||||||
|
|
||||||
|
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
||||||
|
eRoutingSignalType sigType)
|
||||||
|
: this(null, null, outputPort, inputPort, sigType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingNumericEventArgs()
|
||||||
|
: this(null, null, null, null, 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
||||||
|
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
||||||
|
{
|
||||||
|
OutputPort = outputPort;
|
||||||
|
InputPort = inputPort;
|
||||||
|
|
||||||
|
Output = output;
|
||||||
|
Input = input;
|
||||||
|
SigType = sigType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public class RoutingOutputPort : RoutingPort
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The IRoutingOutputs object this port lives on
|
||||||
|
/// </summary>
|
||||||
|
public IRoutingOutputs ParentDevice { get; private set; }
|
||||||
|
|
||||||
|
public InUseTracking InUseTracker { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
object selector, IRoutingOutputs parent, bool isInternal)
|
||||||
|
: base(key, type, connType, selector, isInternal)
|
||||||
|
{
|
||||||
|
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
|
InUseTracker = new InUseTracking();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ParentDevice.Key + ":" + Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public class RoutingOutputPort<TSelector> : RoutingPort<TSelector>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The IRoutingOutputs object this port lives on
|
||||||
|
/// </summary>
|
||||||
|
public IRoutingOutputs ParentDevice { get; private set; }
|
||||||
|
|
||||||
|
public InUseTracking InUseTracker { get; private set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// </summary>
|
||||||
|
/// <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,
|
||||||
|
TSelector selector, IRoutingOutputs parent)
|
||||||
|
: this(key, type, connType, selector, parent, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||||
|
TSelector selector, IRoutingOutputs parent, bool isInternal)
|
||||||
|
: base(key, type, connType, selector, isInternal)
|
||||||
|
{
|
||||||
|
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||||
|
InUseTracker = new InUseTracking();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return ParentDevice.Key + ":" + Key;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
@@ -6,10 +6,10 @@ using PepperDash.Core;
|
|||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
namespace PepperDash.Essentials.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for RoutingInput and Output ports
|
/// Base class for RoutingInput and Output ports
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class RoutingPort : IKeyed
|
public abstract class RoutingPort : IKeyed
|
||||||
{
|
{
|
||||||
public string Key { get; private set; }
|
public string Key { get; private set; }
|
||||||
public eRoutingSignalType Type { get; private set; }
|
public eRoutingSignalType Type { get; private set; }
|
||||||
@@ -29,180 +29,23 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
/*public abstract class RoutingPort<TSelector>:IKeyed
|
||||||
public enum eRoutingSignalType
|
{
|
||||||
{
|
public string Key { get; private set; }
|
||||||
Audio = 1,
|
public eRoutingSignalType Type { get; private set; }
|
||||||
Video = 2,
|
public eRoutingPortConnectionType ConnectionType { get; private set; }
|
||||||
AudioVideo = Audio | Video,
|
public readonly TSelector Selector;
|
||||||
UsbOutput = 8,
|
public bool IsInternal { get; private set; }
|
||||||
UsbInput = 16,
|
public object FeedbackMatchObject { get; set; }
|
||||||
SecondaryAudio = 32
|
public object Port { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
public enum eRoutingPortConnectionType
|
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal)
|
||||||
{
|
{
|
||||||
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
|
Key = key;
|
||||||
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
|
Type = type;
|
||||||
}
|
ConnectionType = connType;
|
||||||
|
Selector = selector;
|
||||||
/// <summary>
|
IsInternal = isInternal;
|
||||||
/// Basic RoutingInput with no statuses.
|
}*/
|
||||||
/// </summary>
|
}
|
||||||
public class RoutingInputPort : RoutingPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The IRoutingInputs object this lives on
|
|
||||||
/// </summary>
|
|
||||||
public IRoutingInputs ParentDevice { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for a basic RoutingInputPort
|
|
||||||
/// </summary>
|
|
||||||
/// <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>
|
|
||||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
|
||||||
object selector, IRoutingInputs parent)
|
|
||||||
: this (key, type, connType, selector, parent, false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor for a virtual routing input port that lives inside a device. For example
|
|
||||||
/// the ports that link a DM card to a DM matrix bus
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="isInternal">true for internal ports</param>
|
|
||||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
|
||||||
object selector, IRoutingInputs parent, bool isInternal)
|
|
||||||
: base(key, type, connType, selector, isInternal)
|
|
||||||
{
|
|
||||||
if (parent == null)
|
|
||||||
throw new ArgumentNullException("parent");
|
|
||||||
ParentDevice = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Static method to get a named port from a named device
|
|
||||||
///// </summary>
|
|
||||||
///// <returns>Returns null if device or port doesn't exist</returns>
|
|
||||||
//public static RoutingInputPort GetDevicePort(string deviceKey, string portKey)
|
|
||||||
//{
|
|
||||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingInputs;
|
|
||||||
// if (sourceDev == null)
|
|
||||||
// return null;
|
|
||||||
// return sourceDev.InputPorts[portKey];
|
|
||||||
//}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Static method to get a named port from a card in a named ICardPortsDevice device
|
|
||||||
///// Uses ICardPortsDevice.GetChildInputPort
|
|
||||||
///// </summary>
|
|
||||||
///// <param name="cardKey">'input-N'</param>
|
|
||||||
///// <returns>null if device, card or port doesn't exist</returns>
|
|
||||||
//public static RoutingInputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
|
|
||||||
//{
|
|
||||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
|
|
||||||
// if (sourceDev == null)
|
|
||||||
// return null;
|
|
||||||
// return sourceDev.GetChildInputPort(cardKey, portKey);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
|
||||||
/// Will provide video statistics on connected signals
|
|
||||||
/// </summary>
|
|
||||||
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Video statuses attached to this port
|
|
||||||
/// </summary>
|
|
||||||
public VideoStatusOutputs VideoStatus { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructor
|
|
||||||
/// </summary>
|
|
||||||
/// <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) :
|
|
||||||
base(key, type, connType, selector, parent)
|
|
||||||
{
|
|
||||||
VideoStatus = new VideoStatusOutputs(funcs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RoutingOutputPort : RoutingPort
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The IRoutingOutputs object this port lives on
|
|
||||||
/// </summary>
|
|
||||||
public IRoutingOutputs ParentDevice { get; private set; }
|
|
||||||
|
|
||||||
public InUseTracking InUseTracker { get; private set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// </summary>
|
|
||||||
/// <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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
|
||||||
object selector, IRoutingOutputs parent, bool isInternal)
|
|
||||||
: base(key, type, connType, selector, isInternal)
|
|
||||||
{
|
|
||||||
if (parent == null)
|
|
||||||
throw new ArgumentNullException("parent");
|
|
||||||
ParentDevice = parent;
|
|
||||||
InUseTracker = new InUseTracking();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return ParentDevice.Key + ":" + Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Static method to get a named port from a named device
|
|
||||||
///// </summary>
|
|
||||||
///// <returns>Returns null if device or port doesn't exist</returns>
|
|
||||||
//public static RoutingOutputPort GetDevicePort(string deviceKey, string portKey)
|
|
||||||
//{
|
|
||||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingOutputs;
|
|
||||||
// if (sourceDev == null)
|
|
||||||
// return null;
|
|
||||||
// var port = sourceDev.OutputPorts[portKey];
|
|
||||||
// if (port == null)
|
|
||||||
// Debug.LogMessage(LogEventLevel.Information, "WARNING: Device '{0}' does does not contain output port '{1}'", deviceKey, portKey);
|
|
||||||
// return port;
|
|
||||||
//}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// Static method to get a named port from a card in a named ICardPortsDevice device
|
|
||||||
///// Uses ICardPortsDevice.GetChildOutputPort on that device
|
|
||||||
///// </summary>
|
|
||||||
///// <param name="cardKey">'input-N' or 'output-N'</param>
|
|
||||||
///// <returns>null if device, card or port doesn't exist</returns>
|
|
||||||
//public static RoutingOutputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
|
|
||||||
//{
|
|
||||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
|
|
||||||
// if (sourceDev == null)
|
|
||||||
// return null;
|
|
||||||
// var port = sourceDev.GetChildOutputPort(cardKey, portKey);
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -23,4 +23,21 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* /// <summary>
|
||||||
|
/// Basically a List , with an indexer to find ports by key name
|
||||||
|
/// </summary>
|
||||||
|
public class RoutingPortCollection<T, TSelector> : List<T> where T : RoutingPort<TSelector>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Case-insensitive port lookup linked to ports' keys
|
||||||
|
/// </summary>
|
||||||
|
public T this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}*/
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public enum eRoutingPortConnectionType
|
||||||
|
{
|
||||||
|
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
|
||||||
|
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
Normal file
16
src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum eRoutingSignalType
|
||||||
|
{
|
||||||
|
Audio = 1,
|
||||||
|
Video = 2,
|
||||||
|
AudioVideo = Audio | Video,
|
||||||
|
UsbOutput = 8,
|
||||||
|
UsbInput = 16,
|
||||||
|
SecondaryAudio = 32
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user