diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs index 1ad4dd7f..f3eec22a 100644 --- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs +++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs @@ -18,8 +18,15 @@ namespace PepperDash.Essentials.Core /// public static class Extensions { + /// + /// Stores pending route requests, keyed by the destination device key. + /// Used primarily to handle routing requests while a device is cooling down. + /// private static readonly Dictionary RouteRequests = new Dictionary(); + /// + /// A queue to process route requests and releases sequentially. + /// private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue"); /// @@ -38,16 +45,49 @@ namespace PepperDash.Essentials.Core ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort); } + + /// + /// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set + /// + /// destination to clear public static void ReleaseRoute(this IRoutingInputs destination) { - routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty)); + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, false)); } + /// + /// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set + /// + /// destination to clear + /// Input to use to find existing route public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey) { - routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey)); + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, false)); } + /// + /// Clears the route on the destination. This will remove any routes that are currently in use + /// + /// Destination + public static void ClearRoute(this IRoutingInputs destination) + { + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, true)); + } + + /// + /// Clears the route on the destination. This will remove any routes that are currently in use + /// + /// destination + /// input to use to find existing route + public static void ClearRoute(this IRoutingInputs destination, string inputPortKey) + { + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, true)); + } + + /// + /// Removes the route request for the destination. This will remove any routes that are currently in use + /// + /// destination device key public static void RemoveRouteRequestForDestination(string destinationKey) { Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey); @@ -130,6 +170,15 @@ namespace PepperDash.Essentials.Core return (audioRouteDescriptor, videoRouteDescriptor); } + /// + /// Internal method to handle the logic for releasing an existing route and making a new one. + /// Handles devices with cooling states by queueing the request. + /// + /// The destination device. + /// The source device. + /// The type of signal to route. + /// The specific destination input port (optional). + /// The specific source output port (optional). private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null) { if (destination == null) throw new ArgumentNullException(nameof(destination)); @@ -184,11 +233,16 @@ namespace PepperDash.Essentials.Core Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key); } - routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty)); + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false)); routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest)); } + /// + /// Executes the actual routing based on a . + /// Finds the route path, adds it to the collection, and executes the switches. + /// + /// The route request details. private static void RunRouteRequest(RouteRequest request) { try @@ -216,14 +270,15 @@ namespace PepperDash.Essentials.Core { Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request); } - } + } /// - /// Will release the existing route on the destination, if it is found in - /// RouteDescriptorCollection.DefaultCollection + /// Will release the existing route on the destination, if it is found in RouteDescriptorCollection.DefaultCollection /// - /// - private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey) + /// + /// The input port key to use to find the route. If empty, will use the first available input port + /// If true, will clear the route on the destination. This will remove any routes that are currently in use + private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey, bool clearRoute) { try { @@ -242,7 +297,7 @@ namespace PepperDash.Essentials.Core if (current != null) { Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key); - current.ReleaseRoutes(); + current.ReleaseRoutes(clearRoute); } } catch (Exception ex) { diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs index d50bcbb0..d3f61e72 100644 --- a/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs +++ b/src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs @@ -1,7 +1,7 @@ namespace PepperDash.Essentials.Core { /// - /// Defines an IRoutingOutputs devices as being a source - the start of the chain + /// Marker interface to identify a device that acts as the origin of a signal path (). /// public interface IRoutingSource : IRoutingOutputs { diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs index 077d4ecd..407133ef 100644 --- a/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs +++ b/src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs @@ -1,5 +1,8 @@ namespace PepperDash.Essentials.Core { + /// + /// Defines a routing device () that supports explicitly clearing a route on an output. + /// public interface IRoutingWithClear : IRouting { /// diff --git a/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs b/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs index dcf4e423..a328441a 100644 --- a/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs +++ b/src/PepperDash.Essentials.Core/Routing/IRoutingWithFeedback.cs @@ -3,14 +3,25 @@ using System; namespace PepperDash.Essentials.Core { + /// + /// Delegate for handling route change events on devices implementing . + /// + /// The routing device where the change occurred. + /// A descriptor of the new route that was established. public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute); /// - /// Defines an IRouting with a feedback event + /// Defines a routing device () that provides feedback about its current routes. /// public interface IRoutingWithFeedback : IRouting { + /// + /// Gets a list describing the currently active routes on this device. + /// List CurrentRoutes { get; } + /// + /// Event triggered when a route changes on this device. + /// event RouteChangedEventHandler RouteChanged; } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs b/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs index 0950e6a6..a157e8d1 100644 --- a/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs +++ b/src/PepperDash.Essentials.Core/Routing/ITxRouting.cs @@ -1,8 +1,18 @@ namespace PepperDash.Essentials.Core { + /// + /// Represents a routing device (typically a transmitter or source) that provides numeric feedback for its current route. + /// Extends . + /// public interface ITxRouting : IRoutingNumeric { + /// + /// Feedback indicating the currently routed video source by its numeric identifier. + /// IntFeedback VideoSourceNumericFeedback { get; } + /// + /// Feedback indicating the currently routed audio source by its numeric identifier. + /// IntFeedback AudioSourceNumericFeedback { get; } } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs index b7dade4c..e02dd30e 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Crestron.SimplSharpPro; @@ -9,35 +10,63 @@ using Serilog.Events; namespace PepperDash.Essentials.Core { /// - /// Represents an collection of individual route steps between Source and Destination + /// Represents a collection of individual route steps between a Source and a Destination device for a specific signal type. /// public class RouteDescriptor { + /// + /// The destination device (sink or midpoint) for the route. + /// public IRoutingInputs Destination { get; private set; } + /// + /// The specific input port on the destination device used for this route. Can be null if not specified or applicable. + /// public RoutingInputPort InputPort { get; private set; } + /// + /// The source device for the route. + /// public IRoutingOutputs Source { get; private set; } + + /// + /// The type of signal being routed (e.g., Audio, Video). This descriptor represents a single signal type. + /// public eRoutingSignalType SignalType { get; private set; } + + /// + /// A list of individual switching steps required to establish the route. + /// public List Routes { get; private set; } - + /// + /// Initializes a new instance of the class for a route without a specific destination input port. + /// + /// The source device. + /// The destination device. + /// The type of signal being routed. public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType) { } + /// + /// Initializes a new instance of the class for a route with a specific destination input port. + /// + /// The source device. + /// The destination device. + /// The destination input port (optional). + /// The signal type for this route. public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType) { Destination = destination; + InputPort = inputPort; Source = source; SignalType = signalType; - InputPort = inputPort; Routes = new List(); } /// - /// Executes all routes described in this collection. Typically called via - /// extension method IRoutingInputs.ReleaseAndMakeRoute() + /// Executes all the switching steps defined in the list. /// public void ExecuteRoutes() { @@ -63,15 +92,27 @@ namespace PepperDash.Essentials.Core } /// - /// Releases all routes in this collection. Typically called via - /// extension method IRoutingInputs.ReleaseAndMakeRoute() + /// Releases the usage tracking for the route and optionally clears the route on the switching devices. /// - public void ReleaseRoutes() + /// If true, attempts to clear the route on the switching devices (e.g., set input to null/0). + public void ReleaseRoutes(bool clearRoute = false) { foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting)) { if (route.SwitchingDevice is IRouting switchingDevice) { + if(clearRoute) + { + try + { + switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType); + } + catch (Exception e) + { + Debug.LogError("Error executing switch: {exception}", e.Message); + } + } + if (route.OutputPort == null) { continue; @@ -90,6 +131,10 @@ namespace PepperDash.Essentials.Core } } + /// + /// Returns a string representation of the route descriptor, including source, destination, and individual route steps. + /// + /// A string describing the route. public override string ToString() { var routesText = Routes.Select(r => r.ToString()).ToArray(); diff --git a/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs index 19d8655b..a8bdf015 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteRequest.cs @@ -4,15 +4,42 @@ using System; namespace PepperDash.Essentials.Core { + /// + /// Represents a request to establish a route between a source and a destination device. + /// public class RouteRequest { + /// + /// The specific input port on the destination device to use for the route. Can be null if the port should be automatically determined or is not applicable. + /// public RoutingInputPort DestinationPort { get; set; } + /// + /// The specific output port on the source device to use for the route. Can be null if the port should be automatically determined or is not applicable. + /// public RoutingOutputPort SourcePort { get; set; } + + /// + /// The destination device (sink or midpoint) for the route. + /// public IRoutingInputs Destination { get; set; } + + /// + /// The source device for the route. + /// public IRoutingOutputs Source { get; set; } + + /// + /// The type of signal being routed (e.g., Audio, Video, AudioVideo). + /// public eRoutingSignalType SignalType { get; set; } + /// + /// Handles the route request after a device's cooldown period has finished. + /// This method is typically subscribed to the IsCoolingDownFeedback.OutputChange event. + /// + /// The object that triggered the event (usually the cooling device). + /// Event arguments indicating the cooldown state change. public void HandleCooldown(object sender, FeedbackEventArgs args) { try @@ -39,6 +66,10 @@ namespace PepperDash.Essentials.Core } } + /// + /// Returns a string representation of the route request. + /// + /// A string describing the source and destination of the route request. public override string ToString() { return $"Route {Source?.Key ?? "No Source Device"}:{SourcePort?.Key ?? "auto"} to {Destination?.Key ?? "No Destination Device"}:{DestinationPort?.Key ?? "auto"}"; diff --git a/src/PepperDash.Essentials.Core/Routing/RouteRequestQueueItem.cs b/src/PepperDash.Essentials.Core/Routing/RouteRequestQueueItem.cs index 05681991..07b52c2c 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteRequestQueueItem.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteRequestQueueItem.cs @@ -5,17 +5,34 @@ using Serilog.Events; namespace PepperDash.Essentials.Core.Routing { + /// + /// Represents an item in the route request queue. + /// public class RouteRequestQueueItem : IQueueMessage { + /// + /// The action to perform for the route request. + /// private readonly Action action; + /// + /// The route request data. + /// private readonly RouteRequest routeRequest; + /// + /// Initializes a new instance of the class. + /// + /// The action to perform. + /// The route request data. public RouteRequestQueueItem(Action routeAction, RouteRequest request) { action = routeAction; routeRequest = request; } + /// + /// Dispatches the route request action. + /// public void Dispatch() { Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest); @@ -23,23 +40,50 @@ namespace PepperDash.Essentials.Core.Routing } } + /// + /// Represents an item in the queue for releasing a route. + /// public class ReleaseRouteQueueItem : IQueueMessage { - private readonly Action action; + /// + /// The action to perform for releasing the route. + /// + private readonly Action action; + /// + /// The destination device whose route is being released. + /// private readonly IRoutingInputs destination; + /// + /// The specific input port key on the destination to release, or null/empty for any/default. + /// private readonly string inputPortKey; + /// + /// Indicates whether to clear the route (send null) or just release the usage tracking. + /// + private readonly bool clearRoute; - public ReleaseRouteQueueItem(Action action, IRoutingInputs destination, string inputPortKey) + /// + /// Initializes a new instance of the class. + /// + /// The action to perform. + /// The destination device. + /// The input port key. + /// True to clear the route, false to just release. + public ReleaseRouteQueueItem(Action action, IRoutingInputs destination, string inputPortKey, bool clearRoute) { this.action = action; this.destination = destination; this.inputPortKey = inputPortKey; + this.clearRoute = clearRoute; } + /// + /// Dispatches the release route action. + /// public void Dispatch() { Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); - action(destination, inputPortKey); + action(destination, inputPortKey, clearRoute); } } } diff --git a/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs b/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs index c425d0a4..6394a3bc 100644 --- a/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs +++ b/src/PepperDash.Essentials.Core/Routing/RouteSwitchDescriptor.cs @@ -1,25 +1,47 @@ namespace PepperDash.Essentials.Core { /// - /// Represents an individual link for a route + /// Represents a single switching step within a larger route, detailing the switching device, input port, and optionally the output port. /// public class RouteSwitchDescriptor { + /// + /// The device performing the switch (derived from the InputPort's parent). + /// public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } } + /// + /// The output port being switched from (relevant for matrix switchers). Null for sink devices. + /// public RoutingOutputPort OutputPort { get; set; } + /// + /// The input port being switched to. + /// public RoutingInputPort InputPort { get; set; } + /// + /// Initializes a new instance of the class for sink devices (no output port). + /// + /// The input port being switched to. public RouteSwitchDescriptor(RoutingInputPort inputPort) { InputPort = inputPort; } + /// + /// Initializes a new instance of the class for matrix switchers. + /// + /// The output port being switched from. + /// The input port being switched to. public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort) { InputPort = inputPort; OutputPort = outputPort; } + /// + /// Returns a string representation of the route switch descriptor. + /// + /// A string describing the switch operation. public override string ToString() { if (SwitchingDevice is IRouting) diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs index 4e2ccc5b..9afdb33d 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs @@ -5,8 +5,17 @@ using System.Linq; namespace PepperDash.Essentials.Core.Routing { + /// + /// Manages routing feedback by subscribing to route changes on midpoint and sink devices, + /// tracing the route back to the original source, and updating the CurrentSourceInfo on sink devices. + /// public class RoutingFeedbackManager:EssentialsDevice { + /// + /// Initializes a new instance of the class. + /// + /// The unique key for this manager device. + /// The name of this manager device. public RoutingFeedbackManager(string key, string name): base(key, name) { AddPreActivationAction(SubscribeForMidpointFeedback); @@ -14,6 +23,9 @@ namespace PepperDash.Essentials.Core.Routing } + /// + /// Subscribes to the RouteChanged event on all devices implementing . + /// private void SubscribeForMidpointFeedback() { var midpointDevices = DeviceManager.AllDevices.OfType(); @@ -24,6 +36,9 @@ namespace PepperDash.Essentials.Core.Routing } } + /// + /// Subscribes to the InputChanged event on all devices implementing . + /// private void SubscribeForSinkFeedback() { var sinkDevices = DeviceManager.AllDevices.OfType(); @@ -34,6 +49,12 @@ namespace PepperDash.Essentials.Core.Routing } } + /// + /// Handles the RouteChanged event from a midpoint device. + /// Triggers an update for all sink devices. + /// + /// The midpoint device that reported a route change. + /// The descriptor of the new route. private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute) { try @@ -51,6 +72,12 @@ namespace PepperDash.Essentials.Core.Routing } } + /// + /// Handles the InputChanged event from a sink device. + /// Triggers an update for the specific sink device. + /// + /// The sink device that reported an input change. + /// The new input port selected on the sink device. private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort) { try @@ -63,6 +90,12 @@ namespace PepperDash.Essentials.Core.Routing } } + /// + /// Updates the CurrentSourceInfo and CurrentSourceInfoKey properties on a destination (sink) device + /// based on its currently selected input port by tracing the route back through tie lines. + /// + /// The destination sink device to update. + /// The currently selected input port on the destination device. private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort) { // Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key); @@ -199,6 +232,12 @@ namespace PepperDash.Essentials.Core.Routing } + /// + /// Recursively traces a route back from a given tie line to find the root source tie line. + /// It navigates through midpoint devices () by checking their current routes. + /// + /// The starting tie line (typically connected to a sink or midpoint). + /// The connected to the original source device, or null if the source cannot be determined. private TieLine GetRootTieLine(TieLine tieLine) { TieLine nextTieLine = null; diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs index 3dbbb6af..c949db4e 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs @@ -5,7 +5,7 @@ using System; namespace PepperDash.Essentials.Core { /// - /// Basic RoutingInput with no statuses. + /// Represents a basic routing input port on a device. /// public class RoutingInputPort : RoutingPort { @@ -41,6 +41,10 @@ namespace PepperDash.Essentials.Core ParentDevice = parent; } + /// + /// Returns a string representation of the input port. + /// + /// A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType". public override string ToString() { return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}"; diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs b/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs index bd4dd489..e0c6bba0 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingInputPortWithVideoStatuses.cs @@ -1,24 +1,25 @@ namespace PepperDash.Essentials.Core { /// - /// A RoutingInputPort for devices like DM-TX and DM input cards. - /// Will provide video statistics on connected signals + /// Represents a routing input port that provides video status feedback (e.g., sync, resolution). + /// Suitable for devices like DM transmitters or DM input cards. /// public class RoutingInputPortWithVideoStatuses : RoutingInputPort { /// - /// Video statuses attached to this port + /// Provides feedback outputs for video statuses associated with this port. /// public VideoStatusOutputs VideoStatus { get; private set; } /// - /// Constructor + /// Initializes a new instance of the class. /// - /// 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 + /// The unique key for this port. + /// The signal type supported by this port. + /// The physical connection type of this port. + /// An object used to refer to this port in the parent device's ExecuteSwitch method. + /// The device this port belongs to. + /// A containing delegates to retrieve video status values. public RoutingInputPortWithVideoStatuses(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, IRoutingInputs parent, VideoStatusFuncsWrapper funcs) : diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs b/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs index 7ede13c2..a0e706ac 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingNumericEventArgs.cs @@ -3,32 +3,72 @@ namespace PepperDash.Essentials.Core { + /// + /// Provides event arguments for routing changes, potentially including numeric or port object references. + /// public class RoutingNumericEventArgs : EventArgs { - + /// + /// The numeric representation of the output, if applicable. + /// public uint? Output { get; set; } + /// + /// The numeric representation of the input, if applicable. + /// public uint? Input { get; set; } + /// + /// The type of signal involved in the routing change. + /// public eRoutingSignalType SigType { get; set; } + /// + /// The input port involved in the routing change, if applicable. + /// public RoutingInputPort InputPort { get; set; } + /// + /// The output port involved in the routing change, if applicable. + /// public RoutingOutputPort OutputPort { get; set; } + /// + /// Initializes a new instance of the class using numeric identifiers. + /// + /// The numeric output identifier. + /// The numeric input identifier. + /// The signal type. public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType) { } + /// + /// Initializes a new instance of the class using port objects. + /// + /// The output port object. + /// The input port object. + /// The signal type. public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort, eRoutingSignalType sigType) : this(null, null, outputPort, inputPort, sigType) { } + /// + /// Initializes a new instance of the class with default values. + /// public RoutingNumericEventArgs() : this(null, null, null, null, 0) { } + /// + /// Initializes a new instance of the class with potentially mixed identifiers. + /// + /// The numeric output identifier (optional). + /// The numeric input identifier (optional). + /// The output port object (optional). + /// The input port object (optional). + /// The signal type. public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort, RoutingInputPort inputPort, eRoutingSignalType sigType) { diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs index fbbd3a22..1cae64c0 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs @@ -4,29 +4,46 @@ using System; namespace PepperDash.Essentials.Core { + /// + /// Represents a basic routing output port on a device. + /// public class RoutingOutputPort : RoutingPort { /// - /// The IRoutingOutputs object this port lives on + /// The IRoutingOutputs object this port lives on. /// - /// [JsonIgnore] public IRoutingOutputs ParentDevice { get; private set; } + /// + /// Tracks which destinations are currently using this output port. + /// public InUseTracking InUseTracker { get; private set; } /// + /// Initializes a new instance of the class. /// - /// 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 + /// The unique key for this port. + /// The signal type supported by this port. + /// The physical connection type of this port. + /// An object used to refer to this port in the parent device's ExecuteSwitch method. + /// The device this port belongs to. public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, IRoutingOutputs parent) : this(key, type, connType, selector, parent, false) { } + /// + /// Initializes a new instance of the class, potentially marking it as internal. + /// + /// The unique key for this port. + /// The signal type supported by this port. + /// The physical connection type of this port. + /// An object used to refer to this port in the parent device's ExecuteSwitch method. + /// The device this port belongs to. + /// True if this port represents an internal connection within a device (e.g., card to backplane). public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, IRoutingOutputs parent, bool isInternal) : base(key, type, connType, selector, isInternal) @@ -35,6 +52,10 @@ namespace PepperDash.Essentials.Core InUseTracker = new InUseTracking(); } + /// + /// Returns a string representation of the output port. + /// + /// A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType". public override string ToString() { return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}"; diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs b/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs index 04e804f1..64fae203 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingPort.cs @@ -4,18 +4,47 @@ namespace PepperDash.Essentials.Core { /// - /// Base class for RoutingInput and Output ports + /// Base class for and . /// public abstract class RoutingPort : IKeyed { + /// + /// The unique key identifying this port within its parent device. + /// public string Key { get; private set; } + /// + /// The type of signal this port handles (e.g., Audio, Video, AudioVideo). + /// public eRoutingSignalType Type { get; private set; } + /// + /// The physical connection type of this port (e.g., Hdmi, Rca, Dm). + /// public eRoutingPortConnectionType ConnectionType { get; private set; } + /// + /// An object (often a number or string) used by the parent routing device to select this port during switching. + /// public readonly object Selector; + /// + /// Indicates if this port represents an internal connection within a device (e.g., card to backplane). + /// public bool IsInternal { get; private set; } + /// + /// An object used to match feedback values to this port, if applicable. + /// public object FeedbackMatchObject { get; set; } + /// + /// A reference to the underlying hardware port object (e.g., SimplSharpPro Port), if applicable. + /// public object Port { get; set; } + /// + /// Initializes a new instance of the class. + /// + /// The unique key for this port. + /// The signal type supported by this port. + /// The physical connection type of this port. + /// The selector object for switching. + /// True if this port is internal. public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal) { Key = key; diff --git a/src/PepperDash.Essentials.Core/Routing/RoutingPortNames.cs b/src/PepperDash.Essentials.Core/Routing/RoutingPortNames.cs index 160b4f16..96a2ca32 100644 --- a/src/PepperDash.Essentials.Core/Routing/RoutingPortNames.cs +++ b/src/PepperDash.Essentials.Core/Routing/RoutingPortNames.cs @@ -7,7 +7,8 @@ using Crestron.SimplSharp; namespace PepperDash.Essentials.Core { /// - /// These should correspond directly with the portNames var in the config tool. + /// Defines constant string values for common routing port keys. + /// These should correspond directly with the portNames var in the config tool. /// public class RoutingPortNames { diff --git a/src/PepperDash.Essentials.Core/Routing/TieLine.cs b/src/PepperDash.Essentials.Core/Routing/TieLine.cs index 51968152..f3a2c71d 100644 --- a/src/PepperDash.Essentials.Core/Routing/TieLine.cs +++ b/src/PepperDash.Essentials.Core/Routing/TieLine.cs @@ -4,14 +4,23 @@ using System.Collections.Generic; namespace PepperDash.Essentials.Core { + /// + /// Represents a connection (tie line) between a and a . + /// public class TieLine { + /// + /// The source output port of the tie line. + /// public RoutingOutputPort SourcePort { get; private set; } + /// + /// The destination input port of the tie line. + /// public RoutingInputPort DestinationPort { get; private set; } //public int InUseCount { get { return DestinationUsingThis.Count; } } /// - /// Gets the type of this tie line. Will either be the type of the desination port + /// Gets the type of this tie line. Will either be the type of the destination port /// or the type of OverrideType when it is set. /// public eRoutingSignalType Type @@ -35,20 +44,27 @@ namespace PepperDash.Essentials.Core //List DestinationUsingThis = new List(); /// - /// For tie lines that represent internal links, like from cards to the matrix in a DM. - /// This property is true if SourcePort and DestinationPort IsInternal - /// property are both true + /// Gets a value indicating whether this tie line represents an internal connection within a device (both source and destination ports are internal). /// public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } } + /// + /// Gets a value indicating whether the signal types of the source and destination ports differ. + /// public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } } + /// + /// Gets a value indicating whether the connection types of the source and destination ports differ. + /// public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } } + /// + /// A descriptive note about any type mismatch, if applicable. + /// public string TypeMismatchNote { get; set; } /// - /// + /// Initializes a new instance of the class. /// - /// - /// + /// The source output port. + /// The destination input port. public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort) { if (sourcePort == null || destinationPort == null) @@ -58,9 +74,11 @@ namespace PepperDash.Essentials.Core } /// - /// Creates a tie line with an overriding Type. See help for OverrideType property for info + /// Creates a tie line with an overriding Type. See help for OverrideType property for info. /// - /// The signal type to limit the link to. Overrides DestinationPort.Type + /// The source output port. + /// The destination input port. + /// The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations. public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType? overrideType) : this(sourcePort, destinationPort) { @@ -68,9 +86,11 @@ namespace PepperDash.Essentials.Core } /// - /// Creates a tie line with an overriding Type. See help for OverrideType property for info + /// Creates a tie line with an overriding Type. See help for OverrideType property for info. /// - /// The signal type to limit the link to. Overrides DestinationPort.Type + /// The source output port. + /// The destination input port. + /// The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations. public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) : this(sourcePort, destinationPort) { @@ -78,18 +98,25 @@ namespace PepperDash.Essentials.Core } /// - /// Will link up video status from supporting inputs to connected outputs + /// Will link up video status from supporting inputs to connected outputs. /// public void Activate() { // Now does nothing } + /// + /// Deactivates the tie line. + /// public void Deactivate() { // Now does nothing } + /// + /// Returns a string representation of the tie line. + /// + /// A string describing the source, destination, and type of the tie line. public override string ToString() { return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key, @@ -99,8 +126,14 @@ namespace PepperDash.Essentials.Core //******************************************************************************** + /// + /// Represents a collection of objects. + /// public class TieLineCollection : List { + /// + /// Gets the default singleton instance of the . + /// public static TieLineCollection Default { get @@ -111,6 +144,9 @@ namespace PepperDash.Essentials.Core } } + /// + /// Backing field for the singleton instance. + /// [JsonIgnore] private static TieLineCollection _Default; } diff --git a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs index e7ffb292..5090f9c2 100644 --- a/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs +++ b/src/PepperDash.Essentials.Core/Routing/TieLineConfig.cs @@ -1,6 +1,4 @@ - - -using System; +using System; using System.Collections.Generic; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; @@ -15,15 +13,44 @@ using Serilog.Events; namespace PepperDash.Essentials.Core.Config { + /// + /// Represents the configuration data for a single tie line between two routing ports. + /// public class TieLineConfig { + /// + /// The key of the source device. + /// public string SourceKey { get; set; } + + /// + /// The key of the source card (if applicable, e.g., in a modular chassis). + /// public string SourceCard { get; set; } + + /// + /// The key of the source output port. + /// public string SourcePort { get; set; } + + /// + /// The key of the destination device. + /// public string DestinationKey { get; set; } + + /// + /// The key of the destination card (if applicable). + /// public string DestinationCard { get; set; } + + /// + /// The key of the destination input port. + /// public string DestinationPort { get; set; } + /// + /// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations. + /// [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)] [JsonConverter(typeof(StringEnumConverter))] public eRoutingSignalType? OverrideType { get; set; } @@ -73,11 +100,19 @@ namespace PepperDash.Essentials.Core.Config return new TieLine(sourceOutputPort, destinationInputPort, OverrideType); } + /// + /// Logs an error message related to creating this tie line configuration. + /// + /// The specific error message. void LogError(string msg) { Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this); } + /// + /// Returns a string representation of the tie line configuration. + /// + /// A string describing the source and destination of the configured tie line. public override string ToString() { return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort, diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs index cc9bc1e2..618924cd 100644 --- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs +++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace PepperDash.Essentials.Devices.Common.Generic { - public class GenericSink : EssentialsDevice, IRoutingSink + public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort { public GenericSink(string key, string name) : base(key, name) { diff --git a/src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodec.cs b/src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodec.cs index 7fdad9b4..84847f35 100644 --- a/src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodec.cs +++ b/src/PepperDash.Essentials.Devices.Common/SoftCodec/GenericSoftCodec.cs @@ -8,9 +8,10 @@ using System.Linq; namespace PepperDash.Essentials.Devices.Common.SoftCodec { - public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingOutputs, IRoutingSinkWithSwitching + public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingSinkWithSwitchingWithInputPort { private RoutingInputPort _currentInputPort; + public RoutingInputPort CurrentInputPort { get => _currentInputPort; set