From 9b64b7b7f38bb88e5b1b91edeb194bb5f8542f21 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 11:40:27 -0600 Subject: [PATCH 01/28] fix:(Essentials) Created MeetingInfo Object if it didn't previously exist --- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) 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 379b1621..d146ec4e 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 @@ -626,6 +626,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom case "isDirectPresentationConnected": case "isSharingBlackMagic": { + Debug.Console(2, this, "Updating sharing status: {0}", a.PropertyName); + + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("", "", "", "", + GetSharingStatus(), GetIsHostMyself()); + } // Update the share status of the meeting info var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); MeetingInfo = meetingInfo; @@ -1828,6 +1835,12 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private bool GetIsHostMyself() { + if (Participants.CurrentParticipants.Count == 0) + { + Debug.Console(2, this, "No current participants"); + return false; + } + var host = Participants.Host; if(host == null) From d50027cc826d41f71a606262f5d625e14af00353 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 17:33:53 -0600 Subject: [PATCH 02/28] feat:(Essentials) Add IHasPresentationOnlyMeeting interface --- .../Essentials Devices Common.csproj | 1 + .../Interfaces/IHasPresentationOnlyMeeting.cs | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs 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 cff1e4c0..fc5f68d4 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 @@ -122,6 +122,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs new file mode 100644 index 00000000..06730d2a --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasPresentationOnlyMeeting.cs @@ -0,0 +1,18 @@ +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces +{ + public interface IHasPresentationOnlyMeeting + { + void StartSharingOnlyMeeting(); + void StartSharingOnlyMeeting(eSharingMeetingMode mode); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration); + void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password); + void StartNormalMeetingFromSharingOnlyMeeting(); + } + + public enum eSharingMeetingMode + { + None, + Laptop, + Ios, + } +} \ No newline at end of file From c255ae1525056acfcc4534baf5dc04239d61a631 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 18:11:14 -0600 Subject: [PATCH 03/28] feat:(Essentials) Add IsSharingMeeting property --- .../VideoCodec/Interfaces/IHasMeetingInfo.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs index 236fde80..cd9e2906 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs @@ -36,8 +36,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces public string ShareStatus { get; private set; } [JsonProperty("isHost")] public Boolean IsHost { get; private set; } + [JsonProperty("isSharingMeeting")] + public Boolean IsSharingMeeting { get; private set; } - public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost) + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting) { Id = id; Name = name; @@ -45,6 +47,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces Password = password; ShareStatus = shareStatus; IsHost = isHost; + IsSharingMeeting = isSharingMeeting; } } From 2d0dcd7336697fdc9eeec2a0466abe468fadeee1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 18:11:48 -0600 Subject: [PATCH 04/28] feat:(Essentials) Add StartLocalPresentMeeting class --- .../VideoCodec/ZoomRoom/ResponseObjects.cs | 4 ++++ 1 file changed, 4 insertions(+) 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 aac45f51..35b6c3b2 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 @@ -756,6 +756,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public class zEvent { + public class StartLocalPresentMeeting + { + public bool Success { get; set; } + } public class NeedWaitForHost { public bool Wait { get; set; } From 44509dc5ae564ec96973f5a444b784db112818c2 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 18:12:13 -0600 Subject: [PATCH 05/28] feat:(Essentials) Add IsSharingMeeting setting to MeetingInfo --- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) 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 d146ec4e..a426193b 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 @@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom IRouting, IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraMute, IHasCameraAutoMode, IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing, IHasZoomRoomLayouts, IHasParticipantPinUnpin, - IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo + IHasParticipantAudioMute, IHasSelfviewSize, IPasswordPrompt, IHasStartMeeting, IHasMeetingInfo, IHasPresentationOnlyMeeting { private const long MeetingRefreshTimer = 60000; public uint DefaultMeetingDurationMin { get; private set; } @@ -472,7 +472,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ReceivingContent.FireUpdate(); // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); MeetingInfo = meetingInfo; } }; @@ -630,11 +630,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (MeetingInfo == null) { - MeetingInfo = new MeetingInfo("", "", "", "", - GetSharingStatus(), GetIsHostMyself()); + //Ignoring for now, as the CallInfo return will create the appropriate value + return; } // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself()); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); MeetingInfo = meetingInfo; break; } @@ -1206,7 +1206,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Participants.CurrentParticipants = participants; // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself()); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting); MeetingInfo = meetingInfo; PrintCurrentCallParticipants(); @@ -1485,6 +1485,18 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; } + case "startlocalpresentmeeting": + { + var result = JsonConvert.DeserializeObject(responseObj.ToString()); + + if (result.Success) + { + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true); + break; + } + + break; + } default: { break; @@ -1776,8 +1788,6 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected override void OnCallStatusChange(CodecActiveCallItem item) { - base.OnCallStatusChange(item); - if (item.Status == eCodecCallStatus.Connected) { @@ -1792,10 +1802,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom host, Status.Call.Info.meeting_password, GetSharingStatus(), - GetIsHostMyself() + GetIsHostMyself(), + !String.Equals(Status.Call.Info.meeting_type,"NORMAL") ); } + base.OnCallStatusChange(item); + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", Status.Call != null ? Status.Call.Status.ToString() : "no call"); @@ -2153,6 +2166,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void Dial(string number) { + Debug.Console(2, this, "Dialing number: {0}", number); _lastDialedMeetingNumber = number; SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); } @@ -2863,7 +2877,37 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } #endregion - } + + #region Implementation of IHasPresentationOnlyMeeting + + public void StartSharingOnlyMeeting() + { + StartSharingOnlyMeeting(eSharingMeetingMode.None, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode) + { + StartSharingOnlyMeeting(mode, 30, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration) + { + StartSharingOnlyMeeting(mode, duration, String.Empty); + } + + public void StartSharingOnlyMeeting(eSharingMeetingMode mode, ushort duration, string password) + { + SendText(String.Format("zCommand Dial Sharing Duration: {0} DisplayState: {1} Password: {2}", duration, mode, password)); + } + + public void StartNormalMeetingFromSharingOnlyMeeting() + { + Debug.Console(2, this, "Converting Sharing Meeting to Normal Meeting"); + SendText("zCommand call sharing ToNormal"); + } + + #endregion + } /// /// Zoom Room specific info object From f791feb848a17ac5cbbd38a82d35e8e7b62f5215 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 18:12:52 -0600 Subject: [PATCH 06/28] feat:(Essentials) Update UI functionality depending on Zoom Meeting type --- .../Essentials/EssentialsHeaderDriver.cs | 12 +++- ...entialsHuddleVtc1PanelAvFunctionsDriver.cs | 9 ++- .../VC/EssentialsVideoCodecUiDriver.cs | 56 ++++++++++++++++--- 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index 8cff13c2..f42b9de2 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -12,6 +12,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; using PepperDash.Essentials.Room.Config; using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.VideoCodec; @@ -191,6 +192,8 @@ namespace PepperDash.Essentials return; } + var meetingInfoCodec = codec as IHasMeetingInfo; + // Set mode of header button if (!codec.IsInCall) { @@ -210,8 +213,15 @@ namespace PepperDash.Essentials Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); if (codec.ActiveCalls.Count > 0) { - if (codec.ActiveCalls.Count == 1) + if (codec.ActiveCalls.Count == 1 && meetingInfoCodec == null) TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "1 Active Call"); + else if (codec.ActiveCalls.Count == 1 && meetingInfoCodec != null) + { + var headerCallStatusLabel = meetingInfoCodec.MeetingInfo.IsSharingMeeting + ? "Sharing Active" + : "Active Meeting"; + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, headerCallStatusLabel); + } else if (codec.ActiveCalls.Count > 1) TriList.SetString(UIStringJoin.HeaderCallStatusLabel, string.Format("{0} Active Calls", codec.ActiveCalls.Count)); } diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 09443d45..246573a9 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -716,10 +716,17 @@ namespace PepperDash.Essentials if (_isZoomRoomWithNoExternalSources) { - (CurrentRoom as IRunDefaultPresentRoute).RunDefaultPresentRoute(); + CurrentRoom.RunDefaultPresentRoute(); // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); + var presentationMeetingCodec = CurrentRoom.VideoCodec as IHasPresentationOnlyMeeting; + + if (presentationMeetingCodec != null && !CurrentRoom.VideoCodec.IsInCall) + { + presentationMeetingCodec.StartSharingOnlyMeeting(eSharingMeetingMode.Laptop); + } + if (CurrentSourcePageManager != null) CurrentSourcePageManager.Hide(); } diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index 7ec44346..9bbaad6f 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -134,11 +134,12 @@ namespace PepperDash.Essentials.UIDrivers.VC VCControlsInterlock = new JoinedSigInterlock(triList); VCCameraControlModeInterlock = new JoinedSigInterlock(triList); + VCControlsInterlock.HideAndClear(); - if (CodecHasFavorites) + /* if (CodecHasFavorites || codec is IHasZoomRoomLayouts) //Checking for Zoom Room...picked a ZoomRoom specific interface to check for VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadWithFavoritesVisible); else - VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); + VCControlsInterlock.SetButDontShow(UIBoolJoin.VCKeypadVisible); */ StagingBarsInterlock = new JoinedSigInterlock(triList); if(Codec is IHasCallHistory) @@ -320,6 +321,7 @@ namespace PepperDash.Essentials.UIDrivers.VC void Codec_CallStatusChange(object sender, CodecCallStatusItemChangeEventArgs e) { var call = e.CallItem; + var meetingInfoSender = sender as IHasMeetingInfo; switch (e.CallItem.Status) { @@ -354,7 +356,10 @@ namespace PepperDash.Essentials.UIDrivers.VC DialStringBuilder.Remove(0, DialStringBuilder.Length); DialStringFeedback.FireUpdate(); Parent.ShowNotificationRibbon("Disconnected", 2000); + Debug.Console(0, "Setting Connect Button mode to 0"); } + + break; case eCodecCallStatus.Disconnecting: break; @@ -375,12 +380,23 @@ namespace PepperDash.Essentials.UIDrivers.VC ShowIncomingModal(call); break; } - default: - break; } - TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = (ushort)(Codec.IsInCall ? 1 : 0); - - uint stageJoin; + + if (meetingInfoSender != null && Codec.IsInCall) + { + var meetingInfo = meetingInfoSender.MeetingInfo; + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (meetingInfo.IsSharingMeeting ? 2 : 1); + } + else + { + + TriList.UShortInput[UIUshortJoin.VCStagingConnectButtonMode].UShortValue = + (ushort) (Codec.IsInCall ? 1 : 0); + } + + uint stageJoin; if (Codec.IsInCall) stageJoin = UIBoolJoin.VCStagingActivePopoverVisible; else @@ -519,14 +535,35 @@ namespace PepperDash.Essentials.UIDrivers.VC TriList.SetSigFalseAction(UIBoolJoin.VCStagingMeetNowPress, MeetNowPress); TriList.SetSigFalseAction(UIBoolJoin.CallStopSharingPress, CallStopSharingPress); + var meetingInfoCodec = Codec as IHasMeetingInfo; + TriList.SetSigFalseAction(UIBoolJoin.CallEndPress, () => { if (Codec.ActiveCalls.Count > 1) { Parent.PopupInterlock.ShowInterlocked(Parent.CallListOrMeetingInfoPopoverVisibilityJoin); } + else if (meetingInfoCodec != null && Codec.ActiveCalls.Count == 1) + { + var meetingInfo = meetingInfoCodec.MeetingInfo; + + if (meetingInfo != null && meetingInfo.IsSharingMeeting) + { + var presentationMeetingCodec = Codec as IHasPresentationOnlyMeeting; + if (presentationMeetingCodec != null) + { + presentationMeetingCodec.StartNormalMeetingFromSharingOnlyMeeting(); + } + } + else + { + Codec.EndAllCalls(); + } + } else + { Codec.EndAllCalls(); + } }); TriList.SetSigFalseAction(UIBoolJoin.CallEndAllConfirmPress, () => @@ -535,7 +572,7 @@ namespace PepperDash.Essentials.UIDrivers.VC Codec.EndAllCalls(); }); - var meetingInfoCodec = Codec as IHasMeetingInfo; + if (meetingInfoCodec != null) { TriList.SetSigFalseAction(UIBoolJoin.MeetingLeavePress, () => @@ -1801,7 +1838,7 @@ namespace PepperDash.Essentials.UIDrivers.VC /// void PasswordKeypadClear() { - PasswordStringBuilder.Remove(0, SearchStringBuilder.Length); + PasswordStringBuilder.Remove(0, PasswordStringBuilder.Length); PasswordStringFeedback.FireUpdate(); PasswordStringCheckEnables(); @@ -1929,6 +1966,7 @@ namespace PepperDash.Essentials.UIDrivers.VC _passwordPromptDialogVisible = false; Parent.Keyboard.Hide(); TriList.SetBool(UIBoolJoin.PasswordPromptDialogVisible, _passwordPromptDialogVisible); + PasswordKeypadClear(); } } } From 93dfb8780b7753d1255b6ed432b83bf51a020670 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 7 Sep 2021 18:13:18 -0600 Subject: [PATCH 07/28] feat:(Essentials) Add doc info and meeting button text join --- PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs | 5 +++++ PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs index 38a86fbb..6741be35 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIStringlJoin.cs @@ -48,6 +48,11 @@ namespace PepperDash.Essentials /// public const uint MeetingNameText = 1009; + /// + /// 1240 - Used to determine text for meeting start button + /// + public const uint MeetingStartButtonText = 1240; + diff --git a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs index ffc7f19e..73356bf3 100644 --- a/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs +++ b/PepperDashEssentials/UI/JoinConstants/UIUshortJoin.cs @@ -7,7 +7,7 @@ { // Video Codec /// - /// 1234: values 0 = Connect, 1 = End + /// 1234: values 0 = Connect, 1 = End, 2 = Start Meeting /// public const uint VCStagingConnectButtonMode = 1234; From 77134f0a3016f423a75bf69c789236f29fa8d447 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 8 Sep 2021 14:15:52 -0600 Subject: [PATCH 08/28] feat:(Essentials_DM) Update AddInputPortWithDebug Adding an `IVideoAttributesBasic` as a parameter for this method relatively easily allows us to get the reported input resolution when it changes. --- .../Chassis/DmChassisController.cs | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 8c6b2ced..168e51f3 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -816,20 +816,47 @@ namespace PepperDash.Essentials.DM /// void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType) { - AddInputPortWithDebug(cardNum, portName, sigType, portType, null); + //Cast is necessary here to determine the correct overload + AddInputPortWithDebug(cardNum, portName, sigType, portType, null, null); + } + + private void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, + eRoutingPortConnectionType portType, ICec cecPort) + { + //Cast is necessary here to determine the correct overload + AddInputPortWithDebug(cardNum, portName, sigType, portType, cecPort, null); } /// - /// Adds InputPort and sets Port as ICec object + /// Adds InputPort and sets Port as ICec object. If videoAttributesBasic is defined, RoutingPort will be RoutingInputPortWithVideoStatuses /// - void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort) + void AddInputPortWithDebug(uint cardNum, string portName, eRoutingSignalType sigType, eRoutingPortConnectionType portType, ICec cecPort, IVideoAttributesBasic videoAttributesBasic) { var portKey = string.Format("inputCard{0}--{1}", cardNum, portName); Debug.Console(2, this, "Adding input port '{0}'", portKey); - var inputPort = new RoutingInputPort(portKey, sigType, portType, Chassis.Inputs[cardNum], this) + + RoutingInputPort inputPort; + + if (videoAttributesBasic != null) { - FeedbackMatchObject = Chassis.Inputs[cardNum] - }; ; + var statusFuncs = new VideoStatusFuncsWrapper + { + VideoResolutionFeedbackFunc = () => videoAttributesBasic.VideoAttributes.GetVideoResolutionString() + }; + inputPort = new RoutingInputPortWithVideoStatuses(portKey, sigType, portType, + Chassis.Inputs[cardNum], this, statusFuncs) + { + FeedbackMatchObject = Chassis.Inputs[cardNum] + }; + } + else + { + inputPort = new RoutingInputPort(portKey, sigType, portType, + Chassis.Inputs[cardNum], this) + { + FeedbackMatchObject = Chassis.Inputs[cardNum] + }; + } if (cecPort != null) inputPort.Port = cecPort; From d00a31e3a62d496464f31ff3b3bca77bfbb5a033 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 8 Sep 2021 14:17:24 -0600 Subject: [PATCH 09/28] feat(Essentials_DM): Add overloads for `AddDmInCardPorts` & `AddHdmiInCardPorts` --- .../Essentials_DM/Chassis/DmChassisController.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 168e51f3..3e66af96 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -672,12 +672,24 @@ namespace PepperDash.Essentials.DM AddInCardHdmiAndAudioLoopPorts(number); } + void AddDmInCardPorts(uint number, ICec cecPort, IVideoAttributesBasic videoAttributes) + { + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmCat, cecPort, videoAttributes); + AddInCardHdmiAndAudioLoopPorts(number); + } + void AddHdmiInCardPorts(uint number, ICec cecPort) { AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, cecPort); AddInCardHdmiAndAudioLoopPorts(number); } + void AddHdmiInCardPorts(uint number, ICec cecPort, IVideoAttributesBasic videoAttributes) + { + AddInputPortWithDebug(number, "hdmiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, cecPort, videoAttributes); + AddInCardHdmiAndAudioLoopPorts(number); + } + void AddInCardHdmiAndAudioLoopPorts(uint number) { AddOutputPortWithDebug(string.Format("inputCard{0}", number), "hdmiLoopOut", eRoutingSignalType.Audio | eRoutingSignalType.Video, From a28a078c4f671246e5c0e0264ecb0fe7adeccf53 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 8 Sep 2021 15:25:43 -0600 Subject: [PATCH 10/28] feat(Essentials_DM): Update all card creation methods to pass appropriate input --- .../Chassis/DmChassisController.cs | 203 +++++++++++------- 1 file changed, 127 insertions(+), 76 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 3e66af96..8cd53e33 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -497,166 +497,189 @@ namespace PepperDash.Essentials.DM { case "dmchd": { - var inputCard = new DmcHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new DmcHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmchddsp": { - var inputCard = new DmcHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new DmcHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4khd": { - var inputCard = new Dmc4kHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new Dmc4kHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4khddsp": { - var inputCard = new Dmc4kHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); + var inputCard = new Dmc4kHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); } break; case "dmc4kzhd": { - var inputCard = new Dmc4kzHd(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzHd(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); break; + } case "dmc4kzhddsp": { - var inputCard = new Dmc4kzHdDsp(number, this.Chassis); - var cecPort = inputCard.HdmiInput as ICec; - AddHdmiInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzHdDsp(number, Chassis); + AddHdmiInCardPorts(number, inputCard.HdmiInput, inputCard.HdmiInput); break; + } case "dmcc": { - var inputCard = new DmcC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); + var inputCard = new DmcC(number, Chassis); + //DmInput doesn't implement ICec...cast was resulting in null anyway + AddDmInCardPorts(number, null, inputCard.DmInput); } break; case "dmccdsp": { - var inputCard = new DmcCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new DmcCDsp(number, Chassis); + //DmInput doesn't implement ICec...cast was resulting in null anyway + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } + case "dmc4kc": { - var inputCard = new Dmc4kC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kC(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kcdsp": { - var inputCard = new Dmc4kCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kCDsp(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kzc": { - var inputCard = new Dmc4kzC(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); - } + var inputCard = new Dmc4kzC(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput,inputCard.DmInput); break; + } + case "dmc4kzcdsp": { - var inputCard = new Dmc4kzCDsp(number, this.Chassis); - var cecPort = inputCard.DmInput as ICec; - AddDmInCardPorts(number, cecPort); + var inputCard = new Dmc4kzCDsp(number, Chassis); + AddDmInCardPorts(number, inputCard.DmInput, inputCard.DmInput); + break; } - break; + case "dmccat": - new DmcCat(number, this.Chassis); - AddDmInCardPorts(number); + { + var inputCard = new DmcCat(number, Chassis); + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } case "dmccatdsp": - new DmcCatDsp(number, this.Chassis); - AddDmInCardPorts(number); + { + var inputCard = new DmcCatDsp(number, Chassis); + AddDmInCardPorts(number, null, inputCard.DmInput); break; + } case "dmcs": - new DmcS(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber); + { + var inputCard = new DmcS(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcsdsp": - new DmcSDsp(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber); + { + var inputCard = new DmcSDsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmMmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcs2": - new DmcS2(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber); + { + var inputCard = new DmcS2(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcs2dsp": - new DmcS2Dsp(number, Chassis); - AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber); + { + var inputCard = new DmcS2Dsp(number, Chassis); + AddInputPortWithDebug(number, "dmIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DmSmFiber, null, inputCard.DmInput); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcsdi": - new DmcSdi(number, Chassis); - AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi); + { + var inputCard = new DmcSdi(number, Chassis); + AddInputPortWithDebug(number, "sdiIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi, null, inputCard.SdiInput); AddOutputPortWithDebug(string.Format("inputCard{0}", number), "sdiOut", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Sdi, null); AddInCardHdmiAndAudioLoopPorts(number); break; + } case "dmcdvi": - new DmcDvi(number, Chassis); - AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Dvi); + { + var inputCard = new DmcDvi(number, Chassis); + AddInputPortWithDebug(number, "dviIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Dvi, null, inputCard.DviInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvga": - new DmcVga(number, Chassis); - AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga); + { + var inputCard = new DmcVga(number, Chassis); + AddInputPortWithDebug(number, "vgaIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Vga, null, inputCard.VgaInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidbnc": - new DmcVidBnc(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidBnc(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidrcaa": - new DmcVidRcaA(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidRcaA(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.LineAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvidrcad": - new DmcVidRcaD(number, Chassis); - AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component); + { + var inputCard = new DmcVidRcaD(number, Chassis); + AddInputPortWithDebug(number, "componentIn", eRoutingSignalType.Video, eRoutingPortConnectionType.Component, null, inputCard.VideoInput); AddInputPortWithDebug(number, "audioIn", eRoutingSignalType.Audio, eRoutingPortConnectionType.DigitalAudio); AddInCardHdmiLoopPort(number); break; + } case "dmcvid4": - new DmcVid4(number, Chassis); + { + var inputCard = new DmcVid4(number, Chassis); AddInputPortWithDebug(number, "compositeIn1", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn2", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn3", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInputPortWithDebug(number, "compositeIn4", eRoutingSignalType.Video, eRoutingPortConnectionType.Composite); AddInCardHdmiLoopPort(number); break; + } case "dmcstr": - new DmcStr(number, Chassis); - AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Streaming); + { + var inputCard = new DmcStr(number, Chassis); + AddInputPortWithDebug(number, "streamIn", eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Streaming, null, inputCard.Source); AddInCardHdmiAndAudioLoopPorts(number); break; + } } } @@ -1089,7 +1112,7 @@ namespace PepperDash.Essentials.DM var inputNumber = Chassis.Outputs[output].VideoOutFeedback == null ? 0 : Chassis. Outputs[output].VideoOutFeedback.Number; - Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", this.Name, inputNumber, output); + Debug.Console(2, this, "DMSwitchVideo:{0} Routed Input:{1} Output:{2}'", Name, inputNumber, output); if (VideoOutputFeedbacks.ContainsKey(output)) { @@ -1116,7 +1139,7 @@ namespace PepperDash.Essentials.DM var inputNumber = Chassis.Outputs[output].AudioOutFeedback == null ? 0 : Chassis. Outputs[output].AudioOutFeedback.Number; - Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", this.Name, inputNumber, output); + Debug.Console(2, this, "DMSwitchAudio:{0} Routed Input:{1} Output:{2}'", Name, inputNumber, output); if (AudioOutputFeedbacks.ContainsKey(output)) { @@ -1427,6 +1450,13 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus != null) + { + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + } } private void LinkStreamInputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) @@ -1467,6 +1497,13 @@ namespace PepperDash.Essentials.DM InputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); trilist.UShortInput[join].UShortValue = InputStreamCardStateFeedbacks[ioSlot].UShortValue; + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus != null) + { + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + } } private void LinkStreamOutputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) @@ -1603,6 +1640,13 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + + var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; + + if (videoStatus != null) + { + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + } } private void LinkAdvancedTxToApi(BasicTriList trilist, DmChassisControllerJoinMap joinMap, @@ -1653,6 +1697,13 @@ namespace PepperDash.Essentials.DM trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = (ushort) transmitter.HdcpSupportCapability; + + var videoStatus = txRoutingInputs.InputPorts.Cast().FirstOrDefault((ip) => ip.Key == DmPortName.AnyVideoIn); + + if (videoStatus != null) + { + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + } } private void LinkTxOnlineFeedbackToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, @@ -1935,15 +1986,15 @@ namespace PepperDash.Essentials.DM { var props = JsonConvert.DeserializeObject - (dc.Properties.ToString()); - return PepperDash.Essentials.DM.DmChassisController. + (dc.Properties.ToString()); + return DmChassisController. GetDmChassisController(dc.Key, dc.Name, type, props); } else if (type.StartsWith("dmmd128x") || type.StartsWith("dmmd64x")) { var props = JsonConvert.DeserializeObject - (dc.Properties.ToString()); - return PepperDash.Essentials.DM.DmBladeChassisController. + (dc.Properties.ToString()); + return DmBladeChassisController. GetDmChassisController(dc.Key, dc.Name, type, props); } From db3d96d44828ec68f999d6867369c1ae2679aa89 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 8 Sep 2021 15:26:16 -0600 Subject: [PATCH 11/28] feat(Essentials_DM): Update DmInputEvent handler for input resolution feedback --- .../Essentials_DM/Chassis/DmChassisController.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 8cd53e33..7356cec3 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -1051,6 +1051,18 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "No index of {0} found in InputStreamCardStateFeedbacks"); break; } + case DMInputEventIds.ResolutionEventId: + { + var inputPort = + InputPorts.Cast() + .FirstOrDefault((ip) => ip.Key.Contains(String.Format("inputCard{0}", args.Number))); + + if (inputPort != null) + { + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + } + break; + } default: { Debug.Console(2, this, "DMInputChange fired for Input {0} with Unhandled EventId: {1}", args.Number, args.EventId); From fa6cabe246387cf850d1b34a97191c2b396ee88f Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 9 Sep 2021 07:44:05 -0600 Subject: [PATCH 12/28] refactor(Essentials_DM): Add event IDS and some logging to get resolution feedback --- .../Essentials_DM/Chassis/DmChassisController.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 7356cec3..de9a088a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -1051,14 +1051,19 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "No index of {0} found in InputStreamCardStateFeedbacks"); break; } + case DMInputEventIds.HorizontalResolutionFeedbackEventId: + case DMInputEventIds.VerticalResolutionFeedbackEventId: + case DMInputEventIds.FramesPerSecondFeedbackEventId: case DMInputEventIds.ResolutionEventId: { + Debug.Console(1, this, "Input {0} resolution updated", args.Number); var inputPort = InputPorts.Cast() .FirstOrDefault((ip) => ip.Key.Contains(String.Format("inputCard{0}", args.Number))); if (inputPort != null) { + Debug.Console(1, this, "Updating resolution feedback for input {0}", args.Number); inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); } break; From 871894e248105655665d45c79814b798711f7986 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 10 Sep 2021 20:25:21 -0600 Subject: [PATCH 13/28] fix: Fix issue with receiving sharing state In some circumstances, the sharing state can be retrieved prior to participants or meeting info being populated. In those cases, Essentials is now creating an empty meeting object if one doesn't exist, and populating host info with None if no host has been indicated yet. --- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 130 ++++++++++++------ 1 file changed, 89 insertions(+), 41 deletions(-) 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 a426193b..ead0a66f 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,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Linq; using System.Text; using Crestron.SimplSharp; @@ -464,18 +465,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom private void SetUpCallFeedbackActions() { - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - - // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); - MeetingInfo = meetingInfo; - } - }; + Status.Call.Sharing.PropertyChanged += HandleSharingStateUpdate; Status.Call.PropertyChanged += (o, a) => { @@ -487,6 +477,39 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom }; } + private void HandleSharingStateUpdate(object sender, PropertyChangedEventArgs a) + { + if (a.PropertyName != "State") + { + return; + } + + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + try + { + + // Update the share status of the meeting info + if (MeetingInfo == null) + { + var sharingStatus = GetSharingStatus(); + + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true); + return; + } + + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host != null ? Participants.Host.Name : "None", + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); + MeetingInfo = meetingInfo; + } + catch (Exception e) + { + Debug.Console(1, this, "Error processing state property update. {0}", e.Message); + Debug.Console(2, this, e.StackTrace); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false); + } + } + /// /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. /// @@ -1614,7 +1637,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } catch (Exception ex) { - Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex.Message); + Debug.Console(2, this, "{0}", ex); + + if (ex.InnerException != null) + { + Debug.Console(1, this,"Error Deserializing feedback inner exception: {0}", ex.InnerException.Message); + Debug.Console(2, this, "{0}", ex.InnerException.StackTrace); + } } } @@ -1822,24 +1852,33 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { string sharingState = "None"; - if (Status.Call.Sharing.State == zEvent.eSharingState.Receiving) + try { - sharingState = "Receiving Content"; - } - if (Status.Sharing.isAirHostClientConnected) - { - sharingState = "Sharing AirPlay"; - } - if (Status.Sharing.isDirectPresentationConnected) - { - sharingState = "Sharing Laptop"; - } - if (Status.Sharing.isSharingBlackMagic) - { - sharingState = "Sharing HDMI Source"; - } + if (Status.Call.Sharing.State == zEvent.eSharingState.Receiving) + { + sharingState = "Receiving Content"; + } + if (Status.Sharing.isAirHostClientConnected) + { + sharingState = "Sharing AirPlay"; + } + if (Status.Sharing.isDirectPresentationConnected) + { + sharingState = "Sharing Laptop"; + } + if (Status.Sharing.isSharingBlackMagic) + { + sharingState = "Sharing HDMI Source"; + } - return sharingState; + return sharingState; + } + catch (Exception e) + { + Debug.Console(1, this, "Exception getting sharing status: {0}", e.Message); + Debug.Console(2, this, "{0}", e.StackTrace); + return sharingState; + } } /// @@ -1848,21 +1887,30 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private bool GetIsHostMyself() { - if (Participants.CurrentParticipants.Count == 0) + try { - Debug.Console(2, this, "No current participants"); + if (Participants.CurrentParticipants.Count == 0) + { + Debug.Console(2, this, "No current participants"); + return false; + } + + var host = Participants.Host; + + if(host == null) + { + Debug.Console(2, this, "Host is currently null"); + return false; + } + Debug.Console(2, this, "Host is: '{0}' IsMyself?: {1}", host.Name, host.IsMyself); + return host.IsMyself; + } + catch (Exception e) + { + Debug.Console(1, "Exception getting isHost: {0}", e.Message); + Debug.Console(2, "{0}", e.StackTrace); return false; } - - var host = Participants.Host; - - if(host == null) - { - Debug.Console(2, this, "Host is currently null"); - return false; - } - Debug.Console(2, this, "Host is: '{0}' IsMyself?: {1}", host.Name, host.IsMyself); - return host.IsMyself; } public override void StartSharing() From 0ae38dddfcb3938cb2d7c548f88469543e68c9a1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Sat, 11 Sep 2021 14:01:07 -0600 Subject: [PATCH 14/28] ci: Update scripts to remove Newtonsoft.Compact.json --- .github/scripts/ZipBuildOutput.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/ZipBuildOutput.ps1 b/.github/scripts/ZipBuildOutput.ps1 index bf3c9d94..dfc6480d 100644 --- a/.github/scripts/ZipBuildOutput.ps1 +++ b/.github/scripts/ZipBuildOutput.ps1 @@ -8,6 +8,7 @@ $destination = "$($Env:GITHUB_HOME)\output" New-Item -ItemType Directory -Force -Path ($destination) Get-ChildItem ($destination) $exclusions = @(git submodule foreach --quiet 'echo $name') +$exclusions += "Newtonsoft.Compact.Json.dll" # Trying to get any .json schema files (not currently working) # Gets any files with the listed extensions. Get-ChildItem -recurse -Path "$($Env:GITHUB_WORKSPACE)" -include "*.clz", "*.cpz", "*.cplz", "*.dll", "*.nuspec" | ForEach-Object { From 73e3b049d83d40018bec52dac1ea7b9d2a6225a7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 13 Sep 2021 08:33:36 -0600 Subject: [PATCH 15/28] build: Update nuspec file for maximum PD Core version --- .../PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec index e736dcbf..c2394428 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 - + From 4ffea1c98fbe25c48353f544db74d04257d8631d Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 13 Sep 2021 08:43:24 -0600 Subject: [PATCH 16/28] fix: Add step to allow meeting number to be sent when entering password --- .../Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs | 1 + 1 file changed, 1 insertion(+) 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 ead0a66f..b53b871d 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 @@ -2209,6 +2209,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public override void Dial(Meeting meeting) { Debug.Console(1, this, "Dialing meeting.Id: {0} Title: {1}", meeting.Id, meeting.Title); + _lastDialedMeetingNumber = meeting.Id; SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); } From fe01842523e2383f00911954d3e31553987e893e Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 16 Sep 2021 07:15:26 -0600 Subject: [PATCH 17/28] fix: Multiple fixes Update to poll meetings every 60 seconds Add property to allow for adjusting when Join button appears Refactor call connected event handler Adjust text for header for sharing-only meeting Add logic to handle waiting for host state --- .../Essentials/EssentialsHeaderDriver.cs | 43 +++-- ...entialsHuddleVtc1PanelAvFunctionsDriver.cs | 158 ++++++++++-------- .../VC/EssentialsVideoCodecUiDriver.cs | 40 ++++- .../Codec/iHasScheduleAwareness.cs | 9 + .../VideoCodec/Interfaces/IHasMeetingInfo.cs | 5 +- .../VideoCodec/ZoomRoom/ResponseObjects.cs | 6 + .../VideoCodec/ZoomRoom/ZoomRoom.cs | 102 ++++++++--- .../ZoomRoom/ZoomRoomPropertiesConfig.cs | 2 + 8 files changed, 249 insertions(+), 116 deletions(-) diff --git a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs index f42b9de2..c63af9d5 100644 --- a/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs +++ b/PepperDashEssentials/UIDrivers/Essentials/EssentialsHeaderDriver.cs @@ -195,22 +195,11 @@ namespace PepperDash.Essentials var meetingInfoCodec = codec as IHasMeetingInfo; // Set mode of header button - if (!codec.IsInCall) - { - HeaderCallButtonIconSig.StringValue = "DND"; - //HeaderCallButton.SetIcon(HeaderListButton.OnHook); - } - else if (codec.ActiveCalls.Any(c => c.Type == eCodecCallType.Video)) - HeaderCallButtonIconSig.StringValue = "Misc-06_Dark"; - //HeaderCallButton.SetIcon(HeaderListButton.Camera); - //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); - else - HeaderCallButtonIconSig.StringValue = "Misc-09_Dark"; - //HeaderCallButton.SetIcon(HeaderListButton.Phone); - //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 1); + SetHeaderCallIcon(codec); // Set the call status text Debug.Console(1, "Active Call Count: {0}", codec.ActiveCalls.Count); + if (codec.ActiveCalls.Count > 0) { if (codec.ActiveCalls.Count == 1 && meetingInfoCodec == null) @@ -218,8 +207,13 @@ namespace PepperDash.Essentials else if (codec.ActiveCalls.Count == 1 && meetingInfoCodec != null) { var headerCallStatusLabel = meetingInfoCodec.MeetingInfo.IsSharingMeeting - ? "Sharing Active" + ? "Sharing-Only Meeting" : "Active Meeting"; + + headerCallStatusLabel = meetingInfoCodec.MeetingInfo.WaitingForHost + ? "Waiting For Host" + : headerCallStatusLabel; + TriList.SetString(UIStringJoin.HeaderCallStatusLabel, headerCallStatusLabel); } else if (codec.ActiveCalls.Count > 1) @@ -229,6 +223,27 @@ namespace PepperDash.Essentials TriList.SetString(UIStringJoin.HeaderCallStatusLabel, "No Active Calls"); } + private void SetHeaderCallIcon(VideoCodecBase codec) + { + if (!codec.IsInCall) + { + HeaderCallButtonIconSig.StringValue = "DND"; + //HeaderCallButton.SetIcon(HeaderListButton.OnHook); + } + else if (codec.ActiveCalls.Any(c => c.Type == eCodecCallType.Video)) + { + HeaderCallButtonIconSig.StringValue = "Misc-06_Dark"; + } + //HeaderCallButton.SetIcon(HeaderListButton.Camera); + //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 2); + else + { + HeaderCallButtonIconSig.StringValue = "Misc-09_Dark"; + } + //HeaderCallButton.SetIcon(HeaderListButton.Phone); + //TriList.SetUshort(UIUshortJoin.CallHeaderButtonMode, 1); + } + /// /// Sets up Header Buttons for the EssentialsHuddleVtc1Room type /// diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 246573a9..67887078 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -471,85 +471,92 @@ namespace PepperDash.Essentials /// void ShowNextMeetingTimerCallback() { + //Update calendar for Zoom. Zoom doesn't automatically update when meetings are in the past + if (_isZoomRoomWithNoExternalSources) + { + CurrentRoom.ScheduleSource.GetSchedule(); + } + // Every 60 seconds, refresh the calendar RefreshMeetingsList(); // check meetings list for the closest, joinable meeting var ss = CurrentRoom.ScheduleSource; var meetings = ss.CodecSchedule.Meetings; - if (meetings.Count > 0) - { - // If the room is off pester the user - // If the room is on, and the meeting is joinable - // and the LastMeetingDismissed != this meeting + if (meetings.Count <= 0) + { + return; + } + // If the room is off pester the user + // If the room is on, and the meeting is joinable + // and the LastMeetingDismissed != this meeting - var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); - Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", - CurrentRoom.OnFeedback.BoolValue, - LastMeetingDismissedId, - lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); + var lastMeetingDismissed = meetings.FirstOrDefault(m => m.Id == LastMeetingDismissedId); + Debug.Console(0, "*#* Room on: {0}, lastMeetingDismissedId: {1} {2} *#*", + CurrentRoom.OnFeedback.BoolValue, + LastMeetingDismissedId, + lastMeetingDismissed != null ? lastMeetingDismissed.StartTime.ToString("t", Global.Culture) : ""); - var meeting = meetings.LastOrDefault(m => m.Joinable); - if (CurrentRoom.OnFeedback.BoolValue - && lastMeetingDismissed == meeting) - { - // meeting no longer joinable, hide popup - if(meeting == null) - HideNextMeetingPopup(); + var meeting = meetings.LastOrDefault(m => m.Joinable); + if (CurrentRoom.OnFeedback.BoolValue + && lastMeetingDismissed == meeting) + { + // meeting no longer joinable, hide popup + if(meeting == null) + HideNextMeetingPopup(); - return; - } + return; + } - LastMeetingDismissedId = null; - // Clear the popup when we run out of meetings - if (meeting == null) - { - HideNextMeetingPopup(); - } - else - { + LastMeetingDismissedId = null; + // Clear the popup when we run out of meetings + if (meeting == null) + { + HideNextMeetingPopup(); + } + else + { - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); - TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture)); - TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture)); - TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); - TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); - TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => - { - HideNextMeetingPopup(); - PopupInterlock.Hide(); - RoomOnAndDialMeeting(meeting); - }); - TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); - TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => - { - HideNextMeetingPopup(); - //CalendarPress(); - RefreshMeetingsList(); - PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); - }); - var indexOfNext = meetings.IndexOf(meeting) + 1; + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Upcoming meeting"); + TriList.SetString(UIStringJoin.NextMeetingStartTimeText, meeting.StartTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingEndTimeText, meeting.EndTime.ToString("t", Global.Culture)); + TriList.SetString(UIStringJoin.NextMeetingTitleText, meeting.Title); + TriList.SetString(UIStringJoin.NextMeetingNameText, meeting.Organizer); + TriList.SetString(UIStringJoin.NextMeetingButtonLabel, "Join"); + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingJoinPress, () => + { + HideNextMeetingPopup(); + PopupInterlock.Hide(); + RoomOnAndDialMeeting(meeting); + }); + TriList.SetString(UIStringJoin.NextMeetingSecondaryButtonLabel, "Show Schedule"); + TriList.SetSigFalseAction(UIBoolJoin.CalendarHeaderButtonPress, () => + { + HideNextMeetingPopup(); + //CalendarPress(); + RefreshMeetingsList(); + PopupInterlock.ShowInterlocked(UIBoolJoin.MeetingsOrContacMethodsListVisible); + }); + var indexOfNext = meetings.IndexOf(meeting) + 1; - // indexOf = 3, 4 meetings : - if (indexOfNext < meetings.Count) - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, - meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); - else - TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); + // indexOf = 3, 4 meetings : + if (indexOfNext < meetings.Count) + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, + meetings[indexOfNext].StartTime.ToString("t", Global.Culture)); + else + TriList.SetString(UIStringJoin.NextMeetingFollowingMeetingText, "No more meetings today"); - TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => - { - // Mark the meeting to not re-harass the user - if(CurrentRoom.OnFeedback.BoolValue) - LastMeetingDismissedId = meeting.Id; - HideNextMeetingPopup(); - }); + TriList.SetSigFalseAction(UIBoolJoin.NextMeetingModalClosePress, () => + { + // Mark the meeting to not re-harass the user + if(CurrentRoom.OnFeedback.BoolValue) + LastMeetingDismissedId = meeting.Id; + HideNextMeetingPopup(); + }); - TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); - } - } + TriList.SetBool(UIBoolJoin.NextMeetingModalVisible, true); + } } /// @@ -575,19 +582,26 @@ namespace PepperDash.Essentials /// void RoomOnAndDialMeeting(Meeting meeting) { + Debug.Console(1, "[RoomOnAndDialMeeting] Joining meeting [{0}]", meeting); Action dialAction = () => { var d = CurrentRoom.ScheduleSource as VideoCodecBase; if (d != null) { + Debug.Console(1, + "[RoomOnAndDialMeeting] [dialAction] Sending command to codec to join meeting {0}", meeting); d.Dial(meeting); LastMeetingDismissedId = meeting.Id; // To prevent prompts for already-joined call } }; - if (CurrentRoom.OnFeedback.BoolValue) - dialAction(); + if (CurrentRoom.OnFeedback.BoolValue) + { + Debug.Console(1, "[RoomOnAndDialMeeting] Room is on."); + dialAction(); + } else - { + { + Debug.Console(1, "RoomOnAndDialMeeting] Room is off or warming. Registering for Warming Feedback"); // Rig a one-time handler to catch when the room is warmed and then dial call EventHandler oneTimeHandler = null; oneTimeHandler = (o, a) => @@ -1086,13 +1100,18 @@ namespace PepperDash.Essentials void meetingInfoCodec_MeetingInfoChanged(object sender, MeetingInfoEventArgs e) { + if (e.Info == null) + { + return; + } + TriList.SetString(UIStringJoin.MeetingIdText, e.Info.Id); TriList.SetString(UIStringJoin.MeetingHostText, e.Info.Host); TriList.SetString(UIStringJoin.MeetingNameText, e.Info.Name); TriList.SetString(UIStringJoin.MeetingPasswordText, e.Info.Password); // Show the password fields if one is present - TriList.SetBool(UIBoolJoin.MeetingPasswordVisible, string.IsNullOrEmpty(e.Info.Password) ? false : true); + TriList.SetBool(UIBoolJoin.MeetingPasswordVisible, !string.IsNullOrEmpty(e.Info.Password)); TriList.SetString(UIStringJoin.CallSharedSourceNameText, e.Info.ShareStatus); @@ -1302,9 +1321,8 @@ namespace PepperDash.Essentials { // See if this is helpful or if the callback response in the codec class maybe doesn't come it time? // Let's build list from event - // CurrentRoom.ScheduleSource.GetSchedule(); - TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); + TriList.SetString(UIStringJoin.MeetingsOrContactMethodListIcon, "Calendar"); TriList.SetString(UIStringJoin.MeetingsOrContactMethodListTitleText, "Today's Meetings"); ushort i = 0; diff --git a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs index 9bbaad6f..cc5ac265 100644 --- a/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs +++ b/PepperDashEssentials/UIDrivers/VC/EssentialsVideoCodecUiDriver.cs @@ -327,20 +327,14 @@ namespace PepperDash.Essentials.UIDrivers.VC { case eCodecCallStatus.Connected: // fire at SRL item - HidePasswordPrompt(); - KeypadMode = eKeypadMode.DTMF; - DialStringBuilder.Remove(0, DialStringBuilder.Length); - DialStringFeedback.FireUpdate(); - DialStringTextCheckEnables(); Parent.ShowNotificationRibbon("Connected", 2000); - StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); - ShowKeypad(); - ((Parent.CurrentRoom as IHasCurrentVolumeControls).CurrentVolumeControls as IBasicVolumeWithFeedback).MuteOff(); + OnCallConnected(); //VCControlsInterlock.ShowInterlocked(UIBoolJoin.VCKeypadVisible); break; case eCodecCallStatus.Connecting: // fire at SRL item Parent.ShowNotificationRibbon("Connecting", 0); + OnCallConnected(); break; case eCodecCallStatus.Dialing: Parent.ShowNotificationRibbon("Connecting", 0); @@ -417,6 +411,36 @@ namespace PepperDash.Essentials.UIDrivers.VC UpdateHeaderActiveCallList(); } + private void OnCallConnected() + { + HidePasswordPrompt(); + KeypadMode = eKeypadMode.DTMF; + DialStringBuilder.Remove(0, DialStringBuilder.Length); + DialStringFeedback.FireUpdate(); + DialStringTextCheckEnables(); + + StagingButtonsFeedbackInterlock.ShowInterlocked(UIBoolJoin.VCStagingKeypadPress); + ShowKeypad(); + + UnmuteRoomOnCallConnect(); + } + + private void UnmuteRoomOnCallConnect() + { + var volControl = Parent.CurrentRoom as IHasCurrentVolumeControls; + + if (volControl == null) + { + return; + } + var currentVolControls = volControl.CurrentVolumeControls as IBasicVolumeWithFeedback; + + if (currentVolControls != null) + { + currentVolControls.MuteOff(); + } + } + /// /// Redraws the calls list on the header /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs index 24f1968b..06edf7bf 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/iHasScheduleAwareness.cs @@ -235,6 +235,15 @@ namespace PepperDash.Essentials.Devices.Common.Codec { Calls = new List(); } + + #region Overrides of Object + + public override string ToString() + { + return String.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime); + } + + #endregion } public class Call diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs index cd9e2906..0720b319 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasMeetingInfo.cs @@ -38,8 +38,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces public Boolean IsHost { get; private set; } [JsonProperty("isSharingMeeting")] public Boolean IsSharingMeeting { get; private set; } + [JsonProperty("waitingForHost")] + public Boolean WaitingForHost { get; private set; } - public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting) + public MeetingInfo(string id, string name, string host, string password, string shareStatus, bool isHost, bool isSharingMeeting, bool waitingForHost) { Id = id; Name = name; @@ -48,6 +50,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces ShareStatus = shareStatus; IsHost = isHost; IsSharingMeeting = isSharingMeeting; + WaitingForHost = waitingForHost; } } 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 35b6c3b2..e6efd063 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 @@ -59,6 +59,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public List AudioOuputs { get; set; } public List Cameras { get; set; } public zEvent.PhoneCallStatus PhoneCall { get; set; } + public zEvent.NeedWaitForHost NeedWaitForHost { get; set; } public ZoomRoomStatus() { @@ -76,6 +77,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom AudioOuputs = new List(); Cameras = new List(); PhoneCall = new zEvent.PhoneCallStatus(); + NeedWaitForHost = new zEvent.NeedWaitForHost(); } } @@ -1475,6 +1477,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom public static List GetGenericParticipantListFromParticipantsResult( List participants) { + if (participants.Count == 0) + { + return new List(); + } //return participants.Select(p => new Participant // { // UserId = p.UserId, 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 b53b871d..7096d894 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 @@ -112,6 +112,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + if (_props.MinutesBeforeMeetingStart > 0) + { + CodecSchedule.MeetingWarningMinutes = _props.MinutesBeforeMeetingStart; + } + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); @@ -494,19 +499,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { var sharingStatus = GetSharingStatus(); - MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true); + MeetingInfo = new MeetingInfo("", "", "", "", sharingStatus, GetIsHostMyself(), true, false); return; } var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host != null ? Participants.Host.Name : "None", - MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); + MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); MeetingInfo = meetingInfo; } catch (Exception e) { Debug.Console(1, this, "Error processing state property update. {0}", e.Message); Debug.Console(2, this, e.StackTrace); - MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false); + MeetingInfo = new MeetingInfo("", "", "", "", "None", false, false, false); } } @@ -657,7 +662,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom return; } // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, GetSharingStatus(), GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); MeetingInfo = meetingInfo; break; } @@ -1229,7 +1234,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Participants.CurrentParticipants = participants; // Update the share status of the meeting info - var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting); + var meetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, Participants.Host.Name, MeetingInfo.Password, MeetingInfo.ShareStatus, GetIsHostMyself(), MeetingInfo.IsSharingMeeting, MeetingInfo.WaitingForHost); MeetingInfo = meetingInfo; PrintCurrentCallParticipants(); @@ -1424,15 +1429,40 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } case "needwaitforhost": { - var needWait = - JsonConvert.DeserializeObject(responseObj.ToString()); + Status.NeedWaitForHost = JsonConvert.DeserializeObject(responseObj.ToString()); - if (needWait.Wait) - { - // TODO: notify user to wait for host - } + Debug.Console(1, this, "NeedWaitForHost: {0}", Status.NeedWaitForHost.Wait); - break; + if (Status.NeedWaitForHost.Wait) + { + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + break; + } + + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, true); + + UpdateCallStatus(); + + break; + } + + if (MeetingInfo == null) + { + MeetingInfo = new MeetingInfo("Waiting For Host", "Waiting For Host", "Waiting For Host", "", + GetSharingStatus(), false, false, false); + break; + } + + MeetingInfo = new MeetingInfo(MeetingInfo.Id, MeetingInfo.Name, MeetingInfo.Host, MeetingInfo.Password, + GetSharingStatus(), GetIsHostMyself(), false, false); + + break; } case "openvideofailforhoststop": { @@ -1514,7 +1544,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (result.Success) { - MeetingInfo = new MeetingInfo("", "", "", "", "", true, true); + MeetingInfo = new MeetingInfo("", "", "", "", "", true, true, MeetingInfo.WaitingForHost); break; } @@ -1704,8 +1734,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// private void UpdateCallStatus() { - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Status.ToString() : "no call"); + Debug.Console(1, this, + "[UpdateCallStatus] Current Call Status: {0} Active Call Count: {1} Need Wait For Host: {2}", + Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count, Status.NeedWaitForHost.Wait); if (Status.Call != null) { @@ -1750,17 +1781,32 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Type = eCodecCallType.Video, }; + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } ActiveCalls.Add(newCall); - //Debug.Console(1, this, "[UpdateCallStatus] IF w/ meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", - //Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); - OnCallStatusChange(newCall); - } - else + } else if (String.IsNullOrEmpty(Status.Call.Info.meeting_id) && Status.NeedWaitForHost.Wait) { - //Debug.Console(1, this, "[UpdateCallStatus] IF w/o meeting_id AcitveCalls.Count == {1} - Current Call Status: {0}", - //Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + var newCall = new CodecActiveCallItem + { + Name = "Waiting For Host", + Number = "Waiting For Host", + Id = "Waiting For Host", + Status = newStatus, + Type = eCodecCallType.Video, + }; + + if (!String.IsNullOrEmpty(_lastDialedMeetingNumber)) + { + _lastDialedMeetingNumber = String.Empty; + } + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); } } } @@ -1771,15 +1817,24 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom switch (callStatus) { case zStatus.eCallStatus.IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Connected; break; case zStatus.eCallStatus.NOT_IN_MEETING: + if (Status.NeedWaitForHost.Wait) + { + Status.NeedWaitForHost.Wait = false; + } existingCall.Status = eCodecCallStatus.Disconnected; break; } Debug.Console(1, this, "[UpdateCallStatus] ELSE ActiveCalls.Count == {1} - Current Call Status: {0}", Status.Call != null ? Status.Call.Status.ToString() : "no call", ActiveCalls.Count); + OnCallStatusChange(existingCall); } @@ -1833,7 +1888,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Status.Call.Info.meeting_password, GetSharingStatus(), GetIsHostMyself(), - !String.Equals(Status.Call.Info.meeting_type,"NORMAL") + !String.Equals(Status.Call.Info.meeting_type,"NORMAL"), + false ); } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs index 38238023..4c9b08c2 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoomPropertiesConfig.cs @@ -25,5 +25,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom //This layout will be selected when a call is connected and no content is being shared public string DefaultCallLayout { get; set; } + + public int MinutesBeforeMeetingStart { get; set; } } } \ No newline at end of file From 9f3a3f64a82a11e4f818044cbc29c11fa3e24c63 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 16 Sep 2021 13:27:09 -0600 Subject: [PATCH 18/28] fix: Update share behavior for Zoom Room --- ...entialsHuddleVtc1PanelAvFunctionsDriver.cs | 22 +++++++++++++++++-- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 10 ++++++++- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs index 67887078..26bdaee9 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddleVTC/EssentialsHuddleVtc1PanelAvFunctionsDriver.cs @@ -9,6 +9,7 @@ using Crestron.SimplSharpPro.UI; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.SmartObjects; using PepperDash.Essentials.Core.PageManagers; using PepperDash.Essentials.Room.Config; @@ -730,16 +731,31 @@ namespace PepperDash.Essentials if (_isZoomRoomWithNoExternalSources) { - CurrentRoom.RunDefaultPresentRoute(); + if (!CurrentRoom.OnFeedback.BoolValue) + { + CurrentRoom.RunDefaultPresentRoute(); + } // For now, if this is a Zoom Room and there are no shareable sources just display the informational subpage TriList.SetBool(UIBoolJoin.ZoomRoomContentSharingVisible, true); var presentationMeetingCodec = CurrentRoom.VideoCodec as IHasPresentationOnlyMeeting; + var farEndContentStatusCodec = CurrentRoom.VideoCodec as IHasFarEndContentStatus; + var receivingContent = false; + + if (farEndContentStatusCodec != null) + { + receivingContent = farEndContentStatusCodec.ReceivingContent.BoolValue; + } if (presentationMeetingCodec != null && !CurrentRoom.VideoCodec.IsInCall) { presentationMeetingCodec.StartSharingOnlyMeeting(eSharingMeetingMode.Laptop); } + else if (CurrentRoom.VideoCodec.IsInCall && !CurrentRoom.VideoCodec.SharingContentIsOnFeedback.BoolValue && + !receivingContent) + { + CurrentRoom.VideoCodec.StartSharing(); + } if (CurrentSourcePageManager != null) CurrentSourcePageManager.Hide(); @@ -755,7 +771,9 @@ namespace PepperDash.Essentials } else // room is on show what's active or select a source if nothing is yet active { - if (CurrentRoom.CurrentSourceInfo == null || (CurrentRoom.VideoCodec != null && CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) + if (CurrentRoom.CurrentSourceInfo == null || + (CurrentRoom.VideoCodec != null && + CurrentRoom.CurrentSourceInfo.SourceDevice.Key == CurrentRoom.VideoCodec.OsdSource.Key)) TriList.SetBool(UIBoolJoin.SelectASourceVisible, true); else if (CurrentSourcePageManager != null) { 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 7096d894..e4a724a2 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 @@ -656,6 +656,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { Debug.Console(2, this, "Updating sharing status: {0}", a.PropertyName); + SharingContentIsOnFeedback.FireUpdate(); if (MeetingInfo == null) { //Ignoring for now, as the CallInfo return will create the appropriate value @@ -1979,7 +1980,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom /// public override void StopSharing() { - SendText("zCommand Call Sharing Disconnect"); + if (Status.Sharing.isSharingBlackMagic) + { + SendText("zCommand Call Sharing HDMI Stop"); + } + else + { + SendText("zCommand Call Sharing Disconnect"); + } } public override void PrivacyModeOn() From 9f1c512909ff48279804dcd33f3bf63600281a46 Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Thu, 29 Jul 2021 17:19:54 -0500 Subject: [PATCH 19/28] Updates to HdMdNxM4kEController.cs to implement regex pattern in place of substring on input definitions. Updated HdMdNxM4kEBridgeableController.cs to handle exception when constructing the device. --- .../Chassis/HdMdNxM4kEBridgeableController.cs | 860 +++++++++--------- .../Chassis/HdMdNxM4kEController.cs | 12 +- 2 files changed, 452 insertions(+), 420 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs index b766ac3c..3eece080 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Newtonsoft.Json; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DeviceSupport; @@ -15,421 +16,446 @@ using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.DM.Chassis { - [Description("Wrapper class for all HdMdNxM4E switchers")] - public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback - { - private HdMdNxM _Chassis; - private HdMd4x14kE _Chassis4x1; - - //IroutingNumericEvent - public event EventHandler NumericSwitchChange; - - public Dictionary InputNames { get; set; } - public Dictionary OutputNames { get; set; } - - public RoutingPortCollection InputPorts { get; private set; } - public RoutingPortCollection OutputPorts { get; private set; } - - public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } - public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } - public FeedbackCollection InputNameFeedbacks { get; private set; } - public FeedbackCollection OutputNameFeedbacks { get; private set; } - public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } - public FeedbackCollection InputHdcpEnableFeedback { get; private set; } - public FeedbackCollection DeviceNameFeedback { get; private set; } - public FeedbackCollection AutoRouteFeedback { get; private set; } - - #region Constructor - - public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, - HdMdNxM4kEBridgeablePropertiesConfig props) - : base(key, name, chassis) - { - _Chassis = chassis; - var _props = props; - - InputNames = props.Inputs; - OutputNames = props.Outputs; - - VideoInputSyncFeedbacks = new FeedbackCollection(); - VideoOutputRouteFeedbacks = new FeedbackCollection(); - InputNameFeedbacks = new FeedbackCollection(); - OutputNameFeedbacks = new FeedbackCollection(); - OutputRouteNameFeedbacks = new FeedbackCollection(); - InputHdcpEnableFeedback = new FeedbackCollection(); - DeviceNameFeedback = new FeedbackCollection(); - AutoRouteFeedback = new FeedbackCollection(); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); - - DeviceNameFeedback.Add(new StringFeedback(this.Name, () => this.Name)); - - if (_Chassis.NumberOfInputs == 1) - { - _Chassis4x1 = _Chassis as HdMd4x14kE; - AutoRouteFeedback.Add(new BoolFeedback(this.Name + "-" + InputNames[1], () => _Chassis4x1.AutoModeOnFeedback.BoolValue)); - } - - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var index = i; - var inputName = InputNames[index]; - _Chassis.Inputs[index].Name.StringValue = inputName; - - InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) - { - FeedbackMatchObject = _Chassis.HdmiInputs[index] - }); - VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].Name.StringValue)); - InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); - } - - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var index = i; - var outputName = OutputNames[index]; - _Chassis.Outputs[i].Name.StringValue = outputName; - - OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) - { - FeedbackMatchObject = _Chassis.HdmiOutputs[index] - }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].Name.StringValue)); - OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); - } - - _Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); - _Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); - - AddPostActivationAction(AddFeedbackCollections); - } - - #endregion - - #region Methods - - /// - /// Raise an event when the status of a switch object changes. - /// - /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType - private void OnSwitchChange(RoutingNumericEventArgs e) - { - var newEvent = NumericSwitchChange; - if (newEvent != null) newEvent(this, e); - } - - public void EnableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; - - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } - - public void DisableHdcp(uint port) - { - if (port > _Chassis.NumberOfInputs) return; - if (port <= 0) return; - - _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); - InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); - } - - public void EnableAutoRoute() - { - if (_Chassis.NumberOfInputs != 1) return; - - if (_Chassis4x1 == null) return; - - _Chassis4x1.AutoModeOn(); - } - - public void DisableAutoRoute() - { - if (_Chassis.NumberOfInputs != 1) return; - - if (_Chassis4x1 == null) return; - - _Chassis4x1.AutoModeOff(); - } - - #region PostActivate - - public void AddFeedbackCollections() - { - AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); - AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks, DeviceNameFeedback); - } - - #endregion - - #region FeedbackCollection Methods - - //Add arrays of collections - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - - public void AddCollectionsToList(params FeedbackCollection[] newFbs) - { - foreach (FeedbackCollection fbCollection in newFbs) - { - foreach (var item in newFbs) - { - AddCollectionToList(item); - } - } - } - - //Add Collections - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - public void AddCollectionToList(FeedbackCollection newFbs) - { - foreach (var f in newFbs) - { - if (f == null) continue; - - AddFeedbackToList(f); - } - } - - //Add Individual Feedbacks - public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) - { - if (newFb == null) return; - - if (!Feedbacks.Contains(newFb)) - { - Feedbacks.Add(newFb); - } - } - - #endregion - - #region IRouting Members - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - // Try to make switch only when necessary. The unit appears to toggle when already selected. - var current = _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut; - if (current != _Chassis.HdmiInputs[(uint)inputSelector]) - _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut = _Chassis.HdmiInputs[(uint)inputSelector]; - } - - #endregion - - #region IRoutingNumeric Members - - public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) - { - ExecuteSwitch(inputSelector, outputSelector, signalType); - } - - #endregion - - #endregion - - #region Bridge Linking - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = new HdMdNxM4kEControllerJoinMap(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."); - } - - IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback[this.Name].LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); - - if (_Chassis4x1 != null) - { - trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); - trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); - AutoRouteFeedback[this.Name + "-" + InputNames[1]].LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); - } - - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - var joinIndex = i - 1; - //Digital - VideoInputSyncFeedbacks[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); - trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(i)); - trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(i)); - - //Serial - InputNameFeedbacks[InputNames[i]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); - } - - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - var joinIndex = i - 1; - //Analog - VideoOutputRouteFeedbacks[OutputNames[i]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); - trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteSwitch(a, i, eRoutingSignalType.AudioVideo)); - - //Serial - OutputNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); - OutputRouteNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); - } - - _Chassis.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler(Chassis_OnlineStatusChange); - - trilist.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler((d, args) => - { - if (args.DeviceOnLine) - { - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - } - }); - } - - - #endregion - - #region Events - - void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) - { - if (!args.DeviceOnLine) return; - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - _Chassis.Inputs[i].Name.StringValue = InputNames[i]; - } - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - _Chassis.Outputs[i].Name.StringValue = OutputNames[i]; - } - - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - } - - void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) - { - if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - - for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) - { - var index = i; - var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1].VideoOutFeedback); - var localOutputPort = - OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1]); - - - VideoOutputRouteFeedbacks[i].FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs((ushort)i, VideoOutputRouteFeedbacks[i].UShortValue, localOutputPort, localInputPort, eRoutingSignalType.AudioVideo)); - } - } - - void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - if (args.EventId != DMInputEventIds.VideoDetectedEventId) return; - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } - } - - #endregion - - #region Factory - - public class HdMdNxM4kEControllerFactory : EssentialsDeviceFactory - { - public HdMdNxM4kEControllerFactory() - { - TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x24ke", "hdmd6x24ke" }; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new HD-MD-NxM-4K-E Device"); - - var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); - - var type = dc.Type.ToLower(); - var control = props.Control; - var ipid = control.IpIdInt; - var address = control.TcpSshProperties.Address; - - switch (type) - { - case ("hdmd4x14ke-bridgeable"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); - case ("hdmd4x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); - case ("hdmd6x24ke"): - return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); - default: - return null; - } - } - } - - #endregion - - - - } + [Description("Wrapper class for all HdMdNxM4E switchers")] + public class HdMdNxM4kEBridgeableController : CrestronGenericBridgeableBaseDevice, IRoutingNumericWithFeedback, IHasFeedback + { + private HdMdNxM _Chassis; + private HdMd4x14kE _Chassis4x1; + + //IroutingNumericEvent + public event EventHandler NumericSwitchChange; + + public Dictionary InputNames { get; set; } + public Dictionary OutputNames { get; set; } + + public RoutingPortCollection InputPorts { get; private set; } + public RoutingPortCollection OutputPorts { get; private set; } + + public FeedbackCollection VideoInputSyncFeedbacks { get; private set; } + public FeedbackCollection VideoOutputRouteFeedbacks { get; private set; } + public FeedbackCollection InputNameFeedbacks { get; private set; } + public FeedbackCollection OutputNameFeedbacks { get; private set; } + public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } + public FeedbackCollection InputHdcpEnableFeedback { get; private set; } + public FeedbackCollection DeviceNameFeedback { get; private set; } + public FeedbackCollection AutoRouteFeedback { get; private set; } + + #region Constructor + + public HdMdNxM4kEBridgeableController(string key, string name, HdMdNxM chassis, + HdMdNxM4kEBridgeablePropertiesConfig props) + : base(key, name, chassis) + { + _Chassis = chassis; + + if (props == null) + { + Debug.Console(1, this, "HdMdNx4keBridgeableController properties are null, failed to build the device"); + return; + } + + + if (props.Inputs != null) + { + foreach (var kvp in props.Inputs) + { + Debug.Console(0, this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + } + InputNames = props.Inputs; + } + if (props.Outputs != null) + { + foreach (var kvp in props.Outputs) + { + Debug.Console(0, this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + } + OutputNames = props.Outputs; + } + //else + //{ + // OutputNames.Add(1, "Output"); + //} + + VideoInputSyncFeedbacks = new FeedbackCollection(); + VideoOutputRouteFeedbacks = new FeedbackCollection(); + InputNameFeedbacks = new FeedbackCollection(); + OutputNameFeedbacks = new FeedbackCollection(); + OutputRouteNameFeedbacks = new FeedbackCollection(); + InputHdcpEnableFeedback = new FeedbackCollection(); + DeviceNameFeedback = new FeedbackCollection(); + AutoRouteFeedback = new FeedbackCollection(); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + var deviceName = string.IsNullOrEmpty(this.Name) ? name : this.Name; + DeviceNameFeedback.Add(new StringFeedback(deviceName, () => deviceName)); + + if (_Chassis.NumberOfInputs == 1) + { + _Chassis4x1 = _Chassis as HdMd4x14kE; + AutoRouteFeedback.Add(new BoolFeedback(deviceName + "-" + InputNames[1], () => _Chassis4x1.AutoModeOnFeedback.BoolValue)); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var index = i; + var inputName = InputNames[index]; + _Chassis.Inputs[index].Name.StringValue = inputName; + + InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, index, this) + { + FeedbackMatchObject = _Chassis.HdmiInputs[index] + }); + VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].Name.StringValue)); + InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var index = i; + var outputName = OutputNames[index]; + _Chassis.Outputs[i].Name.StringValue = outputName; + + OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, + eRoutingPortConnectionType.Hdmi, index, this) + { + FeedbackMatchObject = _Chassis.HdmiOutputs[index] + }); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + OutputNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].Name.StringValue)); + OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); + } + + _Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); + _Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); + + AddPostActivationAction(AddFeedbackCollections); + } + + #endregion + + #region Methods + + /// + /// Raise an event when the status of a switch object changes. + /// + /// Arguments defined as IKeyName sender, output, input, and eRoutingSignalType + private void OnSwitchChange(RoutingNumericEventArgs e) + { + var newEvent = NumericSwitchChange; + if (newEvent != null) newEvent(this, e); + } + + public void EnableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOn(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void DisableHdcp(uint port) + { + if (port > _Chassis.NumberOfInputs) return; + if (port <= 0) return; + + _Chassis.HdmiInputs[port].HdmiInputPort.HdcpSupportOff(); + InputHdcpEnableFeedback[InputNames[port]].FireUpdate(); + } + + public void EnableAutoRoute() + { + if (_Chassis.NumberOfInputs != 1) return; + + if (_Chassis4x1 == null) return; + + _Chassis4x1.AutoModeOn(); + } + + public void DisableAutoRoute() + { + if (_Chassis.NumberOfInputs != 1) return; + + if (_Chassis4x1 == null) return; + + _Chassis4x1.AutoModeOff(); + } + + #region PostActivate + + public void AddFeedbackCollections() + { + AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); + AddCollectionsToList(VideoOutputRouteFeedbacks); + AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks, DeviceNameFeedback); + } + + #endregion + + #region FeedbackCollection Methods + + //Add arrays of collections + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + public void AddCollectionsToList(params FeedbackCollection[] newFbs) + { + foreach (FeedbackCollection fbCollection in newFbs) + { + foreach (var item in newFbs) + { + AddCollectionToList(item); + } + } + } + + //Add Collections + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + public void AddCollectionToList(FeedbackCollection newFbs) + { + foreach (var f in newFbs) + { + if (f == null) continue; + + AddFeedbackToList(f); + } + } + + //Add Individual Feedbacks + public void AddFeedbackToList(PepperDash.Essentials.Core.Feedback newFb) + { + if (newFb == null) return; + + if (!Feedbacks.Contains(newFb)) + { + Feedbacks.Add(newFb); + } + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + // Try to make switch only when necessary. The unit appears to toggle when already selected. + var current = _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut; + if (current != _Chassis.HdmiInputs[(uint)inputSelector]) + _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut = _Chassis.HdmiInputs[(uint)inputSelector]; + } + + #endregion + + #region IRoutingNumeric Members + + public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector, outputSelector, signalType); + } + + #endregion + + #endregion + + #region Bridge Linking + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HdMdNxM4kEControllerJoinMap(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."); + } + + IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + DeviceNameFeedback[this.Name].LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + + if (_Chassis4x1 != null) + { + trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); + trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); + AutoRouteFeedback[this.Name + "-" + InputNames[1]].LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + } + + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + var joinIndex = i - 1; + //Digital + VideoInputSyncFeedbacks[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[i]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(i)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(i)); + + //Serial + InputNameFeedbacks[InputNames[i]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + } + + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + var joinIndex = i - 1; + //Analog + VideoOutputRouteFeedbacks[OutputNames[i]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteSwitch(a, i, eRoutingSignalType.AudioVideo)); + + //Serial + OutputNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); + OutputRouteNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + } + + _Chassis.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler(Chassis_OnlineStatusChange); + + trilist.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler((d, args) => + { + if (args.DeviceOnLine) + { + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + } + }); + } + + + #endregion + + #region Events + + void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) + { + if (!args.DeviceOnLine) return; + for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) + { + _Chassis.Inputs[i].Name.StringValue = InputNames[i]; + } + for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + { + _Chassis.Outputs[i].Name.StringValue = OutputNames[i]; + } + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + } + + void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) + { + if (args.EventId != DMOutputEventIds.VideoOutEventId) return; + + for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) + { + var index = i; + var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1].VideoOutFeedback); + var localOutputPort = + OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1]); + + + VideoOutputRouteFeedbacks[i].FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs((ushort)i, VideoOutputRouteFeedbacks[i].UShortValue, localOutputPort, localInputPort, eRoutingSignalType.AudioVideo)); + } + } + + void Chassis_DMInputChange(Switch device, DMInputEventArgs args) + { + if (args.EventId != DMInputEventIds.VideoDetectedEventId) return; + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + } + + #endregion + + #region Factory + + public class HdMdNxM4kEControllerFactory : EssentialsDeviceFactory + { + public HdMdNxM4kEControllerFactory() + { + TypeNames = new List() { "hdmd4x14ke-bridgeable", "hdmd4x24ke", "hdmd6x24ke" }; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new HD-MD-NxM-4K-E Device"); + + var props = JsonConvert.DeserializeObject(dc.Properties.ToString()); + + var type = dc.Type.ToLower(); + var control = props.Control; + var ipid = control.IpIdInt; + var address = control.TcpSshProperties.Address; + + switch (type) + { + case ("hdmd4x14ke-bridgeable"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x14kE(ipid, address, Global.ControlSystem), props); + case ("hdmd4x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd4x24kE(ipid, address, Global.ControlSystem), props); + case ("hdmd6x24ke"): + return new HdMdNxM4kEBridgeableController(dc.Key, dc.Name, new HdMd6x24kE(ipid, address, Global.ControlSystem), props); + default: + return null; + } + } + } + + #endregion + + + + } } \ No newline at end of file diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs index 57c52627..d19c9752 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Text.RegularExpressions; using Crestron.SimplSharp; using Crestron.SimplSharpPro.DM; using Newtonsoft.Json; @@ -43,14 +44,19 @@ namespace PepperDash.Essentials.DM.Chassis OutputPorts = new RoutingPortCollection(); OutputPorts.Add(new RoutingOutputPort(DmPortName.HdmiOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, null, this)); - + // physical settings if (props != null && props.Inputs != null) { + var inputRegex = new Regex(@"(?\d)", RegexOptions.IgnoreCase); foreach (var kvp in props.Inputs) { - // strip "hdmiIn" - var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); + // get numnbers from key and convert to int + //var inputNum = Convert.ToUInt32(kvp.Key.Substring(6)); + var inputMatch = inputRegex.Match(kvp.Key); + if (inputMatch == null) continue; + + var inputNum = Convert.ToUInt32(inputMatch.Groups["InputNum"].Value); var port = chassis.HdmiInputs[inputNum].HdmiInputPort; // set hdcp disables From 30f63eee03b9f691ace3f699718f6a90877e3d46 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 30 Jul 2021 10:30:32 -0600 Subject: [PATCH 20/28] fix: Remove reference to DMOutput name sig the HD-MD series of switchers appears to not populate the Name sig of the DMOutput, leading to a NullSig exception when attempting to set output names. This is probably because those names can't be set on the device itself, but some experimentation with hardware may be required to determine that completely. --- .../Chassis/HdMdNxM4kEBridgeableController.cs | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs index 3eece080..270e3461 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs @@ -71,10 +71,6 @@ namespace PepperDash.Essentials.DM.Chassis } OutputNames = props.Outputs; } - //else - //{ - // OutputNames.Add(1, "Output"); - //} VideoInputSyncFeedbacks = new FeedbackCollection(); VideoOutputRouteFeedbacks = new FeedbackCollection(); @@ -117,7 +113,7 @@ namespace PepperDash.Essentials.DM.Chassis { var index = i; var outputName = OutputNames[index]; - _Chassis.Outputs[i].Name.StringValue = outputName; + //_Chassis.Outputs[i].Name.StringValue = outputName; OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, index, this) @@ -125,12 +121,12 @@ namespace PepperDash.Essentials.DM.Chassis FeedbackMatchObject = _Chassis.HdmiOutputs[index] }); VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); - OutputNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].Name.StringValue)); + OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); } - _Chassis.DMInputChange += new DMInputEventHandler(Chassis_DMInputChange); - _Chassis.DMOutputChange += new DMOutputEventHandler(Chassis_DMOutputChange); + _Chassis.DMInputChange += Chassis_DMInputChange; + _Chassis.DMOutputChange += Chassis_DMOutputChange; AddPostActivationAction(AddFeedbackCollections); } @@ -354,18 +350,18 @@ namespace PepperDash.Essentials.DM.Chassis OutputRouteNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); } - _Chassis.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler(Chassis_OnlineStatusChange); + _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; - trilist.OnlineStatusChange += new Crestron.SimplSharpPro.OnlineStatusChangeEventHandler((d, args) => + trilist.OnlineStatusChange += (d, args) => { - if (args.DeviceOnLine) - { - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } - } - }); + if (args.DeviceOnLine) + { + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + } + }; } From 70d63a9f99c5eeb4164284a730b13fa03a61cad0 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 30 Jul 2021 10:44:26 -0600 Subject: [PATCH 21/28] chore: Mark non-bridgeable class obsolete Also add a debug message to give the new type to use. --- .../Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs index d19c9752..b47139e2 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEController.cs @@ -14,6 +14,7 @@ using PepperDash.Essentials.DM.Config; namespace PepperDash.Essentials.DM.Chassis { + [Obsolete("Please use HdMdNxM4kEBridgeable Controller")] public class HdMdNxM4kEController : CrestronGenericBaseDevice, IRoutingInputsOutputs, IRouting { public HdMdNxM Chassis { get; private set; } @@ -32,6 +33,7 @@ namespace PepperDash.Essentials.DM.Chassis HdMdNxM4kEPropertiesConfig props) : base(key, name, chassis) { + Debug.Console(0, this, "Type hdmd4x14ke is obsolete. Please use hdmd4x14ke-bridgeable"); Chassis = chassis; // logical ports From cad45c04cbfae8db2053291b3090fc9b7dd1784f Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 20 Sep 2021 16:12:39 -0600 Subject: [PATCH 22/28] fix: Add method to register for the correct event for resolution feedback The resolution feedback change events don't appear to bubble up to the main DMInput change event. It appears to require subscribing to an event on the VideoAttributes class that the input contains. --- .../Chassis/DmChassisController.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index de9a088a..51990f07 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -8,6 +8,7 @@ using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DM; using Crestron.SimplSharpPro.DM.Cards; using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpProInternal; using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; @@ -482,6 +483,29 @@ namespace PepperDash.Essentials.DM } + private void RegisterForInputResolutionFeedback(IVideoAttributesBasic input, uint number, RoutingInputPortWithVideoStatuses inputPort) + { + if (input == null) + { + return; + } + + input.VideoAttributes.AttributeChange += (sender, args) => + { + var inputAttributes = sender as IVideoAttributesBasic; + + if (inputAttributes == null) + { + return; + } + + Debug.Console(1, this, "Input {0} resolution updated", number); + + Debug.Console(1, this, "Updating resolution feedback for input {0}", number); + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + }; + } + /// /// /// @@ -883,6 +907,8 @@ namespace PepperDash.Essentials.DM { FeedbackMatchObject = Chassis.Inputs[cardNum] }; + + RegisterForInputResolutionFeedback(videoAttributesBasic, cardNum, inputPort as RoutingInputPortWithVideoStatuses); } else { From 2e76b6ba0ae81399458c407c3c45cc118785e0f7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 21 Sep 2021 09:40:51 -0600 Subject: [PATCH 23/28] fix(DM): Fix issue with bad cast for IVideoAttributesBasic Also updates to use the DM routing input port rather than the tx AnyVideoInput for the DM input resolutions --- .../Chassis/DmChassisController.cs | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 51990f07..c74aea2b 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -490,15 +490,10 @@ namespace PepperDash.Essentials.DM return; } + Debug.Console(1, this, "Registering for resolution feedback for input {0} using Routing Port {1}", number, inputPort.Key); + input.VideoAttributes.AttributeChange += (sender, args) => { - var inputAttributes = sender as IVideoAttributesBasic; - - if (inputAttributes == null) - { - return; - } - Debug.Console(1, this, "Input {0} resolution updated", number); Debug.Console(1, this, "Updating resolution feedback for input {0}", number); @@ -898,9 +893,15 @@ namespace PepperDash.Essentials.DM if (videoAttributesBasic != null) { + Debug.Console(1, this, "card {0} supports IVideoAttributesBasic", cardNum); var statusFuncs = new VideoStatusFuncsWrapper { - VideoResolutionFeedbackFunc = () => videoAttributesBasic.VideoAttributes.GetVideoResolutionString() + VideoResolutionFeedbackFunc = () => + { + var resolution = videoAttributesBasic.VideoAttributes.GetVideoResolutionString(); + Debug.Console(1, this, "Updating resolution for input {0}. New resolution: {1}", cardNum, resolution); + return resolution; + } }; inputPort = new RoutingInputPortWithVideoStatuses(portKey, sigType, portType, Chassis.Inputs[cardNum], this, statusFuncs) @@ -1496,10 +1497,13 @@ namespace PepperDash.Essentials.DM var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; - if (videoStatus != null) + if (videoStatus == null) { - videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + return; } + + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkStreamInputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) @@ -1686,14 +1690,16 @@ namespace PepperDash.Essentials.DM var videoStatus = inputPort as RoutingInputPortWithVideoStatuses; - if (videoStatus != null) + if (videoStatus == null) { - videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + return; } + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkAdvancedTxToApi(BasicTriList trilist, DmChassisControllerJoinMap joinMap, - uint ioSlot, uint ioSlotJoin, BasicDmTxControllerBase basicTransmitter) + uint ioSlot, uint ioSlotJoin, BasicDmTxControllerBase basicTransmitter) { var transmitter = basicTransmitter as DmTxControllerBase; if (transmitter == null) return; @@ -1707,7 +1713,9 @@ namespace PepperDash.Essentials.DM if (txRoutingInputs == null) return; - var inputPorts = txRoutingInputs.InputPorts.Where((p) => p.Port is EndpointHdmiInput || p.Port is EndpointDisplayPortInput).ToList(); + var inputPorts = + txRoutingInputs.InputPorts.Where( + (p) => p.Port is EndpointHdmiInput || p.Port is EndpointDisplayPortInput).ToList(); if (inputPorts.Count == 0) { @@ -1741,12 +1749,18 @@ namespace PepperDash.Essentials.DM trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = (ushort) transmitter.HdcpSupportCapability; - var videoStatus = txRoutingInputs.InputPorts.Cast().FirstOrDefault((ip) => ip.Key == DmPortName.AnyVideoIn); - if (videoStatus != null) + var videoStatus = + InputPorts[string.Format("inputCard{0}--dmIn", ioSlot)] as RoutingInputPortWithVideoStatuses; + + if (videoStatus == null) { - videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig(trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); + return; } + Debug.Console(1, this, "Linking {0} to join {1} for resolution feedback.", videoStatus.Key, + joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin); + videoStatus.VideoStatus.VideoResolutionFeedback.LinkInputSig( + trilist.StringInput[joinMap.InputCurrentResolution.JoinNumber + ioSlotJoin]); } private void LinkTxOnlineFeedbackToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, From 49e82f107b26e132e25efcc875bdf90b86da12d6 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 21 Sep 2021 12:06:22 -0600 Subject: [PATCH 24/28] fix(Essentials_DM): Updates to try and fix some exceptions --- .../Chassis/HdMdNxM4kEBridgeableController.cs | 82 ++++++++++++++----- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs index 270e3461..1594bbdb 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs @@ -59,7 +59,7 @@ namespace PepperDash.Essentials.DM.Chassis { foreach (var kvp in props.Inputs) { - Debug.Console(0, this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); + Debug.Console(1, this, "props.Inputs: {0}-{1}", kvp.Key, kvp.Value); } InputNames = props.Inputs; } @@ -67,7 +67,7 @@ namespace PepperDash.Essentials.DM.Chassis { foreach (var kvp in props.Outputs) { - Debug.Console(0, this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); + Debug.Console(1, this, "props.Outputs: {0}-{1}", kvp.Key, kvp.Value); } OutputNames = props.Outputs; } @@ -100,7 +100,7 @@ namespace PepperDash.Essentials.DM.Chassis _Chassis.Inputs[index].Name.StringValue = inputName; InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) { FeedbackMatchObject = _Chassis.HdmiInputs[index] }); @@ -116,7 +116,7 @@ namespace PepperDash.Essentials.DM.Chassis //_Chassis.Outputs[i].Name.StringValue = outputName; OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, - eRoutingPortConnectionType.Hdmi, index, this) + eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) { FeedbackMatchObject = _Chassis.HdmiOutputs[index] }); @@ -275,10 +275,19 @@ namespace PepperDash.Essentials.DM.Chassis public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) { + var input = inputSelector as HdMdNxM4kzEHdmiInput; + var output = outputSelector as HdMdNxMHdmiOutput; + + if (output == null) + { + Debug.Console(0, this, "Unable to make switch. output selector is not HdMdNxMHdmiOutput"); + return; + } + // Try to make switch only when necessary. The unit appears to toggle when already selected. - var current = _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut; - if (current != _Chassis.HdmiInputs[(uint)inputSelector]) - _Chassis.HdmiOutputs[(uint)outputSelector].VideoOut = _Chassis.HdmiInputs[(uint)inputSelector]; + var current = output.VideoOut; + if (current != input) + output.VideoOut = input; } #endregion @@ -287,7 +296,9 @@ namespace PepperDash.Essentials.DM.Chassis public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - ExecuteSwitch(inputSelector, outputSelector, signalType); + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + var output = _Chassis.HdmiOutputs[outputSelector]; + ExecuteSwitch(input, output, signalType); } #endregion @@ -327,12 +338,13 @@ namespace PepperDash.Essentials.DM.Chassis for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var joinIndex = i - 1; + var input = i; //Digital VideoInputSyncFeedbacks[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); InputHdcpEnableFeedback[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); InputHdcpEnableFeedback[InputNames[i]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); - trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(i)); - trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(i)); + trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); + trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); //Serial InputNameFeedbacks[InputNames[i]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); @@ -341,9 +353,10 @@ namespace PepperDash.Essentials.DM.Chassis for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) { var joinIndex = i - 1; + var output = i; //Analog VideoOutputRouteFeedbacks[OutputNames[i]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); - trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteSwitch(a, i, eRoutingSignalType.AudioVideo)); + trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); //Serial OutputNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); @@ -354,12 +367,19 @@ namespace PepperDash.Essentials.DM.Chassis trilist.OnlineStatusChange += (d, args) => { - if (args.DeviceOnLine) + if (!args.DeviceOnLine) { - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } + return; + } + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } + + foreach (var name in InputNames) + { + trilist.SetString(joinMap.InputName.JoinNumber + (name.Key - 1), + InputNameFeedbacks[(int) name.Key].StringValue); } }; } @@ -376,10 +396,11 @@ namespace PepperDash.Essentials.DM.Chassis { _Chassis.Inputs[i].Name.StringValue = InputNames[i]; } - for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) + + /*for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) { _Chassis.Outputs[i].Name.StringValue = OutputNames[i]; - } + }*/ foreach (var feedback in Feedbacks) { @@ -391,7 +412,28 @@ namespace PepperDash.Essentials.DM.Chassis { if (args.EventId != DMOutputEventIds.VideoOutEventId) return; - for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) + var output = args.Number; + + var inputNumber = _Chassis.HdmiOutputs[output].VideoOutFeedback == null + ? 0 + : _Chassis.HdmiOutputs[output].VideoOutFeedback.Number; + + var outputName = OutputNames[output]; + + var feedback = VideoOutputRouteFeedbacks[outputName]; + + if (feedback == null) + { + return; + } + var inPort = + InputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output].VideoOutFeedback); + var outPort = OutputPorts.FirstOrDefault(p => p.FeedbackMatchObject == _Chassis.HdmiOutputs[output]); + + feedback.FireUpdate(); + OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); + + /*for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) { var index = i; var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1].VideoOutFeedback); @@ -401,7 +443,7 @@ namespace PepperDash.Essentials.DM.Chassis VideoOutputRouteFeedbacks[i].FireUpdate(); OnSwitchChange(new RoutingNumericEventArgs((ushort)i, VideoOutputRouteFeedbacks[i].UShortValue, localOutputPort, localInputPort, eRoutingSignalType.AudioVideo)); - } + }*/ } void Chassis_DMInputChange(Switch device, DMInputEventArgs args) From eec4484f7887f9e12e3c22da995ef63ee0fa577d Mon Sep 17 00:00:00 2001 From: Jason DeVito Date: Tue, 21 Sep 2021 16:11:13 -0500 Subject: [PATCH 25/28] fix: updates to resolve issues with routing and feedbacks in the HdMdNxM4kEBridgeableController class. --- .../Chassis/HdMdNxM4kEBridgeableController.cs | 151 +++++++++--------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs index 1594bbdb..3d41af3a 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/HdMdNxM4kEBridgeableController.cs @@ -37,8 +37,8 @@ namespace PepperDash.Essentials.DM.Chassis public FeedbackCollection OutputNameFeedbacks { get; private set; } public FeedbackCollection OutputRouteNameFeedbacks { get; private set; } public FeedbackCollection InputHdcpEnableFeedback { get; private set; } - public FeedbackCollection DeviceNameFeedback { get; private set; } - public FeedbackCollection AutoRouteFeedback { get; private set; } + public StringFeedback DeviceNameFeedback { get; private set; } + public BoolFeedback AutoRouteFeedback { get; private set; } #region Constructor @@ -47,6 +47,7 @@ namespace PepperDash.Essentials.DM.Chassis : base(key, name, chassis) { _Chassis = chassis; + Name = name; if (props == null) { @@ -72,32 +73,30 @@ namespace PepperDash.Essentials.DM.Chassis OutputNames = props.Outputs; } + DeviceNameFeedback = new StringFeedback(()=>Name); + VideoInputSyncFeedbacks = new FeedbackCollection(); VideoOutputRouteFeedbacks = new FeedbackCollection(); InputNameFeedbacks = new FeedbackCollection(); OutputNameFeedbacks = new FeedbackCollection(); OutputRouteNameFeedbacks = new FeedbackCollection(); InputHdcpEnableFeedback = new FeedbackCollection(); - DeviceNameFeedback = new FeedbackCollection(); - AutoRouteFeedback = new FeedbackCollection(); - + InputPorts = new RoutingPortCollection(); OutputPorts = new RoutingPortCollection(); - var deviceName = string.IsNullOrEmpty(this.Name) ? name : this.Name; - DeviceNameFeedback.Add(new StringFeedback(deviceName, () => deviceName)); - if (_Chassis.NumberOfInputs == 1) { _Chassis4x1 = _Chassis as HdMd4x14kE; - AutoRouteFeedback.Add(new BoolFeedback(deviceName + "-" + InputNames[1], () => _Chassis4x1.AutoModeOnFeedback.BoolValue)); + AutoRouteFeedback = new BoolFeedback(() => _Chassis4x1.AutoModeOnFeedback.BoolValue); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) { var index = i; var inputName = InputNames[index]; - _Chassis.Inputs[index].Name.StringValue = inputName; + //_Chassis.Inputs[index].Name.StringValue = inputName; + _Chassis.HdmiInputs[index].Name.StringValue = inputName; InputPorts.Add(new RoutingInputPort(inputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, _Chassis.HdmiInputs[index], this) @@ -105,7 +104,8 @@ namespace PepperDash.Essentials.DM.Chassis FeedbackMatchObject = _Chassis.HdmiInputs[index] }); VideoInputSyncFeedbacks.Add(new BoolFeedback(inputName, () => _Chassis.Inputs[index].VideoDetectedFeedback.BoolValue)); - InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].Name.StringValue)); + //InputNameFeedbacks.Add(new StringFeedback(inputName, () => _Chassis.Inputs[index].NameFeedback.StringValue)); + InputNameFeedbacks.Add(new StringFeedback(inputName, () => InputNames[index])); InputHdcpEnableFeedback.Add(new BoolFeedback(inputName, () => _Chassis.HdmiInputs[index].HdmiInputPort.HdcpSupportOnFeedback.BoolValue)); } @@ -113,14 +113,15 @@ namespace PepperDash.Essentials.DM.Chassis { var index = i; var outputName = OutputNames[index]; - //_Chassis.Outputs[i].Name.StringValue = outputName; + //_Chassis.Outputs[index].Name.StringValue = outputName; + //_Chassis.HdmiOutputs[index].Name.StringValue = outputName; OutputPorts.Add(new RoutingOutputPort(outputName, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, _Chassis.HdmiOutputs[index], this) { FeedbackMatchObject = _Chassis.HdmiOutputs[index] }); - VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); + VideoOutputRouteFeedbacks.Add(new IntFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback == null ? 0 : (int)_Chassis.Outputs[index].VideoOutFeedback.Number)); OutputNameFeedbacks.Add(new StringFeedback(outputName, () => OutputNames[index])); OutputRouteNameFeedbacks.Add(new StringFeedback(outputName, () => _Chassis.Outputs[index].VideoOutFeedback.NameFeedback.StringValue)); } @@ -185,9 +186,10 @@ namespace PepperDash.Essentials.DM.Chassis public void AddFeedbackCollections() { + AddFeedbackToList(DeviceNameFeedback); AddCollectionsToList(VideoInputSyncFeedbacks, InputHdcpEnableFeedback); AddCollectionsToList(VideoOutputRouteFeedbacks); - AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks, DeviceNameFeedback); + AddCollectionsToList(InputNameFeedbacks, OutputNameFeedbacks, OutputRouteNameFeedbacks); } #endregion @@ -274,9 +276,10 @@ namespace PepperDash.Essentials.DM.Chassis #region IRouting Members public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - var input = inputSelector as HdMdNxM4kzEHdmiInput; + { + var input = inputSelector as HdMdNxMHdmiInput; //changed from HdMdNxM4kzEHdmiInput; var output = outputSelector as HdMdNxMHdmiOutput; + Debug.Console(2, this, "ExecuteSwitch: input={0} output={1}", input, output); if (output == null) { @@ -286,8 +289,8 @@ namespace PepperDash.Essentials.DM.Chassis // Try to make switch only when necessary. The unit appears to toggle when already selected. var current = output.VideoOut; - if (current != input) - output.VideoOut = input; + if (current != input) + output.VideoOut = input; } #endregion @@ -296,8 +299,11 @@ namespace PepperDash.Essentials.DM.Chassis public void ExecuteNumericSwitch(ushort inputSelector, ushort outputSelector, eRoutingSignalType signalType) { - var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; + var input = inputSelector == 0 ? null : _Chassis.HdmiInputs[inputSelector]; var output = _Chassis.HdmiOutputs[outputSelector]; + + Debug.Console(2, this, "ExecuteNumericSwitch: input={0} output={1}", input, output); + ExecuteSwitch(input, output, signalType); } @@ -326,13 +332,13 @@ namespace PepperDash.Essentials.DM.Chassis } IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - DeviceNameFeedback[this.Name].LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); + DeviceNameFeedback.LinkInputSig(trilist.StringInput[joinMap.Name.JoinNumber]); if (_Chassis4x1 != null) { trilist.SetSigTrueAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOn()); trilist.SetSigFalseAction(joinMap.EnableAutoRoute.JoinNumber, () => _Chassis4x1.AutoModeOff()); - AutoRouteFeedback[this.Name + "-" + InputNames[1]].LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); + AutoRouteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.EnableAutoRoute.JoinNumber]); } for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) @@ -340,14 +346,14 @@ namespace PepperDash.Essentials.DM.Chassis var joinIndex = i - 1; var input = i; //Digital - VideoInputSyncFeedbacks[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); - InputHdcpEnableFeedback[InputNames[i]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); + VideoInputSyncFeedbacks[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.InputSync.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkInputSig(trilist.BooleanInput[joinMap.EnableInputHdcp.JoinNumber + joinIndex]); + InputHdcpEnableFeedback[InputNames[input]].LinkComplementInputSig(trilist.BooleanInput[joinMap.DisableInputHdcp.JoinNumber + joinIndex]); trilist.SetSigTrueAction(joinMap.EnableInputHdcp.JoinNumber + joinIndex, () => EnableHdcp(input)); trilist.SetSigTrueAction(joinMap.DisableInputHdcp.JoinNumber + joinIndex, () => DisableHdcp(input)); - //Serial - InputNameFeedbacks[InputNames[i]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); + //Serial + InputNameFeedbacks[InputNames[input]].LinkInputSig(trilist.StringInput[joinMap.InputName.JoinNumber + joinIndex]); } for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) @@ -355,32 +361,22 @@ namespace PepperDash.Essentials.DM.Chassis var joinIndex = i - 1; var output = i; //Analog - VideoOutputRouteFeedbacks[OutputNames[i]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); + VideoOutputRouteFeedbacks[OutputNames[output]].LinkInputSig(trilist.UShortInput[joinMap.OutputRoute.JoinNumber + joinIndex]); trilist.SetUShortSigAction(joinMap.OutputRoute.JoinNumber + joinIndex, (a) => ExecuteNumericSwitch(a, (ushort) output, eRoutingSignalType.AudioVideo)); //Serial - OutputNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); - OutputRouteNameFeedbacks[OutputNames[i]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); + OutputNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputName.JoinNumber + joinIndex]); + OutputRouteNameFeedbacks[OutputNames[output]].LinkInputSig(trilist.StringInput[joinMap.OutputRoutedName.JoinNumber + joinIndex]); } _Chassis.OnlineStatusChange += Chassis_OnlineStatusChange; trilist.OnlineStatusChange += (d, args) => { - if (!args.DeviceOnLine) - { - return; - } - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } + if (!args.DeviceOnLine) return; - foreach (var name in InputNames) - { - trilist.SetString(joinMap.InputName.JoinNumber + (name.Key - 1), - InputNameFeedbacks[(int) name.Key].StringValue); - } + // feedback updates was moved to the Chassis_OnlineStatusChange + // due to the amount of time it takes for the device to come online }; } @@ -391,21 +387,17 @@ namespace PepperDash.Essentials.DM.Chassis void Chassis_OnlineStatusChange(Crestron.SimplSharpPro.GenericBase currentDevice, Crestron.SimplSharpPro.OnlineOfflineEventArgs args) { - if (!args.DeviceOnLine) return; - for (uint i = 1; i <= _Chassis.NumberOfInputs; i++) - { - _Chassis.Inputs[i].Name.StringValue = InputNames[i]; - } + IsOnline.FireUpdate(); - /*for (uint i = 1; i <= _Chassis.NumberOfOutputs; i++) - { - _Chassis.Outputs[i].Name.StringValue = OutputNames[i]; - }*/ + if (!args.DeviceOnLine) return; + + foreach (var feedback in Feedbacks) + { + feedback.FireUpdate(); + } - foreach (var feedback in Feedbacks) - { - feedback.FireUpdate(); - } + if (_Chassis4x1 != null) + AutoRouteFeedback.FireUpdate(); } void Chassis_DMOutputChange(Switch device, DMOutputEventArgs args) @@ -432,27 +424,40 @@ namespace PepperDash.Essentials.DM.Chassis feedback.FireUpdate(); OnSwitchChange(new RoutingNumericEventArgs(output, inputNumber, outPort, inPort, eRoutingSignalType.AudioVideo)); - - /*for (var i = 0; i < VideoOutputRouteFeedbacks.Count; i++) - { - var index = i; - var localInputPort = InputPorts.FirstOrDefault(p => (DMInput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1].VideoOutFeedback); - var localOutputPort = - OutputPorts.FirstOrDefault(p => (DMOutput)p.FeedbackMatchObject == _Chassis.HdmiOutputs[(uint)index + 1]); - - - VideoOutputRouteFeedbacks[i].FireUpdate(); - OnSwitchChange(new RoutingNumericEventArgs((ushort)i, VideoOutputRouteFeedbacks[i].UShortValue, localOutputPort, localInputPort, eRoutingSignalType.AudioVideo)); - }*/ } void Chassis_DMInputChange(Switch device, DMInputEventArgs args) - { - if (args.EventId != DMInputEventIds.VideoDetectedEventId) return; - foreach (var item in VideoInputSyncFeedbacks) - { - item.FireUpdate(); - } + { + switch (args.EventId) + { + case DMInputEventIds.VideoDetectedEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating VideoInputSyncFeedbacks", args.EventId); + foreach (var item in VideoInputSyncFeedbacks) + { + item.FireUpdate(); + } + break; + } + case DMInputEventIds.InputNameFeedbackEventId: + case DMInputEventIds.InputNameEventId: + case DMInputEventIds.NameFeedbackEventId: + { + Debug.Console(1, this, "Event ID {0}: Updating name feedbacks.", args.EventId); + Debug.Console(1, this, "Input {0} Name {1}", args.Number, + _Chassis.HdmiInputs[args.Number].NameFeedback.StringValue); + foreach (var item in InputNameFeedbacks) + { + item.FireUpdate(); + } + break; + } + default: + { + Debug.Console(1, this, "Unhandled DM Input Event ID {0}", args.EventId); + break; + } + } } #endregion From 8d1f1876437079d04900a884c7ce893d752ed926 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 21 Sep 2021 17:59:12 -0600 Subject: [PATCH 26/28] feat(essentials): #830 Adds new Cisco features Adds IHasDoNotDisturbMode, IHasStandbyMode and IHasHalfWakeMode interfaces. Implements all interfaces on CiscoSparkCodec class. Adds CiscoCodecJoinMap to allow for bridging new Cisco specific functions. --- .../Codec/IHasDoNotDisturb.cs | 45 +++++++ .../Essentials Devices Common.csproj | 3 + .../CiscoCodec/CiscoCodecJoinMap.cs | 120 ++++++++++++++++++ .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 96 +++++++++++++- .../VideoCodec/CiscoCodec/xStatus.cs | 26 +++- .../VideoCodec/Interfaces/IHasStandbyMode.cs | 32 +++++ .../VideoCodec/VideoCodecBase.cs | 2 +- 7 files changed, 319 insertions(+), 5 deletions(-) create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs create mode 100644 essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs new file mode 100644 index 00000000..7bb9b924 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Codec/IHasDoNotDisturb.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.Codec +{ + /// + /// Describes a device that has Do Not Disturb mode capability + /// + public interface IHasDoNotDisturbMode + { + /// + /// Indictes whether Do Not Disturb mode is on (Activated) + /// + BoolFeedback DoNotDisturbModeIsOnFeedback { get; } + + /// + /// Activates Do Not Disturb mode + /// + void ActivateDoNotDisturbMode(); + + /// + /// Deactivates Do Not Disturb mode + /// + void DeactivateDoNotDisturbMode(); + + /// + /// Toggles Do Not Disturb mode + /// + void ToggleDoNotDisturbMode(); + } + + public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode + { + /// + /// Activates Do Not Disturb mode with a timeout + /// + /// + void ActivateDoNotDisturbMode(int timeout); + } +} \ No newline at end of file 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 fc5f68d4..1fcffff2 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 @@ -108,6 +108,7 @@ + @@ -117,6 +118,7 @@ + @@ -125,6 +127,7 @@ + diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs new file mode 100644 index 00000000..8caeaf3e --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoCodecJoinMap.cs @@ -0,0 +1,120 @@ +using System; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges.JoinMaps; + + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco +{ + public class CiscoCodecJoinMap : VideoCodecControllerJoinMap + { + #region Digital + + [JoinName("ActivateDoNotDisturbMode")] + public JoinDataComplete ActivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 221, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Do Not Disturb Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateDoNotDisturbMode")] + public JoinDataComplete DeactivateDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 222, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Do Not Disturb Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ToggleDoNotDisturbMode")] + public JoinDataComplete ToggleDoNotDisturbMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 223, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Toggles Do Not Disturb Mode.", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateStandby")] + public JoinDataComplete ActivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 226, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Standby Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("DeactivateStandby")] + public JoinDataComplete DeactivateStandby = new JoinDataComplete( + new JoinData + { + JoinNumber = 227, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Deactivates Standby Mode. FB High if deactivated.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + [JoinName("ActivateHalfWakeMode")] + public JoinDataComplete ActivateHalfWakeMode = new JoinDataComplete( + new JoinData + { + JoinNumber = 228, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "Activates Half Wake Mode. FB High if active.", + JoinCapabilities = eJoinCapabilities.ToFromSIMPL, + JoinType = eJoinType.Digital + }); + + #endregion + + + #region Analog + + + #endregion + + + #region Serials + + + #endregion + + public CiscoCodecJoinMap(uint joinStart) + : base(joinStart, typeof(CiscoCodecJoinMap)) + { + } + + public CiscoCodecJoinMap(uint joinStart, Type type) + : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index f894141c..904482b0 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -28,7 +28,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public class CiscoSparkCodec : VideoCodecBase, IHasCallHistory, IHasCallFavorites, IHasDirectory, IHasScheduleAwareness, IOccupancyStatusProvider, IHasCodecLayouts, IHasCodecSelfView, - ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute + ICommunicationMonitor, IRouting, IHasCodecCameras, IHasCameraAutoMode, IHasCodecRoomPresets, + IHasExternalSourceSwitching, IHasBranding, IHasCameraOff, IHasCameraMute, IHasDoNotDisturbMode, + IHasHalfWakeMode { private bool _externalSourceChangeRequested; @@ -316,6 +318,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CameraIsMutedFeedback = CameraIsOffFeedback; SupportsCameraOff = true; + DoNotDisturbModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Conference.DoNotDisturb.BoolValue); + HalfWakeModeIsOnFeedback = new BoolFeedback(() => CodecStatus.Status.Standby.State.Value == "Halfwake"); + PresentationViewMaximizedFeedback = new BoolFeedback(() => CurrentPresentationView == "Maximized"); Communication = comm; @@ -413,7 +418,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CodecStatus.Status.Audio.Volume.ValueChangedAction = VolumeLevelFeedback.FireUpdate; CodecStatus.Status.Audio.VolumeMute.ValueChangedAction = MuteFeedback.FireUpdate; CodecStatus.Status.Audio.Microphones.Mute.ValueChangedAction = PrivacyModeIsOnFeedback.FireUpdate; - CodecStatus.Status.Standby.State.ValueChangedAction = StandbyIsOnFeedback.FireUpdate; + CodecStatus.Status.Standby.State.ValueChangedAction = new Action(() => + { + StandbyIsOnFeedback.FireUpdate(); + HalfWakeModeIsOnFeedback.FireUpdate(); + }); CodecStatus.Status.RoomAnalytics.PeoplePresence.ValueChangedAction = RoomIsOccupiedFeedback.FireUpdate; CodecStatus.Status.RoomAnalytics.PeopleCount.Current.ValueChangedAction = PeopleCountFeedback.FireUpdate; CodecStatus.Status.Cameras.SpeakerTrack.Status.ValueChangedAction = CameraAutoModeIsOnFeedback.FireUpdate; @@ -423,6 +432,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco CodecStatus.Status.Video.Layout.LayoutFamily.Local.ValueChangedAction = ComputeLocalLayout; CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = SharingContentIsOnFeedback.FireUpdate; CodecStatus.Status.Conference.Presentation.Mode.ValueChangedAction = FarEndIsSharingContentFeedback.FireUpdate; + CodecStatus.Status.Conference.DoNotDisturb.ValueChangedAction = DoNotDisturbModeIsOnFeedback.FireUpdate; + try { CodecStatus.Status.Video.Input.MainVideoMute.ValueChangedAction = CameraIsOffFeedback.FireUpdate; @@ -1495,7 +1506,49 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) { + var joinMap = new CiscoCodecJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); + + LinkCiscoCodecToApi(trilist, joinMap); + } + + public void LinkCiscoCodecToApi(BasicTriList trilist, CiscoCodecJoinMap joinMap) + { + var dndCodec = this as IHasDoNotDisturbMode; + if (dndCodec != null) + { + dndCodec.DoNotDisturbModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateDoNotDisturbMode.JoinNumber]); + dndCodec.DoNotDisturbModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateDoNotDisturbMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateDoNotDisturbMode.JoinNumber, () => dndCodec.ActivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.DeactivateDoNotDisturbMode.JoinNumber, () => dndCodec.DeactivateDoNotDisturbMode()); + trilist.SetSigFalseAction(joinMap.ToggleDoNotDisturbMode.JoinNumber, () => dndCodec.ToggleDoNotDisturbMode()); + } + + var halfwakeCodec = this as IHasHalfWakeMode; + if (halfwakeCodec != null) + { + halfwakeCodec.StandbyIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateStandby.JoinNumber]); + halfwakeCodec.StandbyIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.DeactivateStandby.JoinNumber]); + halfwakeCodec.HalfWakeModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.ActivateHalfWakeMode.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.ActivateStandby.JoinNumber, () => halfwakeCodec.StandbyActivate()); + trilist.SetSigFalseAction(joinMap.DeactivateStandby.JoinNumber, () => halfwakeCodec.StandbyDeactivate()); + trilist.SetSigFalseAction(joinMap.ActivateHalfWakeMode.JoinNumber, () => halfwakeCodec.HalfwakeActivate()); + } } /// @@ -2069,6 +2122,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco else CameraMuteOn(); } + + #region IHasDoNotDisturbMode Members + + public BoolFeedback DoNotDisturbModeIsOnFeedback { get; private set; } + + public void ActivateDoNotDisturbMode() + { + SendText("xCommand Conference DoNotDisturb Activate"); + } + + public void DeactivateDoNotDisturbMode() + { + SendText("xCommand Conference DoNotDisturb Deactivate"); + } + + public void ToggleDoNotDisturbMode() + { + if (DoNotDisturbModeIsOnFeedback.BoolValue) + { + DeactivateDoNotDisturbMode(); + } + else + { + ActivateDoNotDisturbMode(); + } + } + + #endregion + + #region IHasHalfWakeMode Members + + public BoolFeedback HalfWakeModeIsOnFeedback { get; private set; } + + public void HalfwakeActivate() + { + SendText("xCommand Standby Halfwake"); + } + + #endregion } diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs index c1cd92cf..a32ff5bf 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs @@ -440,9 +440,26 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public CallId CallId { get; set; } } - public class DoNotDisturb + public class DoNotDisturb : ValueProperty { - public string Value { get; set; } + string _Value; + + public bool BoolValue { get; private set; } + + public string Value + { + get + { + return _Value; + } + set + { + _Value = value; + // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false + BoolValue = value == "On" || value == "Active"; + OnValueChanged(); + } + } } public class Mode @@ -600,6 +617,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public Conference2() { Presentation = new Presentation(); + DoNotDisturb = new DoNotDisturb(); } } @@ -1380,12 +1398,16 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco public class State : ValueProperty { + string _value; + public bool BoolValue { get; private set; } public string Value // Valid values are Standby/EnteringStandby/Halfwake/Off { + get { return _value; } set { + _value = value; // If the incoming value is "On" it sets the BoolValue true, otherwise sets it false BoolValue = value == "On" || value == "Standby"; OnValueChanged(); diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs new file mode 100644 index 00000000..55dcd081 --- /dev/null +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/Interfaces/IHasStandbyMode.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + /// + /// Describes a device that has Standby Mode capability + /// + public interface IHasStandbyMode + { + BoolFeedback StandbyIsOnFeedback { get; } + + void StandbyActivate(); + + void StandbyDeactivate(); + } + + /// + /// Describes a device that has Half Waek Mode capability + /// + public interface IHasHalfWakeMode : IHasStandbyMode + { + BoolFeedback HalfWakeModeIsOnFeedback { get; } + + void HalfwakeActivate(); + } +} \ No newline at end of file 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 d34c9e3b..26ed54bb 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 @@ -25,7 +25,7 @@ using Feedback = PepperDash.Essentials.Core.Feedback; namespace PepperDash.Essentials.Devices.Common.VideoCodec { public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced, IHasStandbyMode { private const int XSigEncoding = 28591; protected const int MaxParticipants = 50; From 16db2663a6f82c60f119248c62eb6ff36c966119 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Sep 2021 11:56:06 -0600 Subject: [PATCH 27/28] feat(essentials): #835 comments out some debug messages to clean up console at startup --- .../PepperDashEssentialsBase/Devices/EssentialsDevice.cs | 6 +++--- .../PepperDashEssentialsBase/Factory/DeviceFactory.cs | 4 ++-- .../PepperDashEssentialsBase/Plugins/PluginLoader.cs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs index 46fa819b..a72bd282 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/EssentialsDevice.cs @@ -57,7 +57,7 @@ namespace PepperDash.Essentials.Core public DescriptionAttribute(string description) { - Debug.Console(2, "Setting Description: {0}", description); + //Debug.Console(2, "Setting Description: {0}", description); _Description = description; } @@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core public ConfigSnippetAttribute(string configSnippet) { - Debug.Console(2, "Setting Config Snippet {0}", configSnippet); + //Debug.Console(2, "Setting Config Snippet {0}", configSnippet); _ConfigSnippet = configSnippet; } @@ -103,7 +103,7 @@ namespace PepperDash.Essentials.Core { foreach (var typeName in TypeNames) { - Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + //Debug.Console(2, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; string description = descriptionAttribute[0].Description; var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs index a17e0d91..ebdc87b1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Factory/DeviceFactory.cs @@ -69,13 +69,13 @@ namespace PepperDash.Essentials.Core /// public static void AddFactoryForType(string typeName, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method}); } public static void AddFactoryForType(string typeName, string description, CType cType, Func method) { - Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); + //Debug.Console(1, Debug.ErrorLogLevel.Notice, "Adding factory method for type '{0}'", typeName); if(FactoryMethods.ContainsKey(typeName)) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index 7437d75a..136303e3 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -123,7 +123,7 @@ namespace PepperDash.Essentials /// static LoadedAssembly LoadAssembly(string filePath) { - Debug.Console(2, "Attempting to load {0}", filePath); + //Debug.Console(2, "Attempting to load {0}", filePath); var assembly = Assembly.LoadFrom(filePath); if (assembly != null) { From a165ba911f87356c9cec67d217cfc05c8b87b26c Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Sep 2021 15:31:13 -0600 Subject: [PATCH 28/28] feat(essentials): Minor updates after testing against codec hardware --- .../VideoCodec/CiscoCodec/CiscoSparkCodec.cs | 6 ++++-- .../VideoCodec/CiscoCodec/xStatus.cs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs index 904482b0..d625637c 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/CiscoSparkCodec.cs @@ -586,6 +586,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Status/Audio" + Delimiter + prefix + "/Status/Call" + Delimiter + prefix + "/Status/Conference/Presentation" + Delimiter + + prefix + "/Status/Conference/DoNotDisturb" + Delimiter + prefix + "/Status/Cameras/SpeakerTrack" + Delimiter + prefix + "/Status/RoomAnalytics" + Delimiter + prefix + "/Status/RoomPreset" + Delimiter + @@ -594,10 +595,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco prefix + "/Status/Video/Layout" + Delimiter + prefix + "/Status/Video/Input/MainVideoMute" + Delimiter + prefix + "/Bookings" + Delimiter + - prefix + "/Event/CallDisconnect" + Delimiter + prefix + "/Event/Bookings" + Delimiter + prefix + "/Event/CameraPresetListUpdated" + Delimiter + - prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter; + prefix + "/Event/UserInterface/Presentation/ExternalSource/Selected/SourceIdentifier" + Delimiter + + prefix + "/Event/CallDisconnect" + Delimiter; // Keep CallDisconnect last to detect when feedback registration completes correctly + } #endregion diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs index a32ff5bf..29b9f260 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/CiscoCodec/xStatus.cs @@ -2113,6 +2113,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.Cisco Conference = new Conference2(); SystemUnit = new SystemUnit(); Video = new Video(); + Conference = new Conference2(); } }