diff --git a/.gitignore b/.gitignore index 501c2f26..94b7d400 100644 --- a/.gitignore +++ b/.gitignore @@ -388,3 +388,4 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd +essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj diff --git a/IR Drivers/Comcast X1.ir b/IR Drivers/Comcast X1.ir index bff5afff..4f6c638e 100644 Binary files a/IR Drivers/Comcast X1.ir and b/IR Drivers/Comcast X1.ir differ diff --git a/IR Drivers/DirecTV H21.ir b/IR Drivers/DirecTV H21.ir index 612a9b2b..5744d336 100644 Binary files a/IR Drivers/DirecTV H21.ir and b/IR Drivers/DirecTV H21.ir differ diff --git a/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index 2e1b8394..ba00306d 100644 --- a/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -70,6 +70,14 @@ namespace PepperDash.Essentials.Bridges /// Range reports the highest supported HDCP state level for the corresponding input card /// public uint HdcpSupportCapability { get; set; } + /// + /// DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback + /// + public uint InputStreamCardStatus { get; set; } + /// + /// DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback + /// + public uint OutputStreamCardStatus { get; set; } #endregion #region Serials @@ -115,6 +123,8 @@ namespace PepperDash.Essentials.Bridges InputUsb = 700; //701-899 HdcpSupportState = 1000; //1001-1199 HdcpSupportCapability = 1200; //1201-1399 + InputStreamCardStatus = 1500; //1501-1532 + OutputStreamCardStatus = 1600; //1601-1632 //Serial @@ -145,6 +155,8 @@ namespace PepperDash.Essentials.Bridges OutputEndpointOnline = OutputEndpointOnline + joinOffset; HdcpSupportState = HdcpSupportState + joinOffset; HdcpSupportCapability = HdcpSupportCapability + joinOffset; + InputStreamCardStatus = InputStreamCardStatus + joinOffset; + OutputStreamCardStatus = OutputStreamCardStatus + joinOffset; OutputDisabledByHdcp = OutputDisabledByHdcp + joinOffset; TxAdvancedIsPresent = TxAdvancedIsPresent + joinOffset; } diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 95d8d169..f2a25927 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -12,6 +12,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Fusion; using PepperDash.Essentials.Devices.Common; using PepperDash.Essentials.DM; using PepperDash.Essentials.Fusion; @@ -53,7 +54,7 @@ namespace PepperDash.Essentials if (Debug.DoNotLoadOnNextBoot) { - CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file", + CrestronConsole.AddNewConsoleCommand(s => CrestronInvoke.BeginInvoke((o) => GoWithLoad()), "go", "Loads configuration file", ConsoleAccessLevelEnum.AccessOperator); } @@ -444,11 +445,30 @@ namespace PepperDash.Essentials if (room != null && room is EssentialsRoomBase) { + // default IPID + uint fusionIpId = 0xf1; + + // default to no join map key + string fusionJoinMapKey = string.Empty; + + if (room.Config.Properties["fusion"] != null) + { + Debug.Console(2, "Custom Fusion config found. Using custom values"); + + var fusionConfig = room.Config.Properties["fusion"].ToObject(); + + if (fusionConfig != null) + { + fusionIpId = fusionConfig.IpIdInt; + fusionJoinMapKey = fusionConfig.JoinMapKey; + } + } + if (room is EssentialsHuddleSpaceRoom) { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1)); + DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -459,12 +479,24 @@ namespace PepperDash.Essentials { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1)); + DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); CreateMobileControlBridge(room as EssentialsRoomBase); } + else if (room is EssentialsTechRoom) + { + DeviceManager.AddDevice(room); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); + DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey)); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); + + CreateMobileControlBridge(room); + } else { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsRoom, attempting to add to DeviceManager w/o Fusion"); @@ -569,7 +601,7 @@ namespace PepperDash.Essentials return ((logoDark != null && logoDark == "system") || (logoLight != null && logoLight == "system") || (logo != null && logo == "system")); } - catch (Exception e) + catch { Debug.Console(1, Debug.ErrorLogLevel.Notice, "Unable to find logo information in any room config: {0}", e); return false; diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 8d9da386..c4a68d4e 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -16,8 +16,8 @@ namespace PepperDash.Essentials.Fusion { BooleanSigData CodecIsInCall; - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) - : base(room, ipId) + public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId, string joinMapKey) + : base(room, ipId, joinMapKey) { } @@ -55,25 +55,25 @@ namespace PepperDash.Essentials.Fusion // Map FusionRoom Attributes: // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.AttributeName, eSigIoMask.InputOutputSig); codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, JoinMap.VcCodecInCall.AttributeName, eSigIoMask.InputSigOnly); codec.CallStatusChange += new EventHandler(codec_CallStatusChange); // Online status if (codec is ICommunicationMonitor) { var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); + var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, JoinMap.VcCodecOnline.AttributeName, eSigIoMask.InputSigOnly); codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; c.CommunicationMonitor.StatusChange += (o, a) => { codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, JoinMap.VcCodecOnline.AttributeName); } // Codec IP Address @@ -101,10 +101,10 @@ namespace PepperDash.Essentials.Fusion if (codecHasIpInfo) { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, JoinMap.VcCodecIpAddress.AttributeName, eSigIoMask.InputSigOnly); codecIpAddressSig.InputSig.StringValue = codecIpAddress; - codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, JoinMap.VcCodecIpPort.AttributeName, eSigIoMask.InputSigOnly); codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); } @@ -123,7 +123,7 @@ namespace PepperDash.Essentials.Fusion FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); } - var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Codec", tempAsset.InstanceId); codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); @@ -166,20 +166,19 @@ namespace PepperDash.Essentials.Fusion 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(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", 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); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; @@ -197,9 +196,9 @@ namespace PepperDash.Essentials.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 5) // We only have five spots + if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots break; } @@ -207,7 +206,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > 5) // We only have five spots break; @@ -217,9 +216,9 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 10) // We only have ten spots??? + if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? break; } @@ -283,7 +282,7 @@ namespace PepperDash.Essentials.Fusion if (defaultDisplay is IDisplayUsage) (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - MapDisplayToRoomJoins(1, 158, defaultDisplay); + MapDisplayToRoomJoins(1, JoinMap.Display1Start.JoinNumber, defaultDisplay); var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); @@ -328,7 +327,7 @@ namespace PepperDash.Essentials.Fusion } - protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + protected override void MapDisplayToRoomJoins(int displayIndex, uint joinOffset, DisplayBase display) { string displayName = string.Format("Display {0} - ", displayIndex); diff --git a/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs new file mode 100644 index 00000000..7e465b4d --- /dev/null +++ b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Fusion; + +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsTechRoomFusionSystemController : EssentialsHuddleSpaceFusionSystemControllerBase + { + public EssentialsTechRoomFusionSystemController(EssentialsTechRoom room, uint ipId, string joinMapKey) + : base(room, ipId, joinMapKey) + { + + } + + protected override void SetUpDisplay() + { + try + { + + var displays = (Room as EssentialsTechRoom).Displays; + + Debug.Console(1, this, "Setting up Static Assets for {0} Displays", displays.Count); + + foreach (var display in displays.Values.Cast()) + { + Debug.Console(2, this, "Setting up Static Asset for {0}", display.Key); + + display.UsageTracker = new UsageTracking(display) { UsageIsTracked = true }; + display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + + var dispPowerOnAction = new Action(b => + { + if (!b) + { + display.PowerOn(); + } + }); + var dispPowerOffAction = new Action(b => + { + if (!b) + { + display.PowerOff(); + } + }); + + var deviceConfig = ConfigReader.ConfigObject.GetDeviceForKey(display.Key); + + FusionAsset tempAsset; + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + // Used existing asset + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), + display.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", + tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + + var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; + if (defaultTwoWayDisplay != null) + { + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (display is IDisplayUsage) + { + (display as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + } + + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + } + + // Use extension methods + dispAsset.TrySetMakeModel(display); + dispAsset.TryLinkAssetErrorToCommunication(display); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up displays in Fusion: {0}", e); + } + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index febf40f9..3b373e69 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -133,6 +133,7 @@ + @@ -141,11 +142,13 @@ + + diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index dfaa333d..201b583c 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -22,30 +22,25 @@ namespace PepperDash.Essentials.Room.Config public static Device GetRoomObject(DeviceConfig roomConfig) { var typeName = roomConfig.Type.ToLower(); + if (typeName == "huddle") { - var huddle = new EssentialsHuddleSpaceRoom(roomConfig); - - return huddle; + return new EssentialsHuddleSpaceRoom(roomConfig); } - else if (typeName == "huddlevtc1") - { - var rm = new EssentialsHuddleVtc1Room(roomConfig); - - return rm; - } - else if (typeName == "ddvc01Bridge") - { - return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing. - } - else if (typeName == "dualdisplay") - { - var rm = new EssentialsDualDisplayRoom(roomConfig); + if (typeName == "huddlevtc1") + { + return new EssentialsHuddleVtc1Room(roomConfig); + } + if (typeName == "ddvc01bridge") + { + return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing. + } + if (typeName == "dualdisplay") + { + return new EssentialsDualDisplayRoom(roomConfig); + } - return rm; - } - - return null; + return typeName != "techroom" ? null : new EssentialsTechRoom(roomConfig); } /// @@ -182,6 +177,9 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("volumes")] public EssentialsRoomVolumesConfig Volumes { get; set; } + [JsonProperty("fusion")] + public EssentialsRoomFusionConfig Fusion { get; set; } + [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } @@ -225,6 +223,32 @@ namespace PepperDash.Essentials.Room.Config } + public class EssentialsRoomFusionConfig + { + public uint IpIdInt + { + get + { + try + { + return Convert.ToUInt32(IpId, 16); + } + catch (Exception) + { + throw new FormatException(string.Format("ERROR:Unable to convert IP ID: {0} to hex. Error:\n{1}", IpId)); + } + + } + } + + [JsonProperty("ipId")] + public string IpId { get; set; } + + [JsonProperty("joinMapKey")] + public string JoinMapKey { get; set; } + + } + public class EssentialsRoomMicrophonePrivacyConfig { [JsonProperty("deviceKey")] diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs new file mode 100644 index 00000000..06e67f39 --- /dev/null +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsTechRoomConfig + { + [JsonProperty("dummySourceKey")] + public string DummySourceKey { get; set; } + + [JsonProperty("displays")] + public List Displays; + + [JsonProperty("tuners")] + public List Tuners; + + [JsonProperty("userPin")] + public string UserPin; + + [JsonProperty("techPin")] + public string TechPin; + + [JsonProperty("presetsFileName")] + public string PresetsFileName; + + [JsonProperty("scheduledEvents")] + public List ScheduledEvents; + + [JsonProperty("isPrimary")] public bool IsPrimary; + + [JsonProperty("isTvPresetsProvider")] public bool IsTvPresetsProvider; + + public EssentialsTechRoomConfig() + { + Displays = new List(); + Tuners = new List(); + ScheduledEvents = new List(); + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs new file mode 100644 index 00000000..bfa56646 --- /dev/null +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -0,0 +1,473 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Scheduler; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Presets; +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials +{ + public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider, IBridgeAdvanced, IRunDirectRouteAction + { + private readonly EssentialsTechRoomConfig _config; + private readonly Dictionary _displays; + + private readonly DevicePresetsModel _tunerPresets; + private readonly Dictionary _tuners; + + private Dictionary _currentPresets; + private ScheduledEventGroup _roomScheduledEventGroup; + + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => + { + return _displays.All(kv => kv.Value.IsWarmingUpFeedback.BoolValue); + }; + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => + { + return _displays.All(kv => kv.Value.IsCoolingDownFeedback.BoolValue); + }; + } + } + + public EssentialsTechRoom(DeviceConfig config) : base(config) + { + _config = config.Properties.ToObject(); + + _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); + + _tunerPresets.SetFileName(_config.PresetsFileName); + + _tunerPresets.PresetRecalled += TunerPresetsOnPresetRecalled; + + _tuners = GetDevices(_config.Tuners); + _displays = GetDevices(_config.Displays); + + RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); + + SetUpTunerPresetsFeedback(); + + SubscribeToDisplayFeedbacks(); + + CreateOrUpdateScheduledEvents(); + } + + public Dictionary CurrentPresetsFeedbacks { get; private set; } + + public Dictionary Tuners + { + get { return _tuners; } + } + + public Dictionary Displays + { + get { return _displays; } + } + + public BoolFeedback RoomPowerIsOnFeedback { get; private set; } + + public bool RoomPowerIsOn + { + get { return _displays.All(kv => kv.Value.PowerIsOnFeedback.BoolValue); } + } + + #region ITvPresetsProvider Members + + public DevicePresetsModel TvPresets + { + get { return _tunerPresets; } + } + + #endregion + + private void TunerPresetsOnPresetRecalled(ISetTopBoxNumericKeypad device, string channel) + { + if (!_currentPresets.ContainsKey(device.Key)) + { + return; + } + + _currentPresets[device.Key] = channel; + + if (!CurrentPresetsFeedbacks.ContainsKey(device.Key)) + { + CurrentPresetsFeedbacks[device.Key].FireUpdate(); + } + } + + private void SetUpTunerPresetsFeedback() + { + _currentPresets = new Dictionary(); + CurrentPresetsFeedbacks = new Dictionary(); + + foreach (var setTopBox in _tuners) + { + var tuner = setTopBox.Value; + _currentPresets.Add(tuner.Key, String.Empty); + CurrentPresetsFeedbacks.Add(tuner.Key, new StringFeedback(() => _currentPresets[tuner.Key])); + } + } + + private void SubscribeToDisplayFeedbacks() + { + foreach (var display in _displays) + { + display.Value.PowerIsOnFeedback.OutputChange += + (sender, args) => + { + RoomPowerIsOnFeedback.InvokeFireUpdate(); + IsWarmingUpFeedback.InvokeFireUpdate(); + IsCoolingDownFeedback.InvokeFireUpdate(); + }; + } + } + + private void CreateOrUpdateScheduledEvents() + { + var eventsConfig = _config.ScheduledEvents; + + GetOrCreateScheduleGroup(); + + foreach (var eventConfig in eventsConfig) + { + CreateOrUpdateSingleEvent(eventConfig); + } + + _roomScheduledEventGroup.UserGroupCallBack += HandleScheduledEvent; + } + + private void GetOrCreateScheduleGroup() + { + if (_roomScheduledEventGroup == null) + { + _roomScheduledEventGroup = Scheduler.GetEventGroup(Key) ?? new ScheduledEventGroup(Key); + + Scheduler.AddEventGroup(_roomScheduledEventGroup); + } + + _roomScheduledEventGroup.RetrieveAllEvents(); + } + + private void CreateOrUpdateSingleEvent(ScheduledEventConfig scheduledEvent) + { + if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(scheduledEvent.Key)) + { + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup, HandleScheduledEvent); + return; + } + + var roomEvent = _roomScheduledEventGroup.ScheduledEvents[scheduledEvent.Key]; + + if (!SchedulerUtilities.CheckEventTimeForMatch(roomEvent, DateTime.Parse(scheduledEvent.Time)) && + !SchedulerUtilities.CheckEventRecurrenceForMatch(roomEvent, scheduledEvent.Days)) + { + return; + } + + Debug.Console(1, this, + "Existing event does not match new config properties. Deleting existing event '{0}' and creating new event from configuration", + roomEvent.Name); + + _roomScheduledEventGroup.DeleteEvent(roomEvent); + + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup, HandleScheduledEvent); + } + + public void AddOrUpdateScheduledEvent(ScheduledEventConfig scheduledEvent) + { + //update config based on key of scheduleEvent + GetOrCreateScheduleGroup(); + var existingEventIndex = _config.ScheduledEvents.FindIndex((e) => e.Key == scheduledEvent.Key); + + if (existingEventIndex < 0) + { + _config.ScheduledEvents.Add(scheduledEvent); + } + else + { + _config.ScheduledEvents[existingEventIndex] = scheduledEvent; + } + + //create or update event based on config + CreateOrUpdateSingleEvent(scheduledEvent); + //save config + Config.Properties = JToken.FromObject(_config); + + CustomSetConfig(Config); + //Fire Event + OnScheduledEventUpdate(); + } + + public List GetScheduledEvents() + { + return _config.ScheduledEvents ?? new List(); + } + + private void OnScheduledEventUpdate() + { + var handler = ScheduledEventsChanged; + + if (handler == null) + { + return; + } + + handler(this, new ScheduledEventEventArgs {ScheduledEvents = _config.ScheduledEvents}); + } + + public event EventHandler ScheduledEventsChanged; + + private void HandleScheduledEvent(ScheduledEvent schevent, ScheduledEventCommon.eCallbackReason type) + { + var eventConfig = _config.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); + + if (eventConfig == null) + { + Debug.Console(1, this, "Event with name {0} not found", schevent.Name); + return; + } + + Debug.Console(1, this, "Running actions for event {0}", schevent.Name); + + if (eventConfig.Acknowledgeable) + { + schevent.Acknowledge(); + } + + CrestronInvoke.BeginInvoke((o) => + { + Debug.Console(2, this, "There are {0} actions to execute for this event.", eventConfig.Actions.Count); + + foreach (var a in eventConfig.Actions) + { + Debug.Console(2, this, +@"Attempting to run action: +DeviceKey: {0} +MethodName: {1} +Params: {2}" + , a.DeviceKey, a.MethodName, a.Params); + DeviceJsonApi.DoDeviceAction(a); + } + }); + } + + + public void RoomPowerOn() + { + Debug.Console(2, this, "Room Powering On"); + + var dummySource = DeviceManager.GetDeviceForKey(_config.DummySourceKey) as IRoutingOutputs; + + if (dummySource == null) + { + Debug.Console(1, this, "Unable to get source with key: {0}", _config.DummySourceKey); + return; + } + + foreach (var display in _displays) + { + RunDirectRoute(dummySource, display.Value); + } + } + + public void RoomPowerOff() + { + Debug.Console(2, this, "Room Powering Off"); + + foreach (var display in _displays) + { + display.Value.PowerOff(); + } + } + + private Dictionary GetDevices(ICollection config) where T : IKeyed + { + try + { + var returnValue = DeviceManager.AllDevices.OfType() + .Where(d => config.Contains(d.Key)) + .ToDictionary(d => d.Key, d => d); + + return returnValue; + } + catch + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, + "Error getting devices. Check Essentials Configuration"); + return null; + } + } + + #region Overrides of EssentialsRoomBase + + protected override Func OnFeedbackFunc + { + get { return () => RoomPowerIsOn; } + } + + protected override void EndShutdown() + { + } + + public override void SetDefaultLevels() + { + } + + public override void PowerOnToDefaultOrLastSource() + { + } + + public override bool RunDefaultPresentRoute() + { + return false; + } + + public override void RoomVacatedForTimeoutPeriod(object o) + { + } + + #endregion + + #region Implementation of IBridgeAdvanced + + public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + + var joinMap = new EssentialsTechRoomJoinMap(joinStart); + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!String.IsNullOrEmpty(joinMapSerialized)) + { + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + uint i; + if (_config.IsPrimary) + { + i = 0; + foreach (var feedback in CurrentPresetsFeedbacks) + { + feedback.Value.LinkInputSig(trilist.StringInput[(uint) (joinMap.CurrentPreset.JoinNumber + i)]); + i++; + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) + { + return; + } + + foreach (var feedback in CurrentPresetsFeedbacks) + { + feedback.Value.FireUpdate(); + } + }; + + return; + } + + i = 0; + foreach (var setTopBox in _tuners) + { + var tuner = setTopBox; + + trilist.SetStringSigAction(joinMap.CurrentPreset.JoinNumber + i, s => _tunerPresets.Dial(s, tuner.Value)); + + i++; + } + } + + #endregion + + private class EssentialsTechRoomJoinMap : JoinMapBaseAdvanced + { + [JoinName("currentPreset")] + public JoinDataComplete CurrentPreset = new JoinDataComplete(new JoinData {JoinNumber = 1, JoinSpan = 16}, + new JoinMetadata {Description = "Current Tuner Preset", JoinType = eJoinType.Serial}); + + public EssentialsTechRoomJoinMap(uint joinStart) : base(joinStart, typeof(EssentialsTechRoomJoinMap)) + { + } + } + + #region IRunDirectRouteAction Members + + private void RunDirectRoute(IRoutingOutputs source, IRoutingSink dest) + { + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", dest.Key); + return; + } + + if (source == null) + { + dest.ReleaseRoute(); + if (dest is IHasPowerControl) + (dest as IHasPowerControl).PowerOff(); + } + else + { + dest.ReleaseAndMakeRoute(source, eRoutingSignalType.Video); + } + } + + /// + /// Attempts to route directly between a source and destination + /// + /// + /// + public void RunDirectRoute(string sourceKey, string destinationKey) + { + IRoutingSink dest = null; + + dest = DeviceManager.GetDeviceForKey(destinationKey) as IRoutingSink; + + var source = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs; + + if (source == null || dest == null) + { + Debug.Console(1, this, "Cannot route unknown source or destination '{0}' to {1}", sourceKey, destinationKey); + return; + } + RunDirectRoute(source, dest); + } + + #endregion + } + + public class ScheduledEventEventArgs : EventArgs + { + public List ScheduledEvents; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 151b5461..11ede5a1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -78,7 +78,7 @@ namespace PepperDash.Essentials.Core.Bridges /// /// Bridge API using EISC /// - public class EiscApiAdvanced : BridgeApi + public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor { public EiscApiPropertiesConfig PropertiesConfig { get; private set; } @@ -98,13 +98,35 @@ namespace PepperDash.Essentials.Core.Bridges Eisc.SigChange += Eisc_SigChange; + CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000); + AddPostActivationAction(LinkDevices); + AddPostActivationAction(LinkRooms); + AddPostActivationAction(RegisterEisc); + } + + public override bool CustomActivate() + { + CommunicationMonitor.Start(); + return base.CustomActivate(); + } + + public override bool Deactivate() + { + CommunicationMonitor.Stop(); + return base.Deactivate(); } private void LinkDevices() { Debug.Console(1, this, "Linking Devices..."); + if (PropertiesConfig.Devices == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "No devices linked to this bridge"); + return; + } + foreach (var d in PropertiesConfig.Devices) { var device = DeviceManager.GetDeviceForKey(d.DeviceKey); @@ -130,6 +152,14 @@ namespace PepperDash.Essentials.Core.Bridges bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this); } } + } + + private void RegisterEisc() + { + if (Eisc.Registered) + { + return; + } var registerResult = Eisc.Register(); @@ -142,6 +172,31 @@ namespace PepperDash.Essentials.Core.Bridges Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "EISC registration successful"); } + public void LinkRooms() + { + Debug.Console(1, this, "Linking Rooms..."); + + if (PropertiesConfig.Rooms == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "No rooms linked to this bridge."); + return; + } + + foreach (var room in PropertiesConfig.Rooms) + { + var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced; + + if (rm == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, + "Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey); + continue; + } + + rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this); + } + } + /// /// Adds a join map /// @@ -280,6 +335,12 @@ namespace PepperDash.Essentials.Core.Bridges Debug.Console(2, this, "Error in Eisc_SigChange handler: {0}", e); } } + + #region Implementation of ICommunicationMonitor + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #endregion } public class EiscApiPropertiesConfig @@ -290,6 +351,9 @@ namespace PepperDash.Essentials.Core.Bridges [JsonProperty("devices")] public List Devices { get; set; } + [JsonProperty("rooms")] + public List Rooms { get; set; } + public class ApiDevicePropertiesConfig { @@ -303,6 +367,18 @@ namespace PepperDash.Essentials.Core.Bridges public string JoinMapKey { get; set; } } + public class ApiRoomPropertiesConfig + { + [JsonProperty("roomKey")] + public string RoomKey { get; set; } + + [JsonProperty("joinStart")] + public uint JoinStart { get; set; } + + [JsonProperty("joinMapKey")] + public string JoinMapKey { get; set; } + } + } public class EiscApiAdvancedFactory : EssentialsDeviceFactory diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs index 070e8f61..478f4e29 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs @@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Core.Bridges /// Join this join map will start at /// Type of the child join map public AppleTvJoinMap(uint joinStart, Type type) : base(joinStart, type) - { - } + { + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index 54909d02..ee04bd45 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -76,6 +76,14 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + [JoinName("InputStreamCardState")] + public JoinDataComplete InputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1501, JoinSpan = 32 }, + new JoinMetadata { Description = "DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("OutputStreamCardState")] + public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 }, + new JoinMetadata { Description = "DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + [JoinName("InputNames")] public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs index ddb578d7..75e9ae72 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs @@ -56,6 +56,11 @@ namespace PepperDash.Essentials.Core private void RegisterAndConfigureComPort() { + if (Port == null) + { + Debug.Console(0,this,Debug.ErrorLogLevel.Error, "Configured com Port for this device does not exist."); + return; + } if (Port.Parent is CrestronControlSystem) { var result = Port.Register(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs index a8fa67e4..8a5efe47 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs @@ -104,7 +104,7 @@ namespace PepperDash.Essentials.Core var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey); if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts) return dev.ComPorts[config.ControlPortNumber]; - Debug.Console(0, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); + Debug.Console(0,Debug.ErrorLogLevel.Notice, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); return null; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs index 904bfc74..7cbaa5a1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs @@ -7,6 +7,8 @@ using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; +using Newtonsoft.Json.Linq; + namespace PepperDash.Essentials.Core.Config { /// @@ -27,7 +29,7 @@ namespace PepperDash.Essentials.Core.Config public List TieLines { get; set; } [JsonProperty("joinMaps")] - public Dictionary JoinMaps { get; set; } + public Dictionary JoinMaps { get; set; } /// /// Checks SourceLists for a given list and returns it if found. Otherwise, returns null diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs index 48a026c2..e99206e8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs @@ -1,195 +1,195 @@ -using System; +using System; using System.Linq; using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core; -using PepperDash.Core.Config; - -namespace PepperDash.Essentials.Core.Config -{ - /// - /// Loads the ConfigObject from the file - /// +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Core.Config; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Loads the ConfigObject from the file + /// public class ConfigReader { public const string LocalConfigPresent = @" *************************************************** ************* Using Local config file ************* -***************************************************"; - - public static EssentialsConfig ConfigObject { get; private set; } - - public static bool LoadConfig2() - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file."); - try - { - // Check for local config file first - var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; - - bool localConfigFound = false; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Local config file: '{0}'", filePath); - - // Check for local config directory first - - var configFiles = GetConfigFiles(filePath); - - if (configFiles != null) - { - if (configFiles.Length > 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); - return false; - } - if(configFiles.Length == 1) - { +***************************************************"; + + public static EssentialsConfig ConfigObject { get; private set; } + + public static bool LoadConfig2() + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file."); + try + { + // Check for local config file first + var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; + + bool localConfigFound = false; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Local config file: '{0}'", filePath); + + // Check for local config directory first + + var configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + if(configFiles.Length == 1) + { localConfigFound = true; - - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Local Configuration file not present.", filePath); - - } - - // Check for Portal Config - if(!localConfigFound) - { - filePath = Global.FilePathPrefix + Global.ConfigFileName; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Portal config file: '{0}'", filePath); - - configFiles = GetConfigFiles(filePath); - - if (configFiles != null) - { - Debug.Console(2, "{0} config files found matching pattern", configFiles.Length); - - if (configFiles.Length > 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****"); - return false; - } - else if (configFiles.Length == 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "No config file found."); - return false; - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "ERROR: Portal Configuration file not present. Please load file and reset program."); - return false; - } - } - - // Get the actual file path + + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Local Configuration file not present.", filePath); + + } + + // Check for Portal Config + if(!localConfigFound) + { + filePath = Global.FilePathPrefix + Global.ConfigFileName; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Portal config file: '{0}'", filePath); + + configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + Debug.Console(2, "{0} config files found matching pattern", configFiles.Length); + + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + else if (configFiles.Length == 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "No config file found."); + return false; + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "ERROR: Portal Configuration file not present. Please load file and reset program."); + return false; + } + } + + // Get the actual file path filePath = configFiles[0].FullName; // Generate debug statement if using a local file. if (localConfigFound) { GetLocalFileMessage(filePath); - } - - // Read the file - using (StreamReader fs = new StreamReader(filePath)) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading config file: '{0}'", filePath); - - if (localConfigFound) - { - ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject(); - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Local Config"); - - return true; - } - else - { - var doubleObj = JObject.Parse(fs.ReadToEnd()); - ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); - - // Extract SystemUrl and TemplateUrl into final config output - - if (doubleObj["system_url"] != null) - { - ConfigObject.SystemUrl = doubleObj["system_url"].Value(); - } - - if (doubleObj["template_url"] != null) - { - ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); - } - } - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config"); - - return true; - } - } - catch (Exception e) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e); - return false; - } - } - - /// - /// Returns all the files from the directory specified. - /// - /// - /// - public static FileInfo[] GetConfigFiles(string filePath) - { - // Get the directory - var dir = Path.GetDirectoryName(filePath); - - if (Directory.Exists(dir)) - { - Debug.Console(1, "Searching in Directory '{0}'", dir); - // Get the directory info - var dirInfo = new DirectoryInfo(dir); - - // Get the file name - var fileName = Path.GetFileName(filePath); - Debug.Console(1, "For Config Files matching: '{0}'", fileName); - - // Get the files that match from the directory - return dirInfo.GetFiles(fileName); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Directory not found: ", dir); - - return null; - } - } - - /// - /// Returns the group for a given device key in config - /// - /// - /// - public static string GetGroupForDeviceKey(string key) - { - var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); - return dev == null ? null : dev.Group; + } + + // Read the file + using (StreamReader fs = new StreamReader(filePath)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading config file: '{0}'", filePath); + + if (localConfigFound) + { + ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject(); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Local Config"); + + return true; + } + else + { + var doubleObj = JObject.Parse(fs.ReadToEnd()); + ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); + + // Extract SystemUrl and TemplateUrl into final config output + + if (doubleObj["system_url"] != null) + { + ConfigObject.SystemUrl = doubleObj["system_url"].Value(); + } + + if (doubleObj["template_url"] != null) + { + ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); + } + } + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config"); + + return true; + } + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e); + return false; + } + } + + /// + /// Returns all the files from the directory specified. + /// + /// + /// + public static FileInfo[] GetConfigFiles(string filePath) + { + // Get the directory + var dir = Path.GetDirectoryName(filePath); + + if (Directory.Exists(dir)) + { + Debug.Console(1, "Searching in Directory '{0}'", dir); + // Get the directory info + var dirInfo = new DirectoryInfo(dir); + + // Get the file name + var fileName = Path.GetFileName(filePath); + Debug.Console(1, "For Config Files matching: '{0}'", fileName); + + // Get the files that match from the directory + return dirInfo.GetFiles(fileName); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Directory not found: ", dir); + + return null; + } + } + + /// + /// Returns the group for a given device key in config + /// + /// + /// + public static string GetGroupForDeviceKey(string key) + { + var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + return dev == null ? null : dev.Group; } private static void GetLocalFileMessage(string filePath) @@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core.Config Debug.Console(2, Debug.ErrorLogLevel.Notice, "Found Local config file: '{0}'", filePath); Debug.Console(0, newDebugString.ToString()); - } - - } + } + + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs index 0294a0b5..62ea8b3f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs @@ -1,5 +1,5 @@ using Crestron.SimplSharpPro.DeviceSupport; - +using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.SmartObjects; @@ -8,7 +8,7 @@ namespace PepperDash.Essentials.Core /// /// /// - public interface INumericKeypad + public interface INumericKeypad:IKeyed { void Digit0(bool pressRelease); void Digit1(bool pressRelease); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs new file mode 100644 index 00000000..61b8ec09 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs @@ -0,0 +1,9 @@ +using PepperDash.Essentials.Core.Presets; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + public interface ITvPresetsProvider + { + DevicePresetsModel TvPresets { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs index c7bc7c68..51d5882f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs @@ -83,13 +83,13 @@ namespace PepperDash.Essentials.Core /// public static object GetPropertyByName(string deviceObjectPath, string propertyName) { - var obj = FindObjectOnPath(deviceObjectPath); - if(obj == null) + var dev = FindObjectOnPath(deviceObjectPath); + if(dev == null) return "{ \"error\":\"No Device\"}"; + + object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null); - CType t = obj.GetType(); - - var prop = t.GetProperty(propertyName); + // var prop = t.GetProperty(propertyName); if (prop != null) { return prop; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index 0e4efa10..55bc523a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -360,9 +360,9 @@ namespace PepperDash.Essentials.Core { var device = GetDeviceForKey(s); - if (device == null) return; - var inputPorts = (device as IRoutingInputsOutputs).InputPorts; - var outputPorts = (device as IRoutingInputsOutputs).OutputPorts; + if (device == null) return; + var inputPorts = ((device as IRoutingInputs) != null) ? (device as IRoutingInputs).InputPorts : null; + var outputPorts = ((device as IRoutingOutputs) != null) ? (device as IRoutingOutputs).OutputPorts : null; if (inputPorts != null) { Debug.Console(0, "Device {0} has {1} Input Ports:", s, inputPorts.Count); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs index 8d70bd55..3c691c19 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs @@ -20,7 +20,8 @@ namespace PepperDash.Essentials.Core public IrOutputPortController IrPort { get; private set; } public ushort IrPulseTime { get; set; } - public BoolFeedback PowerIsOnFeedback { get; private set; } + [Obsolete("This property will be removed in version 2.0.0")] + public override BoolFeedback PowerIsOnFeedback { get; protected set; } protected Func PowerIsOnFeedbackFunc { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs index e4a2cb26..17ff8bd1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs @@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Core /// /// /// - public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking + public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IPower { public event SourceInfoChangeHandler CurrentSourceChange; @@ -49,6 +49,9 @@ namespace PepperDash.Essentials.Core public BoolFeedback IsCoolingDownFeedback { get; protected set; } public BoolFeedback IsWarmingUpFeedback { get; private set; } + [Obsolete("This property will be removed in version 2.0.0")] + public abstract BoolFeedback PowerIsOnFeedback { get; protected set; } + public UsageTracking UsageTracker { get; set; } public uint WarmupTime { get; set; } @@ -81,8 +84,6 @@ namespace PepperDash.Essentials.Core } - - public abstract void PowerOn(); public abstract void PowerOff(); public abstract void PowerToggle(); @@ -99,7 +100,7 @@ namespace PepperDash.Essentials.Core } } - public abstract void ExecuteSwitch(object selector); + public abstract void ExecuteSwitch(object selector); protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) @@ -261,7 +262,8 @@ namespace PepperDash.Essentials.Core abstract protected Func CurrentInputFeedbackFunc { get; } - public BoolFeedback PowerIsOnFeedback { get; protected set; } + public override BoolFeedback PowerIsOnFeedback { get; protected set; } + abstract protected Func PowerIsOnFeedbackFunc { get; } @@ -315,7 +317,5 @@ namespace PepperDash.Essentials.Core var newEvent = NumericSwitchChange; if (newEvent != null) newEvent(this, e); } - - } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 39cd7d78..9574743e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -2,164 +2,133 @@ 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.SimplSharp.CrestronXmlLinq; using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.Fusion; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - using PepperDash.Core; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; - - namespace PepperDash.Essentials.Core.Fusion { - public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider - { - public event EventHandler ScheduleChange; - //public event EventHandler MeetingEndWarning; - //public event EventHandler NextMeetingBeginWarning; + public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider + { + protected EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap; - public event EventHandler RoomInfoChange; + private const string RemoteOccupancyXml = "Local{0}"; + private readonly bool _guidFileExists; + + private readonly Dictionary _sourceToFeedbackSigs = + new Dictionary(); + + protected StringSigData CurrentRoomSourceNameSig; public FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); + protected FusionOccupancySensorAsset FusionOccSensor; + protected FusionRemoteOccupancySensor FusionRemoteOccSensor; - protected FusionRoom FusionRoom; - protected EssentialsRoomBase Room; - Dictionary SourceToFeedbackSigs = - new Dictionary(); + protected FusionRoom FusionRoom; + protected Dictionary FusionStaticAssets; + public long PushNotificationTimeout = 5000; + protected EssentialsRoomBase Room; + public long SchedulePollInterval = 300000; - StatusMonitorCollection ErrorMessageRollUp; + private Event _currentMeeting; + private RoomSchedule _currentSchedule; + private CTimer _dailyTimeRequestTimer; + private StatusMonitorCollection _errorMessageRollUp; - protected StringSigData CurrentRoomSourceNameSig; + 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 - StringSigData Ip1; - StringSigData Ip2; - StringSigData Gateway; - StringSigData Hostname; - StringSigData Domain; - StringSigData Dns1; - StringSigData Dns2; - StringSigData Mac1; - StringSigData Mac2; - StringSigData NetMask1; - StringSigData NetMask2; - StringSigData Firmware; - StringSigData[] Program = new StringSigData[10]; + 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 - BooleanSigData[] Source = new BooleanSigData[10]; + private BooleanSigData[] _source = new BooleanSigData[10]; #endregion - RoomSchedule CurrentSchedule; - - Event NextMeeting; - - Event CurrentMeeting; - - protected string RoomGuid + public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId, string joinMapKey) + : base(room.Key + "-fusion") { - get - { - return GUIDs.RoomGuid; - } - - } - - uint IpId; - - FusionRoomGuids GUIDs; - - bool GuidFileExists; - - bool IsRegisteredForSchedulePushNotifications = false; - - CTimer PollTimer = null; - - CTimer PushNotificationTimer = null; - - CTimer DailyTimeRequestTimer = null; - - // Default poll time is 5 min unless overridden by config value - public long SchedulePollInterval = 300000; - - public long PushNotificationTimeout = 5000; - - private const string RemoteOccupancyXml = "Local{0}"; - - protected Dictionary FusionStaticAssets; - - // For use with local occ sensor devices which will relay to Fusion the current occupancy status - protected FusionRemoteOccupancySensor FusionRemoteOccSensor; - - // For use with occ sensor attached to a scheduling panel in Fusion - protected FusionOccupancySensorAsset FusionOccSensor; - - public BoolFeedback RoomIsOccupiedFeedback { get; private set; } - - private string _roomOccupancyRemoteString; - public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } - - protected Func RoomIsOccupiedFeedbackFunc - { - get - { - return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; - } - } - - //ScheduleResponseEvent NextMeeting; - - public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId) - : base(room.Key + "-fusion") - { - try { + JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1); + CrestronConsole.AddNewConsoleCommand((o) => JoinMap.PrintJoinMapInfo(), string.Format("ptjnmp-{0}", Key), "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator); + + if (!string.IsNullOrEmpty(joinMapKey)) + { + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + if (customJoins != null) + { + JoinMap.SetCustomJoinData(customJoins); + } + } + Room = room; - IpId = ipId; + _ipId = ipId; FusionStaticAssets = new Dictionary(); - GUIDs = new FusionRoomGuids(); + _guiDs = new FusionRoomGuids(); - var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + var mac = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); var slot = Global.ControlSystem.ProgramNumber; - string guidFilePath = Global.FilePathPrefix + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + var guidFilePath = Global.FilePathPrefix + + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); - GuidFileExists = File.Exists(guidFilePath); + _guidFileExists = File.Exists(guidFilePath); // Check if file exists - if (!GuidFileExists) + if (!_guidFileExists) { // Does not exist. Create GUIDs - GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets); + _guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac), + FusionStaticAssets); } else { @@ -170,18 +139,19 @@ namespace PepperDash.Essentials.Core.Fusion if (Room.RoomOccupancy != null) { if (Room.OccupancyStatusProviderIsRemote) + { SetUpRemoteOccupancy(); + } else { SetUpLocalOccupancy(); } } - AddPostActivationAction(() => { - CreateSymbolAndBasicSigs(IpId); + CreateSymbolAndBasicSigs(_ipId); SetUpSources(); SetUpCommunitcationMonitors(); SetUpDisplay(); @@ -192,27 +162,51 @@ namespace PepperDash.Essentials.Core.Fusion 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() { - } /// /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. /// /// path for the file - void GenerateGuidFile(string filePath) + private void GenerateGuidFile(string filePath) { if (string.IsNullOrEmpty(filePath)) { @@ -220,32 +214,32 @@ namespace PepperDash.Essentials.Core.Fusion return; } - CCriticalSection _fileLock = new CCriticalSection(); + var fileLock = new CCriticalSection(); try { - if (_fileLock == null || _fileLock.Disposed) + if (fileLock.Disposed) + { return; + } - _fileLock.Enter(); + fileLock.Enter(); Debug.Console(1, this, "Writing GUIDs to file"); - if (FusionOccSensor == null) - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets); - else - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor); + _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); + var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented); - using (StreamWriter sw = new StreamWriter(filePath)) + using (var sw = new StreamWriter(filePath)) { - sw.Write(JSON); + sw.Write(json); sw.Flush(); } Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); - } catch (Exception e) { @@ -253,8 +247,10 @@ namespace PepperDash.Essentials.Core.Fusion } finally { - if (_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); + if (!fileLock.Disposed) + { + fileLock.Leave(); + } } } @@ -262,42 +258,45 @@ namespace PepperDash.Essentials.Core.Fusion /// Reads the guid file from NVRAM /// /// path for te file - void ReadGuidFile(string filePath) + private void ReadGuidFile(string filePath) { - if(string.IsNullOrEmpty(filePath)) + if (string.IsNullOrEmpty(filePath)) { Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified."); return; } - CCriticalSection _fileLock = new CCriticalSection(); + var fileLock = new CCriticalSection(); try { - if(_fileLock == null || _fileLock.Disposed) - return; - - _fileLock.Enter(); - - if(File.Exists(filePath)) + if (fileLock.Disposed) { - var JSON = File.ReadToEnd(filePath, Encoding.ASCII); - - GUIDs = JsonConvert.DeserializeObject(JSON); - - IpId = GUIDs.IpId; - - FusionStaticAssets = GUIDs.StaticAssets; - + return; } - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath); + fileLock.Enter(); - Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid); + 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, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", 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); + 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) @@ -306,46 +305,65 @@ namespace PepperDash.Essentials.Core.Fusion } finally { - if(_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); + if (!fileLock.Disposed) + { + fileLock.Leave(); + } } - } - protected virtual void CreateSymbolAndBasicSigs(uint ipId) - { + 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.Register(); - FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange); + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange); - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange); - FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange); + 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(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + 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); + // 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. - (Room as IHasCurrentSourceInfoChange).CurrentSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, + 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 as EssentialsRoomBase).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as IRunRouteAction).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;"; + 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(); @@ -355,7 +373,7 @@ namespace PepperDash.Essentials.Core.Fusion GetProcessorInfo(); - CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; } protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) @@ -372,82 +390,107 @@ namespace PepperDash.Essentials.Core.Fusion //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; //SerialNumber.InputSig.StringValue = InitialParametersClass. - string response = string.Empty; + var response = string.Empty; - var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); - systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); + var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, JoinMap.ProcessorReboot.AttributeName, 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); + _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, JoinMap.ProcessorIp2.AttributeName, eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, JoinMap.ProcessorGateway.AttributeName, eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, JoinMap.ProcessorHostname.AttributeName, eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, JoinMap.ProcessorDomain.AttributeName, eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, JoinMap.ProcessorDns1.AttributeName, eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, JoinMap.ProcessorDns2.AttributeName, eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, JoinMap.ProcessorMac1.AttributeName, eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, JoinMap.ProcessorMac2.AttributeName, eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, JoinMap.ProcessorNetMask1.AttributeName, eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, 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); + _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]; + 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]; + { + _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); + _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 + 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); + _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); + _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) { - for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) + for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) { - var join = 62 + i; + var join = JoinMap.ProgramNameStart.JoinNumber + i; var progNum = i + 1; - Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); + _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, + string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly); } } - Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; - + _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; } protected void GetCustomProperties() { if (FusionRoom.IsOnline) { - string fusionRoomCustomPropertiesRequest = @"RoomConfigurationRequest"; + const string fusionRoomCustomPropertiesRequest = + @"RoomConfigurationRequest"; - FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = fusionRoomCustomPropertiesRequest; + FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = + fusionRoomCustomPropertiesRequest; } } - void GetTouchpanelInfo() + private void GetTouchpanelInfo() { // TODO: Get IP and Project Name from TP } @@ -456,17 +499,19 @@ namespace PepperDash.Essentials.Core.Fusion { if (args.DeviceOnLine) { - CrestronEnvironment.Sleep(200); + CrestronInvoke.BeginInvoke( (o) => + { + CrestronEnvironment.Sleep(200); - // Send Push Notification Action request: + // Send Push Notification Action request: - string requestID = "InitialPushRequest"; + const string requestId = "InitialPushRequest"; - string actionRequest = - string.Format("\n{0}\n", requestID) + - "RegisterPushModel\n" + - "\n" + + var actionRequest = + string.Format("\n{0}\n", requestId) + + "RegisterPushModel\n" + + "\n" + "\n" + "\n" + "\n" + @@ -485,30 +530,30 @@ namespace PepperDash.Essentials.Core.Fusion "\n" + "\n" + "\n" + - "\n" + - "\n"; + "\n" + + "\n"; - Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; - GetCustomProperties(); + GetCustomProperties(); - // Request current Fusion Server Time - RequestLocalDateTime(null); + // Request current Fusion Server Time + RequestLocalDateTime(null); - // Setup timer to request time daily - if (DailyTimeRequestTimer != null && !DailyTimeRequestTimer.Disposed) - { - DailyTimeRequestTimer.Stop(); - DailyTimeRequestTimer.Dispose(); - } + // Setup timer to request time daily + if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) + { + _dailyTimeRequestTimer.Stop(); + _dailyTimeRequestTimer.Dispose(); + } - DailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); - DailyTimeRequestTimer.Reset(86400000, 86400000); + _dailyTimeRequestTimer.Reset(86400000, 86400000); + }); } - } /// @@ -517,9 +562,10 @@ namespace PepperDash.Essentials.Core.Fusion /// public void RequestLocalDateTime(object callbackObject) { - string timeRequestID = "TimeRequest"; + const string timeRequestId = "TimeRequest"; - string timeRequest = string.Format("{0}", timeRequestID); + var timeRequest = string.Format("{0}", + timeRequestId); FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; } @@ -527,79 +573,78 @@ namespace PepperDash.Essentials.Core.Fusion /// /// Generates a room schedule request for this room for the next 24 hours. /// - /// string identifying this request. Used with a corresponding ScheduleResponse value public void RequestFullRoomSchedule(object callbackObject) { - DateTime now = DateTime.Today; + var now = DateTime.Today; - string currentTime = now.ToString("s"); + var currentTime = now.ToString("s"); - string requestTest = - string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime); + 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(); + if (_isRegisteredForSchedulePushNotifications) + { + _pushNotificationTimer.Stop(); + } } - + /// /// Wrapper method to allow console commands to modify the current meeting end time /// /// meetingID extendTime public void ModifyMeetingEndTimeConsoleHelper(string command) { - string requestID; - string meetingID = null; - int extendMinutes = -1; + var extendMinutes = -1; - requestID = "ModifyMeetingTest12345"; + const string requestId = "ModifyMeetingTest12345"; try { var tokens = command.Split(' '); - meetingID = tokens[0]; extendMinutes = Int32.Parse(tokens[1]); - } catch (Exception e) { Debug.Console(1, this, "Error parsing console command: {0}", e); } - ModifyMeetingEndTime(requestID, extendMinutes); - + 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) + public void ModifyMeetingEndTime(string requestId, int extendMinutes) { - if(CurrentMeeting == null) + if (_currentMeeting == null) { Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); return; - } + } if (extendMinutes > -1) { - if(extendMinutes > 0) + if (extendMinutes > 0) { - var extendTime = CurrentMeeting.dtEnd - DateTime.Now; - double extendMinutesRaw = extendTime.TotalMinutes; + var extendTime = _currentMeeting.dtEnd - DateTime.Now; + var extendMinutesRaw = extendTime.TotalMinutes; - extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw); + extendMinutes = extendMinutes + (int) Math.Round(extendMinutesRaw); } - string requestTest = string.Format( + var requestTest = string.Format( "{0}{1}MeetingChange" - , requestID, RoomGuid, CurrentMeeting.MeetingID, extendMinutes); + , requestId, RoomGuid, _currentMeeting.MeetingID, extendMinutes); Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest); @@ -609,47 +654,45 @@ namespace PepperDash.Essentials.Core.Fusion { 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 CreateAsHocMeeting(string command) + public void CreateAdHocMeeting(string command) { - string requestID = "CreateAdHocMeeting"; + const string requestId = "CreateAdHocMeeting"; - DateTime now = DateTime.Now.AddMinutes(1); + var now = DateTime.Now.AddMinutes(1); now.AddSeconds(-now.Second); // Assume 1 hour meeting if possible - DateTime dtEnd = now.AddHours(1); + var dtEnd = now.AddHours(1); // Check if room is available for 1 hour before next meeting - if (NextMeeting != null) + if (_nextMeeting != null) { - var roomAvailable = NextMeeting.dtEnd.Subtract(dtEnd); + var roomAvailable = _nextMeeting.dtEnd.Subtract(dtEnd); if (roomAvailable.TotalMinutes < 60) { - /// Room not available for full hour, book until next meeting starts - dtEnd = NextMeeting.dtEnd; + // Room not available for full hour, book until next meeting starts + dtEnd = _nextMeeting.dtEnd; } } - string createMeetingRequest = + 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" + - "" + + 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); @@ -659,7 +702,6 @@ namespace PepperDash.Essentials.Core.Fusion //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; - } /// @@ -667,73 +709,73 @@ namespace PepperDash.Essentials.Core.Fusion /// /// /// - protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + 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); + 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 { - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); message.LoadXml(args.Sig.StringValue); var actionResponse = message["ActionResponse"]; - if (actionResponse != null) + if (actionResponse == null) { - var requestID = actionResponse["RequestID"]; + return; + } - if (requestID.InnerText == "InitialPushRequest") + 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) { - if (actionResponse["ActionID"].InnerText == "RegisterPushModel") - { - var parameters = actionResponse["Parameters"]; - - foreach (XmlElement parameter in parameters) + case 1: + _isRegisteredForSchedulePushNotifications = true; + if (_pollTimer != null && !_pollTimer.Disposed) { - if (parameter.HasAttributes) - { - var attributes = parameter.Attributes; - - if (attributes["ID"].Value == "Registered") - { - var isRegistered = Int32.Parse(attributes["Value"].Value); - - if (isRegistered == 1) - { - IsRegisteredForSchedulePushNotifications = true; - - if (PollTimer != null && !PollTimer.Disposed) - { - PollTimer.Stop(); - PollTimer.Dispose(); - } - - PushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, PushNotificationTimeout, PushNotificationTimeout); - - PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); - } - else if (isRegistered == 0) - { - IsRegisteredForSchedulePushNotifications = false; - - if (PushNotificationTimer != null && !PushNotificationTimer.Disposed) - { - PushNotificationTimer.Stop(); - PushNotificationTimer.Dispose(); - } - - PollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, SchedulePollInterval); - - PollTimer.Reset(SchedulePollInterval, SchedulePollInterval); - } - } - } + _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; } } } @@ -746,7 +788,7 @@ namespace PepperDash.Essentials.Core.Fusion { try { - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); message.LoadXml(args.Sig.StringValue); @@ -759,13 +801,15 @@ namespace PepperDash.Essentials.Core.Fusion if (localDateTime != null) { var tempLocalDateTime = localDateTime.InnerText; - - DateTime currentTime = DateTime.Parse(tempLocalDateTime); + + 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); + 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()); } @@ -780,13 +824,13 @@ namespace PepperDash.Essentials.Core.Fusion { // Room info response with custom properties - string roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); + var roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); Debug.Console(2, this, "Fusion Response: \n {0}", roomConfigResponseArgs); try { - XmlDocument roomConfigResponse = new XmlDocument(); + var roomConfigResponse = new XmlDocument(); roomConfigResponse.LoadXml(roomConfigResponseArgs); @@ -794,13 +838,13 @@ namespace PepperDash.Essentials.Core.Fusion if (requestRoomConfiguration != null) { - RoomInformation roomInformation = new RoomInformation(); + var roomInformation = new RoomInformation(); foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes) { if (e.Name == "RoomInformation") { - XmlReader roomInfo = new XmlReader(e.OuterXml); + var roomInfo = new XmlReader(e.OuterXml); roomInformation = CrestronXMLSerialization.DeSerializeObject(roomInfo); } @@ -808,7 +852,7 @@ namespace PepperDash.Essentials.Core.Fusion { foreach (XmlElement el in e) { - FusionCustomProperty customProperty = new FusionCustomProperty(); + var customProperty = new FusionCustomProperty(); if (el.Name == "CustomField") { @@ -838,7 +882,9 @@ namespace PepperDash.Essentials.Core.Fusion var handler = RoomInfoChange; if (handler != null) + { handler(this, new EventArgs()); + } CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation); } @@ -851,7 +897,6 @@ namespace PepperDash.Essentials.Core.Fusion //getRoomInfoBusy = false; //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy; } - } /// @@ -859,130 +904,127 @@ namespace PepperDash.Essentials.Core.Fusion /// /// /// - protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + 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); + 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 - { - ScheduleResponse scheduleResponse = new ScheduleResponse(); + if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) + { + try + { + var scheduleResponse = new ScheduleResponse(); - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); - message.LoadXml(args.Sig.StringValue); + message.LoadXml(args.Sig.StringValue); - var response = message["ScheduleResponse"]; + var response = message["ScheduleResponse"]; - if (response != null) - { - // Check for push notification - if (response["RequestID"].InnerText == "RVRequest") - { - var action = response["Action"]; + if (response != null) + { + // Check for push notification + if (response["RequestID"].InnerText == "RVRequest") + { + var action = response["Action"]; - if (action.OuterXml.IndexOf("RequestSchedule") > -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 + 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 - bool isNextMeeting = false; + 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); + 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); - XmlReader reader = new XmlReader(element.OuterXml); + var reader = new XmlReader(element.OuterXml); - Event tempEvent = new Event(); + var tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); - tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); + scheduleResponse.Events.Add(tempEvent); - 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 + } - // 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; + } - if (isNextMeeting) - { - NextMeeting = tempEvent; // Set Next Meeting - isNextMeeting = false; - } + _currentSchedule.Meetings.Add(tempEvent); + } + } - CurrentSchedule.Meetings.Add(tempEvent); - } + PrintTodaysSchedule(); - } + if (!_isRegisteredForSchedulePushNotifications) + { + _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + } - 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); - } + // 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 /// - void PrintTodaysSchedule() + private void PrintTodaysSchedule() { if (Debug.Level > 1) { - if (CurrentSchedule.Meetings.Count > 0) + if (_currentSchedule.Meetings.Count > 0) { Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); - foreach (Event e in CurrentSchedule.Meetings) + foreach (var e in _currentSchedule.Meetings) { Debug.Console(1, this, "Subject: {0}", e.Subject); Debug.Console(1, this, "Organizer: {0}", e.Organizer); @@ -995,63 +1037,62 @@ namespace PepperDash.Essentials.Core.Fusion } } - protected virtual void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsRoomBase).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 kvp in dict) + 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) { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) + TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + break; } } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsRoomBase).SourceListKey, Room.Key); - } - } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // 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(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // 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 @@ -1059,24 +1100,31 @@ namespace PepperDash.Essentials.Core.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)); + if (deviceTracker == null) + { + return; + } - string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); + var group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); - string currentMeetingId = "-"; + var currentMeetingId = "-"; - if (CurrentMeeting != null) - currentMeetingId = CurrentMeeting.MeetingID; + 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 - string 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); + 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); + 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; @@ -1084,41 +1132,51 @@ namespace PepperDash.Essentials.Core.Fusion } - 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); + 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(() => (Room as IRunRouteAction).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); - } - } + // 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); + } + } - /// - /// - /// - void SetUpCommunitcationMonitors() - { + /// + /// + /// + 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; + // 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; @@ -1140,10 +1198,12 @@ namespace PepperDash.Essentials.Core.Fusion { attrNum = attrNum + touchpanelNum; - if (attrNum > 10) + if (attrNum > JoinMap.XpanelOnlineStart.JoinSpan) + { continue; - attrName = "Online - XPanel " + attrNum; - attrNum += 160; + } + attrName = JoinMap.XpanelOnlineStart.AttributeName + " " + attrNum; + attrNum += JoinMap.XpanelOnlineStart.JoinNumber; touchpanelNum++; } @@ -1151,54 +1211,58 @@ namespace PepperDash.Essentials.Core.Fusion { attrNum = attrNum + xpanelNum; - if (attrNum > 10) + if (attrNum > JoinMap.TouchpanelOnlineStart.JoinSpan) + { continue; - attrName = "Online - Touch Panel " + attrNum; - attrNum += 150; + } + attrName = JoinMap.TouchpanelOnlineStart.AttributeName + " " + attrNum; + attrNum += JoinMap.TouchpanelOnlineStart.JoinNumber; xpanelNum++; } } - //else - if (dev is DisplayBase) - { - attrNum = attrNum + displayNum; - if (attrNum > 10) - continue; - attrName = "Online - Display " + attrNum; - attrNum += 170; + //else + if (dev is DisplayBase) + { + attrNum = attrNum + displayNum; + if (attrNum > JoinMap.DisplayOnlineStart.JoinSpan) + { + continue; + } + attrName = JoinMap.DisplayOnlineStart.AttributeName + " " + attrNum; + attrNum += JoinMap.DisplayOnlineStart.JoinNumber; displayNum++; - } - //else if (dev is DvdDeviceBase) - //{ - // if (attrNum > 5) - // continue; - // attrName = "Device Ok - DVD " + attrNum; - // attrNum += 260; - //} - // add set top box + } + //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 Cresnet roll-up - // add DM-devices 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); - } - } - } + 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() - { + protected virtual void SetUpDisplay() + { try { //Setup Display Usage Monitoring @@ -1207,35 +1271,52 @@ namespace PepperDash.Essentials.Core.Fusion // Consider updating this in multiple display systems - foreach (DisplayBase display in displays) + foreach (var display in displays.Cast()) { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + display.UsageTracker = new UsageTracking(display) {UsageIsTracked = true}; + display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; } - var defaultDisplay = (Room as IHasDefaultDisplay).DefaultDisplay as DisplayBase; + 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(); }); + 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; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - MapDisplayToRoomJoins(1, 158, defaultDisplay); + MapDisplayToRoomJoins(1, JoinMap.Display1Start.JoinNumber, defaultDisplay); - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + var deviceConfig = + ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); //Check for existing asset in GUIDs collection - var tempAsset = new FusionAsset(); + FusionAsset tempAsset; if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) { @@ -1244,11 +1325,13 @@ namespace PepperDash.Essentials.Core.Fusion else { // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), + defaultDisplay.Name, "Display", ""); FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); } - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", + tempAsset.InstanceId); dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; @@ -1257,13 +1340,14 @@ namespace PepperDash.Essentials.Core.Fusion { defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); if (defaultDisplay is IDisplayUsage) + { (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + } defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - } - // Use extension methods + // Use extension methods dispAsset.TrySetMakeModel(defaultDisplay); dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); } @@ -1271,323 +1355,397 @@ namespace PepperDash.Essentials.Core.Fusion { 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) + /// + /// + /// + /// a + protected virtual void MapDisplayToRoomJoins(int displayIndex, uint joinOffset, DisplayBase display) { - string displayName = string.Format("Display {0} - ", displayIndex); + var displayName = string.Format("Display {0} - ", displayIndex); - if (display == (Room as IHasDefaultDisplay).DefaultDisplay) + var hasDefaultDisplay = Room as IHasDefaultDisplay; + if (hasDefaultDisplay == null || display != hasDefaultDisplay.DefaultDisplay) { - // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b)); - (display as IBasicVolumeWithFeedback).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(); }); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - - - var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; - if (defaultTwoWayDisplay != null) + return; + } + // Display volume + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.AttributeName, + eSigIoMask.InputOutputSig); + defaultDisplayVolume.OutputSig.UserObject = new Action(b => + { + var basicVolumeWithFeedback = display as IBasicVolumeWithFeedback; + if (basicVolumeWithFeedback == null) { - defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - defaultTwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(defaultDisplayPowerOff.InputSig); + return; } - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as IRunRouteAction).RunRouteAction("roomOff", Room.SourceListKey); }); ; + basicVolumeWithFeedback.SetVolume(b); + basicVolumeWithFeedback.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(); + } + }); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off", + eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => + { + if (!b) + { + display.PowerOff(); + } + }); + + + var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; + if (defaultTwoWayDisplay != null) + { + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + defaultTwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(defaultDisplayPowerOff.InputSig); } + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, + displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => + { + if (!b) + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); + } + } + }); } - void SetUpError() - { - // Roll up ALL device errors - ErrorMessageRollUp = new StatusMonitorCollection(this); - foreach (var dev in DeviceManager.GetDevices()) - { - var md = dev as ICommunicationMonitor; - if (md != null) - { - 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; - }; - - } + private void SetUpError() + { + // Roll up ALL device errors + _errorMessageRollUp = new StatusMonitorCollection(this); + foreach (var dev in DeviceManager.GetDevices()) + { + var md = dev as ICommunicationMonitor; + if (md != null) + { + _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 /// - void SetUpLocalOccupancy() + private void SetUpLocalOccupancy() { RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); - FusionRoom.FusionAssetStateChange += new FusionAssetStateEventHandler(FusionRoom_FusionAssetStateChange); + FusionRoom.FusionAssetStateChange += FusionRoom_FusionAssetStateChange; // Build Occupancy Asset? // Link sigs? //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0); - - } - void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) + private void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) { - if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId) + 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 /// - void SetUpRemoteOccupancy() - { - + 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 tempOccAsset = _guiDs.OccupancyAsset; - var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId); + if (tempOccAsset == null) + { + FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); + tempOccAsset = FusionOccSensor; + } - occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, + "Occupancy Sensor", tempOccAsset.InstanceId); - var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); - - // Tie to method on occupancy object - //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); + occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + + //var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); + + // 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); - + RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); + Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); + Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; + RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); + //} } - void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e) + 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 - int ExtractNumberFromKey(string key) - { + /// + /// 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; - else return Convert.ToInt32(capture.Groups[1].Value); - } + 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; + /// + /// 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 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; - } - } + CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; + } + } - protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) - { + 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. - // 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; - 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; - } + BoolOutputSig outSig; + if (sigData != null) + { + 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) - { - var outSig = attrData.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; + } + outSig = attrData.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); + } + } + } - 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]; - } + 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 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 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) - { + /// + /// 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; - } + 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) + 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); + 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; - } - } + //************************************************************************************************ + /// + /// 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(); - } - } - } + /// + /// 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; } @@ -1598,27 +1756,22 @@ namespace PepperDash.Essentials.Core.Fusion public string SubErrorMsg { get; set; } public string EmailInfo { get; set; } public List FusionCustomProperties { get; set; } - - public RoomInformation() - { - FusionCustomProperties = new List(); - } } + public class FusionCustomProperty { - public string ID { get; set; } - public string CustomFieldName { get; set; } - public string CustomFieldType { get; set; } - public string CustomFieldValue { get; set; } - 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/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs new file mode 100644 index 00000000..33f2fe1b --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core.Bridges; + + +namespace PepperDash.Essentials.Core.Fusion +{ + public class EssentialsHuddleSpaceRoomFusionRoomJoinMap : JoinMapBaseAdvanced + { + + // Processor Attributes + [JoinName("ProcessorIp1")] + public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Info - Processor - IP 1" }, + new JoinMetadata { Description = "Info - Processor - IP 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorIp2")] + public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = "Info - Processor - IP 2" }, + new JoinMetadata { Description = "Info - Processor - IP 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorGateway")] + public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = "Info - Processor - Gateway" }, + new JoinMetadata { Description = "Info - Processor - Gateway", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorHostname")] + public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = "Info - Processor - Hostname" }, + new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDomain")] + public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = "Info - Processor - Domain" }, + new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDns1")] + public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - DNS 1" }, + new JoinMetadata { Description = "Info - Processor - DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDns2")] + public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = "Info - Processor - DNS 2" }, + new JoinMetadata { Description = "Info - Processor - DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorMac1")] + public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = "Info - Processor - MAC 1" }, + new JoinMetadata { Description = "Info - Processor - MAC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorMac2")] + public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = "Info - Processor - MAC 2" }, + new JoinMetadata { Description = "Info - Processor - MAC 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorNetMask1")] + public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 1" }, + new JoinMetadata { Description = "Info - Processor - Net Mask 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorNetMask2")] + public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 2" }, + new JoinMetadata { Description = "Info - Processor - Net Mask 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorFirmware")] + public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = "Info - Processor - Firmware" }, + new JoinMetadata { Description = "Info - Processor - Firmware", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProgramNameStart")] + public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = "Info - Processor - Program" }, + new JoinMetadata { Description = "Info - Processor - Program", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorReboot")] + public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = "Processor - Reboot" }, + new JoinMetadata { Description = "Processor - Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); + + // Volume Controls + [JoinName("VolumeFader1")] + public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Volume - Fader01" }, + new JoinMetadata { Description = "Volume - Fader01", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); + + // Codec Info + [JoinName("VcCodecInCall")] + public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = "Conf - VC 1 In Call" }, + new JoinMetadata { Description = "Conf - VC 1 In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("VcCodecOnline")] + public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = "Online - VC 1" }, + new JoinMetadata { Description = "Online - VC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("VcCodecIpAddress")] + public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = "IP Address - VC" }, + new JoinMetadata { Description = "IP Address - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("VcCodecIpPort")] + public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = "IP Port - VC" }, + new JoinMetadata { Description = "IP Port - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + // Source Attributes + [JoinName("Display1CurrentSourceName")] + public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = "Display 1 - Current Source" }, + new JoinMetadata { Description = "Display 1 - Current Source", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + + // Device Online Status + [JoinName("TouchpanelOnlineStart")] + public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = "Online - Touch Panel" }, + new JoinMetadata { Description = "Online - Touch Panel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("XpanelOnlineStart")] + public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = "Online - XPanel" }, + new JoinMetadata { Description = "Online - XPanel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("DisplayOnlineStart")] + public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = "Online - Display" }, + new JoinMetadata { Description = "Online - Display", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1LaptopSourceStart")] + public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" }, + new JoinMetadata { Description = "Display 1 - Source Laptop", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1DiscPlayerSourceStart")] + public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" }, + new JoinMetadata { Description = "Display 1 - Source Disc Player", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1SetTopBoxSourceStart")] + public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5, AttributeName = "Display 1 - Source TV" }, + new JoinMetadata { Description = "Display 1 - Source TV", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + // Display 1 + [JoinName("Display1Start")] + public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 158, JoinSpan = 1 }, + new JoinMetadata { Description = "Display 1 Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + + /// + /// Constructor to use when instantiating this Join Map without inheriting from it + /// + /// Join this join map will start at + public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart) + : base(joinStart, typeof(EssentialsHuddleSpaceRoomFusionRoomJoinMap)) + { + + } + + /// + /// Constructor to use when extending this Join map + /// + /// Join this join map will start at + /// Type of the child join map + public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart, Type type) : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index a7b721ef..fb305172 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; using Crestron.SimplSharp.Scheduler; using PepperDash.Core; +using PepperDash.Essentials.Core.Fusion; +using PepperDash.Essentials.Room.Config; +using Activator = System.Activator; namespace PepperDash.Essentials.Core { @@ -14,7 +17,7 @@ namespace PepperDash.Essentials.Core /// public static class Scheduler { - private static Dictionary EventGroups = new Dictionary(); + private static readonly Dictionary EventGroups = new Dictionary(); static Scheduler() { @@ -49,7 +52,6 @@ namespace PepperDash.Essentials.Core /// /// Adds the event group to the global list /// - /// /// public static void AddEventGroup(ScheduledEventGroup eventGroup) { @@ -67,6 +69,13 @@ namespace PepperDash.Essentials.Core if(!EventGroups.ContainsKey(eventGroup.Name)) EventGroups.Remove(eventGroup.Name); } + + public static ScheduledEventGroup GetEventGroup(string key) + { + ScheduledEventGroup returnValue; + + return EventGroups.TryGetValue(key, out returnValue) ? returnValue : null; + } } public static class SchedulerUtilities @@ -135,5 +144,90 @@ namespace PepperDash.Essentials.Core return isMatch; } + + public static bool CheckEventTimeForMatch(ScheduledEvent evnt, DateTime time) + { + return evnt.DateAndTime.Hour == time.Hour && evnt.DateAndTime.Minute == time.Minute; + } + + public static bool CheckEventRecurrenceForMatch(ScheduledEvent evnt, ScheduledEventCommon.eWeekDays days) + { + return evnt.Recurrence.RecurrenceDays == days; + } + + public static void CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group, ScheduledEvent.UserEventCallBack handler) + { + if (group == null) + { + Debug.Console(0, "Unable to create event. Group is null"); + return; + } + var scheduledEvent = new ScheduledEvent(config.Key, group) + { + Acknowledgeable = config.Acknowledgeable, + Persistent = config.Persistent + }; + + scheduledEvent.UserCallBack += handler; + + scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday); + + var eventTime = DateTime.Parse(config.Time); + + if (DateTime.Now > eventTime) + { + eventTime = eventTime.AddDays(1); + } + + Debug.Console(2, "[Scheduler] Current Date day of week: {0} recurrence days: {1}", eventTime.DayOfWeek, + config.Days); + + var dayOfWeekConverted = ConvertDayOfWeek(eventTime); + + Debug.Console(1, "[Scheduler] eventTime Day: {0}", dayOfWeekConverted); + + while (!dayOfWeekConverted.IsFlagSet(config.Days)) + { + eventTime = eventTime.AddDays(1); + + dayOfWeekConverted = ConvertDayOfWeek(eventTime); + } + + scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime); + + scheduledEvent.Recurrence.Weekly(config.Days); + + if (config.Enable) + { + scheduledEvent.Enable(); + } + else + { + scheduledEvent.Disable(); + } + } + + private static ScheduledEventCommon.eWeekDays ConvertDayOfWeek(DateTime eventTime) + { + return (ScheduledEventCommon.eWeekDays) Enum.Parse(typeof(ScheduledEventCommon.eWeekDays), eventTime.DayOfWeek.ToString(), true); + } + + private static bool IsFlagSet(this T value, T flag) where T : struct + { + CheckIsEnum(true); + + var lValue = Convert.ToInt64(value); + var lFlag = Convert.ToInt64(flag); + + return (lValue & lFlag) != 0; + } + + private static void CheckIsEnum(bool withFlags) + { + if (!typeof(T).IsEnum) + throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName)); + if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute))) + throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName)); + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs new file mode 100644 index 00000000..92557319 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Interfaces +{ + public interface ILogStrings : IKeyed + { + /// + /// Defines a class that is capable of logging a string + /// + void SendToLog(IKeyed device, string logMessage); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs new file mode 100644 index 00000000..c43c4e6c --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash.Essentials.Core.Interfaces +{ + public interface ILogStringsWithLevel : IKeyed + { + /// + /// Defines a class that is capable of logging a string with an int level + /// + void SendToLog(IKeyed device, Debug.ErrorLogLevel level,string logMessage); + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index ad5df2e3..b9825749 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Core var joinMap = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; - return joinMap; + return joinMap.ToString(); } /// @@ -45,16 +45,33 @@ namespace PepperDash.Essentials.Core /// public static Dictionary TryGetJoinMapAdvancedForDevice(string joinMapKey) { - if (string.IsNullOrEmpty(joinMapKey)) + try + { + if (string.IsNullOrEmpty(joinMapKey)) + return null; + + if (!ConfigReader.ConfigObject.JoinMaps.ContainsKey(joinMapKey)) + { + Debug.Console(2, "No Join Map found in config with key: '{0}'", joinMapKey); + return null; + } + + Debug.Console(2, "Attempting to load custom join map with key: {0}", joinMapKey); + + var joinMapJToken = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; + + if (joinMapJToken == null) + return null; + + var joinMapData = joinMapJToken.ToObject>(); + + return joinMapData; + } + catch (Exception e) + { + Debug.Console(2, "Error getting join map for key: '{0}'. Error: {1}", joinMapKey, e); return null; - - var joinMapSerialzed = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; - - if (joinMapSerialzed == null) return null; - - var joinMapData = JsonConvert.DeserializeObject>(joinMapSerialzed); - - return joinMapData; + } } } @@ -266,7 +283,7 @@ namespace PepperDash.Essentials.Core @"Join Number: {0} | JoinSpan: '{1}' | Description: '{2}' | Type: '{3}' | Capabilities: '{4}'", join.Value.JoinNumber, join.Value.JoinSpan, - String.IsNullOrEmpty(join.Value.Metadata.Description) ? join.Value.Metadata.Label: join.Value.Metadata.Description, + String.IsNullOrEmpty(join.Value.AttributeName) ? join.Value.Metadata.Label : join.Value.AttributeName, join.Value.Metadata.JoinType.ToString(), join.Value.Metadata.JoinCapabilities.ToString()); } @@ -288,7 +305,7 @@ namespace PepperDash.Essentials.Core } else { - Debug.Console(2, "No mathcing key found in join map for: '{0}'", customJoinData.Key); + Debug.Console(2, "No matching key found in join map for: '{0}'", customJoinData.Key); } } @@ -327,7 +344,10 @@ namespace PepperDash.Essentials.Core None = 0, ToSIMPL = 1, FromSIMPL = 2, - ToFromSIMPL = ToSIMPL | FromSIMPL + ToFromSIMPL = ToSIMPL | FromSIMPL, + ToFusion = 4, + FromFusion = 8, + ToFromFusion = ToFusion | FromFusion, } [Flags] @@ -340,7 +360,7 @@ namespace PepperDash.Essentials.Core DigitalAnalog = Digital | Analog, DigitalSerial = Digital | Serial, AnalogSerial = Analog | Serial, - DigitalAnalogSerial = Digital | Analog | Serial + DigitalAnalogSerial = Digital | Analog | Serial, } /// @@ -394,7 +414,7 @@ namespace PepperDash.Essentials.Core } /// - /// Data describing the join. Can be + /// Data describing the join. Can be overridden from configuratino /// public class JoinData { @@ -408,6 +428,11 @@ namespace PepperDash.Essentials.Core /// [JsonProperty("joinSpan")] public uint JoinSpan { get; set; } + /// + /// Fusion Attribute Name (optional) + /// + [JsonProperty("attributeName")] + public string AttributeName { get; set; } } /// @@ -419,6 +444,10 @@ namespace PepperDash.Essentials.Core private JoinData _data; public JoinMetadata Metadata { get; set; } + /// + /// To store some future information as you please + /// + public object UserObject { get; private set; } public JoinDataComplete(JoinData data, JoinMetadata metadata) { @@ -449,6 +478,11 @@ namespace PepperDash.Essentials.Core get { return _data.JoinSpan; } } + public string AttributeName + { + get { return _data.AttributeName; } + } + public void SetCustomJoinData(JoinData customJoinData) { _data = customJoinData; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs index d9b5b33a..7f985fe0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs @@ -64,30 +64,34 @@ namespace PepperDash.Essentials.Core initialStatus = MonitorStatus.InWarning; prefix = "2:"; } - else if (InWarning.Count() > 0) + else if (IsOk.Count() > 0) initialStatus = MonitorStatus.IsOk; else initialStatus = MonitorStatus.StatusUnknown; // Build the error message string - if (InError.Count() > 0 || InWarning.Count() > 0) - { - StringBuilder sb = new StringBuilder(prefix); - if (InError.Count() > 0) - { - // Do string splits and joins - sb.Append(string.Format("{0} Errors:", InError.Count())); - foreach (var mon in InError) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - if (InWarning.Count() > 0) - { - sb.Append(string.Format("{0} Warnings:", InWarning.Count())); - foreach (var mon in InWarning) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - Message = sb.ToString(); - } + if (InError.Count() > 0 || InWarning.Count() > 0) + { + StringBuilder sb = new StringBuilder(prefix); + if (InError.Count() > 0) + { + // Do string splits and joins + sb.Append(string.Format("{0} Errors:", InError.Count())); + foreach (var mon in InError) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + if (InWarning.Count() > 0) + { + sb.Append(string.Format("{0} Warnings:", InWarning.Count())); + foreach (var mon in InWarning) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + Message = sb.ToString(); + } + else + { + Message = "Room Ok."; + } // Want to fire even if status doesn't change because the message may. Status = initialStatus; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs index eadaab12..ec18c995 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs @@ -1,106 +1,106 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.Diagnostics; -using PepperDash.Core; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.Core.Monitoring -{ - /// - /// Wrapper for the static SystemMonitor class to extend functionality and provide external access - /// to SystemMonitor via APIs - /// - public class SystemMonitorController : EssentialsBridgeableDevice - { - private const long UptimePollTime = 300000; - private CTimer _uptimePollTimer; - - private string _uptime; - private string _lastStart; - - public event EventHandler SystemMonitorPropertiesChanged; - - public Dictionary ProgramStatusFeedbackCollection; - public Dictionary EthernetStatusFeedbackCollection; - - public IntFeedback TimeZoneFeedback { get; protected set; } - public StringFeedback TimeZoneTextFeedback { get; protected set; } - - public StringFeedback IoControllerVersionFeedback { get; protected set; } - public StringFeedback SnmpVersionFeedback { get; protected set; } - public StringFeedback BaCnetAppVersionFeedback { get; protected set; } - public StringFeedback ControllerVersionFeedback { get; protected set; } - - //new feedbacks. Issue #50 - public StringFeedback SerialNumberFeedback { get; protected set; } - public StringFeedback ModelFeedback { get; set; } - - public StringFeedback UptimeFeedback { get; set; } - public StringFeedback LastStartFeedback { get; set; } - - public SystemMonitorController(string key) - : base(key) - { - Debug.Console(2, this, "Adding SystemMonitorController."); - - SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; - - TimeZoneFeedback = new IntFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneNumber); - TimeZoneTextFeedback = new StringFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneName); - - IoControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.IOPVersion); - SnmpVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.SNMPVersion); - BaCnetAppVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.BACNetVersion); - ControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.ControlSystemVersion); - - SerialNumberFeedback = new StringFeedback(() => CrestronEnvironment.SystemInfo.SerialNumber); - ModelFeedback = new StringFeedback(() => InitialParametersClass.ControllerPromptName); - UptimeFeedback = new StringFeedback(() => _uptime); - LastStartFeedback = new StringFeedback(()=> _lastStart); - - ProgramStatusFeedbackCollection = new Dictionary(); - - foreach (var prog in SystemMonitor.ProgramCollection) - { - var program = new ProgramStatusFeedbacks(prog); - ProgramStatusFeedbackCollection.Add(prog.Number, program); - } - - CreateEthernetStatusFeedbacks(); - UpdateEthernetStatusFeeedbacks(); - - _uptimePollTimer = new CTimer(PollUptime,null,0, UptimePollTime); - - SystemMonitor.ProgramChange += SystemMonitor_ProgramChange; - SystemMonitor.TimeZoneInformation.TimeZoneChange += TimeZoneInformation_TimeZoneChange; - CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler; - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler; - } - - private void CrestronEnvironmentOnProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType != eProgramStatusEventType.Stopping) return; - - _uptimePollTimer.Stop(); - _uptimePollTimer.Dispose(); - _uptimePollTimer = null; - } - - private void PollUptime(object obj) - { - var consoleResponse = string.Empty; - - CrestronConsole.SendControlSystemCommand("uptime", ref consoleResponse); - - ParseUptime(consoleResponse); - - UptimeFeedback.FireUpdate(); - LastStartFeedback.FireUpdate(); +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.Diagnostics; +using PepperDash.Core; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.Core.Monitoring +{ + /// + /// Wrapper for the static SystemMonitor class to extend functionality and provide external access + /// to SystemMonitor via APIs + /// + public class SystemMonitorController : EssentialsBridgeableDevice + { + private const long UptimePollTime = 300000; + private CTimer _uptimePollTimer; + + private string _uptime; + private string _lastStart; + + public event EventHandler SystemMonitorPropertiesChanged; + + public Dictionary ProgramStatusFeedbackCollection; + public Dictionary EthernetStatusFeedbackCollection; + + public IntFeedback TimeZoneFeedback { get; protected set; } + public StringFeedback TimeZoneTextFeedback { get; protected set; } + + public StringFeedback IoControllerVersionFeedback { get; protected set; } + public StringFeedback SnmpVersionFeedback { get; protected set; } + public StringFeedback BaCnetAppVersionFeedback { get; protected set; } + public StringFeedback ControllerVersionFeedback { get; protected set; } + + //new feedbacks. Issue #50 + public StringFeedback SerialNumberFeedback { get; protected set; } + public StringFeedback ModelFeedback { get; set; } + + public StringFeedback UptimeFeedback { get; set; } + public StringFeedback LastStartFeedback { get; set; } + + public SystemMonitorController(string key) + : base(key) + { + Debug.Console(2, this, "Adding SystemMonitorController."); + + SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; + + TimeZoneFeedback = new IntFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneNumber); + TimeZoneTextFeedback = new StringFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneName); + + IoControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.IOPVersion); + SnmpVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.SNMPVersion); + BaCnetAppVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.BACNetVersion); + ControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.ControlSystemVersion); + + SerialNumberFeedback = new StringFeedback(() => CrestronEnvironment.SystemInfo.SerialNumber); + ModelFeedback = new StringFeedback(() => InitialParametersClass.ControllerPromptName); + UptimeFeedback = new StringFeedback(() => _uptime); + LastStartFeedback = new StringFeedback(()=> _lastStart); + + ProgramStatusFeedbackCollection = new Dictionary(); + + foreach (var prog in SystemMonitor.ProgramCollection) + { + var program = new ProgramStatusFeedbacks(prog); + ProgramStatusFeedbackCollection.Add(prog.Number, program); + } + + CreateEthernetStatusFeedbacks(); + UpdateEthernetStatusFeeedbacks(); + + _uptimePollTimer = new CTimer(PollUptime,null,0, UptimePollTime); + + SystemMonitor.ProgramChange += SystemMonitor_ProgramChange; + SystemMonitor.TimeZoneInformation.TimeZoneChange += TimeZoneInformation_TimeZoneChange; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler; + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler; + } + + private void CrestronEnvironmentOnProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) return; + + _uptimePollTimer.Stop(); + _uptimePollTimer.Dispose(); + _uptimePollTimer = null; + } + + private void PollUptime(object obj) + { + var consoleResponse = string.Empty; + + CrestronConsole.SendControlSystemCommand("uptime", ref consoleResponse); + + ParseUptime(consoleResponse); + + UptimeFeedback.FireUpdate(); + LastStartFeedback.FireUpdate(); } private void ParseUptime(string response) @@ -123,94 +123,94 @@ namespace PepperDash.Essentials.Core.Monitoring _uptime = uptimeRaw.Substring(forIndex + 4); } - private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return; - - foreach (var fb in EthernetStatusFeedbackCollection) - { - fb.Value.UpdateEthernetStatus(); - } - } - - private void CreateEthernetStatusFeedbacks() - { - EthernetStatusFeedbackCollection = new Dictionary(); - - Debug.Console(2, "Creating {0} EthernetStatusFeedbacks", InitialParametersClass.NumberOfEthernetInterfaces); - - for (short i = 0; i < InitialParametersClass.NumberOfEthernetInterfaces; i++) - { - Debug.Console(2, "Creating EthernetStatusFeedback for Interface {0}", i); - var ethernetInterface = new EthernetStatusFeedbacks(i); - EthernetStatusFeedbackCollection.Add(i, ethernetInterface); - } - } - - private void UpdateEthernetStatusFeeedbacks() - { - foreach (var iface in EthernetStatusFeedbackCollection) - { - iface.Value.CurrentIpAddressFeedback.FireUpdate(); - iface.Value.CurrentSubnetMaskFeedback.FireUpdate(); - iface.Value.CurrentDefaultGatewayFeedback.FireUpdate(); - iface.Value.StaticIpAddressFeedback.FireUpdate(); - iface.Value.StaticSubnetMaskFeedback.FireUpdate(); - iface.Value.StaticDefaultGatewayFeedback.FireUpdate(); - iface.Value.HostNameFeedback.FireUpdate(); - iface.Value.DnsServerFeedback.FireUpdate(); - iface.Value.DomainFeedback.FireUpdate(); - iface.Value.DhcpStatusFeedback.FireUpdate(); - iface.Value.MacAddressFeedback.FireUpdate(); - } - } - - /// - /// Gets data in separate thread - /// - private void RefreshSystemMonitorData() - { - // this takes a while, launch a new thread - CrestronInvoke.BeginInvoke(UpdateFeedback); - } - - private void UpdateFeedback(object o) - { - TimeZoneFeedback.FireUpdate(); - TimeZoneTextFeedback.FireUpdate(); - IoControllerVersionFeedback.FireUpdate(); - SnmpVersionFeedback.FireUpdate(); - BaCnetAppVersionFeedback.FireUpdate(); - ControllerVersionFeedback.FireUpdate(); - SerialNumberFeedback.FireUpdate(); - ModelFeedback.FireUpdate(); - - OnSystemMonitorPropertiesChanged(); - } - - private void OnSystemMonitorPropertiesChanged() - { - var handler = SystemMonitorPropertiesChanged; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - - public override bool CustomActivate() - { - RefreshSystemMonitorData(); - - return base.CustomActivate(); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs) { - var joinMap = new SystemMonitorJoinMap(joinStart); - - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - - if (!string.IsNullOrEmpty(joinMapSerialized)) + if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return; + + foreach (var fb in EthernetStatusFeedbackCollection) + { + fb.Value.UpdateEthernetStatus(); + } + } + + private void CreateEthernetStatusFeedbacks() + { + EthernetStatusFeedbackCollection = new Dictionary(); + + Debug.Console(2, "Creating {0} EthernetStatusFeedbacks", InitialParametersClass.NumberOfEthernetInterfaces); + + for (short i = 0; i < InitialParametersClass.NumberOfEthernetInterfaces; i++) + { + Debug.Console(2, "Creating EthernetStatusFeedback for Interface {0}", i); + var ethernetInterface = new EthernetStatusFeedbacks(i); + EthernetStatusFeedbackCollection.Add(i, ethernetInterface); + } + } + + private void UpdateEthernetStatusFeeedbacks() + { + foreach (var iface in EthernetStatusFeedbackCollection) + { + iface.Value.CurrentIpAddressFeedback.FireUpdate(); + iface.Value.CurrentSubnetMaskFeedback.FireUpdate(); + iface.Value.CurrentDefaultGatewayFeedback.FireUpdate(); + iface.Value.StaticIpAddressFeedback.FireUpdate(); + iface.Value.StaticSubnetMaskFeedback.FireUpdate(); + iface.Value.StaticDefaultGatewayFeedback.FireUpdate(); + iface.Value.HostNameFeedback.FireUpdate(); + iface.Value.DnsServerFeedback.FireUpdate(); + iface.Value.DomainFeedback.FireUpdate(); + iface.Value.DhcpStatusFeedback.FireUpdate(); + iface.Value.MacAddressFeedback.FireUpdate(); + } + } + + /// + /// Gets data in separate thread + /// + private void RefreshSystemMonitorData() + { + // this takes a while, launch a new thread + CrestronInvoke.BeginInvoke(UpdateFeedback); + } + + private void UpdateFeedback(object o) + { + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + IoControllerVersionFeedback.FireUpdate(); + SnmpVersionFeedback.FireUpdate(); + BaCnetAppVersionFeedback.FireUpdate(); + ControllerVersionFeedback.FireUpdate(); + SerialNumberFeedback.FireUpdate(); + ModelFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + + private void OnSystemMonitorPropertiesChanged() + { + var handler = SystemMonitorPropertiesChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + public override bool CustomActivate() + { + RefreshSystemMonitorData(); + + return base.CustomActivate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new SystemMonitorJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) joinMap = JsonConvert.DeserializeObject(joinMapSerialized); if (bridge != null) @@ -220,11 +220,11 @@ namespace PepperDash.Essentials.Core.Monitoring else { Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(2, this, "Linking API starting at join: {0}", joinStart); - + } + + Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(2, this, "Linking API starting at join: {0}", joinStart); + TimeZoneFeedback.LinkInputSig(trilist.UShortInput[joinMap.TimeZone.JoinNumber]); TimeZoneTextFeedback.LinkInputSig(trilist.StringInput[joinMap.TimeZoneName.JoinNumber]); @@ -235,18 +235,18 @@ namespace PepperDash.Essentials.Core.Monitoring SerialNumberFeedback.LinkInputSig(trilist.StringInput[joinMap.SerialNumber.JoinNumber]); ModelFeedback.LinkInputSig(trilist.StringInput[joinMap.Model.JoinNumber]); UptimeFeedback.LinkInputSig(trilist.StringInput[joinMap.Uptime.JoinNumber]); - LastStartFeedback.LinkInputSig(trilist.StringInput[joinMap.LastBoot.JoinNumber]); - - // iterate the program status feedback collection and map all the joins - LinkProgramInfoJoins(this, trilist, joinMap); - - LinkEthernetInfoJoins(this, trilist, joinMap); - } - - private static void LinkEthernetInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, SystemMonitorJoinMap joinMap) + LastStartFeedback.LinkInputSig(trilist.StringInput[joinMap.LastBoot.JoinNumber]); + + // iterate the program status feedback collection and map all the joins + LinkProgramInfoJoins(this, trilist, joinMap); + + LinkEthernetInfoJoins(this, trilist, joinMap); + } + + private static void LinkEthernetInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, SystemMonitorJoinMap joinMap) { - uint ethernetSlotJoinStart = 0; - foreach (var fb in systemMonitorController.EthernetStatusFeedbackCollection) + uint ethernetSlotJoinStart = 0; + foreach (var fb in systemMonitorController.EthernetStatusFeedbackCollection) { fb.Value.CurrentIpAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentIpAddress.JoinNumber]); fb.Value.CurrentSubnetMaskFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentSubnetMask.JoinNumber]); @@ -258,494 +258,500 @@ namespace PepperDash.Essentials.Core.Monitoring fb.Value.MacAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.MacAddress.JoinNumber]); fb.Value.DomainFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.Domain.JoinNumber]); fb.Value.DnsServerFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DnsServer.JoinNumber]); - fb.Value.DhcpStatusFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DhcpStatus.JoinNumber]); - - ethernetSlotJoinStart += joinMap.EthernetOffsetJoin.JoinNumber; - } - } - - private static void LinkProgramInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, - SystemMonitorJoinMap joinMap) + fb.Value.DhcpStatusFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DhcpStatus.JoinNumber]); + + ethernetSlotJoinStart += joinMap.EthernetOffsetJoin.JoinNumber; + } + } + + private static void LinkProgramInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, + SystemMonitorJoinMap joinMap) { - uint programSlotJoinStart = 0; - - foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection) - { + uint programSlotJoinStart = 0; + + foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection) + { var programNumber = p.Value.Program.Number; - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart.JoinNumber, + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart.JoinNumber, b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Start); p.Value.ProgramStartedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStart.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop.JoinNumber, + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop.JoinNumber, b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Stop); p.Value.ProgramStoppedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStop.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister.JoinNumber, - b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register); + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister.JoinNumber, + b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register); p.Value.ProgramRegisteredFeedback.LinkInputSig( trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramRegister.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber, - b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister); + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber, + b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister); p.Value.ProgramUnregisteredFeedback.LinkInputSig( trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber]); - p.Value.ProgramNameFeedback.LinkInputSig(trilist.StringInput[programSlotJoinStart + joinMap.ProgramName.JoinNumber]); + p.Value.ProgramNameFeedback.LinkInputSig(trilist.StringInput[programSlotJoinStart + joinMap.ProgramName.JoinNumber]); p.Value.ProgramCompileTimeFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramCompiledTime.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramCompiledTime.JoinNumber]); p.Value.CrestronDataBaseVersionFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramCrestronDatabaseVersion.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramCrestronDatabaseVersion.JoinNumber]); p.Value.EnvironmentVersionFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramEnvironmentVersion.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramEnvironmentVersion.JoinNumber]); p.Value.AggregatedProgramInfoFeedback.LinkInputSig( trilist.StringInput[programSlotJoinStart + joinMap.AggregatedProgramInfo.JoinNumber]); - programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinNumber; - } - } - - //// Sets the time zone - //public void SetTimeZone(int timeZone) - //{ - // SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone; - //} - - /// - /// Responds to program change events and triggers the appropriate feedbacks to update - /// - /// - /// - private void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args) - { - Debug.Console(2, this, "Program Change Detected for slot: {0}", sender.Number); - Debug.Console(2, this, "Event Type: {0}", args.EventType); - - var program = ProgramStatusFeedbackCollection[sender.Number]; - - switch (args.EventType) - { - case eProgramChangeEventType.OperatingState: - program.ProgramStartedFeedback.FireUpdate(); - program.ProgramStoppedFeedback.FireUpdate(); - program.ProgramInfo.OperatingState = args.OperatingState; - if (args.OperatingState == eProgramOperatingState.Start) - program.GetProgramInfo(); - else - { - program.AggregatedProgramInfoFeedback.FireUpdate(); - program.OnProgramInfoChanged(); - } - break; - case eProgramChangeEventType.RegistrationState: - program.ProgramRegisteredFeedback.FireUpdate(); - program.ProgramUnregisteredFeedback.FireUpdate(); - program.ProgramInfo.RegistrationState = args.RegistrationState; - program.GetProgramInfo(); - break; - } - } - - /// - /// Responds to time zone changes and updates the appropriate feedbacks - /// - /// - private void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args) - { - Debug.Console(2, this, "Time Zone Change Detected."); - TimeZoneFeedback.FireUpdate(); - TimeZoneTextFeedback.FireUpdate(); - - OnSystemMonitorPropertiesChanged(); - } - - public class EthernetStatusFeedbacks - { - public StringFeedback HostNameFeedback { get; protected set; } - public StringFeedback DnsServerFeedback { get; protected set; } - public StringFeedback DomainFeedback { get; protected set; } - public StringFeedback MacAddressFeedback { get; protected set; } - public StringFeedback DhcpStatusFeedback { get; protected set; } - - public StringFeedback CurrentIpAddressFeedback { get; protected set; } - public StringFeedback CurrentSubnetMaskFeedback { get; protected set; } - public StringFeedback CurrentDefaultGatewayFeedback { get; protected set; } - - public StringFeedback StaticIpAddressFeedback { get; protected set; } - public StringFeedback StaticSubnetMaskFeedback { get; protected set; } - public StringFeedback StaticDefaultGatewayFeedback { get; protected set; } - - public EthernetStatusFeedbacks(short adapterIndex) - { - Debug.Console(2, "Ethernet Information for interface {0}", adapterIndex); - Debug.Console(2, "Adapter Index: {1} Hostname: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current Router: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPADDRESS, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPMASK, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static Router: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_ROUTER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} DNS Servers: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} DHCP State: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Domain Name: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} MAC Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex), adapterIndex); - HostNameFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex)); - - CurrentIpAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); - CurrentDefaultGatewayFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); - CurrentSubnetMaskFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); - StaticIpAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); - StaticDefaultGatewayFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); - StaticSubnetMaskFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); - DomainFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex)); - DnsServerFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex)); - MacAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex)); - - DhcpStatusFeedback = new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex)); - } - - public void UpdateEthernetStatus() - { - HostNameFeedback.FireUpdate(); - CurrentIpAddressFeedback.FireUpdate(); - CurrentSubnetMaskFeedback.FireUpdate(); - CurrentDefaultGatewayFeedback.FireUpdate(); - StaticIpAddressFeedback.FireUpdate(); - StaticSubnetMaskFeedback.FireUpdate(); - StaticDefaultGatewayFeedback.FireUpdate(); - DomainFeedback.FireUpdate(); - DnsServerFeedback.FireUpdate(); - MacAddressFeedback.FireUpdate(); - DhcpStatusFeedback.FireUpdate(); - } - } - - - public class ProgramStatusFeedbacks - { - public event EventHandler ProgramInfoChanged; - - public Program Program; - - public ProgramInfo ProgramInfo { get; set; } - - public BoolFeedback ProgramStartedFeedback; - public BoolFeedback ProgramStoppedFeedback; - public BoolFeedback ProgramRegisteredFeedback; - public BoolFeedback ProgramUnregisteredFeedback; - - public StringFeedback ProgramNameFeedback; - public StringFeedback ProgramCompileTimeFeedback; - public StringFeedback CrestronDataBaseVersionFeedback; - // SIMPL windows version - public StringFeedback EnvironmentVersionFeedback; - public StringFeedback AggregatedProgramInfoFeedback; - - public ProgramStatusFeedbacks(Program program) - { - ProgramInfo = new ProgramInfo(program.Number); - - Program = program; - - ProgramInfo.OperatingState = Program.OperatingState; - ProgramInfo.RegistrationState = Program.RegistrationState; - - ProgramStartedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Start); - ProgramStoppedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Stop); - ProgramRegisteredFeedback = - new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Register); - ProgramUnregisteredFeedback = - new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Unregister); - - ProgramNameFeedback = new StringFeedback(() => ProgramInfo.ProgramFile); - ProgramCompileTimeFeedback = new StringFeedback(() => ProgramInfo.CompileTime); - CrestronDataBaseVersionFeedback = new StringFeedback(() => ProgramInfo.CrestronDb); - EnvironmentVersionFeedback = new StringFeedback(() => ProgramInfo.Environment); - - AggregatedProgramInfoFeedback = new StringFeedback(() => JsonConvert.SerializeObject(ProgramInfo)); - - GetProgramInfo(); - } - - /// - /// Retrieves information about a running program - /// - public void GetProgramInfo() - { - CrestronInvoke.BeginInvoke(GetProgramInfo); - } - - private void GetProgramInfo(object o) - { - Debug.Console(2, "Attempting to get program info for slot: {0}", Program.Number); - - string response = null; - - if (Program.RegistrationState == eProgramRegistrationState.Unregister || Program.OperatingState == eProgramOperatingState.Stop) - { - Debug.Console(2, "Program {0} not registered. Setting default values for program information.", - Program.Number); - - ProgramInfo = new ProgramInfo(Program.Number) - { - OperatingState = Program.OperatingState, - RegistrationState = Program.RegistrationState - }; - - return; - } - - var success = CrestronConsole.SendControlSystemCommand( - string.Format("progcomments:{0}", Program.Number), ref response); - - if (!success) - { - Debug.Console(2, "Progcomments Attempt Unsuccessful for slot: {0}", Program.Number); - UpdateFeedbacks(); - return; - } - - if (response.ToLower().Contains("bad or incomplete")) - { - Debug.Console(2, - "Program in slot {0} not running. Setting default ProgramInfo for slot: {0}", - Program.Number); - - // Assume no valid program info. Constructing a new object will wipe all properties - ProgramInfo = new ProgramInfo(Program.Number) - { - OperatingState = Program.OperatingState, - RegistrationState = Program.RegistrationState - }; - - UpdateFeedbacks(); - - return; - } - - - // Shared properteis - ProgramInfo.ProgramFile = ParseConsoleData(response, "Program File", ": ", "\n"); - ProgramInfo.CompilerRevision = ParseConsoleData(response, "Compiler Rev", ": ", "\n"); - ProgramInfo.CompileTime = ParseConsoleData(response, "Compiled On", ": ", "\n"); - ProgramInfo.Include4Dat = ParseConsoleData(response, "Include4.dat", ": ", "\n"); - - - if (ProgramInfo.ProgramFile.Contains(".dll")) - { - // SSP Program - ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ": ", "\n"); - ProgramInfo.ApplicationName = ParseConsoleData(response, "Application Name", ": ", "\n"); - ProgramInfo.ProgramTool = ParseConsoleData(response, "Program Tool", ": ", "\n"); - ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, "Min Firmware Version", ": ", - "\n"); - ProgramInfo.PlugInVersion = ParseConsoleData(response, "PlugInVersion", ": ", "\n"); - } - else if (ProgramInfo.ProgramFile.Contains(".smw")) - { - // SIMPL Windows Program - ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ":", "\n"); - ProgramInfo.SystemName = ParseConsoleData(response, "System Name", ": ", "\n"); - ProgramInfo.CrestronDb = ParseConsoleData(response, "CrestronDB", ": ", "\n"); - ProgramInfo.Environment = ParseConsoleData(response, "Source Env", ": ", "\n"); - ProgramInfo.Programmer = ParseConsoleData(response, "Programmer", ": ", "\n"); - } - Debug.Console(2, "Program info for slot {0} successfully updated", Program.Number); - - UpdateFeedbacks(); - } - - private void UpdateFeedbacks() - { - ProgramNameFeedback.FireUpdate(); - ProgramCompileTimeFeedback.FireUpdate(); - CrestronDataBaseVersionFeedback.FireUpdate(); - EnvironmentVersionFeedback.FireUpdate(); - - AggregatedProgramInfoFeedback.FireUpdate(); - - OnProgramInfoChanged(); - } - - public void OnProgramInfoChanged() - { - //Debug.Console(1, "Firing ProgramInfoChanged for slot: {0}", Program.Number); - var handler = ProgramInfoChanged; - if (handler != null) - { - handler(this, new ProgramInfoEventArgs(ProgramInfo)); - } - } - - private string ParseConsoleData(string data, string line, string startString, string endString) - { - var outputData = ""; - - if (data.Length <= 0) return outputData; - - try - { - //Debug.Console(2, "ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}", data, line, startString, endString); - var linePosition = data.IndexOf(line, StringComparison.Ordinal); - var startPosition = data.IndexOf(startString, linePosition, StringComparison.Ordinal) + - startString.Length; - var endPosition = data.IndexOf(endString, startPosition, StringComparison.Ordinal); - outputData = data.Substring(startPosition, endPosition - startPosition).Trim(); - //Debug.Console(2, "ParseConsoleData Return: {0}", outputData); - } - catch (Exception e) - { - Debug.Console(1, "Error Parsing Console Data:\r{0}", e); - } - - return outputData; - } - } - } - - /// - /// Class for serializing program slot information - /// - public class ProgramInfo - { - // Shared properties - - [JsonProperty("programNumber")] - public uint ProgramNumber { get; private set; } - - [JsonConverter(typeof (StringEnumConverter))] - [JsonProperty("operatingState")] - public eProgramOperatingState OperatingState { get; set; } - - [JsonConverter(typeof (StringEnumConverter))] - [JsonProperty("registrationState")] - public eProgramRegistrationState RegistrationState { get; set; } - - [JsonProperty("programFile")] - public string ProgramFile { get; set; } - - [JsonProperty("friendlyName")] - public string FriendlyName { get; set; } - - [JsonProperty("compilerRevision")] - public string CompilerRevision { get; set; } - - [JsonProperty("compileTime")] - public string CompileTime { get; set; } - - [JsonProperty("include4Dat")] - public string Include4Dat { get; set; } - - // SIMPL Windows properties - [JsonProperty("systemName")] - public string SystemName { get; set; } - - [JsonProperty("crestronDb")] - public string CrestronDb { get; set; } - - [JsonProperty("environment")] - public string Environment { get; set; } - - [JsonProperty("programmer")] - public string Programmer { get; set; } - - - // SSP Properties - [JsonProperty("applicationName")] - public string ApplicationName { get; set; } - - [JsonProperty("programTool")] - public string ProgramTool { get; set; } - - [JsonProperty("minFirmwareVersion")] - public string MinFirmwareVersion { get; set; } - - [JsonProperty("plugInVersion")] - public string PlugInVersion { get; set; } - - public ProgramInfo(uint number) - { - ProgramNumber = number; - - ProgramFile = ""; - FriendlyName = ""; - CompilerRevision = ""; - CompileTime = ""; - Include4Dat = ""; - - SystemName = ""; - CrestronDb = ""; - Environment = ""; - Programmer = ""; - - ApplicationName = ""; - ProgramTool = ""; - MinFirmwareVersion = ""; - PlugInVersion = ""; - } - } - - public class ProgramInfoEventArgs : EventArgs - { - public ProgramInfo ProgramInfo; - - public ProgramInfoEventArgs(ProgramInfo progInfo) - { - ProgramInfo = progInfo; - } - } + programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinNumber; + } + } + + //// Sets the time zone + //public void SetTimeZone(int timeZone) + //{ + // SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone; + //} + + /// + /// Responds to program change events and triggers the appropriate feedbacks to update + /// + /// + /// + private void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args) + { + Debug.Console(2, this, "Program Change Detected for slot: {0}", sender.Number); + Debug.Console(2, this, "Event Type: {0}", args.EventType); + + var program = ProgramStatusFeedbackCollection[sender.Number]; + + switch (args.EventType) + { + case eProgramChangeEventType.OperatingState: + program.ProgramStartedFeedback.FireUpdate(); + program.ProgramStoppedFeedback.FireUpdate(); + program.ProgramInfo.OperatingState = args.OperatingState; + if (args.OperatingState == eProgramOperatingState.Start) + program.GetProgramInfo(); + else + { + program.AggregatedProgramInfoFeedback.FireUpdate(); + program.OnProgramInfoChanged(); + } + break; + case eProgramChangeEventType.RegistrationState: + program.ProgramRegisteredFeedback.FireUpdate(); + program.ProgramUnregisteredFeedback.FireUpdate(); + program.ProgramInfo.RegistrationState = args.RegistrationState; + program.GetProgramInfo(); + break; + } + } + + /// + /// Responds to time zone changes and updates the appropriate feedbacks + /// + /// + private void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args) + { + Debug.Console(2, this, "Time Zone Change Detected."); + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + + public class EthernetStatusFeedbacks + { + public StringFeedback HostNameFeedback { get; protected set; } + public StringFeedback DnsServerFeedback { get; protected set; } + public StringFeedback DomainFeedback { get; protected set; } + public StringFeedback MacAddressFeedback { get; protected set; } + public StringFeedback DhcpStatusFeedback { get; protected set; } + + public StringFeedback CurrentIpAddressFeedback { get; protected set; } + public StringFeedback CurrentSubnetMaskFeedback { get; protected set; } + public StringFeedback CurrentDefaultGatewayFeedback { get; protected set; } + + public StringFeedback StaticIpAddressFeedback { get; protected set; } + public StringFeedback StaticSubnetMaskFeedback { get; protected set; } + public StringFeedback StaticDefaultGatewayFeedback { get; protected set; } + + public EthernetStatusFeedbacks(short adapterIndex) + { + Debug.Console(2, "Ethernet Information for interface {0}", adapterIndex); + Debug.Console(2, "Adapter Index: {1} Hostname: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current Router: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPADDRESS, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPMASK, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static Router: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_ROUTER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} DNS Servers: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} DHCP State: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Domain Name: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} MAC Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex), adapterIndex); + HostNameFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex)); + + CurrentIpAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); + CurrentDefaultGatewayFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); + CurrentSubnetMaskFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); + StaticIpAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); + StaticDefaultGatewayFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); + StaticSubnetMaskFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); + DomainFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex)); + DnsServerFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex)); + MacAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex)); + + DhcpStatusFeedback = new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex)); + } + + public void UpdateEthernetStatus() + { + HostNameFeedback.FireUpdate(); + CurrentIpAddressFeedback.FireUpdate(); + CurrentSubnetMaskFeedback.FireUpdate(); + CurrentDefaultGatewayFeedback.FireUpdate(); + StaticIpAddressFeedback.FireUpdate(); + StaticSubnetMaskFeedback.FireUpdate(); + StaticDefaultGatewayFeedback.FireUpdate(); + DomainFeedback.FireUpdate(); + DnsServerFeedback.FireUpdate(); + MacAddressFeedback.FireUpdate(); + DhcpStatusFeedback.FireUpdate(); + } + } + + + public class ProgramStatusFeedbacks + { + public event EventHandler ProgramInfoChanged; + + public Program Program; + + public ProgramInfo ProgramInfo { get; set; } + + public BoolFeedback ProgramStartedFeedback; + public BoolFeedback ProgramStoppedFeedback; + public BoolFeedback ProgramRegisteredFeedback; + public BoolFeedback ProgramUnregisteredFeedback; + + public StringFeedback ProgramNameFeedback; + public StringFeedback ProgramCompileTimeFeedback; + public StringFeedback CrestronDataBaseVersionFeedback; + // SIMPL windows version + public StringFeedback EnvironmentVersionFeedback; + public StringFeedback AggregatedProgramInfoFeedback; + + public ProgramStatusFeedbacks(Program program) + { + ProgramInfo = new ProgramInfo(program.Number); + + Program = program; + + ProgramInfo.OperatingState = Program.OperatingState; + ProgramInfo.RegistrationState = Program.RegistrationState; + + ProgramStartedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Start); + ProgramStartedFeedback.FireUpdate(); + + ProgramStoppedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Stop); + ProgramStoppedFeedback.FireUpdate(); + + ProgramRegisteredFeedback = + new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Register); + ProgramRegisteredFeedback.FireUpdate(); + + ProgramUnregisteredFeedback = + new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Unregister); + ProgramUnregisteredFeedback.FireUpdate(); + + ProgramNameFeedback = new StringFeedback(() => ProgramInfo.ProgramFile); + ProgramCompileTimeFeedback = new StringFeedback(() => ProgramInfo.CompileTime); + CrestronDataBaseVersionFeedback = new StringFeedback(() => ProgramInfo.CrestronDb); + EnvironmentVersionFeedback = new StringFeedback(() => ProgramInfo.Environment); + AggregatedProgramInfoFeedback = new StringFeedback(() => JsonConvert.SerializeObject(ProgramInfo)); + + GetProgramInfo(); + } + + /// + /// Retrieves information about a running program + /// + public void GetProgramInfo() + { + CrestronInvoke.BeginInvoke(GetProgramInfo); + } + + private void GetProgramInfo(object o) + { + Debug.Console(2, "Attempting to get program info for slot: {0}", Program.Number); + + string response = null; + + if (Program.RegistrationState == eProgramRegistrationState.Unregister || Program.OperatingState == eProgramOperatingState.Stop) + { + Debug.Console(2, "Program {0} not registered. Setting default values for program information.", + Program.Number); + + ProgramInfo = new ProgramInfo(Program.Number) + { + OperatingState = Program.OperatingState, + RegistrationState = Program.RegistrationState + }; + + return; + } + + var success = CrestronConsole.SendControlSystemCommand( + string.Format("progcomments:{0}", Program.Number), ref response); + + if (!success) + { + Debug.Console(2, "Progcomments Attempt Unsuccessful for slot: {0}", Program.Number); + UpdateFeedbacks(); + return; + } + + if (response.ToLower().Contains("bad or incomplete")) + { + Debug.Console(2, + "Program in slot {0} not running. Setting default ProgramInfo for slot: {0}", + Program.Number); + + // Assume no valid program info. Constructing a new object will wipe all properties + ProgramInfo = new ProgramInfo(Program.Number) + { + OperatingState = Program.OperatingState, + RegistrationState = Program.RegistrationState + }; + + UpdateFeedbacks(); + + return; + } + + + // Shared properteis + ProgramInfo.ProgramFile = ParseConsoleData(response, "Program File", ": ", "\n"); + ProgramInfo.CompilerRevision = ParseConsoleData(response, "Compiler Rev", ": ", "\n"); + ProgramInfo.CompileTime = ParseConsoleData(response, "Compiled On", ": ", "\n"); + ProgramInfo.Include4Dat = ParseConsoleData(response, "Include4.dat", ": ", "\n"); + + + if (ProgramInfo.ProgramFile.Contains(".dll")) + { + // SSP Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ": ", "\n"); + ProgramInfo.ApplicationName = ParseConsoleData(response, "Application Name", ": ", "\n"); + ProgramInfo.ProgramTool = ParseConsoleData(response, "Program Tool", ": ", "\n"); + ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, "Min Firmware Version", ": ", + "\n"); + ProgramInfo.PlugInVersion = ParseConsoleData(response, "PlugInVersion", ": ", "\n"); + } + else if (ProgramInfo.ProgramFile.Contains(".smw")) + { + // SIMPL Windows Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ":", "\n"); + ProgramInfo.SystemName = ParseConsoleData(response, "System Name", ": ", "\n"); + ProgramInfo.CrestronDb = ParseConsoleData(response, "CrestronDB", ": ", "\n"); + ProgramInfo.Environment = ParseConsoleData(response, "Source Env", ": ", "\n"); + ProgramInfo.Programmer = ParseConsoleData(response, "Programmer", ": ", "\n"); + } + Debug.Console(2, "Program info for slot {0} successfully updated", Program.Number); + + UpdateFeedbacks(); + } + + private void UpdateFeedbacks() + { + ProgramNameFeedback.FireUpdate(); + ProgramCompileTimeFeedback.FireUpdate(); + CrestronDataBaseVersionFeedback.FireUpdate(); + EnvironmentVersionFeedback.FireUpdate(); + + AggregatedProgramInfoFeedback.FireUpdate(); + + OnProgramInfoChanged(); + } + + public void OnProgramInfoChanged() + { + //Debug.Console(1, "Firing ProgramInfoChanged for slot: {0}", Program.Number); + var handler = ProgramInfoChanged; + if (handler != null) + { + handler(this, new ProgramInfoEventArgs(ProgramInfo)); + } + } + + private string ParseConsoleData(string data, string line, string startString, string endString) + { + var outputData = ""; + + if (data.Length <= 0) return outputData; + + try + { + //Debug.Console(2, "ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}", data, line, startString, endString); + var linePosition = data.IndexOf(line, StringComparison.Ordinal); + var startPosition = data.IndexOf(startString, linePosition, StringComparison.Ordinal) + + startString.Length; + var endPosition = data.IndexOf(endString, startPosition, StringComparison.Ordinal); + outputData = data.Substring(startPosition, endPosition - startPosition).Trim(); + //Debug.Console(2, "ParseConsoleData Return: {0}", outputData); + } + catch (Exception e) + { + Debug.Console(1, "Error Parsing Console Data:\r{0}", e); + } + + return outputData; + } + } + } + + /// + /// Class for serializing program slot information + /// + public class ProgramInfo + { + // Shared properties + + [JsonProperty("programNumber")] + public uint ProgramNumber { get; private set; } + + [JsonConverter(typeof (StringEnumConverter))] + [JsonProperty("operatingState")] + public eProgramOperatingState OperatingState { get; set; } + + [JsonConverter(typeof (StringEnumConverter))] + [JsonProperty("registrationState")] + public eProgramRegistrationState RegistrationState { get; set; } + + [JsonProperty("programFile")] + public string ProgramFile { get; set; } + + [JsonProperty("friendlyName")] + public string FriendlyName { get; set; } + + [JsonProperty("compilerRevision")] + public string CompilerRevision { get; set; } + + [JsonProperty("compileTime")] + public string CompileTime { get; set; } + + [JsonProperty("include4Dat")] + public string Include4Dat { get; set; } + + // SIMPL Windows properties + [JsonProperty("systemName")] + public string SystemName { get; set; } + + [JsonProperty("crestronDb")] + public string CrestronDb { get; set; } + + [JsonProperty("environment")] + public string Environment { get; set; } + + [JsonProperty("programmer")] + public string Programmer { get; set; } + + + // SSP Properties + [JsonProperty("applicationName")] + public string ApplicationName { get; set; } + + [JsonProperty("programTool")] + public string ProgramTool { get; set; } + + [JsonProperty("minFirmwareVersion")] + public string MinFirmwareVersion { get; set; } + + [JsonProperty("plugInVersion")] + public string PlugInVersion { get; set; } + + public ProgramInfo(uint number) + { + ProgramNumber = number; + + ProgramFile = ""; + FriendlyName = ""; + CompilerRevision = ""; + CompileTime = ""; + Include4Dat = ""; + + SystemName = ""; + CrestronDb = ""; + Environment = ""; + Programmer = ""; + + ApplicationName = ""; + ProgramTool = ""; + MinFirmwareVersion = ""; + PlugInVersion = ""; + } + } + + public class ProgramInfoEventArgs : EventArgs + { + public ProgramInfo ProgramInfo; + + public ProgramInfoEventArgs(ProgramInfo progInfo) + { + ProgramInfo = progInfo; + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index adda54bd..00303250 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -199,6 +199,7 @@ + @@ -219,11 +220,14 @@ + + + @@ -282,6 +286,7 @@ + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec index 5db86afc..e736dcbf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec @@ -14,7 +14,7 @@ crestron 3series 4series - + diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index f7275a66..01da1c54 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -402,13 +402,16 @@ namespace PepperDash.Essentials /// Loads a /// /// + /// static void LoadCustomPlugin(IPluginDeviceFactory plugin, LoadedAssembly loadedAssembly) { var passed = Global.IsRunningMinimumVersionOrHigher(plugin.MinimumEssentialsFrameworkVersion); if (!passed) { - Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", plugin.MinimumEssentialsFrameworkVersion); + Debug.Console(0, Debug.ErrorLogLevel.Error, + "\r\n********************\r\n\tPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}\r\n********************", + plugin.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); return; } else diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 4f79626f..d6a9fad0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -1,178 +1,300 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; - using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - using PepperDash.Core; //using SSMono.IO; +using PepperDash.Core.WebApi.Presets; namespace PepperDash.Essentials.Core.Presets { - /// - /// Class that represents the model behind presets display - /// - public class DevicePresetsModel : Device - { - public event EventHandler PresetsLoaded; + /// + /// Class that represents the model behind presets display + /// + public class DevicePresetsModel : Device + { + public delegate void PresetRecalledCallback(ISetTopBoxNumericKeypad device, string channel); - public int PulseTime { get; set; } - public int DigitSpacingMS { get; set; } - public bool PresetsAreLoaded { get; private set; } + public delegate void PresetsSavedCallback(List presets); - public List PresetsList { get { return _PresetsList.ToList(); } } - List _PresetsList; - public int Count { get { return PresetsList != null ? PresetsList.Count : 0; } } + private readonly CCriticalSection _fileOps = new CCriticalSection(); + private readonly bool _initSuccess; - public bool UseLocalImageStorage { get; set; } - public string ImagesLocalHostPrefix { get; set; } - public string ImagesPathPrefix { get; set; } - public string ListPathPrefix { get; set; } + private readonly ISetTopBoxNumericKeypad _setTopBox; - /// - /// The methods on the STB device to call when dialing - /// - Dictionary> DialFunctions; - Action EnterFunction; + /// + /// The methods on the STB device to call when dialing + /// + private Dictionary> _dialFunctions; - bool DialIsRunning; - string FilePath; - bool InitSuccess; - //SSMono.IO.FileSystemWatcher ListWatcher; + private bool _dialIsRunning; + private Action _enterFunction; + private string _filePath; - public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName) - : base(key) - { - PulseTime = 150; - DigitSpacingMS = 150; + public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName) + : this(key, fileName) + { + try + { + _setTopBox = setTopBox; - try - { - // Grab the digit functions from the device - // If any fail, the whole thing fails peacefully - DialFunctions = new Dictionary>(10) - { - { '1', setTopBox.Digit1 }, - { '2', setTopBox.Digit2 }, - { '3', setTopBox.Digit3 }, - { '4', setTopBox.Digit4 }, - { '5', setTopBox.Digit5 }, - { '6', setTopBox.Digit6 }, - { '7', setTopBox.Digit7 }, - { '8', setTopBox.Digit8 }, - { '9', setTopBox.Digit9 }, - { '0', setTopBox.Digit0 }, - { '-', setTopBox.Dash } - }; - } - catch - { - Debug.Console(0, "DevicePresets '{0}', not attached to INumericKeypad device. Ignoring", key); - DialFunctions = null; - return; - } + // Grab the digit functions from the device + // If any fail, the whole thing fails peacefully + _dialFunctions = new Dictionary>(10) + { + {'1', setTopBox.Digit1}, + {'2', setTopBox.Digit2}, + {'3', setTopBox.Digit3}, + {'4', setTopBox.Digit4}, + {'5', setTopBox.Digit5}, + {'6', setTopBox.Digit6}, + {'7', setTopBox.Digit7}, + {'8', setTopBox.Digit8}, + {'9', setTopBox.Digit9}, + {'0', setTopBox.Digit0}, + {'-', setTopBox.Dash} + }; + } + catch + { + Debug.Console(0, "DevicePresets '{0}', not attached to INumericKeypad device. Ignoring", key); + _dialFunctions = null; + return; + } - EnterFunction = setTopBox.KeypadEnter; + _enterFunction = setTopBox.KeypadEnter; + } - UseLocalImageStorage = true; + public DevicePresetsModel(string key, string fileName) : base(key) + { + PulseTime = 150; + DigitSpacingMs = 150; - ImagesLocalHostPrefix = "http://" + CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS,0); - ImagesPathPrefix = @"/presets/images.zip/"; - ListPathPrefix = @"/html/presets/lists/"; + UseLocalImageStorage = true; - SetFileName(fileName); + ImagesLocalHostPrefix = "http://" + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + ImagesPathPrefix = @"/presets/images.zip/"; + ListPathPrefix = @"/html/presets/lists/"; - //ListWatcher = new FileSystemWatcher(@"\HTML\presets\lists"); - //ListWatcher.NotifyFilter = NotifyFilters.LastWrite; - //ListWatcher.EnableRaisingEvents = true; - //ListWatcher.Changed += ListWatcher_Changed; - InitSuccess = true; - } + SetFileName(fileName); + + _initSuccess = true; + } + + public event PresetRecalledCallback PresetRecalled; + public event PresetsSavedCallback PresetsSaved; + + public int PulseTime { get; set; } + public int DigitSpacingMs { get; set; } + public bool PresetsAreLoaded { get; private set; } + + public List PresetsList { get; private set; } + + public int Count + { + get { return PresetsList != null ? PresetsList.Count : 0; } + } + + public bool UseLocalImageStorage { get; set; } + public string ImagesLocalHostPrefix { get; set; } + public string ImagesPathPrefix { get; set; } + public string ListPathPrefix { get; set; } + public event EventHandler PresetsLoaded; - public void SetFileName(string path) - { - FilePath = ListPathPrefix + path; - LoadChannels(); - } + public void SetFileName(string path) + { + _filePath = ListPathPrefix + path; - public void LoadChannels() - { - PresetsAreLoaded = false; - try - { - var pl = JsonConvert.DeserializeObject(Crestron.SimplSharp.CrestronIO.File.ReadToEnd(FilePath, Encoding.ASCII)); - Name = pl.Name; - _PresetsList = pl.Channels; - } - catch (Exception e) - { - Debug.Console(2, this, "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", FilePath, e.Message); - // Just save a default empty list - _PresetsList = new List(); - } - PresetsAreLoaded = true; + Debug.Console(2, this, "Setting presets file path to {0}", _filePath); + LoadChannels(); + } - var handler = PresetsLoaded; - if (handler != null) - handler(this, EventArgs.Empty); - } + public void LoadChannels() + { + try + { + _fileOps.Enter(); - public void Dial(int presetNum) - { - if (presetNum <= _PresetsList.Count) - Dial(_PresetsList[presetNum - 1].Channel); - } + Debug.Console(2, this, "Loading presets from {0}", _filePath); + PresetsAreLoaded = false; + try + { + var pl = JsonConvert.DeserializeObject(File.ReadToEnd(_filePath, Encoding.ASCII)); + Name = pl.Name; + PresetsList = pl.Channels; + } + catch (Exception e) + { + Debug.Console(2, this, + "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", + _filePath, e.Message); + // Just save a default empty list + PresetsList = new List(); + } + PresetsAreLoaded = true; - public void Dial(string chanNum) - { - if (DialIsRunning || !InitSuccess) return; - if (DialFunctions == null) - { - Debug.Console(1, "DevicePresets '{0}', not attached to keypad device. Ignoring channel", Key); - return; - } + var handler = PresetsLoaded; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + finally + { + _fileOps.Leave(); + } + } - DialIsRunning = true; - CrestronInvoke.BeginInvoke(o => - { - foreach (var c in chanNum.ToCharArray()) - { - if (DialFunctions.ContainsKey(c)) - Pulse(DialFunctions[c]); - CrestronEnvironment.Sleep(DigitSpacingMS); - } + public void Dial(int presetNum) + { + if (presetNum <= PresetsList.Count) + { + Dial(PresetsList[presetNum - 1].Channel); + } + } - if (EnterFunction != null) - Pulse(EnterFunction); - DialIsRunning = false; - }); - } + public void Dial(string chanNum) + { + if (_dialIsRunning || !_initSuccess) + { + return; + } + if (_dialFunctions == null) + { + Debug.Console(1, "DevicePresets '{0}', not attached to keypad device. Ignoring channel", Key); + return; + } - void Pulse(Action act) - { - act(true); - CrestronEnvironment.Sleep(PulseTime); - act(false); - } + _dialIsRunning = true; + CrestronInvoke.BeginInvoke(o => + { + foreach (var c in chanNum.ToCharArray()) + { + if (_dialFunctions.ContainsKey(c)) + { + Pulse(_dialFunctions[c]); + } + CrestronEnvironment.Sleep(DigitSpacingMs); + } - /// - /// Event handler for filesystem watcher. When directory changes, this is called - /// - //void ListWatcher_Changed(object sender, FileSystemEventArgs e) - //{ - // Debug.Console(1, this, "folder modified: {0}", e.FullPath); - // if (e.FullPath.Equals(FilePath, StringComparison.OrdinalIgnoreCase)) - // { - // Debug.Console(1, this, "File changed: {0}", e.ChangeType); - // LoadChannels(); - // } - //} - } + if (_enterFunction != null) + { + Pulse(_enterFunction); + } + _dialIsRunning = false; + }); + + if (_setTopBox == null) return; + + OnPresetRecalled(_setTopBox, chanNum); + } + + public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox) + { + if (presetNum <= PresetsList.Count) + { + Dial(PresetsList[presetNum - 1].Channel, setTopBox); + } + } + + public void Dial(string chanNum, ISetTopBoxNumericKeypad setTopBox) + { + _dialFunctions = new Dictionary>(10) + { + {'1', setTopBox.Digit1}, + {'2', setTopBox.Digit2}, + {'3', setTopBox.Digit3}, + {'4', setTopBox.Digit4}, + {'5', setTopBox.Digit5}, + {'6', setTopBox.Digit6}, + {'7', setTopBox.Digit7}, + {'8', setTopBox.Digit8}, + {'9', setTopBox.Digit9}, + {'0', setTopBox.Digit0}, + {'-', setTopBox.Dash} + }; + + _enterFunction = setTopBox.KeypadEnter; + + OnPresetRecalled(setTopBox, chanNum); + + Dial(chanNum); + } + + private void OnPresetRecalled(ISetTopBoxNumericKeypad setTopBox, string channel) + { + var handler = PresetRecalled; + + if (handler == null) + { + return; + } + + handler(setTopBox, channel); + } + + public void UpdatePreset(int index, PresetChannel preset) + { + if (index >= PresetsList.Count) + { + return; + } + + PresetsList[index] = preset; + + SavePresets(); + + OnPresetsSaved(); + } + + public void UpdatePresets(List presets) + { + PresetsList = presets; + + SavePresets(); + + OnPresetsSaved(); + } + + private void SavePresets() + { + try + { + _fileOps.Enter(); + var pl = new PresetsList {Channels = PresetsList, Name = Name}; + var json = JsonConvert.SerializeObject(pl, Formatting.Indented); + + using (var file = File.Open(_filePath, FileMode.Truncate)) + { + file.Write(json, Encoding.UTF8); + } + } + finally + { + _fileOps.Leave(); + } + + } + + private void OnPresetsSaved() + { + var handler = PresetsSaved; + + if (handler == null) return; + + handler(PresetsList); + } + + private void Pulse(Action act) + { + act(true); + CrestronEnvironment.Sleep(PulseTime); + act(false); + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs index b9650e60..259ccbb9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs @@ -10,19 +10,22 @@ namespace PepperDash.Essentials.Core.Presets public class PresetChannel { - [JsonProperty(Required = Required.Always)] + [JsonProperty(Required = Required.Always,PropertyName = "name")] public string Name { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "iconUrl")] public string IconUrl { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "channel")] public string Channel { get; set; } } public class PresetsList { - [JsonProperty(Required=Required.Always)] + [JsonProperty(Required=Required.Always,PropertyName = "name")] public string Name { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "channels")] public List Channels { get; set; } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs index 92c97248..fce6291d 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs @@ -40,7 +40,7 @@ namespace PepperDash_Essentials_Core.Queues private void Validate(IBasicCommunication coms, object message) { - if (_coms == null) + if (coms == null) throw new ArgumentNullException("coms"); if (message == null) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index 1f27fe1e..b4c6befa 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -15,23 +15,37 @@ namespace PepperDash_Essentials_Core.Queues protected readonly Thread _worker; protected readonly CEvent _waitHandle = new CEvent(); - private readonly bool _delayEnabled; - private readonly int _delayTime; + private bool _delayEnabled; + private int _delayTime; /// /// If the instance has been disposed. /// public bool Disposed { get; private set; } + /// + /// Constructor with no thread priority + /// + /// + public GenericQueue(string key) + : this(key, Thread.eThreadPriority.MediumPriority) + { + + } + /// /// Constructor for generic queue with no pacing /// /// Key - public GenericQueue(string key) + /// + public GenericQueue(string key, Thread.eThreadPriority priority) { _key = key; - _queue = new CrestronQueue(); - _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running); + _queue = new CrestronQueue(25); + _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running) + { + Priority = priority + }; CrestronEnvironment.ProgramStatusEventHandler += programEvent => { @@ -49,11 +63,28 @@ namespace PepperDash_Essentials_Core.Queues /// Pacing in ms between actions public GenericQueue(string key, int pacing) : this(key) + { + SetDelayValues(pacing); + } + + /// + /// Constructor with pacing and priority + /// + /// + /// + /// + public GenericQueue(string key, int pacing, Thread.eThreadPriority priority) + : this(key, priority) + { + SetDelayValues(pacing); + } + + private void SetDelayValues(int pacing) { _delayEnabled = pacing > 0; _delayTime = pacing; } - + /// /// Thread callback /// @@ -83,7 +114,7 @@ namespace PepperDash_Essentials_Core.Queues } catch (Exception ex) { - Debug.ConsoleWithLog(0, this, "Caught an exception in the Queue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace); + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Caught an exception in the Queue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace); } } else _waitHandle.Wait(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs new file mode 100644 index 00000000..cd0c2586 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using Crestron.SimplSharp.Scheduler; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsRoomScheduledEventsConfig + { + [JsonProperty("scheduledEvents")] + public List ScheduledEvents; + } + + public class ScheduledEventConfig + { + [JsonProperty("key")] + public string Key; + + [JsonProperty("name")] + public string Name; + + [JsonProperty("days")] + public ScheduledEventCommon.eWeekDays Days; + + [JsonProperty("time")] + public string Time; + + [JsonProperty("actions")] + public List Actions; + + [JsonProperty("persistent")] + public bool Persistent; + + [JsonProperty("acknowledgeable")] + public bool Acknowledgeable; + + [JsonProperty("enable")] + public bool Enable; + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs index 1743bdaa..82871228 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs @@ -41,6 +41,14 @@ namespace PepperDash.Essentials.Core } + /// + /// Simplified routing direct from source to destination + /// + public interface IRunDirectRouteAction + { + void RunDirectRoute(string sourceKey, string destinationKey); + } + /// /// For rooms that default presentation only routing /// diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 09c7988a..3c5567c4 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -49,7 +49,9 @@ namespace PepperDash.Essentials.DM public BoolFeedback EnableAudioBreakawayFeedback { get; private set; } public BoolFeedback EnableUsbBreakawayFeedback { get; private set; } - public Dictionary InputCardHdcpStateFeedbacks { get; private set; } + public Dictionary InputCardHdcpStateFeedbacks { get; private set; } + public Dictionary InputStreamCardStateFeedbacks { get; private set; } + public Dictionary OutputStreamCardStateFeedbacks { get; private set; } public Dictionary InputCardHdcpCapabilityTypes { get; private set; } @@ -223,7 +225,9 @@ namespace PepperDash.Essentials.DM EnableUsbBreakawayFeedback = new BoolFeedback(() => (Chassis as DmMDMnxn).EnableUSBBreakawayFeedback.BoolValue); - InputCardHdcpStateFeedbacks = new Dictionary(); + InputCardHdcpStateFeedbacks = new Dictionary(); + InputStreamCardStateFeedbacks = new Dictionary(); + OutputStreamCardStateFeedbacks = new Dictionary(); InputCardHdcpCapabilityTypes = new Dictionary(); for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) @@ -307,6 +311,33 @@ namespace PepperDash.Essentials.DM // return hdMdNxMHdmiOutput.HdmiOutputPort.DisabledByHdcpFeedback.BoolValue; return false; + }); + OutputStreamCardStateFeedbacks[tempX] = new IntFeedback(() => + { + try + { + var outputCard = Chassis.Outputs[tempX]; + + if (outputCard.Card is DmcStroAV) + { + Debug.Console(2, "Found output stream card in slot: {0}.", tempX); + var streamCard = outputCard.Card as DmcStroAV; + if (streamCard.Control.StartFeedback.BoolValue == true) + return 1; + else if (streamCard.Control.StopFeedback.BoolValue == true) + return 2; + else if (streamCard.Control.PauseFeedback.BoolValue == true) + return 3; + else + return 0; + } + return 0; + } + catch (InvalidOperationException iopex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Error adding output stream card in slot: {0}. Error: {1}", tempX, iopex); + return 0; + } }); } @@ -406,6 +437,33 @@ namespace PepperDash.Essentials.DM Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "The Input Card in slot: {0} supports HDCP 2. Please update the configuration value in the inputCardSupportsHdcp2 object to true. Error: {1}", tempX, iopex); return 0; } + }); + InputStreamCardStateFeedbacks[tempX] = new IntFeedback(() => + { + try + { + var inputCard = Chassis.Inputs[tempX]; + + if (inputCard.Card is DmcStr) + { + Debug.Console(2, "Found input stream card in slot: {0}.", tempX); + var streamCard = inputCard.Card as DmcStr; + if (streamCard.Control.StartFeedback.BoolValue == true) + return 1; + else if (streamCard.Control.StopFeedback.BoolValue == true) + return 2; + else if (streamCard.Control.PauseFeedback.BoolValue == true) + return 3; + else + return 0; + } + return 0; + } + catch (InvalidOperationException iopex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Error adding input stream card in slot: {0}. Error: {1}", tempX, iopex); + return 0; + } }); } } @@ -915,6 +973,19 @@ namespace PepperDash.Essentials.DM else Debug.Console(1, this, "No index of {0} found in InputCardHdcpCapabilityFeedbacks"); break; + } + case DMInputEventIds.StartEventId: + case DMInputEventIds.StopEventId: + case DMInputEventIds.PauseEventId: + { + Debug.Console(2, this, "DM Input {0} Stream Status EventId", args.Number); + if (InputStreamCardStateFeedbacks[args.Number] != null) + { + InputStreamCardStateFeedbacks[args.Number].FireUpdate(); + } + else + Debug.Console(2, this, "No index of {0} found in InputStreamCardStateFeedbacks"); + break; } default: { @@ -1043,6 +1114,19 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "DM Output {0} DisabledByHdcpEventId", args.Number); OutputDisabledByHdcpFeedbacks[args.Number].FireUpdate(); break; + } + case DMOutputEventIds.StartEventId: + case DMOutputEventIds.StopEventId: + case DMOutputEventIds.PauseEventId: + { + Debug.Console(2, this, "DM Output {0} Stream Status EventId", args.Number); + if (OutputStreamCardStateFeedbacks[args.Number] != null) + { + OutputStreamCardStateFeedbacks[args.Number].FireUpdate(); + } + else + Debug.Console(2, this, "No index of {0} found in OutputStreamCardStateFeedbacks"); + break; } default: { @@ -1242,13 +1326,16 @@ namespace PepperDash.Essentials.DM } else { - LinkHdmiInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); - } - - if (RxDictionary.ContainsKey(ioSlot)) - { - LinkRxToApi(trilist, ioSlot, joinMap, ioSlotJoin); - } + LinkHdmiInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); + LinkStreamInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); + } + + if (RxDictionary.ContainsKey(ioSlot)) + { + LinkRxToApi(trilist, ioSlot, joinMap, ioSlotJoin); + } + else + LinkStreamOutputToApi(trilist, ioSlot, joinMap, ioSlotJoin); } } @@ -1295,6 +1382,86 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + } + + private void LinkStreamInputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) + { + var inputPort = InputPorts[string.Format("inputCard{0}--streamIn", ioSlot)]; + if (inputPort == null) + { + return; + } + var streamCard = Chassis.Inputs[ioSlot].Card as DmcStr; + var join = joinMap.InputStreamCardState.JoinNumber + ioSlotJoin; + + Debug.Console(1, "Port value for input card {0} is set as a stream card", ioSlot); + + trilist.SetUShortSigAction(join, s => + { + if (s == 1) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to start", join, s); + streamCard.Control.Start(); + } + else if (s == 2) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to stop", join, s); + streamCard.Control.Stop(); + } + else if (s == 3) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to pause", join, s); + streamCard.Control.Pause(); + } + else + { + Debug.Console(2, this, "Join {0} value {1}: Ignore stream state", join, s); + } + }); + + InputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); + + trilist.UShortInput[join].UShortValue = InputStreamCardStateFeedbacks[ioSlot].UShortValue; + } + + private void LinkStreamOutputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) + { + var outputPort = OutputPorts[string.Format("outputCard{0}--streamOut", ioSlot)]; + if (outputPort == null) + { + return; + } + var streamCard = Chassis.Outputs[ioSlot].Card as DmcStroAV; + var join = joinMap.OutputStreamCardState.JoinNumber + ioSlotJoin; + + Debug.Console(1, "Port value for output card {0} is set as a stream card", ioSlot); + + trilist.SetUShortSigAction(join, s => + { + if (s == 1) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to start", join, s); + streamCard.Control.Start(); + } + else if (s == 2) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to stop", join, s); + streamCard.Control.Stop(); + } + else if (s == 3) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to pause", join, s); + streamCard.Control.Pause(); + } + else + { + Debug.Console(2, this, "Join {0} value {1}: Ignore stream state", join, s); + } + }); + + OutputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); + + trilist.UShortInput[join].UShortValue = OutputStreamCardStateFeedbacks[ioSlot].UShortValue; } private void LinkRxToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs index d7996554..39e549cc 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs @@ -39,12 +39,12 @@ namespace PepperDash.Essentials.DM.Endpoints.DGEs { _dge = device; _dgeEthernetInfo = _dge.ExtenderEthernetReservedSigs; - _dgeEthernetInfo.DeviceExtenderSigChange += (extender, args) => UpdateDeviceInfo(); + //_dgeEthernetInfo.DeviceExtenderSigChange += (extender, args) => UpdateDeviceInfo(); _dgeEthernetInfo.Use(); DeviceInfo = new DeviceInfo(); - _dge.OnlineStatusChange += (currentDevice, args) => { if (args.DeviceOnLine) UpdateDeviceInfo(); }; + //_dge.OnlineStatusChange += (currentDevice, args) => { if (args.DeviceOnLine) UpdateDeviceInfo(); }; _dc = dc; diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs index 1e44396c..fb8f3ac5 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs @@ -1,44 +1,44 @@ -using Crestron.SimplSharpPro; -using System; -using System.Linq; -//using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DM; -using Crestron.SimplSharpPro.DM.Endpoints; -using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.DM -{ - using eVst = eX02VideoSourceType; - using eAst = eX02AudioSourceType; - - +using Crestron.SimplSharpPro; +using System; +using System.Linq; +//using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.DM +{ + using eVst = eX02VideoSourceType; + using eAst = eX02AudioSourceType; + + [Description("Wrapper class for DM-TX-4K-Z-302-C")] - public class DmTx4kz302CController : DmTxControllerBase, ITxRoutingWithFeedback, IHasFeedback, - IIROutputPorts, IComPorts - { - public DmTx4kz302C Tx { get; private set; } - - public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } - public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } - public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } - public RoutingOutputPort DmOut { get; private set; } - public RoutingOutputPort HdmiLoopOut { get; private set; } - - public override StringFeedback ActiveVideoInputFeedback { get; protected set; } - public IntFeedback VideoSourceNumericFeedback { get; protected set; } - public IntFeedback AudioSourceNumericFeedback { get; protected set; } - public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } - public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } - public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } - public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } - public BoolFeedback DisplayPortVideoSyncFeedback { get; protected set; } - - //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + public class DmTx4kz302CController : DmTxControllerBase, ITxRoutingWithFeedback, IHasFeedback, + IIROutputPorts, IComPorts + { + public DmTx4kz302C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } + public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } + public BoolFeedback DisplayPortVideoSyncFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } //public override ushort HdcpSupportCapability { get; protected set; } //IroutingNumericEvent @@ -52,272 +52,286 @@ namespace PepperDash.Essentials.DM { var newEvent = NumericSwitchChange; if (newEvent != null) newEvent(this, e); - } - - /// - /// Helps get the "real" inputs, including when in Auto - /// - public eX02VideoSourceType ActualActiveVideoInput - { - get - { - if (Tx.VideoSourceFeedback != eVst.Auto) - return Tx.VideoSourceFeedback; - if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - return eVst.Hdmi1; - if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) - return eVst.Hdmi2; - - return Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue ? eVst.Vga : eVst.AllDisabled; - } - } - public RoutingPortCollection InputPorts - { - get - { - return new RoutingPortCollection - { - HdmiIn1, - HdmiIn2, - DisplayPortIn, - AnyVideoInput - }; - } - } - public RoutingPortCollection OutputPorts - { - get - { - return new RoutingPortCollection { DmOut, HdmiLoopOut }; - } - } - public DmTx4kz302CController(string key, string name, DmTx4kz302C tx) - : base(key, name, tx) - { - Tx = tx; - - HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, + } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi2; + + return Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue ? eVst.Vga : eVst.AllDisabled; + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + DisplayPortIn, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4kz302CController(string key, string name, DmTx4kz302C tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[1])) { FeedbackMatchObject = eVst.Hdmi1 - }; - HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, + }; + HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[2])) { FeedbackMatchObject = eVst.Hdmi2 - }; - DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.DisplayPortIn, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DisplayPort, eVst.DisplayPort, this, + }; + DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.DisplayPortIn, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DisplayPort, eVst.DisplayPort, this, VideoStatusHelper.GetDisplayPortInputStatusFuncs(tx.DisplayPortInput)) { FeedbackMatchObject = eVst.DisplayPort - }; - ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", - () => ActualActiveVideoInput.ToString()); - - Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; - Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; - Tx.DisplayPortInput.InputStreamChange += DisplayPortInputStreamChange; - Tx.BaseEvent += Tx_BaseEvent; - Tx.OnlineStatusChange += Tx_OnlineStatusChange; - - VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - - HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); - - HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpStateFeedback = - new IntFeedback( - () => - tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback - ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback - : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; - - Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); - - Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); - - DisplayPortVideoSyncFeedback = new BoolFeedback(() => (bool)tx.DisplayPortInput.SyncDetectedFeedback.BoolValue); - - - var combinedFuncs = new VideoStatusFuncsWrapper - { - HdcpActiveFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), - - HdcpStateFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); - return ActualActiveVideoInput == eVst.Hdmi2 ? tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString() : ""; - }, - - VideoResolutionFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); - if (ActualActiveVideoInput == eVst.Hdmi2) - return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); - return ActualActiveVideoInput == eVst.Vga ? tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString() : ""; - }, - VideoSyncFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Vga - && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) - - }; - - AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); - - DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.DmCat, null, this); - HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - - AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, - AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, - AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, - AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, - Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback, DisplayPortVideoSyncFeedback); - - // Set Ports for CEC - HdmiIn1.Port = Tx.HdmiInputs[1]; - HdmiIn2.Port = Tx.HdmiInputs[2]; - HdmiLoopOut.Port = Tx.HdmiOutput; - DmOut.Port = Tx.DmOutput; - } - - void DisplayPortInputStreamChange(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) - { - Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); - - switch (args.EventId) - { - case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: - DisplayPortVideoSyncFeedback.FireUpdate(); - break; - } - } - - - - public override bool CustomActivate() - { - // Link up all of these damned events to the various RoutingPorts via a helper handler - Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); - Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); - - Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); - Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); - - Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); - Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); - - // Base does register and sets up comm monitoring. - return base.CustomActivate(); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); - - if (Hdmi1VideoSyncFeedback != null) - { - Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); - } - if (Hdmi2VideoSyncFeedback != null) - { - Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); - } - if (DisplayPortVideoSyncFeedback != null) - { - DisplayPortVideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input3VideoSyncStatus.JoinNumber]); - } - - LinkDmTxToApi(this, trilist, joinMap, bridge); - } - - public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) - { - Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); - - switch (input) - { - case 0: - { - ExecuteSwitch(eVst.Auto, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 1: - { - ExecuteSwitch(HdmiIn1.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 2: - { - ExecuteSwitch(HdmiIn2.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 3: - { - ExecuteSwitch(DisplayPortIn.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 4: - { - ExecuteSwitch(eVst.AllDisabled, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - } - - - } - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) - Tx.VideoSource = (eVst)inputSelector; - - // NOTE: It's possible that this particular TX model may not like the AudioSource property being set. - // The SIMPL definition only shows a single analog for AudioVideo Source - if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) - //it doesn't - Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); - //Tx.AudioSource = (eAst)inputSelector; - } - - void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) - { - Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); - - switch (args.EventId) - { - case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: - case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: - case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); - HdcpStateFeedback.FireUpdate(); - break; - case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); - break; - } + }; + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.DisplayPortInput.InputStreamChange += DisplayPortInputStreamChange; + Tx.BaseEvent += Tx_BaseEvent; + Tx.OnlineStatusChange += Tx_OnlineStatusChange; + + VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpStateFeedback = + new IntFeedback( + () => + tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback + ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback + : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); + + Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); + + DisplayPortVideoSyncFeedback = new BoolFeedback(() => (bool)tx.DisplayPortInput.SyncDetectedFeedback.BoolValue); + + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + return ActualActiveVideoInput == eVst.Hdmi2 ? tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString() : ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + return ActualActiveVideoInput == eVst.Vga ? tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString() : ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Vga + && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, + Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback, DisplayPortVideoSyncFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + void DisplayPortInputStreamChange(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + switch (args.EventId) + { + case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: + DisplayPortVideoSyncFeedback.FireUpdate(); + break; + } + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); + Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); + + if (Hdmi1VideoSyncFeedback != null) + { + Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); + } + if (Hdmi2VideoSyncFeedback != null) + { + Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); + } + if (DisplayPortVideoSyncFeedback != null) + { + DisplayPortVideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input3VideoSyncStatus.JoinNumber]); + } + + LinkDmTxToApi(this, trilist, joinMap, bridge); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(DisplayPortIn.Selector, null, type); + break; + } + case 4: + { + ExecuteSwitch(eVst.AllDisabled, null, type); + break; + } + default: + { + Debug.Console(2, this, "Unable to execute numeric switch to input {0}", input); + break; + } + + } + + + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + try + { + Debug.Console(2, this, "Attempting to switch InputSelector {0}", ((eVst)inputSelector).ToString()); + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + + // NOTE: It's possible that this particular TX model may not like the AudioSource property being set. + // The SIMPL definition only shows a single analog for AudioVideo Source + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + //it doesn't + Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); + //Tx.AudioSource = (eAst)inputSelector; + } + catch (Exception e) + { + Debug.Console(2, this, "Exception in ExecuteSwitch: {0}", e); + } + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + switch (args.EventId) + { + case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: + case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: + case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + HdcpStateFeedback.FireUpdate(); + break; + case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); + break; + } } void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) @@ -356,57 +370,57 @@ namespace PepperDash.Essentials.DM OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio)); break; } - } - - /// - /// Relays the input stream change to the appropriate RoutingInputPort. - /// - void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) return; - inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); - } - - /// - /// Relays the VideoAttributes change to a RoutingInputPort - /// - void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds - //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", - // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); - switch (eventId) - { - case VideoAttributeEventIds.HdcpActiveFeedbackEventId: - inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HdcpStateFeedbackEventId: - inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: - case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - } - } - - - #region IIROutputPorts Members - public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } - public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } - #endregion - - #region IComPorts Members - public CrestronCollection ComPorts { get { return Tx.ComPorts; } } - public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } - #endregion - } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) return; + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs index fd80ac1f..4d8e41f6 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs @@ -122,7 +122,9 @@ namespace PepperDash.Essentials.DM return new DmTx4kz302CController(key, name, new DmTx4kz302C(chassis.Inputs[num])); if (typeName.StartsWith("dmtx401")) return new DmTx401CController(key, name, new DmTx401C(chassis.Inputs[num])); - } + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(chassis.Inputs[num])); + } else { if (typeName.StartsWith("dmtx200")) @@ -145,7 +147,9 @@ namespace PepperDash.Essentials.DM return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, chassis.Inputs[num])); if (typeName.StartsWith("dmtx401")) return new DmTx401CController(key, name, new DmTx401C(ipid, chassis.Inputs[num])); - } + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(ipid, chassis.Inputs[num])); + } } catch (Exception e) { @@ -355,7 +359,7 @@ namespace PepperDash.Essentials.DM public DmTxControllerFactory() { TypeNames = new List() { "dmtx200c", "dmtx201c", "dmtx201s", "dmtx4k100c", "dmtx4k202c", "dmtx4kz202c", "dmtx4k302c", "dmtx4kz302c", - "dmtx401c", "dmtx401s", "dmtx4k100c1g", "dmtx4kz100c1g" }; + "dmtx401c", "dmtx401s", "dmtx4k100c1g", "dmtx4kz100c1g", "hdbasettx" }; } public override EssentialsDevice BuildDevice(DeviceConfig dc) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs new file mode 100644 index 00000000..800da2a9 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.DM +{ + /// + /// Controller class for suitable for HDBaseT transmitters + /// + [Description("Wrapper Class for HDBaseT devices based on HDTx3CB class")] + public class HDBaseTTxController: BasicDmTxControllerBase, IRoutingInputsOutputs, IComPorts + { + public RoutingInputPort HdmiIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + + public HDBaseTTxController(string key, string name, HDTx3CB tx) + : base(key, name, tx) + { + HdmiIn = new RoutingInputPort(DmPortName.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this) { Port = tx }; + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.DmCat, null, this); + + InputPorts = new RoutingPortCollection { HdmiIn }; + OutputPorts = new RoutingPortCollection { DmOut }; + } + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + #endregion + + #region IRoutingOutputs Members + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IComPorts Members + + public CrestronCollection ComPorts { get { return (Hardware as HDTx3CB).ComPorts; } } + public int NumberOfComPorts { get { return (Hardware as HDTx3CB).NumberOfComPorts; } } + + #endregion + + #region CrestronBridgeableBaseDevice abstract overrides + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HDBaseTTxControllerJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + this.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + + } + + #endregion + } + + public class HDBaseTTxControllerJoinMap : JoinMapBaseAdvanced + { + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "HDBaseT device online feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + /// + /// Plugin device BridgeJoinMap constructor + /// + /// This will be the join it starts on the EISC bridge + public HDBaseTTxControllerJoinMap(uint joinStart) + : base(joinStart, typeof(HDBaseTTxControllerJoinMap)) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 69045ea5..63b20c98 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -65,12 +65,12 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -79,7 +79,7 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False @@ -108,6 +108,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index 54d37613..bfee822e 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -69,12 +69,12 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -83,7 +83,7 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs index 97aacb2d..2746a04a 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/SetTopBox/IRSetTopBoxBase.cs @@ -54,7 +54,7 @@ namespace PepperDash.Essentials.Devices.Common KeypadAccessoryButton1Label = "-"; HasKeypadAccessoryButton2 = true; - KeypadAccessoryButton2Command = "NumericEnter"; + KeypadAccessoryButton2Command = "KEYPAD_ENTER"; KeypadAccessoryButton2Label = "Enter"; AnyVideoOut = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index 0977dea2..8765b0e1 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -1,1257 +1,1279 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharp.Ssh; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharp; -using PepperDash.Core; -using PepperDash.Core.Intersystem; -using PepperDash.Core.Intersystem.Tokens; -using PepperDash.Core.WebApi.Presets; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.Devices; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common.Cameras; -using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using PepperDash_Essentials_Core.Bridges.JoinMaps; -using PepperDash_Essentials_Core.DeviceTypeInterfaces; -using Feedback = PepperDash.Essentials.Core.Feedback; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec -{ - public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced - { - private const int XSigEncoding = 28591; - private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); - protected VideoCodecBase(DeviceConfig config) - : base(config) - { - - StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); - PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); - VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); - MuteFeedback = new BoolFeedback(MuteFeedbackFunc); - SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); - SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); - - ActiveCalls = new List(); - } - - public IBasicCommunication Communication { get; protected set; } - - /// - /// An internal pseudo-source that is routable and connected to the osd input - /// - public DummyRoutingInputsDevice OsdSource { get; protected set; } - - public BoolFeedback StandbyIsOnFeedback { get; private set; } - - protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } - protected abstract Func VolumeLevelFeedbackFunc { get; } - protected abstract Func MuteFeedbackFunc { get; } - protected abstract Func StandbyIsOnFeedbackFunc { get; } - - public List ActiveCalls { get; set; } - - public bool ShowSelfViewByDefault { get; protected set; } - - protected bool SupportsCameraOff; - protected bool SupportsCameraAutoMode; - - public bool IsReady { get; protected set; } - - public virtual List Feedbacks - { - get - { - return new List - { - PrivacyModeIsOnFeedback, - SharingSourceFeedback - }; - } - } - - protected abstract Func SharingSourceFeedbackFunc { get; } - protected abstract Func SharingContentIsOnFeedbackFunc { get; } - - #region ICodecAudio Members - - public abstract void PrivacyModeOn(); - public abstract void PrivacyModeOff(); - public abstract void PrivacyModeToggle(); - public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } - - - public BoolFeedback MuteFeedback { get; private set; } - - public abstract void MuteOff(); - - public abstract void MuteOn(); - - public abstract void SetVolume(ushort level); - - public IntFeedback VolumeLevelFeedback { get; private set; } - - public abstract void MuteToggle(); - - public abstract void VolumeDown(bool pressRelease); - - - public abstract void VolumeUp(bool pressRelease); - - #endregion - - #region IHasContentSharing Members - - public abstract void StartSharing(); - public abstract void StopSharing(); - - public bool AutoShareContentWhileInCall { get; protected set; } - - public StringFeedback SharingSourceFeedback { get; private set; } - public BoolFeedback SharingContentIsOnFeedback { get; private set; } - - #endregion - - #region IHasDialer Members - - /// - /// Fires when the status of any active, dialing, or incoming call changes or is new - /// - public event EventHandler CallStatusChange; - - /// - /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected - /// - public bool IsInCall - { - get - { - var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); - return value; - } - } - - public abstract void Dial(string number); - public abstract void EndCall(CodecActiveCallItem call); - public abstract void EndAllCalls(); - public abstract void AcceptCall(CodecActiveCallItem call); - public abstract void RejectCall(CodecActiveCallItem call); - public abstract void SendDtmf(string s); - - #endregion - - #region IRoutingInputsOutputs Members - - public RoutingPortCollection InputPorts { get; private set; } - - public RoutingPortCollection OutputPorts { get; private set; } - - #endregion - - #region IUsageTracking Members - - /// - /// This object can be added by outside users of this class to provide usage tracking - /// for various services - /// - public UsageTracking UsageTracker { get; set; } - - #endregion - - #region iVideoCodecInfo Members - - public VideoCodecInfo CodecInfo { get; protected set; } - - #endregion - - public event EventHandler IsReadyChange; - public abstract void Dial(Meeting meeting); - - public virtual void Dial(IInvitableContact contact) - { - } - - public abstract void ExecuteSwitch(object selector); - - /// - /// Helper method to fire CallStatusChange event with old and new status - /// - protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) - { - call.Status = newStatus; - - OnCallStatusChange(call); - } - - /// - /// - /// - /// - /// - /// - protected virtual void OnCallStatusChange(CodecActiveCallItem item) - { - var handler = CallStatusChange; - if (handler != null) - { - handler(this, new CodecCallStatusItemChangeEventArgs(item)); - } - - if (AutoShareContentWhileInCall) - { - StartSharing(); - } - - if (UsageTracker != null) - { - if (IsInCall && !UsageTracker.UsageTrackingStarted) - { - UsageTracker.StartDeviceUsage(); - } - else if (UsageTracker.UsageTrackingStarted && !IsInCall) - { - UsageTracker.EndDeviceUsage(); - } - } - } - - /// - /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. - /// - protected void SetIsReady() - { - CrestronInvoke.BeginInvoke( (o) => - { - try - { - IsReady = true; - var h = IsReadyChange; - if (h != null) - { - h(this, new EventArgs()); - } - } - catch (Exception e) - { - Debug.Console(2, this, "Error in SetIsReady() : {0}", e); - } - }); - } - - // **** DEBUGGING THINGS **** - /// - /// - /// - public virtual void ListCalls() - { - var sb = new StringBuilder(); - foreach (var c in ActiveCalls) - { - sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); - } - Debug.Console(1, this, "\n{0}\n", sb.ToString()); - } - - public abstract void StandbyActivate(); - - public abstract void StandbyDeactivate(); - - #region Implementation of IBridgeAdvanced - - public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); - - protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, - EiscApiAdvanced bridge) - { - var joinMap = new VideoCodecControllerJoinMap(joinStart); - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - Debug.Console(1, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); - - - - LinkVideoCodecDtmfToApi(trilist, joinMap); - - LinkVideoCodecCallControlsToApi(trilist, joinMap); - - LinkVideoCodecContentSharingToApi(trilist, joinMap); - - LinkVideoCodecPrivacyToApi(trilist, joinMap); - - LinkVideoCodecVolumeToApi(trilist, joinMap); - - if (codec is ICommunicationMonitor) - { - LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); - } - - if (codec is IHasCodecCameras) - { - LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); - } - - if (codec is IHasCodecSelfView) - { - LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); - } - - if (codec is IHasCameraAutoMode) - { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); - LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); - } - - if (codec is IHasCameraOff) - { - trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); - LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); - } - - if (codec is IHasCodecLayouts) - { - LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); - } - - if (codec is IHasSelfviewPosition) - { - LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); - } - - if (codec is IHasDirectory) - { - LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); - } - - if (codec is IHasScheduleAwareness) - { - LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); - } - - if (codec is IHasParticipants) - { - LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); - } - - if (codec is IHasFarEndContentStatus) - { - (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); - } - - if (codec is IHasPhoneDialing) - { - LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); - } - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - if (codec is IHasDirectory) - { - (codec as IHasDirectory).SetCurrentDirectoryToRoot(); - } - - if (codec is IHasScheduleAwareness) - { - (codec as IHasScheduleAwareness).GetSchedule(); - } - - if (codec is IHasParticipants) - { - UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); - } - - if (codec is IHasCameraAutoMode) - { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); - - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCodecSelfView) - { - (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraAutoMode) - { - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraOff) - { - (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); - } - - if (codec is IHasPhoneDialing) - { - (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); - } - - SharingContentIsOnFeedback.FireUpdate(); - - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - }; - } - - private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, - () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); - - trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); - } - - private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); - - codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); - } - - private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); - - codec.CameraIsOffFeedback.OutputChange += (o, a) => - { - if (a.BoolValue) - { - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - - var autoCodec = codec as IHasCameraAutoMode; - - if (autoCodec == null) return; - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - }; - - if (codec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - - var autoModeCodec = codec as IHasCameraAutoMode; - - if (autoModeCodec == null) return; - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); - } - - private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); - MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); - trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); - trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); - - VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); - - trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); - trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); - - trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); - - } - - private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); - PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); - trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); - trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); - } - - private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - } - - private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.Participants.ParticipantsListHasChanged += (sender, args) => - { - string participantsXSig; - - if (codec.Participants.CurrentParticipants.Count == 0) - { - participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); - return; - } - - participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); - - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort) codec.Participants.CurrentParticipants.Count); - }; - } - - private string UpdateParticipantsXSig(List currentParticipants) - { - const int maxParticipants = 50; - const int maxDigitals = 5; - const int maxStrings = 1; - const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings * maxParticipants; //15 - var stringIndex = 0; - var meetingIndex = 0; - - var tokenArray = new XSigToken[maxParticipants * offset]; - - foreach (var participant in currentParticipants) - { - if (meetingIndex >= maxParticipants * offset) break; - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - while (meetingIndex < maxParticipants*offset) - { - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); - SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); - - SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); - trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); - - trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b); - } - - private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); - - trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => - { - codec.CodecSchedule.MeetingWarningMinutes = i; - }); - - codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); - - codec.CodecSchedule.MeetingEventChange += - (sender, args) => - { - if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) - { - UpdateMeetingsList(codec, trilist, joinMap); - } - }; - } - - private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - var currentTime = DateTime.Now; - var currentMeetings = - codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); - - var meetingsData = UpdateMeetingsListXSig(currentMeetings); - - trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); - - trilist.SetSigFalseAction(joinMap.DialMeeting1.JoinNumber, () => - { - if(codec.CodecSchedule.Meetings[0] != null) - Dial(codec.CodecSchedule.Meetings[0]); - }); - trilist.SetSigFalseAction(joinMap.DialMeeting2.JoinNumber, () => - { - if (codec.CodecSchedule.Meetings[1] != null) - Dial(codec.CodecSchedule.Meetings[1]); - }); - trilist.SetSigFalseAction(joinMap.DialMeeting3.JoinNumber, () => - { - if (codec.CodecSchedule.Meetings[2] != null) - Dial(codec.CodecSchedule.Meetings[2]); - }); - - trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)currentMeetings.Count); - } - - private string UpdateMeetingsListXSig(List meetings) - { - const int maxMeetings = 3; - const int maxDigitals = 2; - const int maxStrings = 7; - const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings*maxMeetings; //15 - var stringIndex = 0; - var meetingIndex = 0; - - var tokenArray = new XSigToken[maxMeetings*offset]; - /* - * Digitals - * IsJoinable - 1 - * IsDialable - 2 - * - * Serials - * Organizer - 1 - * Title - 2 - * Start Date - 3 - * Start Time - 4 - * End Date - 5 - * End Time - 6 - */ - - - foreach(var meeting in meetings) - { - var currentTime = DateTime.Now; - - if(meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; - - if (meetingIndex >= maxMeetings*offset) - { - Debug.Console(2, this, "Max Meetings reached"); - break;} - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToShortDateString()); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToShortTimeString()); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToShortDateString()); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToShortTimeString()); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); - - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - while (meetingIndex < maxMeetings*offset) - { - Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", - meetingIndex, maxMeetings*offset); - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( - trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); - - trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); - - trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); - - trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); - - codec.DirectoryResultReturned += (sender, args) => - { - trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort) args.Directory.CurrentDirectoryResults.Count); - - var clearBytes = XSigHelpers.ClearOutputs(); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); - }; - } - - private void SelectDirectoryEntry(IHasDirectory codec, ushort i) - { - var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; - - if (entry is DirectoryFolder) - { - codec.GetDirectoryFolderContents(entry.FolderId); - return; - } - - var dialableEntry = entry as IInvitableContact; - - if (dialableEntry != null) - { - Dial(dialableEntry); - return; - } - - var entryToDial = entry as DirectoryContact; - - if (entryToDial == null) return; - - Dial(entryToDial.ContactMethods[0].Number); - } - - private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) - { - var contactIndex = 1; - var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; - - foreach(var entry in directory.CurrentDirectoryResults) - { - var arrayIndex = contactIndex - 1; - - if (entry is DirectoryFolder && entry.ParentFolderId == "root") - { - tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); - - contactIndex++; - - continue; - } - - if(isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; - - tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); - - contactIndex++; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, - () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); - - //End All calls for now - trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); - - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - CallStatusChange += (sender, args) => - { - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - Debug.Console(1, this, "Call Direction: {0}", args.CallItem.Direction); - Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); - trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status != eCodecCallStatus.Disconnected); - - if (args.CallItem.Direction == eCodecCallDirection.Incoming) - { - trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); - trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); - } - - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - }; - } - - private string UpdateCallStatusXSig() - { - const int maxCalls = 8; - const int maxStrings = 5; - const int offset = 6; - var stringIndex = 0; - var digitalIndex = maxStrings * maxCalls; - var arrayIndex = 0; - - var tokenArray = new XSigToken[maxCalls*offset]; //set array size for number of calls * pieces of info - - foreach (var call in ActiveCalls) - { - if (arrayIndex >= maxCalls * offset) - break; - //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); - - //serials - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); - tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); - tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex++; - } - while (digitalIndex < maxCalls) - { - //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); - - //serials - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty); - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex++; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0")); - trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1")); - trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2")); - trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3")); - trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4")); - trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5")); - trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6")); - trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7")); - trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8")); - trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9")); - trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*")); - trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#")); - } - - private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); - - codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); - } - - private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); - trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); - - codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => - { - var offCodec = codec as IHasCameraOff; - - if (offCodec != null) - { - if (offCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - }; - - var offModeCodec = codec as IHasCameraOff; - - if (offModeCodec != null) - { - if (offModeCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - } - - private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, - VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); - - codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); - } - - private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - //Camera PTZ - trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.TiltUp(); - else camera.TiltStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.TiltDown(); - else camera.TiltStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.PanLeft(); - else camera.PanStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.PanRight(); - else camera.PanStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.ZoomIn(); - else camera.ZoomStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.ZoomOut(); - else camera.ZoomStop(); - }); - - //Camera Select - trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => - { - if (codec.SelectedCamera == null) return; - - codec.SelectCamera(codec.Cameras[i].Key); - }); - - codec.CameraSelected += (sender, args) => - { - var i = (ushort) codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); - - if (codec is IHasCodecRoomPresets) - { - return; - } - - if (!(args.SelectedCamera is IHasCameraPresets)) - { - return; - } - - var cam = args.SelectedCamera as IHasCameraPresets; - SetCameraPresetNames(cam.Presets); - - (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); - - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, - (a) => - { - cam.PresetSelect(a); - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); - }); - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, - String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - }; - - if (!(codec is IHasCodecRoomPresets)) return; - - var presetCodec = codec as IHasCodecRoomPresets; - - presetCodec.CodecRoomPresetsListHasChanged += - (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); - - //Camera Presets - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => - { - presetCodec.CodecRoomPresetSelect(i); - - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); - }); - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - presetCodec.CodecRoomPresetStore( - trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(ICollection presets) - { - var i = 1; //start index for xsig; - - var tokenArray = new XSigToken[presets.Count]; - - foreach (var preset in presets) - { - var cameraPreset = new XSigSerialToken(i, preset); - tokenArray[i - 1] = cameraPreset; - i++; - } - - return GetXSigString(tokenArray); - } - - private string GetXSigString(XSigToken[] tokenArray) - { - string returnString; - using (var s = new MemoryStream()) - { - using (var tw = new XSigTokenStreamWriter(s, true)) - { - tw.WriteXSigData(tokenArray); - } - - var xSig = s.ToArray(); - - returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); - } - - return returnString; - } - - #endregion - } - - - /// - /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info - /// - public class CodecPhonebookSyncState : IKeyed - { - private bool _InitialSyncComplete; - - public CodecPhonebookSyncState(string key) - { - Key = key; - - CodecDisconnected(); - } - - public bool InitialSyncComplete - { - get { return _InitialSyncComplete; } - private set - { - if (value == true) - { - var handler = InitialSyncCompleted; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - _InitialSyncComplete = value; - } - } - - public bool InitialPhonebookFoldersWasReceived { get; private set; } - - public bool NumberOfContactsWasReceived { get; private set; } - - public bool PhonebookRootEntriesWasRecieved { get; private set; } - - public bool PhonebookHasFolders { get; private set; } - - public int NumberOfContacts { get; private set; } - - #region IKeyed Members - - public string Key { get; private set; } - - #endregion - - public event EventHandler InitialSyncCompleted; - - public void InitialPhonebookFoldersReceived() - { - InitialPhonebookFoldersWasReceived = true; - - CheckSyncStatus(); - } - - public void PhonebookRootEntriesReceived() - { - PhonebookRootEntriesWasRecieved = true; - - CheckSyncStatus(); - } - - public void SetPhonebookHasFolders(bool value) - { - PhonebookHasFolders = value; - - Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); - } - - public void SetNumberOfContacts(int contacts) - { - NumberOfContacts = contacts; - NumberOfContactsWasReceived = true; - - Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); - - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - InitialPhonebookFoldersWasReceived = false; - PhonebookHasFolders = false; - NumberOfContacts = 0; - NumberOfContactsWasReceived = false; - } - - private void CheckSyncStatus() - { - if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) - { - InitialSyncComplete = true; - Debug.Console(1, this, "Initial Phonebook Sync Complete!"); - } - else - { - InitialSyncComplete = false; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Ssh; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Core.Intersystem; +using PepperDash.Core.Intersystem.Tokens; +using PepperDash.Core.WebApi.Presets; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.Bridges.JoinMaps; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; +using Feedback = PepperDash.Essentials.Core.Feedback; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced + { + private const int XSigEncoding = 28591; + private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); + protected VideoCodecBase(DeviceConfig config) + : base(config) + { + + StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); + PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); + VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); + MuteFeedback = new BoolFeedback(MuteFeedbackFunc); + SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); + SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + ActiveCalls = new List(); + } + + public IBasicCommunication Communication { get; protected set; } + + /// + /// An internal pseudo-source that is routable and connected to the osd input + /// + public DummyRoutingInputsDevice OsdSource { get; protected set; } + + public BoolFeedback StandbyIsOnFeedback { get; private set; } + + protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } + protected abstract Func VolumeLevelFeedbackFunc { get; } + protected abstract Func MuteFeedbackFunc { get; } + protected abstract Func StandbyIsOnFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public bool ShowSelfViewByDefault { get; protected set; } + + protected bool SupportsCameraOff; + protected bool SupportsCameraAutoMode; + + public bool IsReady { get; protected set; } + + public virtual List Feedbacks + { + get + { + return new List + { + PrivacyModeIsOnFeedback, + SharingSourceFeedback + }; + } + } + + protected abstract Func SharingSourceFeedbackFunc { get; } + protected abstract Func SharingContentIsOnFeedbackFunc { get; } + + #region ICodecAudio Members + + public abstract void PrivacyModeOn(); + public abstract void PrivacyModeOff(); + public abstract void PrivacyModeToggle(); + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + + public BoolFeedback MuteFeedback { get; private set; } + + public abstract void MuteOff(); + + public abstract void MuteOn(); + + public abstract void SetVolume(ushort level); + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public abstract void MuteToggle(); + + public abstract void VolumeDown(bool pressRelease); + + + public abstract void VolumeUp(bool pressRelease); + + #endregion + + #region IHasContentSharing Members + + public abstract void StartSharing(); + public abstract void StopSharing(); + + public bool AutoShareContentWhileInCall { get; protected set; } + + public StringFeedback SharingSourceFeedback { get; private set; } + public BoolFeedback SharingContentIsOnFeedback { get; private set; } + + #endregion + + #region IHasDialer Members + + /// + /// Fires when the status of any active, dialing, or incoming call changes or is new + /// + public event EventHandler CallStatusChange; + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); + return value; + } + } + + public abstract void Dial(string number); + public abstract void EndCall(CodecActiveCallItem call); + public abstract void EndAllCalls(); + public abstract void AcceptCall(CodecActiveCallItem call); + public abstract void RejectCall(CodecActiveCallItem call); + public abstract void SendDtmf(string s); + + #endregion + + #region IRoutingInputsOutputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + #region iVideoCodecInfo Members + + public VideoCodecInfo CodecInfo { get; protected set; } + + #endregion + + public event EventHandler IsReadyChange; + public abstract void Dial(Meeting meeting); + + public virtual void Dial(IInvitableContact contact) + { + } + + public abstract void ExecuteSwitch(object selector); + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + } + + /// + /// + /// + /// + /// + /// + protected virtual void OnCallStatusChange(CodecActiveCallItem item) + { + var handler = CallStatusChange; + if (handler != null) + { + handler(this, new CodecCallStatusItemChangeEventArgs(item)); + } + + if (AutoShareContentWhileInCall) + { + StartSharing(); + } + + if (UsageTracker != null) + { + if (IsInCall && !UsageTracker.UsageTrackingStarted) + { + UsageTracker.StartDeviceUsage(); + } + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + { + UsageTracker.EndDeviceUsage(); + } + } + } + + /// + /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. + /// + protected void SetIsReady() + { + CrestronInvoke.BeginInvoke((o) => + { + try + { + IsReady = true; + var h = IsReadyChange; + if (h != null) + { + h(this, new EventArgs()); + } + } + catch (Exception e) + { + Debug.Console(2, this, "Error in SetIsReady() : {0}", e); + } + }); + } + + // **** DEBUGGING THINGS **** + /// + /// + /// + public virtual void ListCalls() + { + var sb = new StringBuilder(); + foreach (var c in ActiveCalls) + { + sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); + } + Debug.Console(1, this, "\n{0}\n", sb.ToString()); + } + + public abstract void StandbyActivate(); + + public abstract void StandbyDeactivate(); + + #region Implementation of IBridgeAdvanced + + public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); + + protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new VideoCodecControllerJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + Debug.Console(1, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); + + + + LinkVideoCodecDtmfToApi(trilist, joinMap); + + LinkVideoCodecCallControlsToApi(trilist, joinMap); + + LinkVideoCodecContentSharingToApi(trilist, joinMap); + + LinkVideoCodecPrivacyToApi(trilist, joinMap); + + LinkVideoCodecVolumeToApi(trilist, joinMap); + + if (codec is ICommunicationMonitor) + { + LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); + } + + if (codec is IHasCodecCameras) + { + LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); + } + + if (codec is IHasCodecSelfView) + { + LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); + LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); + } + + if (codec is IHasCameraOff) + { + trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); + LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); + } + + if (codec is IHasCodecLayouts) + { + LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); + } + + if (codec is IHasSelfviewPosition) + { + LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); + } + + if (codec is IHasDirectory) + { + LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); + } + + if (codec is IHasScheduleAwareness) + { + LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); + } + + if (codec is IHasParticipants) + { + LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); + } + + if (codec is IHasFarEndContentStatus) + { + (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); + } + + if (codec is IHasPhoneDialing) + { + LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + if (codec is IHasDirectory) + { + (codec as IHasDirectory).SetCurrentDirectoryToRoot(); + } + + if (codec is IHasScheduleAwareness) + { + (codec as IHasScheduleAwareness).GetSchedule(); + } + + if (codec is IHasParticipants) + { + UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); + + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCodecSelfView) + { + (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraAutoMode) + { + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraOff) + { + (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); + } + + if (codec is IHasPhoneDialing) + { + (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); + } + + SharingContentIsOnFeedback.FireUpdate(); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, + () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); + + trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); + } + + private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); + + codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); + } + + private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); + + codec.CameraIsOffFeedback.OutputChange += (o, a) => + { + if (a.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoCodec = codec as IHasCameraAutoMode; + + if (autoCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + }; + + if (codec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoModeCodec = codec as IHasCameraAutoMode; + + if (autoModeCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + } + + private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); + MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); + trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); + trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); + + VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); + + trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); + trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); + + trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); + + } + + private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); + PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); + trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); + trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); + } + + private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + } + + private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.Participants.ParticipantsListHasChanged += (sender, args) => + { + string participantsXSig; + + if (codec.Participants.CurrentParticipants.Count == 0) + { + participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + return; + } + + participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); + + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + }; + } + + private string UpdateParticipantsXSig(List currentParticipants) + { + const int maxParticipants = 50; + const int maxDigitals = 5; + const int maxStrings = 1; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * maxParticipants; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxParticipants * offset]; + + foreach (var participant in currentParticipants) + { + if (meetingIndex >= maxParticipants * offset) break; + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxParticipants * offset) + { + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); + SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); + + SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); + trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); + + trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b); + } + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + private List _currentMeetings = new List(); + + private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); + + trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => + { + codec.CodecSchedule.MeetingWarningMinutes = i; + }); + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting1.JoinNumber, () => + { + var mtg = 1; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting1.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting2.JoinNumber, () => + { + var mtg = 2; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting2.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting3.JoinNumber, () => + { + var mtg = 3; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting3.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + + codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); + codec.CodecSchedule.MeetingEventChange += (sender, args) => + { + if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) + { + UpdateMeetingsList(codec, trilist, joinMap); + } + }; + } + + private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + var currentTime = DateTime.Now; + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + // - changed var currentMeetings >> field _currentMeetings + //_currentMeetings.Clear(); + _currentMeetings = codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + // - moved the trilist.SetSigFlaseAction(joinMap.DialMeeting1..3.JoinNumber) lambda's to LinkVideoCodecScheduleToApi + + var meetingsData = UpdateMeetingsListXSig(_currentMeetings); + trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); + trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count); + } + + private string UpdateMeetingsListXSig(List meetings) + { + const int maxMeetings = 3; + const int maxDigitals = 2; + const int maxStrings = 7; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * maxMeetings; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxMeetings * offset]; + /* + * Digitals + * IsJoinable - 1 + * IsDialable - 2 + * + * Serials + * Organizer - 1 + * Title - 2 + * Start Date - 3 + * Start Time - 4 + * End Date - 5 + * End Time - 6 + * Id - 7 + */ + + + foreach (var meeting in meetings) + { + var currentTime = DateTime.Now; + + if (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; + + if (meetingIndex >= maxMeetings * offset) + { + Debug.Console(2, this, "Max Meetings reached"); + break; + } + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToShortDateString()); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToShortTimeString()); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToShortDateString()); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToShortTimeString()); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); + + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxMeetings * offset) + { + Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", + meetingIndex, maxMeetings * offset); + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( + trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); + + trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); + + codec.DirectoryResultReturned += (sender, args) => + { + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)args.Directory.CurrentDirectoryResults.Count); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + }; + } + + private void SelectDirectoryEntry(IHasDirectory codec, ushort i) + { + var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + + if (entry is DirectoryFolder) + { + codec.GetDirectoryFolderContents(entry.FolderId); + return; + } + + var dialableEntry = entry as IInvitableContact; + + if (dialableEntry != null) + { + Dial(dialableEntry); + return; + } + + var entryToDial = entry as DirectoryContact; + + if (entryToDial == null) return; + + Dial(entryToDial.ContactMethods[0].Number); + } + + private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) + { + var contactIndex = 1; + var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; + + foreach (var entry in directory.CurrentDirectoryResults) + { + var arrayIndex = contactIndex - 1; + + if (entry is DirectoryFolder && entry.ParentFolderId == "root") + { + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); + + contactIndex++; + + continue; + } + + if (isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; + + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); + + contactIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, + () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); + + //End All calls for now + trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + CallStatusChange += (sender, args) => + { + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + Debug.Console(1, this, "Call Direction: {0}", args.CallItem.Direction); + Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); + trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing); + + if (args.CallItem.Direction == eCodecCallDirection.Incoming) + { + trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); + trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); + } + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private string UpdateCallStatusXSig() + { + const int maxCalls = 8; + const int maxStrings = 5; + const int offset = 6; + var stringIndex = 0; + var digitalIndex = maxStrings * maxCalls; + var arrayIndex = 0; + + var tokenArray = new XSigToken[maxCalls * offset]; //set array size for number of calls * pieces of info + + foreach (var call in ActiveCalls) + { + if (arrayIndex >= maxCalls * offset) + break; + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); + tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); + tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + while (digitalIndex < maxCalls) + { + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0")); + trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1")); + trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2")); + trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3")); + trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4")); + trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5")); + trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6")); + trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7")); + trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8")); + trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9")); + trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*")); + trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#")); + } + + private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); + + codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); + } + + private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); + trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); + + codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => + { + var offCodec = codec as IHasCameraOff; + + if (offCodec != null) + { + if (offCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + }; + + var offModeCodec = codec as IHasCameraOff; + + if (offModeCodec != null) + { + if (offModeCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + } + + private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, + VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); + + codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); + } + + private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + //Camera PTZ + trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltUp(); + else camera.TiltStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltDown(); + else camera.TiltStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanLeft(); + else camera.PanStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanRight(); + else camera.PanStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomIn(); + else camera.ZoomStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomOut(); + else camera.ZoomStop(); + }); + + //Camera Select + trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => + { + if (codec.SelectedCamera == null) return; + + codec.SelectCamera(codec.Cameras[i].Key); + }); + + codec.CameraSelected += (sender, args) => + { + var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); + + if (codec is IHasCodecRoomPresets) + { + return; + } + + if (!(args.SelectedCamera is IHasCameraPresets)) + { + return; + } + + var cam = args.SelectedCamera as IHasCameraPresets; + SetCameraPresetNames(cam.Presets); + + (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); + + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, + (a) => + { + cam.PresetSelect(a); + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, + String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + }; + + if (!(codec is IHasCodecRoomPresets)) return; + + var presetCodec = codec as IHasCodecRoomPresets; + + presetCodec.CodecRoomPresetsListHasChanged += + (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); + + //Camera Presets + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => + { + presetCodec.CodecRoomPresetSelect(i); + + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + presetCodec.CodecRoomPresetStore( + trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(ICollection presets) + { + var i = 1; //start index for xsig; + + var tokenArray = new XSigToken[presets.Count]; + + foreach (var preset in presets) + { + var cameraPreset = new XSigSerialToken(i, preset); + tokenArray[i - 1] = cameraPreset; + i++; + } + + return GetXSigString(tokenArray); + } + + private string GetXSigString(XSigToken[] tokenArray) + { + string returnString; + using (var s = new MemoryStream()) + { + using (var tw = new XSigTokenStreamWriter(s, true)) + { + tw.WriteXSigData(tokenArray); + } + + var xSig = s.ToArray(); + + returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); + } + + return returnString; + } + + #endregion + } + + + /// + /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info + /// + public class CodecPhonebookSyncState : IKeyed + { + private bool _InitialSyncComplete; + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + private void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Phonebook Sync Complete!"); + } + else + { + InitialSyncComplete = false; + } + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index 4f4eddbc..36cce89f 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -1,1335 +1,1335 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using Crestron.SimplSharp; - -using PepperDash.Core; -using PepperDash.Essentials.Devices.Common.Codec; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom -{ - public enum eZoomRoomResponseType - { - zEvent, - zStatus, - zConfiguration, - zCommand - } - - public abstract class NotifiableObject : INotifyPropertyChanged - { - #region INotifyPropertyChanged Members - - public event PropertyChangedEventHandler PropertyChanged; - - protected void NotifyPropertyChanged(string propertyName) - { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - } - - #endregion - } - - /// - /// Used to track the current status of a ZoomRoom - /// - public class ZoomRoomStatus - { - public zStatus.Login Login { get; set; } - public zStatus.SystemUnit SystemUnit { get; set; } - public zStatus.Phonebook Phonebook { get; set; } - public zStatus.Call Call { get; set; } - public zStatus.Capabilities Capabilities { get; set; } - public zStatus.Sharing Sharing { get; set; } - public zStatus.NumberOfScreens NumberOfScreens { get; set; } - public zStatus.Layout Layout { get; set; } - public zStatus.Video Video { get; set; } - public zStatus.CameraShare CameraShare { get; set; } - public List AudioInputs { get; set; } - public List AudioOuputs { get; set; } - public List Cameras { get; set; } - public zEvent.PhoneCallStatus PhoneCall { get; set; } - - public ZoomRoomStatus() - { - Login = new zStatus.Login(); - SystemUnit = new zStatus.SystemUnit(); - Phonebook = new zStatus.Phonebook(); - Call = new zStatus.Call(); - Capabilities = new zStatus.Capabilities(); - Sharing = new zStatus.Sharing(); - NumberOfScreens = new zStatus.NumberOfScreens(); - Layout = new zStatus.Layout(); - Video = new zStatus.Video(); - CameraShare = new zStatus.CameraShare(); - AudioInputs = new List(); - AudioOuputs = new List(); - Cameras = new List(); - PhoneCall = new zEvent.PhoneCallStatus(); - } - } - - /// - /// Used to track the current configuration of a ZoomRoom - /// - public class ZoomRoomConfiguration - { - public zConfiguration.Call Call { get; set; } - public zConfiguration.Audio Audio { get; set; } - public zConfiguration.Video Video { get; set; } - public zConfiguration.Client Client { get; set; } - public zConfiguration.Camera Camera { get; set; } - - public ZoomRoomConfiguration() - { - Call = new zConfiguration.Call(); - Audio = new zConfiguration.Audio(); - Video = new zConfiguration.Video(); - Client = new zConfiguration.Client(); - Camera = new zConfiguration.Camera(); - } - } - - /// - /// Represents a response from a ZoomRoom system - /// - public class Response - { - public Status Status { get; set; } - public bool Sync { get; set; } - [JsonProperty("topKey")] - public string TopKey { get; set; } - [JsonProperty("type")] - public string Type { get; set; } - - public Response() - { - Status = new Status(); - } - } - - public class Status - { - [JsonProperty("message")] - public string Message { get; set; } - [JsonProperty("state")] - public string State { get; set; } - } - - - /// - /// zStatus class stucture - /// - public class zStatus - { - public class Login - { - [JsonProperty("ZAAPI Release")] - public string ZAAPIRelease { get; set; } - [JsonProperty("Zoom Room Release")] - public string ZoomRoomRelease { get; set; } - } - - public class SystemUnit - { - [JsonProperty("email")] - public string Email { get; set; } - [JsonProperty("login_type")] - public string LoginType { get; set; } - [JsonProperty("meeting_number")] - public string MeetingNumber { get; set; } - [JsonProperty("platform")] - public string Platform { get; set; } - [JsonProperty("room_info")] - public RoomInfo RoomInfo { get; set; } - [JsonProperty("room_version")] - public string RoomVersion { get; set; } - - public SystemUnit() - { - RoomInfo = new RoomInfo(); - } - } - - public class RoomInfo - { - [JsonProperty("account_email")] - public string AccountEmail { get; set; } - [JsonProperty("display_version")] - public string DisplayVersion { get; set; } - [JsonProperty("is_auto_answer_enabled")] - public bool AutoAnswerIsEnabled { get; set; } - [JsonProperty("is_auto_answer_selected")] - public bool AutoAnswerIsSelected { get; set; } - [JsonProperty("room_name")] - public string RoomName { get; set; } - } - - public class CloudPbxInfo - { - [JsonProperty("company_number")] - public string CompanyNumber { get; set; } - [JsonProperty("extension")] - public string Extension { get; set; } - [JsonProperty("isValid")] - public bool IsValid { get; set; } - } - - public enum ePresence - { - PRESENCE_OFFLINE, - PRESENCE_ONLINE, - PRESENCE_AWAY, - PRESENCE_BUSY, - PRESENCE_DND - } - - public class Contact - { - [JsonProperty("avatarURL")] - public string AvatarURL { get; set; } - [JsonProperty("cloud_pbx_info")] - public CloudPbxInfo CloudPbxInfo { get; set; } - [JsonProperty("email")] - public string Email { get; set; } - [JsonProperty("firstName")] - public string FirstName { get; set; } - [JsonProperty("index")] - public int Index { get; set; } - [JsonProperty("isLegacy")] - public bool IsLegacy { get; set; } - [JsonProperty("isZoomRoom")] - public bool IsZoomRoom { get; set; } - [JsonProperty("jid")] - public string Jid { get; set; } - [JsonProperty("lastName")] - public string LastName { get; set; } - [JsonProperty("onDesktop")] - public bool OnDesktop { get; set; } - [JsonProperty("onMobile")] - public bool OnMobile { get; set; } - [JsonProperty("phoneNumber")] - public string PhoneNumber { get; set; } - [JsonProperty("presence")] - public ePresence Presence { get; set; } - [JsonProperty("presence_status")] - public int PresenceStatus { get; set; } - [JsonProperty("screenName")] - public string ScreenName { get; set; } - [JsonProperty("sip_phone_number")] - public string SipPhoneNumber { get; set; } - - - public Contact() - { - CloudPbxInfo = new CloudPbxInfo(); - } - } - - /// - /// Used to be able to inplement IInvitableContact on DirectoryContact - /// - public class ZoomDirectoryContact : DirectoryContact, IInvitableContact - { - - } - - public class Phonebook - { - [JsonProperty("Contacts")] - public List Contacts { get; set; } - - public Phonebook() - { - Contacts = new List(); - } - - /// - /// Converts from zStatus.Contact types to generic directory items - /// - /// - public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) - { - var directory = new CodecDirectory(); - - var folders = new List(); - - var roomFolder = new DirectoryFolder(); - - var contactFolder = new DirectoryFolder(); - - var contacts = new List(); - - // Check if there are any zoom rooms - var zoomRooms = zoomContacts.FindAll(c => c.IsZoomRoom); - - if (zoomRooms.Count > 0) - { - // If so, setup a rooms and contacts folder and add them. - roomFolder.Name = "Rooms"; - roomFolder.ParentFolderId = "root"; - roomFolder.FolderId = "rooms"; - - contactFolder.Name = "Contacts"; - contactFolder.ParentFolderId = "root"; - contactFolder.FolderId = "contacts"; - - folders.Add(roomFolder); - folders.Add(contactFolder); - - directory.AddFoldersToDirectory(folders); - } - - try - { - if (zoomContacts.Count == 0) return directory; - { - foreach (Contact c in zoomContacts) - { - var contact = new ZoomDirectoryContact {Name = c.ScreenName, ContactId = c.Jid}; - - if (folders.Count > 0) - { - contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; - } - - contacts.Add(contact); - } - - directory.AddContactsToDirectory(contacts); - } - } - catch (Exception e) - { - Debug.Console(1, "Error converting Zoom Phonebook results to generic: {0}", e); - } - - return directory; - } - } - - public enum eCallStatus - { - UNKNOWN, - NOT_IN_MEETING, - CONNECTING_MEETING, - IN_MEETING, - LOGGED_OUT - } - - public class ClosedCaption - { - public bool Available { get; set; } - } - - public class Call : NotifiableObject - { - private eCallStatus _status; - private List _participants; - - public bool IsInCall; - - public eCallStatus Status - { - get - { - return _status; - } - set - { - if (value != _status) - { - _status = value; - IsInCall = _status == eCallStatus.IN_MEETING || _status == eCallStatus.CONNECTING_MEETING; - NotifyPropertyChanged("Status"); - } - } - } - public ClosedCaption ClosedCaption { get; set; } - public List Participants - { - get - { - return _participants; - } - set - { - _participants = value; - NotifyPropertyChanged("Participants"); - } - } - public zEvent.SharingState Sharing { get; set; } - - public CallRecordInfo CallRecordInfo { get; set; } - - private zCommand.InfoResult _info; - - public zCommand.InfoResult Info - { - get - { - return _info; - } - set - { - _info = value; - NotifyPropertyChanged("Info"); - } - } - - public Call() - { - ClosedCaption = new ClosedCaption(); - Participants = new List(); - Sharing = new zEvent.SharingState(); - CallRecordInfo = new CallRecordInfo(); - Info = new zCommand.InfoResult(); - } - } - - public class Capabilities - { - public bool aec_Setting_Stored_In_ZR { get; set; } - public bool can_Dtmf_For_Invite_By_Phone { get; set; } - public bool can_Mute_On_Entry { get; set; } - public bool can_Ringing_In_Pstn_Call { get; set; } - public bool can_Switch_To_Specific_Camera { get; set; } - public bool is_Airhost_Disabled { get; set; } - public bool pstn_Call_In_Local_resentation { get; set; } - public bool support_Claim_Host { get; set; } - public bool support_Out_Room_Display { get; set; } - public bool support_Pin_And_Spotlight { get; set; } - public bool supports_Audio_Checkup { get; set; } - public bool supports_CheckIn { get; set; } - public bool supports_Cloud_PBX { get; set; } - public bool supports_Encrypted_Connection { get; set; } - public bool supports_Expel_User_Permanently { get; set; } - public bool supports_H323_DTMF { get; set; } - public bool supports_Hdmi_Cec_Control { get; set; } - public bool supports_Highly_Reverberant_Room { get; set; } - public bool supports_Loading_Contacts_Dynamically { get; set; } - public bool supports_Loading_Participants_Dynamically { get; set; } - public bool supports_Mic_Record_Test { get; set; } - public bool supports_Multi_Share { get; set; } - public bool supports_ShareCamera { get; set; } - public bool supports_Share_For_Floating_And_Content_Only { get; set; } - public bool supports_Sip_Call_out { get; set; } - public bool supports_Software_Audio_Processing { get; set; } - public bool supports_Web_Settings_Push { get; set; } - } - - public class Sharing : NotifiableObject - { - private string _dispState; - private string _password; - - public string directPresentationPairingCode { get; set; } - /// - /// Laptop client sharing key - /// - public string directPresentationSharingKey { get; set; } - public string dispState - { - get - { - return _dispState; - } - set - { - if (value != _dispState) - { - _dispState = value; - NotifyPropertyChanged("dispState"); - } - } - } - public bool isAirHostClientConnected { get; set; } - public bool isBlackMagicConnected { get; set; } - public bool isBlackMagicDataAvailable { get; set; } - public bool isDirectPresentationConnected { get; set; } - public bool isSharingBlackMagic { get; set; } - /// - /// IOS Airplay code - /// - public string password - { - get - { - return _password; - } - set - { - if (value != _password) - { - _password = value; - NotifyPropertyChanged("password"); - } - } - } - public string serverName { get; set; } - public string wifiName { get; set; } - } - - public class NumberOfScreens - { - [JsonProperty("NumberOfCECScreens")] - public int NumOfCECScreens { get; set; } - [JsonProperty("NumberOfScreens")] - public int NumOfScreens { get; set; } - } - - /// - /// AudioInputLine/AudioOutputLine/VideoCameraLine list item - /// - public class AudioVideoInputOutputLineItem - { - public string Alias { get; set; } - public string Name { get; set; } - public bool Selected { get; set; } - public bool combinedDevice { get; set; } - public string id { get; set; } - public bool manuallySelected { get; set; } - public int numberOfCombinedDevices { get; set; } - public int ptzComId { get; set; } - } - - public class Video - { - public bool Optimizable { get; set; } - } - - public class CameraShare : NotifiableObject - { - private bool _canControlCamera; - private bool _isSharing; - - [JsonProperty("can_Control_Camera")] - public bool CanControlCamera - { - get - { - return _canControlCamera; - } - set - { - if (value != _canControlCamera) - { - _canControlCamera = value; - NotifyPropertyChanged("CanControlCamera"); - } - } - } - public string id { get; set; } - public bool is_Mirrored { get; set; } - [JsonProperty("is_Sharing")] - public bool IsSharing - { - get - { - return _isSharing; - } - set - { - if (value != _isSharing) - { - _isSharing = value; - NotifyPropertyChanged("IsSharing"); - } - } - } - public int pan_Tilt_Speed { get; set; } - - } - - public class Layout - { - public bool can_Adjust_Floating_Video { get; set; } - public bool can_Switch_Floating_Share_Content { get; set; } - public bool can_Switch_Share_On_All_Screens { get; set; } - public bool can_Switch_Speaker_View { get; set; } - public bool can_Switch_Wall_View { get; set; } - public bool is_In_First_Page { get; set; } - public bool is_In_Last_Page { get; set; } - public bool is_supported { get; set; } - public int video_Count_In_Current_Page { get; set; } - public string video_type { get; set; } - } - - public class CallRecordInfo - { - public bool canRecord { get; set; } - public bool emailRequired { get; set; } - public bool amIRecording { get; set; } - public bool meetingIsBeingRecorded { get; set; } - } - } - - /// - /// zEvent Class Structure - /// - public class zEvent - { - public class NeedWaitForHost - { - public bool Wait { get; set; } - } - - public class IncomingCallIndication - { - public string callerJID { get; set; } - public string calleeJID { get; set; } - public string meetingID { get; set; } - public string password { get; set; } - public string meetingOption { get; set; } - public long MeetingNumber { get; set; } - public string callerName { get; set; } - public string avatarURL { get; set; } - public int lifeTime { get; set; } - public bool accepted { get; set; } - } - - public class CallConnectError - { - public int error_code { get; set; } - public string error_message { get; set; } - } - - public class CallDisconnect - { - public bool Successful - { - get - { - return success == "on"; - } - } - - public string success { get; set; } - } - - public class Layout - { - public bool Sharethumb { get; set; } - } - - public class Call - { - public Layout Layout { get; set; } - } - - public class Client - { - public Call Call { get; set; } - } - - public enum eSharingState - { - None, - Connecting, - Sending, - Receiving, - Send_Receiving - } - - public class SharingState : NotifiableObject - { - private bool _paused; - private eSharingState _state; - - public bool IsSharing; - - [JsonProperty("paused")] - public bool Paused - { - get - { - return _paused; - } - set - { - if (value != _paused) - { - _paused = value; - NotifyPropertyChanged("Paused"); - } - } - } - [JsonProperty("state")] - public eSharingState State - { - get - { - return _state; - } - set - { - if (value != _state) - { - _state = value; - IsSharing = _state == eSharingState.Sending; - NotifyPropertyChanged("State"); - } - } - } - } - - public class PinStatusOfScreenNotification - { - [JsonProperty("can_be_pinned")] - public bool CanBePinned { get; set; } - [JsonProperty("can_pin_share")] - public bool CanPinShare { get; set; } - [JsonProperty("pinned_share_source_id")] - public int PinnedShareSourceId { get; set; } - [JsonProperty("pinned_user_id")] - public int PinnedUserId { get; set; } - [JsonProperty("screen_index")] - public int ScreenIndex { get; set; } - [JsonProperty("screen_layout")] - public int ScreenLayout { get; set; } - [JsonProperty("share_source_type")] - public int ShareSourceType { get; set; } - [JsonProperty("why_cannot_pin_share")] - public string WhyCannotPinShare { get; set; } - } - - public class PhoneCallStatus:NotifiableObject - { - private bool _isIncomingCall; - private string _peerDisplayName; - private string _peerNumber; - - private bool _offHook; - - public string CallId { get; set; } - public bool IsIncomingCall { - get { return _isIncomingCall; } - set - { - if(value == _isIncomingCall) return; - - _isIncomingCall = value; - NotifyPropertyChanged("IsIncomingCall"); - } } - - public string PeerDisplayName - { - get { return _peerDisplayName; } - set - { - if (value == _peerDisplayName) return; - _peerDisplayName = value; - NotifyPropertyChanged("PeerDisplayName"); - } - } - - public string PeerNumber - { - get { return _peerNumber; } - set - { - if (value == _peerNumber) return; - - _peerNumber = value; - NotifyPropertyChanged("PeerNumber"); - } - } - - public string PeerUri { get; set; } - - private ePhoneCallStatus _status; - public ePhoneCallStatus Status - { - get { return _status; } - set - { - _status = value; - OffHook = _status == ePhoneCallStatus.PhoneCallStatus_Accepted || - _status == ePhoneCallStatus.PhoneCallStatus_InCall || - _status == ePhoneCallStatus.PhoneCallStatus_Init || - _status == ePhoneCallStatus.PhoneCallStatus_Ringing; - } - } - - public bool OffHook - { - get { return _offHook; } - set - { - if (value == _offHook) return; - - _offHook = value; - NotifyPropertyChanged("OffHook"); - } - } - } - - public enum ePhoneCallStatus - { - PhoneCallStatus_Ringing, - PhoneCallStatus_Terminated, - PhoneCallStatus_Accepted, - PhoneCallStatus_InCall, - PhoneCallStatus_Init, - } - } - - /// - /// zConfiguration class structure - /// - public class zConfiguration - { - public class Sharing - { - [JsonProperty("optimize_video_sharing")] - public bool OptimizeVideoSharing { get; set; } - } - - public class Camera : NotifiableObject - { - private bool _mute; - - public bool Mute - { - get { return _mute; } - set - { - Debug.Console(1, "Camera Mute response received: {0}", value); - - if (value == _mute) return; - - _mute = value; - NotifyPropertyChanged("Mute"); - } - } - } - - public class Microphone : NotifiableObject - { - private bool _mute; - - public bool Mute - { - get - { - return _mute; - } - set - { - if(value != _mute) - { - _mute = value; - NotifyPropertyChanged("Mute"); - } - } - } - } - - public enum eLayoutStyle - { - Gallery, - Speaker, - Strip, - ShareAll - } - - public enum eLayoutSize - { - Off, - Size1, - Size2, - Size3, - Strip - } - - public enum eLayoutPosition - { - Center, - Up, - Right, - UpRight, - Down, - DownRight, - Left, - UpLeft, - DownLeft - } - - public class Layout:NotifiableObject - { - public bool ShareThumb { get; set; } - public eLayoutStyle Style { get; set; } - public eLayoutSize Size { get; set; } - - private eLayoutPosition _position; - public eLayoutPosition Position { - get { return _position; } - set - { - _position = value; - NotifyPropertyChanged("Position"); - } } - } - - public class Lock - { - public bool Enable { get; set; } - } - - public class ClosedCaption - { - public bool Visible { get; set; } - public int FontSize { get; set; } - } - - public class MuteUserOnEntry - { - public bool Enable { get; set; } - } - - public class Call - { - public Sharing Sharing { get; set; } - public Camera Camera { get; set; } - public Microphone Microphone { get; set; } - public Layout Layout { get; set; } - public Lock Lock { get; set; } - public MuteUserOnEntry MuteUserOnEntry { get; set; } - public ClosedCaption ClosedCaption { get; set; } - - - public Call() - { - Sharing = new Sharing(); - Camera = new Camera(); - Microphone = new Microphone(); - Layout = new Layout(); - Lock = new Lock(); - MuteUserOnEntry = new MuteUserOnEntry(); - ClosedCaption = new ClosedCaption(); - } - } - - public class Audio - { - public Input Input { get; set; } - public Output Output { get; set; } - - public Audio() - { - Input = new Input(); - Output = new Output(); - } - } - - public class Input : Output - { - [JsonProperty("reduce_reverb")] - public bool ReduceReverb { get; set; } - } - - public class Output : NotifiableObject - { - private int _volume; - - [JsonProperty("volume")] - public int Volume - { - get - { - return _volume; - } - set - { - if (value != _volume) - { - _volume = value; - NotifyPropertyChanged("Volume"); - } - } - } - [JsonProperty("selectedId")] - public string SelectedId { get; set; } - [JsonProperty("is_sap_disabled")] - public bool IsSapDisabled { get; set; } - } - - public class Video : NotifiableObject - { - private bool _hideConfSelfVideo; - - [JsonProperty("hide_conf_self_video")] - public bool HideConfSelfVideo - { - get - { - return _hideConfSelfVideo; - } - set - { - if (value != _hideConfSelfVideo) - { - _hideConfSelfVideo = value; - NotifyPropertyChanged("HideConfSelfVideo"); - } - } - } - - public VideoCamera Camera { get; set; } - - public Video() - { - Camera = new VideoCamera(); - } - } - - public class VideoCamera : NotifiableObject - { - private string _selectedId; - - [JsonProperty("selectedId")] - public string SelectedId { - get - { - return _selectedId; - } - set - { - if (value != _selectedId) - { - _selectedId = value; - NotifyPropertyChanged("SelectedId"); - } - } - - } - public bool Mirror { get; set; } - } - - public class Client - { - public string appVersion { get; set; } - public string deviceSystem { get; set; } - } - - } - - /// - /// zCommand class structure - /// - public class zCommand - { - public class BookingsListResult - { - [JsonProperty("accessRole")] - public string AccessRole { get; set; } - [JsonProperty("calendarChangeKey")] - public string CalendarChangeKey { get; set; } - [JsonProperty("calendarID")] - public string CalendarId { get; set; } - [JsonProperty("checkIn")] - public bool CheckIn { get; set; } - [JsonProperty("creatorEmail")] - public string CreatorEmail { get; set; } - [JsonProperty("creatorName")] - public string CreatorName { get; set; } - [JsonProperty("endTime")] - public DateTime EndTime { get; set; } - [JsonProperty("hostName")] - public string HostName { get; set; } - [JsonProperty("isInstantMeeting")] - public bool IsInstantMeeting { get; set; } - [JsonProperty("isPrivate")] - public bool IsPrivate { get; set; } - [JsonProperty("location")] - public string Location { get; set; } - [JsonProperty("meetingName")] - public string MeetingName { get; set; } - [JsonProperty("meetingNumber")] - public string MeetingNumber { get; set; } - [JsonProperty("scheduledFrom")] - public string ScheduledFrom { get; set; } - [JsonProperty("startTime")] - public DateTime StartTime { get; set; } - [JsonProperty("third_party")] - public ThirdParty ThirdParty { get; set; } - } - - public static List GetGenericMeetingsFromBookingResult(List bookings, - int minutesBeforeMeetingStart) - { - var rv = GetGenericMeetingsFromBookingResult(bookings); - - foreach (var meeting in rv) - { - meeting.MinutesBeforeMeeting = minutesBeforeMeetingStart; - } - - return rv; - } - /// - /// Extracts the necessary meeting values from the Zoom bookings response and converts them to the generic class - /// - /// - /// - public static List GetGenericMeetingsFromBookingResult(List bookings) - { - var meetings = new List(); - - if (Debug.Level > 0) - { - Debug.Console(1, "Meetings List:\n"); - } - - foreach (var b in bookings) - { - var meeting = new Meeting(); - - if (b.MeetingNumber != null) - meeting.Id = b.MeetingNumber; - if (b.CreatorName != null) - meeting.Organizer = b.CreatorName; - if (b.MeetingName != null) - meeting.Title = b.MeetingName; - //if (b.Agenda != null) - // meeting.Agenda = b.Agenda.Value; - if (b.StartTime != null) - meeting.StartTime = b.StartTime; - if (b.EndTime != null) - meeting.EndTime = b.EndTime; - - meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; - - // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. - var now = DateTime.Now; - if (meeting.StartTime < now && meeting.EndTime < now) - { - Debug.Console(1, "Skipping meeting {0}. Meeting is in the past.", meeting.Title); - continue; - } - - meetings.Add(meeting); - - if (Debug.Level > 0) - { - Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}", meeting.Title, meeting.Id, meeting.Organizer); - Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); - Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); - } - } - - meetings.OrderBy(m => m.StartTime); - - return meetings; - } - - public class HandStatus - { - [JsonProperty("is_raise_hand")] - public bool IsRaiseHand { get; set; } - [JsonProperty("optimize_vis_validideo_sharing")] - public string IsValid { get; set; } - [JsonProperty("time_stamp")] - public string TimeStamp { get; set; } - } - - public class ListParticipant - { - [JsonProperty("audio_status state")] - public string AudioStatusState { get; set; } - [JsonProperty("audio_status type")] - public string AudioStatusType { get; set; } - [JsonProperty("avatar_url")] - public string AvatarUrl { get; set; } - [JsonProperty("camera_status am_i_controlling")] - public bool CameraStatusAmIControlling { get; set; } - [JsonProperty("camera_status can_i_request_control")] - public bool CameraStatusCanIRequestConrol { get; set; } - [JsonProperty("camera_status can_move_camera")] - public bool CameraStatusCanMoveCamera { get; set; } - [JsonProperty("camera_status can_switch_camera")] - public bool CameraStatusCanSwitchCamera { get; set; } - [JsonProperty("camera_status can_zoom_camera")] - public bool CameraStatusCanZoomCamera { get; set; } - [JsonProperty("can_edit_closed_caption")] - public bool CanEditClosedCaption { get; set; } - [JsonProperty("can_record")] - public bool CanRecord { get; set; } - [JsonProperty("event")] - public string Event { get; set; } - [JsonProperty("hand_status")] - public HandStatus HandStatus { get; set; } - [JsonProperty("isCohost")] - public bool IsCohost { get; set; } - [JsonProperty("is_client_support_closed_caption")] - public bool IsClientSupportClosedCaption { get; set; } - [JsonProperty("is_client_support_coHost")] - public bool IsClientSupportCoHost { get; set; } - [JsonProperty("is_host")] - public bool IsHost { get; set; } - [JsonProperty("is_myself")] - public bool IsMyself { get; set; } - [JsonProperty("is_recording")] - public bool IsRecording { get; set; } - [JsonProperty("is_video_can_mute_byHost")] - public bool IsVideoCanMuteByHost { get; set; } - [JsonProperty("is_video_can_unmute_byHost")] - public bool IsVideoCanUnmuteByHost { get; set; } - [JsonProperty("local_recording_disabled")] - public bool LocalRecordingDisabled { get; set; } - [JsonProperty("user_id")] - public int UserId { get; set; } - [JsonProperty("user_name")] - public string UserName { get; set; } - [JsonProperty("user_type")] - public string UserType { get; set; } - [JsonProperty("video_status has_source")] - public bool VideoStatusHasSource { get; set; } - [JsonProperty("video_status is_receiving")] - public bool VideoStatusIsReceiving { get; set; } - [JsonProperty("video_status is_sending")] - public bool VideoStatusIsSending { get; set; } - - public ListParticipant() - { - HandStatus = new HandStatus(); - } - - public static List GetGenericParticipantListFromParticipantsResult( - List participants) - { - return - participants.Select( - p => - new Participant - { - Name = p.UserName, - IsHost = p.IsHost, - CanMuteVideo = p.IsVideoCanMuteByHost, - CanUnmuteVideo = p.IsVideoCanUnmuteByHost, - AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", - VideoMuteFb = p.VideoStatusIsSending - }).ToList(); - } - } - - public class CallinCountryList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class CalloutCountryList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class TollFreeCallinList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class Info - { - public List callin_country_list { get; set; } - public List callout_country_list { get; set; } - public List toll_free_callin_list { get; set; } - } - - public class ThirdParty - { - public string h323_address { get; set; } - public string meeting_number { get; set; } - public string service_provider { get; set; } - public string sip_address { get; set; } - } - - public class MeetingListItem - { - public string accessRole { get; set; } - public string calendarChangeKey { get; set; } - public string calendarID { get; set; } - public bool checkIn { get; set; } - public string creatorEmail { get; set; } - public string creatorName { get; set; } - public string endTime { get; set; } - public string hostName { get; set; } - public bool isInstantMeeting { get; set; } - public bool isPrivate { get; set; } - public string location { get; set; } - public string meetingName { get; set; } - public string meetingNumber { get; set; } - public string scheduledFrom { get; set; } - public string startTime { get; set; } - public ThirdParty third_party { get; set; } - - public MeetingListItem() - { - third_party = new ThirdParty(); - } - } - - public class InfoResult - { - public Info Info { get; set; } - public bool am_i_original_host { get; set; } - public string default_callin_country { get; set; } - public string dialIn { get; set; } - public string international_url { get; set; } - public string invite_email_content { get; set; } - public string invite_email_subject { get; set; } - public bool is_callin_country_list_available { get; set; } - public bool is_calling_room_system_enabled { get; set; } - public bool is_toll_free_callin_list_available { get; set; } - public bool is_view_only { get; set; } - public bool is_waiting_room { get; set; } - public bool is_webinar { get; set; } - public string meeting_id { get; set; } - public MeetingListItem meeting_list_item { get; set; } - public string meeting_password { get; set; } - public string meeting_type { get; set; } - public int my_userid { get; set; } - public int participant_id { get; set; } - public string real_meeting_id { get; set; } - public string schedule_option { get; set; } - public string schedule_option2 { get; set; } - public string support_callout_type { get; set; } - public string toll_free_number { get; set; } - public string user_type { get; set; } - - public InfoResult() - { - Info = new Info(); - meeting_list_item = new MeetingListItem(); - } - } - - public class Phonebook - { - public List Contacts { get; set; } - public int Limit { get; set; } - public int Offset { get; set; } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public enum eZoomRoomResponseType + { + zEvent, + zStatus, + zConfiguration, + zCommand + } + + public abstract class NotifiableObject : INotifyPropertyChanged + { + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + #endregion + } + + /// + /// Used to track the current status of a ZoomRoom + /// + public class ZoomRoomStatus + { + public zStatus.Login Login { get; set; } + public zStatus.SystemUnit SystemUnit { get; set; } + public zStatus.Phonebook Phonebook { get; set; } + public zStatus.Call Call { get; set; } + public zStatus.Capabilities Capabilities { get; set; } + public zStatus.Sharing Sharing { get; set; } + public zStatus.NumberOfScreens NumberOfScreens { get; set; } + public zStatus.Layout Layout { get; set; } + public zStatus.Video Video { get; set; } + public zStatus.CameraShare CameraShare { get; set; } + public List AudioInputs { get; set; } + public List AudioOuputs { get; set; } + public List Cameras { get; set; } + public zEvent.PhoneCallStatus PhoneCall { get; set; } + + public ZoomRoomStatus() + { + Login = new zStatus.Login(); + SystemUnit = new zStatus.SystemUnit(); + Phonebook = new zStatus.Phonebook(); + Call = new zStatus.Call(); + Capabilities = new zStatus.Capabilities(); + Sharing = new zStatus.Sharing(); + NumberOfScreens = new zStatus.NumberOfScreens(); + Layout = new zStatus.Layout(); + Video = new zStatus.Video(); + CameraShare = new zStatus.CameraShare(); + AudioInputs = new List(); + AudioOuputs = new List(); + Cameras = new List(); + PhoneCall = new zEvent.PhoneCallStatus(); + } + } + + /// + /// Used to track the current configuration of a ZoomRoom + /// + public class ZoomRoomConfiguration + { + public zConfiguration.Call Call { get; set; } + public zConfiguration.Audio Audio { get; set; } + public zConfiguration.Video Video { get; set; } + public zConfiguration.Client Client { get; set; } + public zConfiguration.Camera Camera { get; set; } + + public ZoomRoomConfiguration() + { + Call = new zConfiguration.Call(); + Audio = new zConfiguration.Audio(); + Video = new zConfiguration.Video(); + Client = new zConfiguration.Client(); + Camera = new zConfiguration.Camera(); + } + } + + /// + /// Represents a response from a ZoomRoom system + /// + public class Response + { + public Status Status { get; set; } + public bool Sync { get; set; } + [JsonProperty("topKey")] + public string TopKey { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + + public Response() + { + Status = new Status(); + } + } + + public class Status + { + [JsonProperty("message")] + public string Message { get; set; } + [JsonProperty("state")] + public string State { get; set; } + } + + + /// + /// zStatus class stucture + /// + public class zStatus + { + public class Login + { + [JsonProperty("ZAAPI Release")] + public string ZAAPIRelease { get; set; } + [JsonProperty("Zoom Room Release")] + public string ZoomRoomRelease { get; set; } + } + + public class SystemUnit + { + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("login_type")] + public string LoginType { get; set; } + [JsonProperty("meeting_number")] + public string MeetingNumber { get; set; } + [JsonProperty("platform")] + public string Platform { get; set; } + [JsonProperty("room_info")] + public RoomInfo RoomInfo { get; set; } + [JsonProperty("room_version")] + public string RoomVersion { get; set; } + + public SystemUnit() + { + RoomInfo = new RoomInfo(); + } + } + + public class RoomInfo + { + [JsonProperty("account_email")] + public string AccountEmail { get; set; } + [JsonProperty("display_version")] + public string DisplayVersion { get; set; } + [JsonProperty("is_auto_answer_enabled")] + public bool AutoAnswerIsEnabled { get; set; } + [JsonProperty("is_auto_answer_selected")] + public bool AutoAnswerIsSelected { get; set; } + [JsonProperty("room_name")] + public string RoomName { get; set; } + } + + public class CloudPbxInfo + { + [JsonProperty("company_number")] + public string CompanyNumber { get; set; } + [JsonProperty("extension")] + public string Extension { get; set; } + [JsonProperty("isValid")] + public bool IsValid { get; set; } + } + + public enum ePresence + { + PRESENCE_OFFLINE, + PRESENCE_ONLINE, + PRESENCE_AWAY, + PRESENCE_BUSY, + PRESENCE_DND + } + + public class Contact + { + [JsonProperty("avatarURL")] + public string AvatarURL { get; set; } + [JsonProperty("cloud_pbx_info")] + public CloudPbxInfo CloudPbxInfo { get; set; } + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("firstName")] + public string FirstName { get; set; } + [JsonProperty("index")] + public int Index { get; set; } + [JsonProperty("isLegacy")] + public bool IsLegacy { get; set; } + [JsonProperty("isZoomRoom")] + public bool IsZoomRoom { get; set; } + [JsonProperty("jid")] + public string Jid { get; set; } + [JsonProperty("lastName")] + public string LastName { get; set; } + [JsonProperty("onDesktop")] + public bool OnDesktop { get; set; } + [JsonProperty("onMobile")] + public bool OnMobile { get; set; } + [JsonProperty("phoneNumber")] + public string PhoneNumber { get; set; } + [JsonProperty("presence")] + public ePresence Presence { get; set; } + [JsonProperty("presence_status")] + public int PresenceStatus { get; set; } + [JsonProperty("screenName")] + public string ScreenName { get; set; } + [JsonProperty("sip_phone_number")] + public string SipPhoneNumber { get; set; } + + + public Contact() + { + CloudPbxInfo = new CloudPbxInfo(); + } + } + + /// + /// Used to be able to inplement IInvitableContact on DirectoryContact + /// + public class ZoomDirectoryContact : DirectoryContact, IInvitableContact + { + + } + + public class Phonebook + { + [JsonProperty("Contacts")] + public List Contacts { get; set; } + + public Phonebook() + { + Contacts = new List(); + } + + /// + /// Converts from zStatus.Contact types to generic directory items + /// + /// + public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) + { + var directory = new CodecDirectory(); + + var folders = new List(); + + var roomFolder = new DirectoryFolder(); + + var contactFolder = new DirectoryFolder(); + + var contacts = new List(); + + // Check if there are any zoom rooms + var zoomRooms = zoomContacts.FindAll(c => c.IsZoomRoom); + + if (zoomRooms.Count > 0) + { + // If so, setup a rooms and contacts folder and add them. + roomFolder.Name = "Rooms"; + roomFolder.ParentFolderId = "root"; + roomFolder.FolderId = "rooms"; + + contactFolder.Name = "Contacts"; + contactFolder.ParentFolderId = "root"; + contactFolder.FolderId = "contacts"; + + folders.Add(roomFolder); + folders.Add(contactFolder); + + directory.AddFoldersToDirectory(folders); + } + + try + { + if (zoomContacts.Count == 0) return directory; + { + foreach (Contact c in zoomContacts) + { + var contact = new ZoomDirectoryContact {Name = c.ScreenName, ContactId = c.Jid}; + + if (folders.Count > 0) + { + contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; + } + + contacts.Add(contact); + } + + directory.AddContactsToDirectory(contacts); + } + } + catch (Exception e) + { + Debug.Console(1, "Error converting Zoom Phonebook results to generic: {0}", e); + } + + return directory; + } + } + + public enum eCallStatus + { + UNKNOWN, + NOT_IN_MEETING, + CONNECTING_MEETING, + IN_MEETING, + LOGGED_OUT + } + + public class ClosedCaption + { + public bool Available { get; set; } + } + + public class Call : NotifiableObject + { + private eCallStatus _status; + private List _participants; + + public bool IsInCall; + + public eCallStatus Status + { + get + { + return _status; + } + set + { + if (value != _status) + { + _status = value; + IsInCall = _status == eCallStatus.IN_MEETING || _status == eCallStatus.CONNECTING_MEETING; + NotifyPropertyChanged("Status"); + } + } + } + public ClosedCaption ClosedCaption { get; set; } + public List Participants + { + get + { + return _participants; + } + set + { + _participants = value; + NotifyPropertyChanged("Participants"); + } + } + public zEvent.SharingState Sharing { get; set; } + + public CallRecordInfo CallRecordInfo { get; set; } + + private zCommand.InfoResult _info; + + public zCommand.InfoResult Info + { + get + { + return _info; + } + set + { + _info = value; + NotifyPropertyChanged("Info"); + } + } + + public Call() + { + ClosedCaption = new ClosedCaption(); + Participants = new List(); + Sharing = new zEvent.SharingState(); + CallRecordInfo = new CallRecordInfo(); + Info = new zCommand.InfoResult(); + } + } + + public class Capabilities + { + public bool aec_Setting_Stored_In_ZR { get; set; } + public bool can_Dtmf_For_Invite_By_Phone { get; set; } + public bool can_Mute_On_Entry { get; set; } + public bool can_Ringing_In_Pstn_Call { get; set; } + public bool can_Switch_To_Specific_Camera { get; set; } + public bool is_Airhost_Disabled { get; set; } + public bool pstn_Call_In_Local_resentation { get; set; } + public bool support_Claim_Host { get; set; } + public bool support_Out_Room_Display { get; set; } + public bool support_Pin_And_Spotlight { get; set; } + public bool supports_Audio_Checkup { get; set; } + public bool supports_CheckIn { get; set; } + public bool supports_Cloud_PBX { get; set; } + public bool supports_Encrypted_Connection { get; set; } + public bool supports_Expel_User_Permanently { get; set; } + public bool supports_H323_DTMF { get; set; } + public bool supports_Hdmi_Cec_Control { get; set; } + public bool supports_Highly_Reverberant_Room { get; set; } + public bool supports_Loading_Contacts_Dynamically { get; set; } + public bool supports_Loading_Participants_Dynamically { get; set; } + public bool supports_Mic_Record_Test { get; set; } + public bool supports_Multi_Share { get; set; } + public bool supports_ShareCamera { get; set; } + public bool supports_Share_For_Floating_And_Content_Only { get; set; } + public bool supports_Sip_Call_out { get; set; } + public bool supports_Software_Audio_Processing { get; set; } + public bool supports_Web_Settings_Push { get; set; } + } + + public class Sharing : NotifiableObject + { + private string _dispState; + private string _password; + + public string directPresentationPairingCode { get; set; } + /// + /// Laptop client sharing key + /// + public string directPresentationSharingKey { get; set; } + public string dispState + { + get + { + return _dispState; + } + set + { + if (value != _dispState) + { + _dispState = value; + NotifyPropertyChanged("dispState"); + } + } + } + public bool isAirHostClientConnected { get; set; } + public bool isBlackMagicConnected { get; set; } + public bool isBlackMagicDataAvailable { get; set; } + public bool isDirectPresentationConnected { get; set; } + public bool isSharingBlackMagic { get; set; } + /// + /// IOS Airplay code + /// + public string password + { + get + { + return _password; + } + set + { + if (value != _password) + { + _password = value; + NotifyPropertyChanged("password"); + } + } + } + public string serverName { get; set; } + public string wifiName { get; set; } + } + + public class NumberOfScreens + { + [JsonProperty("NumberOfCECScreens")] + public int NumOfCECScreens { get; set; } + [JsonProperty("NumberOfScreens")] + public int NumOfScreens { get; set; } + } + + /// + /// AudioInputLine/AudioOutputLine/VideoCameraLine list item + /// + public class AudioVideoInputOutputLineItem + { + public string Alias { get; set; } + public string Name { get; set; } + public bool Selected { get; set; } + public bool combinedDevice { get; set; } + public string id { get; set; } + public bool manuallySelected { get; set; } + public int numberOfCombinedDevices { get; set; } + public int ptzComId { get; set; } + } + + public class Video + { + public bool Optimizable { get; set; } + } + + public class CameraShare : NotifiableObject + { + private bool _canControlCamera; + private bool _isSharing; + + [JsonProperty("can_Control_Camera")] + public bool CanControlCamera + { + get + { + return _canControlCamera; + } + set + { + if (value != _canControlCamera) + { + _canControlCamera = value; + NotifyPropertyChanged("CanControlCamera"); + } + } + } + public string id { get; set; } + public bool is_Mirrored { get; set; } + [JsonProperty("is_Sharing")] + public bool IsSharing + { + get + { + return _isSharing; + } + set + { + if (value != _isSharing) + { + _isSharing = value; + NotifyPropertyChanged("IsSharing"); + } + } + } + public int pan_Tilt_Speed { get; set; } + + } + + public class Layout + { + public bool can_Adjust_Floating_Video { get; set; } + public bool can_Switch_Floating_Share_Content { get; set; } + public bool can_Switch_Share_On_All_Screens { get; set; } + public bool can_Switch_Speaker_View { get; set; } + public bool can_Switch_Wall_View { get; set; } + public bool is_In_First_Page { get; set; } + public bool is_In_Last_Page { get; set; } + public bool is_supported { get; set; } + public int video_Count_In_Current_Page { get; set; } + public string video_type { get; set; } + } + + public class CallRecordInfo + { + public bool canRecord { get; set; } + public bool emailRequired { get; set; } + public bool amIRecording { get; set; } + public bool meetingIsBeingRecorded { get; set; } + } + } + + /// + /// zEvent Class Structure + /// + public class zEvent + { + public class NeedWaitForHost + { + public bool Wait { get; set; } + } + + public class IncomingCallIndication + { + public string callerJID { get; set; } + public string calleeJID { get; set; } + public string meetingID { get; set; } + public string password { get; set; } + public string meetingOption { get; set; } + public long MeetingNumber { get; set; } + public string callerName { get; set; } + public string avatarURL { get; set; } + public int lifeTime { get; set; } + public bool accepted { get; set; } + } + + public class CallConnectError + { + public int error_code { get; set; } + public string error_message { get; set; } + } + + public class CallDisconnect + { + public bool Successful + { + get + { + return success == "on"; + } + } + + public string success { get; set; } + } + + public class Layout + { + public bool Sharethumb { get; set; } + } + + public class Call + { + public Layout Layout { get; set; } + } + + public class Client + { + public Call Call { get; set; } + } + + public enum eSharingState + { + None, + Connecting, + Sending, + Receiving, + Send_Receiving + } + + public class SharingState : NotifiableObject + { + private bool _paused; + private eSharingState _state; + + public bool IsSharing; + + [JsonProperty("paused")] + public bool Paused + { + get + { + return _paused; + } + set + { + if (value != _paused) + { + _paused = value; + NotifyPropertyChanged("Paused"); + } + } + } + [JsonProperty("state")] + public eSharingState State + { + get + { + return _state; + } + set + { + if (value != _state) + { + _state = value; + IsSharing = _state == eSharingState.Sending; + NotifyPropertyChanged("State"); + } + } + } + } + + public class PinStatusOfScreenNotification + { + [JsonProperty("can_be_pinned")] + public bool CanBePinned { get; set; } + [JsonProperty("can_pin_share")] + public bool CanPinShare { get; set; } + [JsonProperty("pinned_share_source_id")] + public int PinnedShareSourceId { get; set; } + [JsonProperty("pinned_user_id")] + public int PinnedUserId { get; set; } + [JsonProperty("screen_index")] + public int ScreenIndex { get; set; } + [JsonProperty("screen_layout")] + public int ScreenLayout { get; set; } + [JsonProperty("share_source_type")] + public int ShareSourceType { get; set; } + [JsonProperty("why_cannot_pin_share")] + public string WhyCannotPinShare { get; set; } + } + + public class PhoneCallStatus:NotifiableObject + { + private bool _isIncomingCall; + private string _peerDisplayName; + private string _peerNumber; + + private bool _offHook; + + public string CallId { get; set; } + public bool IsIncomingCall { + get { return _isIncomingCall; } + set + { + if(value == _isIncomingCall) return; + + _isIncomingCall = value; + NotifyPropertyChanged("IsIncomingCall"); + } } + + public string PeerDisplayName + { + get { return _peerDisplayName; } + set + { + if (value == _peerDisplayName) return; + _peerDisplayName = value; + NotifyPropertyChanged("PeerDisplayName"); + } + } + + public string PeerNumber + { + get { return _peerNumber; } + set + { + if (value == _peerNumber) return; + + _peerNumber = value; + NotifyPropertyChanged("PeerNumber"); + } + } + + public string PeerUri { get; set; } + + private ePhoneCallStatus _status; + public ePhoneCallStatus Status + { + get { return _status; } + set + { + _status = value; + OffHook = _status == ePhoneCallStatus.PhoneCallStatus_Accepted || + _status == ePhoneCallStatus.PhoneCallStatus_InCall || + _status == ePhoneCallStatus.PhoneCallStatus_Init || + _status == ePhoneCallStatus.PhoneCallStatus_Ringing; + } + } + + public bool OffHook + { + get { return _offHook; } + set + { + if (value == _offHook) return; + + _offHook = value; + NotifyPropertyChanged("OffHook"); + } + } + } + + public enum ePhoneCallStatus + { + PhoneCallStatus_Ringing, + PhoneCallStatus_Terminated, + PhoneCallStatus_Accepted, + PhoneCallStatus_InCall, + PhoneCallStatus_Init, + } + } + + /// + /// zConfiguration class structure + /// + public class zConfiguration + { + public class Sharing + { + [JsonProperty("optimize_video_sharing")] + public bool OptimizeVideoSharing { get; set; } + } + + public class Camera : NotifiableObject + { + private bool _mute; + + public bool Mute + { + get { return _mute; } + set + { + Debug.Console(1, "Camera Mute response received: {0}", value); + + if (value == _mute) return; + + _mute = value; + NotifyPropertyChanged("Mute"); + } + } + } + + public class Microphone : NotifiableObject + { + private bool _mute; + + public bool Mute + { + get + { + return _mute; + } + set + { + if(value != _mute) + { + _mute = value; + NotifyPropertyChanged("Mute"); + } + } + } + } + + public enum eLayoutStyle + { + Gallery, + Speaker, + Strip, + ShareAll + } + + public enum eLayoutSize + { + Off, + Size1, + Size2, + Size3, + Strip + } + + public enum eLayoutPosition + { + Center, + Up, + Right, + UpRight, + Down, + DownRight, + Left, + UpLeft, + DownLeft + } + + public class Layout:NotifiableObject + { + public bool ShareThumb { get; set; } + public eLayoutStyle Style { get; set; } + public eLayoutSize Size { get; set; } + + private eLayoutPosition _position; + public eLayoutPosition Position { + get { return _position; } + set + { + _position = value; + NotifyPropertyChanged("Position"); + } } + } + + public class Lock + { + public bool Enable { get; set; } + } + + public class ClosedCaption + { + public bool Visible { get; set; } + public int FontSize { get; set; } + } + + public class MuteUserOnEntry + { + public bool Enable { get; set; } + } + + public class Call + { + public Sharing Sharing { get; set; } + public Camera Camera { get; set; } + public Microphone Microphone { get; set; } + public Layout Layout { get; set; } + public Lock Lock { get; set; } + public MuteUserOnEntry MuteUserOnEntry { get; set; } + public ClosedCaption ClosedCaption { get; set; } + + + public Call() + { + Sharing = new Sharing(); + Camera = new Camera(); + Microphone = new Microphone(); + Layout = new Layout(); + Lock = new Lock(); + MuteUserOnEntry = new MuteUserOnEntry(); + ClosedCaption = new ClosedCaption(); + } + } + + public class Audio + { + public Input Input { get; set; } + public Output Output { get; set; } + + public Audio() + { + Input = new Input(); + Output = new Output(); + } + } + + public class Input : Output + { + [JsonProperty("reduce_reverb")] + public bool ReduceReverb { get; set; } + } + + public class Output : NotifiableObject + { + private int _volume; + + [JsonProperty("volume")] + public int Volume + { + get + { + return _volume; + } + set + { + if (value != _volume) + { + _volume = value; + NotifyPropertyChanged("Volume"); + } + } + } + [JsonProperty("selectedId")] + public string SelectedId { get; set; } + [JsonProperty("is_sap_disabled")] + public bool IsSapDisabled { get; set; } + } + + public class Video : NotifiableObject + { + private bool _hideConfSelfVideo; + + [JsonProperty("hide_conf_self_video")] + public bool HideConfSelfVideo + { + get + { + return _hideConfSelfVideo; + } + set + { + if (value != _hideConfSelfVideo) + { + _hideConfSelfVideo = value; + NotifyPropertyChanged("HideConfSelfVideo"); + } + } + } + + public VideoCamera Camera { get; set; } + + public Video() + { + Camera = new VideoCamera(); + } + } + + public class VideoCamera : NotifiableObject + { + private string _selectedId; + + [JsonProperty("selectedId")] + public string SelectedId { + get + { + return _selectedId; + } + set + { + if (value != _selectedId) + { + _selectedId = value; + NotifyPropertyChanged("SelectedId"); + } + } + + } + public bool Mirror { get; set; } + } + + public class Client + { + public string appVersion { get; set; } + public string deviceSystem { get; set; } + } + + } + + /// + /// zCommand class structure + /// + public class zCommand + { + public class BookingsListResult + { + [JsonProperty("accessRole")] + public string AccessRole { get; set; } + [JsonProperty("calendarChangeKey")] + public string CalendarChangeKey { get; set; } + [JsonProperty("calendarID")] + public string CalendarId { get; set; } + [JsonProperty("checkIn")] + public bool CheckIn { get; set; } + [JsonProperty("creatorEmail")] + public string CreatorEmail { get; set; } + [JsonProperty("creatorName")] + public string CreatorName { get; set; } + [JsonProperty("endTime")] + public DateTime EndTime { get; set; } + [JsonProperty("hostName")] + public string HostName { get; set; } + [JsonProperty("isInstantMeeting")] + public bool IsInstantMeeting { get; set; } + [JsonProperty("isPrivate")] + public bool IsPrivate { get; set; } + [JsonProperty("location")] + public string Location { get; set; } + [JsonProperty("meetingName")] + public string MeetingName { get; set; } + [JsonProperty("meetingNumber")] + public string MeetingNumber { get; set; } + [JsonProperty("scheduledFrom")] + public string ScheduledFrom { get; set; } + [JsonProperty("startTime")] + public DateTime StartTime { get; set; } + [JsonProperty("third_party")] + public ThirdParty ThirdParty { get; set; } + } + + public static List GetGenericMeetingsFromBookingResult(List bookings, + int minutesBeforeMeetingStart) + { + var rv = GetGenericMeetingsFromBookingResult(bookings); + + foreach (var meeting in rv) + { + meeting.MinutesBeforeMeeting = minutesBeforeMeetingStart; + } + + return rv; + } + /// + /// Extracts the necessary meeting values from the Zoom bookings response and converts them to the generic class + /// + /// + /// + public static List GetGenericMeetingsFromBookingResult(List bookings) + { + var meetings = new List(); + + if (Debug.Level > 0) + { + Debug.Console(1, "Meetings List:\n"); + } + + foreach (var b in bookings) + { + var meeting = new Meeting(); + + if (b.MeetingNumber != null) + meeting.Id = b.MeetingNumber; + if (b.CreatorName != null) + meeting.Organizer = b.CreatorName; + if (b.MeetingName != null) + meeting.Title = b.MeetingName; + //if (b.Agenda != null) + // meeting.Agenda = b.Agenda.Value; + if (b.StartTime != null) + meeting.StartTime = b.StartTime; + if (b.EndTime != null) + meeting.EndTime = b.EndTime; + + meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; + + // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. + var now = DateTime.Now; + if (meeting.StartTime < now && meeting.EndTime < now) + { + Debug.Console(1, "Skipping meeting {0}. Meeting is in the past.", meeting.Title); + continue; + } + + meetings.Add(meeting); + + if (Debug.Level > 0) + { + Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}", meeting.Title, meeting.Id, meeting.Organizer); + Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); + Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); + } + } + + meetings.OrderBy(m => m.StartTime); + + return meetings; + } + + public class HandStatus + { + [JsonProperty("is_raise_hand")] + public bool IsRaiseHand { get; set; } + [JsonProperty("optimize_vis_validideo_sharing")] + public string IsValid { get; set; } + [JsonProperty("time_stamp")] + public string TimeStamp { get; set; } + } + + public class ListParticipant + { + [JsonProperty("audio_status state")] + public string AudioStatusState { get; set; } + [JsonProperty("audio_status type")] + public string AudioStatusType { get; set; } + [JsonProperty("avatar_url")] + public string AvatarUrl { get; set; } + [JsonProperty("camera_status am_i_controlling")] + public bool CameraStatusAmIControlling { get; set; } + [JsonProperty("camera_status can_i_request_control")] + public bool CameraStatusCanIRequestConrol { get; set; } + [JsonProperty("camera_status can_move_camera")] + public bool CameraStatusCanMoveCamera { get; set; } + [JsonProperty("camera_status can_switch_camera")] + public bool CameraStatusCanSwitchCamera { get; set; } + [JsonProperty("camera_status can_zoom_camera")] + public bool CameraStatusCanZoomCamera { get; set; } + [JsonProperty("can_edit_closed_caption")] + public bool CanEditClosedCaption { get; set; } + [JsonProperty("can_record")] + public bool CanRecord { get; set; } + [JsonProperty("event")] + public string Event { get; set; } + [JsonProperty("hand_status")] + public HandStatus HandStatus { get; set; } + [JsonProperty("isCohost")] + public bool IsCohost { get; set; } + [JsonProperty("is_client_support_closed_caption")] + public bool IsClientSupportClosedCaption { get; set; } + [JsonProperty("is_client_support_coHost")] + public bool IsClientSupportCoHost { get; set; } + [JsonProperty("is_host")] + public bool IsHost { get; set; } + [JsonProperty("is_myself")] + public bool IsMyself { get; set; } + [JsonProperty("is_recording")] + public bool IsRecording { get; set; } + [JsonProperty("is_video_can_mute_byHost")] + public bool IsVideoCanMuteByHost { get; set; } + [JsonProperty("is_video_can_unmute_byHost")] + public bool IsVideoCanUnmuteByHost { get; set; } + [JsonProperty("local_recording_disabled")] + public bool LocalRecordingDisabled { get; set; } + [JsonProperty("user_id")] + public int UserId { get; set; } + [JsonProperty("user_name")] + public string UserName { get; set; } + [JsonProperty("user_type")] + public string UserType { get; set; } + [JsonProperty("video_status has_source")] + public bool VideoStatusHasSource { get; set; } + [JsonProperty("video_status is_receiving")] + public bool VideoStatusIsReceiving { get; set; } + [JsonProperty("video_status is_sending")] + public bool VideoStatusIsSending { get; set; } + + public ListParticipant() + { + HandStatus = new HandStatus(); + } + + public static List GetGenericParticipantListFromParticipantsResult( + List participants) + { + return + participants.Select( + p => + new Participant + { + Name = p.UserName, + IsHost = p.IsHost, + CanMuteVideo = p.IsVideoCanMuteByHost, + CanUnmuteVideo = p.IsVideoCanUnmuteByHost, + AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", + VideoMuteFb = p.VideoStatusIsSending + }).ToList(); + } + } + + public class CallinCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class CalloutCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class TollFreeCallinList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class Info + { + public List callin_country_list { get; set; } + public List callout_country_list { get; set; } + public List toll_free_callin_list { get; set; } + } + + public class ThirdParty + { + public string h323_address { get; set; } + public string meeting_number { get; set; } + public string service_provider { get; set; } + public string sip_address { get; set; } + } + + public class MeetingListItem + { + public string accessRole { get; set; } + public string calendarChangeKey { get; set; } + public string calendarID { get; set; } + public bool checkIn { get; set; } + public string creatorEmail { get; set; } + public string creatorName { get; set; } + public string endTime { get; set; } + public string hostName { get; set; } + public bool isInstantMeeting { get; set; } + public bool isPrivate { get; set; } + public string location { get; set; } + public string meetingName { get; set; } + public string meetingNumber { get; set; } + public string scheduledFrom { get; set; } + public string startTime { get; set; } + public ThirdParty third_party { get; set; } + + public MeetingListItem() + { + third_party = new ThirdParty(); + } + } + + public class InfoResult + { + public Info Info { get; set; } + public bool am_i_original_host { get; set; } + public string default_callin_country { get; set; } + public string dialIn { get; set; } + public string international_url { get; set; } + public string invite_email_content { get; set; } + public string invite_email_subject { get; set; } + public bool is_callin_country_list_available { get; set; } + public bool is_calling_room_system_enabled { get; set; } + public bool is_toll_free_callin_list_available { get; set; } + public bool is_view_only { get; set; } + public bool is_waiting_room { get; set; } + public bool is_webinar { get; set; } + public string meeting_id { get; set; } + public MeetingListItem meeting_list_item { get; set; } + public string meeting_password { get; set; } + public string meeting_type { get; set; } + public int my_userid { get; set; } + public int participant_id { get; set; } + public string real_meeting_id { get; set; } + public string schedule_option { get; set; } + public string schedule_option2 { get; set; } + public string support_callout_type { get; set; } + public string toll_free_number { get; set; } + public string user_type { get; set; } + + public InfoResult() + { + Info = new Info(); + meeting_list_item = new MeetingListItem(); + } + } + + public class Phonebook + { + public List Contacts { get; set; } + public int Limit { get; set; } + public int Offset { get; set; } + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs index 8f7b6876..980c7a61 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs @@ -1,2085 +1,2106 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.CrestronThread; -using Crestron.SimplSharpPro.DeviceSupport; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common.Cameras; -using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using PepperDash_Essentials_Core.DeviceTypeInterfaces; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom -{ - public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, - IRouting, - IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraAutoMode, - IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing - { - private const long MeetingRefreshTimer = 60000; - private const uint DefaultMeetingDurationMin = 30; - private const string Delimiter = "\x0D\x0A"; - private readonly CrestronQueue _receiveQueue; - - - private readonly Thread _receiveThread; - - private readonly ZoomRoomSyncState _syncState; - public bool CommDebuggingIsOn; - private CodecDirectory _currentDirectoryResult; - private uint _jsonCurlyBraceCounter; - private bool _jsonFeedbackMessageIsIncoming; - private StringBuilder _jsonMessage; - private int _previousVolumeLevel; - private CameraBase _selectedCamera; - - private readonly ZoomRoomPropertiesConfig _props; - - public ZoomRoom(DeviceConfig config, IBasicCommunication comm) - : base(config) - { - _props = JsonConvert.DeserializeObject(config.Properties.ToString()); - - // The queue that will collect the repsonses in the order they are received - _receiveQueue = new CrestronQueue(1024); - - // The thread responsible for dequeuing and processing the messages - _receiveThread = new Thread(o => ProcessQueue(), null) {Priority = Thread.eThreadPriority.MediumPriority}; - - Communication = comm; - - if (_props.CommunicationMonitorProperties != null) - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, - _props.CommunicationMonitorProperties); - } - else - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, - "zStatus SystemUnit\r"); - } - - DeviceManager.AddDevice(CommunicationMonitor); - - Status = new ZoomRoomStatus(); - - Configuration = new ZoomRoomConfiguration(); - - CodecInfo = new ZoomRoomInfo(Status, Configuration); - - _syncState = new ZoomRoomSyncState(Key + "--Sync", this); - - _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; - - PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - - PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; - PortGather.LineReceived += Port_LineReceived; - - CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, - eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - - Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, - eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); - - CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); - - CameraAutoModeIsOnFeedback = new BoolFeedback(CameraAutoModeIsOnFeedbackFunc); - - CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); - - ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); - - SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); - - SetUpFeedbackActions(); - - Cameras = new List(); - - SetUpDirectory(); - - Participants = new CodecParticipants(); - - SupportsCameraOff = _props.SupportsCameraOff; - SupportsCameraAutoMode = _props.SupportsCameraAutoMode; - - PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); - CallerIdNameFeedback = new StringFeedback(CallerIdNameFeedbackFunc); - CallerIdNumberFeedback = new StringFeedback(CallerIdNumberFeedbackFunc); - } - - public CommunicationGather PortGather { get; private set; } - - public ZoomRoomStatus Status { get; private set; } - - public ZoomRoomConfiguration Configuration { get; private set; } - - //CTimer LoginMessageReceivedTimer; - //CTimer RetryConnectionTimer; - - /// - /// Gets and returns the scaled volume of the codec - /// - protected override Func VolumeLevelFeedbackFunc - { - get - { - return () => CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); - } - } - - protected override Func PrivacyModeIsOnFeedbackFunc - { - get { return () => Configuration.Call.Microphone.Mute; } - } - - protected override Func StandbyIsOnFeedbackFunc - { - get { return () => false; } - } - - protected override Func SharingSourceFeedbackFunc - { - get { return () => Status.Sharing.dispState; } - } - - protected override Func SharingContentIsOnFeedbackFunc - { - get { return () => Status.Call.Sharing.IsSharing; } - } - - protected Func FarEndIsSharingContentFeedbackFunc - { - get { return () => Status.Call.Sharing.State == zEvent.eSharingState.Receiving; } - } - - protected override Func MuteFeedbackFunc - { - get { return () => Configuration.Audio.Output.Volume == 0; } - } - - //protected Func RoomIsOccupiedFeedbackFunc - //{ - // get - // { - // return () => false; - // } - //} - - //protected Func PeopleCountFeedbackFunc - //{ - // get - // { - // return () => 0; - // } - //} - - protected Func SelfViewIsOnFeedbackFunc - { - get { return () => !Configuration.Video.HideConfSelfVideo; } - } - - protected Func CameraIsOffFeedbackFunc - { - get { return () => Configuration.Call.Camera.Mute; } - } - - protected Func CameraAutoModeIsOnFeedbackFunc - { - get { return () => false; } - } - - protected Func SelfviewPipPositionFeedbackFunc - { - get { return () => _currentSelfviewPipPosition.Command; } - } - - protected Func LocalLayoutFeedbackFunc - { - get { return () => ""; } - } - - protected Func LocalLayoutIsProminentFeedbackFunc - { - get { return () => false; } - } - - - public RoutingInputPort CodecOsdIn { get; private set; } - public RoutingOutputPort Output1 { get; private set; } - - #region ICommunicationMonitor Members - - public StatusMonitorBase CommunicationMonitor { get; private set; } - - #endregion - - #region IHasCodecCameras Members - - public event EventHandler CameraSelected; - - public List Cameras { get; private set; } - - public CameraBase SelectedCamera - { - get { return _selectedCamera; } - private set - { - _selectedCamera = value; - SelectedCameraFeedback.FireUpdate(); - ControllingFarEndCameraFeedback.FireUpdate(); - - var handler = CameraSelected; - if (handler != null) - { - handler(this, new CameraSelectedEventArgs(SelectedCamera)); - } - } - } - - - public StringFeedback SelectedCameraFeedback { get; private set; } - - public void SelectCamera(string key) - { - if (Cameras == null) - { - return; - } - - var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); - if (camera != null) - { - Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); - SelectedCamera = camera; - } - else - { - Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); - } - } - - public CameraBase FarEndCamera { get; private set; } - - public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } - - #endregion - - #region IHasCodecSelfView Members - - public BoolFeedback SelfviewIsOnFeedback { get; private set; } - - public void SelfViewModeOn() - { - SendText("zConfiguration Video hide_conf_self_video: off"); - } - - public void SelfViewModeOff() - { - SendText("zConfiguration Video hide_conf_self_video: on"); - } - - public void SelfViewModeToggle() - { - if (SelfviewIsOnFeedback.BoolValue) - { - SelfViewModeOff(); - } - else - { - SelfViewModeOn(); - } - } - - #endregion - - #region IHasDirectoryHistoryStack Members - - public event EventHandler DirectoryResultReturned; - public CodecDirectory DirectoryRoot { get; private set; } - - public CodecDirectory CurrentDirectoryResult - { - get { return _currentDirectoryResult; } - } - - public CodecPhonebookSyncState PhonebookSyncState { get; private set; } - - public void SearchDirectory(string searchString) - { - var directoryResults = new CodecDirectory(); - - directoryResults.AddContactsToDirectory( - DirectoryRoot.CurrentDirectoryResults.FindAll( - c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); - - DirectoryBrowseHistoryStack.Clear(); - _currentDirectoryResult = directoryResults; - - OnDirectoryResultReturned(directoryResults); - } - - public void GetDirectoryFolderContents(string folderId) - { - var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; - - directoryResults.AddContactsToDirectory( - DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); - - DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); - - _currentDirectoryResult = directoryResults; - - OnDirectoryResultReturned(directoryResults); - } - - public void SetCurrentDirectoryToRoot() - { - DirectoryBrowseHistoryStack.Clear(); - - _currentDirectoryResult = DirectoryRoot; - - OnDirectoryResultReturned(DirectoryRoot); - } - - public void GetDirectoryParentFolderContents() - { - if (DirectoryBrowseHistoryStack.Count == 0) - { - return; - } - - var currentDirectory = DirectoryBrowseHistoryStack.Pop(); - - _currentDirectoryResult = currentDirectory; - - OnDirectoryResultReturned(currentDirectory); - } - - public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } - - public List DirectoryBrowseHistory { get; private set; } - - public Stack DirectoryBrowseHistoryStack { get; private set; } - - #endregion - - #region IHasScheduleAwareness Members - - public CodecScheduleAwareness CodecSchedule { get; private set; } - - public void GetSchedule() - { - GetBookings(); - } - - #endregion - - #region IRouting Members - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - ExecuteSwitch(inputSelector); - } - - #endregion - - private void SyncState_InitialSyncCompleted(object sender, EventArgs e) - { - SetUpRouting(); - - SetIsReady(); - } - - private void SetUpCallFeedbackActions() - { - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; - - Status.Call.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Info") - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - } - }; - } - - /// - /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. - /// - private void SetUpFeedbackActions() - { - Configuration.Audio.Output.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Volume") - { - VolumeLevelFeedback.FireUpdate(); - MuteFeedback.FireUpdate(); - } - }; - - Configuration.Call.Microphone.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Mute") - { - PrivacyModeIsOnFeedback.FireUpdate(); - } - }; - - Configuration.Video.PropertyChanged += (o, a) => - { - if (a.PropertyName == "HideConfSelfVideo") - { - SelfviewIsOnFeedback.FireUpdate(); - } - }; - Configuration.Video.Camera.PropertyChanged += (o, a) => - { - if (a.PropertyName == "SelectedId") - { - SelectCamera(Configuration.Video.Camera.SelectedId); - // this will in turn fire the affected feedbacks - } - }; - - Configuration.Call.Camera.PropertyChanged += (o, a) => - { - Debug.Console(1, this, "Configuration.Call.Camera.PropertyChanged: {0}", a.PropertyName); - - if (a.PropertyName != "Mute") return; - - CameraIsOffFeedback.FireUpdate(); - CameraAutoModeIsOnFeedback.FireUpdate(); - }; - - Configuration.Call.Layout.PropertyChanged += (o, a) => - { - if (a.PropertyName != "Position") return; - - ComputeSelfviewPipStatus(); - - SelfviewPipPositionFeedback.FireUpdate(); - }; - - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; - - Status.Call.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Info") - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - } - }; - - Status.Sharing.PropertyChanged += (o, a) => - { - switch (a.PropertyName) - { - case "dispState": - SharingSourceFeedback.FireUpdate(); - break; - case "password": - break; - } - }; - - Status.PhoneCall.PropertyChanged += (o, a) => - { - switch (a.PropertyName) - { - case "IsIncomingCall": - Debug.Console(1, this, "Incoming Phone Call: {0}", Status.PhoneCall.IsIncomingCall); - break; - case "PeerDisplayName": - Debug.Console(1, this, "Peer Display Name: {0}", Status.PhoneCall.PeerDisplayName); - CallerIdNameFeedback.FireUpdate(); - break; - case "PeerNumber": - Debug.Console(1, this, "Peer Number: {0}", Status.PhoneCall.PeerNumber); - CallerIdNumberFeedback.FireUpdate(); - break; - case "OffHook": - Debug.Console(1, this, "Phone is OffHook: {0}", Status.PhoneCall.OffHook); - PhoneOffHookFeedback.FireUpdate(); - break; - } - }; - } - - private void SetUpDirectory() - { - DirectoryRoot = new CodecDirectory(); - - DirectoryBrowseHistory = new List(); - DirectoryBrowseHistoryStack = new Stack(); - - CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => _currentDirectoryResult != DirectoryRoot); - - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - } - - private void SetUpRouting() - { - // Set up input ports - CreateOsdSource(); - InputPorts.Add(CodecOsdIn); - - // Set up output ports - OutputPorts.Add(Output1); - } - - /// - /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input - /// to enable routing - /// - private void CreateOsdSource() - { - OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); - DeviceManager.AddDevice(OsdSource); - var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); - TieLineCollection.Default.Add(tl); - - //foreach(var input in Status.Video. - } - - /// - /// Starts the HTTP feedback server and syncronizes state of codec - /// - /// - public override bool CustomActivate() - { - CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", - ConsoleAccessLevelEnum.AccessOperator); - if (!_props.DisablePhonebookAutoDownload) - { - CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), - "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", - ConsoleAccessLevelEnum.AccessOperator); - } - - CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", - "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); - - var socket = Communication as ISocketStatus; - if (socket != null) - { - socket.ConnectionChange += socket_ConnectionChange; - } - - CommDebuggingIsOn = false; - - Communication.Connect(); - - CommunicationMonitor.Start(); - - return base.CustomActivate(); - } - - public void SetCommDebug(string s) - { - if (s == "1") - { - CommDebuggingIsOn = true; - Debug.Console(0, this, "Comm Debug Enabled."); - } - else - { - CommDebuggingIsOn = false; - Debug.Console(0, this, "Comm Debug Disabled."); - } - } - - private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) - { - Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); - if (e.Client.IsConnected) - { - } - else - { - _syncState.CodecDisconnected(); - PhonebookSyncState.CodecDisconnected(); - } - } - - public void SendText(string command) - { - if (CommDebuggingIsOn) - { - Debug.Console(1, this, "Sending: '{0}'", command); - } - - Communication.SendText(command + Delimiter); - } - - /// - /// Gathers responses and enqueues them. - /// - /// - /// - private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) - { - //if (CommDebuggingIsOn) - // Debug.Console(1, this, "Gathered: '{0}'", args.Text); - - _receiveQueue.Enqueue(args.Text); - - // If the receive thread has for some reason stopped, this will restart it - if (_receiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) - { - _receiveThread.Start(); - } - } - - - /// - /// Runs in it's own thread to dequeue messages in the order they were received to be processed - /// - /// - private object ProcessQueue() - { - try - { - while (true) - { - var message = _receiveQueue.Dequeue(); - - ProcessMessage(message); - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error Processing Queue: {0}", e); - } - - return null; - } - - - /// - /// Queues the initial queries to be sent upon connection - /// - private void SetUpSyncQueries() - { - // zStatus - _syncState.AddQueryToQueue("zStatus Call Status"); - _syncState.AddQueryToQueue("zStatus Audio Input Line"); - _syncState.AddQueryToQueue("zStatus Audio Output Line"); - _syncState.AddQueryToQueue("zStatus Video Camera Line"); - _syncState.AddQueryToQueue("zStatus Video Optimizable"); - _syncState.AddQueryToQueue("zStatus Capabilities"); - _syncState.AddQueryToQueue("zStatus Sharing"); - _syncState.AddQueryToQueue("zStatus CameraShare"); - _syncState.AddQueryToQueue("zStatus Call Layout"); - _syncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); - _syncState.AddQueryToQueue("zStatus NumberOfScreens"); - - // zConfiguration - - _syncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); - _syncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); - _syncState.AddQueryToQueue("zConfiguration Call Camera Mute"); - _syncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); - _syncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); - _syncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); - _syncState.AddQueryToQueue("zConfiguration Audio Input volume"); - _syncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); - _syncState.AddQueryToQueue("zConfiguration Audio Output volume"); - _syncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); - _syncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); - _syncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); - _syncState.AddQueryToQueue("zConfiguration Client appVersion"); - _syncState.AddQueryToQueue("zConfiguration Client deviceSystem"); - _syncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Style"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Size"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Position"); - _syncState.AddQueryToQueue("zConfiguration Call Lock Enable"); - _syncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); - _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); - _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); - - // zCommand - - if (!_props.DisablePhonebookAutoDownload) - { - _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); - } - - _syncState.AddQueryToQueue("zCommand Bookings List"); - _syncState.AddQueryToQueue("zCommand Call ListParticipants"); - _syncState.AddQueryToQueue("zCommand Call Info"); - - - _syncState.StartSync(); - } - - /// - /// Processes messages as they are dequeued - /// - /// - private void ProcessMessage(string message) - { - // Counts the curly braces - if (message.Contains("client_loop: send disconnect: Broken pipe")) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, - "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); - - return; - } - - if (message.Contains('{')) - { - _jsonCurlyBraceCounter++; - } - - if (message.Contains('}')) - { - _jsonCurlyBraceCounter--; - } - - Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); - - if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) - // Check for the beginning of a new JSON message - { - _jsonFeedbackMessageIsIncoming = true; - _jsonCurlyBraceCounter = 1; // reset the counter for each new message - - _jsonMessage = new StringBuilder(); - - _jsonMessage.Append(message); - - if (CommDebuggingIsOn) - { - Debug.Console(2, this, "Incoming JSON message..."); - } - - return; - } - if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) - // Check for the end of a JSON message - { - _jsonMessage.Append(message); - - if (_jsonCurlyBraceCounter == 0) - { - _jsonFeedbackMessageIsIncoming = false; - - if (CommDebuggingIsOn) - { - Debug.Console(2, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); - } - - // Forward the complete message to be deserialized - DeserializeResponse(_jsonMessage.ToString()); - } - - //JsonMessage = new StringBuilder(); - return; - } - - // NOTE: This must happen after the above conditions have been checked - // Append subsequent partial JSON fragments to the string builder - if (_jsonFeedbackMessageIsIncoming) - { - _jsonMessage.Append(message); - - //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); - return; - } - - if (CommDebuggingIsOn) - { - Debug.Console(1, this, "Non-JSON response: '{0}'", message); - } - - _jsonCurlyBraceCounter = 0; // reset on non-JSON response - - if (!_syncState.InitialSyncComplete) - { - switch (message.Trim().ToLower()) // remove the whitespace - { - case "*r login successful": - { - _syncState.LoginMessageReceived(); - - // Fire up a thread to send the intial commands. - CrestronInvoke.BeginInvoke(o => - { - Thread.Sleep(100); - // disable echo of commands - SendText("echo off"); - Thread.Sleep(100); - // set feedback exclusions - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); - Thread.Sleep(100); - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); - Thread.Sleep(100); - - if (!_props.DisablePhonebookAutoDownload) - { - SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); - } - // switch to json format - SendText("format json"); - }); - - break; - } - } - } - } - - /// - /// Deserializes a JSON formatted response - /// - /// - private void DeserializeResponse(string response) - { - try - { - var trimmedResponse = response.Trim(); - - if (trimmedResponse.Length <= 0) - { - return; - } - - var message = JObject.Parse(trimmedResponse); - - var eType = - (eZoomRoomResponseType) - Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); - - var topKey = message["topKey"].Value(); - - var responseObj = message[topKey]; - - Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); - - switch (eType) - { - case eZoomRoomResponseType.zConfiguration: - { - switch (topKey.ToLower()) - { - case "call": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); - - break; - } - case "audio": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); - - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); - - break; - } - case "client": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); - - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zCommand: - { - switch (topKey.ToLower()) - { - case "inforesult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); - break; - } - case "phonebooklistresult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); - - if (!PhonebookSyncState.InitialSyncComplete) - { - PhonebookSyncState.InitialPhonebookFoldersReceived(); - PhonebookSyncState.PhonebookRootEntriesReceived(); - PhonebookSyncState.SetPhonebookHasFolders(false); - PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); - } - - var directoryResults = - zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); - - DirectoryRoot = directoryResults; - - _currentDirectoryResult = DirectoryRoot; - - OnDirectoryResultReturned(directoryResults); - - break; - } - case "listparticipantsresult": - { - Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); - - switch (responseObj.Type) - { - case JTokenType.Array: - Status.Call.Participants = - JsonConvert.DeserializeObject>( - responseObj.ToString()); - break; - case JTokenType.Object: - { - // this is a single participant event notification - - var participant = - JsonConvert.DeserializeObject( - responseObj.ToString()); - - if (participant != null) - { - switch (participant.Event) - { - case "ZRCUserChangedEventUserInfoUpdated": - case "ZRCUserChangedEventLeftMeeting": - { - var existingParticipant = - Status.Call.Participants.FirstOrDefault( - p => p.UserId.Equals(participant.UserId)); - - if (existingParticipant != null) - { - switch (participant.Event) - { - case "ZRCUserChangedEventLeftMeeting": - Status.Call.Participants.Remove(existingParticipant); - break; - case "ZRCUserChangedEventUserInfoUpdated": - JsonConvert.PopulateObject(responseObj.ToString(), - existingParticipant); - break; - } - } - } - break; - case "ZRCUserChangedEventJoinedMeeting": - Status.Call.Participants.Add(participant); - break; - } - } - } - break; - } - - var participants = - zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( - Status.Call.Participants); - - Participants.CurrentParticipants = participants; - - PrintCurrentCallParticipants(); - - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zEvent: - { - switch (topKey.ToLower()) - { - case "phonebook": - { - if (responseObj["Updated Contact"] != null) - { - var updatedContact = - JsonConvert.DeserializeObject( - responseObj["Updated Contact"].ToString()); - - var existingContact = - Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); - - if (existingContact != null) - { - // Update existing contact - JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), - existingContact); - } - } - else if (responseObj["Added Contact"] != null) - { - var jToken = responseObj["Updated Contact"]; - if (jToken != null) - { - var newContact = - JsonConvert.DeserializeObject( - jToken.ToString()); - - // Add a new contact - Status.Phonebook.Contacts.Add(newContact); - } - } - - break; - } - case "bookingslistresult": - { - if (!_syncState.InitialSyncComplete) - { - _syncState.LastQueryResponseReceived(); - } - - var codecBookings = JsonConvert.DeserializeObject>( - responseObj.ToString()); - - if (codecBookings != null && codecBookings.Count > 0) - { - CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult( - codecBookings, CodecSchedule.MeetingWarningMinutes); - } - - break; - } - case "bookings updated": - { - GetBookings(); - - break; - } - case "sharingstate": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); - - SetLayout(); - - break; - } - case "incomingcallindication": - { - var incomingCall = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var newCall = new CodecActiveCallItem - { - Direction = eCodecCallDirection.Incoming, - Status = eCodecCallStatus.Ringing, - Type = eCodecCallType.Unknown, - Name = incomingCall.callerName, - Id = incomingCall.callerJID - }; - - ActiveCalls.Add(newCall); - - OnCallStatusChange(newCall); - } - - break; - } - case "treatedincomingcallindication": - { - var incomingCall = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var existingCall = - ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); - - if (existingCall != null) - { - existingCall.Status = !incomingCall.accepted - ? eCodecCallStatus.Disconnected - : eCodecCallStatus.Connecting; - - OnCallStatusChange(existingCall); - } - - UpdateCallStatus(); - } - - break; - } - case "calldisconnect": - { - var disconnectEvent = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (disconnectEvent.Successful) - { - if (ActiveCalls.Count > 0) - { - var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); - - if (activeCall != null) - { - activeCall.Status = eCodecCallStatus.Disconnected; - - OnCallStatusChange(activeCall); - } - } - var emptyList = new List(); - Participants.CurrentParticipants = emptyList; - } - - UpdateCallStatus(); - break; - } - case "callconnecterror": - { - UpdateCallStatus(); - break; - } - case "videounmuterequest": - { - // TODO: notify room of a request to unmute video - break; - } - case "meetingneedspassword": - { - // TODO: notify user to enter a password - break; - } - case "needwaitforhost": - { - var needWait = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (needWait.Wait) - { - // TODO: notify user to wait for host - } - - break; - } - case "openvideofailforhoststop": - { - // TODO: notify user that host has disabled unmuting video - break; - } - case "updatedcallrecordinfo": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); - - break; - } - case "phonecallstatus": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zStatus: - { - switch (topKey.ToLower()) - { - case "login": - { - _syncState.LoginMessageReceived(); - - if (!_syncState.InitialQueryMessagesWereSent) - { - SetUpSyncQueries(); - } - - JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); - - break; - } - case "systemunit": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); - - break; - } - case "call": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); - - UpdateCallStatus(); - - break; - } - case "capabilities": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); - break; - } - case "sharing": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); - - break; - } - case "numberofscreens": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); - break; - } - case "camerashare": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); - break; - } - case "layout": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); - break; - } - case "audio input line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); - break; - } - case "audio output line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); - break; - } - case "video camera line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); - - if (!_syncState.CamerasHaveBeenSetUp) - { - SetUpCameras(); - } - - break; - } - default: - { - break; - } - } - - break; - } - default: - { - Debug.Console(1, "Unknown Response Type:"); - break; - } - } - } - catch (Exception ex) - { - Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); - } - } - - private void SetLayout() - { - if (!_props.AutoDefaultLayouts) return; - - if ( - (Status.Call.Sharing.State == zEvent.eSharingState.Receiving || - Status.Call.Sharing.State == zEvent.eSharingState.Sending)) - { - SendText(String.Format("zconfiguration call layout style: {0}", - _props.DefaultSharingLayout)); - } - else - { - SendText(String.Format("zconfiguration call layout style: {0}", - _props.DefaultCallLayout)); - } - } - - public void PrintCurrentCallParticipants() - { - if (Debug.Level <= 0) - { - return; - } - - Debug.Console(1, this, "****************************Call Participants***************************"); - foreach (var participant in Participants.CurrentParticipants) - { - Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name, - participant.AudioMuteFb, participant.IsHost); - } - Debug.Console(1, this, "************************************************************************"); - } - - /// - /// Retrieves bookings list - /// - private void GetBookings() - { - SendText("zCommand Bookings List"); - } - - - /// - /// Updates the current call status - /// - private void UpdateCallStatus() - { - if (Status.Call != null) - { - var callStatus = Status.Call.Status; - - // If not currently in a meeting, intialize the call object - if (callStatus != zStatus.eCallStatus.IN_MEETING || callStatus != zStatus.eCallStatus.CONNECTING_MEETING) - { - Status.Call = new zStatus.Call {Status = callStatus}; - - SetUpCallFeedbackActions(); - } - - if (ActiveCalls.Count == 0) - { - if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || - callStatus == zStatus.eCallStatus.IN_MEETING) - { - var newStatus = eCodecCallStatus.Unknown; - - switch (callStatus) - { - case zStatus.eCallStatus.CONNECTING_MEETING: - newStatus = eCodecCallStatus.Connecting; - break; - case zStatus.eCallStatus.IN_MEETING: - newStatus = eCodecCallStatus.Connected; - break; - } - - var newCall = new CodecActiveCallItem {Status = newStatus}; - - ActiveCalls.Add(newCall); - - OnCallStatusChange(newCall); - } - } - else - { - var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); - - switch (callStatus) - { - case zStatus.eCallStatus.IN_MEETING: - existingCall.Status = eCodecCallStatus.Connected; - break; - case zStatus.eCallStatus.NOT_IN_MEETING: - existingCall.Status = eCodecCallStatus.Disconnected; - break; - } - - OnCallStatusChange(existingCall); - } - } - - Debug.Console(1, this, "****************************Active Calls*********************************"); - - // Clean up any disconnected calls left in the list - for (int i = 0; i < ActiveCalls.Count; i++) - { - var call = ActiveCalls[i]; - - Debug.Console(1, this, - @"Name: {0} - ID: {1} - IsActive: {2} - Status: {3} - Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); - - if (!call.IsActiveCall) - { - Debug.Console(1, this, "******Removing Inactive Call: {0}******", call.Name); - ActiveCalls.Remove(call); - } - } - Debug.Console(1, this, "**************************************************************************"); - - //clear participants list after call cleanup - if (ActiveCalls.Count == 0) - { - Participants.CurrentParticipants = new List(); - } - } - - protected override void OnCallStatusChange(CodecActiveCallItem item) - { - base.OnCallStatusChange(item); - - if (_props.AutoDefaultLayouts) - { - SetLayout(); - } - } - - public override void StartSharing() - { - SendText("zCommand Call Sharing HDMI Start"); - } - - /// - /// Stops sharing the current presentation - /// - public override void StopSharing() - { - SendText("zCommand Call Sharing Disconnect"); - } - - public override void PrivacyModeOn() - { - SendText("zConfiguration Call Microphone Mute: on"); - } - - public override void PrivacyModeOff() - { - SendText("zConfiguration Call Microphone Mute: off"); - } - - public override void PrivacyModeToggle() - { - if (PrivacyModeIsOnFeedback.BoolValue) - { - PrivacyModeOff(); - } - else - { - PrivacyModeOn(); - } - } - - public override void MuteOff() - { - SetVolume((ushort) _previousVolumeLevel); - } - - public override void MuteOn() - { - _previousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall - - SetVolume(0); - } - - public override void MuteToggle() - { - if (MuteFeedback.BoolValue) - { - MuteOff(); - } - else - { - MuteOn(); - } - } - - - /// - /// Increments the voluem - /// - /// - public override void VolumeUp(bool pressRelease) - { - // TODO: Implment volume decrement that calls SetVolume() - } - - /// - /// Decrements the volume - /// - /// - public override void VolumeDown(bool pressRelease) - { - // TODO: Implment volume decrement that calls SetVolume() - } - - /// - /// Scales the level and sets the codec to the specified level within its range - /// - /// level from slider (0-65535 range) - public override void SetVolume(ushort level) - { - var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); - SendText(string.Format("zConfiguration Audio Output volume: {0}", scaledLevel)); - } - - /// - /// Recalls the default volume on the codec - /// - public void VolumeSetToDefault() - { - } - - /// - /// - /// - public override void StandbyActivate() - { - // No corresponding function on device - } - - /// - /// - /// - public override void StandbyDeactivate() - { - // No corresponding function on device - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); - } - - public override void ExecuteSwitch(object selector) - { - var action = selector as Action; - if (action == null) - { - return; - } - - action(); - } - - public void AcceptCall() - { - var incomingCall = - ActiveCalls.FirstOrDefault( - c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - - AcceptCall(incomingCall); - } - - public override void AcceptCall(CodecActiveCallItem call) - { - SendText(string.Format("zCommand Call Accept callerJID: {0}", call.Id)); - - call.Status = eCodecCallStatus.Connected; - - OnCallStatusChange(call); - - UpdateCallStatus(); - } - - public void RejectCall() - { - var incomingCall = - ActiveCalls.FirstOrDefault( - c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - - RejectCall(incomingCall); - } - - public override void RejectCall(CodecActiveCallItem call) - { - SendText(string.Format("zCommand Call Reject callerJID: {0}", call.Id)); - - call.Status = eCodecCallStatus.Disconnected; - - OnCallStatusChange(call); - - UpdateCallStatus(); - } - - public override void Dial(Meeting meeting) - { - SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); - } - - public override void Dial(string number) - { - SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); - } - - /// - /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. - /// Currently only invites a single user - /// - /// - public override void Dial(IInvitableContact contact) - { - var ic = contact as zStatus.ZoomDirectoryContact; - - if (ic != null) - { - Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); - - if (!IsInCall) - { - SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, - ic.ContactId)); - } - else - { - SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); - } - } - } - - public override void EndCall(CodecActiveCallItem call) - { - SendText("zCommand Call Disconnect"); - } - - public override void EndAllCalls() - { - SendText("zCommand Call Disconnect"); - } - - public override void SendDtmf(string s) - { - SendDtmfToPhone(s); - } - - /// - /// Call when directory results are updated - /// - /// - private void OnDirectoryResultReturned(CodecDirectory result) - { - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - - // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology - var handler = DirectoryResultReturned; - if (handler != null) - { - handler(this, new DirectoryEventArgs - { - Directory = result, - DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue - }); - } - - //PrintDirectory(result); - } - - /// - /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data - /// - private void SetUpCameras() - { - SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); - - ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); - - foreach (var cam in Status.Cameras) - { - var camera = new ZoomRoomCamera(cam.id, cam.Name, this); - - Cameras.Add(camera); - - if (cam.Selected) - { - SelectedCamera = camera; - } - } - - if (IsInCall) - { - UpdateFarEndCameras(); - } - - _syncState.CamerasSetUp(); - } - - /// - /// Dynamically creates far end cameras for call participants who have far end control enabled. - /// - private void UpdateFarEndCameras() - { - // TODO: set up far end cameras for the current call - } - - #region Implementation of IHasParticipants - - public CodecParticipants Participants { get; private set; } - - #endregion - - #region Implementation of IHasCameraOff - - public BoolFeedback CameraIsOffFeedback { get; private set; } - - public void CameraOff() - { - SendText("zConfiguration Call Camera Mute: On"); - } - - #endregion - - #region Implementation of IHasCameraAutoMode - - //Zoom doesn't support camera auto modes. Setting this to just unmute video - public void CameraAutoModeOn() - { - throw new NotImplementedException("Zoom Room Doesn't support camera auto mode"); - } - - //Zoom doesn't support camera auto modes. Setting this to just unmute video - public void CameraAutoModeOff() - { - SendText("zConfiguration Call Camera Mute: Off"); - } - - public void CameraAutoModeToggle() - { - throw new NotImplementedException("Zoom Room doesn't support camera auto mode"); - } - - public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } - - #endregion - - #region Implementation of IHasFarEndContentStatus - - public BoolFeedback ReceivingContent { get; private set; } - - #endregion - - #region Implementation of IHasSelfviewPosition - - private CodecCommandWithLabel _currentSelfviewPipPosition; - - public StringFeedback SelfviewPipPositionFeedback { get; private set; } - - public void SelfviewPipPositionSet(CodecCommandWithLabel position) - { - SendText(String.Format("zConfiguration Call Layout Position: {0}", position.Command)); - } - - public void SelfviewPipPositionToggle() - { - if (_currentSelfviewPipPosition != null) - { - var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1; - - if (nextPipPositionIndex >= SelfviewPipPositions.Count) - // Check if we need to loop back to the first item in the list - nextPipPositionIndex = 0; - - SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); - } - } - - public List SelfviewPipPositions = new List() - { - new CodecCommandWithLabel("UpLeft", "Center Left"), - new CodecCommandWithLabel("UpRight", "Center Right"), - new CodecCommandWithLabel("DownRight", "Lower Right"), - new CodecCommandWithLabel("DownLeft", "Lower Left") - }; - - private void ComputeSelfviewPipStatus() - { - _currentSelfviewPipPosition = - SelfviewPipPositions.FirstOrDefault( - p => p.Command.ToLower().Equals(Configuration.Call.Layout.Position.ToString().ToLower())); - } - - #endregion - - #region Implementation of IHasPhoneDialing - - private Func PhoneOffHookFeedbackFunc {get {return () => Status.PhoneCall.OffHook; }} - private Func CallerIdNameFeedbackFunc { get { return () => Status.PhoneCall.PeerDisplayName; } } - private Func CallerIdNumberFeedbackFunc { get { return () => Status.PhoneCall.PeerNumber; } } - - public BoolFeedback PhoneOffHookFeedback { get; private set; } - public StringFeedback CallerIdNameFeedback { get; private set; } - public StringFeedback CallerIdNumberFeedback { get; private set; } - - public void DialPhoneCall(string number) - { - SendText(String.Format("zCommand Dial PhoneCallOut Number: {0}", number)); - } - - public void EndPhoneCall() - { - SendText(String.Format("zCommand Dial PhoneHangUp CallId: {0}", Status.PhoneCall.CallId)); - } - - public void SendDtmfToPhone(string digit) - { - SendText(String.Format("zCommand SendSipDTMF CallId: {0} Key: {1}", Status.PhoneCall.CallId, digit)); - } - - #endregion - } - - /// - /// Zoom Room specific info object - /// - public class ZoomRoomInfo : VideoCodecInfo - { - public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) - { - Status = status; - Configuration = configuration; - } - - public ZoomRoomStatus Status { get; private set; } - public ZoomRoomConfiguration Configuration { get; private set; } - - public override bool AutoAnswerEnabled - { - get { return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; } - } - - public override string E164Alias - { - get - { - if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) - { - return Status.SystemUnit.MeetingNumber; - } - return string.Empty; - } - } - - public override string H323Id - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) - { - return Status.Call.Info.meeting_list_item.third_party.h323_address; - } - return string.Empty; - } - } - - public override string IpAddress - { - get - { - if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) - { - return Status.SystemUnit.RoomInfo.AccountEmail; - } - return string.Empty; - } - } - - public override bool MultiSiteOptionIsEnabled - { - get { return true; } - } - - public override string SipPhoneNumber - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) - { - return Status.Call.Info.dialIn; - } - return string.Empty; - } - } - - public override string SipUri - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) - { - return Status.Call.Info.meeting_list_item.third_party.sip_address; - } - return string.Empty; - } - } - } - - /// - /// Tracks the initial sycnronization state when establishing a new connection - /// - public class ZoomRoomSyncState : IKeyed - { - private readonly ZoomRoom _parent; - private readonly CrestronQueue _syncQueries; - private bool _initialSyncComplete; - - public ZoomRoomSyncState(string key, ZoomRoom parent) - { - _parent = parent; - Key = key; - _syncQueries = new CrestronQueue(50); - CodecDisconnected(); - } - - public bool InitialSyncComplete - { - get { return _initialSyncComplete; } - private set - { - if (value) - { - var handler = InitialSyncCompleted; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - _initialSyncComplete = value; - } - } - - public bool LoginMessageWasReceived { get; private set; } - - public bool InitialQueryMessagesWereSent { get; private set; } - - public bool LastQueryResponseWasReceived { get; private set; } - - public bool CamerasHaveBeenSetUp { get; private set; } - - #region IKeyed Members - - public string Key { get; private set; } - - #endregion - - public event EventHandler InitialSyncCompleted; - - public void StartSync() - { - DequeueQueries(); - } - - private void DequeueQueries() - { - while (!_syncQueries.IsEmpty) - { - var query = _syncQueries.Dequeue(); - - _parent.SendText(query); - } - - InitialQueryMessagesSent(); - } - - public void AddQueryToQueue(string query) - { - _syncQueries.Enqueue(query); - } - - public void LoginMessageReceived() - { - LoginMessageWasReceived = true; - Debug.Console(1, this, "Login Message Received."); - CheckSyncStatus(); - } - - public void InitialQueryMessagesSent() - { - InitialQueryMessagesWereSent = true; - Debug.Console(1, this, "Query Messages Sent."); - CheckSyncStatus(); - } - - public void LastQueryResponseReceived() - { - LastQueryResponseWasReceived = true; - Debug.Console(1, this, "Last Query Response Received."); - CheckSyncStatus(); - } - - public void CamerasSetUp() - { - CamerasHaveBeenSetUp = true; - Debug.Console(1, this, "Cameras Set Up."); - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - _syncQueries.Clear(); - LoginMessageWasReceived = false; - InitialQueryMessagesWereSent = false; - LastQueryResponseWasReceived = false; - CamerasHaveBeenSetUp = false; - InitialSyncComplete = false; - } - - private void CheckSyncStatus() - { - if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && - CamerasHaveBeenSetUp) - { - InitialSyncComplete = true; - Debug.Console(1, this, "Initial Codec Sync Complete!"); - } - else - { - InitialSyncComplete = false; - } - } - } - - public class ZoomRoomFactory : EssentialsDeviceFactory - { - public ZoomRoomFactory() - { - TypeNames = new List {"zoomroom"}; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new ZoomRoom Device"); - var comm = CommFactory.CreateCommForDevice(dc); - return new ZoomRoom(dc, comm); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, + IRouting, + IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraAutoMode, + IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing + { + private const long MeetingRefreshTimer = 60000; + private const uint DefaultMeetingDurationMin = 30; + private const string Delimiter = "\x0D\x0A"; + private readonly CrestronQueue _receiveQueue; + + + private readonly Thread _receiveThread; + + private readonly ZoomRoomSyncState _syncState; + public bool CommDebuggingIsOn; + private CodecDirectory _currentDirectoryResult; + private uint _jsonCurlyBraceCounter; + private bool _jsonFeedbackMessageIsIncoming; + private StringBuilder _jsonMessage; + private int _previousVolumeLevel; + private CameraBase _selectedCamera; + + private readonly ZoomRoomPropertiesConfig _props; + + public ZoomRoom(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + _props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + // The queue that will collect the repsonses in the order they are received + _receiveQueue = new CrestronQueue(1024); + + // The thread responsible for dequeuing and processing the messages + _receiveThread = new Thread(o => ProcessQueue(), null) {Priority = Thread.eThreadPriority.MediumPriority}; + + Communication = comm; + + if (_props.CommunicationMonitorProperties != null) + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, + _props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, + "zStatus SystemUnit\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + Status = new ZoomRoomStatus(); + + Configuration = new ZoomRoomConfiguration(); + + CodecInfo = new ZoomRoomInfo(Status, Configuration); + + _syncState = new ZoomRoomSyncState(Key + "--Sync", this); + + _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; + PortGather.LineReceived += Port_LineReceived; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + + Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + + CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); + + CameraAutoModeIsOnFeedback = new BoolFeedback(CameraAutoModeIsOnFeedbackFunc); + + CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); + + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + + SetUpFeedbackActions(); + + Cameras = new List(); + + SetUpDirectory(); + + Participants = new CodecParticipants(); + + SupportsCameraOff = _props.SupportsCameraOff; + SupportsCameraAutoMode = _props.SupportsCameraAutoMode; + + PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); + CallerIdNameFeedback = new StringFeedback(CallerIdNameFeedbackFunc); + CallerIdNumberFeedback = new StringFeedback(CallerIdNumberFeedbackFunc); + } + + public CommunicationGather PortGather { get; private set; } + + public ZoomRoomStatus Status { get; private set; } + + public ZoomRoomConfiguration Configuration { get; private set; } + + //CTimer LoginMessageReceivedTimer; + //CTimer RetryConnectionTimer; + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get { return () => Configuration.Call.Microphone.Mute; } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get { return () => false; } + } + + protected override Func SharingSourceFeedbackFunc + { + get { return () => Status.Sharing.dispState; } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get { return () => Status.Call.Sharing.IsSharing; } + } + + protected Func FarEndIsSharingContentFeedbackFunc + { + get { return () => Status.Call.Sharing.State == zEvent.eSharingState.Receiving; } + } + + protected override Func MuteFeedbackFunc + { + get { return () => Configuration.Audio.Output.Volume == 0; } + } + + //protected Func RoomIsOccupiedFeedbackFunc + //{ + // get + // { + // return () => false; + // } + //} + + //protected Func PeopleCountFeedbackFunc + //{ + // get + // { + // return () => 0; + // } + //} + + protected Func SelfViewIsOnFeedbackFunc + { + get { return () => !Configuration.Video.HideConfSelfVideo; } + } + + protected Func CameraIsOffFeedbackFunc + { + get { return () => Configuration.Call.Camera.Mute; } + } + + protected Func CameraAutoModeIsOnFeedbackFunc + { + get { return () => false; } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return + () => + _currentSelfviewPipPosition != null + ? _currentSelfviewPipPosition.Command ?? "Unknown" + : "Unknown"; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get { return () => ""; } + } + + protected Func LocalLayoutIsProminentFeedbackFunc + { + get { return () => false; } + } + + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingOutputPort Output1 { get; private set; } + + #region ICommunicationMonitor Members + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #endregion + + #region IHasCodecCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + public CameraBase SelectedCamera + { + get { return _selectedCamera; } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) + { + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + + public StringFeedback SelectedCameraFeedback { get; private set; } + + public void SelectCamera(string key) + { + if (Cameras == null) + { + return; + } + + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + { + Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); + } + } + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + #region IHasCodecSelfView Members + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public void SelfViewModeOn() + { + SendText("zConfiguration Video hide_conf_self_video: off"); + } + + public void SelfViewModeOff() + { + SendText("zConfiguration Video hide_conf_self_video: on"); + } + + public void SelfViewModeToggle() + { + if (SelfviewIsOnFeedback.BoolValue) + { + SelfViewModeOff(); + } + else + { + SelfViewModeOn(); + } + } + + #endregion + + #region IHasDirectoryHistoryStack Members + + public event EventHandler DirectoryResultReturned; + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecDirectory CurrentDirectoryResult + { + get { return _currentDirectoryResult; } + } + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + public void SearchDirectory(string searchString) + { + var directoryResults = new CodecDirectory(); + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll( + c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); + + DirectoryBrowseHistoryStack.Clear(); + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void GetDirectoryFolderContents(string folderId) + { + var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); + + DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); + + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistoryStack.Clear(); + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(DirectoryRoot); + } + + public void GetDirectoryParentFolderContents() + { + if (DirectoryBrowseHistoryStack.Count == 0) + { + return; + } + + var currentDirectory = DirectoryBrowseHistoryStack.Pop(); + + _currentDirectoryResult = currentDirectory; + + OnDirectoryResultReturned(currentDirectory); + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + public List DirectoryBrowseHistory { get; private set; } + + public Stack DirectoryBrowseHistoryStack { get; private set; } + + #endregion + + #region IHasScheduleAwareness Members + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + public void GetSchedule() + { + GetBookings(); + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + } + + #endregion + + private void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + SetUpRouting(); + + SetIsReady(); + } + + private void SetUpCallFeedbackActions() + { + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + } + + /// + /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. + /// + private void SetUpFeedbackActions() + { + Configuration.Audio.Output.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Volume") + { + VolumeLevelFeedback.FireUpdate(); + MuteFeedback.FireUpdate(); + } + }; + + Configuration.Call.Microphone.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Mute") + { + PrivacyModeIsOnFeedback.FireUpdate(); + } + }; + + Configuration.Video.PropertyChanged += (o, a) => + { + if (a.PropertyName == "HideConfSelfVideo") + { + SelfviewIsOnFeedback.FireUpdate(); + } + }; + Configuration.Video.Camera.PropertyChanged += (o, a) => + { + if (a.PropertyName == "SelectedId") + { + SelectCamera(Configuration.Video.Camera.SelectedId); + // this will in turn fire the affected feedbacks + } + }; + + Configuration.Call.Camera.PropertyChanged += (o, a) => + { + Debug.Console(1, this, "Configuration.Call.Camera.PropertyChanged: {0}", a.PropertyName); + + if (a.PropertyName != "Mute") return; + + CameraIsOffFeedback.FireUpdate(); + CameraAutoModeIsOnFeedback.FireUpdate(); + }; + + Configuration.Call.Layout.PropertyChanged += (o, a) => + { + if (a.PropertyName != "Position") return; + + ComputeSelfviewPipStatus(); + + SelfviewPipPositionFeedback.FireUpdate(); + }; + + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + + Status.Sharing.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "dispState": + SharingSourceFeedback.FireUpdate(); + break; + case "password": + break; + } + }; + + Status.PhoneCall.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "IsIncomingCall": + Debug.Console(1, this, "Incoming Phone Call: {0}", Status.PhoneCall.IsIncomingCall); + break; + case "PeerDisplayName": + Debug.Console(1, this, "Peer Display Name: {0}", Status.PhoneCall.PeerDisplayName); + CallerIdNameFeedback.FireUpdate(); + break; + case "PeerNumber": + Debug.Console(1, this, "Peer Number: {0}", Status.PhoneCall.PeerNumber); + CallerIdNumberFeedback.FireUpdate(); + break; + case "OffHook": + Debug.Console(1, this, "Phone is OffHook: {0}", Status.PhoneCall.OffHook); + PhoneOffHookFeedback.FireUpdate(); + break; + } + }; + } + + private void SetUpDirectory() + { + DirectoryRoot = new CodecDirectory(); + + DirectoryBrowseHistory = new List(); + DirectoryBrowseHistoryStack = new Stack(); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => _currentDirectoryResult != DirectoryRoot); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + } + + private void SetUpRouting() + { + // Set up input ports + CreateOsdSource(); + InputPorts.Add(CodecOsdIn); + + // Set up output ports + OutputPorts.Add(Output1); + } + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + private void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + + //foreach(var input in Status.Video. + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", + ConsoleAccessLevelEnum.AccessOperator); + if (!_props.DisablePhonebookAutoDownload) + { + CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), + "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", + ConsoleAccessLevelEnum.AccessOperator); + } + + CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", + "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += socket_ConnectionChange; + } + + CommDebuggingIsOn = false; + + Communication.Connect(); + + CommunicationMonitor.Start(); + + return base.CustomActivate(); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); + if (e.Client.IsConnected) + { + } + else + { + _syncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + } + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + { + Debug.Console(1, this, "Sending: '{0}'", command); + } + + Communication.SendText(command + Delimiter); + } + + /// + /// Gathers responses and enqueues them. + /// + /// + /// + private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + //if (CommDebuggingIsOn) + // Debug.Console(1, this, "Gathered: '{0}'", args.Text); + + _receiveQueue.Enqueue(args.Text); + + // If the receive thread has for some reason stopped, this will restart it + if (_receiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) + { + _receiveThread.Start(); + } + } + + + /// + /// Runs in it's own thread to dequeue messages in the order they were received to be processed + /// + /// + private object ProcessQueue() + { + try + { + while (true) + { + var message = _receiveQueue.Dequeue(); + + ProcessMessage(message); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error Processing Queue: {0}", e); + } + + return null; + } + + + /// + /// Queues the initial queries to be sent upon connection + /// + private void SetUpSyncQueries() + { + // zStatus + _syncState.AddQueryToQueue("zStatus Call Status"); + _syncState.AddQueryToQueue("zStatus Audio Input Line"); + _syncState.AddQueryToQueue("zStatus Audio Output Line"); + _syncState.AddQueryToQueue("zStatus Video Camera Line"); + _syncState.AddQueryToQueue("zStatus Video Optimizable"); + _syncState.AddQueryToQueue("zStatus Capabilities"); + _syncState.AddQueryToQueue("zStatus Sharing"); + _syncState.AddQueryToQueue("zStatus CameraShare"); + _syncState.AddQueryToQueue("zStatus Call Layout"); + _syncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); + _syncState.AddQueryToQueue("zStatus NumberOfScreens"); + + // zConfiguration + + _syncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); + _syncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); + _syncState.AddQueryToQueue("zConfiguration Call Camera Mute"); + _syncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); + _syncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); + _syncState.AddQueryToQueue("zConfiguration Audio Input volume"); + _syncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Output volume"); + _syncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); + _syncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); + _syncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); + _syncState.AddQueryToQueue("zConfiguration Client appVersion"); + _syncState.AddQueryToQueue("zConfiguration Client deviceSystem"); + _syncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Style"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Size"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Position"); + _syncState.AddQueryToQueue("zConfiguration Call Lock Enable"); + _syncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); + + // zCommand + + if (!_props.DisablePhonebookAutoDownload) + { + _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); + } + + _syncState.AddQueryToQueue("zCommand Bookings List"); + _syncState.AddQueryToQueue("zCommand Call ListParticipants"); + _syncState.AddQueryToQueue("zCommand Call Info"); + + + _syncState.StartSync(); + } + + /// + /// Processes messages as they are dequeued + /// + /// + private void ProcessMessage(string message) + { + // Counts the curly braces + if (message.Contains("client_loop: send disconnect: Broken pipe")) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, + "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); + + return; + } + + if (message.Contains('{')) + { + _jsonCurlyBraceCounter++; + } + + if (message.Contains('}')) + { + _jsonCurlyBraceCounter--; + } + + Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); + + if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) + // Check for the beginning of a new JSON message + { + _jsonFeedbackMessageIsIncoming = true; + _jsonCurlyBraceCounter = 1; // reset the counter for each new message + + _jsonMessage = new StringBuilder(); + + _jsonMessage.Append(message); + + if (CommDebuggingIsOn) + { + Debug.Console(2, this, "Incoming JSON message..."); + } + + return; + } + if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) + // Check for the end of a JSON message + { + _jsonMessage.Append(message); + + if (_jsonCurlyBraceCounter == 0) + { + _jsonFeedbackMessageIsIncoming = false; + + if (CommDebuggingIsOn) + { + Debug.Console(2, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); + } + + // Forward the complete message to be deserialized + DeserializeResponse(_jsonMessage.ToString()); + } + + //JsonMessage = new StringBuilder(); + return; + } + + // NOTE: This must happen after the above conditions have been checked + // Append subsequent partial JSON fragments to the string builder + if (_jsonFeedbackMessageIsIncoming) + { + _jsonMessage.Append(message); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (CommDebuggingIsOn) + { + Debug.Console(1, this, "Non-JSON response: '{0}'", message); + } + + _jsonCurlyBraceCounter = 0; // reset on non-JSON response + + if (!_syncState.InitialSyncComplete) + { + switch (message.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + _syncState.LoginMessageReceived(); + + // Fire up a thread to send the intial commands. + CrestronInvoke.BeginInvoke(o => + { + Thread.Sleep(100); + // disable echo of commands + SendText("echo off"); + Thread.Sleep(100); + // set feedback exclusions + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); + Thread.Sleep(100); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); + Thread.Sleep(100); + + if (!_props.DisablePhonebookAutoDownload) + { + SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + } + // switch to json format + SendText("format json"); + }); + + break; + } + } + } + } + + /// + /// Deserializes a JSON formatted response + /// + /// + private void DeserializeResponse(string response) + { + try + { + var trimmedResponse = response.Trim(); + + if (trimmedResponse.Length <= 0) + { + return; + } + + var message = JObject.Parse(trimmedResponse); + + var eType = + (eZoomRoomResponseType) + Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); + + var topKey = message["topKey"].Value(); + + var responseObj = message[topKey]; + + Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); + + switch (eType) + { + case eZoomRoomResponseType.zConfiguration: + { + switch (topKey.ToLower()) + { + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); + + break; + } + case "audio": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); + + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); + + break; + } + case "client": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zCommand: + { + switch (topKey.ToLower()) + { + case "inforesult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); + break; + } + case "phonebooklistresult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + + if (!PhonebookSyncState.InitialSyncComplete) + { + PhonebookSyncState.InitialPhonebookFoldersReceived(); + PhonebookSyncState.PhonebookRootEntriesReceived(); + PhonebookSyncState.SetPhonebookHasFolders(false); + PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); + } + + var directoryResults = + zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + + DirectoryRoot = directoryResults; + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(directoryResults); + + break; + } + case "listparticipantsresult": + { + Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); + + switch (responseObj.Type) + { + case JTokenType.Array: + Status.Call.Participants = + JsonConvert.DeserializeObject>( + responseObj.ToString()); + break; + case JTokenType.Object: + { + // this is a single participant event notification + + var participant = + JsonConvert.DeserializeObject( + responseObj.ToString()); + + if (participant != null) + { + switch (participant.Event) + { + case "ZRCUserChangedEventUserInfoUpdated": + case "ZRCUserChangedEventLeftMeeting": + { + var existingParticipant = + Status.Call.Participants.FirstOrDefault( + p => p.UserId.Equals(participant.UserId)); + + if (existingParticipant != null) + { + switch (participant.Event) + { + case "ZRCUserChangedEventLeftMeeting": + Status.Call.Participants.Remove(existingParticipant); + break; + case "ZRCUserChangedEventUserInfoUpdated": + JsonConvert.PopulateObject(responseObj.ToString(), + existingParticipant); + break; + } + } + } + break; + case "ZRCUserChangedEventJoinedMeeting": + Status.Call.Participants.Add(participant); + break; + } + } + } + break; + } + + var participants = + zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( + Status.Call.Participants); + + Participants.CurrentParticipants = participants; + + PrintCurrentCallParticipants(); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zEvent: + { + switch (topKey.ToLower()) + { + case "phonebook": + { + if (responseObj["Updated Contact"] != null) + { + var updatedContact = + JsonConvert.DeserializeObject( + responseObj["Updated Contact"].ToString()); + + var existingContact = + Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); + + if (existingContact != null) + { + // Update existing contact + JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), + existingContact); + } + } + else if (responseObj["Added Contact"] != null) + { + var jToken = responseObj["Updated Contact"]; + if (jToken != null) + { + var newContact = + JsonConvert.DeserializeObject( + jToken.ToString()); + + // Add a new contact + Status.Phonebook.Contacts.Add(newContact); + } + } + + break; + } + case "bookingslistresult": + { + if (!_syncState.InitialSyncComplete) + { + _syncState.LastQueryResponseReceived(); + } + + var codecBookings = JsonConvert.DeserializeObject>( + responseObj.ToString()); + + if (codecBookings != null && codecBookings.Count > 0) + { + CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult( + codecBookings, CodecSchedule.MeetingWarningMinutes); + } + + break; + } + case "bookings updated": + { + GetBookings(); + + break; + } + case "sharingstate": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); + + SetLayout(); + + break; + } + case "incomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var newCall = new CodecActiveCallItem + { + Direction = eCodecCallDirection.Incoming, + Status = eCodecCallStatus.Ringing, + Type = eCodecCallType.Unknown, + Name = incomingCall.callerName, + Id = incomingCall.callerJID + }; + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } + + break; + } + case "treatedincomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var existingCall = + ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); + + if (existingCall != null) + { + existingCall.Status = !incomingCall.accepted + ? eCodecCallStatus.Disconnected + : eCodecCallStatus.Connecting; + + OnCallStatusChange(existingCall); + } + + UpdateCallStatus(); + } + + break; + } + case "calldisconnect": + { + var disconnectEvent = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (disconnectEvent.Successful) + { + if (ActiveCalls.Count > 0) + { + var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); + + if (activeCall != null) + { + activeCall.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(activeCall); + } + } + var emptyList = new List(); + Participants.CurrentParticipants = emptyList; + } + + UpdateCallStatus(); + break; + } + case "callconnecterror": + { + UpdateCallStatus(); + break; + } + case "videounmuterequest": + { + // TODO: notify room of a request to unmute video + break; + } + case "meetingneedspassword": + { + // TODO: notify user to enter a password + break; + } + case "needwaitforhost": + { + var needWait = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (needWait.Wait) + { + // TODO: notify user to wait for host + } + + break; + } + case "openvideofailforhoststop": + { + // TODO: notify user that host has disabled unmuting video + break; + } + case "updatedcallrecordinfo": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); + + break; + } + case "phonecallstatus": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zStatus: + { + switch (topKey.ToLower()) + { + case "login": + { + _syncState.LoginMessageReceived(); + + if (!_syncState.InitialQueryMessagesWereSent) + { + SetUpSyncQueries(); + } + + JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); + + break; + } + case "systemunit": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); + + break; + } + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); + + UpdateCallStatus(); + + break; + } + case "capabilities": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); + break; + } + case "sharing": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); + + break; + } + case "numberofscreens": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); + break; + } + case "camerashare": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); + break; + } + case "layout": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); + break; + } + case "audio input line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); + break; + } + case "audio output line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); + break; + } + case "video camera line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); + + if (!_syncState.CamerasHaveBeenSetUp) + { + SetUpCameras(); + } + + break; + } + default: + { + break; + } + } + + break; + } + default: + { + Debug.Console(1, "Unknown Response Type:"); + break; + } + } + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + } + } + + private void SetLayout() + { + if (!_props.AutoDefaultLayouts) return; + + if ( + (Status.Call.Sharing.State == zEvent.eSharingState.Receiving || + Status.Call.Sharing.State == zEvent.eSharingState.Sending)) + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultSharingLayout)); + } + else + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultCallLayout)); + } + } + + public void PrintCurrentCallParticipants() + { + if (Debug.Level <= 0) + { + return; + } + + Debug.Console(1, this, "****************************Call Participants***************************"); + foreach (var participant in Participants.CurrentParticipants) + { + Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name, + participant.AudioMuteFb, participant.IsHost); + } + Debug.Console(1, this, "************************************************************************"); + } + + /// + /// Retrieves bookings list + /// + private void GetBookings() + { + SendText("zCommand Bookings List"); + } + + + /// + /// Updates the current call status + /// + private void UpdateCallStatus() + { + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + if (Status.Call != null) + { + var callStatus = Status.Call.Status; + + // If not currently in a meeting, intialize the call object + if (callStatus != zStatus.eCallStatus.IN_MEETING && callStatus != zStatus.eCallStatus.CONNECTING_MEETING) + { + Debug.Console(1, this, "Creating new Status.Call object"); + Status.Call = new zStatus.Call {Status = callStatus}; + + SetUpCallFeedbackActions(); + } + + if (ActiveCalls.Count == 0) + { + if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || + callStatus == zStatus.eCallStatus.IN_MEETING) + { + var newStatus = eCodecCallStatus.Unknown; + + switch (callStatus) + { + case zStatus.eCallStatus.CONNECTING_MEETING: + newStatus = eCodecCallStatus.Connecting; + break; + case zStatus.eCallStatus.IN_MEETING: + newStatus = eCodecCallStatus.Connected; + break; + } + + var newCall = new CodecActiveCallItem {Status = newStatus}; + + ActiveCalls.Add(newCall); + + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + OnCallStatusChange(newCall); + } + } + else + { + var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); + + switch (callStatus) + { + case zStatus.eCallStatus.IN_MEETING: + existingCall.Status = eCodecCallStatus.Connected; + break; + case zStatus.eCallStatus.NOT_IN_MEETING: + existingCall.Status = eCodecCallStatus.Disconnected; + break; + } + + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + OnCallStatusChange(existingCall); + } + } + + Debug.Console(1, this, "****************************Active Calls*********************************"); + + // Clean up any disconnected calls left in the list + for (int i = 0; i < ActiveCalls.Count; i++) + { + var call = ActiveCalls[i]; + + Debug.Console(1, this, + @"Name: {0} + ID: {1} + IsActive: {2} + Status: {3} + Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); + + if (!call.IsActiveCall) + { + Debug.Console(1, this, "******Removing Inactive Call: {0}******", call.Name); + ActiveCalls.Remove(call); + } + } + Debug.Console(1, this, "**************************************************************************"); + + //clear participants list after call cleanup + if (ActiveCalls.Count == 0) + { + Participants.CurrentParticipants = new List(); + } + } + + protected override void OnCallStatusChange(CodecActiveCallItem item) + { + base.OnCallStatusChange(item); + + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + if (_props.AutoDefaultLayouts) + { + SetLayout(); + } + } + + public override void StartSharing() + { + SendText("zCommand Call Sharing HDMI Start"); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + SendText("zCommand Call Sharing Disconnect"); + } + + public override void PrivacyModeOn() + { + SendText("zConfiguration Call Microphone Mute: on"); + } + + public override void PrivacyModeOff() + { + SendText("zConfiguration Call Microphone Mute: off"); + } + + public override void PrivacyModeToggle() + { + if (PrivacyModeIsOnFeedback.BoolValue) + { + PrivacyModeOff(); + } + else + { + PrivacyModeOn(); + } + } + + public override void MuteOff() + { + SetVolume((ushort) _previousVolumeLevel); + } + + public override void MuteOn() + { + _previousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall + + SetVolume(0); + } + + public override void MuteToggle() + { + if (MuteFeedback.BoolValue) + { + MuteOff(); + } + else + { + MuteOn(); + } + } + + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + // TODO: Implment volume decrement that calls SetVolume() + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + // TODO: Implment volume decrement that calls SetVolume() + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("zConfiguration Audio Output volume: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + } + + /// + /// + /// + public override void StandbyActivate() + { + // No corresponding function on device + } + + /// + /// + /// + public override void StandbyDeactivate() + { + // No corresponding function on device + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); + } + + public override void ExecuteSwitch(object selector) + { + var action = selector as Action; + if (action == null) + { + return; + } + + action(); + } + + public void AcceptCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + AcceptCall(incomingCall); + } + + public override void AcceptCall(CodecActiveCallItem call) + { + SendText(string.Format("zCommand Call Accept callerJID: {0}", call.Id)); + + call.Status = eCodecCallStatus.Connected; + + OnCallStatusChange(call); + + UpdateCallStatus(); + } + + public void RejectCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + RejectCall(incomingCall); + } + + public override void RejectCall(CodecActiveCallItem call) + { + SendText(string.Format("zCommand Call Reject callerJID: {0}", call.Id)); + + call.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(call); + + UpdateCallStatus(); + } + + public override void Dial(Meeting meeting) + { + Debug.Console(1, this,"Dialing meeting.Id: {0} Title: {1}", meeting.Id, meeting.Title); + SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); + } + + public override void Dial(string number) + { + SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); + } + + /// + /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. + /// Currently only invites a single user + /// + /// + public override void Dial(IInvitableContact contact) + { + var ic = contact as zStatus.ZoomDirectoryContact; + + if (ic != null) + { + Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); + + if (!IsInCall) + { + SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, + ic.ContactId)); + } + else + { + SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); + } + } + } + + public override void EndCall(CodecActiveCallItem call) + { + SendText("zCommand Call Disconnect"); + } + + public override void EndAllCalls() + { + SendText("zCommand Call Disconnect"); + } + + public override void SendDtmf(string s) + { + SendDtmfToPhone(s); + } + + /// + /// Call when directory results are updated + /// + /// + private void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + + //PrintDirectory(result); + } + + /// + /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data + /// + private void SetUpCameras() + { + SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + foreach (var cam in Status.Cameras) + { + var camera = new ZoomRoomCamera(cam.id, cam.Name, this); + + Cameras.Add(camera); + + if (cam.Selected) + { + SelectedCamera = camera; + } + } + + if (IsInCall) + { + UpdateFarEndCameras(); + } + + _syncState.CamerasSetUp(); + } + + /// + /// Dynamically creates far end cameras for call participants who have far end control enabled. + /// + private void UpdateFarEndCameras() + { + // TODO: set up far end cameras for the current call + } + + #region Implementation of IHasParticipants + + public CodecParticipants Participants { get; private set; } + + #endregion + + #region Implementation of IHasCameraOff + + public BoolFeedback CameraIsOffFeedback { get; private set; } + + public void CameraOff() + { + SendText("zConfiguration Call Camera Mute: On"); + } + + #endregion + + #region Implementation of IHasCameraAutoMode + + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOn() + { + throw new NotImplementedException("Zoom Room Doesn't support camera auto mode"); + } + + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOff() + { + SendText("zConfiguration Call Camera Mute: Off"); + } + + public void CameraAutoModeToggle() + { + throw new NotImplementedException("Zoom Room doesn't support camera auto mode"); + } + + public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } + + #endregion + + #region Implementation of IHasFarEndContentStatus + + public BoolFeedback ReceivingContent { get; private set; } + + #endregion + + #region Implementation of IHasSelfviewPosition + + private CodecCommandWithLabel _currentSelfviewPipPosition; + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(String.Format("zConfiguration Call Layout Position: {0}", position.Command)); + } + + public void SelfviewPipPositionToggle() + { + if (_currentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) + // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("UpLeft", "Center Left"), + new CodecCommandWithLabel("UpRight", "Center Right"), + new CodecCommandWithLabel("DownRight", "Lower Right"), + new CodecCommandWithLabel("DownLeft", "Lower Left") + }; + + private void ComputeSelfviewPipStatus() + { + _currentSelfviewPipPosition = + SelfviewPipPositions.FirstOrDefault( + p => p.Command.ToLower().Equals(Configuration.Call.Layout.Position.ToString().ToLower())); + } + + #endregion + + #region Implementation of IHasPhoneDialing + + private Func PhoneOffHookFeedbackFunc {get {return () => Status.PhoneCall.OffHook; }} + private Func CallerIdNameFeedbackFunc { get { return () => Status.PhoneCall.PeerDisplayName; } } + private Func CallerIdNumberFeedbackFunc { get { return () => Status.PhoneCall.PeerNumber; } } + + public BoolFeedback PhoneOffHookFeedback { get; private set; } + public StringFeedback CallerIdNameFeedback { get; private set; } + public StringFeedback CallerIdNumberFeedback { get; private set; } + + public void DialPhoneCall(string number) + { + SendText(String.Format("zCommand Dial PhoneCallOut Number: {0}", number)); + } + + public void EndPhoneCall() + { + SendText(String.Format("zCommand Dial PhoneHangUp CallId: {0}", Status.PhoneCall.CallId)); + } + + public void SendDtmfToPhone(string digit) + { + SendText(String.Format("zCommand SendSipDTMF CallId: {0} Key: {1}", Status.PhoneCall.CallId, digit)); + } + + #endregion + } + + /// + /// Zoom Room specific info object + /// + public class ZoomRoomInfo : VideoCodecInfo + { + public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) + { + Status = status; + Configuration = configuration; + } + + public ZoomRoomStatus Status { get; private set; } + public ZoomRoomConfiguration Configuration { get; private set; } + + public override bool AutoAnswerEnabled + { + get { return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; } + } + + public override string E164Alias + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) + { + return Status.SystemUnit.MeetingNumber; + } + return string.Empty; + } + } + + public override string H323Id + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) + { + return Status.Call.Info.meeting_list_item.third_party.h323_address; + } + return string.Empty; + } + } + + public override string IpAddress + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) + { + return Status.SystemUnit.RoomInfo.AccountEmail; + } + return string.Empty; + } + } + + public override bool MultiSiteOptionIsEnabled + { + get { return true; } + } + + public override string SipPhoneNumber + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) + { + return Status.Call.Info.dialIn; + } + return string.Empty; + } + } + + public override string SipUri + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) + { + return Status.Call.Info.meeting_list_item.third_party.sip_address; + } + return string.Empty; + } + } + } + + /// + /// Tracks the initial sycnronization state when establishing a new connection + /// + public class ZoomRoomSyncState : IKeyed + { + private readonly ZoomRoom _parent; + private readonly CrestronQueue _syncQueries; + private bool _initialSyncComplete; + + public ZoomRoomSyncState(string key, ZoomRoom parent) + { + _parent = parent; + Key = key; + _syncQueries = new CrestronQueue(50); + CodecDisconnected(); + } + + public bool InitialSyncComplete + { + get { return _initialSyncComplete; } + private set + { + if (value) + { + var handler = InitialSyncCompleted; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + _initialSyncComplete = value; + } + } + + public bool LoginMessageWasReceived { get; private set; } + + public bool InitialQueryMessagesWereSent { get; private set; } + + public bool LastQueryResponseWasReceived { get; private set; } + + public bool CamerasHaveBeenSetUp { get; private set; } + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; + + public void StartSync() + { + DequeueQueries(); + } + + private void DequeueQueries() + { + while (!_syncQueries.IsEmpty) + { + var query = _syncQueries.Dequeue(); + + _parent.SendText(query); + } + + InitialQueryMessagesSent(); + } + + public void AddQueryToQueue(string query) + { + _syncQueries.Enqueue(query); + } + + public void LoginMessageReceived() + { + LoginMessageWasReceived = true; + Debug.Console(1, this, "Login Message Received."); + CheckSyncStatus(); + } + + public void InitialQueryMessagesSent() + { + InitialQueryMessagesWereSent = true; + Debug.Console(1, this, "Query Messages Sent."); + CheckSyncStatus(); + } + + public void LastQueryResponseReceived() + { + LastQueryResponseWasReceived = true; + Debug.Console(1, this, "Last Query Response Received."); + CheckSyncStatus(); + } + + public void CamerasSetUp() + { + CamerasHaveBeenSetUp = true; + Debug.Console(1, this, "Cameras Set Up."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + _syncQueries.Clear(); + LoginMessageWasReceived = false; + InitialQueryMessagesWereSent = false; + LastQueryResponseWasReceived = false; + CamerasHaveBeenSetUp = false; + InitialSyncComplete = false; + } + + private void CheckSyncStatus() + { + if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && + CamerasHaveBeenSetUp) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + { + InitialSyncComplete = false; + } + } + } + + public class ZoomRoomFactory : EssentialsDeviceFactory + { + public ZoomRoomFactory() + { + TypeNames = new List {"zoomroom"}; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new ZoomRoom Device"); + var comm = CommFactory.CreateCommForDevice(dc); + return new ZoomRoom(dc, comm); + } + } } \ No newline at end of file diff --git a/packages.config b/packages.config index c138dd1b..b60e8578 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file