diff --git a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
index da717cb4..c38c0fbd 100644
--- a/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
+++ b/src/PepperDash.Essentials.Core/PepperDash.Essentials.Core.csproj
@@ -21,6 +21,9 @@
pdbonly
+
+
+
diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
new file mode 100644
index 00000000..4d33ed4a
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs
@@ -0,0 +1,252 @@
+using PepperDash.Core;
+using Serilog.Events;
+using System.Collections.Generic;
+using System.Linq;
+
+
+namespace PepperDash.Essentials.Core
+{
+
+ ///
+ /// Extensions added to any IRoutingInputs classes to provide discovery-based routing
+ /// on those destinations.
+ ///
+ public static class Extensions
+ {
+ private static readonly Dictionary RouteRequests = new Dictionary();
+ ///
+ /// 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
+ ///
+ 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();
+ }
+
+ ///
+ /// Will release the existing route on the destination, if it is found in
+ /// RouteDescriptorCollection.DefaultCollection
+ ///
+ ///
+ 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();
+ }
+ }
+
+ ///
+ /// 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
+ ///
+ 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;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// The RoutingOutputPort whose link is being checked for a route
+ /// Prevents Devices from being twice-checked
+ /// This recursive function should not be called with AudioVideo
+ /// Just an informational counter
+ /// The RouteDescriptor being populated as the route is discovered
+ /// true if source is hit
+ static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
+ RoutingOutputPort outputPortToUse, List 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();
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/GenericExtensions.cs b/src/PepperDash.Essentials.Core/Routing/GenericExtensions.cs
new file mode 100644
index 00000000..4406d14f
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/GenericExtensions.cs
@@ -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
+{
+
+ ///
+ /// Extensions added to any IRoutingInputs classes to provide discovery-based routing
+ /// on those destinations.
+ ///
+ public static class GenericExtensions
+ {
+ //private static readonly Dictionary> RouteRequests = new();
+
+ ///
+ /// 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
+ ///
+ 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;
+ var existingRouteRequest = destination.GetRouteRequest();
+
+ //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(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(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();
+ }
+
+ ///
+ /// Will release the existing route on the destination, if it is found in
+ /// RouteDescriptorCollection.DefaultCollection
+ ///
+ ///
+ public static void ReleaseRoute(this IRoutingSink destination)
+ {
+ var existingRouteRequest = destination.GetRouteRequest();
+
+ if (existingRouteRequest != null && destination is IWarmingCooling)
+ {
+ var coolingDevice = destination as IWarmingCooling;
+
+ coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
+ }
+
+ destination.UpdateRouteRequest(null);
+
+ var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
+ if (current != null)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
+ current.ReleaseRoutes();
+ }
+ }
+
+ ///
+ /// 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
+ ///
+ 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;
+ }
+
+ ///
+ /// 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.
+ ///
+ ///
+ ///
+ /// The RoutingOutputPort whose link is being checked for a route
+ /// Prevents Devices from being twice-checked
+ /// This recursive function should not be called with AudioVideo
+ /// Just an informational counter
+ /// The RouteDescriptor being populated as the route is discovered
+ /// true if source is hit
+ static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
+ RoutingOutputPort outputPortToUse, List> 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>();
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs b/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs
new file mode 100644
index 00000000..20b46520
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IHasCurrentSourceInfoChange.cs
@@ -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
+{
+ ///
+ /// The handler type for a Room's SourceInfoChange
+ ///
+ public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);
+ //*******************************************************************************************
+ // Interfaces
+
+ ///
+ /// For rooms with a single presentation source, change event
+ ///
+ public interface IHasCurrentSourceInfoChange
+ {
+ string CurrentSourceInfoKey { get; set; }
+ SourceListItem CurrentSourceInfo { get; set; }
+ event SourceInfoChangeHandler CurrentSourceChange;
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IInputSync.cs b/src/PepperDash.Essentials.Core/Routing/IInputSync.cs
index 465b48a2..accb70a7 100644
--- a/src/PepperDash.Essentials.Core/Routing/IInputSync.cs
+++ b/src/PepperDash.Essentials.Core/Routing/IInputSync.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Routing
{
- public interface IVideoSync: IKeyed
+ public interface IVideoSync : IKeyed
{
bool VideoSyncDetected { get; }
diff --git a/src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs b/src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
new file mode 100644
index 00000000..bddcbcf7
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
@@ -0,0 +1,10 @@
+namespace PepperDash.Essentials.Core.Routing
+{
+ ///
+ /// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
+ ///
+ public interface IRmcRouting : IRoutingNumeric
+ {
+ IntFeedback AudioVideoSourceNumericFeedback { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRmcRoutingWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRmcRoutingWithFeedback.cs
new file mode 100644
index 00000000..3648204c
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRmcRoutingWithFeedback.cs
@@ -0,0 +1,11 @@
+using PepperDash.Essentials.Core.Routing;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines an IRmcRouting with a feedback event
+ ///
+ public interface IRmcRoutingWithFeedback : IRmcRouting
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRouting.cs b/src/PepperDash.Essentials.Core/Routing/IRouting.cs
new file mode 100644
index 00000000..e3231858
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRouting.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// 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.
+ ///
+ public interface IRouting : IRoutingInputsOutputs
+ {
+ void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
+ }
+
+ /*public interface IRouting : IRoutingInputsOutputs
+ {
+ void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
new file mode 100644
index 00000000..b8b22915
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
@@ -0,0 +1,16 @@
+using System;
+
+using PepperDash.Core;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines an event structure for reporting output route data
+ ///
+ public interface IRoutingFeedback : IKeyName
+ {
+ event EventHandler NumericSwitchChange;
+ //void OnSwitchChange(RoutingNumericEventArgs e);
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingHasVideoInputSyncFeedbacks.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingHasVideoInputSyncFeedbacks.cs
new file mode 100644
index 00000000..0f6d7834
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingHasVideoInputSyncFeedbacks.cs
@@ -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 VideoInputSyncFeedbacks { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
new file mode 100644
index 00000000..ff96fded
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
@@ -0,0 +1,18 @@
+using PepperDash.Core;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines a class that has a collection of RoutingInputPorts
+ ///
+ public interface IRoutingInputs : IKeyed
+ {
+ RoutingPortCollection InputPorts { get; }
+ }
+
+/* public interface IRoutingInputs : IKeyed
+ {
+ RoutingPortCollection, TSelector> InputPorts { get; }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingInputsExtensions.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingInputsExtensions.cs
deleted file mode 100644
index 605569d2..00000000
--- a/src/PepperDash.Essentials.Core/Routing/IRoutingInputsExtensions.cs
+++ /dev/null
@@ -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;
- }
- }
- }
-
- ///
- /// Extensions added to any IRoutingInputs classes to provide discovery-based routing
- /// on those destinations.
- ///
- public static class IRoutingInputsExtensions
- {
- private static Dictionary RouteRequests = new Dictionary();
- ///
- /// 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
- ///
- 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();
- }
-
- ///
- /// Will release the existing route on the destination, if it is found in
- /// RouteDescriptorCollection.DefaultCollection
- ///
- ///
- 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();
- }
- }
-
- ///
- /// 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
- ///
- 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;
- }
-
- ///
- /// 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.
- ///
- ///
- ///
- /// The RoutingOutputPort whose link is being checked for a route
- /// Prevents Devices from being twice-checked
- /// This recursive function should not be called with AudioVideo
- /// Just an informational counter
- /// The RouteDescriptor being populated as the route is discovered
- /// true if source is hit
- static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
- RoutingOutputPort outputPortToUse, List 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();
- 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
-
-
- ///
- /// A collection of RouteDescriptors - typically the static DefaultCollection is used
- ///
- public class RouteDescriptorCollection
- {
- public static RouteDescriptorCollection DefaultCollection
- {
- get
- {
- if (_DefaultCollection == null)
- _DefaultCollection = new RouteDescriptorCollection();
- return _DefaultCollection;
- }
- }
- static RouteDescriptorCollection _DefaultCollection;
-
- List RouteDescriptors = new List();
-
- ///
- /// 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.
- ///
- ///
- 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);
- }
-
- ///
- /// Gets the RouteDescriptor for a destination
- ///
- /// null if no RouteDescriptor for a destination exists
- public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
- {
- return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
- }
-
- ///
- /// Returns the RouteDescriptor for a given destination AND removes it from collection.
- /// Returns null if no route with the provided destination exists.
- ///
- public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
- {
- var descr = GetRouteDescriptorForDestination(destination);
- if (descr != null)
- RouteDescriptors.Remove(descr);
- return descr;
- }
- }
-
- ///
- /// Represents an collection of individual route steps between Source and Destination
- ///
- public class RouteDescriptor
- {
- public IRoutingInputs Destination { get; private set; }
- public IRoutingOutputs Source { get; private set; }
- public eRoutingSignalType SignalType { get; private set; }
- public List Routes { get; private set; }
-
-
- public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
- {
- Destination = destination;
- Source = source;
- SignalType = signalType;
- Routes = new List();
- }
-
- ///
- /// Executes all routes described in this collection. Typically called via
- /// extension method IRoutingInputs.ReleaseAndMakeRoute()
- ///
- 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);
- }
- }
- }
-
- ///
- /// Releases all routes in this collection. Typically called via
- /// extension method IRoutingInputs.ReleaseAndMakeRoute()
- ///
- 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));
- }
- }
-
- ///
- /// Represents an individual link for a route
- ///
- 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);
-
- }
- }
-}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingInputsOutputs.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingInputsOutputs.cs
new file mode 100644
index 00000000..e202fa3c
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingInputsOutputs.cs
@@ -0,0 +1,16 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// For devices like RMCs, baluns, other devices with no switching.
+ ///
+ public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
+ {
+ }
+
+/* ///
+ /// For devices like RMCs, baluns, other devices with no switching.
+ ///
+ public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
+ {
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingNumeric.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingNumeric.cs
new file mode 100644
index 00000000..d41909f1
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingNumeric.cs
@@ -0,0 +1,7 @@
+namespace PepperDash.Essentials.Core
+{
+ public interface IRoutingNumeric : IRouting
+ {
+ void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingNumericWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingNumericWithFeedback.cs
new file mode 100644
index 00000000..e278a193
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingNumericWithFeedback.cs
@@ -0,0 +1,9 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines an IRoutingNumeric with a feedback event
+ ///
+ public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
new file mode 100644
index 00000000..d3bc70de
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
@@ -0,0 +1,19 @@
+using PepperDash.Core;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines a class that has a collection of RoutingOutputPorts
+ ///
+
+ public interface IRoutingOutputs : IKeyed
+ {
+ RoutingPortCollection OutputPorts { get; }
+ }
+
+/* public interface IRoutingOutputs : IKeyed
+ {
+ RoutingPortCollection, TSelector> OutputPorts { get; }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
new file mode 100644
index 00000000..c51ed12f
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
@@ -0,0 +1,22 @@
+using PepperDash.Essentials.Core.Routing;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// For fixed-source endpoint devices
+ ///
+ public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
+ {
+
+ }
+
+ /*///
+ /// For fixed-source endpoint devices
+ ///
+ public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
+ {
+ void UpdateRouteRequest(RouteRequest request);
+
+ RouteRequest GetRouteRequest();
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs
new file mode 100644
index 00000000..b31bd973
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSinkWithSwitching.cs
@@ -0,0 +1,20 @@
+using System;
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Endpoint device like a display, that selects inputs
+ ///
+ public interface IRoutingSinkWithSwitching : IRoutingSink
+ {
+ void ExecuteSwitch(object inputSelector);
+ }
+
+/* ///
+ /// Endpoint device like a display, that selects inputs
+ ///
+ public interface IRoutingSinkWithSwitching : IRoutingSink
+ {
+ void ExecuteSwitch(TSelector inputSelector);
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
new file mode 100644
index 00000000..6ae51a2b
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
@@ -0,0 +1,9 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines an IRoutingOutputs devices as being a source - the start of the chain
+ ///
+ public interface IRoutingSource : IRoutingOutputs
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
new file mode 100644
index 00000000..077d4ecd
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
@@ -0,0 +1,12 @@
+namespace PepperDash.Essentials.Core
+{
+ public interface IRoutingWithClear : IRouting
+ {
+ ///
+ /// Clears a route to an output, however a device needs to do that
+ ///
+ /// Output to clear
+ /// signal type to clear
+ void ClearRoute(object outputSelector, eRoutingSignalType signalType);
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs b/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
new file mode 100644
index 00000000..0950e6a6
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
@@ -0,0 +1,8 @@
+namespace PepperDash.Essentials.Core
+{
+ public interface ITxRouting : IRoutingNumeric
+ {
+ IntFeedback VideoSourceNumericFeedback { get; }
+ IntFeedback AudioSourceNumericFeedback { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/ITxRoutingWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/ITxRoutingWithFeedback.cs
new file mode 100644
index 00000000..484fa134
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/ITxRoutingWithFeedback.cs
@@ -0,0 +1,9 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Defines an IRmcRouting with a feedback event
+ ///
+ public interface ITxRoutingWithFeedback : ITxRouting
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
new file mode 100644
index 00000000..ebacc809
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
@@ -0,0 +1,152 @@
+using System.Collections.Generic;
+using System.Linq;
+using Crestron.SimplSharpPro;
+
+using PepperDash.Core;
+using Serilog.Events;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents an collection of individual route steps between Source and Destination
+ ///
+ public class RouteDescriptor
+ {
+ public IRoutingInputs Destination { get; private set; }
+ public IRoutingOutputs Source { get; private set; }
+ public eRoutingSignalType SignalType { get; private set; }
+ public List Routes { get; private set; }
+
+
+ public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
+ {
+ Destination = destination;
+ Source = source;
+ SignalType = signalType;
+ Routes = new List();
+ }
+
+ ///
+ /// Executes all routes described in this collection. Typically called via
+ /// extension method IRoutingInputs.ReleaseAndMakeRoute()
+ ///
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// Releases all routes in this collection. Typically called via
+ /// extension method IRoutingInputs.ReleaseAndMakeRoute()
+ ///
+ 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));
+ }
+ }
+
+ /*///
+ /// Represents an collection of individual route steps between Source and Destination
+ ///
+ public class RouteDescriptor
+ {
+ public IRoutingInputs Destination { get; private set; }
+ public IRoutingOutputs Source { get; private set; }
+ public eRoutingSignalType SignalType { get; private set; }
+ public List> Routes { get; private set; }
+
+
+ public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
+ {
+ Destination = destination;
+ Source = source;
+ SignalType = signalType;
+ Routes = new List>();
+ }
+
+ ///
+ /// Executes all routes described in this collection. Typically called via
+ /// extension method IRoutingInputs.ReleaseAndMakeRoute()
+ ///
+ 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);
+ }
+ }
+ }
+
+ ///
+ /// Releases all routes in this collection. Typically called via
+ /// extension method IRoutingInputs.ReleaseAndMakeRoute()
+ ///
+ 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));
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs b/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs
new file mode 100644
index 00000000..c9e3da37
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RouteDescriptorCollection.cs
@@ -0,0 +1,123 @@
+using System.Collections.Generic;
+using System.Linq;
+
+using PepperDash.Core;
+using Serilog.Events;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// A collection of RouteDescriptors - typically the static DefaultCollection is used
+ ///
+ public class RouteDescriptorCollection
+ {
+ public static RouteDescriptorCollection DefaultCollection
+ {
+ get
+ {
+ if (_DefaultCollection == null)
+ _DefaultCollection = new RouteDescriptorCollection();
+ return _DefaultCollection;
+ }
+ }
+ private static RouteDescriptorCollection _DefaultCollection;
+
+ private readonly List RouteDescriptors = new List();
+
+ ///
+ /// 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.
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Gets the RouteDescriptor for a destination
+ ///
+ /// null if no RouteDescriptor for a destination exists
+ public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
+ {
+ return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
+ }
+
+ ///
+ /// Returns the RouteDescriptor for a given destination AND removes it from collection.
+ /// Returns null if no route with the provided destination exists.
+ ///
+ public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
+ {
+ var descr = GetRouteDescriptorForDestination(destination);
+ if (descr != null)
+ RouteDescriptors.Remove(descr);
+ return descr;
+ }
+ }
+
+ /*///
+ /// A collection of RouteDescriptors - typically the static DefaultCollection is used
+ ///
+ public class RouteDescriptorCollection
+ {
+ public static RouteDescriptorCollection DefaultCollection
+ {
+ get
+ {
+ if (_DefaultCollection == null)
+ _DefaultCollection = new RouteDescriptorCollection();
+ return _DefaultCollection;
+ }
+ }
+ private static RouteDescriptorCollection _DefaultCollection;
+
+ private readonly List RouteDescriptors = new List();
+
+ ///
+ /// 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.
+ ///
+ ///
+ 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);
+ }
+
+ ///
+ /// Gets the RouteDescriptor for a destination
+ ///
+ /// null if no RouteDescriptor for a destination exists
+ public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
+ {
+ return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
+ }
+
+ ///
+ /// Returns the RouteDescriptor for a given destination AND removes it from collection.
+ /// Returns null if no route with the provided destination exists.
+ ///
+ public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
+ {
+ var descr = GetRouteDescriptorForDestination(destination);
+ if (descr != null)
+ RouteDescriptors.Remove(descr);
+ return descr;
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
new file mode 100644
index 00000000..f4836c6d
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
@@ -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
+ {
+ 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;
+ }
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs b/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs
new file mode 100644
index 00000000..7b9fcabe
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs
@@ -0,0 +1,60 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Represents an individual link for a route
+ ///
+ 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);
+ }
+ }
+
+ /*///
+ /// Represents an individual link for a route
+ ///
+ 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);
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
new file mode 100644
index 00000000..7da945cb
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
@@ -0,0 +1,77 @@
+using System;
+
+
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// Basic RoutingInput with no statuses.
+ ///
+ public class RoutingInputPort : RoutingPort
+ {
+ ///
+ /// The IRoutingInputs object this lives on
+ ///
+ public IRoutingInputs ParentDevice { get; private set; }
+
+ ///
+ /// Constructor for a basic RoutingInputPort
+ ///
+ /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
+ /// May be string, number, whatever
+ /// The IRoutingInputs object this lives on
+ public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
+ object selector, IRoutingInputs parent)
+ : this (key, type, connType, selector, parent, false)
+ {
+ }
+
+ ///
+ /// 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
+ ///
+ /// true for internal ports
+ 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;
+ }
+ }
+
+ /*///
+ /// Basic RoutingInput with no statuses.
+ ///
+ public class RoutingInputPort : RoutingPort
+ {
+ ///
+ /// The IRoutingInputs object this lives on
+ ///
+ public IRoutingInputs ParentDevice { get; private set; }
+
+ ///
+ /// Constructor for a basic RoutingInputPort
+ ///
+ /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
+ /// May be string, number, whatever
+ /// The IRoutingInputs object this lives on
+ public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
+ TSelector selector, IRoutingInputs parent)
+ : this(key, type, connType, selector, parent, false)
+ {
+ }
+
+ ///
+ /// 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
+ ///
+ /// true for internal ports
+ public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
+ TSelector selector, IRoutingInputs parent, bool isInternal)
+ : base(key, type, connType, selector, isInternal)
+ {
+ ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs b/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs
new file mode 100644
index 00000000..bd4dd489
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs
@@ -0,0 +1,30 @@
+namespace PepperDash.Essentials.Core
+{
+ ///
+ /// A RoutingInputPort for devices like DM-TX and DM input cards.
+ /// Will provide video statistics on connected signals
+ ///
+ public class RoutingInputPortWithVideoStatuses : RoutingInputPort
+ {
+ ///
+ /// Video statuses attached to this port
+ ///
+ public VideoStatusOutputs VideoStatus { get; private set; }
+
+ ///
+ /// Constructor
+ ///
+ /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
+ /// May be string, number, whatever
+ /// The IRoutingInputs object this lives on
+ /// A VideoStatusFuncsWrapper used to assign the callback funcs that will get
+ /// the values for the various stats
+ public RoutingInputPortWithVideoStatuses(string key,
+ eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
+ IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
+ base(key, type, connType, selector, parent)
+ {
+ VideoStatus = new VideoStatusOutputs(funcs);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingInterfaces.cs b/src/PepperDash.Essentials.Core/Routing/RoutingInterfaces.cs
deleted file mode 100644
index bfaa27db..00000000
--- a/src/PepperDash.Essentials.Core/Routing/RoutingInterfaces.cs
+++ /dev/null
@@ -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
-{
-
- ///
- /// The handler type for a Room's SourceInfoChange
- ///
- public delegate void SourceInfoChangeHandler(/*EssentialsRoomBase room,*/ SourceListItem info, ChangeType type);
-
-
- //*******************************************************************************************
- // Interfaces
-
- ///
- /// For rooms with a single presentation source, change event
- ///
- public interface IHasCurrentSourceInfoChange
- {
- string CurrentSourceInfoKey { get; set; }
- SourceListItem CurrentSourceInfo { get; set; }
- event SourceInfoChangeHandler CurrentSourceChange;
- }
-
- ///
- /// Defines a class that has a collection of RoutingInputPorts
- ///
- public interface IRoutingInputs : IKeyed
- {
- RoutingPortCollection InputPorts { get; }
- }
-
- ///
- /// Defines a class that has a collection of RoutingOutputPorts
- ///
-
- public interface IRoutingOutputs : IKeyed
- {
- RoutingPortCollection OutputPorts { get; }
- }
-
- ///
- /// For fixed-source endpoint devices
- ///
- public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
- {
-
- }
-
- ///
- /// Endpoint device like a display, that selects inputs
- ///
- public interface IRoutingSinkWithSwitching : IRoutingSink
- {
- //void ClearRoute();
- void ExecuteSwitch(object inputSelector);
- }
-
- ///
- /// For devices like RMCs, baluns, other devices with no switching.
- ///
- public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
- {
- }
-
- ///
- /// 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.
- ///
- public interface IRouting : IRoutingInputsOutputs
- {
- void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
- }
-
- public interface IRoutingWithClear : IRouting
- {
- ///
- /// Clears a route to an output, however a device needs to do that
- ///
- /// Output to clear
- /// signal type to clear
- 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; }
- }
-
- ///
- /// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
- ///
- public interface IRmcRouting : IRoutingNumeric
- {
- IntFeedback AudioVideoSourceNumericFeedback { get; }
- }
-
-
- ///
- /// Defines an IRmcRouting with a feedback event
- ///
- public interface ITxRoutingWithFeedback : ITxRouting
- {
- }
-
- ///
- /// Defines an IRmcRouting with a feedback event
- ///
- public interface IRmcRoutingWithFeedback : IRmcRouting
- {
- }
-
- ///
- /// Defines an IRoutingOutputs devices as being a source - the start of the chain
- ///
- public interface IRoutingSource : IRoutingOutputs
- {
- }
-
- ///
- /// Defines an event structure for reporting output route data
- ///
- public interface IRoutingFeedback : IKeyName
- {
- event EventHandler NumericSwitchChange;
- //void OnSwitchChange(RoutingNumericEventArgs e);
- }
-
- ///
- /// Defines an IRoutingNumeric with a feedback event
- ///
- public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
- {
- }
-
- ///
- /// Defines an IRouting with a feedback event
- ///
- 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 VideoInputSyncFeedbacks { get; }
- }
-}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs b/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs
new file mode 100644
index 00000000..7ede13c2
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
new file mode 100644
index 00000000..445b3072
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
@@ -0,0 +1,75 @@
+using System;
+
+
+namespace PepperDash.Essentials.Core
+{
+ public class RoutingOutputPort : RoutingPort
+ {
+ ///
+ /// The IRoutingOutputs object this port lives on
+ ///
+ public IRoutingOutputs ParentDevice { get; private set; }
+
+ public InUseTracking InUseTracker { get; private set; }
+
+
+ ///
+ ///
+ /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
+ /// May be string, number, whatever
+ /// The IRoutingOutputs object this port lives on
+ 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 : RoutingPort
+ {
+ ///
+ /// The IRoutingOutputs object this port lives on
+ ///
+ public IRoutingOutputs ParentDevice { get; private set; }
+
+ public InUseTracking InUseTracker { get; private set; }
+
+
+ ///
+ ///
+ /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
+ /// May be string, number, whatever
+ /// The IRoutingOutputs object this port lives on
+ 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;
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs
index b6ef0001..21ab0743 100644
--- a/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs
@@ -6,10 +6,10 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core
{
- ///
- /// Base class for RoutingInput and Output ports
- ///
- public abstract class RoutingPort : IKeyed
+ ///
+ /// Base class for RoutingInput and Output ports
+ ///
+ public abstract class RoutingPort : IKeyed
{
public string Key { get; private set; }
public eRoutingSignalType Type { get; private set; }
@@ -29,180 +29,23 @@ namespace PepperDash.Essentials.Core
}
}
- [Flags]
- public enum eRoutingSignalType
- {
- Audio = 1,
- Video = 2,
- AudioVideo = Audio | Video,
- UsbOutput = 8,
- UsbInput = 16,
- SecondaryAudio = 32
- }
+ /*public abstract class RoutingPort:IKeyed
+ {
+ public string Key { get; private set; }
+ public eRoutingSignalType Type { get; private set; }
+ public eRoutingPortConnectionType ConnectionType { get; private set; }
+ public readonly TSelector Selector;
+ public bool IsInternal { get; private set; }
+ public object FeedbackMatchObject { get; set; }
+ public object Port { get; set; }
- public enum eRoutingPortConnectionType
- {
- None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
- Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
- }
-
- ///
- /// Basic RoutingInput with no statuses.
- ///
- public class RoutingInputPort : RoutingPort
- {
- ///
- /// The IRoutingInputs object this lives on
- ///
- public IRoutingInputs ParentDevice { get; private set; }
-
- ///
- /// Constructor for a basic RoutingInputPort
- ///
- /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
- /// May be string, number, whatever
- /// The IRoutingInputs object this lives on
- public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
- object selector, IRoutingInputs parent)
- : this (key, type, connType, selector, parent, false)
- {
- }
-
- ///
- /// 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
- ///
- /// true for internal ports
- 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;
- }
-
-
-
-
- /////
- ///// Static method to get a named port from a named device
- /////
- ///// Returns null if device or port doesn't exist
- //public static RoutingInputPort GetDevicePort(string deviceKey, string portKey)
- //{
- // var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingInputs;
- // if (sourceDev == null)
- // return null;
- // return sourceDev.InputPorts[portKey];
- //}
-
- /////
- ///// Static method to get a named port from a card in a named ICardPortsDevice device
- ///// Uses ICardPortsDevice.GetChildInputPort
- /////
- ///// 'input-N'
- ///// null if device, card or port doesn't exist
- //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);
- //}
- }
-
- ///
- /// A RoutingInputPort for devices like DM-TX and DM input cards.
- /// Will provide video statistics on connected signals
- ///
- public class RoutingInputPortWithVideoStatuses : RoutingInputPort
- {
- ///
- /// Video statuses attached to this port
- ///
- public VideoStatusOutputs VideoStatus { get; private set; }
-
- ///
- /// Constructor
- ///
- /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
- /// May be string, number, whatever
- /// The IRoutingInputs object this lives on
- /// A VideoStatusFuncsWrapper used to assign the callback funcs that will get
- /// the values for the various stats
- 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
- {
- ///
- /// The IRoutingOutputs object this port lives on
- ///
- public IRoutingOutputs ParentDevice { get; private set; }
-
- public InUseTracking InUseTracker { get; private set; }
-
-
- ///
- ///
- /// An object used to refer to this port in the IRouting device's ExecuteSwitch method.
- /// May be string, number, whatever
- /// The IRoutingOutputs object this port lives on
- 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;
- }
-
- /////
- ///// Static method to get a named port from a named device
- /////
- ///// Returns null if device or port doesn't exist
- //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;
- //}
-
- /////
- ///// Static method to get a named port from a card in a named ICardPortsDevice device
- ///// Uses ICardPortsDevice.GetChildOutputPort on that device
- /////
- ///// 'input-N' or 'output-N'
- ///// null if device, card or port doesn't exist
- //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);
- //}
- }
+ public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal)
+ {
+ Key = key;
+ Type = type;
+ ConnectionType = connType;
+ Selector = selector;
+ IsInternal = isInternal;
+ }*/
+ }
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingPortCollection.cs b/src/PepperDash.Essentials.Core/Routing/RoutingPortCollection.cs
index ba972ab7..0b9d765c 100644
--- a/src/PepperDash.Essentials.Core/Routing/RoutingPortCollection.cs
+++ b/src/PepperDash.Essentials.Core/Routing/RoutingPortCollection.cs
@@ -23,4 +23,21 @@ namespace PepperDash.Essentials.Core
}
}
}
+
+/* ///
+ /// Basically a List , with an indexer to find ports by key name
+ ///
+ public class RoutingPortCollection : List where T : RoutingPort
+ {
+ ///
+ /// Case-insensitive port lookup linked to ports' keys
+ ///
+ public T this[string key]
+ {
+ get
+ {
+ return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
+ }
+ }
+ }*/
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/eRoutingPortConnectionType.cs b/src/PepperDash.Essentials.Core/Routing/eRoutingPortConnectionType.cs
new file mode 100644
index 00000000..436ef9e6
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/eRoutingPortConnectionType.cs
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs b/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
new file mode 100644
index 00000000..b5f3d9f7
--- /dev/null
+++ b/src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
@@ -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
+ }
+}
\ No newline at end of file