diff --git a/src/PepperDash.Essentials.Core/Routing/Extensions.cs b/src/PepperDash.Essentials.Core/Routing/Extensions.cs index 17f1e50d..5d09176f 100644 --- a/src/PepperDash.Essentials.Core/Routing/Extensions.cs +++ b/src/PepperDash.Essentials.Core/Routing/Extensions.cs @@ -1,11 +1,11 @@ -using Crestron.SimplSharpPro.Keypads; -using PepperDash.Essentials.Core.Queues; -using PepperDash.Essentials.Core.Routing; -using Serilog.Events; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Crestron.SimplSharpPro.Keypads; +using PepperDash.Essentials.Core.Queues; +using PepperDash.Essentials.Core.Routing; +using Serilog.Events; using Debug = PepperDash.Core.Debug; @@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort) { // if it's a single signal type, find the route - if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) && + if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) && !(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio))) { var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType); @@ -134,14 +134,15 @@ namespace PepperDash.Essentials.Core } // otherwise, audioVideo needs to be handled as two steps. - Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key); + Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", destination, source.Key, signalType); RouteDescriptor audioRouteDescriptor; if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio)) { audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.SecondaryAudio); - } else + } + else { audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio); } @@ -199,13 +200,13 @@ namespace PepperDash.Essentials.Core Source = source, SourcePort = sourcePort, 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; @@ -219,7 +220,7 @@ namespace PepperDash.Essentials.Core //New Request if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true) - { + { coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown; RouteRequests.Add(destination.Key, routeRequest); @@ -239,9 +240,9 @@ namespace PepperDash.Essentials.Core Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key); } - routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false)); + routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, destinationPort?.Key ?? string.Empty, false)); - routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest)); + routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest)); } /// @@ -272,7 +273,8 @@ namespace PepperDash.Essentials.Core audioOrSingleRoute.ExecuteRoutes(); videoRoute?.ExecuteRoutes(); - } catch(Exception ex) + } + catch (Exception ex) { Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request); } @@ -305,9 +307,10 @@ namespace PepperDash.Essentials.Core Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key); current.ReleaseRoutes(clearRoute); } - } catch (Exception ex) + } + catch (Exception ex) { - Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); + Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'", null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey); } } diff --git a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs index 8f2fc1e2..64617770 100644 --- a/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs +++ b/src/PepperDash.Essentials.Devices.Common/Generic/GenericSink.cs @@ -1,8 +1,10 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using PepperDash.Core; using PepperDash.Core.Logging; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Routing; using Serilog.Events; namespace PepperDash.Essentials.Devices.Common.Generic @@ -10,8 +12,17 @@ namespace PepperDash.Essentials.Devices.Common.Generic /// /// Represents a GenericSink /// - public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort + public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort, ICurrentSources { + /// + public Dictionary CurrentSources { get; private set; } + + /// + public Dictionary CurrentSourceKeys { get; private set; } + + /// + public event EventHandler CurrentSourcesChanged; + /// /// Initializes a new instance of the GenericSink class /// @@ -24,6 +35,49 @@ namespace PepperDash.Essentials.Devices.Common.Generic var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this); InputPorts.Add(inputPort); + + CurrentSources = new Dictionary + { + { eRoutingSignalType.Audio, null }, + { eRoutingSignalType.Video, null }, + }; + + CurrentSourceKeys = new Dictionary + { + { eRoutingSignalType.Audio, string.Empty }, + { eRoutingSignalType.Video, string.Empty }, + }; + } + + /// + public void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem) + { + foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType))) + { + var flagValue = Convert.ToInt32(type); + // Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag). + // (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set. + if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0) + { + this.LogDebug("Skipping {type}", type); + continue; + } + + this.LogDebug("setting {type}", type); + + if (signalType.HasFlag(type)) + { + UpdateCurrentSources(type, sourceListKey, sourceListItem); + } + } + // Raise the CurrentSourcesChanged event + CurrentSourcesChanged?.Invoke(this, EventArgs.Empty); + } + + private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem) + { + CurrentSources[signalType] = sourceListItem; + CurrentSourceKeys[signalType] = sourceListKey; } /// diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs index 9643a607..a7c4614b 100644 --- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs +++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/CurrentSourcesMessenger.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; @@ -39,10 +40,14 @@ namespace PepperDash.Essentials.AppServer.Messengers sourceDevice.CurrentSourcesChanged += (sender, e) => { + // need to copy the dictionaries to avoid enumeration issues + var currentSourceKeys = sourceDevice.CurrentSourceKeys.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + var currentSources = sourceDevice.CurrentSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + PostStatusMessage(JToken.FromObject(new { - currentSourceKeys = sourceDevice.CurrentSourceKeys, - currentSources = sourceDevice.CurrentSources + currentSourceKeys, + currentSources, })); }; }