From c5aa8d29cb381c767febe095bface43a3d22c606 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 26 Jun 2020 08:24:38 -0600 Subject: [PATCH] refactoring room classes --- .../Room/Types/EssentialsHuddleSpaceRoom.cs | 359 ++----------- .../Room/Types/EssentialsHuddleVtc1Room.cs | 2 +- .../Room/EssentialsRoomBase.cs | 478 ++++++++++++++++-- .../Routing/RoutingPort.cs | 2 +- 4 files changed, 476 insertions(+), 365 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs index 864f195d..b4f34547 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleSpaceRoom.cs @@ -1,9 +1,7 @@ using System; -using System.Linq; +using System.Collections.Generic; using Crestron.SimplSharp; - using Newtonsoft.Json; - using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; @@ -11,59 +9,15 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IHasCurrentSourceInfoChange, IRunRouteAction, IRunDefaultPresentRoute, IHasCurrentVolumeControls, IHasDefaultDisplay - { - // - public event SourceInfoChangeHandler CurrentSourceChange; + public class EssentialsHuddleSpaceRoom : EssentialsRoomBase, IRunRouteAction, + IRunDefaultPresentRoute, IHasCurrentVolumeControls, IHasDefaultDisplay + { + public EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; private set; } - public EssentialsHuddleRoomPropertiesConfig PropertiesConfig { get; private set; } - - public IRoutingSinkWithSwitching DefaultDisplay { get; private set; } - public IRoutingSink DefaultAudioDevice { get; private set; } - public IBasicVolumeControls DefaultVolumeControls { get; private set; } - - public bool ExcludeFromGlobalFunctions { get; set; } - - public string DefaultSourceItem { get; set; } - - public ushort DefaultVolume { get; set; } - - /// - /// If room is off, enables power on to last source. Default true - /// - public bool EnablePowerOnToLastSource { get; set; } - string _lastSourceKey; - - /// - /// The SourceListItem last run - containing names and icons - /// - public SourceListItem CurrentSourceInfo - { - get { return _currentSourceInfo; } - set - { - if (value == _currentSourceInfo) return; - - var handler = CurrentSourceChange; - // remove from in-use tracker, if so equipped - if(_currentSourceInfo != null && _currentSourceInfo.SourceDevice is IInUseTracking) - (_currentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); - - if (handler != null) - handler(_currentSourceInfo, ChangeType.WillChange); - - _currentSourceInfo = value; - - // add to in-use tracking - if (_currentSourceInfo != null && _currentSourceInfo.SourceDevice is IInUseTracking) - (_currentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); - if (handler != null) - handler( _currentSourceInfo, ChangeType.DidChange); - } - } - SourceListItem _currentSourceInfo; - - public string CurrentSourceInfoKey { get; set; } + /// + /// If room is off, enables power on to last source. Default true + /// + public bool EnablePowerOnToLastSource { get; set; } public EssentialsHuddleSpaceRoom(DeviceConfig config) : base(config) @@ -72,10 +26,12 @@ namespace PepperDash.Essentials { PropertiesConfig = JsonConvert.DeserializeObject (config.Properties.ToString()); - DefaultDisplay = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching; + DefaultDisplay = + DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultDisplayKey) as IRoutingSinkWithSwitching; - DefaultAudioDevice = DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IRoutingSinkWithSwitching; + DefaultAudioDevice = + DeviceManager.GetDeviceForKey(PropertiesConfig.DefaultAudioKey) as IRoutingSinkWithSwitching; Initialize(); } @@ -85,30 +41,36 @@ namespace PepperDash.Essentials } } - private void Initialize() - { + private void Initialize() + { if (DefaultAudioDevice is IBasicVolumeControls) + { DefaultVolumeControls = DefaultAudioDevice as IBasicVolumeControls; + } else if (DefaultAudioDevice is IHasVolumeDevice) + { DefaultVolumeControls = (DefaultAudioDevice as IHasVolumeDevice).VolumeDevice; + } CurrentVolumeControls = DefaultVolumeControls; SourceListKey = "default"; EnablePowerOnToLastSource = true; var disp = DefaultDisplay as DisplayBase; - if (disp == null) return; + if (disp == null) + { + return; + } IsWarmingFeedbackFunc = () => disp.IsWarmingUpFeedback.BoolValue; IsCoolingFeedbackFunc = () => disp.IsCoolingDownFeedback.BoolValue; OnFeedbackFunc = () => CurrentSourceInfo != null - && CurrentSourceInfo.Type == eSourceListItemType.Route; + && CurrentSourceInfo.Type == eSourceListItemType.Route; InitializeDisplay(disp); - } - + } protected override void IsCoolingDownFeedbackOnOutputChange(object sender, FeedbackEventArgs feedbackEventArgs) { @@ -119,7 +81,10 @@ namespace PepperDash.Essentials { var display = sender as DisplayBase; - if (display == null) return; + if (display == null) + { + return; + } if (display.PowerIsOnFeedback.BoolValue == OnFeedback.BoolValue) { @@ -169,10 +134,13 @@ namespace PepperDash.Essentials protected override void CustomSetConfig(DeviceConfig config) { - var newPropertiesConfig = JsonConvert.DeserializeObject(config.Properties.ToString()); + var newPropertiesConfig = + JsonConvert.DeserializeObject(config.Properties.ToString()); if (newPropertiesConfig != null) + { PropertiesConfig = newPropertiesConfig; + } ConfigWriter.UpdateRoomConfig(config); } @@ -186,7 +154,7 @@ namespace PepperDash.Essentials RunDefaultPresentRoute(); - CrestronEnvironment.Sleep(1000); + //CrestronEnvironment.Sleep(1000); //why? Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting down room"); @@ -205,15 +173,17 @@ namespace PepperDash.Essentials } RunRouteAction(DefaultSourceItem); - return true; + return true; } public override bool CustomActivate() { // Add Occupancy object from config if (PropertiesConfig.Occupancy != null) + { SetRoomOccupancy(DeviceManager.GetDeviceForKey(PropertiesConfig.Occupancy.DeviceKey) as IOccupancyStatusProvider, PropertiesConfig.Occupancy.TimeoutMinutes); + } this.LogoUrlLightBkgnd = PropertiesConfig.LogoLight.GetLogoUrlLight(); this.LogoUrlDarkBkgnd = PropertiesConfig.LogoDark.GetLogoUrlDark(); @@ -225,204 +195,17 @@ namespace PepperDash.Essentials } /// - /// + /// Will power the room on with the last-used source /// - /// - public void RunRouteAction(string routeKey) - { - RunRouteAction(routeKey, () => { }); - } - - /// - /// - /// - /// - /// - public void RunRouteAction(string routeKey, string sourceListKey) + public override void PowerOnToDefaultOrLastSource() { - RunRouteAction(routeKey, new Action(() => { })); - } - - /// - /// - /// - /// - /// - /// - public void RunRouteAction(string routeKey, string sourceListKey, Action successCallback) - { - if (string.IsNullOrEmpty(sourceListKey)) + if (!EnablePowerOnToLastSource || LastSourceKey == null) { - RunRouteAction(routeKey, successCallback); + return; } - else - throw new NotImplementedException(); + RunRouteAction(LastSourceKey); } - /// - /// Gets a source from config list SourceListKey and dynamically build and executes the - /// route or commands - /// - public void RunRouteAction(string routeKey, Action successCallback) - { - // Run this on a separate thread - //new CTimer - CrestronInvoke.BeginInvoke(o => - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Run route action '{0}'", routeKey); - var dict = ConfigReader.ConfigObject.GetSourceListForKey(SourceListKey); - if (dict == null) - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found", SourceListKey); - return; - } - - // Try to get the list item by it's string key - if (!dict.ContainsKey(routeKey)) - { - Debug.Console(1, this, "WARNING: No item '{0}' found on config list '{1}'", - routeKey, SourceListKey); - return; - } - - var item = dict[routeKey]; - //Debug.Console(2, this, "Action {0} has {1} steps", - // item.SourceKey, item.RouteList.Count); - - // End usage timer on last source - if (!string.IsNullOrEmpty(_lastSourceKey)) - { - var lastSource = dict[_lastSourceKey].SourceDevice; - - try - { - if (lastSource is IUsageTracking) - (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); - } - catch (Exception e) - { - Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); - } - } - - // Let's run it - if (routeKey.ToLower() != "roomoff") - { - _lastSourceKey = routeKey; - } - else - { - CurrentSourceInfoKey = null; - } - - foreach (var route in item.RouteList) - { - // if there is a $defaultAll on route, run two separate - if (route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase)) - { - // Going to assume a single-path route for now - var tempVideo = new SourceRouteListItem - { - DestinationKey = "$defaultDisplay", - SourceKey = route.SourceKey, - Type = eRoutingSignalType.Video - }; - DoRoute(tempVideo); - - //var tempAudio = new SourceRouteListItem - //{ - // DestinationKey = "$defaultAudio", - // SourceKey = route.SourceKey, - // Type = eRoutingSignalType.Audio - //}; - //DoRoute(tempAudio); - //continue; -- not sure why this was here - } - else - DoRoute(route); - } - - // Start usage timer on routed source - if (item.SourceDevice is IUsageTracking) - { - (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); - } - - - - - // Set volume control, using default if non provided - IBasicVolumeControls volDev = null; - // Handle special cases for volume control - if (string.IsNullOrEmpty(item.VolumeControlKey) - || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) - volDev = DefaultVolumeControls; - else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) - volDev = DefaultDisplay as IBasicVolumeControls; - // Or a specific device, probably rarely used. - else - { - var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); - if (dev is IBasicVolumeControls) - volDev = dev as IBasicVolumeControls; - else if (dev is IHasVolumeDevice) - volDev = (dev as IHasVolumeDevice).VolumeDevice; - } - - if (volDev != CurrentVolumeControls) - { - // zero the volume on the device we are leaving. - // Set the volume to default on device we are entering - if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback) - { - var vd = CurrentVolumeControls as IBasicVolumeWithFeedback; - SavedVolumeLevels[vd] = (uint) vd.VolumeLevelFeedback.IntValue; - vd.SetVolume(0); - } - CurrentVolumeControls = volDev; - if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback) - { - var vd = CurrentVolumeControls as IBasicVolumeWithFeedback; - ushort vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort) SavedVolumeLevels[vd] : DefaultVolume); - vd.SetVolume(vol); - } - } - - - - // store the name and UI info for routes - if (item.SourceKey == "$off") - { - CurrentSourceInfoKey = routeKey; - CurrentSourceInfo = null; - } - else if (item.SourceKey != null) - { - CurrentSourceInfoKey = routeKey; - CurrentSourceInfo = item; - } - // And finally, set the "control". This will trigger event - //CurrentControlDevice = DeviceManager.GetDeviceForKey(item.SourceKey) as Device; - - OnFeedback.FireUpdate(); - - // report back when done - if (successCallback != null) - successCallback(); - - }, 0); // end of CTimer - } - - /// - /// Will power the room on with the last-used source - /// - public override void PowerOnToDefaultOrLastSource() - { - if (!EnablePowerOnToLastSource || _lastSourceKey == null) - return; - RunRouteAction(_lastSourceKey); - } - /// /// Does what it says /// @@ -431,64 +214,14 @@ namespace PepperDash.Essentials Debug.Console(1, this, "Restoring default levels"); var vc = CurrentVolumeControls as IBasicVolumeWithFeedback; if (vc != null) + { vc.SetVolume(DefaultVolume); + } } - /// - /// - /// - /// - /// - private bool DoRoute(SourceRouteListItem route) - { - IRoutingSink dest; - - if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) - dest = DefaultAudioDevice; - else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) - dest = DefaultDisplay; - else - dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSink; - - if (dest == null) - { - Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); - return false; - } - - if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) - { - dest.ReleaseRoute(); - if (dest is IHasPowerControl) - (dest as IHasPowerControl).PowerOff(); - } - else - { - var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; - if (source == null) - { - Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, route.DestinationKey); - return false; - } - dest.ReleaseAndMakeRoute(source, route.Type); - } - return true; - } - public override void RoomVacatedForTimeoutPeriod(object o) { - //Implement this + //TODO: Implement RoomVacatedForTimeoutPeriod } - - /// - /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions - /// - public static void AllRoomsOff() - { - var allRooms = DeviceManager.AllDevices.OfType().Where(d => - !d.ExcludeFromGlobalFunctions); - foreach (var room in allRooms) - room.RunRouteAction("roomOff"); - } - } + } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs index ed9582a0..f831bf26 100644 --- a/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs +++ b/PepperDashEssentials/Room/Types/EssentialsHuddleVtc1Room.cs @@ -344,7 +344,7 @@ namespace PepperDash.Essentials /// /// /// - public void RunRouteAction(string routeKey) + public override void RunRouteAction(string routeKey) { RunRouteAction(routeKey, () => { }); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs index df0aaa44..e9d844f0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/EssentialsRoomBase.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; using PepperDash.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices; @@ -9,9 +11,21 @@ namespace PepperDash.Essentials.Core /// /// /// - public abstract class EssentialsRoomBase : ReconfigurableDevice + public abstract class EssentialsRoomBase : ReconfigurableDevice, IHasCurrentSourceInfoChange { public event EventHandler CurrentVolumeDeviceChange; + public event SourceInfoChangeHandler CurrentSourceChange; + public string CurrentSourceInfoKey { get; set; } + + public IRoutingSinkWithSwitching DefaultDisplay { get; protected set; } + public IRoutingSink DefaultAudioDevice { get; protected set; } + public IBasicVolumeControls DefaultVolumeControls { get; protected set; } + + protected CCriticalSection RoutingLock = new CCriticalSection(); + + public string DefaultSourceItem { get; set; } + + public ushort DefaultVolume { get; set; } /// /// Sets the volume control device, and attaches/removes InUseTrackers with "audio" @@ -31,8 +45,10 @@ namespace PepperDash.Essentials.Core if (handler != null) { - CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(CurrentAudioDevice, value, ChangeType.WillChange)); - CurrentVolumeDeviceChange(this, new VolumeDeviceChangeEventArgs(CurrentAudioDevice, value, ChangeType.DidChange)); + CurrentVolumeDeviceChange(this, + new VolumeDeviceChangeEventArgs(CurrentAudioDevice, value, ChangeType.WillChange)); + CurrentVolumeDeviceChange(this, + new VolumeDeviceChangeEventArgs(CurrentAudioDevice, value, ChangeType.DidChange)); } var oldDevice = value as IInUseTracking; @@ -43,6 +59,52 @@ namespace PepperDash.Essentials.Core CurrentAudioDevice = value; } } + + protected string LastSourceKey; + + /// + /// The SourceListItem last run - containing names and icons + /// + public SourceListItem CurrentSourceInfo + { + get { return _currentSourceInfo; } + set + { + if (value == _currentSourceInfo) + { + return; + } + + var handler = CurrentSourceChange; + // remove from in-use tracker, if so equipped + if (_currentSourceInfo != null && _currentSourceInfo.SourceDevice is IInUseTracking) + { + (_currentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.RemoveUser(this, "control"); + } + + if (handler != null) + { + handler(_currentSourceInfo, ChangeType.WillChange); + } + + _currentSourceInfo = value; + + // add to in-use tracking + if (_currentSourceInfo != null && _currentSourceInfo.SourceDevice is IInUseTracking) + { + (_currentSourceInfo.SourceDevice as IInUseTracking).InUseTracker.AddUser(this, "control"); + } + if (handler != null) + { + handler(_currentSourceInfo, ChangeType.DidChange); + } + } + } + + private SourceListItem _currentSourceInfo; + + public bool ExcludeFromGlobalFunctions { get; set; } + protected IBasicVolumeControls CurrentAudioDevice; public BoolFeedback OnFeedback { get; private set; } @@ -61,6 +123,7 @@ namespace PepperDash.Essentials.Core protected Func IsWarmingFeedbackFunc; protected Func IsCoolingFeedbackFunc; + /// /// The config name of the source list /// @@ -100,42 +163,21 @@ namespace PepperDash.Essentials.Core /// protected Func OnFeedbackFunc; - protected Dictionary SavedVolumeLevels = new Dictionary(); + protected Dictionary SavedVolumeLevels = + new Dictionary(); - /// - /// When volume control devices change, should we zero the one that we are leaving? - /// - public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; } + /// + /// When volume control devices change, should we zero the one that we are leaving? + /// + public bool ZeroVolumeWhenSwtichingVolumeDevices { get; private set; } protected EssentialsRoomBase(DeviceConfig config) : base(config) { - // Setup the ShutdownPromptTimer - ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer"); - ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) => - { - if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue) - ShutdownType = eShutdownType.None; - }; - ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered + SetupShutdownPrompt(); - ShutdownPromptSeconds = 60; - ShutdownVacancySeconds = 120; - - ShutdownType = eShutdownType.None; - - RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer"); - //RoomVacancyShutdownTimer.IsRunningFeedback.OutputChange += (o, a) => - //{ - // if (!RoomVacancyShutdownTimer.IsRunningFeedback.BoolValue) - // ShutdownType = ShutdownType.Vacancy; - //}; - RoomVacancyShutdownTimer.HasFinished += RoomVacancyShutdownPromptTimer_HasFinished; // Shutdown is triggered - - RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning - RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt - VacancyMode = eVacancyMode.None; + SetupRoomVacancyShutdown(); OnFeedback = new BoolFeedback(OnFeedbackFunc); @@ -145,10 +187,42 @@ namespace PepperDash.Essentials.Core AddPostActivationAction(() => { if (RoomOccupancy != null) + { OnRoomOccupancyIsSet(); + } }); } + private void SetupRoomVacancyShutdown() + { + RoomVacancyShutdownTimer = new SecondsCountdownTimer(Key + "-vacancyOffTimer"); + + RoomVacancyShutdownTimer.HasFinished += RoomVacancyShutdownPromptTimer_HasFinished; // Shutdown is triggered + + RoomVacancyShutdownPromptSeconds = 1500; // 25 min to prompt warning + RoomVacancyShutdownSeconds = 240; // 4 min after prompt will trigger shutdown prompt + VacancyMode = eVacancyMode.None; + } + + private void SetupShutdownPrompt() + { + // Setup the ShutdownPromptTimer + ShutdownPromptTimer = new SecondsCountdownTimer(Key + "-offTimer"); + ShutdownPromptTimer.IsRunningFeedback.OutputChange += (o, a) => + { + if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue) + { + ShutdownType = eShutdownType.None; + } + }; + ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered + + ShutdownPromptSeconds = 60; + ShutdownVacancySeconds = 120; + + ShutdownType = eShutdownType.None; + } + protected void InitializeDisplay(DisplayBase display) { // Link power, warming, cooling to display @@ -188,11 +262,11 @@ namespace PepperDash.Essentials.Core StartRoomVacancyTimer(eVacancyMode.InShutdownWarning); break; case eVacancyMode.InShutdownWarning: - { - StartShutdown(eShutdownType.Vacancy); - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting Down due to vacancy."); - break; - } + { + StartShutdown(eShutdownType.Vacancy); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Shutting Down due to vacancy."); + break; + } } } @@ -216,7 +290,8 @@ namespace PepperDash.Essentials.Core ShutdownType = type; ShutdownPromptTimer.Start(); - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "ShutdownPromptTimer Started. Type: {0}. Seconds: {1}", ShutdownType, ShutdownPromptTimer.SecondsToCount); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "ShutdownPromptTimer Started. Type: {0}. Seconds: {1}", + ShutdownType, ShutdownPromptTimer.SecondsToCount); } public void StartRoomVacancyTimer(eVacancyMode mode) @@ -236,7 +311,8 @@ namespace PepperDash.Essentials.Core VacancyMode = mode; RoomVacancyShutdownTimer.Start(); - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", VacancyMode, RoomVacancyShutdownTimer.SecondsToCount); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Vacancy Timer Started. Mode: {0}. Seconds: {1}", + VacancyMode, RoomVacancyShutdownTimer.SecondsToCount); } /// @@ -269,23 +345,28 @@ namespace PepperDash.Essentials.Core { var provider = statusProvider as IKeyed; - if (provider == null) - { - Debug.Console(0, this, "ERROR: Occupancy sensor device is null"); - return; - } + if (provider == null) + { + Debug.Console(0, this, "ERROR: Occupancy sensor device is null"); + return; + } Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Room Occupancy set to device: '{0}'", provider.Key); Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Timeout Minutes from Config is: {0}", timeoutMinutes); // If status provider is fusion, set flag to remote if (statusProvider is Fusion.EssentialsHuddleSpaceFusionSystemControllerBase) + { OccupancyStatusProviderIsRemote = true; + } - if(timeoutMinutes > 0) - RoomVacancyShutdownSeconds = timeoutMinutes * 60; + if (timeoutMinutes > 0) + { + RoomVacancyShutdownSeconds = timeoutMinutes*60; + } - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "RoomVacancyShutdownSeconds set to {0}", RoomVacancyShutdownSeconds); + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "RoomVacancyShutdownSeconds set to {0}", + RoomVacancyShutdownSeconds); RoomOccupancy = statusProvider; @@ -295,11 +376,13 @@ namespace PepperDash.Essentials.Core OnRoomOccupancyIsSet(); } - void OnRoomOccupancyIsSet() + private void OnRoomOccupancyIsSet() { var handler = RoomOccupancyIsSet; if (handler != null) + { handler(this, new EventArgs()); + } } /// @@ -313,7 +396,7 @@ namespace PepperDash.Essentials.Core /// public abstract bool RunDefaultPresentRoute(); - void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e) + private void RoomIsOccupiedFeedback_OutputChange(object sender, EventArgs e) { if (RoomOccupancy.RoomIsOccupiedFeedback.BoolValue == false) { @@ -334,8 +417,303 @@ namespace PepperDash.Essentials.Core /// /// public abstract void RoomVacatedForTimeoutPeriod(object o); + + /// + /// + /// + /// + public virtual void RunRouteAction(string routeKey) + { + RunRouteAction(routeKey, String.Empty, () => { }); + } + + /// + /// Gets a source from config list SourceListKey and dynamically build and executes the + /// route or commands + /// + public virtual void RunRouteAction(string routeKey, Action successCallback) + { + RunRouteAction(routeKey, String.Empty, successCallback); + } + + /// + /// + /// + /// + /// + public virtual void RunRouteAction(string routeKey, string sourceListKey) + { + RunRouteAction(routeKey, sourceListKey, () => { }); + } + + /// + /// + /// + /// + /// + /// + public virtual void RunRouteAction(string routeKey, string sourceListKey, Action successCallback) + { + var routeObject = + new {RouteKey = routeKey, SourceListKey = sourceListKey, SuccessCallback = successCallback}; + CrestronInvoke.BeginInvoke(RunRouteAction, routeObject); // end of BeginInvoke + } + + protected virtual void RunRouteAction(object routeObject) + { + try + { + RoutingLock.Enter(); + + var routeObj = new {RouteKey = "", SourceListKey = "", SuccessCallback = new Action(() => { })}; + + routeObj = Cast(routeObj, routeObject); + + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Run route action '{0}'", routeObj.RouteKey); + var sourceList = GetSourceListForKey(routeObj.RouteKey, routeObj.SourceListKey); + + if (sourceList == null) + { + Debug.Console(0, this, "No source list found for key {0}", routeObj.SourceListKey); + return; + } + + var item = sourceList[routeObj.RouteKey]; + + // End usage timer on last source + StopUsageTrackingOnCurrentSource(sourceList); + + // Let's run it + if (routeObj.RouteKey.ToLower() != "roomoff") + { + LastSourceKey = routeObj.RouteKey; + } + else + { + CurrentSourceInfoKey = null; + } + + foreach (var route in item.RouteList) + { + var tempVideo = new SourceRouteListItem + { + DestinationKey = "$defaultDisplay", + SourceKey = route.SourceKey, + Type = eRoutingSignalType.Video + }; + + var routeItem = route.DestinationKey.Equals("$defaultAll", StringComparison.OrdinalIgnoreCase) + ? tempVideo + : route; + + DoRoute(routeItem); + } + + // Start usage timer on routed source + if (item.SourceDevice is IUsageTracking) + { + (item.SourceDevice as IUsageTracking).UsageTracker.StartDeviceUsage(); + } + + // Set volume control, using default if non provided + SetVolumeControl(item); + + // store the name and UI info for routes + if (item.SourceKey == "$off") + { + CurrentSourceInfoKey = routeObj.RouteKey; + CurrentSourceInfo = null; + } + else if (item.SourceKey != null) + { + CurrentSourceInfoKey = routeObj.RouteKey; + CurrentSourceInfo = item; + } + + OnFeedback.FireUpdate(); + + // report back when done + if (routeObj.SuccessCallback != null) + { + routeObj.SuccessCallback(); + } + } + finally + { + RoutingLock.Leave(); + } + } + + private static T Cast(T typeHolder, object m) + { + return (T) m; + } + + /// + /// + /// + /// + /// + protected void DoRoute(SourceRouteListItem route) + { + var dest = GetDestination(route); + + if (route.SourceKey.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IPower) + { + (dest as IPower).PowerOff(); + } + } + else + { + var source = DeviceManager.GetDeviceForKey(route.SourceKey) as IRoutingOutputs; + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", route.SourceKey, + route.DestinationKey); + return; + } + dest.ReleaseAndMakeRoute(source, route.Type); + } + } + + private IRoutingSink GetDestination(SourceRouteListItem route) + { + IRoutingSink dest; + if (route.DestinationKey.Equals("$defaultaudio", StringComparison.OrdinalIgnoreCase)) + { + dest = DefaultAudioDevice; + } + else if (route.DestinationKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + { + dest = DefaultDisplay; + } + else + { + dest = DeviceManager.GetDeviceForKey(route.DestinationKey) as IRoutingSink; + } + + if (dest != null) + { + return dest; + } + + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", route.DestinationKey); + return dest; + } + + private void SetVolumeControl(SourceListItem item) + { + IBasicVolumeControls volDev = null; + // Handle special cases for volume control + if (string.IsNullOrEmpty(item.VolumeControlKey) + || item.VolumeControlKey.Equals("$defaultAudio", StringComparison.OrdinalIgnoreCase)) + { + volDev = DefaultVolumeControls; + } + else if (item.VolumeControlKey.Equals("$defaultDisplay", StringComparison.OrdinalIgnoreCase)) + { + volDev = DefaultDisplay as IBasicVolumeControls; + } + else + { + var dev = DeviceManager.GetDeviceForKey(item.VolumeControlKey); + if (dev is IBasicVolumeControls) + { + volDev = dev as IBasicVolumeControls; + } + else if (dev is IHasVolumeDevice) + { + volDev = (dev as IHasVolumeDevice).VolumeDevice; + } + } + + if (volDev == CurrentVolumeControls) + { + return; + } + + IBasicVolumeWithFeedback vd; + // zero the volume on the device we are leaving. + // Set the volume to default on device we are entering + if (ZeroVolumeWhenSwtichingVolumeDevices && CurrentVolumeControls is IBasicVolumeWithFeedback) + { + vd = CurrentVolumeControls as IBasicVolumeWithFeedback; + SavedVolumeLevels[vd] = (uint) vd.VolumeLevelFeedback.IntValue; + vd.SetVolume(0); + } + CurrentVolumeControls = volDev; + if (!ZeroVolumeWhenSwtichingVolumeDevices || !(CurrentVolumeControls is IBasicVolumeWithFeedback)) + { + return; + } + + vd = CurrentVolumeControls as IBasicVolumeWithFeedback; + var vol = (SavedVolumeLevels.ContainsKey(vd) ? (ushort) SavedVolumeLevels[vd] : DefaultVolume); + vd.SetVolume(vol); + } + + private void StopUsageTrackingOnCurrentSource(Dictionary sourceList) + { + if (string.IsNullOrEmpty(LastSourceKey)) + { + return; + } + + var lastSource = sourceList[LastSourceKey].SourceDevice; + + try + { + if (lastSource is IUsageTracking) + { + (lastSource as IUsageTracking).UsageTracker.EndDeviceUsage(); + } + } + catch (Exception e) + { + Debug.Console(1, this, "*#* EXCEPTION in end usage tracking (257):\r{0}", e); + } + } + + private Dictionary GetSourceListForKey(string routeKey, string sourceListKey) + { + var slKey = String.IsNullOrEmpty(sourceListKey) ? SourceListKey : sourceListKey; + + var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(slKey); + + if (sourceList == null) + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found", slKey); + return null; + } + + // Try to get the list item by it's string key + if (sourceList.ContainsKey(routeKey)) + { + return sourceList; + } + + Debug.Console(1, this, "WARNING: No source list '{0}' found in config source lists '{1}'", + routeKey, SourceListKey); + return null; + } + + /// + /// Runs "roomOff" action on all rooms not set to ExcludeFromGlobalFunctions + /// + public static void AllRoomsOff() + { + var allRooms = DeviceManager.AllDevices.OfType().Where(d => + !d.ExcludeFromGlobalFunctions); + foreach (var room in allRooms) + { + room.RunRouteAction("roomOff"); + } + } } - + /// /// To describe the various ways a room may be shutting down /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs index cfd6f09e..35b431d9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Routing/RoutingPort.cs @@ -139,7 +139,7 @@ namespace PepperDash.Essentials.Core } } - public class RoutingOutputPort : RoutingPort + public class RoutingOutputPort : RoutingPort, IInUseTracking { /// /// The IRoutingOutputs object this port lives on