From 8d0ea82402f8d5822587985f053035bed8e315df Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 21 Jul 2020 13:27:15 -0600 Subject: [PATCH] lots of changes. Getting things working --- PepperDashEssentials/ControlSystem.cs | 68 +- .../EssentialsHuddleVtc1FusionController.cs | 49 +- .../UIDrivers/DualDisplayRouting.cs | 247 +- ...ntialsDualDisplayPanelAvFunctionsDriver.cs | 153 +- ...entialsHuddleVtc1PanelAvFunctionsDriver.cs | 37 +- .../EssentialsDualDisplayFusionController.cs | 36 +- ...> EssentialsFusionSystemControllerBase.cs} | 3532 +++++++++-------- .../EssentialsHuddleSpaceFusionController.cs | 13 +- .../EssentialsHuddleVtc1FusionController.cs | 66 +- .../RoomOnToDefaultSourceWhenOccupied.cs | 4 +- .../Rooms/EssentialsRoomBase.cs | 2 +- .../Rooms/Types/EssentialsDualDisplayRoom.cs | 18 +- .../Rooms/Types/EssentialsHuddleVtc1Room.cs | 1 - 13 files changed, 2071 insertions(+), 2155 deletions(-) rename essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/{EssentialsHuddleSpaceFusionSystemControllerBase.cs => EssentialsFusionSystemControllerBase.cs} (92%) diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 31d897e2..a9389fc1 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -11,13 +11,18 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Devices; using PepperDash.Essentials.Core.Fusion; +using PepperDash.Essentials.Core.Monitoring; using PepperDash.Essentials.Core.Rooms.Config; +using PepperDash.Essentials.Core.Touchpanels; using PepperDash.Essentials.DM; //using PepperDash.Essentials.Room.MobileControl; using Newtonsoft.Json; using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.DM.Config; +using PepperDash_Essentials_Core.Fusion; namespace PepperDash.Essentials { @@ -51,20 +56,17 @@ namespace PepperDash.Essentials if (Debug.DoNotLoadOnNextBoot) { - CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file", + CrestronConsole.AddNewConsoleCommand(s => GoWithLoadDeferred(), "go", "Loads configuration file", ConsoleAccessLevelEnum.AccessOperator); } CrestronConsole.AddNewConsoleCommand(PluginLoader.ReportAssemblyVersions, "reportversions", "Reports the versions of the loaded assemblies", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(PepperDash.Essentials.Core.DeviceFactory.GetDeviceFactoryTypes, "gettypes", "Gets the device types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(Core.DeviceFactory.GetDeviceFactoryTypes, "gettypes", "Gets the device types that can be built. Accepts a filter string.", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(BridgeHelper.PrintJoinMap, "getjoinmap", "map(s) for bridge or device on bridge [brKey [devKey]]", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s); - }, "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s), "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => { @@ -77,27 +79,29 @@ namespace PepperDash.Essentials { CrestronConsole.ConsoleCommandResponse ("Current running configuration. This is the merged system and template configuration"); - CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject - (ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented)); + CrestronConsole.ConsoleCommandResponse(JsonConvert.SerializeObject + (ConfigReader.ConfigObject, Formatting.Indented)); }, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => - { - CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" + - "System URL: {0}\r" + - "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl); - }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" + + "System URL: {0}\r" + + "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl), "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); if (!Debug.DoNotLoadOnNextBoot) { - GoWithLoad(); + GoWithLoad(null); return; } SystemMonitor.ProgramInitialization.ProgramInitializationComplete = true; } + private void GoWithLoadDeferred() + { + CrestronInvoke.BeginInvoke(GoWithLoad); + } + /// /// Determines if the program is running on a processor (appliance) or server (VC-4). /// @@ -113,13 +117,11 @@ namespace PepperDash.Essentials var dirSeparator = Global.DirectorySeparator; - string directoryPrefix; - - directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory(); + var directoryPrefix = Directory.GetApplicationRootDirectory(); var fullVersion = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false); - AssemblyInformationalVersionAttribute fullVersionAtt = fullVersion[0] as AssemblyInformationalVersionAttribute; + var fullVersionAtt = fullVersion[0] as AssemblyInformationalVersionAttribute; Global.SetAssemblyVersion(fullVersionAtt.InformationalVersion); @@ -170,7 +172,7 @@ namespace PepperDash.Essentials /// /// Begins the process of loading resources including plugins and configuration data /// - public void GoWithLoad() + public void GoWithLoad(object notUsed) { try { @@ -319,10 +321,10 @@ namespace PepperDash.Essentials { // Build the processor wrapper class - DeviceManager.AddDevice(new PepperDash.Essentials.Core.Devices.CrestronProcessor("processor")); + DeviceManager.AddDevice(new CrestronProcessor("processor")); // Add global System Monitor device - DeviceManager.AddDevice(new PepperDash.Essentials.Core.Monitoring.SystemMonitorController("systemMonitor")); + DeviceManager.AddDevice(new SystemMonitorController("systemMonitor")); foreach (var devConf in ConfigReader.ConfigObject.Devices) { @@ -339,28 +341,28 @@ namespace PepperDash.Essentials devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper()); // Check if the processor is a DMPS model - if (this.ControllerPrompt.IndexOf("dmps", StringComparison.OrdinalIgnoreCase) > -1) + if (ControllerPrompt.IndexOf("dmps", StringComparison.OrdinalIgnoreCase) > -1) { - Debug.Console(2, "Adding DmpsRoutingController for {0} to Device Manager.", this.ControllerPrompt); + Debug.Console(2, "Adding DmpsRoutingController for {0} to Device Manager.", ControllerPrompt); - var propertiesConfig = JsonConvert.DeserializeObject(devConf.Properties.ToString()); + var propertiesConfig = JsonConvert.DeserializeObject(devConf.Properties.ToString()); if(propertiesConfig == null) - propertiesConfig = new DM.Config.DmpsRoutingPropertiesConfig(); + propertiesConfig = new DmpsRoutingPropertiesConfig(); - var dmpsRoutingController = DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", this.ControllerPrompt, propertiesConfig); + var dmpsRoutingController = DmpsRoutingController.GetDmpsRoutingController("processor-avRouting", ControllerPrompt, propertiesConfig); DeviceManager.AddDevice(dmpsRoutingController); } - else if (this.ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) + else if (ControllerPrompt.IndexOf("mpc3", StringComparison.OrdinalIgnoreCase) > -1) { Debug.Console(2, "MPC3 processor type detected. Adding Mpc3TouchpanelController."); var butToken = devConf.Properties["buttons"]; if (butToken != null) { - var buttons = butToken.ToObject>(); - var tpController = new Essentials.Core.Touchpanels.Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); + var buttons = butToken.ToObject>(); + var tpController = new Mpc3TouchpanelController(devConf.Key, devConf.Name, Global.ControlSystem, buttons); DeviceManager.AddDevice(tpController); } else @@ -383,7 +385,7 @@ namespace PepperDash.Essentials IKeyed newDev = null; if (newDev == null) - newDev = PepperDash.Essentials.Core.DeviceFactory.GetDevice(devConf); + newDev = Core.DeviceFactory.GetDevice(devConf); // //if (newDev == null) @@ -455,7 +457,7 @@ namespace PepperDash.Essentials Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); DeviceManager.AddDevice( - new EssentialsHuddleSpaceFusionSystemControllerBase(huddleRoom, 0xf1)); + new EssentialsHuddleSpaceFusionController(huddleRoom, 0xf1)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -472,7 +474,7 @@ namespace PepperDash.Essentials Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsDualDisplayRoom, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController(ddRoom, 0xf1)); + DeviceManager.AddDevice(new EssentialsDualDisplayFusionController(ddRoom, 0xf1)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 722698a7..3061e65f 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -10,7 +10,7 @@ using PepperDash.Essentials.Core.Fusion; namespace PepperDash.Essentials.Fusion { - public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase + public class EssentialsHuddleVtc1FusionController : EssentialsFusionSystemControllerBase { private BooleanSigData _codecIsInCall; @@ -168,53 +168,6 @@ namespace PepperDash.Essentials.Fusion // These methods are overridden because they access the room class which is of a different type - protected override void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += - FusionRoomSchedule_DeviceExtenderSigChange; - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += - ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; - FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", - "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", - "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, "FusCreateMeeting", - "Creates and Ad Hoc meeting for on hour or until the next meeting", - ConsoleAccessLevelEnum.AccessOperator); - - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); - - // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", - eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - - var essentialsHuddleVtc1Room = Room as EssentialsHuddleVtc1Room; - if (essentialsHuddleVtc1Room != null) - { - essentialsHuddleVtc1Room.CurrentSourceChange += Room_CurrentSourceInfoChange; - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction( - essentialsHuddleVtc1Room.PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction( - () => essentialsHuddleVtc1Room.RunRouteAction("roomOff", Room.SourceListKey)); - } - - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; - } - protected override void SetUpSources() { // Sources diff --git a/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs b/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs index 1aeb7a3c..c0223cd0 100644 --- a/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs +++ b/PepperDashEssentials/UIDrivers/DualDisplayRouting.cs @@ -1,231 +1,24 @@ -//using System; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using Crestron.SimplSharp; -//using Crestron.SimplSharpPro; -//using Crestron.SimplSharpPro.DeviceSupport; -//using PepperDash.Core; -//using PepperDash.Essentials.Core; -//using PepperDash.Essentials.Core.SmartObjects; -//using PepperDash.Essentials.Core.PageManagers; +using System; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDashEssentials.UIDrivers.EssentialsDualDisplay; -//namespace PepperDash.Essentials -//{ -// public class DualDisplaySimpleOrAdvancedRouting : PanelDriverBase -// { -// EssentialsPresentationPanelAvFunctionsDriver Parent; +namespace PepperDash.Essentials +{ + public class DualDisplaySimpleOrAdvancedRouting : PanelDriverBase + { + private readonly EssentialsDualDisplayPanelAvFunctionsDriver _parent; + private EssentialsDualDisplayRoom _currentRoom; -// /// -// /// Smart Object 3200 -// /// -// SubpageReferenceList SourcesSrl; + private SourceListItem PendingSource; + + public DualDisplaySimpleOrAdvancedRouting(EssentialsDualDisplayPanelAvFunctionsDriver parent) + : base(parent.TriList) + { + _parent = parent; + _currentRoom = _parent.CurrentRoom as EssentialsDualDisplayRoom; + } -// /// -// /// For tracking feedback on last selected -// /// -// BoolInputSig LastSelectedSourceSig; -// /// -// /// The source that has been selected and is awaiting assignment to a display -// /// -// SourceListItem PendingSource; - -// bool IsSharingModeAdvanced; - -// public DualDisplaySimpleOrAdvancedRouting(EssentialsPresentationPanelAvFunctionsDriver parent) : base(parent.TriList) -// { -// Parent = parent; -// SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); - -// TriList.SetSigFalseAction(UIBoolJoin.ToggleSharingModePress, ToggleSharingModePressed); - -// TriList.SetSigFalseAction(UIBoolJoin.Display1AudioButtonPressAndFb, Display1AudioPress); -// TriList.SetSigFalseAction(UIBoolJoin.Display1ControlButtonPress, Display1ControlPress); -// TriList.SetSigTrueAction(UIBoolJoin.Display1SelectPressAndFb, Display1Press); - -// TriList.SetSigFalseAction(UIBoolJoin.Display2AudioButtonPressAndFb, Display2AudioPress); -// TriList.SetSigFalseAction(UIBoolJoin.Display2ControlButtonPress, Display2ControlPress); -// TriList.SetSigTrueAction(UIBoolJoin.Display2SelectPressAndFb, Display2Press); -// } - -// /// -// /// -// /// -// public override void Show() -// { -// TriList.BooleanInput[UIBoolJoin.ToggleSharingModeVisible].BoolValue = true; -// TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = true; -// if(IsSharingModeAdvanced) -// TriList.BooleanInput[UIBoolJoin.DualDisplayPageVisible].BoolValue = true; -// else -// TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; -// base.Show(); -// } - -// /// -// /// -// /// -// //public override void Hide() -// //{ -// // TriList.BooleanInput[UIBoolJoin.ToggleSharingModeVisible].BoolValue = false; -// // TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; -// // if(IsSharingModeAdvanced) -// // TriList.BooleanInput[UIBoolJoin.DualDisplayPageVisible].BoolValue = false; -// // else -// // TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; -// // base.Hide(); -// //} - -// public void SetCurrentRoomFromParent() -// { -// if (IsSharingModeAdvanced) -// return; // add stuff here -// else -// SetupSourceListForSimpleRouting(); -// } - -// /// -// /// -// /// -// void SetupSourceListForSimpleRouting() -// { -// // get the source list config and set up the source list -// var config = ConfigReader.ConfigObject.SourceLists; -// if (config.ContainsKey(Parent.CurrentRoom.SourceListKey)) -// { -// var srcList = config[Parent.CurrentRoom.SourceListKey] -// .Values.ToList().OrderBy(s => s.Order); -// // Setup sources list -// uint i = 1; // counter for UI list -// foreach (var srcConfig in srcList) -// { -// if (!srcConfig.IncludeInSourceList) // Skip sources marked this way -// continue; - -// var sourceKey = srcConfig.SourceKey; -// var actualSource = DeviceManager.GetDeviceForKey(sourceKey) as Device; -// if (actualSource == null) -// { -// Debug.Console(0, "Cannot assign missing source '{0}' to source UI list", -// srcConfig.SourceKey); -// continue; -// } -// var localSrcItem = srcConfig; // lambda scope below -// var localIndex = i; -// SourcesSrl.GetBoolFeedbackSig(i, 1).UserObject = new Action(b => -// { -// if (IsSharingModeAdvanced) -// { -// if (LastSelectedSourceSig != null) -// LastSelectedSourceSig.BoolValue = false; -// SourceListButtonPress(localSrcItem); -// LastSelectedSourceSig = SourcesSrl.BoolInputSig(localIndex, 1); -// LastSelectedSourceSig.BoolValue = true; -// } -// else -// Parent.CurrentRoom.DoSourceToAllDestinationsRoute(localSrcItem); -// }); -// SourcesSrl.StringInputSig(i, 1).StringValue = srcConfig.PreferredName; -// i++; - -// //var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, -// // b => { if (!b) UiSelectSource(localSrcConfig); }); -// //SourcesSrl.AddItem(item); // add to the SRL -// //item.RegisterForSourceChange(Parent.CurrentRoom); -// } -// SourcesSrl.Count = (ushort)(i - 1); -// Parent.CurrentRoom.CurrentSingleSourceChange += CurrentRoom_CurrentSourceInfoChange; -// Parent.CurrentRoom.CurrentDisplay1SourceChange += CurrentRoom_CurrentDisplay1SourceChange; -// Parent.CurrentRoom.CurrentDisplay2SourceChange += CurrentRoom_CurrentDisplay2SourceChange; -// } -// } - -// void SetupSourceListForAdvancedRouting() -// { - -// } - -// void CurrentRoom_CurrentSourceInfoChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) -// { - -// } - -// void CurrentRoom_CurrentDisplay1SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) -// { -// TriList.StringInput[UIStringJoin.Display1SourceLabel].StringValue = PendingSource.PreferredName; - -// } - -// void CurrentRoom_CurrentDisplay2SourceChange(EssentialsRoomBase room, SourceListItem info, ChangeType type) -// { -// TriList.StringInput[UIStringJoin.Display2SourceLabel].StringValue = PendingSource.PreferredName; -// } - -// /// -// /// -// /// -// void ToggleSharingModePressed() -// { -// Hide(); -// IsSharingModeAdvanced = !IsSharingModeAdvanced; -// TriList.BooleanInput[UIBoolJoin.ToggleSharingModePress].BoolValue = IsSharingModeAdvanced; -// Show(); -// } - -// public void SourceListButtonPress(SourceListItem item) -// { -// // start the timer -// // show FB on potential source -// TriList.BooleanInput[UIBoolJoin.Display1AudioButtonEnable].BoolValue = false; -// TriList.BooleanInput[UIBoolJoin.Display1ControlButtonEnable].BoolValue = false; -// TriList.BooleanInput[UIBoolJoin.Display2AudioButtonEnable].BoolValue = false; -// TriList.BooleanInput[UIBoolJoin.Display2ControlButtonEnable].BoolValue = false; -// PendingSource = item; -// } - -// void EnableAppropriateDisplayButtons() -// { -// TriList.BooleanInput[UIBoolJoin.Display1AudioButtonEnable].BoolValue = true; -// TriList.BooleanInput[UIBoolJoin.Display1ControlButtonEnable].BoolValue = true; -// TriList.BooleanInput[UIBoolJoin.Display2AudioButtonEnable].BoolValue = true; -// TriList.BooleanInput[UIBoolJoin.Display2ControlButtonEnable].BoolValue = true; -// if (LastSelectedSourceSig != null) -// LastSelectedSourceSig.BoolValue = false; -// } - -// public void Display1Press() -// { -// EnableAppropriateDisplayButtons(); -// Parent.CurrentRoom.SourceToDisplay1(PendingSource); -// // Enable end meeting -// } - -// public void Display1AudioPress() -// { - -// } - - -// public void Display1ControlPress() -// { - -// } - -// public void Display2Press() -// { -// EnableAppropriateDisplayButtons(); -// Parent.CurrentRoom.SourceToDisplay2(PendingSource); -// } - -// public void Display2AudioPress() -// { - -// } - -// public void Display2ControlPress() -// { - -// } -// } -//} \ No newline at end of file + } +} \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/EssentialsDualDisplay/EssentialsDualDisplayPanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsDualDisplay/EssentialsDualDisplayPanelAvFunctionsDriver.cs index 50d7c5e2..1a74c990 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsDualDisplay/EssentialsDualDisplayPanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsDualDisplay/EssentialsDualDisplayPanelAvFunctionsDriver.cs @@ -13,20 +13,114 @@ namespace PepperDashEssentials.UIDrivers.EssentialsDualDisplay private EssentialsDualDisplayRoom _currentRoom; private readonly BoolInputSig _routeToggleVisibility; + private readonly BoolInputSig _dualDisplayControlVisibility; - private readonly BoolFeedback _sharingMode; + private readonly BoolOutputSig _sharingModeSig; + + private readonly BoolFeedback _sharingModeFeedback; + private readonly BoolFeedback _dualDisplayVisiblityFeedback; + private readonly BoolFeedback _routeToggleVisiblityFeedback; public EssentialsDualDisplayPanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) : base(parent, config) { _routeToggleVisibility = parent.TriList.BooleanInput[UIBoolJoin.ToggleSharingModeVisible]; + _dualDisplayControlVisibility = parent.TriList.BooleanInput[UIBoolJoin.DualDisplayPageVisible]; + _sharingModeSig = parent.TriList.BooleanOutput[UIBoolJoin.ToggleSharingModePress]; - _sharingMode = new BoolFeedback(() => _currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced); + _sharingModeSig.SetBoolSigAction(ToggleVideoBehavior); - _sharingMode.LinkInputSig(parent.TriList.BooleanInput[UIBoolJoin.ToggleSharingModePress]); + _sharingModeFeedback = new BoolFeedback(() => _currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced); + _sharingModeFeedback.LinkInputSig(parent.TriList.BooleanInput[UIBoolJoin.ToggleSharingModePress]); + + _dualDisplayVisiblityFeedback = + new BoolFeedback( + () => + _currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced && + CurrentMode == UiDisplayMode.Presentation); + _dualDisplayVisiblityFeedback.LinkInputSig(_dualDisplayControlVisibility); + + _routeToggleVisiblityFeedback = + new BoolFeedback( + () => _currentRoom.RoomConfig.EnableVideoBehaviorToggle && CurrentMode == UiDisplayMode.Presentation); + _routeToggleVisiblityFeedback.LinkInputSig(_routeToggleVisibility); } #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + protected override void ShowCurrentSource() + { + if (_currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced) return; + + base.ShowCurrentSource(); + } + + #endregion + + private void ToggleVideoBehavior(bool value) + { + if (!value) return; + + _currentRoom.VideoRoutingBehavior = _currentRoom.VideoRoutingBehavior == EVideoBehavior.Basic + ? EVideoBehavior.Advanced + : EVideoBehavior.Basic; + + _sharingModeFeedback.FireUpdate(); + _dualDisplayVisiblityFeedback.FireUpdate(); + + TriList.SetBool(UIBoolJoin.SelectASourceVisible, _currentRoom.VideoRoutingBehavior == EVideoBehavior.Basic); + + if (_currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced) + { + CurrentSourcePageManager.Hide(); + } + else + { + if (_currentRoom.CurrentSourceInfo != null) + { + UiSelectSource(_currentRoom.CurrentSourceInfoKey); + TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); + } + CurrentSourcePageManager.Show(); + } + } + + #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + + #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + + protected override void SetCurrentRoom(EssentialsHuddleVtc1Room room) + { + _currentRoom = room as EssentialsDualDisplayRoom; + + base.SetCurrentRoom(room); + } + + #endregion + + #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + + protected override void ActivityShareButtonPressed() + { + base.ActivityShareButtonPressed(); + + _dualDisplayVisiblityFeedback.FireUpdate(); + _routeToggleVisiblityFeedback.FireUpdate(); + } + + #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + + public override void ActivityCallButtonPressed() + { + base.ActivityCallButtonPressed(); + + _dualDisplayVisiblityFeedback.FireUpdate(); + _routeToggleVisiblityFeedback.FireUpdate(); + } + + #endregion + + #endregion + protected override void SetupSourceList() { var inCall = _currentRoom.InCallFeedback.BoolValue; @@ -40,24 +134,65 @@ namespace PepperDashEssentials.UIDrivers.EssentialsDualDisplay SourceStagingSrl.Clear(); uint i = 1; - foreach (var src in sourceList.Where( - src => - src.Value.IncludeInSourceList && (inCall && !src.Value.DisableCodecSharing) && - (CurrentMode != UiDisplayMode.Call && !src.Value.DisableCodecSharing))) + foreach (var src in sourceList) { var source = src.Value; var sourceKey = src.Key; Debug.Console(1, "**** {0}, {1}, {2}, {3}, {4}", source.PreferredName, source.IncludeInSourceList, source.DisableCodecSharing, inCall, CurrentMode); - var srlItem = new SubpageReferenceListSourceItem(i++, SourceStagingSrl, src.Value, + if (!source.IncludeInSourceList || (inCall && source.DisableCodecSharing) + || CurrentMode == UiDisplayMode.Call && source.DisableCodecSharing) + { + Debug.Console(1, "Skipping {0}", source.PreferredName); + continue; + } + var srlItem = new SubpageReferenceListSourceItem(i++, SourceStagingSrl, source, b => { if (!b) UiSelectSource(sourceKey); }); SourceStagingSrl.AddItem(srlItem); srlItem.RegisterForSourceChange(_currentRoom); } + SourceStagingSrl.Count = (ushort) (i - 1); + Debug.Console(2, "Dual Display Source Count: {0}", SourceStagingSrl.Count); } + #region Overrides of EssentialsHuddleVtc1PanelAvFunctionsDriver + + protected override void SetSourceFeedback() + { + if (!CurrentRoom.OnFeedback.BoolValue) + { + // If there's no default, show UI elements + if (!CurrentRoom.RunDefaultPresentRoute() && _currentRoom.VideoRoutingBehavior == EVideoBehavior.Basic) + { + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + } + } + else // room is on show what's active or select a source if nothing is yet active + { + if (CurrentRoom.CurrentSourceInfo == null || + CurrentRoom.CurrentSourceInfoKey == EssentialsHuddleVtc1Room.DefaultCodecRouteString) + { + TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); + return; + } + + if (CurrentSourcePageManager == null) return; + + if (_currentRoom.VideoRoutingBehavior == EVideoBehavior.Advanced) + { + CurrentSourcePageManager.Hide(); + return; + } + + CurrentSourcePageManager.Show(); + + } + } + + #endregion + private void RefreshRoom(EssentialsDualDisplayRoom room) { RefreshCurrentRoom(room); @@ -65,7 +200,7 @@ namespace PepperDashEssentials.UIDrivers.EssentialsDualDisplay _currentRoom = room; _routeToggleVisibility.BoolValue = _currentRoom.RoomConfig.EnableVideoBehaviorToggle; - _sharingMode.FireUpdate(); + _sharingModeFeedback.FireUpdate(); } private void UiSelectSource(string sourceKey) diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 96af4c9b..53ca8e56 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -91,7 +91,7 @@ namespace PepperDash.Essentials /// /// Current page manager running for a source /// - private PageManager _currentSourcePageManager; + protected PageManager CurrentSourcePageManager; /// /// Tracks the last meeting that was cancelled @@ -270,7 +270,7 @@ namespace PepperDash.Essentials /// /// /// - public void ActivityCallButtonPressed() + public virtual void ActivityCallButtonPressed() { if (VcDriver.IsVisible) { @@ -281,9 +281,9 @@ namespace PepperDash.Essentials TriList.SetBool(UIBoolJoin.StartPageVisible, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SelectASourceVisible, false); - if (_currentSourcePageManager != null) + if (CurrentSourcePageManager != null) { - _currentSourcePageManager.Hide(); + CurrentSourcePageManager.Hide(); } PowerOnFromCall(); CurrentMode = UiDisplayMode.Call; @@ -691,6 +691,14 @@ namespace PepperDash.Essentials TriList.SetBool(UIBoolJoin.CallStagingBarVisible, false); TriList.SetBool(UIBoolJoin.SourceStagingBarVisible, true); // Run default source when room is off and share is pressed + SetSourceFeedback(); + CurrentMode = UiDisplayMode.Presentation; + SetupSourceList(); + SetActivityFooterFeedbacks(); + } + + protected virtual void SetSourceFeedback() + { if (!CurrentRoom.OnFeedback.BoolValue) { // If there's no default, show UI elements @@ -706,14 +714,11 @@ namespace PepperDash.Essentials { TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); } - else if (_currentSourcePageManager != null) + else if (CurrentSourcePageManager != null) { - _currentSourcePageManager.Show(); + CurrentSourcePageManager.Show(); } } - CurrentMode = UiDisplayMode.Presentation; - SetupSourceList(); - SetActivityFooterFeedbacks(); } /// @@ -754,7 +759,7 @@ namespace PepperDash.Essentials /// /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them /// - private void ShowCurrentSource() + protected virtual void ShowCurrentSource() { if (CurrentRoom.CurrentSourceInfo == null) { @@ -795,7 +800,7 @@ namespace PepperDash.Essentials pm = new DefaultPageManager(uiDev, TriList); } _pageManagers[uiDev] = pm; - _currentSourcePageManager = pm; + CurrentSourcePageManager = pm; pm.Show(); } @@ -1061,7 +1066,7 @@ namespace PepperDash.Essentials } } - private void SetCurrentRoom(EssentialsHuddleVtc1Room room) + protected virtual void SetCurrentRoom(EssentialsHuddleVtc1Room room) { if (_currentRoom == room || room == null) { @@ -1346,10 +1351,10 @@ namespace PepperDash.Essentials // Hide whatever is showing if (IsVisible) { - if (_currentSourcePageManager != null) + if (CurrentSourcePageManager != null) { - _currentSourcePageManager.Hide(); - _currentSourcePageManager = null; + CurrentSourcePageManager.Hide(); + CurrentSourcePageManager = null; } } @@ -1394,7 +1399,7 @@ namespace PepperDash.Essentials /// /// Refreshes and shows the room's current source /// - private void RefreshSourceInfo() + protected virtual void RefreshSourceInfo() { var routeInfo = CurrentRoom.CurrentSourceInfo; // This will show off popup too diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsDualDisplayFusionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsDualDisplayFusionController.cs index 88364e80..fa063806 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsDualDisplayFusionController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsDualDisplayFusionController.cs @@ -14,27 +14,41 @@ namespace PepperDash.Essentials.Core.Fusion _room = room; } - #region Overrides of EssentialsHuddleSpaceFusionSystemControllerBase + #region Overrides of EssentialsHuddleVtc1FusionController protected override void ExecuteCustomSteps() { - var leftDisplay = _room.LeftDisplay as DisplayBase; - var rightDisplay = _room.RightDisplay as DisplayBase; - - SetUpDisplay(leftDisplay); - SetUpDisplay(rightDisplay); + SetUpDisplays(); base.ExecuteCustomSteps(); } #endregion - #region Overrides of EssentialsHuddleSpaceFusionSystemControllerBase - - protected override void SetUpDisplay() + private void SetUpDisplays() { - Debug.Console(1, this, "No default Display fo this room"); + if (_room == null) return; + + var leftDisplay = _room.LeftDisplay as DisplayBase; + var rightDisplay = _room.RightDisplay as DisplayBase; + + SetUpDisplay(leftDisplay); + SetUpDisplay(rightDisplay); } + protected override void SetUpDefaultDisplay() + { + Debug.Console(0, this, "No default display for Dual Display Room"); + } + + #region Overrides of EssentialsFusionSystemControllerBase + + protected override void SetUpDefaultDisplayAsset() + { + Debug.Console(0, this, "No default display for Dual Display Room"); + } + + #endregion + private void SetUpDisplay(DisplayBase display) { FusionAsset tempAsset; @@ -77,7 +91,5 @@ namespace PepperDash.Essentials.Core.Fusion { throw new NotImplementedException(); } - - #endregion } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsFusionSystemControllerBase.cs similarity index 92% rename from essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsFusionSystemControllerBase.cs index d2ead037..08606dcd 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsFusionSystemControllerBase.cs @@ -1,1733 +1,1801 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharp.CrestronXml; -using Crestron.SimplSharp.CrestronXml.Serialization; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.Fusion; -using Newtonsoft.Json; -using PepperDash.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.Rooms; - -namespace PepperDash.Essentials.Core.Fusion -{ - public class EssentialsHuddleSpaceFusionSystemControllerBase : EssentialsDevice, IOccupancyStatusProvider - { - private const string RemoteOccupancyXml = "Local{0}"; - private readonly bool _guidFileExists; - - private readonly Dictionary _sourceToFeedbackSigs = - new Dictionary(); - - private Event _currentMeeting; - - protected StringSigData CurrentRoomSourceNameSig; - - private RoomSchedule _currentSchedule; - public FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); - private CTimer _dailyTimeRequestTimer; - protected FusionOccupancySensorAsset FusionOccSensor; - protected FusionRemoteOccupancySensor FusionRemoteOccSensor; - protected FusionRoom FusionRoom; - protected Dictionary FusionStaticAssets; - - private FusionRoomGuids _guiDs; - private uint _ipId; - - private bool _isRegisteredForSchedulePushNotifications; - private Event _nextMeeting; - - private CTimer _pollTimer; - public long PushNotificationTimeout = 5000; - - private CTimer _pushNotificationTimer; - protected EssentialsRoomBase Room; - - // Default poll time is 5 min unless overridden by config value - public long SchedulePollInterval = 300000; - private StatusMonitorCollection _errorMessageRollUp; - - private string _roomOccupancyRemoteString; - - #region System Info Sigs - - //StringSigData SystemName; - //StringSigData Model; - //StringSigData SerialNumber; - //StringSigData Uptime; - - #endregion - - #region Processor Info Sigs - - private readonly StringSigData[] _program = new StringSigData[10]; - private StringSigData _dns1; - private StringSigData _dns2; - private StringSigData _domain; - private StringSigData _firmware; - private StringSigData _gateway; - private StringSigData _hostname; - private StringSigData _ip1; - private StringSigData _ip2; - private StringSigData _mac1; - private StringSigData _mac2; - private StringSigData _netMask1; - private StringSigData _netMask2; - - #endregion - - #region Default Display Source Sigs - - private BooleanSigData[] _source = new BooleanSigData[10]; - - #endregion - - public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId) - : base(room.Key + "-fusion") - { - try - { - Room = room; - - _ipId = ipId; - - FusionStaticAssets = new Dictionary(); - - _guiDs = new FusionRoomGuids(); - - var mac = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); - - var slot = Global.ControlSystem.ProgramNumber; - - var guidFilePath = Global.FilePathPrefix + - string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); - - _guidFileExists = File.Exists(guidFilePath); - - // Check if file exists - if (!_guidFileExists) - { - // Does not exist. Create GUIDs - _guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac), - FusionStaticAssets); - } - else - { - // Exists. Read GUIDs - ReadGuidFile(guidFilePath); - } - - Initialize(); - - if (Room.RoomOccupancy != null) - { - if (Room.OccupancyStatusProviderIsRemote) - { - SetUpRemoteOccupancy(); - } - else - { - SetUpLocalOccupancy(); - } - } - - // Make it so! - FusionRVI.GenerateFileForAllFusionDevices(); - - GenerateGuidFile(guidFilePath); - } - catch (Exception e) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e); - } - } - - protected string RoomGuid - { - get { return _guiDs.RoomGuid; } - } - - public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } - - protected Func RoomIsOccupiedFeedbackFunc - { - get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; } - } - - #region IOccupancyStatusProvider Members - - public BoolFeedback RoomIsOccupiedFeedback { get; private set; } - - #endregion - - public event EventHandler ScheduleChange; - //public event EventHandler MeetingEndWarning; - //public event EventHandler NextMeetingBeginWarning; - - public event EventHandler RoomInfoChange; - - //ScheduleResponseEvent NextMeeting; - - /// - /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files - /// - protected virtual void ExecuteCustomSteps() - { - } - - protected void Initialize() - { - CreateSymbolAndBasicSigs(_ipId); - SetUpSources(); - SetUpCommunitcationMonitors(); - SetUpDisplay(); - SetUpError(); - ExecuteCustomSteps(); - } - - /// - /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. - /// - /// path for the file - private void GenerateGuidFile(string filePath) - { - if (string.IsNullOrEmpty(filePath)) - { - Debug.Console(0, this, "Error writing guid file. No path specified."); - return; - } - - var fileLock = new CCriticalSection(); - - try - { - if (fileLock.Disposed) - { - return; - } - - fileLock.Enter(); - - Debug.Console(1, this, "Writing GUIDs to file"); - - _guiDs = FusionOccSensor == null - ? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets) - : new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets, FusionOccSensor); - - var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented); - - using (var sw = new StreamWriter(filePath)) - { - sw.Write(json); - sw.Flush(); - } - - Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); - } - catch (Exception e) - { - Debug.Console(0, this, "Error writing guid file: {0}", e); - } - finally - { - if (!fileLock.Disposed) - { - fileLock.Leave(); - } - } - } - - /// - /// Reads the guid file from NVRAM - /// - /// path for te file - private void ReadGuidFile(string filePath) - { - if (string.IsNullOrEmpty(filePath)) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified."); - return; - } - - var fileLock = new CCriticalSection(); - - try - { - if (fileLock.Disposed) - { - return; - } - - fileLock.Enter(); - - if (File.Exists(filePath)) - { - var json = File.ReadToEnd(filePath, Encoding.ASCII); - - _guiDs = JsonConvert.DeserializeObject(json); - - _ipId = _guiDs.IpId; - - FusionStaticAssets = _guiDs.StaticAssets; - } - - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", - filePath); - - Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, _ipId, RoomGuid); - - foreach (var item in FusionStaticAssets) - { - Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, - item.Value.SlotNumber, item.Value.InstanceId); - } - } - catch (Exception e) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file: {0}", e); - } - finally - { - if (!fileLock.Disposed) - { - fileLock.Leave(); - } - } - } - - protected virtual void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += - FusionRoomSchedule_DeviceExtenderSigChange; - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += - ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; - FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", - "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", - "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, "FusCreateMeeting", - "Creates and Ad Hoc meeting for on hour or until the next meeting", - ConsoleAccessLevelEnum.AccessOperator); - - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); - - // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", - eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; - if (hasCurrentSourceInfoChange != null) - { - hasCurrentSourceInfoChange.CurrentSourceChange += - Room_CurrentSourceInfoChange; - } - - - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction( - Room.PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction( - () => - { - var runRouteAction = Room as IRunRouteAction; - if (runRouteAction != null) - { - runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); - } - }); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - FusionRoom.ErrorMessage.InputSig.StringValue = - "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; - - SetUpEthernetValues(); - - GetProcessorEthernetValues(); - - GetSystemInfo(); - - GetProcessorInfo(); - - CrestronEnvironment.EthernetEventHandler += - CrestronEnvironment_EthernetEventHandler; - } - - protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) - { - GetProcessorEthernetValues(); - } - } - - protected void GetSystemInfo() - { - //SystemName.InputSig.StringValue = Room.Name; - //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; - //SerialNumber.InputSig.StringValue = InitialParametersClass. - - var response = string.Empty; - - var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); - systemReboot.OutputSig.SetSigFalseAction( - () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); - } - - protected void SetUpEthernetValues() - { - _ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); - _ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); - _gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); - _hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); - _domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); - _dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); - _dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); - _mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); - _mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); - _netMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); - _netMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); - } - - protected void GetProcessorEthernetValues() - { - _ip1.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); - _gateway.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); - _hostname.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); - _domain.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - - var dnsServers = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); - _dns1.InputSig.StringValue = dnsServers[0]; - if (dnsServers.Length > 1) - { - _dns2.InputSig.StringValue = dnsServers[1]; - } - - _mac1.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); - _netMask1.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); - - // Interface 1 - - if (InitialParametersClass.NumberOfEthernetInterfaces > 1) - // Only get these values if the processor has more than 1 NIC - { - _ip2.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); - _mac2.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); - _netMask2.InputSig.StringValue = - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); - } - } - - protected void GetProcessorInfo() - { - _firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); - - if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) - { - for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) - { - var join = 62 + i; - var progNum = i + 1; - _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, - string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); - } - } - - _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; - } - - protected void GetCustomProperties() - { - if (FusionRoom.IsOnline) - { - const string fusionRoomCustomPropertiesRequest = @"RoomConfigurationRequest"; - - FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = - fusionRoomCustomPropertiesRequest; - } - } - - private void GetTouchpanelInfo() - { - // TODO: Get IP and Project Name from TP - } - - protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) - { - if (args.DeviceOnLine) - { - CrestronEnvironment.Sleep(200); - - // Send Push Notification Action request: - - const string requestId = "InitialPushRequest"; - - - var actionRequest = - string.Format("\n{0}\n", requestId) + - "RegisterPushModel\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - - Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; - - GetCustomProperties(); - - // Request current Fusion Server Time - RequestLocalDateTime(null); - - // Setup timer to request time daily - if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) - { - _dailyTimeRequestTimer.Stop(); - _dailyTimeRequestTimer.Dispose(); - } - - _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); - - _dailyTimeRequestTimer.Reset(86400000, 86400000); - } - } - - /// - /// Requests the local date and time from the Fusion Server - /// - /// - public void RequestLocalDateTime(object callbackObject) - { - const string timeRequestId = "TimeRequest"; - - var timeRequest = string.Format("{0}", - timeRequestId); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; - } - - /// - /// Generates a room schedule request for this room for the next 24 hours. - /// - public void RequestFullRoomSchedule(object callbackObject) - { - var now = DateTime.Today; - - var currentTime = now.ToString("s"); - - var requestTest = - string.Format( - "FullSchedleRequest{0}{1}24", - RoomGuid, currentTime); - - Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; - - if (_isRegisteredForSchedulePushNotifications) - { - _pushNotificationTimer.Stop(); - } - } - - /// - /// Wrapper method to allow console commands to modify the current meeting end time - /// - /// meetingID extendTime - public void ModifyMeetingEndTimeConsoleHelper(string command) - { - var extendMinutes = -1; - - const string requestId = "ModifyMeetingTest12345"; - - try - { - var tokens = command.Split(' '); - - //var meetingId = tokens[0]; never used, commenting out for now - extendMinutes = Int32.Parse(tokens[1]); - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing console command: {0}", e); - } - - ModifyMeetingEndTime(requestId, extendMinutes); - } - - /// - /// Ends or Extends the current meeting by the specified number of minutes. - /// - /// - /// Number of minutes to extend the meeting. A value of 0 will end the meeting. - public void ModifyMeetingEndTime(string requestId, int extendMinutes) - { - if (_currentMeeting == null) - { - Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); - return; - } - - if (extendMinutes > -1) - { - if (extendMinutes > 0) - { - var extendTime = _currentMeeting.dtEnd - DateTime.Now; - var extendMinutesRaw = extendTime.TotalMinutes; - - extendMinutes = extendMinutes + (int) Math.Round(extendMinutesRaw); - } - - - var requestTest = string.Format( - "{0}{1}MeetingChange" - , requestId, RoomGuid, _currentMeeting.MeetingID, extendMinutes); - - Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest; - } - else - { - Debug.Console(1, this, "Invalid time specified"); - } - } - - /// - /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour. - /// - public void CreateAdHocMeeting(string command) - { - const string requestId = "CreateAdHocMeeting"; - - var now = DateTime.Now.AddMinutes(1); - - now.AddSeconds(-now.Second); - - // Assume 1 hour meeting if possible - var dtEnd = now.AddHours(1); - - // Check if room is available for 1 hour before next meeting - if (_nextMeeting != null) - { - var roomAvailable = _nextMeeting.dtEnd.Subtract(dtEnd); - - if (roomAvailable.TotalMinutes < 60) - { - // Room not available for full hour, book until next meeting starts - dtEnd = _nextMeeting.dtEnd; - } - } - - var createMeetingRequest = - "" + - string.Format("{0}", requestId) + - string.Format("{0}", RoomGuid) + - "" + - string.Format("{0}", now.ToString("s")) + - string.Format("{0}", dtEnd.ToString("s")) + - "AdHoc Meeting" + - "Room User" + - "Example Message" + - "" + - ""; - - Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest; - - //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); - - //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; - } - - /// - /// Event handler method for Device Extender sig changes - /// - /// - /// - protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, - SigEventArgs args) - { - Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, - args.Sig.StringValue); - - - if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) - { - try - { - var message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var actionResponse = message["ActionResponse"]; - - if (actionResponse == null) - { - return; - } - var requestId = actionResponse["RequestID"]; - - if (requestId.InnerText != "InitialPushRequest") - { - return; - } - if (actionResponse["ActionID"].InnerText != "RegisterPushModel") - { - return; - } - var parameters = actionResponse["Parameters"]; - - foreach (var isRegistered in from XmlElement parameter in parameters - where parameter.HasAttributes - select parameter.Attributes - into attributes - where attributes["ID"].Value == "Registered" - select Int32.Parse(attributes["Value"].Value)) - { - switch (isRegistered) - { - case 1: - _isRegisteredForSchedulePushNotifications = true; - if (_pollTimer != null && !_pollTimer.Disposed) - { - _pollTimer.Stop(); - _pollTimer.Dispose(); - } - _pushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, - PushNotificationTimeout, PushNotificationTimeout); - _pushNotificationTimer.Reset(PushNotificationTimeout, - PushNotificationTimeout); - break; - case 0: - _isRegisteredForSchedulePushNotifications = false; - if (_pushNotificationTimer != null && !_pushNotificationTimer.Disposed) - { - _pushNotificationTimer.Stop(); - _pushNotificationTimer.Dispose(); - } - _pollTimer = new CTimer(RequestFullRoomSchedule, null, - SchedulePollInterval, SchedulePollInterval); - _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); - break; - } - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse) - { - try - { - var message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var localDateTimeResponse = message["LocalTimeResponse"]; - - if (localDateTimeResponse != null) - { - var localDateTime = localDateTimeResponse["LocalDateTime"]; - - if (localDateTime != null) - { - var tempLocalDateTime = localDateTime.InnerText; - - var currentTime = DateTime.Parse(tempLocalDateTime); - - Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime); - - // Parse time and date from response and insert values - CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute, - (ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day, - (ushort) currentTime.Year); - - Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); - } - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigResponse) - { - // Room info response with custom properties - - var roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); - - Debug.Console(2, this, "Fusion Response: \n {0}", roomConfigResponseArgs); - - try - { - var roomConfigResponse = new XmlDocument(); - - roomConfigResponse.LoadXml(roomConfigResponseArgs); - - var requestRoomConfiguration = roomConfigResponse["RoomConfigurationResponse"]; - - if (requestRoomConfiguration != null) - { - var roomInformation = new RoomInformation(); - - foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes) - { - if (e.Name == "RoomInformation") - { - var roomInfo = new XmlReader(e.OuterXml); - - roomInformation = CrestronXMLSerialization.DeSerializeObject(roomInfo); - } - else if (e.Name == "CustomFields") - { - foreach (XmlElement el in e) - { - var customProperty = new FusionCustomProperty(); - - if (el.Name == "CustomField") - { - customProperty.Id = el.Attributes["ID"].Value; - } - - foreach (XmlElement elm in el) - { - if (elm.Name == "CustomFieldName") - { - customProperty.CustomFieldName = elm.InnerText; - } - if (elm.Name == "CustomFieldType") - { - customProperty.CustomFieldType = elm.InnerText; - } - if (elm.Name == "CustomFieldValue") - { - customProperty.CustomFieldValue = elm.InnerText; - } - } - - roomInformation.FusionCustomProperties.Add(customProperty); - } - } - } - - var handler = RoomInfoChange; - if (handler != null) - { - handler(this, new EventArgs()); - } - - CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation); - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing Custom Properties response: {0}", e); - } - //PrintRoomInfo(); - //getRoomInfoBusy = false; - //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy; - } - } - - /// - /// Event handler method for Device Extender sig changes - /// - /// - /// - protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, - SigEventArgs args) - { - Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, - args.Sig.Name, args.Sig.StringValue); - - - if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) - { - try - { - var scheduleResponse = new ScheduleResponse(); - - var message = new XmlDocument(); - - message.LoadXml(args.Sig.StringValue); - - var response = message["ScheduleResponse"]; - - if (response != null) - { - // Check for push notification - if (response["RequestID"].InnerText == "RVRequest") - { - var action = response["Action"]; - - if (action.OuterXml.IndexOf("RequestSchedule", StringComparison.Ordinal) > -1) - { - _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); - } - } - else // Not a push notification - { - _currentSchedule = new RoomSchedule(); // Clear Current Schedule - _currentMeeting = null; // Clear Current Meeting - _nextMeeting = null; // Clear Next Meeting - - var isNextMeeting = false; - - foreach (XmlElement element in message.FirstChild.ChildNodes) - { - if (element.Name == "RequestID") - { - scheduleResponse.RequestID = element.InnerText; - } - else if (element.Name == "RoomID") - { - scheduleResponse.RoomID = element.InnerText; - } - else if (element.Name == "RoomName") - { - scheduleResponse.RoomName = element.InnerText; - } - else if (element.Name == "Event") - { - Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); - - var reader = new XmlReader(element.OuterXml); - - var tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); - - scheduleResponse.Events.Add(tempEvent); - - // Check is this is the current event - if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now) - { - _currentMeeting = tempEvent; // Set Current Meeting - isNextMeeting = true; // Flag that next element is next meeting - } - - if (isNextMeeting) - { - _nextMeeting = tempEvent; // Set Next Meeting - isNextMeeting = false; - } - - _currentSchedule.Meetings.Add(tempEvent); - } - } - - PrintTodaysSchedule(); - - if (!_isRegisteredForSchedulePushNotifications) - { - _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); - } - - // Fire Schedule Change Event - var handler = ScheduleChange; - - if (handler != null) - { - handler(this, new ScheduleChangeEventArgs {Schedule = _currentSchedule}); - } - } - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) - { - Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, - args.Sig.Name, args.Sig.StringValue); - } - } - - /// - /// Prints today's schedule to console for debugging - /// - private void PrintTodaysSchedule() - { - if (Debug.Level > 1) - { - if (_currentSchedule.Meetings.Count > 0) - { - Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); - - foreach (var e in _currentSchedule.Meetings) - { - Debug.Console(1, this, "Subject: {0}", e.Subject); - Debug.Console(1, this, "Organizer: {0}", e.Organizer); - Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); - Debug.Console(1, this, "Start Time: {0}", e.dtStart); - Debug.Console(1, this, "End Time: {0}", e.dtEnd); - Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); - } - } - } - } - - protected virtual void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); - if (dict != null) - { - // NEW PROCESS: - // Make these lists and insert the fusion attributes by iterating these - var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); - uint i = 1; - foreach (var kvp in setTopBoxes) - { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - { - break; - } - } - - var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); - i = 1; - foreach (var kvp in discPlayers) - { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - { - break; - } - } - - var laptops = dict.Where(d => d.Value.SourceDevice is Devices.Laptop); - i = 1; - foreach (var kvp in laptops) - { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 10) // We only have ten spots??? - { - break; - } - } - - foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType()) - { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true}; - usageDevice.UsageTracker.DeviceUsageEnded += - UsageTracker_DeviceUsageEnded; - } - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - Room.SourceListKey, Room.Key); - } - } - - /// - /// Collects usage data from source and sends to Fusion - /// - /// - /// - protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) - { - var deviceTracker = sender as UsageTracking; - - //var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); never used... - - if (deviceTracker == null) - { - return; - } - - var group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); - - var currentMeetingId = "-"; - - if (_currentMeeting != null) - { - currentMeetingId = _currentMeeting.MeetingID; - } - - //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]" - // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here - var deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", - e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), - @group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); - - Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", - deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); - - FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage; - - Debug.Console(1, this, "Device usage string: {0}", deviceUsage); - } - - - protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) - { - Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", - attrName, attrNum, pSrc.Key); - try - { - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); - // Need feedback when this source is selected - // Event handler, added below, will compare source changes with this sig dict - _sourceToFeedbackSigs.Add(pSrc, sigD.InputSig); - - // And respond to selection in Fusion - sigD.OutputSig.SetSigFalseAction( - () => - { - var runRouteAction = Room as IRunRouteAction; - if (runRouteAction != null) - { - runRouteAction.RunRouteAction(routeKey, Room.SourceListKey); - } - }); - } - catch (Exception) - { - Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", - attrNum, attrName, pSrc.Key); - } - } - - /// - /// - /// - private void SetUpCommunitcationMonitors() - { - uint displayNum = 0; - uint touchpanelNum = 0; - uint xpanelNum = 0; - - // Attach to all room's devices with monitors. - //foreach (var dev in DeviceManager.Devices) - foreach (var dev in DeviceManager.GetDevices()) - { - if (!(dev is ICommunicationMonitor)) - { - continue; - } - - string attrName = null; - uint attrNum = 1; - - //var keyNum = ExtractNumberFromKey(dev.Key); - //if (keyNum == -1) - //{ - // Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", - // dev.Key); - // continue; - //} - //uint attrNum = Convert.ToUInt32(keyNum); - - // Check for UI devices - var uiDev = dev as IHasBasicTriListWithSmartObject; - if (uiDev != null) - { - if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) - { - attrNum = attrNum + touchpanelNum; - - if (attrNum > 10) - { - continue; - } - attrName = "Online - XPanel " + attrNum; - attrNum += 160; - - touchpanelNum++; - } - else - { - attrNum = attrNum + xpanelNum; - - if (attrNum > 10) - { - continue; - } - attrName = "Online - Touch Panel " + attrNum; - attrNum += 150; - - xpanelNum++; - } - } - - //else - if (dev is DisplayBase) - { - attrNum = attrNum + displayNum; - if (attrNum > 10) - { - continue; - } - attrName = "Online - Display " + attrNum; - attrNum += 170; - - displayNum++; - } - //else if (dev is DvdDeviceBase) - //{ - // if (attrNum > 5) - // continue; - // attrName = "Device Ok - DVD " + attrNum; - // attrNum += 260; - //} - // add set top box - - // add Cresnet roll-up - - // add DM-devices roll-up - - if (attrName != null) - { - // Link comm status to sig and update - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); - var smd = dev as ICommunicationMonitor; - sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; - smd.CommunicationMonitor.StatusChange += - (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); - } - } - } - - protected virtual void SetUpDisplay() - { - try - { - //Setup Display Usage Monitoring - - var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); - - // Consider updating this in multiple display systems - - foreach (var display in displays.Cast()) - { - display.UsageTracker = new UsageTracking(display) {UsageIsTracked = true}; - display.UsageTracker.DeviceUsageEnded += - UsageTracker_DeviceUsageEnded; - } - - var hasDefaultDisplay = Room as IHasDefaultDisplay; - if (hasDefaultDisplay == null) - { - return; - } - - var defaultDisplay = hasDefaultDisplay.DefaultDisplay as DisplayBase; - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); - return; - } - - var dispPowerOnAction = new Action(b => - { - if (!b) - { - defaultDisplay.PowerOn(); - } - }); - var dispPowerOffAction = new Action(b => - { - if (!b) - { - defaultDisplay.PowerOff(); - } - }); - - // Display to fusion room sigs - FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); - if (defaultDisplay is IDisplayUsage) - { - (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - } - - - MapDisplayToRoomJoins(1, 158, defaultDisplay); - - - - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); - } - } - - /// - /// Maps room attributes to a display at a specified index - /// - /// - /// - /// - /// a - protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) - { - var displayName = string.Format("Display {0} - ", displayIndex); - - - var hasDefaultDisplay = Room as IHasDefaultDisplay; - if (hasDefaultDisplay == null || display != hasDefaultDisplay.DefaultDisplay) - { - return; - } - // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", - eSigIoMask.InputOutputSig); - defaultDisplayVolume.OutputSig.UserObject = - new Action(b => - { - var basicVolumeWithFeedback = display as IBasicVolumeWithFeedback; - if (basicVolumeWithFeedback != null) - { - basicVolumeWithFeedback.SetVolume(b); - } - }); - var volumeWithFeedback = display as IBasicVolumeWithFeedback; - if (volumeWithFeedback != null) - { - volumeWithFeedback.VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); - } - - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On", - eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => - { - if (!b) - { - display.PowerOn(); - } - }); - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Power Off - //var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1,displayName + "Power Off", eSigIoMask.InputOutputSig); //not used - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => - { - if (!b) - { - display.PowerOff(); - } - }); - - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, - displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => - { - if (b) - { - return; - } - var runRouteAction = Room as IRunRouteAction; - if (runRouteAction != null) - { - runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); - } - }); - - } - - private void SetUpError() - { - // Roll up ALL device errors - _errorMessageRollUp = new StatusMonitorCollection(this); - foreach (var md in DeviceManager.GetDevices().OfType()) - { - _errorMessageRollUp.AddMonitor(md.CommunicationMonitor); - Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", - md.CommunicationMonitor.Parent.Key); - } - _errorMessageRollUp.Start(); - FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; - _errorMessageRollUp.StatusChange += - (o, a) => { FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; }; - } - - /// - /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel. The occupancy status of the room will be read from Fusion - /// - private void SetUpLocalOccupancy() - { - RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); - - FusionRoom.FusionAssetStateChange += FusionRoom_FusionAssetStateChange; - - // Build Occupancy Asset? - // Link sigs? - - //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0); - } - - private void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) - { - if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || - args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId) - { - RoomIsOccupiedFeedback.FireUpdate(); - } - } - - /// - /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion - /// - private void SetUpRemoteOccupancy() - { - // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely. - // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5) - - //if (Room.OccupancyObj != null) - //{ - - var tempOccAsset = _guiDs.OccupancyAsset; - - if (tempOccAsset == null) - { - FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); - tempOccAsset = FusionOccSensor; - } - - var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, - "Occupancy Sensor", tempOccAsset.InstanceId); - - occSensorAsset.RoomOccupied.AddSigToRVIFile = true; - - //var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes",eSigIoMask.InputOutputSig); //not used - - // Tie to method on occupancy object - //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); - - - RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); - Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); - Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; - RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); - - //} - } - - private void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e) - { - _roomOccupancyRemoteString = String.Format(RemoteOccupancyXml, e.BoolValue ? "Occupied" : "Unoccupied"); - RoomOccupancyRemoteStringFeedback.FireUpdate(); - } - - /// - /// Helper to get the number from the end of a device's key string - /// - /// -1 if no number matched - private int ExtractNumberFromKey(string key) - { - var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)"); - if (!capture.Success) - { - return -1; - } - - return Convert.ToInt32(capture.Groups[1].Value); - } - - /// - /// Event handler for when room source changes - /// - protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type) - { - // Handle null. Nothing to do when switching from or to null - if (info == null || info.SourceDevice == null) - { - return; - } - - var dev = info.SourceDevice; - if (type == ChangeType.WillChange) - { - if (_sourceToFeedbackSigs.ContainsKey(dev)) - { - _sourceToFeedbackSigs[dev].BoolValue = false; - } - } - else - { - if (_sourceToFeedbackSigs.ContainsKey(dev)) - { - _sourceToFeedbackSigs[dev].BoolValue = true; - } - //var name = (room == null ? "" : room.Name); - CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; - } - } - - protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) - { - // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, - // even though they all contain sigs. - - var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); - if (sigData != null) - { - var outSig = sigData.OutputSig; - if (outSig.UserObject is Action) - { - (outSig.UserObject as Action).Invoke(outSig.BoolValue); - } - else if (outSig.UserObject is Action) - { - (outSig.UserObject as Action).Invoke(outSig.UShortValue); - } - else if (outSig.UserObject is Action) - { - (outSig.UserObject as Action).Invoke(outSig.StringValue); - } - return; - } - - var attrData = (args.UserConfiguredSigDetail as BooleanSigData); - if (attrData == null) - { - return; - } - - var aOutSig = attrData.OutputSig; - if (aOutSig.UserObject is Action) - { - (aOutSig.UserObject as Action).Invoke(aOutSig.BoolValue); - } - else if (aOutSig.UserObject is Action) - { - (aOutSig.UserObject as Action).Invoke(aOutSig.UShortValue); - } - else if (aOutSig.UserObject is Action) - { - (aOutSig.UserObject as Action).Invoke(aOutSig.StringValue); - } - } - } - - - public static class FusionRoomExtensions - { - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) - { - throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - } - number -= 49; - fr.AddSig(eSigType.Bool, number, name, mask); - return fr.UserDefinedBooleanSigDetails[number]; - } - - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) - { - throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - } - number -= 49; - fr.AddSig(eSigType.UShort, number, name, mask); - return fr.UserDefinedUShortSigDetails[number]; - } - - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) - { - throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - } - number -= 49; - fr.AddSig(eSigType.String, number, name, mask); - return fr.UserDefinedStringSigDetails[number]; - } - - /// - /// Creates and returns a static asset - /// - /// the new asset - public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, - string instanceId) - { - Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); - - fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); - return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; - } - - public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, - string type, string instanceId) - { - Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, - instanceId); - - fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId); - return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor; - } - } - - //************************************************************************************************ - /// - /// Extensions to enhance Fusion room, asset and signal creation. - /// - public static class FusionStaticAssetExtensions - { - /// - /// Tries to set a Fusion asset with the make and model of a device. - /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. - /// Otherwise, does nothing. - /// - public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) - { - var mm = device as IMakeModel; - if (mm != null) - { - asset.ParamMake.Value = mm.DeviceMake; - asset.ParamModel.Value = mm.DeviceModel; - } - } - - /// - /// Tries to attach the AssetError input on a Fusion asset to a Device's - /// CommunicationMonitor.StatusChange event. Does nothing if the device is not - /// IStatusMonitor - /// - /// - /// - public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) - { - if (device is ICommunicationMonitor) - { - var monitor = (device as ICommunicationMonitor).CommunicationMonitor; - monitor.StatusChange += (o, a) => - { - // Link connected and error inputs on asset - asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = a.Status.ToString(); - }; - // set current value - asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); - } - } - } - - public class RoomInformation - { - public RoomInformation() - { - FusionCustomProperties = new List(); - } - - public string Id { get; set; } - public string Name { get; set; } - public string Location { get; set; } - public string Description { get; set; } - public string TimeZone { get; set; } - public string WebcamUrl { get; set; } - public string BacklogMsg { get; set; } - public string SubErrorMsg { get; set; } - public string EmailInfo { get; set; } - public List FusionCustomProperties { get; set; } - } - - public class FusionCustomProperty - { - public FusionCustomProperty() - { - } - - public FusionCustomProperty(string id) - { - Id = id; - } - - public string Id { get; set; } - public string CustomFieldName { get; set; } - public string CustomFieldType { get; set; } - public string CustomFieldValue { get; set; } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.CrestronXml; +using Crestron.SimplSharp.CrestronXml.Serialization; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.Fusion; +using Newtonsoft.Json; +using PepperDash.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Rooms; + +namespace PepperDash.Essentials.Core.Fusion +{ + public abstract class EssentialsFusionSystemControllerBase : EssentialsDevice, IOccupancyStatusProvider + { + private const string RemoteOccupancyXml = "Local{0}"; + private readonly bool _guidFileExists; + + private readonly Dictionary _sourceToFeedbackSigs = + new Dictionary(); + + protected string GuidFilePath; + protected bool Initialized; + protected StringSigData CurrentRoomSourceNameSig; + + public FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); + protected FusionOccupancySensorAsset FusionOccSensor; + protected FusionRemoteOccupancySensor FusionRemoteOccSensor; + protected FusionRoom FusionRoom; + protected Dictionary FusionStaticAssets; + public long PushNotificationTimeout = 5000; + protected EssentialsRoomBase Room; + + // Default poll time is 5 min unless overridden by config value + public long SchedulePollInterval = 300000; + private Event _currentMeeting; + private RoomSchedule _currentSchedule; + private CTimer _dailyTimeRequestTimer; + private StatusMonitorCollection _errorMessageRollUp; + + private FusionRoomGuids _guiDs; + private uint _ipId; + + private bool _isRegisteredForSchedulePushNotifications; + private Event _nextMeeting; + + private CTimer _pollTimer; + + private CTimer _pushNotificationTimer; + + private string _roomOccupancyRemoteString; + + #region System Info Sigs + + //StringSigData SystemName; + //StringSigData Model; + //StringSigData SerialNumber; + //StringSigData Uptime; + + #endregion + + #region Processor Info Sigs + + private readonly StringSigData[] _program = new StringSigData[10]; + private StringSigData _dns1; + private StringSigData _dns2; + private StringSigData _domain; + private StringSigData _firmware; + private StringSigData _gateway; + private StringSigData _hostname; + private StringSigData _ip1; + private StringSigData _ip2; + private StringSigData _mac1; + private StringSigData _mac2; + private StringSigData _netMask1; + private StringSigData _netMask2; + + #endregion + + #region Default Display Source Sigs + + private BooleanSigData[] _source = new BooleanSigData[10]; + + #endregion + + protected EssentialsFusionSystemControllerBase(EssentialsRoomBase room, uint ipId) + : base(room.Key + "-fusion") + { + try + { + Room = room; + + _ipId = ipId; + + FusionStaticAssets = new Dictionary(); + + _guiDs = new FusionRoomGuids(); + + var mac = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + + var slot = Global.ControlSystem.ProgramNumber; + + GuidFilePath = Global.FilePathPrefix + + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + + _guidFileExists = File.Exists(GuidFilePath); + + // Check if file exists + if (!_guidFileExists) + { + // Does not exist. Create GUIDs + _guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac), + FusionStaticAssets); + } + else + { + // Exists. Read GUIDs + ReadGuidFile(GuidFilePath); + } + + if (Room.RoomOccupancy != null) + { + if (Room.OccupancyStatusProviderIsRemote) + { + SetUpRemoteOccupancy(); + } + else + { + SetUpLocalOccupancy(); + } + } + + CreateSymbolAndBasicSigs(_ipId); + + AddPostActivationAction(Initialize); + } + catch (Exception e) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e); + } + } + + protected string RoomGuid + { + get { return _guiDs.RoomGuid; } + } + + public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } + + protected Func RoomIsOccupiedFeedbackFunc + { + get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; } + } + + #region IOccupancyStatusProvider Members + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + #endregion + + public event EventHandler ScheduleChange; + //public event EventHandler MeetingEndWarning; + //public event EventHandler NextMeetingBeginWarning; + + public event EventHandler RoomInfoChange; + + //ScheduleResponseEvent NextMeeting; + + /// + /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files + /// + protected virtual void ExecuteCustomSteps() + { + } + + protected void Initialize() + { + + SetUpSources(); + SetUpCommunitcationMonitors(); + SetUpDisplay(); + SetUpError(); + ExecuteCustomSteps(); + + FusionRVI.GenerateFileForAllFusionDevices(); + + GenerateGuidFile(GuidFilePath); + } + + /// + /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. + /// + /// path for the file + private void GenerateGuidFile(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + Debug.Console(0, this, "Error writing guid file. No path specified."); + return; + } + + var fileLock = new CCriticalSection(); + + try + { + if (fileLock.Disposed) + { + return; + } + + fileLock.Enter(); + + Debug.Console(1, this, "Writing GUIDs to file"); + + _guiDs = FusionOccSensor == null + ? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets) + : new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets, FusionOccSensor); + + var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented); + + using (var sw = new StreamWriter(filePath)) + { + sw.Write(json); + sw.Flush(); + } + + Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); + } + catch (Exception e) + { + Debug.Console(0, this, "Error writing guid file: {0}", e); + } + finally + { + if (!fileLock.Disposed) + { + fileLock.Leave(); + } + } + } + + /// + /// Reads the guid file from NVRAM + /// + /// path for te file + private void ReadGuidFile(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified."); + return; + } + + var fileLock = new CCriticalSection(); + + try + { + if (fileLock.Disposed) + { + return; + } + + fileLock.Enter(); + + if (File.Exists(filePath)) + { + var json = File.ReadToEnd(filePath, Encoding.ASCII); + + _guiDs = JsonConvert.DeserializeObject(json); + + _ipId = _guiDs.IpId; + + FusionStaticAssets = _guiDs.StaticAssets; + } + + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", + filePath); + + Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, _ipId, RoomGuid); + + foreach (var item in FusionStaticAssets) + { + Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, + item.Value.SlotNumber, item.Value.InstanceId); + } + } + catch (Exception e) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file: {0}", e); + } + finally + { + if (!fileLock.Disposed) + { + fileLock.Leave(); + } + } + } + + protected void CreateSymbolAndBasicSigs(uint ipId) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + + if (FusionRoom != null) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Room symbol with GUID {0} already created", + RoomGuid); + return; + } + + FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); + FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); + + FusionRoom.Register(); + + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += + FusionRoomSchedule_DeviceExtenderSigChange; + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += + ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; + FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; + + CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", + "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", + "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, "FusCreateMeeting", + "Creates and Ad Hoc meeting for on hour or until the next meeting", + ConsoleAccessLevelEnum.AccessOperator); + + // Room to fusion room + Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); + + // Moved to + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", + eSigIoMask.InputSigOnly); + // Don't think we need to get current status of this as nothing should be alive yet. + var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; + if (hasCurrentSourceInfoChange != null) + { + hasCurrentSourceInfoChange.CurrentSourceChange += + Room_CurrentSourceInfoChange; + } + + + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction( + Room.PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction( + () => + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); + } + }); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + FusionRoom.ErrorMessage.InputSig.StringValue = + "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; + + SetUpEthernetValues(); + + GetProcessorEthernetValues(); + + GetSystemInfo(); + + GetProcessorInfo(); + + CrestronEnvironment.EthernetEventHandler += + CrestronEnvironment_EthernetEventHandler; + } + + protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) + { + GetProcessorEthernetValues(); + } + } + + protected void GetSystemInfo() + { + //SystemName.InputSig.StringValue = Room.Name; + //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; + //SerialNumber.InputSig.StringValue = InitialParametersClass. + + var response = string.Empty; + + var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); + systemReboot.OutputSig.SetSigFalseAction( + () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); + } + + protected void SetUpEthernetValues() + { + _ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); + } + + protected void GetProcessorEthernetValues() + { + _ip1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + _gateway.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); + _hostname.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + _domain.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); + + var dnsServers = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); + _dns1.InputSig.StringValue = dnsServers[0]; + if (dnsServers.Length > 1) + { + _dns2.InputSig.StringValue = dnsServers[1]; + } + + _mac1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + _netMask1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); + + // Interface 1 + + if (InitialParametersClass.NumberOfEthernetInterfaces > 1) + // Only get these values if the processor has more than 1 NIC + { + _ip2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); + _mac2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); + _netMask2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); + } + } + + protected void GetProcessorInfo() + { + _firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); + + if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) + { + for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) + { + var join = 62 + i; + var progNum = i + 1; + _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, + string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); + } + } + + _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; + } + + protected void GetCustomProperties() + { + if (FusionRoom.IsOnline) + { + const string fusionRoomCustomPropertiesRequest = + @"RoomConfigurationRequest"; + + FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = + fusionRoomCustomPropertiesRequest; + } + } + + private void GetTouchpanelInfo() + { + // TODO: Get IP and Project Name from TP + } + + protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) + { + if (args.DeviceOnLine) + { + CrestronEnvironment.Sleep(200); + + // Send Push Notification Action request: + + const string requestId = "InitialPushRequest"; + + + var actionRequest = + string.Format("\n{0}\n", requestId) + + "RegisterPushModel\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; + + GetCustomProperties(); + + // Request current Fusion Server Time + RequestLocalDateTime(null); + + // Setup timer to request time daily + if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) + { + _dailyTimeRequestTimer.Stop(); + _dailyTimeRequestTimer.Dispose(); + } + + _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + + _dailyTimeRequestTimer.Reset(86400000, 86400000); + } + } + + /// + /// Requests the local date and time from the Fusion Server + /// + /// + public void RequestLocalDateTime(object callbackObject) + { + const string timeRequestId = "TimeRequest"; + + var timeRequest = string.Format("{0}", + timeRequestId); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; + } + + /// + /// Generates a room schedule request for this room for the next 24 hours. + /// + public void RequestFullRoomSchedule(object callbackObject) + { + var now = DateTime.Today; + + var currentTime = now.ToString("s"); + + var requestTest = + string.Format( + "FullSchedleRequest{0}{1}24", + RoomGuid, currentTime); + + Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; + + if (_isRegisteredForSchedulePushNotifications) + { + _pushNotificationTimer.Stop(); + } + } + + /// + /// Wrapper method to allow console commands to modify the current meeting end time + /// + /// meetingID extendTime + public void ModifyMeetingEndTimeConsoleHelper(string command) + { + var extendMinutes = -1; + + const string requestId = "ModifyMeetingTest12345"; + + try + { + var tokens = command.Split(' '); + + //var meetingId = tokens[0]; never used, commenting out for now + extendMinutes = Int32.Parse(tokens[1]); + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing console command: {0}", e); + } + + ModifyMeetingEndTime(requestId, extendMinutes); + } + + /// + /// Ends or Extends the current meeting by the specified number of minutes. + /// + /// + /// Number of minutes to extend the meeting. A value of 0 will end the meeting. + public void ModifyMeetingEndTime(string requestId, int extendMinutes) + { + if (_currentMeeting == null) + { + Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); + return; + } + + if (extendMinutes > -1) + { + if (extendMinutes > 0) + { + var extendTime = _currentMeeting.dtEnd - DateTime.Now; + var extendMinutesRaw = extendTime.TotalMinutes; + + extendMinutes = extendMinutes + (int) Math.Round(extendMinutesRaw); + } + + + var requestTest = string.Format( + "{0}{1}MeetingChange" + , requestId, RoomGuid, _currentMeeting.MeetingID, extendMinutes); + + Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = requestTest; + } + else + { + Debug.Console(1, this, "Invalid time specified"); + } + } + + /// + /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour. + /// + public void CreateAdHocMeeting(string command) + { + const string requestId = "CreateAdHocMeeting"; + + var now = DateTime.Now.AddMinutes(1); + + now.AddSeconds(-now.Second); + + // Assume 1 hour meeting if possible + var dtEnd = now.AddHours(1); + + // Check if room is available for 1 hour before next meeting + if (_nextMeeting != null) + { + var roomAvailable = _nextMeeting.dtEnd.Subtract(dtEnd); + + if (roomAvailable.TotalMinutes < 60) + { + // Room not available for full hour, book until next meeting starts + dtEnd = _nextMeeting.dtEnd; + } + } + + var createMeetingRequest = + "" + + string.Format("{0}", requestId) + + string.Format("{0}", RoomGuid) + + "" + + string.Format("{0}", now.ToString("s")) + + string.Format("{0}", dtEnd.ToString("s")) + + "AdHoc Meeting" + + "Room User" + + "Example Message" + + "" + + ""; + + Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = createMeetingRequest; + + //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); + + //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; + } + + /// + /// Event handler method for Device Extender sig changes + /// + /// + /// + protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, + SigEventArgs args) + { + Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, + args.Sig.StringValue); + + + if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) + { + try + { + var message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var actionResponse = message["ActionResponse"]; + + if (actionResponse == null) + { + return; + } + var requestId = actionResponse["RequestID"]; + + if (requestId.InnerText != "InitialPushRequest") + { + return; + } + if (actionResponse["ActionID"].InnerText != "RegisterPushModel") + { + return; + } + var parameters = actionResponse["Parameters"]; + + foreach (var isRegistered in from XmlElement parameter in parameters + where parameter.HasAttributes + select parameter.Attributes + into attributes + where attributes["ID"].Value == "Registered" + select Int32.Parse(attributes["Value"].Value)) + { + switch (isRegistered) + { + case 1: + _isRegisteredForSchedulePushNotifications = true; + if (_pollTimer != null && !_pollTimer.Disposed) + { + _pollTimer.Stop(); + _pollTimer.Dispose(); + } + _pushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, + PushNotificationTimeout, PushNotificationTimeout); + _pushNotificationTimer.Reset(PushNotificationTimeout, + PushNotificationTimeout); + break; + case 0: + _isRegisteredForSchedulePushNotifications = false; + if (_pushNotificationTimer != null && !_pushNotificationTimer.Disposed) + { + _pushNotificationTimer.Stop(); + _pushNotificationTimer.Dispose(); + } + _pollTimer = new CTimer(RequestFullRoomSchedule, null, + SchedulePollInterval, SchedulePollInterval); + _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + break; + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing ActionQueryResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQueryResponse) + { + try + { + var message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var localDateTimeResponse = message["LocalTimeResponse"]; + + if (localDateTimeResponse != null) + { + var localDateTime = localDateTimeResponse["LocalDateTime"]; + + if (localDateTime != null) + { + var tempLocalDateTime = localDateTime.InnerText; + + var currentTime = DateTime.Parse(tempLocalDateTime); + + Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime); + + // Parse time and date from response and insert values + CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute, + (ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day, + (ushort) currentTime.Year); + + Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing LocalDateTimeQueryResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigResponse) + { + // Room info response with custom properties + + var roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); + + Debug.Console(2, this, "Fusion Response: \n {0}", roomConfigResponseArgs); + + try + { + var roomConfigResponse = new XmlDocument(); + + roomConfigResponse.LoadXml(roomConfigResponseArgs); + + var requestRoomConfiguration = roomConfigResponse["RoomConfigurationResponse"]; + + if (requestRoomConfiguration != null) + { + var roomInformation = new RoomInformation(); + + foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes) + { + if (e.Name == "RoomInformation") + { + var roomInfo = new XmlReader(e.OuterXml); + + roomInformation = CrestronXMLSerialization.DeSerializeObject(roomInfo); + } + else if (e.Name == "CustomFields") + { + foreach (XmlElement el in e) + { + var customProperty = new FusionCustomProperty(); + + if (el.Name == "CustomField") + { + customProperty.Id = el.Attributes["ID"].Value; + } + + foreach (XmlElement elm in el) + { + if (elm.Name == "CustomFieldName") + { + customProperty.CustomFieldName = elm.InnerText; + } + if (elm.Name == "CustomFieldType") + { + customProperty.CustomFieldType = elm.InnerText; + } + if (elm.Name == "CustomFieldValue") + { + customProperty.CustomFieldValue = elm.InnerText; + } + } + + roomInformation.FusionCustomProperties.Add(customProperty); + } + } + } + + var handler = RoomInfoChange; + if (handler != null) + { + handler(this, new EventArgs()); + } + + CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing Custom Properties response: {0}", e); + } + //PrintRoomInfo(); + //getRoomInfoBusy = false; + //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy; + } + } + + /// + /// Event handler method for Device Extender sig changes + /// + /// + /// + protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, + SigEventArgs args) + { + Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, + args.Sig.Name, args.Sig.StringValue); + + + if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) + { + try + { + var scheduleResponse = new ScheduleResponse(); + + var message = new XmlDocument(); + + message.LoadXml(args.Sig.StringValue); + + var response = message["ScheduleResponse"]; + + if (response != null) + { + // Check for push notification + if (response["RequestID"].InnerText == "RVRequest") + { + var action = response["Action"]; + + if (action.OuterXml.IndexOf("RequestSchedule", StringComparison.Ordinal) > -1) + { + _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); + } + } + else // Not a push notification + { + _currentSchedule = new RoomSchedule(); // Clear Current Schedule + _currentMeeting = null; // Clear Current Meeting + _nextMeeting = null; // Clear Next Meeting + + var isNextMeeting = false; + + foreach (XmlElement element in message.FirstChild.ChildNodes) + { + if (element.Name == "RequestID") + { + scheduleResponse.RequestID = element.InnerText; + } + else if (element.Name == "RoomID") + { + scheduleResponse.RoomID = element.InnerText; + } + else if (element.Name == "RoomName") + { + scheduleResponse.RoomName = element.InnerText; + } + else if (element.Name == "Event") + { + Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); + + var reader = new XmlReader(element.OuterXml); + + var tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); + + scheduleResponse.Events.Add(tempEvent); + + // Check is this is the current event + if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now) + { + _currentMeeting = tempEvent; // Set Current Meeting + isNextMeeting = true; // Flag that next element is next meeting + } + + if (isNextMeeting) + { + _nextMeeting = tempEvent; // Set Next Meeting + isNextMeeting = false; + } + + _currentSchedule.Meetings.Add(tempEvent); + } + } + + PrintTodaysSchedule(); + + if (!_isRegisteredForSchedulePushNotifications) + { + _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + } + + // Fire Schedule Change Event + var handler = ScheduleChange; + + if (handler != null) + { + handler(this, new ScheduleChangeEventArgs {Schedule = _currentSchedule}); + } + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) + { + Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, + args.Sig.Name, args.Sig.StringValue); + } + } + + /// + /// Prints today's schedule to console for debugging + /// + private void PrintTodaysSchedule() + { + if (Debug.Level > 1) + { + if (_currentSchedule.Meetings.Count > 0) + { + Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); + + foreach (var e in _currentSchedule.Meetings) + { + Debug.Console(1, this, "Subject: {0}", e.Subject); + Debug.Console(1, this, "Organizer: {0}", e.Organizer); + Debug.Console(1, this, "MeetingID: {0}", e.MeetingID); + Debug.Console(1, this, "Start Time: {0}", e.dtStart); + Debug.Console(1, this, "End Time: {0}", e.dtEnd); + Debug.Console(1, this, "Duration: {0}\n", e.DurationInMinutes); + } + } + } + } + + protected virtual void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); + if (dict != null) + { + // NEW PROCESS: + // Make these lists and insert the fusion attributes by iterating these + var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); + uint i = 1; + foreach (var kvp in setTopBoxes) + { + TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + { + break; + } + } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + { + break; + } + } + + var laptops = dict.Where(d => d.Value.SourceDevice is Devices.Laptop); + i = 1; + foreach (var kvp in laptops) + { + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 10) // We only have ten spots??? + { + break; + } + } + + foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType()) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true}; + usageDevice.UsageTracker.DeviceUsageEnded += + UsageTracker_DeviceUsageEnded; + } + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + Room.SourceListKey, Room.Key); + } + } + + /// + /// Collects usage data from source and sends to Fusion + /// + /// + /// + protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) + { + var deviceTracker = sender as UsageTracking; + + //var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); never used... + + if (deviceTracker == null) + { + return; + } + + var group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); + + var currentMeetingId = "-"; + + if (_currentMeeting != null) + { + currentMeetingId = _currentMeeting.MeetingID; + } + + //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]" + // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here + var deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", + e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), + @group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); + + Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", + deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); + + FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage; + + Debug.Console(1, this, "Device usage string: {0}", deviceUsage); + } + + + protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) + { + Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", + attrName, attrNum, pSrc.Key); + try + { + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); + // Need feedback when this source is selected + // Event handler, added below, will compare source changes with this sig dict + _sourceToFeedbackSigs.Add(pSrc, sigD.InputSig); + + // And respond to selection in Fusion + sigD.OutputSig.SetSigFalseAction( + () => + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction(routeKey, Room.SourceListKey); + } + }); + } + catch (Exception) + { + Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", + attrNum, attrName, pSrc.Key); + } + } + + /// + /// + /// + private void SetUpCommunitcationMonitors() + { + uint displayNum = 0; + uint touchpanelNum = 0; + uint xpanelNum = 0; + + // Attach to all room's devices with monitors. + //foreach (var dev in DeviceManager.Devices) + foreach (var dev in DeviceManager.GetDevices()) + { + if (!(dev is ICommunicationMonitor)) + { + continue; + } + + string attrName = null; + uint attrNum = 1; + + //var keyNum = ExtractNumberFromKey(dev.Key); + //if (keyNum == -1) + //{ + // Debug.Console(1, this, "WARNING: Cannot link device '{0}' to numbered Fusion monitoring attributes", + // dev.Key); + // continue; + //} + //uint attrNum = Convert.ToUInt32(keyNum); + + // Check for UI devices + var uiDev = dev as IHasBasicTriListWithSmartObject; + if (uiDev != null) + { + if (uiDev.Panel is Crestron.SimplSharpPro.UI.XpanelForSmartGraphics) + { + attrNum = attrNum + touchpanelNum; + + if (attrNum > 10) + { + continue; + } + attrName = "Online - XPanel " + attrNum; + attrNum += 160; + + touchpanelNum++; + } + else + { + attrNum = attrNum + xpanelNum; + + if (attrNum > 10) + { + continue; + } + attrName = "Online - Touch Panel " + attrNum; + attrNum += 150; + + xpanelNum++; + } + } + + //else + if (dev is DisplayBase) + { + attrNum = attrNum + displayNum; + if (attrNum > 10) + { + continue; + } + attrName = "Online - Display " + attrNum; + attrNum += 170; + + displayNum++; + } + //else if (dev is DvdDeviceBase) + //{ + // if (attrNum > 5) + // continue; + // attrName = "Device Ok - DVD " + attrNum; + // attrNum += 260; + //} + // add set top box + + // add Cresnet roll-up + + // add DM-devices roll-up + + if (attrName != null) + { + // Link comm status to sig and update + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); + var smd = dev as ICommunicationMonitor; + sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; + smd.CommunicationMonitor.StatusChange += + (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); + } + } + } + + protected virtual void SetUpDisplay() + { + try + { + SetUpDisplayUsageTracking(); + + SetUpDefaultDisplay(); + + SetUpDefaultDisplayAsset(); + + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); + } + } + + protected virtual void SetUpDisplayUsageTracking() + { + var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); + + foreach (var display in displays.Cast()) + { + display.UsageTracker = new UsageTracking(display) {UsageIsTracked = true}; + display.UsageTracker.DeviceUsageEnded += + UsageTracker_DeviceUsageEnded; + } + } + + protected virtual void SetUpDefaultDisplay() + { + var hasDefaultDisplay = Room as IHasDefaultDisplay; + if (hasDefaultDisplay == null) + { + return; + } + + var defaultDisplay = hasDefaultDisplay.DefaultDisplay as DisplayBase; + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); + return; + } + + var dispPowerOnAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOn(); + } + }); + var dispPowerOffAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOff(); + } + }); + + // Display to fusion room sigs + FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (defaultDisplay is IDisplayUsage) + { + (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + } + MapDisplayToRoomJoins(1, 158, defaultDisplay); + } + + protected virtual void SetUpDefaultDisplayAsset() + { + var defaultDisplay = Room.DefaultDisplay as DisplayBase; + + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); + return; + } + + var deviceConfig = + ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + + //Check for existing asset in GUIDs collection + + FusionAsset tempAsset; + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), + defaultDisplay.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispPowerOnAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOn(); + } + }); + var dispPowerOffAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOff(); + } + }); + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", + tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); + // Use extension methods + dispAsset.TrySetMakeModel(defaultDisplay); + dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); + } + + /// + /// Maps room attributes to a display at a specified index + /// + /// + /// + /// + /// a + protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + { + var displayName = string.Format("Display {0} - ", displayIndex); + + + var hasDefaultDisplay = Room as IHasDefaultDisplay; + if (hasDefaultDisplay == null || display != hasDefaultDisplay.DefaultDisplay) + { + return; + } + // Display volume + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", + eSigIoMask.InputOutputSig); + defaultDisplayVolume.OutputSig.UserObject = + new Action(b => + { + var basicVolumeWithFeedback = display as IBasicVolumeWithFeedback; + if (basicVolumeWithFeedback != null) + { + basicVolumeWithFeedback.SetVolume(b); + } + }); + var volumeWithFeedback = display as IBasicVolumeWithFeedback; + if (volumeWithFeedback != null) + { + volumeWithFeedback.VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); + } + + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On", + eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => + { + if (!b) + { + display.PowerOn(); + } + }); + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Power Off + //var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1,displayName + "Power Off", eSigIoMask.InputOutputSig); //not used + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => + { + if (!b) + { + display.PowerOff(); + } + }); + + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, + displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => + { + if (b) + { + return; + } + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); + } + }); + } + + private void SetUpError() + { + // Roll up ALL device errors + _errorMessageRollUp = new StatusMonitorCollection(this); + foreach (var md in DeviceManager.GetDevices().OfType()) + { + _errorMessageRollUp.AddMonitor(md.CommunicationMonitor); + Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", + md.CommunicationMonitor.Parent.Key); + } + _errorMessageRollUp.Start(); + FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; + _errorMessageRollUp.StatusChange += + (o, a) => { FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; }; + } + + /// + /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel. The occupancy status of the room will be read from Fusion + /// + private void SetUpLocalOccupancy() + { + RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); + + FusionRoom.FusionAssetStateChange += FusionRoom_FusionAssetStateChange; + + // Build Occupancy Asset? + // Link sigs? + + //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0); + } + + private void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) + { + if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || + args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId) + { + RoomIsOccupiedFeedback.FireUpdate(); + } + } + + /// + /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion + /// + private void SetUpRemoteOccupancy() + { + // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely. + // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5) + + //if (Room.OccupancyObj != null) + //{ + + var tempOccAsset = _guiDs.OccupancyAsset; + + if (tempOccAsset == null) + { + FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); + tempOccAsset = FusionOccSensor; + } + + var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, + "Occupancy Sensor", tempOccAsset.InstanceId); + + occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + + //var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes",eSigIoMask.InputOutputSig); //not used + + // Tie to method on occupancy object + //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); + + + RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); + Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); + Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; + RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); + + //} + } + + private void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e) + { + _roomOccupancyRemoteString = String.Format(RemoteOccupancyXml, e.BoolValue ? "Occupied" : "Unoccupied"); + RoomOccupancyRemoteStringFeedback.FireUpdate(); + } + + /// + /// Helper to get the number from the end of a device's key string + /// + /// -1 if no number matched + private int ExtractNumberFromKey(string key) + { + var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)"); + if (!capture.Success) + { + return -1; + } + + return Convert.ToInt32(capture.Groups[1].Value); + } + + /// + /// Event handler for when room source changes + /// + protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type) + { + // Handle null. Nothing to do when switching from or to null + if (info == null || info.SourceDevice == null) + { + return; + } + + var dev = info.SourceDevice; + if (type == ChangeType.WillChange) + { + if (_sourceToFeedbackSigs.ContainsKey(dev)) + { + _sourceToFeedbackSigs[dev].BoolValue = false; + } + } + else + { + if (_sourceToFeedbackSigs.ContainsKey(dev)) + { + _sourceToFeedbackSigs[dev].BoolValue = true; + } + //var name = (room == null ? "" : room.Name); + CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; + } + } + + protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) + { + // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, + // even though they all contain sigs. + + var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); + if (sigData != null) + { + var outSig = sigData.OutputSig; + if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.BoolValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.UShortValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.StringValue); + } + return; + } + + var attrData = (args.UserConfiguredSigDetail as BooleanSigData); + if (attrData == null) + { + return; + } + + var aOutSig = attrData.OutputSig; + if (aOutSig.UserObject is Action) + { + (aOutSig.UserObject as Action).Invoke(aOutSig.BoolValue); + } + else if (aOutSig.UserObject is Action) + { + (aOutSig.UserObject as Action).Invoke(aOutSig.UShortValue); + } + else if (aOutSig.UserObject is Action) + { + (aOutSig.UserObject as Action).Invoke(aOutSig.StringValue); + } + } + } + + + public static class FusionRoomExtensions + { + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.Bool, number, name, mask); + return fr.UserDefinedBooleanSigDetails[number]; + } + + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.UShort, number, name, mask); + return fr.UserDefinedUShortSigDetails[number]; + } + + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.String, number, name, mask); + return fr.UserDefinedStringSigDetails[number]; + } + + /// + /// Creates and returns a static asset + /// + /// the new asset + public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, + string instanceId) + { + Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); + + fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); + return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; + } + + public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, + string type, string instanceId) + { + Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, + instanceId); + + fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId); + return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor; + } + } + + //************************************************************************************************ + /// + /// Extensions to enhance Fusion room, asset and signal creation. + /// + public static class FusionStaticAssetExtensions + { + /// + /// Tries to set a Fusion asset with the make and model of a device. + /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. + /// Otherwise, does nothing. + /// + public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) + { + var mm = device as IMakeModel; + if (mm != null) + { + asset.ParamMake.Value = mm.DeviceMake; + asset.ParamModel.Value = mm.DeviceModel; + } + } + + /// + /// Tries to attach the AssetError input on a Fusion asset to a Device's + /// CommunicationMonitor.StatusChange event. Does nothing if the device is not + /// IStatusMonitor + /// + /// + /// + public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) + { + if (device is ICommunicationMonitor) + { + var monitor = (device as ICommunicationMonitor).CommunicationMonitor; + monitor.StatusChange += (o, a) => + { + // Link connected and error inputs on asset + asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = a.Status.ToString(); + }; + // set current value + asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); + } + } + } + + public class RoomInformation + { + public RoomInformation() + { + FusionCustomProperties = new List(); + } + + public string Id { get; set; } + public string Name { get; set; } + public string Location { get; set; } + public string Description { get; set; } + public string TimeZone { get; set; } + public string WebcamUrl { get; set; } + public string BacklogMsg { get; set; } + public string SubErrorMsg { get; set; } + public string EmailInfo { get; set; } + public List FusionCustomProperties { get; set; } + } + + public class FusionCustomProperty + { + public FusionCustomProperty() + { + } + + public FusionCustomProperty(string id) + { + Id = id; + } + + public string Id { get; set; } + public string CustomFieldName { get; set; } + public string CustomFieldType { get; set; } + public string CustomFieldValue { get; set; } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionController.cs index 2588b05b..300658b4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionController.cs @@ -1,19 +1,16 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Fusion; namespace PepperDash_Essentials_Core.Fusion { - public class EssentialsHuddleSpaceFusionController:EssentialsHuddleSpaceFusionSystemControllerBase + public class EssentialsHuddleSpaceFusionController:EssentialsFusionSystemControllerBase { + private EssentialsHuddleSpaceRoom _room; public EssentialsHuddleSpaceFusionController(EssentialsHuddleSpaceRoom room, uint ipId) : base(room, ipId) { + _room = room; + Initialize(); } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleVtc1FusionController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleVtc1FusionController.cs index fe16f5cb..d1dd1e49 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -6,11 +6,10 @@ using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core.Fusion { - public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase + public class EssentialsHuddleVtc1FusionController : EssentialsFusionSystemControllerBase { - private BooleanSigData _codecIsInCall; - private readonly EssentialsHuddleVtc1Room _room; + private BooleanSigData _codecIsInCall; public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) : base(room, ipId) @@ -148,67 +147,6 @@ namespace PepperDash.Essentials.Core.Fusion } } - #region Overrides of EssentialsHuddleSpaceFusionSystemControllerBase - - protected override void SetUpDisplay() - { - base.SetUpDisplay(); - - var defaultDisplay = _room.DefaultDisplay as DisplayBase; - - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); - return; - } - - var deviceConfig = - ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); - - //Check for existing asset in GUIDs collection - - FusionAsset tempAsset; - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), - defaultDisplay.Name, "Display", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var dispPowerOnAction = new Action(b => - { - if (!b) - { - defaultDisplay.PowerOn(); - } - }); - var dispPowerOffAction = new Action(b => - { - if (!b) - { - defaultDisplay.PowerOff(); - } - }); - - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", - tempAsset.InstanceId); - dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; - dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); - // Use extension methods - dispAsset.TrySetMakeModel(defaultDisplay); - dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); - } - - #endregion - private void codec_CallStatusChange(object sender, Devices.Codec.CodecCallStatusItemChangeEventArgs e) { var codec = _room.VideoCodec; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs index 50c22255..fa7688a1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Behaviours/RoomOnToDefaultSourceWhenOccupied.cs @@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Core.Rooms public EssentialsRoomBase Room { get; private set; } - private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom; + private Fusion.EssentialsFusionSystemControllerBase FusionRoom; public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) : base (config) @@ -58,7 +58,7 @@ namespace PepperDash.Essentials.Core.Rooms var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion"; - FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase; + FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.EssentialsFusionSystemControllerBase; if (FusionRoom == null) Debug.Console(1, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/EssentialsRoomBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/EssentialsRoomBase.cs index 43f60d86..91699c08 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/EssentialsRoomBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/EssentialsRoomBase.cs @@ -391,7 +391,7 @@ namespace PepperDash.Essentials.Core 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) + if (statusProvider is Fusion.EssentialsFusionSystemControllerBase) { OccupancyStatusProviderIsRemote = true; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsDualDisplayRoom.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsDualDisplayRoom.cs index 262de32f..bafdc303 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsDualDisplayRoom.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsDualDisplayRoom.cs @@ -13,11 +13,26 @@ namespace PepperDash.Essentials.Core public const string DefaultDestinationListKey = "default"; private const string LeftDestinationKey = "leftDisplay"; private const string RightDestinationKey = "rightDisplay"; + private EVideoBehavior _videoRoutingBehavior; public EssentialsDualDisplayRoomPropertiesConfig RoomConfig { get; private set; } public EAudioBehavior AudioRoutingBehavior { get; set; } - public EVideoBehavior VideoRoutingBehavior { get; set; } + + public EVideoBehavior VideoRoutingBehavior + { + get { return _videoRoutingBehavior; } + set + { + _videoRoutingBehavior = value; + var handler = VideoRoutingBehaviorChanged; + + if (handler == null) return; + handler(this, new EventArgs()); + } + } + + public event EventHandler VideoRoutingBehaviorChanged; private string _destinationListKey; @@ -153,7 +168,6 @@ namespace PepperDash.Essentials.Core throw new NotImplementedException(); } - protected override void IsWarmingUpFeedbackOnOutputChange(object sender, FeedbackEventArgs args) { IsWarmingUpFeedback.FireUpdate(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsHuddleVtc1Room.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsHuddleVtc1Room.cs index 9b7ee331..2c635901 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsHuddleVtc1Room.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Rooms/Types/EssentialsHuddleVtc1Room.cs @@ -1,7 +1,6 @@ using System; using Newtonsoft.Json; using PepperDash.Core; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Devices.AudioCodec; using PepperDash.Essentials.Core.Devices.Codec;