From 56e106ff32612eeb21d2cec30df9ca3ab236d979 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 24 Nov 2020 15:46:33 -0700 Subject: [PATCH 01/89] #512 add null check and return unknown if it is null --- .../Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs index 8f7b6876..54d9f070 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 @@ -205,7 +205,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected Func SelfviewPipPositionFeedbackFunc { - get { return () => _currentSelfviewPipPosition.Command; } + get { return () => _currentSelfviewPipPosition.Command ?? "Unknown"; } } protected Func LocalLayoutFeedbackFunc From 14ad0eee48699d9287b2d1b7441246eb996d92fd Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 24 Nov 2020 16:04:01 -0700 Subject: [PATCH 02/89] adding some debug statements to try and suss out what's going on --- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) 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 54d9f070..97cfd954 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 @@ -205,7 +205,14 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom protected Func SelfviewPipPositionFeedbackFunc { - get { return () => _currentSelfviewPipPosition.Command ?? "Unknown"; } + get + { + return + () => + _currentSelfviewPipPosition != null + ? _currentSelfviewPipPosition.Command ?? "Unknown" + : "Unknown"; + } } protected Func LocalLayoutFeedbackFunc @@ -1373,6 +1380,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.Sharing.State.ToString() : "no call"); + if (Status.Call != null) { var callStatus = Status.Call.Status; @@ -1406,6 +1416,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ActiveCalls.Add(newCall); + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + OnCallStatusChange(newCall); } } @@ -1423,6 +1436,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom break; } + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + OnCallStatusChange(existingCall); } } @@ -1460,6 +1476,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { base.OnCallStatusChange(item); + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + if (_props.AutoDefaultLayouts) { SetLayout(); From b41dd23c7f456c1c8b6f9879efe3f5a5acd32671 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 30 Nov 2020 14:29:57 -0600 Subject: [PATCH 03/89] Hotfix testing with Zoom PC to find what is triggering a layout change. Currently unresolved --- .../VideoCodec/ZoomRoom/ResponseObjects.cs | 2 +- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 20 +++++++++++-------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index 4f4eddbc..3be18c1d 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 @@ -96,7 +96,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom Audio = new zConfiguration.Audio(); Video = new zConfiguration.Video(); Client = new zConfiguration.Client(); - Camera = new zConfiguration.Camera(); + Camera = new zConfiguration.Camera(); } } 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 97cfd954..3c07bcfc 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 @@ -491,9 +491,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom }; Status.Call.Sharing.PropertyChanged += (o, a) => - { + { if (a.PropertyName == "State") - { + { + Debug.Console(1, this, "[SetUpFeedbackActions] Status.Call.Sharing.PropertyChanged: {0}", Status.Call.Sharing.State); SharingContentIsOnFeedback.FireUpdate(); ReceivingContent.FireUpdate(); } @@ -904,7 +905,9 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom { case "call": { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); + Debug.Console(1, this, "[DeserializeResponse] sharing status 1: {0}", Status.Call.Sharing.State.ToString()); + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); + Debug.Console(1, this, "[DeserializeResponse] sharing status 2: {0}", Status.Call.Sharing.State.ToString()); break; } @@ -1386,10 +1389,11 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom if (Status.Call != null) { var callStatus = Status.Call.Status; + Debug.Console(1, this, "[UpdateCallStatus] callStatus: {0}", callStatus.ToString()); // If not currently in a meeting, intialize the call object if (callStatus != zStatus.eCallStatus.IN_MEETING || callStatus != zStatus.eCallStatus.CONNECTING_MEETING) - { + { Status.Call = new zStatus.Call {Status = callStatus}; SetUpCallFeedbackActions(); @@ -1417,8 +1421,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom ActiveCalls.Add(newCall); Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + OnCallStatusChange(newCall); } } @@ -1437,8 +1441,8 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom } Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + OnCallStatusChange(existingCall); } } From 91dda3213eae40a6d9f4cf42d0613ab5fb15f859 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 30 Nov 2020 13:42:27 -0700 Subject: [PATCH 04/89] fix an IF statement to be correct --- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 4201 ++++++++--------- 1 file changed, 2099 insertions(+), 2102 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 3c07bcfc..fc440e5b 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,2108 +1,2105 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.CrestronThread; -using Crestron.SimplSharpPro.DeviceSupport; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common.Cameras; -using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using PepperDash_Essentials_Core.DeviceTypeInterfaces; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom -{ - public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, - IRouting, - IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraAutoMode, - IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing - { - private const long MeetingRefreshTimer = 60000; - private const uint DefaultMeetingDurationMin = 30; - private const string Delimiter = "\x0D\x0A"; - private readonly CrestronQueue _receiveQueue; - - - private readonly Thread _receiveThread; - - private readonly ZoomRoomSyncState _syncState; - public bool CommDebuggingIsOn; - private CodecDirectory _currentDirectoryResult; - private uint _jsonCurlyBraceCounter; - private bool _jsonFeedbackMessageIsIncoming; - private StringBuilder _jsonMessage; - private int _previousVolumeLevel; - private CameraBase _selectedCamera; - - private readonly ZoomRoomPropertiesConfig _props; - - public ZoomRoom(DeviceConfig config, IBasicCommunication comm) - : base(config) - { - _props = JsonConvert.DeserializeObject(config.Properties.ToString()); - - // The queue that will collect the repsonses in the order they are received - _receiveQueue = new CrestronQueue(1024); - - // The thread responsible for dequeuing and processing the messages - _receiveThread = new Thread(o => ProcessQueue(), null) {Priority = Thread.eThreadPriority.MediumPriority}; - - Communication = comm; - - if (_props.CommunicationMonitorProperties != null) - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, - _props.CommunicationMonitorProperties); - } - else - { - CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, - "zStatus SystemUnit\r"); - } - - DeviceManager.AddDevice(CommunicationMonitor); - - Status = new ZoomRoomStatus(); - - Configuration = new ZoomRoomConfiguration(); - - CodecInfo = new ZoomRoomInfo(Status, Configuration); - - _syncState = new ZoomRoomSyncState(Key + "--Sync", this); - - _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; - - PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); - - PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; - PortGather.LineReceived += Port_LineReceived; - - CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, - eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); - - Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, - eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); - - CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); - - CameraAutoModeIsOnFeedback = new BoolFeedback(CameraAutoModeIsOnFeedbackFunc); - - CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); - - ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); - - SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); - - SetUpFeedbackActions(); - - Cameras = new List(); - - SetUpDirectory(); - - Participants = new CodecParticipants(); - - SupportsCameraOff = _props.SupportsCameraOff; - SupportsCameraAutoMode = _props.SupportsCameraAutoMode; - - PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); - CallerIdNameFeedback = new StringFeedback(CallerIdNameFeedbackFunc); - CallerIdNumberFeedback = new StringFeedback(CallerIdNumberFeedbackFunc); - } - - public CommunicationGather PortGather { get; private set; } - - public ZoomRoomStatus Status { get; private set; } - - public ZoomRoomConfiguration Configuration { get; private set; } - - //CTimer LoginMessageReceivedTimer; - //CTimer RetryConnectionTimer; - - /// - /// Gets and returns the scaled volume of the codec - /// - protected override Func VolumeLevelFeedbackFunc - { - get - { - return () => CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); - } - } - - protected override Func PrivacyModeIsOnFeedbackFunc - { - get { return () => Configuration.Call.Microphone.Mute; } - } - - protected override Func StandbyIsOnFeedbackFunc - { - get { return () => false; } - } - - protected override Func SharingSourceFeedbackFunc - { - get { return () => Status.Sharing.dispState; } - } - - protected override Func SharingContentIsOnFeedbackFunc - { - get { return () => Status.Call.Sharing.IsSharing; } - } - - protected Func FarEndIsSharingContentFeedbackFunc - { - get { return () => Status.Call.Sharing.State == zEvent.eSharingState.Receiving; } - } - - protected override Func MuteFeedbackFunc - { - get { return () => Configuration.Audio.Output.Volume == 0; } - } - - //protected Func RoomIsOccupiedFeedbackFunc - //{ - // get - // { - // return () => false; - // } - //} - - //protected Func PeopleCountFeedbackFunc - //{ - // get - // { - // return () => 0; - // } - //} - - protected Func SelfViewIsOnFeedbackFunc - { - get { return () => !Configuration.Video.HideConfSelfVideo; } - } - - protected Func CameraIsOffFeedbackFunc - { - get { return () => Configuration.Call.Camera.Mute; } - } - - protected Func CameraAutoModeIsOnFeedbackFunc - { - get { return () => false; } - } - - protected Func SelfviewPipPositionFeedbackFunc - { - get - { - return - () => - _currentSelfviewPipPosition != null - ? _currentSelfviewPipPosition.Command ?? "Unknown" - : "Unknown"; - } - } - - protected Func LocalLayoutFeedbackFunc - { - get { return () => ""; } - } - - protected Func LocalLayoutIsProminentFeedbackFunc - { - get { return () => false; } - } - - - public RoutingInputPort CodecOsdIn { get; private set; } - public RoutingOutputPort Output1 { get; private set; } - - #region ICommunicationMonitor Members - - public StatusMonitorBase CommunicationMonitor { get; private set; } - - #endregion - - #region IHasCodecCameras Members - - public event EventHandler CameraSelected; - - public List Cameras { get; private set; } - - public CameraBase SelectedCamera - { - get { return _selectedCamera; } - private set - { - _selectedCamera = value; - SelectedCameraFeedback.FireUpdate(); - ControllingFarEndCameraFeedback.FireUpdate(); - - var handler = CameraSelected; - if (handler != null) - { - handler(this, new CameraSelectedEventArgs(SelectedCamera)); - } - } - } - - - public StringFeedback SelectedCameraFeedback { get; private set; } - - public void SelectCamera(string key) - { - if (Cameras == null) - { - return; - } - - var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); - if (camera != null) - { - Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); - SelectedCamera = camera; - } - else - { - Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); - } - } - - public CameraBase FarEndCamera { get; private set; } - - public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } - - #endregion - - #region IHasCodecSelfView Members - - public BoolFeedback SelfviewIsOnFeedback { get; private set; } - - public void SelfViewModeOn() - { - SendText("zConfiguration Video hide_conf_self_video: off"); - } - - public void SelfViewModeOff() - { - SendText("zConfiguration Video hide_conf_self_video: on"); - } - - public void SelfViewModeToggle() - { - if (SelfviewIsOnFeedback.BoolValue) - { - SelfViewModeOff(); - } - else - { - SelfViewModeOn(); - } - } - - #endregion - - #region IHasDirectoryHistoryStack Members - - public event EventHandler DirectoryResultReturned; - public CodecDirectory DirectoryRoot { get; private set; } - - public CodecDirectory CurrentDirectoryResult - { - get { return _currentDirectoryResult; } - } - - public CodecPhonebookSyncState PhonebookSyncState { get; private set; } - - public void SearchDirectory(string searchString) - { - var directoryResults = new CodecDirectory(); - - directoryResults.AddContactsToDirectory( - DirectoryRoot.CurrentDirectoryResults.FindAll( - c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); - - DirectoryBrowseHistoryStack.Clear(); - _currentDirectoryResult = directoryResults; - - OnDirectoryResultReturned(directoryResults); - } - - public void GetDirectoryFolderContents(string folderId) - { - var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; - - directoryResults.AddContactsToDirectory( - DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); - - DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); - - _currentDirectoryResult = directoryResults; - - OnDirectoryResultReturned(directoryResults); - } - - public void SetCurrentDirectoryToRoot() - { - DirectoryBrowseHistoryStack.Clear(); - - _currentDirectoryResult = DirectoryRoot; - - OnDirectoryResultReturned(DirectoryRoot); - } - - public void GetDirectoryParentFolderContents() - { - if (DirectoryBrowseHistoryStack.Count == 0) - { - return; - } - - var currentDirectory = DirectoryBrowseHistoryStack.Pop(); - - _currentDirectoryResult = currentDirectory; - - OnDirectoryResultReturned(currentDirectory); - } - - public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } - - public List DirectoryBrowseHistory { get; private set; } - - public Stack DirectoryBrowseHistoryStack { get; private set; } - - #endregion - - #region IHasScheduleAwareness Members - - public CodecScheduleAwareness CodecSchedule { get; private set; } - - public void GetSchedule() - { - GetBookings(); - } - - #endregion - - #region IRouting Members - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - ExecuteSwitch(inputSelector); - } - - #endregion - - private void SyncState_InitialSyncCompleted(object sender, EventArgs e) - { - SetUpRouting(); - - SetIsReady(); - } - - private void SetUpCallFeedbackActions() - { - Status.Call.Sharing.PropertyChanged += (o, a) => - { - if (a.PropertyName == "State") - { - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; - - Status.Call.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Info") - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - } - }; - } - - /// - /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. - /// - private void SetUpFeedbackActions() - { - Configuration.Audio.Output.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Volume") - { - VolumeLevelFeedback.FireUpdate(); - MuteFeedback.FireUpdate(); - } - }; - - Configuration.Call.Microphone.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Mute") - { - PrivacyModeIsOnFeedback.FireUpdate(); - } - }; - - Configuration.Video.PropertyChanged += (o, a) => - { - if (a.PropertyName == "HideConfSelfVideo") - { - SelfviewIsOnFeedback.FireUpdate(); - } - }; - Configuration.Video.Camera.PropertyChanged += (o, a) => - { - if (a.PropertyName == "SelectedId") - { - SelectCamera(Configuration.Video.Camera.SelectedId); - // this will in turn fire the affected feedbacks - } - }; - - Configuration.Call.Camera.PropertyChanged += (o, a) => - { - Debug.Console(1, this, "Configuration.Call.Camera.PropertyChanged: {0}", a.PropertyName); - - if (a.PropertyName != "Mute") return; - - CameraIsOffFeedback.FireUpdate(); - CameraAutoModeIsOnFeedback.FireUpdate(); - }; - - Configuration.Call.Layout.PropertyChanged += (o, a) => - { - if (a.PropertyName != "Position") return; - - ComputeSelfviewPipStatus(); - - SelfviewPipPositionFeedback.FireUpdate(); - }; - - Status.Call.Sharing.PropertyChanged += (o, a) => +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.CrestronThread; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public class ZoomRoom : VideoCodecBase, IHasCodecSelfView, IHasDirectoryHistoryStack, ICommunicationMonitor, + IRouting, + IHasScheduleAwareness, IHasCodecCameras, IHasParticipants, IHasCameraOff, IHasCameraAutoMode, + IHasFarEndContentStatus, IHasSelfviewPosition, IHasPhoneDialing + { + private const long MeetingRefreshTimer = 60000; + private const uint DefaultMeetingDurationMin = 30; + private const string Delimiter = "\x0D\x0A"; + private readonly CrestronQueue _receiveQueue; + + + private readonly Thread _receiveThread; + + private readonly ZoomRoomSyncState _syncState; + public bool CommDebuggingIsOn; + private CodecDirectory _currentDirectoryResult; + private uint _jsonCurlyBraceCounter; + private bool _jsonFeedbackMessageIsIncoming; + private StringBuilder _jsonMessage; + private int _previousVolumeLevel; + private CameraBase _selectedCamera; + + private readonly ZoomRoomPropertiesConfig _props; + + public ZoomRoom(DeviceConfig config, IBasicCommunication comm) + : base(config) + { + _props = JsonConvert.DeserializeObject(config.Properties.ToString()); + + // The queue that will collect the repsonses in the order they are received + _receiveQueue = new CrestronQueue(1024); + + // The thread responsible for dequeuing and processing the messages + _receiveThread = new Thread(o => ProcessQueue(), null) {Priority = Thread.eThreadPriority.MediumPriority}; + + Communication = comm; + + if (_props.CommunicationMonitorProperties != null) { - if (a.PropertyName == "State") + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, + _props.CommunicationMonitorProperties); + } + else + { + CommunicationMonitor = new GenericCommunicationMonitor(this, Communication, 30000, 120000, 300000, + "zStatus SystemUnit\r"); + } + + DeviceManager.AddDevice(CommunicationMonitor); + + Status = new ZoomRoomStatus(); + + Configuration = new ZoomRoomConfiguration(); + + CodecInfo = new ZoomRoomInfo(Status, Configuration); + + _syncState = new ZoomRoomSyncState(Key + "--Sync", this); + + _syncState.InitialSyncCompleted += SyncState_InitialSyncCompleted; + + PhonebookSyncState = new CodecPhonebookSyncState(Key + "--PhonebookSync"); + + PortGather = new CommunicationGather(Communication, "\x0A") {IncludeDelimiter = true}; + PortGather.LineReceived += Port_LineReceived; + + CodecOsdIn = new RoutingInputPort(RoutingPortNames.CodecOsd, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, new Action(StopSharing), this); + + Output1 = new RoutingOutputPort(RoutingPortNames.AnyVideoOut, + eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + SelfviewIsOnFeedback = new BoolFeedback(SelfViewIsOnFeedbackFunc); + + CameraIsOffFeedback = new BoolFeedback(CameraIsOffFeedbackFunc); + + CameraAutoModeIsOnFeedback = new BoolFeedback(CameraAutoModeIsOnFeedbackFunc); + + CodecSchedule = new CodecScheduleAwareness(MeetingRefreshTimer); + + ReceivingContent = new BoolFeedback(FarEndIsSharingContentFeedbackFunc); + + SelfviewPipPositionFeedback = new StringFeedback(SelfviewPipPositionFeedbackFunc); + + SetUpFeedbackActions(); + + Cameras = new List(); + + SetUpDirectory(); + + Participants = new CodecParticipants(); + + SupportsCameraOff = _props.SupportsCameraOff; + SupportsCameraAutoMode = _props.SupportsCameraAutoMode; + + PhoneOffHookFeedback = new BoolFeedback(PhoneOffHookFeedbackFunc); + CallerIdNameFeedback = new StringFeedback(CallerIdNameFeedbackFunc); + CallerIdNumberFeedback = new StringFeedback(CallerIdNumberFeedbackFunc); + } + + public CommunicationGather PortGather { get; private set; } + + public ZoomRoomStatus Status { get; private set; } + + public ZoomRoomConfiguration Configuration { get; private set; } + + //CTimer LoginMessageReceivedTimer; + //CTimer RetryConnectionTimer; + + /// + /// Gets and returns the scaled volume of the codec + /// + protected override Func VolumeLevelFeedbackFunc + { + get + { + return () => CrestronEnvironment.ScaleWithLimits(Configuration.Audio.Output.Volume, 100, 0, 65535, 0); + } + } + + protected override Func PrivacyModeIsOnFeedbackFunc + { + get { return () => Configuration.Call.Microphone.Mute; } + } + + protected override Func StandbyIsOnFeedbackFunc + { + get { return () => false; } + } + + protected override Func SharingSourceFeedbackFunc + { + get { return () => Status.Sharing.dispState; } + } + + protected override Func SharingContentIsOnFeedbackFunc + { + get { return () => Status.Call.Sharing.IsSharing; } + } + + protected Func FarEndIsSharingContentFeedbackFunc + { + get { return () => Status.Call.Sharing.State == zEvent.eSharingState.Receiving; } + } + + protected override Func MuteFeedbackFunc + { + get { return () => Configuration.Audio.Output.Volume == 0; } + } + + //protected Func RoomIsOccupiedFeedbackFunc + //{ + // get + // { + // return () => false; + // } + //} + + //protected Func PeopleCountFeedbackFunc + //{ + // get + // { + // return () => 0; + // } + //} + + protected Func SelfViewIsOnFeedbackFunc + { + get { return () => !Configuration.Video.HideConfSelfVideo; } + } + + protected Func CameraIsOffFeedbackFunc + { + get { return () => Configuration.Call.Camera.Mute; } + } + + protected Func CameraAutoModeIsOnFeedbackFunc + { + get { return () => false; } + } + + protected Func SelfviewPipPositionFeedbackFunc + { + get + { + return + () => + _currentSelfviewPipPosition != null + ? _currentSelfviewPipPosition.Command ?? "Unknown" + : "Unknown"; + } + } + + protected Func LocalLayoutFeedbackFunc + { + get { return () => ""; } + } + + protected Func LocalLayoutIsProminentFeedbackFunc + { + get { return () => false; } + } + + + public RoutingInputPort CodecOsdIn { get; private set; } + public RoutingOutputPort Output1 { get; private set; } + + #region ICommunicationMonitor Members + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #endregion + + #region IHasCodecCameras Members + + public event EventHandler CameraSelected; + + public List Cameras { get; private set; } + + public CameraBase SelectedCamera + { + get { return _selectedCamera; } + private set + { + _selectedCamera = value; + SelectedCameraFeedback.FireUpdate(); + ControllingFarEndCameraFeedback.FireUpdate(); + + var handler = CameraSelected; + if (handler != null) { - Debug.Console(1, this, "[SetUpFeedbackActions] Status.Call.Sharing.PropertyChanged: {0}", Status.Call.Sharing.State); - SharingContentIsOnFeedback.FireUpdate(); - ReceivingContent.FireUpdate(); - } - }; - - Status.Call.PropertyChanged += (o, a) => - { - if (a.PropertyName == "Info") - { - Debug.Console(1, this, "Updating Call Status"); - UpdateCallStatus(); - } - }; - - Status.Sharing.PropertyChanged += (o, a) => - { - switch (a.PropertyName) - { - case "dispState": - SharingSourceFeedback.FireUpdate(); - break; - case "password": - break; - } - }; - - Status.PhoneCall.PropertyChanged += (o, a) => - { - switch (a.PropertyName) - { - case "IsIncomingCall": - Debug.Console(1, this, "Incoming Phone Call: {0}", Status.PhoneCall.IsIncomingCall); - break; - case "PeerDisplayName": - Debug.Console(1, this, "Peer Display Name: {0}", Status.PhoneCall.PeerDisplayName); - CallerIdNameFeedback.FireUpdate(); - break; - case "PeerNumber": - Debug.Console(1, this, "Peer Number: {0}", Status.PhoneCall.PeerNumber); - CallerIdNumberFeedback.FireUpdate(); - break; - case "OffHook": - Debug.Console(1, this, "Phone is OffHook: {0}", Status.PhoneCall.OffHook); - PhoneOffHookFeedback.FireUpdate(); - break; - } - }; - } - - private void SetUpDirectory() - { - DirectoryRoot = new CodecDirectory(); - - DirectoryBrowseHistory = new List(); - DirectoryBrowseHistoryStack = new Stack(); - - CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => _currentDirectoryResult != DirectoryRoot); - - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - } - - private void SetUpRouting() - { - // Set up input ports - CreateOsdSource(); - InputPorts.Add(CodecOsdIn); - - // Set up output ports - OutputPorts.Add(Output1); - } - - /// - /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input - /// to enable routing - /// - private void CreateOsdSource() - { - OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); - DeviceManager.AddDevice(OsdSource); - var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); - TieLineCollection.Default.Add(tl); - - //foreach(var input in Status.Video. - } - - /// - /// Starts the HTTP feedback server and syncronizes state of codec - /// - /// - public override bool CustomActivate() - { - CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", - ConsoleAccessLevelEnum.AccessOperator); - if (!_props.DisablePhonebookAutoDownload) - { - CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), - "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", - ConsoleAccessLevelEnum.AccessOperator); - } - - CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", - "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); - - var socket = Communication as ISocketStatus; - if (socket != null) - { - socket.ConnectionChange += socket_ConnectionChange; - } - - CommDebuggingIsOn = false; - - Communication.Connect(); - - CommunicationMonitor.Start(); - - return base.CustomActivate(); - } - - public void SetCommDebug(string s) - { - if (s == "1") - { - CommDebuggingIsOn = true; - Debug.Console(0, this, "Comm Debug Enabled."); - } - else - { - CommDebuggingIsOn = false; - Debug.Console(0, this, "Comm Debug Disabled."); - } - } - - private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) - { - Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); - if (e.Client.IsConnected) - { - } - else - { - _syncState.CodecDisconnected(); - PhonebookSyncState.CodecDisconnected(); - } - } - - public void SendText(string command) - { - if (CommDebuggingIsOn) - { - Debug.Console(1, this, "Sending: '{0}'", command); - } - - Communication.SendText(command + Delimiter); - } - - /// - /// Gathers responses and enqueues them. - /// - /// - /// - private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) - { - //if (CommDebuggingIsOn) - // Debug.Console(1, this, "Gathered: '{0}'", args.Text); - - _receiveQueue.Enqueue(args.Text); - - // If the receive thread has for some reason stopped, this will restart it - if (_receiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) - { - _receiveThread.Start(); - } - } - - - /// - /// Runs in it's own thread to dequeue messages in the order they were received to be processed - /// - /// - private object ProcessQueue() - { - try - { - while (true) - { - var message = _receiveQueue.Dequeue(); - - ProcessMessage(message); - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error Processing Queue: {0}", e); - } - - return null; - } - - - /// - /// Queues the initial queries to be sent upon connection - /// - private void SetUpSyncQueries() - { - // zStatus - _syncState.AddQueryToQueue("zStatus Call Status"); - _syncState.AddQueryToQueue("zStatus Audio Input Line"); - _syncState.AddQueryToQueue("zStatus Audio Output Line"); - _syncState.AddQueryToQueue("zStatus Video Camera Line"); - _syncState.AddQueryToQueue("zStatus Video Optimizable"); - _syncState.AddQueryToQueue("zStatus Capabilities"); - _syncState.AddQueryToQueue("zStatus Sharing"); - _syncState.AddQueryToQueue("zStatus CameraShare"); - _syncState.AddQueryToQueue("zStatus Call Layout"); - _syncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); - _syncState.AddQueryToQueue("zStatus NumberOfScreens"); - - // zConfiguration - - _syncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); - _syncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); - _syncState.AddQueryToQueue("zConfiguration Call Camera Mute"); - _syncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); - _syncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); - _syncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); - _syncState.AddQueryToQueue("zConfiguration Audio Input volume"); - _syncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); - _syncState.AddQueryToQueue("zConfiguration Audio Output volume"); - _syncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); - _syncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); - _syncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); - _syncState.AddQueryToQueue("zConfiguration Client appVersion"); - _syncState.AddQueryToQueue("zConfiguration Client deviceSystem"); - _syncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Style"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Size"); - _syncState.AddQueryToQueue("zConfiguration Call Layout Position"); - _syncState.AddQueryToQueue("zConfiguration Call Lock Enable"); - _syncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); - _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); - _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); - - // zCommand - - if (!_props.DisablePhonebookAutoDownload) - { - _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); - } - - _syncState.AddQueryToQueue("zCommand Bookings List"); - _syncState.AddQueryToQueue("zCommand Call ListParticipants"); - _syncState.AddQueryToQueue("zCommand Call Info"); - - - _syncState.StartSync(); - } - - /// - /// Processes messages as they are dequeued - /// - /// - private void ProcessMessage(string message) - { - // Counts the curly braces - if (message.Contains("client_loop: send disconnect: Broken pipe")) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, - "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); - - return; - } - - if (message.Contains('{')) - { - _jsonCurlyBraceCounter++; - } - - if (message.Contains('}')) - { - _jsonCurlyBraceCounter--; - } - - Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); - - if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) - // Check for the beginning of a new JSON message - { - _jsonFeedbackMessageIsIncoming = true; - _jsonCurlyBraceCounter = 1; // reset the counter for each new message - - _jsonMessage = new StringBuilder(); - - _jsonMessage.Append(message); - - if (CommDebuggingIsOn) - { - Debug.Console(2, this, "Incoming JSON message..."); - } - - return; - } - if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) - // Check for the end of a JSON message - { - _jsonMessage.Append(message); - - if (_jsonCurlyBraceCounter == 0) - { - _jsonFeedbackMessageIsIncoming = false; - - if (CommDebuggingIsOn) - { - Debug.Console(2, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); - } - - // Forward the complete message to be deserialized - DeserializeResponse(_jsonMessage.ToString()); - } - - //JsonMessage = new StringBuilder(); - return; - } - - // NOTE: This must happen after the above conditions have been checked - // Append subsequent partial JSON fragments to the string builder - if (_jsonFeedbackMessageIsIncoming) - { - _jsonMessage.Append(message); - - //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); - return; - } - - if (CommDebuggingIsOn) - { - Debug.Console(1, this, "Non-JSON response: '{0}'", message); - } - - _jsonCurlyBraceCounter = 0; // reset on non-JSON response - - if (!_syncState.InitialSyncComplete) - { - switch (message.Trim().ToLower()) // remove the whitespace - { - case "*r login successful": - { - _syncState.LoginMessageReceived(); - - // Fire up a thread to send the intial commands. - CrestronInvoke.BeginInvoke(o => - { - Thread.Sleep(100); - // disable echo of commands - SendText("echo off"); - Thread.Sleep(100); - // set feedback exclusions - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); - Thread.Sleep(100); - SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); - Thread.Sleep(100); - - if (!_props.DisablePhonebookAutoDownload) - { - SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); - } - // switch to json format - SendText("format json"); - }); - - break; - } - } - } - } - - /// - /// Deserializes a JSON formatted response - /// - /// - private void DeserializeResponse(string response) - { - try - { - var trimmedResponse = response.Trim(); - - if (trimmedResponse.Length <= 0) - { - return; - } - - var message = JObject.Parse(trimmedResponse); - - var eType = - (eZoomRoomResponseType) - Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); - - var topKey = message["topKey"].Value(); - - var responseObj = message[topKey]; - - Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); - - switch (eType) - { - case eZoomRoomResponseType.zConfiguration: - { - switch (topKey.ToLower()) - { - case "call": - { - Debug.Console(1, this, "[DeserializeResponse] sharing status 1: {0}", Status.Call.Sharing.State.ToString()); + handler(this, new CameraSelectedEventArgs(SelectedCamera)); + } + } + } + + + public StringFeedback SelectedCameraFeedback { get; private set; } + + public void SelectCamera(string key) + { + if (Cameras == null) + { + return; + } + + var camera = Cameras.FirstOrDefault(c => c.Key.IndexOf(key, StringComparison.OrdinalIgnoreCase) > -1); + if (camera != null) + { + Debug.Console(1, this, "Selected Camera with key: '{0}'", camera.Key); + SelectedCamera = camera; + } + else + { + Debug.Console(1, this, "Unable to select camera with key: '{0}'", key); + } + } + + public CameraBase FarEndCamera { get; private set; } + + public BoolFeedback ControllingFarEndCameraFeedback { get; private set; } + + #endregion + + #region IHasCodecSelfView Members + + public BoolFeedback SelfviewIsOnFeedback { get; private set; } + + public void SelfViewModeOn() + { + SendText("zConfiguration Video hide_conf_self_video: off"); + } + + public void SelfViewModeOff() + { + SendText("zConfiguration Video hide_conf_self_video: on"); + } + + public void SelfViewModeToggle() + { + if (SelfviewIsOnFeedback.BoolValue) + { + SelfViewModeOff(); + } + else + { + SelfViewModeOn(); + } + } + + #endregion + + #region IHasDirectoryHistoryStack Members + + public event EventHandler DirectoryResultReturned; + public CodecDirectory DirectoryRoot { get; private set; } + + public CodecDirectory CurrentDirectoryResult + { + get { return _currentDirectoryResult; } + } + + public CodecPhonebookSyncState PhonebookSyncState { get; private set; } + + public void SearchDirectory(string searchString) + { + var directoryResults = new CodecDirectory(); + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll( + c => c.Name.IndexOf(searchString, 0, StringComparison.OrdinalIgnoreCase) > -1)); + + DirectoryBrowseHistoryStack.Clear(); + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void GetDirectoryFolderContents(string folderId) + { + var directoryResults = new CodecDirectory {ResultsFolderId = folderId}; + + directoryResults.AddContactsToDirectory( + DirectoryRoot.CurrentDirectoryResults.FindAll(c => c.ParentFolderId.Equals(folderId))); + + DirectoryBrowseHistoryStack.Push(_currentDirectoryResult); + + _currentDirectoryResult = directoryResults; + + OnDirectoryResultReturned(directoryResults); + } + + public void SetCurrentDirectoryToRoot() + { + DirectoryBrowseHistoryStack.Clear(); + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(DirectoryRoot); + } + + public void GetDirectoryParentFolderContents() + { + if (DirectoryBrowseHistoryStack.Count == 0) + { + return; + } + + var currentDirectory = DirectoryBrowseHistoryStack.Pop(); + + _currentDirectoryResult = currentDirectory; + + OnDirectoryResultReturned(currentDirectory); + } + + public BoolFeedback CurrentDirectoryResultIsNotDirectoryRoot { get; private set; } + + public List DirectoryBrowseHistory { get; private set; } + + public Stack DirectoryBrowseHistoryStack { get; private set; } + + #endregion + + #region IHasScheduleAwareness Members + + public CodecScheduleAwareness CodecSchedule { get; private set; } + + public void GetSchedule() + { + GetBookings(); + } + + #endregion + + #region IRouting Members + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + ExecuteSwitch(inputSelector); + } + + #endregion + + private void SyncState_InitialSyncCompleted(object sender, EventArgs e) + { + SetUpRouting(); + + SetIsReady(); + } + + private void SetUpCallFeedbackActions() + { + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + } + + /// + /// Subscribes to the PropertyChanged events on the state objects and fires the corresponding feedbacks. + /// + private void SetUpFeedbackActions() + { + Configuration.Audio.Output.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Volume") + { + VolumeLevelFeedback.FireUpdate(); + MuteFeedback.FireUpdate(); + } + }; + + Configuration.Call.Microphone.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Mute") + { + PrivacyModeIsOnFeedback.FireUpdate(); + } + }; + + Configuration.Video.PropertyChanged += (o, a) => + { + if (a.PropertyName == "HideConfSelfVideo") + { + SelfviewIsOnFeedback.FireUpdate(); + } + }; + Configuration.Video.Camera.PropertyChanged += (o, a) => + { + if (a.PropertyName == "SelectedId") + { + SelectCamera(Configuration.Video.Camera.SelectedId); + // this will in turn fire the affected feedbacks + } + }; + + Configuration.Call.Camera.PropertyChanged += (o, a) => + { + Debug.Console(1, this, "Configuration.Call.Camera.PropertyChanged: {0}", a.PropertyName); + + if (a.PropertyName != "Mute") return; + + CameraIsOffFeedback.FireUpdate(); + CameraAutoModeIsOnFeedback.FireUpdate(); + }; + + Configuration.Call.Layout.PropertyChanged += (o, a) => + { + if (a.PropertyName != "Position") return; + + ComputeSelfviewPipStatus(); + + SelfviewPipPositionFeedback.FireUpdate(); + }; + + Status.Call.Sharing.PropertyChanged += (o, a) => + { + if (a.PropertyName == "State") + { + SharingContentIsOnFeedback.FireUpdate(); + ReceivingContent.FireUpdate(); + } + }; + + Status.Call.PropertyChanged += (o, a) => + { + if (a.PropertyName == "Info") + { + Debug.Console(1, this, "Updating Call Status"); + UpdateCallStatus(); + } + }; + + Status.Sharing.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "dispState": + SharingSourceFeedback.FireUpdate(); + break; + case "password": + break; + } + }; + + Status.PhoneCall.PropertyChanged += (o, a) => + { + switch (a.PropertyName) + { + case "IsIncomingCall": + Debug.Console(1, this, "Incoming Phone Call: {0}", Status.PhoneCall.IsIncomingCall); + break; + case "PeerDisplayName": + Debug.Console(1, this, "Peer Display Name: {0}", Status.PhoneCall.PeerDisplayName); + CallerIdNameFeedback.FireUpdate(); + break; + case "PeerNumber": + Debug.Console(1, this, "Peer Number: {0}", Status.PhoneCall.PeerNumber); + CallerIdNumberFeedback.FireUpdate(); + break; + case "OffHook": + Debug.Console(1, this, "Phone is OffHook: {0}", Status.PhoneCall.OffHook); + PhoneOffHookFeedback.FireUpdate(); + break; + } + }; + } + + private void SetUpDirectory() + { + DirectoryRoot = new CodecDirectory(); + + DirectoryBrowseHistory = new List(); + DirectoryBrowseHistoryStack = new Stack(); + + CurrentDirectoryResultIsNotDirectoryRoot = new BoolFeedback(() => _currentDirectoryResult != DirectoryRoot); + + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + } + + private void SetUpRouting() + { + // Set up input ports + CreateOsdSource(); + InputPorts.Add(CodecOsdIn); + + // Set up output ports + OutputPorts.Add(Output1); + } + + /// + /// Creates the fake OSD source, and connects it's AudioVideo output to the CodecOsdIn input + /// to enable routing + /// + private void CreateOsdSource() + { + OsdSource = new DummyRoutingInputsDevice(Key + "[osd]"); + DeviceManager.AddDevice(OsdSource); + var tl = new TieLine(OsdSource.AudioVideoOutputPort, CodecOsdIn); + TieLineCollection.Default.Add(tl); + + //foreach(var input in Status.Video. + } + + /// + /// Starts the HTTP feedback server and syncronizes state of codec + /// + /// + public override bool CustomActivate() + { + CrestronConsole.AddNewConsoleCommand(SetCommDebug, "SetCodecCommDebug", "0 for Off, 1 for on", + ConsoleAccessLevelEnum.AccessOperator); + if (!_props.DisablePhonebookAutoDownload) + { + CrestronConsole.AddNewConsoleCommand(s => SendText("zCommand Phonebook List Offset: 0 Limit: 512"), + "GetZoomRoomContacts", "Triggers a refresh of the codec phonebook", + ConsoleAccessLevelEnum.AccessOperator); + } + + CrestronConsole.AddNewConsoleCommand(s => GetBookings(), "GetZoomRoomBookings", + "Triggers a refresh of the booking data for today", ConsoleAccessLevelEnum.AccessOperator); + + var socket = Communication as ISocketStatus; + if (socket != null) + { + socket.ConnectionChange += socket_ConnectionChange; + } + + CommDebuggingIsOn = false; + + Communication.Connect(); + + CommunicationMonitor.Start(); + + return base.CustomActivate(); + } + + public void SetCommDebug(string s) + { + if (s == "1") + { + CommDebuggingIsOn = true; + Debug.Console(0, this, "Comm Debug Enabled."); + } + else + { + CommDebuggingIsOn = false; + Debug.Console(0, this, "Comm Debug Disabled."); + } + } + + private void socket_ConnectionChange(object sender, GenericSocketStatusChageEventArgs e) + { + Debug.Console(1, this, "Socket status change {0}", e.Client.ClientStatus); + if (e.Client.IsConnected) + { + } + else + { + _syncState.CodecDisconnected(); + PhonebookSyncState.CodecDisconnected(); + } + } + + public void SendText(string command) + { + if (CommDebuggingIsOn) + { + Debug.Console(1, this, "Sending: '{0}'", command); + } + + Communication.SendText(command + Delimiter); + } + + /// + /// Gathers responses and enqueues them. + /// + /// + /// + private void Port_LineReceived(object dev, GenericCommMethodReceiveTextArgs args) + { + //if (CommDebuggingIsOn) + // Debug.Console(1, this, "Gathered: '{0}'", args.Text); + + _receiveQueue.Enqueue(args.Text); + + // If the receive thread has for some reason stopped, this will restart it + if (_receiveThread.ThreadState != Thread.eThreadStates.ThreadRunning) + { + _receiveThread.Start(); + } + } + + + /// + /// Runs in it's own thread to dequeue messages in the order they were received to be processed + /// + /// + private object ProcessQueue() + { + try + { + while (true) + { + var message = _receiveQueue.Dequeue(); + + ProcessMessage(message); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error Processing Queue: {0}", e); + } + + return null; + } + + + /// + /// Queues the initial queries to be sent upon connection + /// + private void SetUpSyncQueries() + { + // zStatus + _syncState.AddQueryToQueue("zStatus Call Status"); + _syncState.AddQueryToQueue("zStatus Audio Input Line"); + _syncState.AddQueryToQueue("zStatus Audio Output Line"); + _syncState.AddQueryToQueue("zStatus Video Camera Line"); + _syncState.AddQueryToQueue("zStatus Video Optimizable"); + _syncState.AddQueryToQueue("zStatus Capabilities"); + _syncState.AddQueryToQueue("zStatus Sharing"); + _syncState.AddQueryToQueue("zStatus CameraShare"); + _syncState.AddQueryToQueue("zStatus Call Layout"); + _syncState.AddQueryToQueue("zStatus Call ClosedCaption Available"); + _syncState.AddQueryToQueue("zStatus NumberOfScreens"); + + // zConfiguration + + _syncState.AddQueryToQueue("zConfiguration Call Sharing optimize_video_sharing"); + _syncState.AddQueryToQueue("zConfiguration Call Microphone Mute"); + _syncState.AddQueryToQueue("zConfiguration Call Camera Mute"); + _syncState.AddQueryToQueue("zConfiguration Audio Input SelectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Input is_sap_disabled"); + _syncState.AddQueryToQueue("zConfiguration Audio Input reduce_reverb"); + _syncState.AddQueryToQueue("zConfiguration Audio Input volume"); + _syncState.AddQueryToQueue("zConfiguration Audio Output selectedId"); + _syncState.AddQueryToQueue("zConfiguration Audio Output volume"); + _syncState.AddQueryToQueue("zConfiguration Video hide_conf_self_video"); + _syncState.AddQueryToQueue("zConfiguration Video Camera selectedId"); + _syncState.AddQueryToQueue("zConfiguration Video Camera Mirror"); + _syncState.AddQueryToQueue("zConfiguration Client appVersion"); + _syncState.AddQueryToQueue("zConfiguration Client deviceSystem"); + _syncState.AddQueryToQueue("zConfiguration Call Layout ShareThumb"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Style"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Size"); + _syncState.AddQueryToQueue("zConfiguration Call Layout Position"); + _syncState.AddQueryToQueue("zConfiguration Call Lock Enable"); + _syncState.AddQueryToQueue("zConfiguration Call MuteUserOnEntry Enable"); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption FontSize "); + _syncState.AddQueryToQueue("zConfiguration Call ClosedCaption Visible"); + + // zCommand + + if (!_props.DisablePhonebookAutoDownload) + { + _syncState.AddQueryToQueue("zCommand Phonebook List Offset: 0 Limit: 512"); + } + + _syncState.AddQueryToQueue("zCommand Bookings List"); + _syncState.AddQueryToQueue("zCommand Call ListParticipants"); + _syncState.AddQueryToQueue("zCommand Call Info"); + + + _syncState.StartSync(); + } + + /// + /// Processes messages as they are dequeued + /// + /// + private void ProcessMessage(string message) + { + // Counts the curly braces + if (message.Contains("client_loop: send disconnect: Broken pipe")) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, + "Zoom Room Controller or App connected. Essentials will NOT control the Zoom Room until it is disconnected."); + + return; + } + + if (message.Contains('{')) + { + _jsonCurlyBraceCounter++; + } + + if (message.Contains('}')) + { + _jsonCurlyBraceCounter--; + } + + Debug.Console(2, this, "JSON Curly Brace Count: {0}", _jsonCurlyBraceCounter); + + if (!_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "{" + Delimiter) + // Check for the beginning of a new JSON message + { + _jsonFeedbackMessageIsIncoming = true; + _jsonCurlyBraceCounter = 1; // reset the counter for each new message + + _jsonMessage = new StringBuilder(); + + _jsonMessage.Append(message); + + if (CommDebuggingIsOn) + { + Debug.Console(2, this, "Incoming JSON message..."); + } + + return; + } + if (_jsonFeedbackMessageIsIncoming && message.Trim('\x20') == "}" + Delimiter) + // Check for the end of a JSON message + { + _jsonMessage.Append(message); + + if (_jsonCurlyBraceCounter == 0) + { + _jsonFeedbackMessageIsIncoming = false; + + if (CommDebuggingIsOn) + { + Debug.Console(2, this, "Complete JSON Received:\n{0}", _jsonMessage.ToString()); + } + + // Forward the complete message to be deserialized + DeserializeResponse(_jsonMessage.ToString()); + } + + //JsonMessage = new StringBuilder(); + return; + } + + // NOTE: This must happen after the above conditions have been checked + // Append subsequent partial JSON fragments to the string builder + if (_jsonFeedbackMessageIsIncoming) + { + _jsonMessage.Append(message); + + //Debug.Console(1, this, "Building JSON:\n{0}", JsonMessage.ToString()); + return; + } + + if (CommDebuggingIsOn) + { + Debug.Console(1, this, "Non-JSON response: '{0}'", message); + } + + _jsonCurlyBraceCounter = 0; // reset on non-JSON response + + if (!_syncState.InitialSyncComplete) + { + switch (message.Trim().ToLower()) // remove the whitespace + { + case "*r login successful": + { + _syncState.LoginMessageReceived(); + + // Fire up a thread to send the intial commands. + CrestronInvoke.BeginInvoke(o => + { + Thread.Sleep(100); + // disable echo of commands + SendText("echo off"); + Thread.Sleep(100); + // set feedback exclusions + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callin_country_list"); + Thread.Sleep(100); + SendText("zFeedback Register Op: ex Path: /Event/InfoResult/info/callout_country_list"); + Thread.Sleep(100); + + if (!_props.DisablePhonebookAutoDownload) + { + SendText("zFeedback Register Op: ex Path: /Event/Phonebook/AddedContact"); + } + // switch to json format + SendText("format json"); + }); + + break; + } + } + } + } + + /// + /// Deserializes a JSON formatted response + /// + /// + private void DeserializeResponse(string response) + { + try + { + var trimmedResponse = response.Trim(); + + if (trimmedResponse.Length <= 0) + { + return; + } + + var message = JObject.Parse(trimmedResponse); + + var eType = + (eZoomRoomResponseType) + Enum.Parse(typeof (eZoomRoomResponseType), message["type"].Value(), true); + + var topKey = message["topKey"].Value(); + + var responseObj = message[topKey]; + + Debug.Console(1, "{0} Response Received. topKey: '{1}'\n{2}", eType, topKey, responseObj.ToString()); + + switch (eType) + { + case eZoomRoomResponseType.zConfiguration: + { + switch (topKey.ToLower()) + { + case "call": + { JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Call); - Debug.Console(1, this, "[DeserializeResponse] sharing status 2: {0}", Status.Call.Sharing.State.ToString()); - - break; - } - case "audio": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); - - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); - - break; - } - case "client": - { - JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); - - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zCommand: - { - switch (topKey.ToLower()) - { - case "inforesult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); - break; - } - case "phonebooklistresult": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); - - if (!PhonebookSyncState.InitialSyncComplete) - { - PhonebookSyncState.InitialPhonebookFoldersReceived(); - PhonebookSyncState.PhonebookRootEntriesReceived(); - PhonebookSyncState.SetPhonebookHasFolders(false); - PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); - } - - var directoryResults = - zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); - - DirectoryRoot = directoryResults; - - _currentDirectoryResult = DirectoryRoot; - - OnDirectoryResultReturned(directoryResults); - - break; - } - case "listparticipantsresult": - { - Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); - - switch (responseObj.Type) - { - case JTokenType.Array: - Status.Call.Participants = - JsonConvert.DeserializeObject>( - responseObj.ToString()); - break; - case JTokenType.Object: - { - // this is a single participant event notification - - var participant = - JsonConvert.DeserializeObject( - responseObj.ToString()); - - if (participant != null) - { - switch (participant.Event) - { - case "ZRCUserChangedEventUserInfoUpdated": - case "ZRCUserChangedEventLeftMeeting": - { - var existingParticipant = - Status.Call.Participants.FirstOrDefault( - p => p.UserId.Equals(participant.UserId)); - - if (existingParticipant != null) - { - switch (participant.Event) - { - case "ZRCUserChangedEventLeftMeeting": - Status.Call.Participants.Remove(existingParticipant); - break; - case "ZRCUserChangedEventUserInfoUpdated": - JsonConvert.PopulateObject(responseObj.ToString(), - existingParticipant); - break; - } - } - } - break; - case "ZRCUserChangedEventJoinedMeeting": - Status.Call.Participants.Add(participant); - break; - } - } - } - break; - } - - var participants = - zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( - Status.Call.Participants); - - Participants.CurrentParticipants = participants; - - PrintCurrentCallParticipants(); - - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zEvent: - { - switch (topKey.ToLower()) - { - case "phonebook": - { - if (responseObj["Updated Contact"] != null) - { - var updatedContact = - JsonConvert.DeserializeObject( - responseObj["Updated Contact"].ToString()); - - var existingContact = - Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); - - if (existingContact != null) - { - // Update existing contact - JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), - existingContact); - } - } - else if (responseObj["Added Contact"] != null) - { - var jToken = responseObj["Updated Contact"]; - if (jToken != null) - { - var newContact = - JsonConvert.DeserializeObject( - jToken.ToString()); - - // Add a new contact - Status.Phonebook.Contacts.Add(newContact); - } - } - - break; - } - case "bookingslistresult": - { - if (!_syncState.InitialSyncComplete) - { - _syncState.LastQueryResponseReceived(); - } - - var codecBookings = JsonConvert.DeserializeObject>( - responseObj.ToString()); - - if (codecBookings != null && codecBookings.Count > 0) - { - CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult( - codecBookings, CodecSchedule.MeetingWarningMinutes); - } - - break; - } - case "bookings updated": - { - GetBookings(); - - break; - } - case "sharingstate": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); - - SetLayout(); - - break; - } - case "incomingcallindication": - { - var incomingCall = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var newCall = new CodecActiveCallItem - { - Direction = eCodecCallDirection.Incoming, - Status = eCodecCallStatus.Ringing, - Type = eCodecCallType.Unknown, - Name = incomingCall.callerName, - Id = incomingCall.callerJID - }; - - ActiveCalls.Add(newCall); - - OnCallStatusChange(newCall); - } - - break; - } - case "treatedincomingcallindication": - { - var incomingCall = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (incomingCall != null) - { - var existingCall = - ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); - - if (existingCall != null) - { - existingCall.Status = !incomingCall.accepted - ? eCodecCallStatus.Disconnected - : eCodecCallStatus.Connecting; - - OnCallStatusChange(existingCall); - } - - UpdateCallStatus(); - } - - break; - } - case "calldisconnect": - { - var disconnectEvent = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (disconnectEvent.Successful) - { - if (ActiveCalls.Count > 0) - { - var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); - - if (activeCall != null) - { - activeCall.Status = eCodecCallStatus.Disconnected; - - OnCallStatusChange(activeCall); - } - } - var emptyList = new List(); - Participants.CurrentParticipants = emptyList; - } - - UpdateCallStatus(); - break; - } - case "callconnecterror": - { - UpdateCallStatus(); - break; - } - case "videounmuterequest": - { - // TODO: notify room of a request to unmute video - break; - } - case "meetingneedspassword": - { - // TODO: notify user to enter a password - break; - } - case "needwaitforhost": - { - var needWait = - JsonConvert.DeserializeObject(responseObj.ToString()); - - if (needWait.Wait) - { - // TODO: notify user to wait for host - } - - break; - } - case "openvideofailforhoststop": - { - // TODO: notify user that host has disabled unmuting video - break; - } - case "updatedcallrecordinfo": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); - - break; - } - case "phonecallstatus": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); - break; - } - default: - { - break; - } - } - break; - } - case eZoomRoomResponseType.zStatus: - { - switch (topKey.ToLower()) - { - case "login": - { - _syncState.LoginMessageReceived(); - - if (!_syncState.InitialQueryMessagesWereSent) - { - SetUpSyncQueries(); - } - - JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); - - break; - } - case "systemunit": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); - - break; - } - case "call": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); - - UpdateCallStatus(); - - break; - } - case "capabilities": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); - break; - } - case "sharing": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); - - break; - } - case "numberofscreens": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); - break; - } - case "video": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); - break; - } - case "camerashare": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); - break; - } - case "layout": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); - break; - } - case "audio input line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); - break; - } - case "audio output line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); - break; - } - case "video camera line": - { - JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); - - if (!_syncState.CamerasHaveBeenSetUp) - { - SetUpCameras(); - } - - break; - } - default: - { - break; - } - } - - break; - } - default: - { - Debug.Console(1, "Unknown Response Type:"); - break; - } - } - } - catch (Exception ex) - { - Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); - } - } - - private void SetLayout() - { - if (!_props.AutoDefaultLayouts) return; - - if ( - (Status.Call.Sharing.State == zEvent.eSharingState.Receiving || - Status.Call.Sharing.State == zEvent.eSharingState.Sending)) - { - SendText(String.Format("zconfiguration call layout style: {0}", - _props.DefaultSharingLayout)); - } - else - { - SendText(String.Format("zconfiguration call layout style: {0}", - _props.DefaultCallLayout)); - } - } - - public void PrintCurrentCallParticipants() - { - if (Debug.Level <= 0) - { - return; - } - - Debug.Console(1, this, "****************************Call Participants***************************"); - foreach (var participant in Participants.CurrentParticipants) - { - Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name, - participant.AudioMuteFb, participant.IsHost); - } - Debug.Console(1, this, "************************************************************************"); - } - - /// - /// Retrieves bookings list - /// - private void GetBookings() - { - SendText("zCommand Bookings List"); - } - - - /// - /// Updates the current call status - /// - private void UpdateCallStatus() - { - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - - if (Status.Call != null) - { - var callStatus = Status.Call.Status; - Debug.Console(1, this, "[UpdateCallStatus] callStatus: {0}", callStatus.ToString()); - - // If not currently in a meeting, intialize the call object - if (callStatus != zStatus.eCallStatus.IN_MEETING || callStatus != zStatus.eCallStatus.CONNECTING_MEETING) - { - Status.Call = new zStatus.Call {Status = callStatus}; - - SetUpCallFeedbackActions(); - } - - if (ActiveCalls.Count == 0) - { - if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || - callStatus == zStatus.eCallStatus.IN_MEETING) - { - var newStatus = eCodecCallStatus.Unknown; - - switch (callStatus) - { - case zStatus.eCallStatus.CONNECTING_MEETING: - newStatus = eCodecCallStatus.Connecting; - break; - case zStatus.eCallStatus.IN_MEETING: - newStatus = eCodecCallStatus.Connected; - break; - } - - var newCall = new CodecActiveCallItem {Status = newStatus}; - - ActiveCalls.Add(newCall); - - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + + break; + } + case "audio": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Audio); + + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Video); + + break; + } + case "client": + { + JsonConvert.PopulateObject(responseObj.ToString(), Configuration.Client); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zCommand: + { + switch (topKey.ToLower()) + { + case "inforesult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Info); + break; + } + case "phonebooklistresult": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Phonebook); + + if (!PhonebookSyncState.InitialSyncComplete) + { + PhonebookSyncState.InitialPhonebookFoldersReceived(); + PhonebookSyncState.PhonebookRootEntriesReceived(); + PhonebookSyncState.SetPhonebookHasFolders(false); + PhonebookSyncState.SetNumberOfContacts(Status.Phonebook.Contacts.Count); + } + + var directoryResults = + zStatus.Phonebook.ConvertZoomContactsToGeneric(Status.Phonebook.Contacts); + + DirectoryRoot = directoryResults; + + _currentDirectoryResult = DirectoryRoot; + + OnDirectoryResultReturned(directoryResults); + + break; + } + case "listparticipantsresult": + { + Debug.Console(1, this, "JTokenType: {0}", responseObj.Type); + + switch (responseObj.Type) + { + case JTokenType.Array: + Status.Call.Participants = + JsonConvert.DeserializeObject>( + responseObj.ToString()); + break; + case JTokenType.Object: + { + // this is a single participant event notification + + var participant = + JsonConvert.DeserializeObject( + responseObj.ToString()); + + if (participant != null) + { + switch (participant.Event) + { + case "ZRCUserChangedEventUserInfoUpdated": + case "ZRCUserChangedEventLeftMeeting": + { + var existingParticipant = + Status.Call.Participants.FirstOrDefault( + p => p.UserId.Equals(participant.UserId)); + + if (existingParticipant != null) + { + switch (participant.Event) + { + case "ZRCUserChangedEventLeftMeeting": + Status.Call.Participants.Remove(existingParticipant); + break; + case "ZRCUserChangedEventUserInfoUpdated": + JsonConvert.PopulateObject(responseObj.ToString(), + existingParticipant); + break; + } + } + } + break; + case "ZRCUserChangedEventJoinedMeeting": + Status.Call.Participants.Add(participant); + break; + } + } + } + break; + } + + var participants = + zCommand.ListParticipant.GetGenericParticipantListFromParticipantsResult( + Status.Call.Participants); + + Participants.CurrentParticipants = participants; + + PrintCurrentCallParticipants(); + + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zEvent: + { + switch (topKey.ToLower()) + { + case "phonebook": + { + if (responseObj["Updated Contact"] != null) + { + var updatedContact = + JsonConvert.DeserializeObject( + responseObj["Updated Contact"].ToString()); + + var existingContact = + Status.Phonebook.Contacts.FirstOrDefault(c => c.Jid.Equals(updatedContact.Jid)); + + if (existingContact != null) + { + // Update existing contact + JsonConvert.PopulateObject(responseObj["Updated Contact"].ToString(), + existingContact); + } + } + else if (responseObj["Added Contact"] != null) + { + var jToken = responseObj["Updated Contact"]; + if (jToken != null) + { + var newContact = + JsonConvert.DeserializeObject( + jToken.ToString()); + + // Add a new contact + Status.Phonebook.Contacts.Add(newContact); + } + } + + break; + } + case "bookingslistresult": + { + if (!_syncState.InitialSyncComplete) + { + _syncState.LastQueryResponseReceived(); + } + + var codecBookings = JsonConvert.DeserializeObject>( + responseObj.ToString()); + + if (codecBookings != null && codecBookings.Count > 0) + { + CodecSchedule.Meetings = zCommand.GetGenericMeetingsFromBookingResult( + codecBookings, CodecSchedule.MeetingWarningMinutes); + } + + break; + } + case "bookings updated": + { + GetBookings(); + + break; + } + case "sharingstate": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.Sharing); + + SetLayout(); + + break; + } + case "incomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var newCall = new CodecActiveCallItem + { + Direction = eCodecCallDirection.Incoming, + Status = eCodecCallStatus.Ringing, + Type = eCodecCallType.Unknown, + Name = incomingCall.callerName, + Id = incomingCall.callerJID + }; + + ActiveCalls.Add(newCall); + + OnCallStatusChange(newCall); + } + + break; + } + case "treatedincomingcallindication": + { + var incomingCall = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (incomingCall != null) + { + var existingCall = + ActiveCalls.FirstOrDefault(c => c.Id.Equals(incomingCall.callerJID)); + + if (existingCall != null) + { + existingCall.Status = !incomingCall.accepted + ? eCodecCallStatus.Disconnected + : eCodecCallStatus.Connecting; + + OnCallStatusChange(existingCall); + } + + UpdateCallStatus(); + } + + break; + } + case "calldisconnect": + { + var disconnectEvent = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (disconnectEvent.Successful) + { + if (ActiveCalls.Count > 0) + { + var activeCall = ActiveCalls.FirstOrDefault(c => c.IsActiveCall); + + if (activeCall != null) + { + activeCall.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(activeCall); + } + } + var emptyList = new List(); + Participants.CurrentParticipants = emptyList; + } + + UpdateCallStatus(); + break; + } + case "callconnecterror": + { + UpdateCallStatus(); + break; + } + case "videounmuterequest": + { + // TODO: notify room of a request to unmute video + break; + } + case "meetingneedspassword": + { + // TODO: notify user to enter a password + break; + } + case "needwaitforhost": + { + var needWait = + JsonConvert.DeserializeObject(responseObj.ToString()); + + if (needWait.Wait) + { + // TODO: notify user to wait for host + } + + break; + } + case "openvideofailforhoststop": + { + // TODO: notify user that host has disabled unmuting video + break; + } + case "updatedcallrecordinfo": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call.CallRecordInfo); + + break; + } + case "phonecallstatus": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.PhoneCall); + break; + } + default: + { + break; + } + } + break; + } + case eZoomRoomResponseType.zStatus: + { + switch (topKey.ToLower()) + { + case "login": + { + _syncState.LoginMessageReceived(); + + if (!_syncState.InitialQueryMessagesWereSent) + { + SetUpSyncQueries(); + } + + JsonConvert.PopulateObject(responseObj.ToString(), Status.Login); + + break; + } + case "systemunit": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.SystemUnit); + + break; + } + case "call": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Call); + + UpdateCallStatus(); + + break; + } + case "capabilities": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Capabilities); + break; + } + case "sharing": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Sharing); + + break; + } + case "numberofscreens": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.NumberOfScreens); + break; + } + case "video": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Video); + break; + } + case "camerashare": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.CameraShare); + break; + } + case "layout": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Layout); + break; + } + case "audio input line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioInputs); + break; + } + case "audio output line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.AudioOuputs); + break; + } + case "video camera line": + { + JsonConvert.PopulateObject(responseObj.ToString(), Status.Cameras); + + if (!_syncState.CamerasHaveBeenSetUp) + { + SetUpCameras(); + } + + break; + } + default: + { + break; + } + } + + break; + } + default: + { + Debug.Console(1, "Unknown Response Type:"); + break; + } + } + } + catch (Exception ex) + { + Debug.Console(1, this, "Error Deserializing feedback: {0}", ex); + } + } + + private void SetLayout() + { + if (!_props.AutoDefaultLayouts) return; + + if ( + (Status.Call.Sharing.State == zEvent.eSharingState.Receiving || + Status.Call.Sharing.State == zEvent.eSharingState.Sending)) + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultSharingLayout)); + } + else + { + SendText(String.Format("zconfiguration call layout style: {0}", + _props.DefaultCallLayout)); + } + } + + public void PrintCurrentCallParticipants() + { + if (Debug.Level <= 0) + { + return; + } + + Debug.Console(1, this, "****************************Call Participants***************************"); + foreach (var participant in Participants.CurrentParticipants) + { + Debug.Console(1, this, "Name: {0} Audio: {1} IsHost: {2}", participant.Name, + participant.AudioMuteFb, participant.IsHost); + } + Debug.Console(1, this, "************************************************************************"); + } + + /// + /// Retrieves bookings list + /// + private void GetBookings() + { + SendText("zCommand Bookings List"); + } + + + /// + /// Updates the current call status + /// + private void UpdateCallStatus() + { + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + if (Status.Call != null) + { + var callStatus = Status.Call.Status; + + // If not currently in a meeting, intialize the call object + if (callStatus != zStatus.eCallStatus.IN_MEETING && callStatus != zStatus.eCallStatus.CONNECTING_MEETING) + { + Debug.Console(1, this, "Creating new Status.Call object"); + Status.Call = new zStatus.Call {Status = callStatus}; + + SetUpCallFeedbackActions(); + } + + if (ActiveCalls.Count == 0) + { + if (callStatus == zStatus.eCallStatus.CONNECTING_MEETING || + callStatus == zStatus.eCallStatus.IN_MEETING) + { + var newStatus = eCodecCallStatus.Unknown; + + switch (callStatus) + { + case zStatus.eCallStatus.CONNECTING_MEETING: + newStatus = eCodecCallStatus.Connecting; + break; + case zStatus.eCallStatus.IN_MEETING: + newStatus = eCodecCallStatus.Connected; + break; + } + + var newCall = new CodecActiveCallItem {Status = newStatus}; + + ActiveCalls.Add(newCall); + + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - - OnCallStatusChange(newCall); - } - } - else - { - var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); - - switch (callStatus) - { - case zStatus.eCallStatus.IN_MEETING: - existingCall.Status = eCodecCallStatus.Connected; - break; - case zStatus.eCallStatus.NOT_IN_MEETING: - existingCall.Status = eCodecCallStatus.Disconnected; - break; - } - - Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", + + OnCallStatusChange(newCall); + } + } + else + { + var existingCall = ActiveCalls.FirstOrDefault(c => !c.Status.Equals(eCodecCallStatus.Ringing)); + + switch (callStatus) + { + case zStatus.eCallStatus.IN_MEETING: + existingCall.Status = eCodecCallStatus.Connected; + break; + case zStatus.eCallStatus.NOT_IN_MEETING: + existingCall.Status = eCodecCallStatus.Disconnected; + break; + } + + Debug.Console(1, this, "[UpdateCallStatus] Current Call Status: {0}", Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - - OnCallStatusChange(existingCall); - } - } - - Debug.Console(1, this, "****************************Active Calls*********************************"); - - // Clean up any disconnected calls left in the list - for (int i = 0; i < ActiveCalls.Count; i++) - { - var call = ActiveCalls[i]; - - Debug.Console(1, this, - @"Name: {0} - ID: {1} - IsActive: {2} - Status: {3} - Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); - - if (!call.IsActiveCall) - { - Debug.Console(1, this, "******Removing Inactive Call: {0}******", call.Name); - ActiveCalls.Remove(call); - } - } - Debug.Console(1, this, "**************************************************************************"); - - //clear participants list after call cleanup - if (ActiveCalls.Count == 0) - { - Participants.CurrentParticipants = new List(); - } - } - - protected override void OnCallStatusChange(CodecActiveCallItem item) - { - base.OnCallStatusChange(item); - - Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", - Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); - - if (_props.AutoDefaultLayouts) - { - SetLayout(); - } - } - - public override void StartSharing() - { - SendText("zCommand Call Sharing HDMI Start"); - } - - /// - /// Stops sharing the current presentation - /// - public override void StopSharing() - { - SendText("zCommand Call Sharing Disconnect"); - } - - public override void PrivacyModeOn() - { - SendText("zConfiguration Call Microphone Mute: on"); - } - - public override void PrivacyModeOff() - { - SendText("zConfiguration Call Microphone Mute: off"); - } - - public override void PrivacyModeToggle() - { - if (PrivacyModeIsOnFeedback.BoolValue) - { - PrivacyModeOff(); - } - else - { - PrivacyModeOn(); - } - } - - public override void MuteOff() - { - SetVolume((ushort) _previousVolumeLevel); - } - - public override void MuteOn() - { - _previousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall - - SetVolume(0); - } - - public override void MuteToggle() - { - if (MuteFeedback.BoolValue) - { - MuteOff(); - } - else - { - MuteOn(); - } - } - - - /// - /// Increments the voluem - /// - /// - public override void VolumeUp(bool pressRelease) - { - // TODO: Implment volume decrement that calls SetVolume() - } - - /// - /// Decrements the volume - /// - /// - public override void VolumeDown(bool pressRelease) - { - // TODO: Implment volume decrement that calls SetVolume() - } - - /// - /// Scales the level and sets the codec to the specified level within its range - /// - /// level from slider (0-65535 range) - public override void SetVolume(ushort level) - { - var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); - SendText(string.Format("zConfiguration Audio Output volume: {0}", scaledLevel)); - } - - /// - /// Recalls the default volume on the codec - /// - public void VolumeSetToDefault() - { - } - - /// - /// - /// - public override void StandbyActivate() - { - // No corresponding function on device - } - - /// - /// - /// - public override void StandbyDeactivate() - { - // No corresponding function on device - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); - } - - public override void ExecuteSwitch(object selector) - { - var action = selector as Action; - if (action == null) - { - return; - } - - action(); - } - - public void AcceptCall() - { - var incomingCall = - ActiveCalls.FirstOrDefault( - c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - - AcceptCall(incomingCall); - } - - public override void AcceptCall(CodecActiveCallItem call) - { - SendText(string.Format("zCommand Call Accept callerJID: {0}", call.Id)); - - call.Status = eCodecCallStatus.Connected; - - OnCallStatusChange(call); - - UpdateCallStatus(); - } - - public void RejectCall() - { - var incomingCall = - ActiveCalls.FirstOrDefault( - c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); - - RejectCall(incomingCall); - } - - public override void RejectCall(CodecActiveCallItem call) - { - SendText(string.Format("zCommand Call Reject callerJID: {0}", call.Id)); - - call.Status = eCodecCallStatus.Disconnected; - - OnCallStatusChange(call); - - UpdateCallStatus(); - } - - public override void Dial(Meeting meeting) - { - SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); - } - - public override void Dial(string number) - { - SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); - } - - /// - /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. - /// Currently only invites a single user - /// - /// - public override void Dial(IInvitableContact contact) - { - var ic = contact as zStatus.ZoomDirectoryContact; - - if (ic != null) - { - Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); - - if (!IsInCall) - { - SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, - ic.ContactId)); - } - else - { - SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); - } - } - } - - public override void EndCall(CodecActiveCallItem call) - { - SendText("zCommand Call Disconnect"); - } - - public override void EndAllCalls() - { - SendText("zCommand Call Disconnect"); - } - - public override void SendDtmf(string s) - { - SendDtmfToPhone(s); - } - - /// - /// Call when directory results are updated - /// - /// - private void OnDirectoryResultReturned(CodecDirectory result) - { - CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); - - // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology - var handler = DirectoryResultReturned; - if (handler != null) - { - handler(this, new DirectoryEventArgs - { - Directory = result, - DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue - }); - } - - //PrintDirectory(result); - } - - /// - /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data - /// - private void SetUpCameras() - { - SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); - - ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); - - foreach (var cam in Status.Cameras) - { - var camera = new ZoomRoomCamera(cam.id, cam.Name, this); - - Cameras.Add(camera); - - if (cam.Selected) - { - SelectedCamera = camera; - } - } - - if (IsInCall) - { - UpdateFarEndCameras(); - } - - _syncState.CamerasSetUp(); - } - - /// - /// Dynamically creates far end cameras for call participants who have far end control enabled. - /// - private void UpdateFarEndCameras() - { - // TODO: set up far end cameras for the current call - } - - #region Implementation of IHasParticipants - - public CodecParticipants Participants { get; private set; } - - #endregion - - #region Implementation of IHasCameraOff - - public BoolFeedback CameraIsOffFeedback { get; private set; } - - public void CameraOff() - { - SendText("zConfiguration Call Camera Mute: On"); - } - - #endregion - - #region Implementation of IHasCameraAutoMode - - //Zoom doesn't support camera auto modes. Setting this to just unmute video - public void CameraAutoModeOn() - { - throw new NotImplementedException("Zoom Room Doesn't support camera auto mode"); - } - - //Zoom doesn't support camera auto modes. Setting this to just unmute video - public void CameraAutoModeOff() - { - SendText("zConfiguration Call Camera Mute: Off"); - } - - public void CameraAutoModeToggle() - { - throw new NotImplementedException("Zoom Room doesn't support camera auto mode"); - } - - public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } - - #endregion - - #region Implementation of IHasFarEndContentStatus - - public BoolFeedback ReceivingContent { get; private set; } - - #endregion - - #region Implementation of IHasSelfviewPosition - - private CodecCommandWithLabel _currentSelfviewPipPosition; - - public StringFeedback SelfviewPipPositionFeedback { get; private set; } - - public void SelfviewPipPositionSet(CodecCommandWithLabel position) - { - SendText(String.Format("zConfiguration Call Layout Position: {0}", position.Command)); - } - - public void SelfviewPipPositionToggle() - { - if (_currentSelfviewPipPosition != null) - { - var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1; - - if (nextPipPositionIndex >= SelfviewPipPositions.Count) - // Check if we need to loop back to the first item in the list - nextPipPositionIndex = 0; - - SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); - } - } - - public List SelfviewPipPositions = new List() - { - new CodecCommandWithLabel("UpLeft", "Center Left"), - new CodecCommandWithLabel("UpRight", "Center Right"), - new CodecCommandWithLabel("DownRight", "Lower Right"), - new CodecCommandWithLabel("DownLeft", "Lower Left") - }; - - private void ComputeSelfviewPipStatus() - { - _currentSelfviewPipPosition = - SelfviewPipPositions.FirstOrDefault( - p => p.Command.ToLower().Equals(Configuration.Call.Layout.Position.ToString().ToLower())); - } - - #endregion - - #region Implementation of IHasPhoneDialing - - private Func PhoneOffHookFeedbackFunc {get {return () => Status.PhoneCall.OffHook; }} - private Func CallerIdNameFeedbackFunc { get { return () => Status.PhoneCall.PeerDisplayName; } } - private Func CallerIdNumberFeedbackFunc { get { return () => Status.PhoneCall.PeerNumber; } } - - public BoolFeedback PhoneOffHookFeedback { get; private set; } - public StringFeedback CallerIdNameFeedback { get; private set; } - public StringFeedback CallerIdNumberFeedback { get; private set; } - - public void DialPhoneCall(string number) - { - SendText(String.Format("zCommand Dial PhoneCallOut Number: {0}", number)); - } - - public void EndPhoneCall() - { - SendText(String.Format("zCommand Dial PhoneHangUp CallId: {0}", Status.PhoneCall.CallId)); - } - - public void SendDtmfToPhone(string digit) - { - SendText(String.Format("zCommand SendSipDTMF CallId: {0} Key: {1}", Status.PhoneCall.CallId, digit)); - } - - #endregion - } - - /// - /// Zoom Room specific info object - /// - public class ZoomRoomInfo : VideoCodecInfo - { - public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) - { - Status = status; - Configuration = configuration; - } - - public ZoomRoomStatus Status { get; private set; } - public ZoomRoomConfiguration Configuration { get; private set; } - - public override bool AutoAnswerEnabled - { - get { return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; } - } - - public override string E164Alias - { - get - { - if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) - { - return Status.SystemUnit.MeetingNumber; - } - return string.Empty; - } - } - - public override string H323Id - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) - { - return Status.Call.Info.meeting_list_item.third_party.h323_address; - } - return string.Empty; - } - } - - public override string IpAddress - { - get - { - if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) - { - return Status.SystemUnit.RoomInfo.AccountEmail; - } - return string.Empty; - } - } - - public override bool MultiSiteOptionIsEnabled - { - get { return true; } - } - - public override string SipPhoneNumber - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) - { - return Status.Call.Info.dialIn; - } - return string.Empty; - } - } - - public override string SipUri - { - get - { - if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) - { - return Status.Call.Info.meeting_list_item.third_party.sip_address; - } - return string.Empty; - } - } - } - - /// - /// Tracks the initial sycnronization state when establishing a new connection - /// - public class ZoomRoomSyncState : IKeyed - { - private readonly ZoomRoom _parent; - private readonly CrestronQueue _syncQueries; - private bool _initialSyncComplete; - - public ZoomRoomSyncState(string key, ZoomRoom parent) - { - _parent = parent; - Key = key; - _syncQueries = new CrestronQueue(50); - CodecDisconnected(); - } - - public bool InitialSyncComplete - { - get { return _initialSyncComplete; } - private set - { - if (value) - { - var handler = InitialSyncCompleted; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - _initialSyncComplete = value; - } - } - - public bool LoginMessageWasReceived { get; private set; } - - public bool InitialQueryMessagesWereSent { get; private set; } - - public bool LastQueryResponseWasReceived { get; private set; } - - public bool CamerasHaveBeenSetUp { get; private set; } - - #region IKeyed Members - - public string Key { get; private set; } - - #endregion - - public event EventHandler InitialSyncCompleted; - - public void StartSync() - { - DequeueQueries(); - } - - private void DequeueQueries() - { - while (!_syncQueries.IsEmpty) - { - var query = _syncQueries.Dequeue(); - - _parent.SendText(query); - } - - InitialQueryMessagesSent(); - } - - public void AddQueryToQueue(string query) - { - _syncQueries.Enqueue(query); - } - - public void LoginMessageReceived() - { - LoginMessageWasReceived = true; - Debug.Console(1, this, "Login Message Received."); - CheckSyncStatus(); - } - - public void InitialQueryMessagesSent() - { - InitialQueryMessagesWereSent = true; - Debug.Console(1, this, "Query Messages Sent."); - CheckSyncStatus(); - } - - public void LastQueryResponseReceived() - { - LastQueryResponseWasReceived = true; - Debug.Console(1, this, "Last Query Response Received."); - CheckSyncStatus(); - } - - public void CamerasSetUp() - { - CamerasHaveBeenSetUp = true; - Debug.Console(1, this, "Cameras Set Up."); - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - _syncQueries.Clear(); - LoginMessageWasReceived = false; - InitialQueryMessagesWereSent = false; - LastQueryResponseWasReceived = false; - CamerasHaveBeenSetUp = false; - InitialSyncComplete = false; - } - - private void CheckSyncStatus() - { - if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && - CamerasHaveBeenSetUp) - { - InitialSyncComplete = true; - Debug.Console(1, this, "Initial Codec Sync Complete!"); - } - else - { - InitialSyncComplete = false; - } - } - } - - public class ZoomRoomFactory : EssentialsDeviceFactory - { - public ZoomRoomFactory() - { - TypeNames = new List {"zoomroom"}; - } - - public override EssentialsDevice BuildDevice(DeviceConfig dc) - { - Debug.Console(1, "Factory Attempting to create new ZoomRoom Device"); - var comm = CommFactory.CreateCommForDevice(dc); - return new ZoomRoom(dc, comm); - } - } + + OnCallStatusChange(existingCall); + } + } + + Debug.Console(1, this, "****************************Active Calls*********************************"); + + // Clean up any disconnected calls left in the list + for (int i = 0; i < ActiveCalls.Count; i++) + { + var call = ActiveCalls[i]; + + Debug.Console(1, this, + @"Name: {0} + ID: {1} + IsActive: {2} + Status: {3} + Direction: {4}", call.Name, call.Id, call.IsActiveCall, call.Status, call.Direction); + + if (!call.IsActiveCall) + { + Debug.Console(1, this, "******Removing Inactive Call: {0}******", call.Name); + ActiveCalls.Remove(call); + } + } + Debug.Console(1, this, "**************************************************************************"); + + //clear participants list after call cleanup + if (ActiveCalls.Count == 0) + { + Participants.CurrentParticipants = new List(); + } + } + + protected override void OnCallStatusChange(CodecActiveCallItem item) + { + base.OnCallStatusChange(item); + + Debug.Console(1, this, "[OnCallStatusChange] Current Call Status: {0}", + Status.Call != null ? Status.Call.Sharing.State.ToString() : "no call"); + + if (_props.AutoDefaultLayouts) + { + SetLayout(); + } + } + + public override void StartSharing() + { + SendText("zCommand Call Sharing HDMI Start"); + } + + /// + /// Stops sharing the current presentation + /// + public override void StopSharing() + { + SendText("zCommand Call Sharing Disconnect"); + } + + public override void PrivacyModeOn() + { + SendText("zConfiguration Call Microphone Mute: on"); + } + + public override void PrivacyModeOff() + { + SendText("zConfiguration Call Microphone Mute: off"); + } + + public override void PrivacyModeToggle() + { + if (PrivacyModeIsOnFeedback.BoolValue) + { + PrivacyModeOff(); + } + else + { + PrivacyModeOn(); + } + } + + public override void MuteOff() + { + SetVolume((ushort) _previousVolumeLevel); + } + + public override void MuteOn() + { + _previousVolumeLevel = Configuration.Audio.Output.Volume; // Store the previous level for recall + + SetVolume(0); + } + + public override void MuteToggle() + { + if (MuteFeedback.BoolValue) + { + MuteOff(); + } + else + { + MuteOn(); + } + } + + + /// + /// Increments the voluem + /// + /// + public override void VolumeUp(bool pressRelease) + { + // TODO: Implment volume decrement that calls SetVolume() + } + + /// + /// Decrements the volume + /// + /// + public override void VolumeDown(bool pressRelease) + { + // TODO: Implment volume decrement that calls SetVolume() + } + + /// + /// Scales the level and sets the codec to the specified level within its range + /// + /// level from slider (0-65535 range) + public override void SetVolume(ushort level) + { + var scaledLevel = CrestronEnvironment.ScaleWithLimits(level, 65535, 0, 100, 0); + SendText(string.Format("zConfiguration Audio Output volume: {0}", scaledLevel)); + } + + /// + /// Recalls the default volume on the codec + /// + public void VolumeSetToDefault() + { + } + + /// + /// + /// + public override void StandbyActivate() + { + // No corresponding function on device + } + + /// + /// + /// + public override void StandbyDeactivate() + { + // No corresponding function on device + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + LinkVideoCodecToApi(this, trilist, joinStart, joinMapKey, bridge); + } + + public override void ExecuteSwitch(object selector) + { + var action = selector as Action; + if (action == null) + { + return; + } + + action(); + } + + public void AcceptCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + AcceptCall(incomingCall); + } + + public override void AcceptCall(CodecActiveCallItem call) + { + SendText(string.Format("zCommand Call Accept callerJID: {0}", call.Id)); + + call.Status = eCodecCallStatus.Connected; + + OnCallStatusChange(call); + + UpdateCallStatus(); + } + + public void RejectCall() + { + var incomingCall = + ActiveCalls.FirstOrDefault( + c => c.Status.Equals(eCodecCallStatus.Ringing) && c.Direction.Equals(eCodecCallDirection.Incoming)); + + RejectCall(incomingCall); + } + + public override void RejectCall(CodecActiveCallItem call) + { + SendText(string.Format("zCommand Call Reject callerJID: {0}", call.Id)); + + call.Status = eCodecCallStatus.Disconnected; + + OnCallStatusChange(call); + + UpdateCallStatus(); + } + + public override void Dial(Meeting meeting) + { + SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); + } + + public override void Dial(string number) + { + SendText(string.Format("zCommand Dial Join meetingNumber: {0}", number)); + } + + /// + /// Invites a contact to either a new meeting (if not already in a meeting) or the current meeting. + /// Currently only invites a single user + /// + /// + public override void Dial(IInvitableContact contact) + { + var ic = contact as zStatus.ZoomDirectoryContact; + + if (ic != null) + { + Debug.Console(1, this, "Attempting to Dial (Invite): {0}", ic.Name); + + if (!IsInCall) + { + SendText(string.Format("zCommand Invite Duration: {0} user: {1}", DefaultMeetingDurationMin, + ic.ContactId)); + } + else + { + SendText(string.Format("zCommand Call invite user: {0}", ic.ContactId)); + } + } + } + + public override void EndCall(CodecActiveCallItem call) + { + SendText("zCommand Call Disconnect"); + } + + public override void EndAllCalls() + { + SendText("zCommand Call Disconnect"); + } + + public override void SendDtmf(string s) + { + SendDtmfToPhone(s); + } + + /// + /// Call when directory results are updated + /// + /// + private void OnDirectoryResultReturned(CodecDirectory result) + { + CurrentDirectoryResultIsNotDirectoryRoot.FireUpdate(); + + // This will return the latest results to all UIs. Multiple indendent UI Directory browsing will require a different methodology + var handler = DirectoryResultReturned; + if (handler != null) + { + handler(this, new DirectoryEventArgs + { + Directory = result, + DirectoryIsOnRoot = !CurrentDirectoryResultIsNotDirectoryRoot.BoolValue + }); + } + + //PrintDirectory(result); + } + + /// + /// Builds the cameras List by using the Zoom Room zStatus.Cameras data. Could later be modified to build from config data + /// + private void SetUpCameras() + { + SelectedCameraFeedback = new StringFeedback(() => Configuration.Video.Camera.SelectedId); + + ControllingFarEndCameraFeedback = new BoolFeedback(() => SelectedCamera is IAmFarEndCamera); + + foreach (var cam in Status.Cameras) + { + var camera = new ZoomRoomCamera(cam.id, cam.Name, this); + + Cameras.Add(camera); + + if (cam.Selected) + { + SelectedCamera = camera; + } + } + + if (IsInCall) + { + UpdateFarEndCameras(); + } + + _syncState.CamerasSetUp(); + } + + /// + /// Dynamically creates far end cameras for call participants who have far end control enabled. + /// + private void UpdateFarEndCameras() + { + // TODO: set up far end cameras for the current call + } + + #region Implementation of IHasParticipants + + public CodecParticipants Participants { get; private set; } + + #endregion + + #region Implementation of IHasCameraOff + + public BoolFeedback CameraIsOffFeedback { get; private set; } + + public void CameraOff() + { + SendText("zConfiguration Call Camera Mute: On"); + } + + #endregion + + #region Implementation of IHasCameraAutoMode + + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOn() + { + throw new NotImplementedException("Zoom Room Doesn't support camera auto mode"); + } + + //Zoom doesn't support camera auto modes. Setting this to just unmute video + public void CameraAutoModeOff() + { + SendText("zConfiguration Call Camera Mute: Off"); + } + + public void CameraAutoModeToggle() + { + throw new NotImplementedException("Zoom Room doesn't support camera auto mode"); + } + + public BoolFeedback CameraAutoModeIsOnFeedback { get; private set; } + + #endregion + + #region Implementation of IHasFarEndContentStatus + + public BoolFeedback ReceivingContent { get; private set; } + + #endregion + + #region Implementation of IHasSelfviewPosition + + private CodecCommandWithLabel _currentSelfviewPipPosition; + + public StringFeedback SelfviewPipPositionFeedback { get; private set; } + + public void SelfviewPipPositionSet(CodecCommandWithLabel position) + { + SendText(String.Format("zConfiguration Call Layout Position: {0}", position.Command)); + } + + public void SelfviewPipPositionToggle() + { + if (_currentSelfviewPipPosition != null) + { + var nextPipPositionIndex = SelfviewPipPositions.IndexOf(_currentSelfviewPipPosition) + 1; + + if (nextPipPositionIndex >= SelfviewPipPositions.Count) + // Check if we need to loop back to the first item in the list + nextPipPositionIndex = 0; + + SelfviewPipPositionSet(SelfviewPipPositions[nextPipPositionIndex]); + } + } + + public List SelfviewPipPositions = new List() + { + new CodecCommandWithLabel("UpLeft", "Center Left"), + new CodecCommandWithLabel("UpRight", "Center Right"), + new CodecCommandWithLabel("DownRight", "Lower Right"), + new CodecCommandWithLabel("DownLeft", "Lower Left") + }; + + private void ComputeSelfviewPipStatus() + { + _currentSelfviewPipPosition = + SelfviewPipPositions.FirstOrDefault( + p => p.Command.ToLower().Equals(Configuration.Call.Layout.Position.ToString().ToLower())); + } + + #endregion + + #region Implementation of IHasPhoneDialing + + private Func PhoneOffHookFeedbackFunc {get {return () => Status.PhoneCall.OffHook; }} + private Func CallerIdNameFeedbackFunc { get { return () => Status.PhoneCall.PeerDisplayName; } } + private Func CallerIdNumberFeedbackFunc { get { return () => Status.PhoneCall.PeerNumber; } } + + public BoolFeedback PhoneOffHookFeedback { get; private set; } + public StringFeedback CallerIdNameFeedback { get; private set; } + public StringFeedback CallerIdNumberFeedback { get; private set; } + + public void DialPhoneCall(string number) + { + SendText(String.Format("zCommand Dial PhoneCallOut Number: {0}", number)); + } + + public void EndPhoneCall() + { + SendText(String.Format("zCommand Dial PhoneHangUp CallId: {0}", Status.PhoneCall.CallId)); + } + + public void SendDtmfToPhone(string digit) + { + SendText(String.Format("zCommand SendSipDTMF CallId: {0} Key: {1}", Status.PhoneCall.CallId, digit)); + } + + #endregion + } + + /// + /// Zoom Room specific info object + /// + public class ZoomRoomInfo : VideoCodecInfo + { + public ZoomRoomInfo(ZoomRoomStatus status, ZoomRoomConfiguration configuration) + { + Status = status; + Configuration = configuration; + } + + public ZoomRoomStatus Status { get; private set; } + public ZoomRoomConfiguration Configuration { get; private set; } + + public override bool AutoAnswerEnabled + { + get { return Status.SystemUnit.RoomInfo.AutoAnswerIsEnabled; } + } + + public override string E164Alias + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.MeetingNumber)) + { + return Status.SystemUnit.MeetingNumber; + } + return string.Empty; + } + } + + public override string H323Id + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.h323_address)) + { + return Status.Call.Info.meeting_list_item.third_party.h323_address; + } + return string.Empty; + } + } + + public override string IpAddress + { + get + { + if (!string.IsNullOrEmpty(Status.SystemUnit.RoomInfo.AccountEmail)) + { + return Status.SystemUnit.RoomInfo.AccountEmail; + } + return string.Empty; + } + } + + public override bool MultiSiteOptionIsEnabled + { + get { return true; } + } + + public override string SipPhoneNumber + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.dialIn)) + { + return Status.Call.Info.dialIn; + } + return string.Empty; + } + } + + public override string SipUri + { + get + { + if (!string.IsNullOrEmpty(Status.Call.Info.meeting_list_item.third_party.sip_address)) + { + return Status.Call.Info.meeting_list_item.third_party.sip_address; + } + return string.Empty; + } + } + } + + /// + /// Tracks the initial sycnronization state when establishing a new connection + /// + public class ZoomRoomSyncState : IKeyed + { + private readonly ZoomRoom _parent; + private readonly CrestronQueue _syncQueries; + private bool _initialSyncComplete; + + public ZoomRoomSyncState(string key, ZoomRoom parent) + { + _parent = parent; + Key = key; + _syncQueries = new CrestronQueue(50); + CodecDisconnected(); + } + + public bool InitialSyncComplete + { + get { return _initialSyncComplete; } + private set + { + if (value) + { + var handler = InitialSyncCompleted; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + _initialSyncComplete = value; + } + } + + public bool LoginMessageWasReceived { get; private set; } + + public bool InitialQueryMessagesWereSent { get; private set; } + + public bool LastQueryResponseWasReceived { get; private set; } + + public bool CamerasHaveBeenSetUp { get; private set; } + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; + + public void StartSync() + { + DequeueQueries(); + } + + private void DequeueQueries() + { + while (!_syncQueries.IsEmpty) + { + var query = _syncQueries.Dequeue(); + + _parent.SendText(query); + } + + InitialQueryMessagesSent(); + } + + public void AddQueryToQueue(string query) + { + _syncQueries.Enqueue(query); + } + + public void LoginMessageReceived() + { + LoginMessageWasReceived = true; + Debug.Console(1, this, "Login Message Received."); + CheckSyncStatus(); + } + + public void InitialQueryMessagesSent() + { + InitialQueryMessagesWereSent = true; + Debug.Console(1, this, "Query Messages Sent."); + CheckSyncStatus(); + } + + public void LastQueryResponseReceived() + { + LastQueryResponseWasReceived = true; + Debug.Console(1, this, "Last Query Response Received."); + CheckSyncStatus(); + } + + public void CamerasSetUp() + { + CamerasHaveBeenSetUp = true; + Debug.Console(1, this, "Cameras Set Up."); + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + _syncQueries.Clear(); + LoginMessageWasReceived = false; + InitialQueryMessagesWereSent = false; + LastQueryResponseWasReceived = false; + CamerasHaveBeenSetUp = false; + InitialSyncComplete = false; + } + + private void CheckSyncStatus() + { + if (LoginMessageWasReceived && InitialQueryMessagesWereSent && LastQueryResponseWasReceived && + CamerasHaveBeenSetUp) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Codec Sync Complete!"); + } + else + { + InitialSyncComplete = false; + } + } + } + + public class ZoomRoomFactory : EssentialsDeviceFactory + { + public ZoomRoomFactory() + { + TypeNames = new List {"zoomroom"}; + } + + public override EssentialsDevice BuildDevice(DeviceConfig dc) + { + Debug.Console(1, "Factory Attempting to create new ZoomRoom Device"); + var comm = CommFactory.CreateCommForDevice(dc); + return new ZoomRoom(dc, comm); + } + } } \ No newline at end of file From e22c71853f7ebc6f1ddc4e405b47058f6fdcba31 Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Wed, 2 Dec 2020 13:10:47 -0500 Subject: [PATCH 05/89] Starts adding support for starting and stopping dm stream cards via API bridge --- .../JoinMaps/DmChassisControllerJoinMap.cs | 12 + .../JoinMaps/DmChassisControllerJoinMap.cs | 8 + .../Chassis/DmChassisController.cs | 220 +++++++++++++++++- 3 files changed, 231 insertions(+), 9 deletions(-) diff --git a/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index 2e1b8394..ba00306d 100644 --- a/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/PepperDashEssentials/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -70,6 +70,14 @@ namespace PepperDash.Essentials.Bridges /// Range reports the highest supported HDCP state level for the corresponding input card /// public uint HdcpSupportCapability { get; set; } + /// + /// DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback + /// + public uint InputStreamCardStatus { get; set; } + /// + /// DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback + /// + public uint OutputStreamCardStatus { get; set; } #endregion #region Serials @@ -115,6 +123,8 @@ namespace PepperDash.Essentials.Bridges InputUsb = 700; //701-899 HdcpSupportState = 1000; //1001-1199 HdcpSupportCapability = 1200; //1201-1399 + InputStreamCardStatus = 1500; //1501-1532 + OutputStreamCardStatus = 1600; //1601-1632 //Serial @@ -145,6 +155,8 @@ namespace PepperDash.Essentials.Bridges OutputEndpointOnline = OutputEndpointOnline + joinOffset; HdcpSupportState = HdcpSupportState + joinOffset; HdcpSupportCapability = HdcpSupportCapability + joinOffset; + InputStreamCardStatus = InputStreamCardStatus + joinOffset; + OutputStreamCardStatus = OutputStreamCardStatus + joinOffset; OutputDisabledByHdcp = OutputDisabledByHdcp + joinOffset; TxAdvancedIsPresent = TxAdvancedIsPresent + joinOffset; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs index 54909d02..ee04bd45 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/DmChassisControllerJoinMap.cs @@ -76,6 +76,14 @@ namespace PepperDash.Essentials.Core.Bridges public JoinDataComplete HdcpSupportCapability = new JoinDataComplete(new JoinData { JoinNumber = 1201, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Input HDCP Support Capability", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + [JoinName("InputStreamCardState")] + public JoinDataComplete InputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1501, JoinSpan = 32 }, + new JoinMetadata { Description = "DM Chassis Stream Input Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + + [JoinName("OutputStreamCardState")] + public JoinDataComplete OutputStreamCardState = new JoinDataComplete(new JoinData { JoinNumber = 1601, JoinSpan = 32 }, + new JoinMetadata { Description = "DM Chassis Stream Output Start (1), Stop (2), Pause (3) with Feedback", JoinCapabilities = eJoinCapabilities.FromSIMPL, JoinType = eJoinType.Analog }); + [JoinName("InputNames")] public JoinDataComplete InputNames = new JoinDataComplete(new JoinData { JoinNumber = 101, JoinSpan = 32 }, new JoinMetadata { Description = "DM Chassis Input Name", JoinCapabilities = eJoinCapabilities.ToSIMPL, JoinType = eJoinType.Serial }); diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index 09c7988a..db4e517f 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -49,7 +49,9 @@ namespace PepperDash.Essentials.DM public BoolFeedback EnableAudioBreakawayFeedback { get; private set; } public BoolFeedback EnableUsbBreakawayFeedback { get; private set; } - public Dictionary InputCardHdcpStateFeedbacks { get; private set; } + public Dictionary InputCardHdcpStateFeedbacks { get; private set; } + public Dictionary InputStreamCardStateFeedbacks { get; private set; } + public Dictionary OutputStreamCardStateFeedbacks { get; private set; } public Dictionary InputCardHdcpCapabilityTypes { get; private set; } @@ -223,7 +225,9 @@ namespace PepperDash.Essentials.DM EnableUsbBreakawayFeedback = new BoolFeedback(() => (Chassis as DmMDMnxn).EnableUSBBreakawayFeedback.BoolValue); - InputCardHdcpStateFeedbacks = new Dictionary(); + InputCardHdcpStateFeedbacks = new Dictionary(); + InputStreamCardStateFeedbacks = new Dictionary(); + OutputStreamCardStateFeedbacks = new Dictionary(); InputCardHdcpCapabilityTypes = new Dictionary(); for (uint x = 1; x <= Chassis.NumberOfOutputs; x++) @@ -307,6 +311,33 @@ namespace PepperDash.Essentials.DM // return hdMdNxMHdmiOutput.HdmiOutputPort.DisabledByHdcpFeedback.BoolValue; return false; + }); + OutputStreamCardStateFeedbacks[tempX] = new IntFeedback(() => + { + try + { + var outputCard = Chassis.Outputs[tempX]; + + if (outputCard.Card is DmcStroAV) + { + Debug.Console(0, "Found output stream card in slot: {0}.", tempX); + var streamCard = outputCard.Card as DmcStroAV; + if (streamCard.Control.StartFeedback.BoolValue == true) + return 1; + else if (streamCard.Control.StopFeedback.BoolValue == true) + return 2; + else if (streamCard.Control.PauseFeedback.BoolValue == true) + return 3; + else + return 0; + } + return 0; + } + catch (InvalidOperationException iopex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Error adding output stream card in slot: {0}. Error: {1}", tempX, iopex); + return 0; + } }); } @@ -406,6 +437,33 @@ namespace PepperDash.Essentials.DM Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "The Input Card in slot: {0} supports HDCP 2. Please update the configuration value in the inputCardSupportsHdcp2 object to true. Error: {1}", tempX, iopex); return 0; } + }); + InputStreamCardStateFeedbacks[tempX] = new IntFeedback(() => + { + try + { + var inputCard = Chassis.Inputs[tempX]; + + if (inputCard.Card is DmcStr) + { + Debug.Console(0, "Found input stream card in slot: {0}.", tempX); + var streamCard = inputCard.Card as DmcStr; + if (streamCard.Control.StartFeedback.BoolValue == true) + return 1; + else if (streamCard.Control.StopFeedback.BoolValue == true) + return 2; + else if (streamCard.Control.PauseFeedback.BoolValue == true) + return 3; + else + return 0; + } + return 0; + } + catch (InvalidOperationException iopex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Error adding input stream card in slot: {0}. Error: {1}", tempX, iopex); + return 0; + } }); } } @@ -915,6 +973,42 @@ namespace PepperDash.Essentials.DM else Debug.Console(1, this, "No index of {0} found in InputCardHdcpCapabilityFeedbacks"); break; + } + case DMInputEventIds.StartEventId: + case DMInputEventIds.StopEventId: + case DMInputEventIds.PauseEventId: + { + Debug.Console(2, this, "DM Input {0} Stream Status EventId", args.Number); + if (InputStreamCardStateFeedbacks[args.Number] != null) + { + var streamCard = Chassis.Inputs[args.Number].Card as DmcStr; + InputStreamCardStateFeedbacks[args.Number] = new IntFeedback(() => { + if (streamCard.Control.StartFeedback.BoolValue == true) + { + Debug.Console(1, this, "Found start feedback"); + return 1; + } + else if (streamCard.Control.StopFeedback.BoolValue == true) + { + Debug.Console(1, this, "Found stop feedback"); + return 2; + } + else if (streamCard.Control.PauseFeedback.BoolValue == true) + { + Debug.Console(1, this, "Found pause feedback"); + return 3; + } + else + { + Debug.Console(1, this, "Found no feedback"); + return 0; + } + }); + InputStreamCardStateFeedbacks[args.Number].FireUpdate(); + } + else + Debug.Console(1, this, "No index of {0} found in InputStreamCardStateFeedbacks"); + break; } default: { @@ -1043,6 +1137,31 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "DM Output {0} DisabledByHdcpEventId", args.Number); OutputDisabledByHdcpFeedbacks[args.Number].FireUpdate(); break; + } + case DMOutputEventIds.StartEventId: + case DMOutputEventIds.StopEventId: + case DMOutputEventIds.PauseEventId: + { + Debug.Console(2, this, "DM Output {0} Stream Status EventId", args.Number); + if (OutputStreamCardStateFeedbacks[args.Number] != null) + { + var streamCard = Chassis.Outputs[args.Number].Card as DmcStroAV; + OutputStreamCardStateFeedbacks[args.Number] = new IntFeedback(() => + { + if (streamCard.Control.StartFeedback.BoolValue == true) + return 1; + else if (streamCard.Control.StopFeedback.BoolValue == true) + return 2; + else if (streamCard.Control.PauseFeedback.BoolValue == true) + return 3; + else + return 0; + }); + OutputStreamCardStateFeedbacks[args.Number].FireUpdate(); + } + else + Debug.Console(1, this, "No index of {0} found in OutputStreamCardStateFeedbacks"); + break; } default: { @@ -1242,13 +1361,16 @@ namespace PepperDash.Essentials.DM } else { - LinkHdmiInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); - } - - if (RxDictionary.ContainsKey(ioSlot)) - { - LinkRxToApi(trilist, ioSlot, joinMap, ioSlotJoin); - } + LinkHdmiInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); + LinkStreamInputToApi(trilist, ioSlot, joinMap, ioSlotJoin); + } + + if (RxDictionary.ContainsKey(ioSlot)) + { + LinkRxToApi(trilist, ioSlot, joinMap, ioSlotJoin); + } + else + LinkStreamOutputToApi(trilist, ioSlot, joinMap, ioSlotJoin); } } @@ -1295,6 +1417,86 @@ namespace PepperDash.Essentials.DM { trilist.UShortInput[joinMap.HdcpSupportCapability.JoinNumber + ioSlotJoin].UShortValue = 1; } + } + + private void LinkStreamInputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) + { + var inputPort = InputPorts[string.Format("inputCard{0}--streamIn", ioSlot)]; + if (inputPort == null) + { + return; + } + var streamCard = Chassis.Inputs[ioSlot].Card as DmcStr; + var join = joinMap.InputStreamCardState.JoinNumber + ioSlotJoin; + + Debug.Console(1, "Port value for input card {0} is set as a stream card", ioSlot); + + trilist.SetUShortSigAction(join, s => + { + if (s == 1) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to start", join, s); + streamCard.Control.Start(); + } + else if (s == 2) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to stop", join, s); + streamCard.Control.Stop(); + } + else if (s == 3) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to pause", join, s); + streamCard.Control.Pause(); + } + else + { + Debug.Console(2, this, "Join {0} value {1}: Ignore stream state", join, s); + } + }); + + InputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); + + trilist.UShortInput[join].UShortValue = InputStreamCardStateFeedbacks[ioSlot].UShortValue; + } + + private void LinkStreamOutputToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) + { + var outputPort = OutputPorts[string.Format("outputCard{0}--streamOut", ioSlot)]; + if (outputPort == null) + { + return; + } + var streamCard = Chassis.Outputs[ioSlot].Card as DmcStroAV; + var join = joinMap.OutputStreamCardState.JoinNumber + ioSlotJoin; + + Debug.Console(1, "Port value for output card {0} is set as a stream card", ioSlot); + + trilist.SetUShortSigAction(join, s => + { + if (s == 1) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to start", join, s); + streamCard.Control.Start(); + } + else if (s == 2) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to stop", join, s); + streamCard.Control.Stop(); + } + else if (s == 3) + { + Debug.Console(2, this, "Join {0} value {1}: Setting stream state to pause", join, s); + streamCard.Control.Pause(); + } + else + { + Debug.Console(2, this, "Join {0} value {1}: Ignore stream state", join, s); + } + }); + + OutputStreamCardStateFeedbacks[ioSlot].LinkInputSig(trilist.UShortInput[join]); + + trilist.UShortInput[join].UShortValue = OutputStreamCardStateFeedbacks[ioSlot].UShortValue; } private void LinkRxToApi(BasicTriList trilist, uint ioSlot, DmChassisControllerJoinMap joinMap, uint ioSlotJoin) From ab5dd5f7569eb6a4464df1ce587bd89577ae79ba Mon Sep 17 00:00:00 2001 From: Alex Johnson Date: Wed, 2 Dec 2020 13:51:44 -0500 Subject: [PATCH 06/89] Fixes streaming card feedback and removes excess debug --- .../Chassis/DmChassisController.cs | 43 ++----------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs index db4e517f..3c5567c4 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Chassis/DmChassisController.cs @@ -320,7 +320,7 @@ namespace PepperDash.Essentials.DM if (outputCard.Card is DmcStroAV) { - Debug.Console(0, "Found output stream card in slot: {0}.", tempX); + Debug.Console(2, "Found output stream card in slot: {0}.", tempX); var streamCard = outputCard.Card as DmcStroAV; if (streamCard.Control.StartFeedback.BoolValue == true) return 1; @@ -446,7 +446,7 @@ namespace PepperDash.Essentials.DM if (inputCard.Card is DmcStr) { - Debug.Console(0, "Found input stream card in slot: {0}.", tempX); + Debug.Console(2, "Found input stream card in slot: {0}.", tempX); var streamCard = inputCard.Card as DmcStr; if (streamCard.Control.StartFeedback.BoolValue == true) return 1; @@ -981,33 +981,10 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "DM Input {0} Stream Status EventId", args.Number); if (InputStreamCardStateFeedbacks[args.Number] != null) { - var streamCard = Chassis.Inputs[args.Number].Card as DmcStr; - InputStreamCardStateFeedbacks[args.Number] = new IntFeedback(() => { - if (streamCard.Control.StartFeedback.BoolValue == true) - { - Debug.Console(1, this, "Found start feedback"); - return 1; - } - else if (streamCard.Control.StopFeedback.BoolValue == true) - { - Debug.Console(1, this, "Found stop feedback"); - return 2; - } - else if (streamCard.Control.PauseFeedback.BoolValue == true) - { - Debug.Console(1, this, "Found pause feedback"); - return 3; - } - else - { - Debug.Console(1, this, "Found no feedback"); - return 0; - } - }); InputStreamCardStateFeedbacks[args.Number].FireUpdate(); } else - Debug.Console(1, this, "No index of {0} found in InputStreamCardStateFeedbacks"); + Debug.Console(2, this, "No index of {0} found in InputStreamCardStateFeedbacks"); break; } default: @@ -1145,22 +1122,10 @@ namespace PepperDash.Essentials.DM Debug.Console(2, this, "DM Output {0} Stream Status EventId", args.Number); if (OutputStreamCardStateFeedbacks[args.Number] != null) { - var streamCard = Chassis.Outputs[args.Number].Card as DmcStroAV; - OutputStreamCardStateFeedbacks[args.Number] = new IntFeedback(() => - { - if (streamCard.Control.StartFeedback.BoolValue == true) - return 1; - else if (streamCard.Control.StopFeedback.BoolValue == true) - return 2; - else if (streamCard.Control.PauseFeedback.BoolValue == true) - return 3; - else - return 0; - }); OutputStreamCardStateFeedbacks[args.Number].FireUpdate(); } else - Debug.Console(1, this, "No index of {0} found in OutputStreamCardStateFeedbacks"); + Debug.Console(2, this, "No index of {0} found in OutputStreamCardStateFeedbacks"); break; } default: From f9925f9ec9d9c9de3666a59a55419ec8e0a67872 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 3 Dec 2020 16:46:10 -0700 Subject: [PATCH 07/89] Add PowerIsOnFeedback back to DisplayBase and marked it as Obsolete --- .../PepperDashEssentialsBase/Display/BasicIrDisplay.cs | 3 ++- .../PepperDashEssentialsBase/Display/DisplayBase.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs index 8d70bd55..3c691c19 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/BasicIrDisplay.cs @@ -20,7 +20,8 @@ namespace PepperDash.Essentials.Core public IrOutputPortController IrPort { get; private set; } public ushort IrPulseTime { get; set; } - public BoolFeedback PowerIsOnFeedback { get; private set; } + [Obsolete("This property will be removed in version 2.0.0")] + public override BoolFeedback PowerIsOnFeedback { get; protected set; } protected Func PowerIsOnFeedbackFunc { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs index e4a2cb26..4e461c1c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs @@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Core /// /// /// - public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking + public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking, IPower { public event SourceInfoChangeHandler CurrentSourceChange; @@ -49,6 +49,9 @@ namespace PepperDash.Essentials.Core public BoolFeedback IsCoolingDownFeedback { get; protected set; } public BoolFeedback IsWarmingUpFeedback { get; private set; } + [Obsolete("This property will be removed in version 2.0.0")] + public abstract BoolFeedback PowerIsOnFeedback { get; protected set; } + public UsageTracking UsageTracker { get; set; } public uint WarmupTime { get; set; } @@ -261,7 +264,8 @@ namespace PepperDash.Essentials.Core abstract protected Func CurrentInputFeedbackFunc { get; } - public BoolFeedback PowerIsOnFeedback { get; protected set; } + public override BoolFeedback PowerIsOnFeedback { get; protected set; } + abstract protected Func PowerIsOnFeedbackFunc { get; } From 0e4edca08a31c558428599ef51e93eda4f6407ee Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 11:05:46 -0700 Subject: [PATCH 08/89] update plugin dependency check message --- .../PepperDashEssentialsBase/Plugins/PluginLoader.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index f7275a66..3e8cbc38 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -402,13 +402,16 @@ namespace PepperDash.Essentials /// Loads a /// /// + /// static void LoadCustomPlugin(IPluginDeviceFactory plugin, LoadedAssembly loadedAssembly) { var passed = Global.IsRunningMinimumVersionOrHigher(plugin.MinimumEssentialsFrameworkVersion); if (!passed) { - Debug.Console(0, Debug.ErrorLogLevel.Error, "Plugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin", plugin.MinimumEssentialsFrameworkVersion); + Debug.Console(0, Debug.ErrorLogLevel.Error, + "**********/r/nPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}/r/n**********", + plugin.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); return; } else From 93a5f2e3b2831f7ca1c65eaafc69e2e8a30a8271 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 12:18:08 -0700 Subject: [PATCH 09/89] fix slases --- .../PepperDashEssentialsBase/Plugins/PluginLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index 3e8cbc38..f0a7aeeb 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -410,7 +410,7 @@ namespace PepperDash.Essentials if (!passed) { Debug.Console(0, Debug.ErrorLogLevel.Error, - "**********/r/nPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}/r/n**********", + "**********\r\nPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}\r\n**********", plugin.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); return; } From 008a052045003b2667063fba0a6e1973c763bb45 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 1 Dec 2020 16:20:59 -0700 Subject: [PATCH 10/89] getting started with EssentialsTechRoom --- .../Room/Config/EssentialsRoomConfig.cs | 35 +- .../Room/Config/EssentialsTechRoomConfig.cs | 20 ++ .../Room/Types/EssentialsTechRoom.cs | 94 ++++++ .../Presets/DevicePresets.cs | 305 ++++++++++-------- 4 files changed, 291 insertions(+), 163 deletions(-) create mode 100644 PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs create mode 100644 PepperDashEssentials/Room/Types/EssentialsTechRoom.cs diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index dfaa333d..3aee1e20 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -22,30 +22,25 @@ namespace PepperDash.Essentials.Room.Config public static Device GetRoomObject(DeviceConfig roomConfig) { var typeName = roomConfig.Type.ToLower(); + if (typeName == "huddle") { - var huddle = new EssentialsHuddleSpaceRoom(roomConfig); - - return huddle; + return new EssentialsHuddleSpaceRoom(roomConfig); } - else if (typeName == "huddlevtc1") - { - var rm = new EssentialsHuddleVtc1Room(roomConfig); - - return rm; - } - else if (typeName == "ddvc01Bridge") - { - return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing. - } - else if (typeName == "dualdisplay") - { - var rm = new EssentialsDualDisplayRoom(roomConfig); + if (typeName == "huddlevtc1") + { + return new EssentialsHuddleVtc1Room(roomConfig); + } + if (typeName == "ddvc01Bridge") + { + return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing. + } + if (typeName == "dualdisplay") + { + return new EssentialsDualDisplayRoom(roomConfig); + } - return rm; - } - - return null; + return typeName != "techRoom" ? null : new EssentialsTechRoom(roomConfig); } /// diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs new file mode 100644 index 00000000..8f35461b --- /dev/null +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsTechRoomConfig + { + public List Displays; + public List Tuners; + public string ScheduleProviderKey; + public string UserPin; + public string TechPin; + public string PresetsFileName; + + public EssentialsTechRoomConfig() + { + Displays = new List(); + Tuners = new List(); + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs new file mode 100644 index 00000000..ba87df5d --- /dev/null +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Presets; +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Room.Config; + +namespace PepperDash.Essentials +{ + public class EssentialsTechRoom:EssentialsRoomBase + { + private Dictionary _tuners; + private Dictionary _displays; + + private DevicePresetsModel _tunerPresets; + + private readonly EssentialsTechRoomConfig _config; + + + public EssentialsTechRoom(DeviceConfig config) : base(config) + { + _config = config.Properties.ToObject(); + + _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); + + _tuners = GetDevices(_config.Tuners); + _displays = GetDevices(_config.Displays); + } + + private Dictionary GetDevices(ICollection config) where T:IKeyed + { + try + { + var returnValue = DeviceManager.AllDevices.OfType() + .Where(d => config.Contains(d.Key)) + .ToDictionary(d => d.Key, d => d); + + return returnValue; + } + catch + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error getting devices. Check Essentials Configuration"); + return null; + } + } + + #region Overrides of EssentialsRoomBase + + protected override Func IsWarmingFeedbackFunc + { + get { throw new NotImplementedException(); } + } + + protected override Func IsCoolingFeedbackFunc + { + get { throw new NotImplementedException(); } + } + + protected override Func OnFeedbackFunc + { + get { throw new NotImplementedException(); } + } + + protected override void EndShutdown() + { + throw new NotImplementedException(); + } + + public override void SetDefaultLevels() + { + throw new NotImplementedException(); + } + + public override void PowerOnToDefaultOrLastSource() + { + throw new NotImplementedException(); + } + + public override bool RunDefaultPresentRoute() + { + throw new NotImplementedException(); + } + + public override void RoomVacatedForTimeoutPeriod(object o) + { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 4f79626f..f0378691 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -1,178 +1,197 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; - using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - using PepperDash.Core; //using SSMono.IO; namespace PepperDash.Essentials.Core.Presets { - /// - /// Class that represents the model behind presets display - /// - public class DevicePresetsModel : Device - { - public event EventHandler PresetsLoaded; + /// + /// Class that represents the model behind presets display + /// + public class DevicePresetsModel : Device + { + private readonly bool _initSuccess; - public int PulseTime { get; set; } - public int DigitSpacingMS { get; set; } - public bool PresetsAreLoaded { get; private set; } + /// + /// The methods on the STB device to call when dialing + /// + private Dictionary> _dialFunctions; - public List PresetsList { get { return _PresetsList.ToList(); } } - List _PresetsList; - public int Count { get { return PresetsList != null ? PresetsList.Count : 0; } } + private bool _dialIsRunning; + private Action _enterFunction; + private string _filePath; - public bool UseLocalImageStorage { get; set; } - public string ImagesLocalHostPrefix { get; set; } - public string ImagesPathPrefix { get; set; } - public string ListPathPrefix { get; set; } + public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName) + : this(key, fileName) + { + try + { + // Grab the digit functions from the device + // If any fail, the whole thing fails peacefully + _dialFunctions = new Dictionary>(10) + { + {'1', setTopBox.Digit1}, + {'2', setTopBox.Digit2}, + {'3', setTopBox.Digit3}, + {'4', setTopBox.Digit4}, + {'5', setTopBox.Digit5}, + {'6', setTopBox.Digit6}, + {'7', setTopBox.Digit7}, + {'8', setTopBox.Digit8}, + {'9', setTopBox.Digit9}, + {'0', setTopBox.Digit0}, + {'-', setTopBox.Dash} + }; + } + catch + { + Debug.Console(0, "DevicePresets '{0}', not attached to INumericKeypad device. Ignoring", key); + _dialFunctions = null; + return; + } - /// - /// The methods on the STB device to call when dialing - /// - Dictionary> DialFunctions; - Action EnterFunction; + _enterFunction = setTopBox.KeypadEnter; + } - bool DialIsRunning; - string FilePath; - bool InitSuccess; - //SSMono.IO.FileSystemWatcher ListWatcher; + public DevicePresetsModel(string key, string fileName) : base(key) + { + PulseTime = 150; + DigitSpacingMs = 150; - public DevicePresetsModel(string key, ISetTopBoxNumericKeypad setTopBox, string fileName) - : base(key) - { - PulseTime = 150; - DigitSpacingMS = 150; + UseLocalImageStorage = true; - try - { - // Grab the digit functions from the device - // If any fail, the whole thing fails peacefully - DialFunctions = new Dictionary>(10) - { - { '1', setTopBox.Digit1 }, - { '2', setTopBox.Digit2 }, - { '3', setTopBox.Digit3 }, - { '4', setTopBox.Digit4 }, - { '5', setTopBox.Digit5 }, - { '6', setTopBox.Digit6 }, - { '7', setTopBox.Digit7 }, - { '8', setTopBox.Digit8 }, - { '9', setTopBox.Digit9 }, - { '0', setTopBox.Digit0 }, - { '-', setTopBox.Dash } - }; - } - catch - { - Debug.Console(0, "DevicePresets '{0}', not attached to INumericKeypad device. Ignoring", key); - DialFunctions = null; - return; - } + ImagesLocalHostPrefix = "http://" + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + ImagesPathPrefix = @"/presets/images.zip/"; + ListPathPrefix = @"/html/presets/lists/"; - EnterFunction = setTopBox.KeypadEnter; + SetFileName(fileName); - UseLocalImageStorage = true; + _initSuccess = true; + } - ImagesLocalHostPrefix = "http://" + CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS,0); - ImagesPathPrefix = @"/presets/images.zip/"; - ListPathPrefix = @"/html/presets/lists/"; + public int PulseTime { get; set; } + public int DigitSpacingMs { get; set; } + public bool PresetsAreLoaded { get; private set; } - SetFileName(fileName); + public List PresetsList { get; private set; } - //ListWatcher = new FileSystemWatcher(@"\HTML\presets\lists"); - //ListWatcher.NotifyFilter = NotifyFilters.LastWrite; - //ListWatcher.EnableRaisingEvents = true; - //ListWatcher.Changed += ListWatcher_Changed; - InitSuccess = true; - } + public int Count + { + get { return PresetsList != null ? PresetsList.Count : 0; } + } + + public bool UseLocalImageStorage { get; set; } + public string ImagesLocalHostPrefix { get; set; } + public string ImagesPathPrefix { get; set; } + public string ListPathPrefix { get; set; } + public event EventHandler PresetsLoaded; - public void SetFileName(string path) - { - FilePath = ListPathPrefix + path; - LoadChannels(); - } + public void SetFileName(string path) + { + _filePath = ListPathPrefix + path; + LoadChannels(); + } - public void LoadChannels() - { - PresetsAreLoaded = false; - try - { - var pl = JsonConvert.DeserializeObject(Crestron.SimplSharp.CrestronIO.File.ReadToEnd(FilePath, Encoding.ASCII)); - Name = pl.Name; - _PresetsList = pl.Channels; - } - catch (Exception e) - { - Debug.Console(2, this, "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", FilePath, e.Message); - // Just save a default empty list - _PresetsList = new List(); - } - PresetsAreLoaded = true; + public void LoadChannels() + { + PresetsAreLoaded = false; + try + { + var pl = JsonConvert.DeserializeObject(File.ReadToEnd(_filePath, Encoding.ASCII)); + Name = pl.Name; + PresetsList = pl.Channels; + } + catch (Exception e) + { + Debug.Console(2, this, + "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", + _filePath, e.Message); + // Just save a default empty list + PresetsList = new List(); + } + PresetsAreLoaded = true; - var handler = PresetsLoaded; - if (handler != null) - handler(this, EventArgs.Empty); - } + var handler = PresetsLoaded; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } - public void Dial(int presetNum) - { - if (presetNum <= _PresetsList.Count) - Dial(_PresetsList[presetNum - 1].Channel); - } + public void Dial(int presetNum) + { + if (presetNum <= PresetsList.Count) + { + Dial(PresetsList[presetNum - 1].Channel); + } + } - public void Dial(string chanNum) - { - if (DialIsRunning || !InitSuccess) return; - if (DialFunctions == null) - { - Debug.Console(1, "DevicePresets '{0}', not attached to keypad device. Ignoring channel", Key); - return; - } + public void Dial(string chanNum) + { + if (_dialIsRunning || !_initSuccess) + { + return; + } + if (_dialFunctions == null) + { + Debug.Console(1, "DevicePresets '{0}', not attached to keypad device. Ignoring channel", Key); + return; + } - DialIsRunning = true; - CrestronInvoke.BeginInvoke(o => - { - foreach (var c in chanNum.ToCharArray()) - { - if (DialFunctions.ContainsKey(c)) - Pulse(DialFunctions[c]); - CrestronEnvironment.Sleep(DigitSpacingMS); - } + _dialIsRunning = true; + CrestronInvoke.BeginInvoke(o => + { + foreach (var c in chanNum.ToCharArray()) + { + if (_dialFunctions.ContainsKey(c)) + { + Pulse(_dialFunctions[c]); + } + CrestronEnvironment.Sleep(DigitSpacingMs); + } - if (EnterFunction != null) - Pulse(EnterFunction); - DialIsRunning = false; - }); - } + if (_enterFunction != null) + { + Pulse(_enterFunction); + } + _dialIsRunning = false; + }); + } - void Pulse(Action act) - { - act(true); - CrestronEnvironment.Sleep(PulseTime); - act(false); - } + public void Dial(string chanNum, ISetTopBoxNumericKeypad setTopBox) + { + _dialFunctions = new Dictionary>(10) + { + {'1', setTopBox.Digit1}, + {'2', setTopBox.Digit2}, + {'3', setTopBox.Digit3}, + {'4', setTopBox.Digit4}, + {'5', setTopBox.Digit5}, + {'6', setTopBox.Digit6}, + {'7', setTopBox.Digit7}, + {'8', setTopBox.Digit8}, + {'9', setTopBox.Digit9}, + {'0', setTopBox.Digit0}, + {'-', setTopBox.Dash} + }; - /// - /// Event handler for filesystem watcher. When directory changes, this is called - /// - //void ListWatcher_Changed(object sender, FileSystemEventArgs e) - //{ - // Debug.Console(1, this, "folder modified: {0}", e.FullPath); - // if (e.FullPath.Equals(FilePath, StringComparison.OrdinalIgnoreCase)) - // { - // Debug.Console(1, this, "File changed: {0}", e.ChangeType); - // LoadChannels(); - // } - //} - } + _enterFunction = setTopBox.KeypadEnter; + + Dial(chanNum); + } + + private void Pulse(Action act) + { + act(true); + CrestronEnvironment.Sleep(PulseTime); + act(false); + } + } } \ No newline at end of file From 7a8c1f31656e035d75c3449bef88d3530e82a98d Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 2 Dec 2020 13:37:53 -0700 Subject: [PATCH 11/89] adding some overloads --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 2 ++ .../PepperDashEssentialsBase/Presets/DevicePresets.cs | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index ba87df5d..079a2ff6 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -26,6 +26,8 @@ namespace PepperDash.Essentials _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); + _tunerPresets.LoadChannels(); + _tuners = GetDevices(_config.Tuners); _displays = GetDevices(_config.Displays); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index f0378691..43fdf59a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -165,6 +165,14 @@ namespace PepperDash.Essentials.Core.Presets }); } + public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox) + { + if (presetNum <= PresetsList.Count) + { + Dial(PresetsList[presetNum - 1].Channel, setTopBox); + } + } + public void Dial(string chanNum, ISetTopBoxNumericKeypad setTopBox) { _dialFunctions = new Dictionary>(10) From 9171610e343ab072bc553d00b5049081f78e2e50 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 3 Dec 2020 13:26:55 -0700 Subject: [PATCH 12/89] getting files back in the project --- PepperDashEssentials/PepperDashEssentials.csproj | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index 42d0e288..969c1f40 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -141,11 +141,13 @@ + + From 9888fbf047b6a2672895de256d2c13b892754553 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 13:24:00 -0700 Subject: [PATCH 13/89] added save presets method & expose some properties --- .../Room/Types/EssentialsTechRoom.cs | 60 +++++++++++++++++-- .../Presets/DevicePresets.cs | 22 +++++++ 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 079a2ff6..0680f46b 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -12,13 +12,37 @@ namespace PepperDash.Essentials { public class EssentialsTechRoom:EssentialsRoomBase { - private Dictionary _tuners; - private Dictionary _displays; + private readonly Dictionary _tuners; + private readonly Dictionary _displays; - private DevicePresetsModel _tunerPresets; + private readonly DevicePresetsModel _tunerPresets; private readonly EssentialsTechRoomConfig _config; + public DevicePresetsModel TunerPresets + { + get + { + return _tunerPresets; + } + } + + public Dictionary Tuners + { + get { return _tuners; } + } + + public Dictionary Displays + { + get { return _displays; } + } + + public BoolFeedback RoomPowerIsOnFeedback { get; private set; } + + public bool RoomPowerIsOn + { + get { return _displays.All(kv => kv.Value.PowerIsOnFeedback.BoolValue); } + } public EssentialsTechRoom(DeviceConfig config) : base(config) { @@ -30,6 +54,32 @@ namespace PepperDash.Essentials _tuners = GetDevices(_config.Tuners); _displays = GetDevices(_config.Displays); + + RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); + } + + private void SubscribeToDisplayFeedbacks() + { + foreach (var display in _displays) + { + display.Value.PowerIsOnFeedback.OutputChange += (sender, args) => RoomPowerIsOnFeedback.InvokeFireUpdate(); + } + } + + public void RoomPowerOn() + { + foreach (var display in _displays) + { + display.Value.PowerOn(); + } + } + + public void RoomPowerOff() + { + foreach (var display in _displays) + { + display.Value.PowerOff(); + } } private Dictionary GetDevices(ICollection config) where T:IKeyed @@ -47,7 +97,9 @@ namespace PepperDash.Essentials Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error getting devices. Check Essentials Configuration"); return null; } - } + } + + #region Overrides of EssentialsRoomBase diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 43fdf59a..ccce6ea7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -195,6 +195,28 @@ namespace PepperDash.Essentials.Core.Presets Dial(chanNum); } + public void UpdatePreset(int index, PresetChannel preset) + { + if (index >= PresetsList.Count) + { + return; + } + + PresetsList[index] = preset; + + SavePresets(); + } + + private void SavePresets() + { + var json = JsonConvert.SerializeObject(PresetsList); + + using (var file = File.Open(_filePath, FileMode.Truncate)) + { + file.Write(json, Encoding.UTF8); + } + } + private void Pulse(Action act) { act(true); From f11bdcfd539f9300fc6ebdd3c20f6e8170014689 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 16:21:48 -0700 Subject: [PATCH 14/89] add schedule config and schedule stuff --- .../Room/Config/EssentialsTechRoomConfig.cs | 2 + .../Room/Types/EssentialsTechRoom.cs | 68 +++++++++++++++++++ .../Global/Scheduler.cs | 36 ++++++++++ .../EssentialsRoomScheduledEventsConfig.cs | 37 ++++++++++ 4 files changed, 143 insertions(+) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index 8f35461b..3e4e0dc7 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -10,11 +10,13 @@ namespace PepperDash.Essentials.Room.Config public string UserPin; public string TechPin; public string PresetsFileName; + public EssentialsRoomScheduledEventsConfig RoomScheduledEvents; public EssentialsTechRoomConfig() { Displays = new List(); Tuners = new List(); + RoomScheduledEvents = new EssentialsRoomScheduledEventsConfig(); } } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 0680f46b..7a6d9c7e 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Scheduler; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; @@ -19,6 +21,8 @@ namespace PepperDash.Essentials private readonly EssentialsTechRoomConfig _config; + private ScheduledEventGroup _roomScheduledEventGroup; + public DevicePresetsModel TunerPresets { get @@ -56,6 +60,10 @@ namespace PepperDash.Essentials _displays = GetDevices(_config.Displays); RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); + + SubscribeToDisplayFeedbacks(); + + CreateScheduledEvents(); } private void SubscribeToDisplayFeedbacks() @@ -66,6 +74,66 @@ namespace PepperDash.Essentials } } + private void CreateScheduledEvents() + { + var eventsConfig = _config.RoomScheduledEvents; + + _roomScheduledEventGroup = new ScheduledEventGroup(Key); + + _roomScheduledEventGroup.RetrieveAllEvents(); + + Scheduler.AddEventGroup(_roomScheduledEventGroup); + + foreach (var eventConfig in eventsConfig.ScheduledEvents) + { + if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(eventConfig.Name)) + { + SchedulerUtilities.CreateEventFromConfig(eventConfig, _roomScheduledEventGroup); + continue; + } + + var roomEvent = _roomScheduledEventGroup.ScheduledEvents[eventConfig.Key]; + + if (!SchedulerUtilities.CheckEventTimeForMatch(roomEvent, DateTime.Parse(eventConfig.Time)) && + !SchedulerUtilities.CheckEventRecurrenceForMatch(roomEvent, eventConfig.Days)) + { + continue; + } + Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", roomEvent.Name); + + _roomScheduledEventGroup.DeleteEvent(roomEvent); + + SchedulerUtilities.CreateEventFromConfig(eventConfig, _roomScheduledEventGroup); + } + + _roomScheduledEventGroup.UserGroupCallBack += HandleScheduledEvent; + } + + private void HandleScheduledEvent(ScheduledEvent schevent, ScheduledEventCommon.eCallbackReason type) + { + var eventConfig = _config.RoomScheduledEvents.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); + + if (eventConfig == null) + { + Debug.Console(1, this, "Event with name {0} not found", schevent.Name); + return; + } + + if (eventConfig.Acknowledgeable) + { + schevent.Acknowledge(); + } + + CrestronInvoke.BeginInvoke((o) => + { + foreach (var a in eventConfig.Actions) + { + DeviceJsonApi.DoDeviceAction(a.Value); + } + }); + } + + public void RoomPowerOn() { foreach (var display in _displays) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index a7b721ef..85832cba 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -6,6 +6,8 @@ using Crestron.SimplSharp; using Crestron.SimplSharp.Scheduler; using PepperDash.Core; +using PepperDash.Essentials.Core.Fusion; +using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials.Core { @@ -135,5 +137,39 @@ namespace PepperDash.Essentials.Core return isMatch; } + + public static bool CheckEventTimeForMatch(ScheduledEvent evnt, DateTime time) + { + return evnt.DateAndTime.Hour == time.Hour && evnt.DateAndTime.Minute == time.Minute; + } + + public static bool CheckEventRecurrenceForMatch(ScheduledEvent evnt, ScheduledEventCommon.eWeekDays days) + { + return evnt.Recurrence.RecurrenceDays == days; + } + + public static void CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group) + { + if (group == null) + { + Debug.Console(0, "Unable to create event. Group is null"); + return; + } + var scheduledEvent = new ScheduledEvent(config.Key, group) + { + Acknowledgeable = config.Acknowledgeable, + Persistent = config.Persistent + }; + + scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday); + + scheduledEvent.Recurrence.Weekly(config.Days); + + var eventTime = DateTime.Parse(config.Time); + + if (DateTime.Now < eventTime) eventTime.AddDays(1); + + scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime); + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs new file mode 100644 index 00000000..50c77f1f --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using Crestron.SimplSharp.Scheduler; +using Newtonsoft.Json; +using PepperDash.Essentials.Core; + +namespace PepperDash.Essentials.Room.Config +{ + public class EssentialsRoomScheduledEventsConfig + { + [JsonProperty("scheduledEvents")] + public List ScheduledEvents; + } + + public class ScheduledEventConfig + { + [JsonProperty("key")] + public string Key; + + [JsonProperty("name")] + public string Name; + + [JsonProperty("days")] + public ScheduledEventCommon.eWeekDays Days; + + [JsonProperty("time")] + public string Time; + + [JsonProperty("actions")] + public Dictionary Actions; + + [JsonProperty("persistent")] + public bool Persistent; + + [JsonProperty("acknowledgeable")] + public bool Acknowledgeable; + } +} \ No newline at end of file From 45c1e25e4fb50166e9cfc0ec95985f1dffe82b26 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 21:35:31 -0700 Subject: [PATCH 15/89] Change to list of actions instead of dictionary --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 2 +- .../Room/Config/EssentialsRoomScheduledEventsConfig.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 7a6d9c7e..f0521d64 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -128,7 +128,7 @@ namespace PepperDash.Essentials { foreach (var a in eventConfig.Actions) { - DeviceJsonApi.DoDeviceAction(a.Value); + DeviceJsonApi.DoDeviceAction(a); } }); } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs index 50c77f1f..e16524ce 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -26,7 +26,7 @@ namespace PepperDash.Essentials.Room.Config public string Time; [JsonProperty("actions")] - public Dictionary Actions; + public List Actions; [JsonProperty("persistent")] public bool Persistent; From fc5d4f946d0c7438d56dd411178fee5eddfdbfd8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Dec 2020 21:36:07 -0700 Subject: [PATCH 16/89] added scheduled events config --- .../PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 6d4def73..1a2fc594 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -278,6 +278,7 @@ + From 05e2422cb405be3a0df6c2c4ae04ea379d27f560 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:48:07 -0700 Subject: [PATCH 17/89] refactoring some methods add handling for Scheduled Events --- .../Room/Types/EssentialsTechRoom.cs | 167 ++++++++++++------ 1 file changed, 110 insertions(+), 57 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index f0521d64..cb8ebfa1 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharp.Scheduler; +using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; @@ -12,23 +13,37 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsTechRoom:EssentialsRoomBase + public class EssentialsTechRoom : EssentialsRoomBase { - private readonly Dictionary _tuners; + private readonly EssentialsTechRoomConfig _config; private readonly Dictionary _displays; private readonly DevicePresetsModel _tunerPresets; - - private readonly EssentialsTechRoomConfig _config; + private readonly Dictionary _tuners; private ScheduledEventGroup _roomScheduledEventGroup; + public EssentialsTechRoom(DeviceConfig config) : base(config) + { + _config = config.Properties.ToObject(); + + _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); + + _tunerPresets.LoadChannels(); + + _tuners = GetDevices(_config.Tuners); + _displays = GetDevices(_config.Displays); + + RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); + + SubscribeToDisplayFeedbacks(); + + CreateOrUpdateScheduledEvents(); + } + public DevicePresetsModel TunerPresets { - get - { - return _tunerPresets; - } + get { return _tunerPresets; } } public Dictionary Tuners @@ -48,70 +63,104 @@ namespace PepperDash.Essentials get { return _displays.All(kv => kv.Value.PowerIsOnFeedback.BoolValue); } } - public EssentialsTechRoom(DeviceConfig config) : base(config) - { - _config = config.Properties.ToObject(); - - _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); - - _tunerPresets.LoadChannels(); - - _tuners = GetDevices(_config.Tuners); - _displays = GetDevices(_config.Displays); - - RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); - - SubscribeToDisplayFeedbacks(); - - CreateScheduledEvents(); - } - private void SubscribeToDisplayFeedbacks() { foreach (var display in _displays) { - display.Value.PowerIsOnFeedback.OutputChange += (sender, args) => RoomPowerIsOnFeedback.InvokeFireUpdate(); + display.Value.PowerIsOnFeedback.OutputChange += + (sender, args) => RoomPowerIsOnFeedback.InvokeFireUpdate(); } } - private void CreateScheduledEvents() + private void CreateOrUpdateScheduledEvents() { var eventsConfig = _config.RoomScheduledEvents; - _roomScheduledEventGroup = new ScheduledEventGroup(Key); + GetOrCreateScheduleGroup(); - _roomScheduledEventGroup.RetrieveAllEvents(); - - Scheduler.AddEventGroup(_roomScheduledEventGroup); - - foreach (var eventConfig in eventsConfig.ScheduledEvents) + foreach (var eventConfig in eventsConfig) { - if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(eventConfig.Name)) - { - SchedulerUtilities.CreateEventFromConfig(eventConfig, _roomScheduledEventGroup); - continue; - } - - var roomEvent = _roomScheduledEventGroup.ScheduledEvents[eventConfig.Key]; - - if (!SchedulerUtilities.CheckEventTimeForMatch(roomEvent, DateTime.Parse(eventConfig.Time)) && - !SchedulerUtilities.CheckEventRecurrenceForMatch(roomEvent, eventConfig.Days)) - { - continue; - } - Debug.Console(1, this, "Existing event does not match new config properties. Deleting exisiting event: '{0}'", roomEvent.Name); - - _roomScheduledEventGroup.DeleteEvent(roomEvent); - - SchedulerUtilities.CreateEventFromConfig(eventConfig, _roomScheduledEventGroup); + CreateOrUpdateSingleEvent(eventConfig); } _roomScheduledEventGroup.UserGroupCallBack += HandleScheduledEvent; } + private void GetOrCreateScheduleGroup() + { + if (_roomScheduledEventGroup == null) + { + _roomScheduledEventGroup = Scheduler.GetEventGroup(Key) ?? new ScheduledEventGroup(Key); + + Scheduler.AddEventGroup(_roomScheduledEventGroup); + } + + _roomScheduledEventGroup.RetrieveAllEvents(); + } + + private void CreateOrUpdateSingleEvent(ScheduledEventConfig scheduledEvent) + { + if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(scheduledEvent.Name)) + { + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); + return; + } + + var roomEvent = _roomScheduledEventGroup.ScheduledEvents[scheduledEvent.Key]; + + if (!SchedulerUtilities.CheckEventTimeForMatch(roomEvent, DateTime.Parse(scheduledEvent.Time)) && + !SchedulerUtilities.CheckEventRecurrenceForMatch(roomEvent, scheduledEvent.Days)) + { + return; + } + + Debug.Console(1, this, + "Existing event does not match new config properties. Deleting existing event '{0}' and creating new event from configuration", + roomEvent.Name); + + _roomScheduledEventGroup.DeleteEvent(roomEvent); + + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); + } + + public void AddOrUpdateScheduledEvent(ScheduledEventConfig scheduledEvent) + { + //update config based on key of scheduleEvent + GetOrCreateScheduleGroup(); + var existingEvent = _config.RoomScheduledEvents.FirstOrDefault(e => e.Key == scheduledEvent.Key); + + if (existingEvent == null) + { + _config.RoomScheduledEvents.Add(scheduledEvent); + } + + //create or update event based on config + CreateOrUpdateSingleEvent(scheduledEvent); + //save config + Config.Properties = JToken.FromObject(_config); + + CustomSetConfig(Config); + //Fire Event + OnScheduledEventUpdate(); + } + + public void OnScheduledEventUpdate() + { + var handler = ScheduledEventsChanged; + + if (handler == null) + { + return; + } + + handler(this, new ScheduledEventEventArgs {ScheduledEvents = _config.RoomScheduledEvents}); + } + + public event EventHandler ScheduledEventsChanged; + private void HandleScheduledEvent(ScheduledEvent schevent, ScheduledEventCommon.eCallbackReason type) { - var eventConfig = _config.RoomScheduledEvents.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); + var eventConfig = _config.RoomScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); if (eventConfig == null) { @@ -150,7 +199,7 @@ namespace PepperDash.Essentials } } - private Dictionary GetDevices(ICollection config) where T:IKeyed + private Dictionary GetDevices(ICollection config) where T : IKeyed { try { @@ -162,13 +211,12 @@ namespace PepperDash.Essentials } catch { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error getting devices. Check Essentials Configuration"); + Debug.Console(0, this, Debug.ErrorLogLevel.Error, + "Error getting devices. Check Essentials Configuration"); return null; } } - - #region Overrides of EssentialsRoomBase protected override Func IsWarmingFeedbackFunc @@ -213,4 +261,9 @@ namespace PepperDash.Essentials #endregion } + + public class ScheduledEventEventArgs : EventArgs + { + public List ScheduledEvents; + } } \ No newline at end of file From 3a024b8d4c0cb08cd94c5060a0cb07da795026b9 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:48:21 -0700 Subject: [PATCH 18/89] moved ScheduledEvents list to main object --- PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index 3e4e0dc7..e4b88e96 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -10,13 +10,13 @@ namespace PepperDash.Essentials.Room.Config public string UserPin; public string TechPin; public string PresetsFileName; - public EssentialsRoomScheduledEventsConfig RoomScheduledEvents; + public List RoomScheduledEvents; public EssentialsTechRoomConfig() { Displays = new List(); Tuners = new List(); - RoomScheduledEvents = new EssentialsRoomScheduledEventsConfig(); + RoomScheduledEvents = new List(); } } } \ No newline at end of file From 0dc2e9d134e185d68a28e4fea0e13c19a3bb96f7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:48:34 -0700 Subject: [PATCH 19/89] Added logic to create EssentialsTechRoom --- PepperDashEssentials/ControlSystem.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 460086e0..4e24de9f 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -12,6 +12,7 @@ using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Fusion; using PepperDash.Essentials.Devices.Common; using PepperDash.Essentials.DM; using PepperDash.Essentials.Fusion; @@ -436,7 +437,7 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1)); + DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, 0xf1)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -454,6 +455,18 @@ namespace PepperDash.Essentials CreateMobileControlBridge(room); } + else if (room is EssentialsTechRoom) + { + DeviceManager.AddDevice(room); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); + DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase(room, 0xF1)); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); + + CreateMobileControlBridge(room); + } else { Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsRoom, attempting to add to DeviceManager w/o Fusion"); From ab6d44e60461497e9ec29c1b178aa986fb220feb Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:48:48 -0700 Subject: [PATCH 20/89] added enable property to ScheduledEventConfig --- .../Room/Config/EssentialsRoomScheduledEventsConfig.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs index e16524ce..366f453c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -33,5 +33,8 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("acknowledgeable")] public bool Acknowledgeable; + + [JsonProperty("enable")] + public bool Enable; } } \ No newline at end of file From d2b7e71c4adb254b8ff406d03c9e52b3e874a1d8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:49:30 -0700 Subject: [PATCH 21/89] Added GetScheduledEventGroup Method added logic to enable/disable events --- .../Global/Scheduler.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 85832cba..64ba57df 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Crestron.SimplSharp; using Crestron.SimplSharp.Scheduler; using PepperDash.Core; -using PepperDash.Essentials.Core.Fusion; using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials.Core @@ -16,7 +13,7 @@ namespace PepperDash.Essentials.Core /// public static class Scheduler { - private static Dictionary EventGroups = new Dictionary(); + private static readonly Dictionary EventGroups = new Dictionary(); static Scheduler() { @@ -51,7 +48,6 @@ namespace PepperDash.Essentials.Core /// /// Adds the event group to the global list /// - /// /// public static void AddEventGroup(ScheduledEventGroup eventGroup) { @@ -69,6 +65,13 @@ namespace PepperDash.Essentials.Core if(!EventGroups.ContainsKey(eventGroup.Name)) EventGroups.Remove(eventGroup.Name); } + + public static ScheduledEventGroup GetEventGroup(string key) + { + ScheduledEventGroup returnValue; + + return EventGroups.TryGetValue(key, out returnValue) ? returnValue : null; + } } public static class SchedulerUtilities @@ -161,6 +164,15 @@ namespace PepperDash.Essentials.Core Persistent = config.Persistent }; + if (config.Enable) + { + scheduledEvent.Resume(); + } + else + { + scheduledEvent.Pause(); + } + scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday); scheduledEvent.Recurrence.Weekly(config.Days); From 43d7fab04d25a27456f30a603505634c353aecf7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 11:50:48 -0700 Subject: [PATCH 22/89] refactored Fusion Base Class --- ...lsHuddleSpaceFusionSystemControllerBase.cs | 1769 +++++++++-------- 1 file changed, 952 insertions(+), 817 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 39cd7d78..9270ed37 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -2,164 +2,118 @@ using System.Collections.Generic; using System.Linq; using System.Text; - using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml.Serialization; -using Crestron.SimplSharp.CrestronXmlLinq; using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.Fusion; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - using PepperDash.Core; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; - - namespace PepperDash.Essentials.Core.Fusion { - public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider - { - public event EventHandler ScheduleChange; - //public event EventHandler MeetingEndWarning; - //public event EventHandler NextMeetingBeginWarning; + public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider + { + private const string RemoteOccupancyXml = "Local{0}"; + private readonly bool _guidFileExists; - public event EventHandler RoomInfoChange; + private readonly Dictionary _sourceToFeedbackSigs = + new Dictionary(); + + protected StringSigData CurrentRoomSourceNameSig; public FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); + protected FusionOccupancySensorAsset FusionOccSensor; + protected FusionRemoteOccupancySensor FusionRemoteOccSensor; - protected FusionRoom FusionRoom; - protected EssentialsRoomBase Room; - Dictionary SourceToFeedbackSigs = - new Dictionary(); + protected FusionRoom FusionRoom; + protected Dictionary FusionStaticAssets; + public long PushNotificationTimeout = 5000; + protected EssentialsRoomBase Room; + public long SchedulePollInterval = 300000; - StatusMonitorCollection ErrorMessageRollUp; + private Event _currentMeeting; + private RoomSchedule _currentSchedule; + private CTimer _dailyTimeRequestTimer; + private StatusMonitorCollection _errorMessageRollUp; - protected StringSigData CurrentRoomSourceNameSig; + private FusionRoomGuids _guiDs; + private uint _ipId; + + private bool _isRegisteredForSchedulePushNotifications; + private Event _nextMeeting; + + private CTimer _pollTimer; + + private CTimer _pushNotificationTimer; + + private string _roomOccupancyRemoteString; #region System Info Sigs + //StringSigData SystemName; //StringSigData Model; //StringSigData SerialNumber; //StringSigData Uptime; + #endregion - #region Processor Info Sigs - StringSigData Ip1; - StringSigData Ip2; - StringSigData Gateway; - StringSigData Hostname; - StringSigData Domain; - StringSigData Dns1; - StringSigData Dns2; - StringSigData Mac1; - StringSigData Mac2; - StringSigData NetMask1; - StringSigData NetMask2; - StringSigData Firmware; - StringSigData[] Program = new StringSigData[10]; + private readonly StringSigData[] _program = new StringSigData[10]; + private StringSigData _dns1; + private StringSigData _dns2; + private StringSigData _domain; + private StringSigData _firmware; + private StringSigData _gateway; + private StringSigData _hostname; + private StringSigData _ip1; + private StringSigData _ip2; + private StringSigData _mac1; + private StringSigData _mac2; + private StringSigData _netMask1; + private StringSigData _netMask2; + #endregion #region Default Display Source Sigs - BooleanSigData[] Source = new BooleanSigData[10]; + private BooleanSigData[] _source = new BooleanSigData[10]; #endregion - RoomSchedule CurrentSchedule; - - Event NextMeeting; - - Event CurrentMeeting; - - protected string RoomGuid - { - get - { - return GUIDs.RoomGuid; - } - - } - - uint IpId; - - FusionRoomGuids GUIDs; - - bool GuidFileExists; - - bool IsRegisteredForSchedulePushNotifications = false; - - CTimer PollTimer = null; - - CTimer PushNotificationTimer = null; - - CTimer DailyTimeRequestTimer = null; - - // Default poll time is 5 min unless overridden by config value - public long SchedulePollInterval = 300000; - - public long PushNotificationTimeout = 5000; - - private const string RemoteOccupancyXml = "Local{0}"; - - protected Dictionary FusionStaticAssets; - - // For use with local occ sensor devices which will relay to Fusion the current occupancy status - protected FusionRemoteOccupancySensor FusionRemoteOccSensor; - - // For use with occ sensor attached to a scheduling panel in Fusion - protected FusionOccupancySensorAsset FusionOccSensor; - - public BoolFeedback RoomIsOccupiedFeedback { get; private set; } - - private string _roomOccupancyRemoteString; - public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } - - protected Func RoomIsOccupiedFeedbackFunc - { - get - { - return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; - } - } - - //ScheduleResponseEvent NextMeeting; - public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId) - : base(room.Key + "-fusion") - { - + : base(room.Key + "-fusion") + { try { - Room = room; - IpId = ipId; + _ipId = ipId; FusionStaticAssets = new Dictionary(); - GUIDs = new FusionRoomGuids(); + _guiDs = new FusionRoomGuids(); - var mac = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + var mac = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); var slot = Global.ControlSystem.ProgramNumber; - string guidFilePath = Global.FilePathPrefix + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); + var guidFilePath = Global.FilePathPrefix + + string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); - GuidFileExists = File.Exists(guidFilePath); + _guidFileExists = File.Exists(guidFilePath); // Check if file exists - if (!GuidFileExists) + if (!_guidFileExists) { // Does not exist. Create GUIDs - GUIDs = new FusionRoomGuids(Room.Name, ipId, GUIDs.GenerateNewRoomGuid(slot, mac), FusionStaticAssets); + _guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac), + FusionStaticAssets); } else { @@ -170,18 +124,19 @@ namespace PepperDash.Essentials.Core.Fusion if (Room.RoomOccupancy != null) { if (Room.OccupancyStatusProviderIsRemote) + { SetUpRemoteOccupancy(); + } else { SetUpLocalOccupancy(); } } - AddPostActivationAction(() => { - CreateSymbolAndBasicSigs(IpId); + CreateSymbolAndBasicSigs(_ipId); SetUpSources(); SetUpCommunitcationMonitors(); SetUpDisplay(); @@ -192,27 +147,51 @@ namespace PepperDash.Essentials.Core.Fusion GenerateGuidFile(guidFilePath); }); - } catch (Exception e) { Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Building Fusion System Controller: {0}", e); } - } + } + + protected string RoomGuid + { + get { return _guiDs.RoomGuid; } + } + + public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } + + protected Func RoomIsOccupiedFeedbackFunc + { + get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; } + } + + #region IOccupancyStatusProvider Members + + public BoolFeedback RoomIsOccupiedFeedback { get; private set; } + + #endregion + + public event EventHandler ScheduleChange; + //public event EventHandler MeetingEndWarning; + //public event EventHandler NextMeetingBeginWarning; + + public event EventHandler RoomInfoChange; + + //ScheduleResponseEvent NextMeeting; /// /// Used for extension classes to execute whatever steps are necessary before generating the RVI and GUID files /// protected virtual void ExecuteCustomSteps() { - } /// /// Generates the guid file in NVRAM. If the file already exists it will be overwritten. /// /// path for the file - void GenerateGuidFile(string filePath) + private void GenerateGuidFile(string filePath) { if (string.IsNullOrEmpty(filePath)) { @@ -220,32 +199,32 @@ namespace PepperDash.Essentials.Core.Fusion return; } - CCriticalSection _fileLock = new CCriticalSection(); + var fileLock = new CCriticalSection(); try { - if (_fileLock == null || _fileLock.Disposed) + if (fileLock.Disposed) + { return; + } - _fileLock.Enter(); + fileLock.Enter(); Debug.Console(1, this, "Writing GUIDs to file"); - if (FusionOccSensor == null) - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets); - else - GUIDs = new FusionRoomGuids(Room.Name, IpId, RoomGuid, FusionStaticAssets, FusionOccSensor); + _guiDs = FusionOccSensor == null + ? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets) + : new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets, FusionOccSensor); - var JSON = JsonConvert.SerializeObject(GUIDs, Newtonsoft.Json.Formatting.Indented); + var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented); - using (StreamWriter sw = new StreamWriter(filePath)) + using (var sw = new StreamWriter(filePath)) { - sw.Write(JSON); + sw.Write(json); sw.Flush(); } Debug.Console(1, this, "Guids successfully written to file '{0}'", filePath); - } catch (Exception e) { @@ -253,8 +232,10 @@ namespace PepperDash.Essentials.Core.Fusion } finally { - if (_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); + if (!fileLock.Disposed) + { + fileLock.Leave(); + } } } @@ -262,42 +243,45 @@ namespace PepperDash.Essentials.Core.Fusion /// Reads the guid file from NVRAM /// /// path for te file - void ReadGuidFile(string filePath) + private void ReadGuidFile(string filePath) { - if(string.IsNullOrEmpty(filePath)) + if (string.IsNullOrEmpty(filePath)) { Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Error reading guid file. No path specified."); return; } - CCriticalSection _fileLock = new CCriticalSection(); + var fileLock = new CCriticalSection(); try { - if(_fileLock == null || _fileLock.Disposed) - return; - - _fileLock.Enter(); - - if(File.Exists(filePath)) + if (fileLock.Disposed) { - var JSON = File.ReadToEnd(filePath, Encoding.ASCII); - - GUIDs = JsonConvert.DeserializeObject(JSON); - - IpId = GUIDs.IpId; - - FusionStaticAssets = GUIDs.StaticAssets; - + return; } - Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath); + fileLock.Enter(); - Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, IpId, RoomGuid); + if (File.Exists(filePath)) + { + var json = File.ReadToEnd(filePath, Encoding.ASCII); + + _guiDs = JsonConvert.DeserializeObject(json); + + _ipId = _guiDs.IpId; + + FusionStaticAssets = _guiDs.StaticAssets; + } + + Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", + filePath); + + Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, _ipId, RoomGuid); foreach (var item in FusionStaticAssets) { - Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, item.Value.SlotNumber, item.Value.InstanceId); + Debug.Console(1, this, "\nAsset Name: {0}\nAsset No: {1}\n Guid: {2}", item.Value.Name, + item.Value.SlotNumber, item.Value.InstanceId); } } catch (Exception e) @@ -306,46 +290,65 @@ namespace PepperDash.Essentials.Core.Fusion } finally { - if(_fileLock != null && !_fileLock.Disposed) - _fileLock.Leave(); + if (!fileLock.Disposed) + { + fileLock.Leave(); + } } - } - protected virtual void CreateSymbolAndBasicSigs(uint ipId) - { + protected virtual void CreateSymbolAndBasicSigs(uint ipId) + { Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - FusionRoom.Register(); + FusionRoom.Register(); - FusionRoom.FusionStateChange += new FusionStateEventHandler(FusionRoom_FusionStateChange); + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(FusionRoomSchedule_DeviceExtenderSigChange); - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += new DeviceExtenderJoinChangeEventHandler(ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange); - FusionRoom.OnlineStatusChange += new OnlineStatusChangeEventHandler(FusionRoom_OnlineStatusChange); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += + FusionRoomSchedule_DeviceExtenderSigChange; + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += + ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; + FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", + "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", + "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", + "Creates and Ad Hoc meeting for on hour or until the next meeting", + ConsoleAccessLevelEnum.AccessOperator); - // Room to fusion room - Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); + // Room to fusion room + Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); - // Don't think we need to get current status of this as nothing should be alive yet. - (Room as IHasCurrentSourceInfoChange).CurrentSourceChange += new SourceInfoChangeHandler(Room_CurrentSourceInfoChange); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", + eSigIoMask.InputSigOnly); + // Don't think we need to get current status of this as nothing should be alive yet. + var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; + if (hasCurrentSourceInfoChange != null) + { + hasCurrentSourceInfoChange.CurrentSourceChange += Room_CurrentSourceInfoChange; + } - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsRoomBase).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as IRunRouteAction).RunRouteAction("roomOff", Room.SourceListKey)); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - FusionRoom.ErrorMessage.InputSig.StringValue = - "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction(Room.PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); + } + }); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + FusionRoom.ErrorMessage.InputSig.StringValue = + "3: 7 Errors: This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;This is a really long error message;"; SetUpEthernetValues(); @@ -355,7 +358,7 @@ namespace PepperDash.Essentials.Core.Fusion GetProcessorInfo(); - CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; } protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) @@ -372,82 +375,107 @@ namespace PepperDash.Essentials.Core.Fusion //Model.InputSig.StringValue = InitialParametersClass.ControllerPromptName; //SerialNumber.InputSig.StringValue = InitialParametersClass. - string response = string.Empty; + var response = string.Empty; var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); - systemReboot.OutputSig.SetSigFalseAction(() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); + systemReboot.OutputSig.SetSigFalseAction( + () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); } protected void SetUpEthernetValues() { - Ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); - Ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); - Gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); - Hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); - Domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); - Dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); - Dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); - Mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); - Mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); - NetMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); - NetMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); + _ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); } protected void GetProcessorEthernetValues() { - Ip1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); - Gateway.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); - Hostname.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); - Domain.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); + _ip1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0); + _gateway.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, 0); + _hostname.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0); + _domain.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - var dnsServers = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); - Dns1.InputSig.StringValue = dnsServers[0]; + var dnsServers = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, 0).Split(','); + _dns1.InputSig.StringValue = dnsServers[0]; if (dnsServers.Length > 1) - Dns2.InputSig.StringValue = dnsServers[1]; + { + _dns2.InputSig.StringValue = dnsServers[1]; + } - Mac1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); - NetMask1.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); + _mac1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); + _netMask1.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 0); // Interface 1 - if (InitialParametersClass.NumberOfEthernetInterfaces > 1) // Only get these values if the processor has more than 1 NIC + if (InitialParametersClass.NumberOfEthernetInterfaces > 1) + // Only get these values if the processor has more than 1 NIC { - Ip2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); - Mac2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); - NetMask2.InputSig.StringValue = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); + _ip2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 1); + _mac2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 1); + _netMask2.InputSig.StringValue = + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, 1); } } protected void GetProcessorInfo() { - - Firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); + _firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) { - for (int i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) + for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) { var join = 62 + i; var progNum = i + 1; - Program[i] = FusionRoom.CreateOffsetStringSig((uint)join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); + _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, + string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); } } - Firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; - + _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; } protected void GetCustomProperties() { if (FusionRoom.IsOnline) { - string fusionRoomCustomPropertiesRequest = @"RoomConfigurationRequest"; + const string fusionRoomCustomPropertiesRequest = + @"RoomConfigurationRequest"; - FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = fusionRoomCustomPropertiesRequest; + FusionRoom.ExtenderFusionRoomDataReservedSigs.RoomConfigQuery.StringValue = + fusionRoomCustomPropertiesRequest; } } - void GetTouchpanelInfo() + private void GetTouchpanelInfo() { // TODO: Get IP and Project Name from TP } @@ -460,33 +488,33 @@ namespace PepperDash.Essentials.Core.Fusion // Send Push Notification Action request: - string requestID = "InitialPushRequest"; + const string requestId = "InitialPushRequest"; - string actionRequest = - string.Format("\n{0}\n", requestID) + + var actionRequest = + string.Format("\n{0}\n", requestId) + "RegisterPushModel\n" + "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + "\n" + - "\n"; + "\n"; Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); @@ -498,17 +526,16 @@ namespace PepperDash.Essentials.Core.Fusion RequestLocalDateTime(null); // Setup timer to request time daily - if (DailyTimeRequestTimer != null && !DailyTimeRequestTimer.Disposed) + if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) { - DailyTimeRequestTimer.Stop(); - DailyTimeRequestTimer.Dispose(); + _dailyTimeRequestTimer.Stop(); + _dailyTimeRequestTimer.Dispose(); } - DailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); - DailyTimeRequestTimer.Reset(86400000, 86400000); + _dailyTimeRequestTimer.Reset(86400000, 86400000); } - } /// @@ -517,9 +544,10 @@ namespace PepperDash.Essentials.Core.Fusion /// public void RequestLocalDateTime(object callbackObject) { - string timeRequestID = "TimeRequest"; + const string timeRequestId = "TimeRequest"; - string timeRequest = string.Format("{0}", timeRequestID); + var timeRequest = string.Format("{0}", + timeRequestId); FusionRoom.ExtenderFusionRoomDataReservedSigs.LocalDateTimeQuery.StringValue = timeRequest; } @@ -527,79 +555,78 @@ namespace PepperDash.Essentials.Core.Fusion /// /// Generates a room schedule request for this room for the next 24 hours. /// - /// string identifying this request. Used with a corresponding ScheduleResponse value public void RequestFullRoomSchedule(object callbackObject) { - DateTime now = DateTime.Today; + var now = DateTime.Today; - string currentTime = now.ToString("s"); + var currentTime = now.ToString("s"); - string requestTest = - string.Format("FullSchedleRequest{0}{1}24", RoomGuid, currentTime); + var requestTest = + string.Format( + "FullSchedleRequest{0}{1}24", + RoomGuid, currentTime); Debug.Console(2, this, "Sending Fusion ScheduleQuery: \n{0}", requestTest); FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleQuery.StringValue = requestTest; - if (IsRegisteredForSchedulePushNotifications) - PushNotificationTimer.Stop(); + if (_isRegisteredForSchedulePushNotifications) + { + _pushNotificationTimer.Stop(); + } } - + /// /// Wrapper method to allow console commands to modify the current meeting end time /// /// meetingID extendTime public void ModifyMeetingEndTimeConsoleHelper(string command) { - string requestID; - string meetingID = null; - int extendMinutes = -1; + var extendMinutes = -1; - requestID = "ModifyMeetingTest12345"; + const string requestId = "ModifyMeetingTest12345"; try { var tokens = command.Split(' '); - meetingID = tokens[0]; extendMinutes = Int32.Parse(tokens[1]); - } catch (Exception e) { Debug.Console(1, this, "Error parsing console command: {0}", e); } - ModifyMeetingEndTime(requestID, extendMinutes); - + ModifyMeetingEndTime(requestId, extendMinutes); } /// /// Ends or Extends the current meeting by the specified number of minutes. /// + /// /// Number of minutes to extend the meeting. A value of 0 will end the meeting. - public void ModifyMeetingEndTime(string requestID, int extendMinutes) + public void ModifyMeetingEndTime(string requestId, int extendMinutes) { - if(CurrentMeeting == null) + if (_currentMeeting == null) { Debug.Console(1, this, "No meeting in progress. Unable to modify end time."); return; - } + } if (extendMinutes > -1) { - if(extendMinutes > 0) + if (extendMinutes > 0) { - var extendTime = CurrentMeeting.dtEnd - DateTime.Now; - double extendMinutesRaw = extendTime.TotalMinutes; + var extendTime = _currentMeeting.dtEnd - DateTime.Now; + var extendMinutesRaw = extendTime.TotalMinutes; - extendMinutes = extendMinutes + (int)Math.Round(extendMinutesRaw); + extendMinutes = extendMinutes + (int) Math.Round(extendMinutesRaw); } - string requestTest = string.Format( + var requestTest = string.Format( "{0}{1}MeetingChange" - , requestID, RoomGuid, CurrentMeeting.MeetingID, extendMinutes); + , requestId, RoomGuid, _currentMeeting.MeetingID, extendMinutes); Debug.Console(1, this, "Sending MeetingChange Request: \n{0}", requestTest); @@ -609,8 +636,6 @@ namespace PepperDash.Essentials.Core.Fusion { Debug.Console(1, this, "Invalid time specified"); } - - } /// @@ -618,38 +643,38 @@ namespace PepperDash.Essentials.Core.Fusion /// public void CreateAsHocMeeting(string command) { - string requestID = "CreateAdHocMeeting"; + const string requestId = "CreateAdHocMeeting"; - DateTime now = DateTime.Now.AddMinutes(1); + var now = DateTime.Now.AddMinutes(1); now.AddSeconds(-now.Second); // Assume 1 hour meeting if possible - DateTime dtEnd = now.AddHours(1); + var dtEnd = now.AddHours(1); // Check if room is available for 1 hour before next meeting - if (NextMeeting != null) + if (_nextMeeting != null) { - var roomAvailable = NextMeeting.dtEnd.Subtract(dtEnd); + var roomAvailable = _nextMeeting.dtEnd.Subtract(dtEnd); if (roomAvailable.TotalMinutes < 60) { - /// Room not available for full hour, book until next meeting starts - dtEnd = NextMeeting.dtEnd; + // Room not available for full hour, book until next meeting starts + dtEnd = _nextMeeting.dtEnd; } } - string createMeetingRequest = + var createMeetingRequest = "" + - string.Format("{0}", requestID) + - string.Format("{0}", RoomGuid) + - "" + - string.Format("{0}", now.ToString("s")) + - string.Format("{0}", dtEnd.ToString("s")) + - "AdHoc Meeting" + - "Room User" + - "Example Message" + - "" + + string.Format("{0}", requestId) + + string.Format("{0}", RoomGuid) + + "" + + string.Format("{0}", now.ToString("s")) + + string.Format("{0}", dtEnd.ToString("s")) + + "AdHoc Meeting" + + "Room User" + + "Example Message" + + "" + ""; Debug.Console(2, this, "Sending CreateMeeting Request: \n{0}", createMeetingRequest); @@ -659,7 +684,6 @@ namespace PepperDash.Essentials.Core.Fusion //Debug.Console(1, this, "Sending CreateMeeting Request: \n{0}", command); //FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateMeeting.StringValue = command; - } /// @@ -667,73 +691,73 @@ namespace PepperDash.Essentials.Core.Fusion /// /// /// - protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + protected void ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, + SigEventArgs args) { - Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + Debug.Console(2, this, "Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, + args.Sig.StringValue); if (args.Sig == FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQueryResponse) { try { - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); message.LoadXml(args.Sig.StringValue); var actionResponse = message["ActionResponse"]; - if (actionResponse != null) + if (actionResponse == null) { - var requestID = actionResponse["RequestID"]; + return; + } - if (requestID.InnerText == "InitialPushRequest") + var requestId = actionResponse["RequestID"]; + + if (requestId.InnerText != "InitialPushRequest") + { + return; + } + + if (actionResponse["ActionID"].InnerText != "RegisterPushModel") + { + return; + } + + var parameters = actionResponse["Parameters"]; + + foreach (var isRegistered in from XmlElement parameter in parameters + where parameter.HasAttributes + select parameter.Attributes + into attributes + where attributes["ID"].Value == "Registered" + select Int32.Parse(attributes["Value"].Value)) + { + switch (isRegistered) { - if (actionResponse["ActionID"].InnerText == "RegisterPushModel") - { - var parameters = actionResponse["Parameters"]; - - foreach (XmlElement parameter in parameters) + case 1: + _isRegisteredForSchedulePushNotifications = true; + if (_pollTimer != null && !_pollTimer.Disposed) { - if (parameter.HasAttributes) - { - var attributes = parameter.Attributes; - - if (attributes["ID"].Value == "Registered") - { - var isRegistered = Int32.Parse(attributes["Value"].Value); - - if (isRegistered == 1) - { - IsRegisteredForSchedulePushNotifications = true; - - if (PollTimer != null && !PollTimer.Disposed) - { - PollTimer.Stop(); - PollTimer.Dispose(); - } - - PushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, PushNotificationTimeout, PushNotificationTimeout); - - PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); - } - else if (isRegistered == 0) - { - IsRegisteredForSchedulePushNotifications = false; - - if (PushNotificationTimer != null && !PushNotificationTimer.Disposed) - { - PushNotificationTimer.Stop(); - PushNotificationTimer.Dispose(); - } - - PollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, SchedulePollInterval); - - PollTimer.Reset(SchedulePollInterval, SchedulePollInterval); - } - } - } + _pollTimer.Stop(); + _pollTimer.Dispose(); } - } + _pushNotificationTimer = new CTimer(RequestFullRoomSchedule, null, + PushNotificationTimeout, PushNotificationTimeout); + _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); + break; + case 0: + _isRegisteredForSchedulePushNotifications = false; + if (_pushNotificationTimer != null && !_pushNotificationTimer.Disposed) + { + _pushNotificationTimer.Stop(); + _pushNotificationTimer.Dispose(); + } + _pollTimer = new CTimer(RequestFullRoomSchedule, null, SchedulePollInterval, + SchedulePollInterval); + _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + break; } } } @@ -746,7 +770,7 @@ namespace PepperDash.Essentials.Core.Fusion { try { - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); message.LoadXml(args.Sig.StringValue); @@ -759,13 +783,15 @@ namespace PepperDash.Essentials.Core.Fusion if (localDateTime != null) { var tempLocalDateTime = localDateTime.InnerText; - - DateTime currentTime = DateTime.Parse(tempLocalDateTime); + + var currentTime = DateTime.Parse(tempLocalDateTime); Debug.Console(1, this, "DateTime from Fusion Server: {0}", currentTime); // Parse time and date from response and insert values - CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, (ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort)currentTime.Year); + CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute, + (ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day, + (ushort) currentTime.Year); Debug.Console(1, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); } @@ -780,13 +806,13 @@ namespace PepperDash.Essentials.Core.Fusion { // Room info response with custom properties - string roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); + var roomConfigResponseArgs = args.Sig.StringValue.Replace("&", "and"); Debug.Console(2, this, "Fusion Response: \n {0}", roomConfigResponseArgs); try { - XmlDocument roomConfigResponse = new XmlDocument(); + var roomConfigResponse = new XmlDocument(); roomConfigResponse.LoadXml(roomConfigResponseArgs); @@ -794,13 +820,13 @@ namespace PepperDash.Essentials.Core.Fusion if (requestRoomConfiguration != null) { - RoomInformation roomInformation = new RoomInformation(); + var roomInformation = new RoomInformation(); foreach (XmlElement e in roomConfigResponse.FirstChild.ChildNodes) { if (e.Name == "RoomInformation") { - XmlReader roomInfo = new XmlReader(e.OuterXml); + var roomInfo = new XmlReader(e.OuterXml); roomInformation = CrestronXMLSerialization.DeSerializeObject(roomInfo); } @@ -808,7 +834,7 @@ namespace PepperDash.Essentials.Core.Fusion { foreach (XmlElement el in e) { - FusionCustomProperty customProperty = new FusionCustomProperty(); + var customProperty = new FusionCustomProperty(); if (el.Name == "CustomField") { @@ -838,7 +864,9 @@ namespace PepperDash.Essentials.Core.Fusion var handler = RoomInfoChange; if (handler != null) + { handler(this, new EventArgs()); + } CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation); } @@ -851,7 +879,6 @@ namespace PepperDash.Essentials.Core.Fusion //getRoomInfoBusy = false; //_DynFusion.API.EISC.BooleanInput[Constants.GetRoomInfo].BoolValue = getRoomInfoBusy; } - } /// @@ -859,130 +886,127 @@ namespace PepperDash.Essentials.Core.Fusion /// /// /// - protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, SigEventArgs args) + protected void FusionRoomSchedule_DeviceExtenderSigChange(DeviceExtender currentDeviceExtender, + SigEventArgs args) { - Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); + Debug.Console(2, this, "Scehdule Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, + args.Sig.Name, args.Sig.StringValue); - if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) - { - try - { - ScheduleResponse scheduleResponse = new ScheduleResponse(); + if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.ScheduleResponse) + { + try + { + var scheduleResponse = new ScheduleResponse(); - XmlDocument message = new XmlDocument(); + var message = new XmlDocument(); - message.LoadXml(args.Sig.StringValue); + message.LoadXml(args.Sig.StringValue); - var response = message["ScheduleResponse"]; + var response = message["ScheduleResponse"]; - if (response != null) - { - // Check for push notification - if (response["RequestID"].InnerText == "RVRequest") - { - var action = response["Action"]; + if (response != null) + { + // Check for push notification + if (response["RequestID"].InnerText == "RVRequest") + { + var action = response["Action"]; - if (action.OuterXml.IndexOf("RequestSchedule") > -1) - { - PushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); - } - } - else // Not a push notification - { - CurrentSchedule = new RoomSchedule(); // Clear Current Schedule - CurrentMeeting = null; // Clear Current Meeting - NextMeeting = null; // Clear Next Meeting + if (action.OuterXml.IndexOf("RequestSchedule", StringComparison.Ordinal) > -1) + { + _pushNotificationTimer.Reset(PushNotificationTimeout, PushNotificationTimeout); + } + } + else // Not a push notification + { + _currentSchedule = new RoomSchedule(); // Clear Current Schedule + _currentMeeting = null; // Clear Current Meeting + _nextMeeting = null; // Clear Next Meeting - bool isNextMeeting = false; + var isNextMeeting = false; - foreach (XmlElement element in message.FirstChild.ChildNodes) - { - if (element.Name == "RequestID") - { - scheduleResponse.RequestID = element.InnerText; - } - else if (element.Name == "RoomID") - { - scheduleResponse.RoomID = element.InnerText; - } - else if (element.Name == "RoomName") - { - scheduleResponse.RoomName = element.InnerText; - } - else if (element.Name == "Event") - { - Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); + foreach (XmlElement element in message.FirstChild.ChildNodes) + { + if (element.Name == "RequestID") + { + scheduleResponse.RequestID = element.InnerText; + } + else if (element.Name == "RoomID") + { + scheduleResponse.RoomID = element.InnerText; + } + else if (element.Name == "RoomName") + { + scheduleResponse.RoomName = element.InnerText; + } + else if (element.Name == "Event") + { + Debug.Console(2, this, "Event Found:\n{0}", element.OuterXml); - XmlReader reader = new XmlReader(element.OuterXml); + var reader = new XmlReader(element.OuterXml); - Event tempEvent = new Event(); + var tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); - tempEvent = CrestronXMLSerialization.DeSerializeObject(reader); + scheduleResponse.Events.Add(tempEvent); - scheduleResponse.Events.Add(tempEvent); + // Check is this is the current event + if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now) + { + _currentMeeting = tempEvent; // Set Current Meeting + isNextMeeting = true; // Flag that next element is next meeting + } - // Check is this is the current event - if (tempEvent.dtStart <= DateTime.Now && tempEvent.dtEnd >= DateTime.Now) - { - CurrentMeeting = tempEvent; // Set Current Meeting - isNextMeeting = true; // Flag that next element is next meeting - } + if (isNextMeeting) + { + _nextMeeting = tempEvent; // Set Next Meeting + isNextMeeting = false; + } - if (isNextMeeting) - { - NextMeeting = tempEvent; // Set Next Meeting - isNextMeeting = false; - } + _currentSchedule.Meetings.Add(tempEvent); + } + } - CurrentSchedule.Meetings.Add(tempEvent); - } + PrintTodaysSchedule(); - } + if (!_isRegisteredForSchedulePushNotifications) + { + _pollTimer.Reset(SchedulePollInterval, SchedulePollInterval); + } - PrintTodaysSchedule(); - - if (!IsRegisteredForSchedulePushNotifications) - PollTimer.Reset(SchedulePollInterval, SchedulePollInterval); - - // Fire Schedule Change Event - var handler = ScheduleChange; - - if (handler != null) - { - handler(this, new ScheduleChangeEventArgs() { Schedule = CurrentSchedule }); - } - - } - } - - - - } - catch (Exception e) - { - Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); - } - } - else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) - { - Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, args.Sig.Name, args.Sig.StringValue); - } + // Fire Schedule Change Event + var handler = ScheduleChange; + if (handler != null) + { + handler(this, new ScheduleChangeEventArgs {Schedule = _currentSchedule}); + } + } + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error parsing ScheduleResponse: {0}", e); + } + } + else if (args.Sig == FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.CreateResponse) + { + Debug.Console(2, this, "Create Meeting Response Event: {0}\n Sig: {1}\nFusionResponse:\n{2}", args.Event, + args.Sig.Name, args.Sig.StringValue); + } } /// /// Prints today's schedule to console for debugging /// - void PrintTodaysSchedule() + private void PrintTodaysSchedule() { if (Debug.Level > 1) { - if (CurrentSchedule.Meetings.Count > 0) + if (_currentSchedule.Meetings.Count > 0) { Debug.Console(1, this, "Today's Schedule for '{0}'\n", Room.Name); - foreach (Event e in CurrentSchedule.Meetings) + foreach (var e in _currentSchedule.Meetings) { Debug.Console(1, this, "Subject: {0}", e.Subject); Debug.Console(1, this, "Organizer: {0}", e.Organizer); @@ -995,63 +1019,62 @@ namespace PepperDash.Essentials.Core.Fusion } } - protected virtual void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsRoomBase).SourceListKey); - if (dict != null) - { - // NEW PROCESS: - // Make these lists and insert the fusion attributes by iterating these - var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); - uint i = 1; - foreach (var kvp in setTopBoxes) - { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); - i = 1; - foreach (var kvp in discPlayers) - { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 5) // We only have five spots - break; - } - - var laptops = dict.Where(d => d.Value.SourceDevice is Devices.Laptop); - i = 1; - foreach (var kvp in laptops) - { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); - i++; - if (i > 10) // We only have ten spots??? - break; - } - - foreach (var kvp in dict) + protected virtual void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey(Room.SourceListKey); + if (dict != null) + { + // NEW PROCESS: + // Make these lists and insert the fusion attributes by iterating these + var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); + uint i = 1; + foreach (var kvp in setTopBoxes) { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) + TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + break; } } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsRoomBase).SourceListKey, Room.Key); - } - } + + var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); + i = 1; + foreach (var kvp in discPlayers) + { + TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 5) // We only have five spots + { + break; + } + } + + var laptops = dict.Where(d => d.Value.SourceDevice is Devices.Laptop); + i = 1; + foreach (var kvp in laptops) + { + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + i++; + if (i > 10) // We only have ten spots??? + { + break; + } + } + + foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType()) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true}; + usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + } + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + Room.SourceListKey, Room.Key); + } + } /// /// Collects usage data from source and sends to Fusion @@ -1059,24 +1082,31 @@ namespace PepperDash.Essentials.Core.Fusion /// /// protected void UsageTracker_DeviceUsageEnded(object sender, DeviceUsageEventArgs e) - { + { var deviceTracker = sender as UsageTracking; - var configDevice = ConfigReader.ConfigObject.Devices.Where(d => d.Key.Equals(deviceTracker.Parent)); + if (deviceTracker == null) + { + return; + } - string group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); + var group = ConfigReader.GetGroupForDeviceKey(deviceTracker.Parent.Key); - string currentMeetingId = "-"; + var currentMeetingId = "-"; - if (CurrentMeeting != null) - currentMeetingId = CurrentMeeting.MeetingID; + if (_currentMeeting != null) + { + currentMeetingId = _currentMeeting.MeetingID; + } //String Format: "USAGE||[Date YYYY-MM-DD]||[Time HH-mm-ss]||TIME||[Asset_Type]||[Asset_Name]||[Minutes_used]||[Asset_ID]||[Meeting_ID]" // [Asset_ID] property does not appear to be used in Crestron SSI examples. They are sending "-" instead so that's what is replicated here - string deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), - group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); + var deviceUsage = string.Format("USAGE||{0}||{1}||TIME||{2}||{3}||-||{4}||-||{5}||{6}||\r\n", + e.UsageEndTime.ToString("yyyy-MM-dd"), e.UsageEndTime.ToString("HH:mm:ss"), + @group, deviceTracker.Parent.Name, e.MinutesUsed, "-", currentMeetingId); - Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); + Debug.Console(1, this, "Device usage for: {0} ended at {1}. In use for {2} minutes", + deviceTracker.Parent.Name, e.UsageEndTime, e.MinutesUsed); FusionRoom.DeviceUsage.InputSig.StringValue = deviceUsage; @@ -1084,41 +1114,51 @@ namespace PepperDash.Essentials.Core.Fusion } - protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) - { - Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", - attrName, attrNum, pSrc.Key); - try - { - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); - // Need feedback when this source is selected - // Event handler, added below, will compare source changes with this sig dict - SourceToFeedbackSigs.Add(pSrc, sigD.InputSig); + protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc) + { + Debug.Console(2, this, "Creating attribute '{0}' with join {1} for source {2}", + attrName, attrNum, pSrc.Key); + try + { + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); + // Need feedback when this source is selected + // Event handler, added below, will compare source changes with this sig dict + _sourceToFeedbackSigs.Add(pSrc, sigD.InputSig); - // And respond to selection in Fusion - sigD.OutputSig.SetSigFalseAction(() => (Room as IRunRouteAction).RunRouteAction(routeKey, Room.SourceListKey)); - } - catch (Exception) - { - Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", attrNum, attrName, pSrc.Key); - } - } + // And respond to selection in Fusion + sigD.OutputSig.SetSigFalseAction(() => + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction(routeKey, Room.SourceListKey); + } + }); + } + catch (Exception) + { + Debug.Console(2, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING", + attrNum, attrName, pSrc.Key); + } + } - /// - /// - /// - void SetUpCommunitcationMonitors() - { + /// + /// + /// + private void SetUpCommunitcationMonitors() + { uint displayNum = 0; uint touchpanelNum = 0; uint xpanelNum = 0; - // Attach to all room's devices with monitors. - //foreach (var dev in DeviceManager.Devices) - foreach (var dev in DeviceManager.GetDevices()) - { - if (!(dev is ICommunicationMonitor)) - continue; + // Attach to all room's devices with monitors. + //foreach (var dev in DeviceManager.Devices) + foreach (var dev in DeviceManager.GetDevices()) + { + if (!(dev is ICommunicationMonitor)) + { + continue; + } string attrName = null; uint attrNum = 1; @@ -1141,7 +1181,9 @@ namespace PepperDash.Essentials.Core.Fusion attrNum = attrNum + touchpanelNum; if (attrNum > 10) + { continue; + } attrName = "Online - XPanel " + attrNum; attrNum += 160; @@ -1152,7 +1194,9 @@ namespace PepperDash.Essentials.Core.Fusion attrNum = attrNum + xpanelNum; if (attrNum > 10) + { continue; + } attrName = "Online - Touch Panel " + attrNum; attrNum += 150; @@ -1160,45 +1204,47 @@ namespace PepperDash.Essentials.Core.Fusion } } - //else - if (dev is DisplayBase) - { - attrNum = attrNum + displayNum; - if (attrNum > 10) - continue; - attrName = "Online - Display " + attrNum; - attrNum += 170; + //else + if (dev is DisplayBase) + { + attrNum = attrNum + displayNum; + if (attrNum > 10) + { + continue; + } + attrName = "Online - Display " + attrNum; + attrNum += 170; displayNum++; - } - //else if (dev is DvdDeviceBase) - //{ - // if (attrNum > 5) - // continue; - // attrName = "Device Ok - DVD " + attrNum; - // attrNum += 260; - //} - // add set top box + } + //else if (dev is DvdDeviceBase) + //{ + // if (attrNum > 5) + // continue; + // attrName = "Device Ok - DVD " + attrNum; + // attrNum += 260; + //} + // add set top box - // add Cresnet roll-up + // add Cresnet roll-up - // add DM-devices roll-up + // add DM-devices roll-up - if (attrName != null) - { - // Link comm status to sig and update - var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); - var smd = dev as ICommunicationMonitor; - sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; - smd.CommunicationMonitor.StatusChange += (o, a) => - { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); - } - } - } + if (attrName != null) + { + // Link comm status to sig and update + var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); + var smd = dev as ICommunicationMonitor; + sigD.InputSig.BoolValue = smd.CommunicationMonitor.Status == MonitorStatus.IsOk; + smd.CommunicationMonitor.StatusChange += + (o, a) => { sigD.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", dev.Key, attrName); + } + } + } - protected virtual void SetUpDisplay() - { + protected virtual void SetUpDisplay() + { try { //Setup Display Usage Monitoring @@ -1207,35 +1253,52 @@ namespace PepperDash.Essentials.Core.Fusion // Consider updating this in multiple display systems - foreach (DisplayBase display in displays) + foreach (var display in displays.Cast()) { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + display.UsageTracker = new UsageTracking(display) {UsageIsTracked = true}; + display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; } - var defaultDisplay = (Room as IHasDefaultDisplay).DefaultDisplay as DisplayBase; + var hasDefaultDisplay = Room as IHasDefaultDisplay; + if (hasDefaultDisplay == null) + { + return; + } + var defaultDisplay = hasDefaultDisplay.DefaultDisplay as DisplayBase; if (defaultDisplay == null) { Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); return; } - var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); - var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); + var dispPowerOnAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOn(); + } + }); + var dispPowerOffAction = new Action(b => + { + if (!b) + { + defaultDisplay.PowerOff(); + } + }); // Display to fusion room sigs FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; MapDisplayToRoomJoins(1, 158, defaultDisplay); - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + var deviceConfig = + ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); //Check for existing asset in GUIDs collection - var tempAsset = new FusionAsset(); + FusionAsset tempAsset; if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) { @@ -1244,11 +1307,13 @@ namespace PepperDash.Essentials.Core.Fusion else { // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), + defaultDisplay.Name, "Display", ""); FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); } - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", + tempAsset.InstanceId); dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; @@ -1257,13 +1322,14 @@ namespace PepperDash.Essentials.Core.Fusion { defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); if (defaultDisplay is IDisplayUsage) + { (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + } defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - } - // Use extension methods + // Use extension methods dispAsset.TrySetMakeModel(defaultDisplay); dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); } @@ -1271,323 +1337,397 @@ namespace PepperDash.Essentials.Core.Fusion { Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); } - - } + } /// /// Maps room attributes to a display at a specified index /// - /// - /// a + /// + /// + /// + /// a protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) { - string displayName = string.Format("Display {0} - ", displayIndex); + var displayName = string.Format("Display {0} - ", displayIndex); - if (display == (Room as IHasDefaultDisplay).DefaultDisplay) + var hasDefaultDisplay = Room as IHasDefaultDisplay; + if (hasDefaultDisplay == null || display != hasDefaultDisplay.DefaultDisplay) { - // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - defaultDisplayVolume.OutputSig.UserObject = new Action(b => (display as IBasicVolumeWithFeedback).SetVolume(b)); - (display as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); - - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - - - var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; - if (defaultTwoWayDisplay != null) + return; + } + // Display volume + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", + eSigIoMask.InputOutputSig); + defaultDisplayVolume.OutputSig.UserObject = new Action(b => + { + var basicVolumeWithFeedback = display as IBasicVolumeWithFeedback; + if (basicVolumeWithFeedback == null) { - defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - defaultTwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(defaultDisplayPowerOff.InputSig); + return; } - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as IRunRouteAction).RunRouteAction("roomOff", Room.SourceListKey); }); ; + basicVolumeWithFeedback.SetVolume(b); + basicVolumeWithFeedback.VolumeLevelFeedback.LinkInputSig(defaultDisplayVolume.InputSig); + }); + + + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On", + eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => + { + if (!b) + { + display.PowerOn(); + } + }); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off", + eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => + { + if (!b) + { + display.PowerOff(); + } + }); + + + var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; + if (defaultTwoWayDisplay != null) + { + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + defaultTwoWayDisplay.PowerIsOnFeedback.LinkComplementInputSig(defaultDisplayPowerOff.InputSig); } + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8, + displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => + { + if (!b) + { + var runRouteAction = Room as IRunRouteAction; + if (runRouteAction != null) + { + runRouteAction.RunRouteAction("roomOff", Room.SourceListKey); + } + } + }); } - void SetUpError() - { - // Roll up ALL device errors - ErrorMessageRollUp = new StatusMonitorCollection(this); - foreach (var dev in DeviceManager.GetDevices()) - { - var md = dev as ICommunicationMonitor; - if (md != null) - { - ErrorMessageRollUp.AddMonitor(md.CommunicationMonitor); - Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", md.CommunicationMonitor.Parent.Key); - } - } - ErrorMessageRollUp.Start(); - FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; - ErrorMessageRollUp.StatusChange += (o, a) => - { - FusionRoom.ErrorMessage.InputSig.StringValue = ErrorMessageRollUp.Message; - }; - - } + private void SetUpError() + { + // Roll up ALL device errors + _errorMessageRollUp = new StatusMonitorCollection(this); + foreach (var dev in DeviceManager.GetDevices()) + { + var md = dev as ICommunicationMonitor; + if (md != null) + { + _errorMessageRollUp.AddMonitor(md.CommunicationMonitor); + Debug.Console(2, this, "Adding '{0}' to room's overall error monitor", + md.CommunicationMonitor.Parent.Key); + } + } + _errorMessageRollUp.Start(); + FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; + _errorMessageRollUp.StatusChange += + (o, a) => { FusionRoom.ErrorMessage.InputSig.StringValue = _errorMessageRollUp.Message; }; + } /// /// Sets up a local occupancy sensor, such as one attached to a Fusion Scheduling panel. The occupancy status of the room will be read from Fusion /// - void SetUpLocalOccupancy() + private void SetUpLocalOccupancy() { RoomIsOccupiedFeedback = new BoolFeedback(RoomIsOccupiedFeedbackFunc); - FusionRoom.FusionAssetStateChange += new FusionAssetStateEventHandler(FusionRoom_FusionAssetStateChange); + FusionRoom.FusionAssetStateChange += FusionRoom_FusionAssetStateChange; // Build Occupancy Asset? // Link sigs? //Room.SetRoomOccupancy(this as IOccupancyStatusProvider, 0); - - } - void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) + private void FusionRoom_FusionAssetStateChange(FusionBase device, FusionAssetStateEventArgs args) { - if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId) + if (args.EventId == FusionAssetEventId.RoomOccupiedReceivedEventId || + args.EventId == FusionAssetEventId.RoomUnoccupiedReceivedEventId) + { RoomIsOccupiedFeedback.FireUpdate(); - + } } /// /// Sets up remote occupancy that will relay the occupancy status determined by local system devices to Fusion /// - void SetUpRemoteOccupancy() - { - + private void SetUpRemoteOccupancy() + { // Need to have the room occupancy object first and somehow determine the slot number of the Occupancy asset but will not be able to use the UID from config likely. // Consider defining an object just for Room Occupancy (either eAssetType.Occupancy Sensor (local) or eAssetType.RemoteOccupancySensor (from Fusion sched. panel)) and reserving slot 4 for that asset (statics would start at 5) //if (Room.OccupancyObj != null) //{ - var tempOccAsset = GUIDs.OccupancyAsset; - - if(tempOccAsset == null) - { - FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); - tempOccAsset = FusionOccSensor; - } + var tempOccAsset = _guiDs.OccupancyAsset; - var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, "Occupancy Sensor", tempOccAsset.InstanceId); + if (tempOccAsset == null) + { + FusionOccSensor = new FusionOccupancySensorAsset(eAssetType.OccupancySensor); + tempOccAsset = FusionOccSensor; + } - occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + var occSensorAsset = FusionRoom.CreateOccupancySensorAsset(tempOccAsset.SlotNumber, tempOccAsset.Name, + "Occupancy Sensor", tempOccAsset.InstanceId); - var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); - - // Tie to method on occupancy object - //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); + occSensorAsset.RoomOccupied.AddSigToRVIFile = true; + + //var occSensorShutdownMinutes = FusionRoom.CreateOffsetUshortSig(70, "Occ Shutdown - Minutes", eSigIoMask.InputOutputSig); + + // Tie to method on occupancy object + //occSensorShutdownMinutes.OutputSig.UserObject(new Action(ushort)(b => Room.OccupancyObj.SetShutdownMinutes(b)); - RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); - Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); - Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; - RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); - + RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString); + Room.RoomOccupancy.RoomIsOccupiedFeedback.LinkInputSig(occSensorAsset.RoomOccupied.InputSig); + Room.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange; + RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig); + //} } - void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e) + private void RoomIsOccupiedFeedback_OutputChange(object sender, FeedbackEventArgs e) { _roomOccupancyRemoteString = String.Format(RemoteOccupancyXml, e.BoolValue ? "Occupied" : "Unoccupied"); RoomOccupancyRemoteStringFeedback.FireUpdate(); } - /// - /// Helper to get the number from the end of a device's key string - /// - /// -1 if no number matched - int ExtractNumberFromKey(string key) - { + /// + /// Helper to get the number from the end of a device's key string + /// + /// -1 if no number matched + private int ExtractNumberFromKey(string key) + { var capture = System.Text.RegularExpressions.Regex.Match(key, @"\b(\d+)"); - if (!capture.Success) - return -1; - else return Convert.ToInt32(capture.Groups[1].Value); - } + if (!capture.Success) + { + return -1; + } + return Convert.ToInt32(capture.Groups[1].Value); + } - /// - /// Event handler for when room source changes - /// - protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type) - { - // Handle null. Nothing to do when switching from or to null - if (info == null || info.SourceDevice == null) - return; + /// + /// Event handler for when room source changes + /// + protected void Room_CurrentSourceInfoChange(SourceListItem info, ChangeType type) + { + // Handle null. Nothing to do when switching from or to null + if (info == null || info.SourceDevice == null) + { + return; + } - var dev = info.SourceDevice; - if (type == ChangeType.WillChange) - { - if (SourceToFeedbackSigs.ContainsKey(dev)) - SourceToFeedbackSigs[dev].BoolValue = false; - } - else - { - if (SourceToFeedbackSigs.ContainsKey(dev)) - SourceToFeedbackSigs[dev].BoolValue = true; + var dev = info.SourceDevice; + if (type == ChangeType.WillChange) + { + if (_sourceToFeedbackSigs.ContainsKey(dev)) + { + _sourceToFeedbackSigs[dev].BoolValue = false; + } + } + else + { + if (_sourceToFeedbackSigs.ContainsKey(dev)) + { + _sourceToFeedbackSigs[dev].BoolValue = true; + } //var name = (room == null ? "" : room.Name); - CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; - } - } + CurrentRoomSourceNameSig.InputSig.StringValue = info.SourceDevice.Name; + } + } - protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) - { + protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args) + { + // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, + // even though they all contain sigs. - // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, - // even though they all contain sigs. + var sigData = args.UserConfiguredSigDetail as BooleanSigDataFixedName; - var sigData = (args.UserConfiguredSigDetail as BooleanSigDataFixedName); - if (sigData != null) - { - var outSig = sigData.OutputSig; - if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.BoolValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.UShortValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.StringValue); - return; - } + BoolOutputSig outSig; + if (sigData != null) + { + outSig = sigData.OutputSig; + if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.BoolValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.UShortValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.StringValue); + } + return; + } - var attrData = (args.UserConfiguredSigDetail as BooleanSigData); - if (attrData != null) - { - var outSig = attrData.OutputSig; - if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.BoolValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.UShortValue); - else if (outSig.UserObject is Action) - (outSig.UserObject as Action).Invoke(outSig.StringValue); - return; - } - - } - } + var attrData = (args.UserConfiguredSigDetail as BooleanSigData); + if (attrData == null) + { + return; + } + outSig = attrData.OutputSig; + if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.BoolValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.UShortValue); + } + else if (outSig.UserObject is Action) + { + (outSig.UserObject as Action).Invoke(outSig.StringValue); + } + } + } - public static class FusionRoomExtensions - { - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.Bool, number, name, mask); - return fr.UserDefinedBooleanSigDetails[number]; - } + public static class FusionRoomExtensions + { + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.Bool, number, name, mask); + return fr.UserDefinedBooleanSigDetails[number]; + } - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.UShort, number, name, mask); - return fr.UserDefinedUShortSigDetails[number]; - } + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.UShort, number, name, mask); + return fr.UserDefinedUShortSigDetails[number]; + } - /// - /// Creates and returns a fusion attribute. The join number will match the established Simpl - /// standard of 50+, and will generate a 50+ join in the RVI. It calls - /// FusionRoom.AddSig with join number - 49 - /// - /// The new attribute - public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) - { - if (number < 50) throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); - number -= 49; - fr.AddSig(eSigType.String, number, name, mask); - return fr.UserDefinedStringSigDetails[number]; - } + /// + /// Creates and returns a fusion attribute. The join number will match the established Simpl + /// standard of 50+, and will generate a 50+ join in the RVI. It calls + /// FusionRoom.AddSig with join number - 49 + /// + /// The new attribute + public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) + { + if (number < 50) + { + throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); + } + number -= 49; + fr.AddSig(eSigType.String, number, name, mask); + return fr.UserDefinedStringSigDetails[number]; + } - /// - /// Creates and returns a static asset - /// - /// the new asset - public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) - { + /// + /// Creates and returns a static asset + /// + /// the new asset + public static FusionStaticAsset CreateStaticAsset(this FusionRoom fr, uint number, string name, string type, + string instanceId) + { Debug.Console(0, "Adding Fusion Static Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); - fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); - return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; - } + fr.AddAsset(eAssetType.StaticAsset, number, name, type, instanceId); + return fr.UserConfigurableAssetDetails[number].Asset as FusionStaticAsset; + } - public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, string type, string instanceId) + public static FusionOccupancySensor CreateOccupancySensorAsset(this FusionRoom fr, uint number, string name, + string type, string instanceId) { - Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, instanceId); + Debug.Console(0, "Adding Fusion Occupancy Sensor Asset '{0}' to slot {1} with GUID: '{2}'", name, number, + instanceId); fr.AddAsset(eAssetType.OccupancySensor, number, name, type, instanceId); return fr.UserConfigurableAssetDetails[number].Asset as FusionOccupancySensor; } - } + } - //************************************************************************************************ - /// - /// Extensions to enhance Fusion room, asset and signal creation. - /// - public static class FusionStaticAssetExtensions - { - /// - /// Tries to set a Fusion asset with the make and model of a device. - /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. - /// Otherwise, does nothing. - /// - public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) - { - var mm = device as IMakeModel; - if (mm != null) - { - asset.ParamMake.Value = mm.DeviceMake; - asset.ParamModel.Value = mm.DeviceModel; - } - } + //************************************************************************************************ + /// + /// Extensions to enhance Fusion room, asset and signal creation. + /// + public static class FusionStaticAssetExtensions + { + /// + /// Tries to set a Fusion asset with the make and model of a device. + /// If the provided Device is IMakeModel, will set the corresponding parameters on the fusion static asset. + /// Otherwise, does nothing. + /// + public static void TrySetMakeModel(this FusionStaticAsset asset, Device device) + { + var mm = device as IMakeModel; + if (mm != null) + { + asset.ParamMake.Value = mm.DeviceMake; + asset.ParamModel.Value = mm.DeviceModel; + } + } - /// - /// Tries to attach the AssetError input on a Fusion asset to a Device's - /// CommunicationMonitor.StatusChange event. Does nothing if the device is not - /// IStatusMonitor - /// - /// - /// - public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) - { - if (device is ICommunicationMonitor) - { - var monitor = (device as ICommunicationMonitor).CommunicationMonitor; - monitor.StatusChange += (o, a) => - { - // Link connected and error inputs on asset - asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = a.Status.ToString(); - }; - // set current value - asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; - asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); - } - } - } + /// + /// Tries to attach the AssetError input on a Fusion asset to a Device's + /// CommunicationMonitor.StatusChange event. Does nothing if the device is not + /// IStatusMonitor + /// + /// + /// + public static void TryLinkAssetErrorToCommunication(this FusionStaticAsset asset, Device device) + { + if (device is ICommunicationMonitor) + { + var monitor = (device as ICommunicationMonitor).CommunicationMonitor; + monitor.StatusChange += (o, a) => + { + // Link connected and error inputs on asset + asset.Connected.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = a.Status.ToString(); + }; + // set current value + asset.Connected.InputSig.BoolValue = monitor.Status == MonitorStatus.IsOk; + asset.AssetError.InputSig.StringValue = monitor.Status.ToString(); + } + } + } public class RoomInformation { + public RoomInformation() + { + FusionCustomProperties = new List(); + } + public string ID { get; set; } public string Name { get; set; } public string Location { get; set; } @@ -1598,27 +1738,22 @@ namespace PepperDash.Essentials.Core.Fusion public string SubErrorMsg { get; set; } public string EmailInfo { get; set; } public List FusionCustomProperties { get; set; } - - public RoomInformation() - { - FusionCustomProperties = new List(); - } } + public class FusionCustomProperty { - public string ID { get; set; } - public string CustomFieldName { get; set; } - public string CustomFieldType { get; set; } - public string CustomFieldValue { get; set; } - public FusionCustomProperty() { - } public FusionCustomProperty(string id) { ID = id; } + + public string ID { get; set; } + public string CustomFieldName { get; set; } + public string CustomFieldType { get; set; } + public string CustomFieldValue { get; set; } } } \ No newline at end of file From 9c4650b4afd573ae05ae4fa85bf9f59b8b4c1f6d Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 16:32:06 -0700 Subject: [PATCH 23/89] fixes to get it to load --- .../Room/Config/EssentialsRoomConfig.cs | 4 +-- .../Room/Config/EssentialsTechRoomConfig.cs | 17 +++++++++-- .../Room/Types/EssentialsTechRoom.cs | 28 +++++++++---------- .../Global/Scheduler.cs | 8 ++++-- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 3aee1e20..79844ce4 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -31,7 +31,7 @@ namespace PepperDash.Essentials.Room.Config { return new EssentialsHuddleVtc1Room(roomConfig); } - if (typeName == "ddvc01Bridge") + if (typeName == "ddvc01bridge") { return new Device(roomConfig.Key, roomConfig.Name); // placeholder device that does nothing. } @@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Room.Config return new EssentialsDualDisplayRoom(roomConfig); } - return typeName != "techRoom" ? null : new EssentialsTechRoom(roomConfig); + return typeName != "techroom" ? null : new EssentialsTechRoom(roomConfig); } /// diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index e4b88e96..ccf9ec6d 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -1,22 +1,33 @@ using System.Collections.Generic; +using Newtonsoft.Json; namespace PepperDash.Essentials.Room.Config { public class EssentialsTechRoomConfig { + [JsonProperty("displays")] public List Displays; + + [JsonProperty("tuners")] public List Tuners; - public string ScheduleProviderKey; + + [JsonProperty("userPin")] public string UserPin; + + [JsonProperty("techPin")] public string TechPin; + + [JsonProperty("presetFileName")] public string PresetsFileName; - public List RoomScheduledEvents; + + [JsonProperty("scheduledEvents")] + public List ScheduledEvents; public EssentialsTechRoomConfig() { Displays = new List(); Tuners = new List(); - RoomScheduledEvents = new List(); + ScheduledEvents = new List(); } } } \ No newline at end of file diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index cb8ebfa1..c7f474eb 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -74,7 +74,7 @@ namespace PepperDash.Essentials private void CreateOrUpdateScheduledEvents() { - var eventsConfig = _config.RoomScheduledEvents; + var eventsConfig = _config.ScheduledEvents; GetOrCreateScheduleGroup(); @@ -127,11 +127,11 @@ namespace PepperDash.Essentials { //update config based on key of scheduleEvent GetOrCreateScheduleGroup(); - var existingEvent = _config.RoomScheduledEvents.FirstOrDefault(e => e.Key == scheduledEvent.Key); + var existingEvent = _config.ScheduledEvents.FirstOrDefault(e => e.Key == scheduledEvent.Key); if (existingEvent == null) { - _config.RoomScheduledEvents.Add(scheduledEvent); + _config.ScheduledEvents.Add(scheduledEvent); } //create or update event based on config @@ -144,7 +144,7 @@ namespace PepperDash.Essentials OnScheduledEventUpdate(); } - public void OnScheduledEventUpdate() + private void OnScheduledEventUpdate() { var handler = ScheduledEventsChanged; @@ -153,14 +153,14 @@ namespace PepperDash.Essentials return; } - handler(this, new ScheduledEventEventArgs {ScheduledEvents = _config.RoomScheduledEvents}); + handler(this, new ScheduledEventEventArgs {ScheduledEvents = _config.ScheduledEvents}); } public event EventHandler ScheduledEventsChanged; private void HandleScheduledEvent(ScheduledEvent schevent, ScheduledEventCommon.eCallbackReason type) { - var eventConfig = _config.RoomScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); + var eventConfig = _config.ScheduledEvents.FirstOrDefault(e => e.Key == schevent.Name); if (eventConfig == null) { @@ -221,42 +221,42 @@ namespace PepperDash.Essentials protected override Func IsWarmingFeedbackFunc { - get { throw new NotImplementedException(); } + get { return () => false; } } protected override Func IsCoolingFeedbackFunc { - get { throw new NotImplementedException(); } + get { return () => false; } } protected override Func OnFeedbackFunc { - get { throw new NotImplementedException(); } + get { return () => RoomPowerIsOn; } } protected override void EndShutdown() { - throw new NotImplementedException(); + } public override void SetDefaultLevels() { - throw new NotImplementedException(); + } public override void PowerOnToDefaultOrLastSource() { - throw new NotImplementedException(); + } public override bool RunDefaultPresentRoute() { - throw new NotImplementedException(); + return false; } public override void RoomVacatedForTimeoutPeriod(object o) { - throw new NotImplementedException(); + } #endregion diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 64ba57df..6544f1f4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -175,13 +175,15 @@ namespace PepperDash.Essentials.Core scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday); - scheduledEvent.Recurrence.Weekly(config.Days); - var eventTime = DateTime.Parse(config.Time); - if (DateTime.Now < eventTime) eventTime.AddDays(1); + if (DateTime.Now > eventTime) eventTime = eventTime.AddDays(1); scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime); + + scheduledEvent.Recurrence.Weekly(config.Days); + + } } } \ No newline at end of file From c4755f23cd7a80e70e749e184ca07e1d640a5860 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 16:58:33 -0700 Subject: [PATCH 24/89] added logic to subscribe to scheduled event --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 6 +++++- .../PepperDashEssentialsBase/Global/Scheduler.cs | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index c7f474eb..f6fe1b39 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -120,7 +120,9 @@ namespace PepperDash.Essentials _roomScheduledEventGroup.DeleteEvent(roomEvent); - SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); + var tempEvent = SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); + + tempEvent.UserCallBack += HandleScheduledEvent; } public void AddOrUpdateScheduledEvent(ScheduledEventConfig scheduledEvent) @@ -168,6 +170,8 @@ namespace PepperDash.Essentials return; } + Debug.Console(1, this, "Running actions for event {0}", schevent.Name); + if (eventConfig.Acknowledgeable) { schevent.Acknowledge(); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 6544f1f4..9f27a15c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -151,12 +151,12 @@ namespace PepperDash.Essentials.Core return evnt.Recurrence.RecurrenceDays == days; } - public static void CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group) + public static ScheduledEvent CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group) { if (group == null) { Debug.Console(0, "Unable to create event. Group is null"); - return; + return null; } var scheduledEvent = new ScheduledEvent(config.Key, group) { @@ -183,7 +183,7 @@ namespace PepperDash.Essentials.Core scheduledEvent.Recurrence.Weekly(config.Days); - + return scheduledEvent; } } } \ No newline at end of file From 099e387570ff9be4efef9029c1736728c0927c3f Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 7 Dec 2020 17:21:05 -0700 Subject: [PATCH 25/89] getting things in the right order for scheduling --- .../Room/Types/EssentialsTechRoom.cs | 6 ++--- .../Global/Scheduler.cs | 22 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index f6fe1b39..b9edbb70 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -102,7 +102,7 @@ namespace PepperDash.Essentials { if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(scheduledEvent.Name)) { - SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup, HandleScheduledEvent); return; } @@ -120,9 +120,7 @@ namespace PepperDash.Essentials _roomScheduledEventGroup.DeleteEvent(roomEvent); - var tempEvent = SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup); - - tempEvent.UserCallBack += HandleScheduledEvent; + SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup, HandleScheduledEvent); } public void AddOrUpdateScheduledEvent(ScheduledEventConfig scheduledEvent) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 9f27a15c..11232896 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -151,12 +151,12 @@ namespace PepperDash.Essentials.Core return evnt.Recurrence.RecurrenceDays == days; } - public static ScheduledEvent CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group) + public static void CreateEventFromConfig(ScheduledEventConfig config, ScheduledEventGroup group, ScheduledEvent.UserEventCallBack handler) { if (group == null) { Debug.Console(0, "Unable to create event. Group is null"); - return null; + return; } var scheduledEvent = new ScheduledEvent(config.Key, group) { @@ -164,14 +164,7 @@ namespace PepperDash.Essentials.Core Persistent = config.Persistent }; - if (config.Enable) - { - scheduledEvent.Resume(); - } - else - { - scheduledEvent.Pause(); - } + scheduledEvent.UserCallBack += handler; scheduledEvent.DateAndTime.SetFirstDayOfWeek(ScheduledEventCommon.eFirstDayOfWeek.Sunday); @@ -183,7 +176,14 @@ namespace PepperDash.Essentials.Core scheduledEvent.Recurrence.Weekly(config.Days); - return scheduledEvent; + if (config.Enable) + { + scheduledEvent.Enable(); + } + else + { + scheduledEvent.Disable(); + } } } } \ No newline at end of file From 2eaf21b1e0790178fb425de114a57fd198e79770 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 08:01:01 -0700 Subject: [PATCH 26/89] Fix event retrieval --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index b9edbb70..da362173 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -100,7 +100,7 @@ namespace PepperDash.Essentials private void CreateOrUpdateSingleEvent(ScheduledEventConfig scheduledEvent) { - if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(scheduledEvent.Name)) + if (!_roomScheduledEventGroup.ScheduledEvents.ContainsKey(scheduledEvent.Key)) { SchedulerUtilities.CreateEventFromConfig(scheduledEvent, _roomScheduledEventGroup, HandleScheduledEvent); return; From 2c36c0f2cbe87c893045268520f6c54a5a1491d8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 08:01:25 -0700 Subject: [PATCH 27/89] Run `GoWithLoad` command in separate thread when using donotloadonnextboot --- PepperDashEssentials/ControlSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 4e24de9f..58a545f2 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -53,7 +53,7 @@ namespace PepperDash.Essentials if (Debug.DoNotLoadOnNextBoot) { - CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file", + CrestronConsole.AddNewConsoleCommand(s => CrestronInvoke.BeginInvoke((o) => GoWithLoad()), "go", "Loads configuration file", ConsoleAccessLevelEnum.AccessOperator); } From a78b29b0a12087bb0c311db5abe09a35e0349654 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 08:01:58 -0700 Subject: [PATCH 28/89] #523 Add NullCheck and Debug message for com Port --- .../PepperDashEssentialsBase/Comm and IR/ComPortController.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs index ddb578d7..8d2f2db1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs @@ -56,6 +56,10 @@ namespace PepperDash.Essentials.Core private void RegisterAndConfigureComPort() { + if (Port == null) + { + Debug.Console(0,this,Debug.ErrorLogLevel.Error, "Configured com Port for this device does not exist."); + } if (Port.Parent is CrestronControlSystem) { var result = Port.Register(); From 4d67279827f0d436d177a8a850af4650aaf893b8 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 08:02:35 -0700 Subject: [PATCH 29/89] Log message to error log when configured com port doesn't exist --- .../PepperDashEssentialsBase/Comm and IR/CommFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs index a8fa67e4..8a5efe47 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/CommFactory.cs @@ -104,7 +104,7 @@ namespace PepperDash.Essentials.Core var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey); if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts) return dev.ComPorts[config.ControlPortNumber]; - Debug.Console(0, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); + Debug.Console(0,Debug.ErrorLogLevel.Notice, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber); return null; } From d990930b19565eac741169b2f6b60f6ba4a95879 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 11:05:52 -0700 Subject: [PATCH 30/89] #523 Add return statement --- .../PepperDashEssentialsBase/Comm and IR/ComPortController.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs index 8d2f2db1..75e9ae72 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Comm and IR/ComPortController.cs @@ -59,6 +59,7 @@ namespace PepperDash.Essentials.Core if (Port == null) { Debug.Console(0,this,Debug.ErrorLogLevel.Error, "Configured com Port for this device does not exist."); + return; } if (Port.Parent is CrestronControlSystem) { From 8feb7a142c2863424e9cd8f61cba05aff42ae0e7 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 11:06:06 -0700 Subject: [PATCH 31/89] #524 Fix Fusion Printing --- .../Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 9270ed37..dcc86635 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -276,7 +276,7 @@ namespace PepperDash.Essentials.Core.Fusion Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Fusion Guids successfully read from file: {0}", filePath); - Debug.Console(1, this, "\nRoom Name: {0}\nIPID: {1:x}\n RoomGuid: {2}", Room.Name, _ipId, RoomGuid); + Debug.Console(1, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _ipId, RoomGuid); foreach (var item in FusionStaticAssets) { From 9204ad2701268fb3f5d40a7331fe69be9170c833 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 12:48:26 -0700 Subject: [PATCH 32/89] #525 Add Rooms Array & LinkToRooms method --- .../Bridges/BridgeBase.cs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 151b5461..443a720e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -131,6 +131,16 @@ namespace PepperDash.Essentials.Core.Bridges } } + RegisterEisc(); + } + + private void RegisterEisc() + { + if (Eisc.Registered) + { + return; + } + var registerResult = Eisc.Register(); if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success) @@ -142,6 +152,27 @@ namespace PepperDash.Essentials.Core.Bridges Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "EISC registration successful"); } + public void LinkToRooms() + { + Debug.Console(1, this, "Linking Rooms..."); + + foreach (var room in PropertiesConfig.Rooms) + { + var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced; + + if (rm == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, + "Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey); + continue; + } + + rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this); + } + + RegisterEisc(); + } + /// /// Adds a join map /// @@ -290,6 +321,9 @@ namespace PepperDash.Essentials.Core.Bridges [JsonProperty("devices")] public List Devices { get; set; } + [JsonProperty("rooms")] + public List Rooms { get; set; } + public class ApiDevicePropertiesConfig { @@ -303,6 +337,18 @@ namespace PepperDash.Essentials.Core.Bridges public string JoinMapKey { get; set; } } + public class ApiRoomPropertiesConfig + { + [JsonProperty("roomKey")] + public string RoomKey { get; set; } + + [JsonProperty("joinStart")] + public uint JoinStart { get; set; } + + [JsonProperty("joinMapKey")] + public string JoinMapKey { get; set; } + } + } public class EiscApiAdvancedFactory : EssentialsDeviceFactory From 2e636082bb016bebb165ec55d8f58f464e17170c Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 12:50:55 -0700 Subject: [PATCH 33/89] #526 Add ITvPresetsProvider interface --- .../DeviceTypeInterfaces/ITvPresetsProvider.cs | 9 +++++++++ .../PepperDash_Essentials_Core.csproj | 1 + 2 files changed, 10 insertions(+) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs new file mode 100644 index 00000000..61b8ec09 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/ITvPresetsProvider.cs @@ -0,0 +1,9 @@ +using PepperDash.Essentials.Core.Presets; + +namespace PepperDash.Essentials.Core.DeviceTypeInterfaces +{ + public interface ITvPresetsProvider + { + DevicePresetsModel TvPresets { get; } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 1a2fc594..49f4b25a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -199,6 +199,7 @@ + From 748b1ca14767f92677c9c92d40fd61592f882c0b Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 12:51:20 -0700 Subject: [PATCH 34/89] Add config properties Implement ITvPresetsProvider --- PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs | 4 ++++ PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index ccf9ec6d..7b470574 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -23,6 +23,10 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("scheduledEvents")] public List ScheduledEvents; + [JsonProperty("isPrimary")] public bool IsPrimary; + + [JsonProperty("isTvPresetsProvider")] public bool IsTvPresetsProvider; + public EssentialsTechRoomConfig() { Displays = new List(); diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index da362173..e639a4a0 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -7,13 +7,14 @@ using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.Presets; using PepperDash.Essentials.Devices.Common; using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsTechRoom : EssentialsRoomBase + public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider { private readonly EssentialsTechRoomConfig _config; private readonly Dictionary _displays; @@ -41,7 +42,7 @@ namespace PepperDash.Essentials CreateOrUpdateScheduledEvents(); } - public DevicePresetsModel TunerPresets + public DevicePresetsModel TvPresets { get { return _tunerPresets; } } From 169e89774853612a367d7b58749a8baf0d45e5dd Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 12:54:35 -0700 Subject: [PATCH 35/89] Add CriticalSection for file ops --- .../Presets/DevicePresets.cs | 60 ++++++++++++------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index ccce6ea7..84b3cbd3 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -15,6 +15,7 @@ namespace PepperDash.Essentials.Core.Presets /// public class DevicePresetsModel : Device { + private CCriticalSection _fileOps = new CCriticalSection(); private readonly bool _initSuccess; /// @@ -101,27 +102,35 @@ namespace PepperDash.Essentials.Core.Presets public void LoadChannels() { - PresetsAreLoaded = false; try { - var pl = JsonConvert.DeserializeObject(File.ReadToEnd(_filePath, Encoding.ASCII)); - Name = pl.Name; - PresetsList = pl.Channels; - } - catch (Exception e) - { - Debug.Console(2, this, - "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", - _filePath, e.Message); - // Just save a default empty list - PresetsList = new List(); - } - PresetsAreLoaded = true; + _fileOps.Enter(); + PresetsAreLoaded = false; + try + { + var pl = JsonConvert.DeserializeObject(File.ReadToEnd(_filePath, Encoding.ASCII)); + Name = pl.Name; + PresetsList = pl.Channels; + } + catch (Exception e) + { + Debug.Console(2, this, + "LoadChannels: Error reading presets file. These presets will be empty:\r '{0}'\r Error:{1}", + _filePath, e.Message); + // Just save a default empty list + PresetsList = new List(); + } + PresetsAreLoaded = true; - var handler = PresetsLoaded; - if (handler != null) + var handler = PresetsLoaded; + if (handler != null) + { + handler(this, EventArgs.Empty); + } + } + finally { - handler(this, EventArgs.Empty); + _fileOps.Leave(); } } @@ -209,12 +218,21 @@ namespace PepperDash.Essentials.Core.Presets private void SavePresets() { - var json = JsonConvert.SerializeObject(PresetsList); - - using (var file = File.Open(_filePath, FileMode.Truncate)) + try { - file.Write(json, Encoding.UTF8); + _fileOps.Enter(); + var json = JsonConvert.SerializeObject(PresetsList); + + using (var file = File.Open(_filePath, FileMode.Truncate)) + { + file.Write(json, Encoding.UTF8); + } } + finally + { + _fileOps.Leave(); + } + } private void Pulse(Action act) From 56cf54a6443b521c3e3b6be7b26db51eb289a9e1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 16:24:03 -0700 Subject: [PATCH 36/89] Added LinkRooms as a PostActivationAction --- .../Bridges/BridgeBase.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 443a720e..86db81ba 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -99,12 +99,19 @@ namespace PepperDash.Essentials.Core.Bridges Eisc.SigChange += Eisc_SigChange; AddPostActivationAction(LinkDevices); + AddPostActivationAction(LinkRooms); } private void LinkDevices() { Debug.Console(1, this, "Linking Devices..."); + if (PropertiesConfig.Devices == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "No devices linked to this bridge"); + return; + } + foreach (var d in PropertiesConfig.Devices) { var device = DeviceManager.GetDeviceForKey(d.DeviceKey); @@ -152,10 +159,16 @@ namespace PepperDash.Essentials.Core.Bridges Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "EISC registration successful"); } - public void LinkToRooms() + public void LinkRooms() { Debug.Console(1, this, "Linking Rooms..."); + if (PropertiesConfig.Rooms == null) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "No rooms linked to this bridge."); + return; + } + foreach (var room in PropertiesConfig.Rooms) { var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced; From b09c1517385cd270b07eca21658810f304debd90 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 16:24:22 -0700 Subject: [PATCH 37/89] Added logic to send presets to far end --- .../DeviceTypeInterfaces/INumeric.cs | 4 +-- .../Presets/DevicePresets.cs | 29 ++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs index 0294a0b5..62ea8b3f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/DeviceTypeInterfaces/INumeric.cs @@ -1,5 +1,5 @@ using Crestron.SimplSharpPro.DeviceSupport; - +using PepperDash.Core; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.SmartObjects; @@ -8,7 +8,7 @@ namespace PepperDash.Essentials.Core /// /// /// - public interface INumericKeypad + public interface INumericKeypad:IKeyed { void Digit0(bool pressRelease); void Digit1(bool pressRelease); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 84b3cbd3..55522cf0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -7,6 +7,7 @@ using Newtonsoft.Json; using PepperDash.Core; //using SSMono.IO; +using PepperDash.Core.WebApi.Presets; namespace PepperDash.Essentials.Core.Presets { @@ -15,9 +16,13 @@ namespace PepperDash.Essentials.Core.Presets /// public class DevicePresetsModel : Device { - private CCriticalSection _fileOps = new CCriticalSection(); + public delegate void PresetChangedCallback(ISetTopBoxNumericKeypad device, string channel); + + private readonly CCriticalSection _fileOps = new CCriticalSection(); private readonly bool _initSuccess; + private ISetTopBoxNumericKeypad _setTopBox; + /// /// The methods on the STB device to call when dialing /// @@ -32,6 +37,8 @@ namespace PepperDash.Essentials.Core.Presets { try { + _setTopBox = setTopBox; + // Grab the digit functions from the device // If any fail, the whole thing fails peacefully _dialFunctions = new Dictionary>(10) @@ -76,6 +83,8 @@ namespace PepperDash.Essentials.Core.Presets _initSuccess = true; } + public event PresetChangedCallback PresetChanged; + public int PulseTime { get; set; } public int DigitSpacingMs { get; set; } public bool PresetsAreLoaded { get; private set; } @@ -172,6 +181,10 @@ namespace PepperDash.Essentials.Core.Presets } _dialIsRunning = false; }); + + if (_setTopBox == null) return; + + OnPresetChanged(_setTopBox, chanNum); } public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox) @@ -201,9 +214,23 @@ namespace PepperDash.Essentials.Core.Presets _enterFunction = setTopBox.KeypadEnter; + OnPresetChanged(setTopBox, chanNum); + Dial(chanNum); } + private void OnPresetChanged(ISetTopBoxNumericKeypad setTopBox, string channel) + { + var handler = PresetChanged; + + if (handler == null) + { + return; + } + + handler(setTopBox, channel); + } + public void UpdatePreset(int index, PresetChannel preset) { if (index >= PresetsList.Count) From 7e8f216afb77cbfa393901e47948723cf2884267 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 16:24:35 -0700 Subject: [PATCH 38/89] added logic to send presets to far end --- .../Room/Types/EssentialsTechRoom.cs | 119 ++++++++++++++++-- 1 file changed, 110 insertions(+), 9 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index e639a4a0..74eaac6a 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -3,9 +3,12 @@ using System.Collections.Generic; using System.Linq; using Crestron.SimplSharp; using Crestron.SimplSharp.Scheduler; +using Crestron.SimplSharpPro.DeviceSupport; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.Presets; @@ -14,7 +17,7 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider + public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider, IBridgeAdvanced { private readonly EssentialsTechRoomConfig _config; private readonly Dictionary _displays; @@ -22,6 +25,7 @@ namespace PepperDash.Essentials private readonly DevicePresetsModel _tunerPresets; private readonly Dictionary _tuners; + private Dictionary _currentPresets; private ScheduledEventGroup _roomScheduledEventGroup; public EssentialsTechRoom(DeviceConfig config) : base(config) @@ -32,20 +36,21 @@ namespace PepperDash.Essentials _tunerPresets.LoadChannels(); + _tunerPresets.PresetChanged += TunerPresetsOnPresetChanged; + _tuners = GetDevices(_config.Tuners); _displays = GetDevices(_config.Displays); RoomPowerIsOnFeedback = new BoolFeedback(() => RoomPowerIsOn); + SetUpTunerPresetsFeedback(); + SubscribeToDisplayFeedbacks(); CreateOrUpdateScheduledEvents(); } - public DevicePresetsModel TvPresets - { - get { return _tunerPresets; } - } + public Dictionary CurrentPresetsFeedbacks { get; private set; } public Dictionary Tuners { @@ -64,6 +69,43 @@ namespace PepperDash.Essentials get { return _displays.All(kv => kv.Value.PowerIsOnFeedback.BoolValue); } } + #region ITvPresetsProvider Members + + public DevicePresetsModel TvPresets + { + get { return _tunerPresets; } + } + + #endregion + + private void TunerPresetsOnPresetChanged(ISetTopBoxNumericKeypad device, string channel) + { + if (!_currentPresets.ContainsKey(device.Key)) + { + return; + } + + _currentPresets[device.Key] = channel; + + if (!CurrentPresetsFeedbacks.ContainsKey(device.Key)) + { + CurrentPresetsFeedbacks[device.Key].FireUpdate(); + } + } + + private void SetUpTunerPresetsFeedback() + { + _currentPresets = new Dictionary(); + CurrentPresetsFeedbacks = new Dictionary(); + + foreach (var setTopBox in _tuners) + { + var tuner = setTopBox.Value; + _currentPresets.Add(tuner.Key, String.Empty); + CurrentPresetsFeedbacks.Add(tuner.Key, new StringFeedback(() => _currentPresets[tuner.Key])); + } + } + private void SubscribeToDisplayFeedbacks() { foreach (var display in _displays) @@ -239,17 +281,14 @@ namespace PepperDash.Essentials protected override void EndShutdown() { - } public override void SetDefaultLevels() { - } public override void PowerOnToDefaultOrLastSource() { - } public override bool RunDefaultPresentRoute() @@ -259,10 +298,72 @@ namespace PepperDash.Essentials public override void RoomVacatedForTimeoutPeriod(object o) { - } #endregion + + #region Implementation of IBridgeAdvanced + + public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + + var joinMap = new EssentialsTechRoomJoinMap(joinStart); + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!String.IsNullOrEmpty(joinMapSerialized)) + { + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + uint i; + if (_config.IsPrimary) + { + i = 0; + foreach (var feedback in CurrentPresetsFeedbacks) + { + feedback.Value.LinkInputSig(trilist.StringInput[(uint) (joinMap.CurrentPreset.JoinNumber + i)]); + i++; + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) + { + return; + } + + foreach (var feedback in CurrentPresetsFeedbacks) + { + feedback.Value.FireUpdate(); + } + }; + } + + i = 0; + foreach (var setTopBox in _tuners) + { + var tuner = setTopBox; + + trilist.SetStringSigAction(joinMap.CurrentPreset.JoinNumber + i, s => _tunerPresets.Dial(s, tuner.Value)); + } + } + + #endregion + + private class EssentialsTechRoomJoinMap : JoinMapBaseAdvanced + { + [JoinName("currentPreset")] + public JoinDataComplete CurrentPreset = new JoinDataComplete(new JoinData {JoinNumber = 1, JoinSpan = 16}, + new JoinMetadata {Description = "Current Tuner Preset", JoinType = eJoinType.Serial}); + + public EssentialsTechRoomJoinMap(uint joinStart) : base(joinStart, typeof(EssentialsTechRoomJoinMap)) + { + } + } } public class ScheduledEventEventArgs : EventArgs From 6e4fa48b9d1b16dd8e5bdb2698f9c5da80c9665e Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 8 Dec 2020 16:34:00 -0700 Subject: [PATCH 39/89] rearrange message formatting --- .../PepperDashEssentialsBase/Plugins/PluginLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs index f0a7aeeb..01da1c54 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Plugins/PluginLoader.cs @@ -410,7 +410,7 @@ namespace PepperDash.Essentials if (!passed) { Debug.Console(0, Debug.ErrorLogLevel.Error, - "**********\r\nPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}\r\n**********", + "\r\n********************\r\n\tPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}\r\n********************", plugin.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); return; } From 3e5685994341174ecd36d48e256e8594a9863316 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 9 Dec 2020 15:14:52 -0700 Subject: [PATCH 40/89] add UpdatePresets method --- .../PepperDashEssentialsBase/Presets/DevicePresets.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 55522cf0..85e3038e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -243,6 +243,13 @@ namespace PepperDash.Essentials.Core.Presets SavePresets(); } + public void UpdatePresets(List presets) + { + PresetsList = presets; + + SavePresets(); + } + private void SavePresets() { try From f8ae6264f7e5bc1d62fa9a89242a93422c4d6c77 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 9 Dec 2020 16:18:21 -0700 Subject: [PATCH 41/89] add some debug statements and fix presets file loading --- PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs | 2 +- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 2 +- .../PepperDashEssentialsBase/Presets/DevicePresets.cs | 4 ++++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index 7b470574..292a6b7c 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -17,7 +17,7 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("techPin")] public string TechPin; - [JsonProperty("presetFileName")] + [JsonProperty("presetsFileName")] public string PresetsFileName; [JsonProperty("scheduledEvents")] diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 74eaac6a..c90e9414 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -34,7 +34,7 @@ namespace PepperDash.Essentials _tunerPresets = new DevicePresetsModel(String.Format("{0}-presets", config.Key), _config.PresetsFileName); - _tunerPresets.LoadChannels(); + _tunerPresets.SetFileName(_config.PresetsFileName); _tunerPresets.PresetChanged += TunerPresetsOnPresetChanged; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 85e3038e..6fb228a4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -106,6 +106,8 @@ namespace PepperDash.Essentials.Core.Presets public void SetFileName(string path) { _filePath = ListPathPrefix + path; + + Debug.Console(2, this, "Setting presets file path to {0}", _filePath); LoadChannels(); } @@ -114,6 +116,8 @@ namespace PepperDash.Essentials.Core.Presets try { _fileOps.Enter(); + + Debug.Console(2, this, "Loading presets from {0}", _filePath); PresetsAreLoaded = false; try { From 1ee87c0499964a2f1bb9034afe4358e8328a0084 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 9 Dec 2020 16:37:14 -0700 Subject: [PATCH 42/89] add some debug statements and fix presets file loading --- .../Presets/PresetChannel.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs index b9650e60..259ccbb9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/PresetChannel.cs @@ -10,19 +10,22 @@ namespace PepperDash.Essentials.Core.Presets public class PresetChannel { - [JsonProperty(Required = Required.Always)] + [JsonProperty(Required = Required.Always,PropertyName = "name")] public string Name { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "iconUrl")] public string IconUrl { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "channel")] public string Channel { get; set; } } public class PresetsList { - [JsonProperty(Required=Required.Always)] + [JsonProperty(Required=Required.Always,PropertyName = "name")] public string Name { get; set; } - [JsonProperty(Required = Required.Always)] + + [JsonProperty(Required = Required.Always, PropertyName = "channels")] public List Channels { get; set; } } } \ No newline at end of file From 01ddf1721ca8a944a7106978197b1d1d17241caa Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 11 Dec 2020 15:42:38 -0700 Subject: [PATCH 43/89] add method to get scheduled events --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 5 +++++ .../PepperDashEssentialsBase/Global/Scheduler.cs | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index c90e9414..3dc45c3a 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -187,6 +187,11 @@ namespace PepperDash.Essentials OnScheduledEventUpdate(); } + public List GetScheduledEvents() + { + return _config.ScheduledEvents ?? new List(); + } + private void OnScheduledEventUpdate() { var handler = ScheduledEventsChanged; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 11232896..192f7dd0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -170,7 +170,15 @@ namespace PepperDash.Essentials.Core var eventTime = DateTime.Parse(config.Time); - if (DateTime.Now > eventTime) eventTime = eventTime.AddDays(1); + if (DateTime.Now > eventTime) + { + eventTime = eventTime.AddDays(1); + } + + while (!config.Days.ToString().ToLower().Contains(eventTime.DayOfWeek.ToString().ToLower())) + { + eventTime = eventTime.AddDays(1); + } scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime); From eb114b4a951d25a16906c61efdbe6914c0c44844 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 15 Dec 2020 08:44:10 -0700 Subject: [PATCH 44/89] make Days enum serialize to string --- .../Room/Config/EssentialsRoomScheduledEventsConfig.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs index 366f453c..88ac7db9 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Crestron.SimplSharp.Scheduler; using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using PepperDash.Essentials.Core; namespace PepperDash.Essentials.Room.Config @@ -20,6 +21,7 @@ namespace PepperDash.Essentials.Room.Config public string Name; [JsonProperty("days")] + [JsonConverter(typeof(StringEnumConverter))] public ScheduledEventCommon.eWeekDays Days; [JsonProperty("time")] From a4a99f4a9b045f5660a7931ec32be2d906f03828 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 15 Dec 2020 09:47:08 -0700 Subject: [PATCH 45/89] remove JsonConverter Attribute --- .../Room/Config/EssentialsRoomScheduledEventsConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs index 88ac7db9..cd0c2586 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Config/EssentialsRoomScheduledEventsConfig.cs @@ -21,7 +21,6 @@ namespace PepperDash.Essentials.Room.Config public string Name; [JsonProperty("days")] - [JsonConverter(typeof(StringEnumConverter))] public ScheduledEventCommon.eWeekDays Days; [JsonProperty("time")] From d2c308c0092f1c9f735037a8ba2704de0f9c86e2 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 15 Dec 2020 09:47:35 -0700 Subject: [PATCH 46/89] Add methods & logic to make sure... ...day & time is in the list of recurrence days --- .../Global/Scheduler.cs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs index 192f7dd0..fb305172 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Global/Scheduler.cs @@ -1,10 +1,14 @@ using System; using System.Collections.Generic; +using System.Linq; using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; using Crestron.SimplSharp.Scheduler; using PepperDash.Core; +using PepperDash.Essentials.Core.Fusion; using PepperDash.Essentials.Room.Config; +using Activator = System.Activator; namespace PepperDash.Essentials.Core { @@ -175,9 +179,18 @@ namespace PepperDash.Essentials.Core eventTime = eventTime.AddDays(1); } - while (!config.Days.ToString().ToLower().Contains(eventTime.DayOfWeek.ToString().ToLower())) + Debug.Console(2, "[Scheduler] Current Date day of week: {0} recurrence days: {1}", eventTime.DayOfWeek, + config.Days); + + var dayOfWeekConverted = ConvertDayOfWeek(eventTime); + + Debug.Console(1, "[Scheduler] eventTime Day: {0}", dayOfWeekConverted); + + while (!dayOfWeekConverted.IsFlagSet(config.Days)) { eventTime = eventTime.AddDays(1); + + dayOfWeekConverted = ConvertDayOfWeek(eventTime); } scheduledEvent.DateAndTime.SetAbsoluteEventTime(eventTime); @@ -193,5 +206,28 @@ namespace PepperDash.Essentials.Core scheduledEvent.Disable(); } } + + private static ScheduledEventCommon.eWeekDays ConvertDayOfWeek(DateTime eventTime) + { + return (ScheduledEventCommon.eWeekDays) Enum.Parse(typeof(ScheduledEventCommon.eWeekDays), eventTime.DayOfWeek.ToString(), true); + } + + private static bool IsFlagSet(this T value, T flag) where T : struct + { + CheckIsEnum(true); + + var lValue = Convert.ToInt64(value); + var lFlag = Convert.ToInt64(flag); + + return (lValue & lFlag) != 0; + } + + private static void CheckIsEnum(bool withFlags) + { + if (!typeof(T).IsEnum) + throw new ArgumentException(string.Format("Type '{0}' is not an enum", typeof(T).FullName)); + if (withFlags && !Attribute.IsDefined(typeof(T), typeof(FlagsAttribute))) + throw new ArgumentException(string.Format("Type '{0}' doesn't have the 'Flags' attribute", typeof(T).FullName)); + } } } \ No newline at end of file From 91eec8c258cc7f1446676e8719e1594f375449e9 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 15 Dec 2020 16:43:09 -0700 Subject: [PATCH 47/89] fix scheduled event saving --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 3dc45c3a..acd44710 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -170,12 +170,16 @@ namespace PepperDash.Essentials { //update config based on key of scheduleEvent GetOrCreateScheduleGroup(); - var existingEvent = _config.ScheduledEvents.FirstOrDefault(e => e.Key == scheduledEvent.Key); + var existingEventIndex = _config.ScheduledEvents.FindIndex((e) => e.Key == scheduledEvent.Key); - if (existingEvent == null) + if (existingEventIndex < 0) { _config.ScheduledEvents.Add(scheduledEvent); } + else + { + _config.ScheduledEvents[existingEventIndex] = scheduledEvent; + } //create or update event based on config CreateOrUpdateSingleEvent(scheduledEvent); From 1cad1976eedb719fe926318f911f317eac0b686d Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Wed, 16 Dec 2020 14:39:53 -0500 Subject: [PATCH 48/89] #499 Adds interfaces ILogStrings and ILogStringsWithLevel to PepperDash_Essentials_Core.Interfaces --- .../Interfaces/ILogStrings.cs | 17 +++++++++++++++++ .../Interfaces/ILogStringsWithLevel.cs | 18 ++++++++++++++++++ .../PepperDash_Essentials_Core.csproj | 2 ++ 3 files changed, 37 insertions(+) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs new file mode 100644 index 00000000..441292e8 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core.IKeyed; + +namespace PepperDash_Essentials_Core.Interfaces +{ + public interface ILogStrings : IKeyed + { + /// + /// Defines a class that is capable of logging a string + /// + void SendToLog(IKeyed device, string logMessage); + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs new file mode 100644 index 00000000..636128c9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using PepperDash.Core; + +namespace PepperDash_Essentials_Core.Interfaces +{ + public interface ILogStringsWithLevel + { + /// + /// Defines a class that is capable of logging a string with an int level + /// + void SendToLog(IKeyed device, Debug.ErrorLogLevel level,string logMessage); + } + +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 6d4def73..39e68257 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -223,6 +223,8 @@ + + From fc91ba7c1e3a57cd9e01a9a9727f158632c1c4c8 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Wed, 16 Dec 2020 15:00:30 -0500 Subject: [PATCH 49/89] Fixes Namespace and Implements IKeyed in both interfaces. --- .../PepperDashEssentialsBase/Interfaces/ILogStrings.cs | 4 ++-- .../Interfaces/ILogStringsWithLevel.cs | 4 ++-- .../Essentials_DM/PepperDash_Essentials_DM.csproj | 6 +++--- .../Essentials Devices Common.csproj | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs index 441292e8..92557319 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStrings.cs @@ -3,9 +3,9 @@ using System.Collections.Generic; using System.Linq; using System.Text; using Crestron.SimplSharp; -using PepperDash.Core.IKeyed; +using PepperDash.Core; -namespace PepperDash_Essentials_Core.Interfaces +namespace PepperDash.Essentials.Core.Interfaces { public interface ILogStrings : IKeyed { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs index 636128c9..c43c4e6c 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Interfaces/ILogStringsWithLevel.cs @@ -5,9 +5,9 @@ using System.Text; using Crestron.SimplSharp; using PepperDash.Core; -namespace PepperDash_Essentials_Core.Interfaces +namespace PepperDash.Essentials.Core.Interfaces { - public interface ILogStringsWithLevel + public interface ILogStringsWithLevel : IKeyed { /// /// Defines a class that is capable of logging a string with an int level diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index 69045ea5..b1ea1b42 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -65,12 +65,12 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -79,7 +79,7 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj index 54d37613..bfee822e 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/Essentials Devices Common.csproj @@ -69,12 +69,12 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll False False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll False @@ -83,7 +83,7 @@ False - ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe + ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpPro.exe False From 66cd39c0138b0d92b9030fd30b7b96c3b236a953 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 16 Dec 2020 16:21:10 -0700 Subject: [PATCH 50/89] changed event names and added saved event --- .../Room/Types/EssentialsTechRoom.cs | 4 +-- .../Presets/DevicePresets.cs | 28 ++++++++++++++----- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index acd44710..535cab22 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -36,7 +36,7 @@ namespace PepperDash.Essentials _tunerPresets.SetFileName(_config.PresetsFileName); - _tunerPresets.PresetChanged += TunerPresetsOnPresetChanged; + _tunerPresets.PresetRecalled += TunerPresetsOnPresetRecalled; _tuners = GetDevices(_config.Tuners); _displays = GetDevices(_config.Displays); @@ -78,7 +78,7 @@ namespace PepperDash.Essentials #endregion - private void TunerPresetsOnPresetChanged(ISetTopBoxNumericKeypad device, string channel) + private void TunerPresetsOnPresetRecalled(ISetTopBoxNumericKeypad device, string channel) { if (!_currentPresets.ContainsKey(device.Key)) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 6fb228a4..d2724d77 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -16,12 +16,14 @@ namespace PepperDash.Essentials.Core.Presets /// public class DevicePresetsModel : Device { - public delegate void PresetChangedCallback(ISetTopBoxNumericKeypad device, string channel); + public delegate void PresetRecalledCallback(ISetTopBoxNumericKeypad device, string channel); + + public delegate void PresetsSavedCallback(List presets); private readonly CCriticalSection _fileOps = new CCriticalSection(); private readonly bool _initSuccess; - private ISetTopBoxNumericKeypad _setTopBox; + private readonly ISetTopBoxNumericKeypad _setTopBox; /// /// The methods on the STB device to call when dialing @@ -83,7 +85,8 @@ namespace PepperDash.Essentials.Core.Presets _initSuccess = true; } - public event PresetChangedCallback PresetChanged; + public event PresetRecalledCallback PresetRecalled; + public event PresetsSavedCallback PresetsSaved; public int PulseTime { get; set; } public int DigitSpacingMs { get; set; } @@ -188,7 +191,7 @@ namespace PepperDash.Essentials.Core.Presets if (_setTopBox == null) return; - OnPresetChanged(_setTopBox, chanNum); + OnPresetRecalled(_setTopBox, chanNum); } public void Dial(int presetNum, ISetTopBoxNumericKeypad setTopBox) @@ -218,14 +221,14 @@ namespace PepperDash.Essentials.Core.Presets _enterFunction = setTopBox.KeypadEnter; - OnPresetChanged(setTopBox, chanNum); + OnPresetRecalled(setTopBox, chanNum); Dial(chanNum); } - private void OnPresetChanged(ISetTopBoxNumericKeypad setTopBox, string channel) + private void OnPresetRecalled(ISetTopBoxNumericKeypad setTopBox, string channel) { - var handler = PresetChanged; + var handler = PresetRecalled; if (handler == null) { @@ -265,6 +268,8 @@ namespace PepperDash.Essentials.Core.Presets { file.Write(json, Encoding.UTF8); } + + } finally { @@ -273,6 +278,15 @@ namespace PepperDash.Essentials.Core.Presets } + private void OnPresetsSaved() + { + var handler = PresetsSaved; + + if (handler == null) return; + + handler(PresetsList); + } + private void Pulse(Action act) { act(true); From 695ff5487f4e5db7f45db1a01a295cfd56829a44 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Dec 2020 10:38:31 -0700 Subject: [PATCH 51/89] actually fire the PresetsSaved event --- .../PepperDashEssentialsBase/Presets/DevicePresets.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index d2724d77..85e5dfdb 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -248,6 +248,8 @@ namespace PepperDash.Essentials.Core.Presets PresetsList[index] = preset; SavePresets(); + + OnPresetsSaved(); } public void UpdatePresets(List presets) @@ -255,6 +257,8 @@ namespace PepperDash.Essentials.Core.Presets PresetsList = presets; SavePresets(); + + OnPresetsSaved(); } private void SavePresets() From 57ebd2b6086732e089e0eecab82ab4a6698dec0b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 17 Dec 2020 14:07:48 -0700 Subject: [PATCH 52/89] Adds IRunDirectAction to EssentialsTechRoom --- .../Room/Types/EssentialsTechRoom.cs | 55 +- .../Room/Interfaces.cs | 8 + .../VideoCodec/ZoomRoom/ResponseObjects.cs | 2666 ++++++++--------- 3 files changed, 1394 insertions(+), 1335 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 535cab22..515a4355 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -17,7 +17,7 @@ using PepperDash.Essentials.Room.Config; namespace PepperDash.Essentials { - public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider, IBridgeAdvanced + public class EssentialsTechRoom : EssentialsRoomBase, ITvPresetsProvider, IBridgeAdvanced, IRunDirectRouteAction { private readonly EssentialsTechRoomConfig _config; private readonly Dictionary _displays; @@ -28,6 +28,8 @@ namespace PepperDash.Essentials private Dictionary _currentPresets; private ScheduledEventGroup _roomScheduledEventGroup; + private GenericSource DummySource = new GenericSource("$off", "Dummy Source"); + public EssentialsTechRoom(DeviceConfig config) : base(config) { _config = config.Properties.ToObject(); @@ -48,6 +50,8 @@ namespace PepperDash.Essentials SubscribeToDisplayFeedbacks(); CreateOrUpdateScheduledEvents(); + + DeviceManager.AddDevice(DummySource); } public Dictionary CurrentPresetsFeedbacks { get; private set; } @@ -241,7 +245,7 @@ namespace PepperDash.Essentials { foreach (var display in _displays) { - display.Value.PowerOn(); + } } @@ -373,6 +377,53 @@ namespace PepperDash.Essentials { } } + + #region IRunDirectRouteAction Members + + private void RunDirectRoute(IRoutingOutputs source, IRoutingSink dest) + { + if (dest == null) + { + Debug.Console(1, this, "Cannot route, unknown destination '{0}'", dest.Key); + return; + } + + if (source.Key.Equals("$off", StringComparison.OrdinalIgnoreCase)) + { + dest.ReleaseRoute(); + if (dest is IHasPowerControl) + (dest as IHasPowerControl).PowerOff(); + } + else + { + if (source == null) + { + Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", source.Key, dest.Key); + return; + } + dest.ReleaseAndMakeRoute(source, eRoutingSignalType.Video); + } + } + + /// + /// Attempts to route directly between a source and destination + /// + /// + /// + public void RunDirectRoute(string sourceKey, string destinationKey) + { + IRoutingSink dest = null; + + dest = DeviceManager.GetDeviceForKey(destinationKey) as IRoutingSink; + + var source = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs; + + RunDirectRoute(source, dest); + } + + + + #endregion } public class ScheduledEventEventArgs : EventArgs diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs index 1743bdaa..82871228 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Room/Interfaces.cs @@ -41,6 +41,14 @@ namespace PepperDash.Essentials.Core } + /// + /// Simplified routing direct from source to destination + /// + public interface IRunDirectRouteAction + { + void RunDirectRoute(string sourceKey, string destinationKey); + } + /// /// For rooms that default presentation only routing /// diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs index 3be18c1d..36cce89f 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ResponseObjects.cs @@ -1,1335 +1,1335 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using Crestron.SimplSharp; - -using PepperDash.Core; -using PepperDash.Essentials.Devices.Common.Codec; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom -{ - public enum eZoomRoomResponseType - { - zEvent, - zStatus, - zConfiguration, - zCommand - } - - public abstract class NotifiableObject : INotifyPropertyChanged - { - #region INotifyPropertyChanged Members - - public event PropertyChangedEventHandler PropertyChanged; - - protected void NotifyPropertyChanged(string propertyName) - { - if (PropertyChanged != null) - { - PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); - } - } - - #endregion - } - - /// - /// Used to track the current status of a ZoomRoom - /// - public class ZoomRoomStatus - { - public zStatus.Login Login { get; set; } - public zStatus.SystemUnit SystemUnit { get; set; } - public zStatus.Phonebook Phonebook { get; set; } - public zStatus.Call Call { get; set; } - public zStatus.Capabilities Capabilities { get; set; } - public zStatus.Sharing Sharing { get; set; } - public zStatus.NumberOfScreens NumberOfScreens { get; set; } - public zStatus.Layout Layout { get; set; } - public zStatus.Video Video { get; set; } - public zStatus.CameraShare CameraShare { get; set; } - public List AudioInputs { get; set; } - public List AudioOuputs { get; set; } - public List Cameras { get; set; } - public zEvent.PhoneCallStatus PhoneCall { get; set; } - - public ZoomRoomStatus() - { - Login = new zStatus.Login(); - SystemUnit = new zStatus.SystemUnit(); - Phonebook = new zStatus.Phonebook(); - Call = new zStatus.Call(); - Capabilities = new zStatus.Capabilities(); - Sharing = new zStatus.Sharing(); - NumberOfScreens = new zStatus.NumberOfScreens(); - Layout = new zStatus.Layout(); - Video = new zStatus.Video(); - CameraShare = new zStatus.CameraShare(); - AudioInputs = new List(); - AudioOuputs = new List(); - Cameras = new List(); - PhoneCall = new zEvent.PhoneCallStatus(); - } - } - - /// - /// Used to track the current configuration of a ZoomRoom - /// - public class ZoomRoomConfiguration - { - public zConfiguration.Call Call { get; set; } - public zConfiguration.Audio Audio { get; set; } - public zConfiguration.Video Video { get; set; } - public zConfiguration.Client Client { get; set; } - public zConfiguration.Camera Camera { get; set; } - - public ZoomRoomConfiguration() - { - Call = new zConfiguration.Call(); - Audio = new zConfiguration.Audio(); - Video = new zConfiguration.Video(); - Client = new zConfiguration.Client(); +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec.ZoomRoom +{ + public enum eZoomRoomResponseType + { + zEvent, + zStatus, + zConfiguration, + zCommand + } + + public abstract class NotifiableObject : INotifyPropertyChanged + { + #region INotifyPropertyChanged Members + + public event PropertyChangedEventHandler PropertyChanged; + + protected void NotifyPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + { + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + #endregion + } + + /// + /// Used to track the current status of a ZoomRoom + /// + public class ZoomRoomStatus + { + public zStatus.Login Login { get; set; } + public zStatus.SystemUnit SystemUnit { get; set; } + public zStatus.Phonebook Phonebook { get; set; } + public zStatus.Call Call { get; set; } + public zStatus.Capabilities Capabilities { get; set; } + public zStatus.Sharing Sharing { get; set; } + public zStatus.NumberOfScreens NumberOfScreens { get; set; } + public zStatus.Layout Layout { get; set; } + public zStatus.Video Video { get; set; } + public zStatus.CameraShare CameraShare { get; set; } + public List AudioInputs { get; set; } + public List AudioOuputs { get; set; } + public List Cameras { get; set; } + public zEvent.PhoneCallStatus PhoneCall { get; set; } + + public ZoomRoomStatus() + { + Login = new zStatus.Login(); + SystemUnit = new zStatus.SystemUnit(); + Phonebook = new zStatus.Phonebook(); + Call = new zStatus.Call(); + Capabilities = new zStatus.Capabilities(); + Sharing = new zStatus.Sharing(); + NumberOfScreens = new zStatus.NumberOfScreens(); + Layout = new zStatus.Layout(); + Video = new zStatus.Video(); + CameraShare = new zStatus.CameraShare(); + AudioInputs = new List(); + AudioOuputs = new List(); + Cameras = new List(); + PhoneCall = new zEvent.PhoneCallStatus(); + } + } + + /// + /// Used to track the current configuration of a ZoomRoom + /// + public class ZoomRoomConfiguration + { + public zConfiguration.Call Call { get; set; } + public zConfiguration.Audio Audio { get; set; } + public zConfiguration.Video Video { get; set; } + public zConfiguration.Client Client { get; set; } + public zConfiguration.Camera Camera { get; set; } + + public ZoomRoomConfiguration() + { + Call = new zConfiguration.Call(); + Audio = new zConfiguration.Audio(); + Video = new zConfiguration.Video(); + Client = new zConfiguration.Client(); Camera = new zConfiguration.Camera(); - } - } - - /// - /// Represents a response from a ZoomRoom system - /// - public class Response - { - public Status Status { get; set; } - public bool Sync { get; set; } - [JsonProperty("topKey")] - public string TopKey { get; set; } - [JsonProperty("type")] - public string Type { get; set; } - - public Response() - { - Status = new Status(); - } - } - - public class Status - { - [JsonProperty("message")] - public string Message { get; set; } - [JsonProperty("state")] - public string State { get; set; } - } - - - /// - /// zStatus class stucture - /// - public class zStatus - { - public class Login - { - [JsonProperty("ZAAPI Release")] - public string ZAAPIRelease { get; set; } - [JsonProperty("Zoom Room Release")] - public string ZoomRoomRelease { get; set; } - } - - public class SystemUnit - { - [JsonProperty("email")] - public string Email { get; set; } - [JsonProperty("login_type")] - public string LoginType { get; set; } - [JsonProperty("meeting_number")] - public string MeetingNumber { get; set; } - [JsonProperty("platform")] - public string Platform { get; set; } - [JsonProperty("room_info")] - public RoomInfo RoomInfo { get; set; } - [JsonProperty("room_version")] - public string RoomVersion { get; set; } - - public SystemUnit() - { - RoomInfo = new RoomInfo(); - } - } - - public class RoomInfo - { - [JsonProperty("account_email")] - public string AccountEmail { get; set; } - [JsonProperty("display_version")] - public string DisplayVersion { get; set; } - [JsonProperty("is_auto_answer_enabled")] - public bool AutoAnswerIsEnabled { get; set; } - [JsonProperty("is_auto_answer_selected")] - public bool AutoAnswerIsSelected { get; set; } - [JsonProperty("room_name")] - public string RoomName { get; set; } - } - - public class CloudPbxInfo - { - [JsonProperty("company_number")] - public string CompanyNumber { get; set; } - [JsonProperty("extension")] - public string Extension { get; set; } - [JsonProperty("isValid")] - public bool IsValid { get; set; } - } - - public enum ePresence - { - PRESENCE_OFFLINE, - PRESENCE_ONLINE, - PRESENCE_AWAY, - PRESENCE_BUSY, - PRESENCE_DND - } - - public class Contact - { - [JsonProperty("avatarURL")] - public string AvatarURL { get; set; } - [JsonProperty("cloud_pbx_info")] - public CloudPbxInfo CloudPbxInfo { get; set; } - [JsonProperty("email")] - public string Email { get; set; } - [JsonProperty("firstName")] - public string FirstName { get; set; } - [JsonProperty("index")] - public int Index { get; set; } - [JsonProperty("isLegacy")] - public bool IsLegacy { get; set; } - [JsonProperty("isZoomRoom")] - public bool IsZoomRoom { get; set; } - [JsonProperty("jid")] - public string Jid { get; set; } - [JsonProperty("lastName")] - public string LastName { get; set; } - [JsonProperty("onDesktop")] - public bool OnDesktop { get; set; } - [JsonProperty("onMobile")] - public bool OnMobile { get; set; } - [JsonProperty("phoneNumber")] - public string PhoneNumber { get; set; } - [JsonProperty("presence")] - public ePresence Presence { get; set; } - [JsonProperty("presence_status")] - public int PresenceStatus { get; set; } - [JsonProperty("screenName")] - public string ScreenName { get; set; } - [JsonProperty("sip_phone_number")] - public string SipPhoneNumber { get; set; } - - - public Contact() - { - CloudPbxInfo = new CloudPbxInfo(); - } - } - - /// - /// Used to be able to inplement IInvitableContact on DirectoryContact - /// - public class ZoomDirectoryContact : DirectoryContact, IInvitableContact - { - - } - - public class Phonebook - { - [JsonProperty("Contacts")] - public List Contacts { get; set; } - - public Phonebook() - { - Contacts = new List(); - } - - /// - /// Converts from zStatus.Contact types to generic directory items - /// - /// - public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) - { - var directory = new CodecDirectory(); - - var folders = new List(); - - var roomFolder = new DirectoryFolder(); - - var contactFolder = new DirectoryFolder(); - - var contacts = new List(); - - // Check if there are any zoom rooms - var zoomRooms = zoomContacts.FindAll(c => c.IsZoomRoom); - - if (zoomRooms.Count > 0) - { - // If so, setup a rooms and contacts folder and add them. - roomFolder.Name = "Rooms"; - roomFolder.ParentFolderId = "root"; - roomFolder.FolderId = "rooms"; - - contactFolder.Name = "Contacts"; - contactFolder.ParentFolderId = "root"; - contactFolder.FolderId = "contacts"; - - folders.Add(roomFolder); - folders.Add(contactFolder); - - directory.AddFoldersToDirectory(folders); - } - - try - { - if (zoomContacts.Count == 0) return directory; - { - foreach (Contact c in zoomContacts) - { - var contact = new ZoomDirectoryContact {Name = c.ScreenName, ContactId = c.Jid}; - - if (folders.Count > 0) - { - contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; - } - - contacts.Add(contact); - } - - directory.AddContactsToDirectory(contacts); - } - } - catch (Exception e) - { - Debug.Console(1, "Error converting Zoom Phonebook results to generic: {0}", e); - } - - return directory; - } - } - - public enum eCallStatus - { - UNKNOWN, - NOT_IN_MEETING, - CONNECTING_MEETING, - IN_MEETING, - LOGGED_OUT - } - - public class ClosedCaption - { - public bool Available { get; set; } - } - - public class Call : NotifiableObject - { - private eCallStatus _status; - private List _participants; - - public bool IsInCall; - - public eCallStatus Status - { - get - { - return _status; - } - set - { - if (value != _status) - { - _status = value; - IsInCall = _status == eCallStatus.IN_MEETING || _status == eCallStatus.CONNECTING_MEETING; - NotifyPropertyChanged("Status"); - } - } - } - public ClosedCaption ClosedCaption { get; set; } - public List Participants - { - get - { - return _participants; - } - set - { - _participants = value; - NotifyPropertyChanged("Participants"); - } - } - public zEvent.SharingState Sharing { get; set; } - - public CallRecordInfo CallRecordInfo { get; set; } - - private zCommand.InfoResult _info; - - public zCommand.InfoResult Info - { - get - { - return _info; - } - set - { - _info = value; - NotifyPropertyChanged("Info"); - } - } - - public Call() - { - ClosedCaption = new ClosedCaption(); - Participants = new List(); - Sharing = new zEvent.SharingState(); - CallRecordInfo = new CallRecordInfo(); - Info = new zCommand.InfoResult(); - } - } - - public class Capabilities - { - public bool aec_Setting_Stored_In_ZR { get; set; } - public bool can_Dtmf_For_Invite_By_Phone { get; set; } - public bool can_Mute_On_Entry { get; set; } - public bool can_Ringing_In_Pstn_Call { get; set; } - public bool can_Switch_To_Specific_Camera { get; set; } - public bool is_Airhost_Disabled { get; set; } - public bool pstn_Call_In_Local_resentation { get; set; } - public bool support_Claim_Host { get; set; } - public bool support_Out_Room_Display { get; set; } - public bool support_Pin_And_Spotlight { get; set; } - public bool supports_Audio_Checkup { get; set; } - public bool supports_CheckIn { get; set; } - public bool supports_Cloud_PBX { get; set; } - public bool supports_Encrypted_Connection { get; set; } - public bool supports_Expel_User_Permanently { get; set; } - public bool supports_H323_DTMF { get; set; } - public bool supports_Hdmi_Cec_Control { get; set; } - public bool supports_Highly_Reverberant_Room { get; set; } - public bool supports_Loading_Contacts_Dynamically { get; set; } - public bool supports_Loading_Participants_Dynamically { get; set; } - public bool supports_Mic_Record_Test { get; set; } - public bool supports_Multi_Share { get; set; } - public bool supports_ShareCamera { get; set; } - public bool supports_Share_For_Floating_And_Content_Only { get; set; } - public bool supports_Sip_Call_out { get; set; } - public bool supports_Software_Audio_Processing { get; set; } - public bool supports_Web_Settings_Push { get; set; } - } - - public class Sharing : NotifiableObject - { - private string _dispState; - private string _password; - - public string directPresentationPairingCode { get; set; } - /// - /// Laptop client sharing key - /// - public string directPresentationSharingKey { get; set; } - public string dispState - { - get - { - return _dispState; - } - set - { - if (value != _dispState) - { - _dispState = value; - NotifyPropertyChanged("dispState"); - } - } - } - public bool isAirHostClientConnected { get; set; } - public bool isBlackMagicConnected { get; set; } - public bool isBlackMagicDataAvailable { get; set; } - public bool isDirectPresentationConnected { get; set; } - public bool isSharingBlackMagic { get; set; } - /// - /// IOS Airplay code - /// - public string password - { - get - { - return _password; - } - set - { - if (value != _password) - { - _password = value; - NotifyPropertyChanged("password"); - } - } - } - public string serverName { get; set; } - public string wifiName { get; set; } - } - - public class NumberOfScreens - { - [JsonProperty("NumberOfCECScreens")] - public int NumOfCECScreens { get; set; } - [JsonProperty("NumberOfScreens")] - public int NumOfScreens { get; set; } - } - - /// - /// AudioInputLine/AudioOutputLine/VideoCameraLine list item - /// - public class AudioVideoInputOutputLineItem - { - public string Alias { get; set; } - public string Name { get; set; } - public bool Selected { get; set; } - public bool combinedDevice { get; set; } - public string id { get; set; } - public bool manuallySelected { get; set; } - public int numberOfCombinedDevices { get; set; } - public int ptzComId { get; set; } - } - - public class Video - { - public bool Optimizable { get; set; } - } - - public class CameraShare : NotifiableObject - { - private bool _canControlCamera; - private bool _isSharing; - - [JsonProperty("can_Control_Camera")] - public bool CanControlCamera - { - get - { - return _canControlCamera; - } - set - { - if (value != _canControlCamera) - { - _canControlCamera = value; - NotifyPropertyChanged("CanControlCamera"); - } - } - } - public string id { get; set; } - public bool is_Mirrored { get; set; } - [JsonProperty("is_Sharing")] - public bool IsSharing - { - get - { - return _isSharing; - } - set - { - if (value != _isSharing) - { - _isSharing = value; - NotifyPropertyChanged("IsSharing"); - } - } - } - public int pan_Tilt_Speed { get; set; } - - } - - public class Layout - { - public bool can_Adjust_Floating_Video { get; set; } - public bool can_Switch_Floating_Share_Content { get; set; } - public bool can_Switch_Share_On_All_Screens { get; set; } - public bool can_Switch_Speaker_View { get; set; } - public bool can_Switch_Wall_View { get; set; } - public bool is_In_First_Page { get; set; } - public bool is_In_Last_Page { get; set; } - public bool is_supported { get; set; } - public int video_Count_In_Current_Page { get; set; } - public string video_type { get; set; } - } - - public class CallRecordInfo - { - public bool canRecord { get; set; } - public bool emailRequired { get; set; } - public bool amIRecording { get; set; } - public bool meetingIsBeingRecorded { get; set; } - } - } - - /// - /// zEvent Class Structure - /// - public class zEvent - { - public class NeedWaitForHost - { - public bool Wait { get; set; } - } - - public class IncomingCallIndication - { - public string callerJID { get; set; } - public string calleeJID { get; set; } - public string meetingID { get; set; } - public string password { get; set; } - public string meetingOption { get; set; } - public long MeetingNumber { get; set; } - public string callerName { get; set; } - public string avatarURL { get; set; } - public int lifeTime { get; set; } - public bool accepted { get; set; } - } - - public class CallConnectError - { - public int error_code { get; set; } - public string error_message { get; set; } - } - - public class CallDisconnect - { - public bool Successful - { - get - { - return success == "on"; - } - } - - public string success { get; set; } - } - - public class Layout - { - public bool Sharethumb { get; set; } - } - - public class Call - { - public Layout Layout { get; set; } - } - - public class Client - { - public Call Call { get; set; } - } - - public enum eSharingState - { - None, - Connecting, - Sending, - Receiving, - Send_Receiving - } - - public class SharingState : NotifiableObject - { - private bool _paused; - private eSharingState _state; - - public bool IsSharing; - - [JsonProperty("paused")] - public bool Paused - { - get - { - return _paused; - } - set - { - if (value != _paused) - { - _paused = value; - NotifyPropertyChanged("Paused"); - } - } - } - [JsonProperty("state")] - public eSharingState State - { - get - { - return _state; - } - set - { - if (value != _state) - { - _state = value; - IsSharing = _state == eSharingState.Sending; - NotifyPropertyChanged("State"); - } - } - } - } - - public class PinStatusOfScreenNotification - { - [JsonProperty("can_be_pinned")] - public bool CanBePinned { get; set; } - [JsonProperty("can_pin_share")] - public bool CanPinShare { get; set; } - [JsonProperty("pinned_share_source_id")] - public int PinnedShareSourceId { get; set; } - [JsonProperty("pinned_user_id")] - public int PinnedUserId { get; set; } - [JsonProperty("screen_index")] - public int ScreenIndex { get; set; } - [JsonProperty("screen_layout")] - public int ScreenLayout { get; set; } - [JsonProperty("share_source_type")] - public int ShareSourceType { get; set; } - [JsonProperty("why_cannot_pin_share")] - public string WhyCannotPinShare { get; set; } - } - - public class PhoneCallStatus:NotifiableObject - { - private bool _isIncomingCall; - private string _peerDisplayName; - private string _peerNumber; - - private bool _offHook; - - public string CallId { get; set; } - public bool IsIncomingCall { - get { return _isIncomingCall; } - set - { - if(value == _isIncomingCall) return; - - _isIncomingCall = value; - NotifyPropertyChanged("IsIncomingCall"); - } } - - public string PeerDisplayName - { - get { return _peerDisplayName; } - set - { - if (value == _peerDisplayName) return; - _peerDisplayName = value; - NotifyPropertyChanged("PeerDisplayName"); - } - } - - public string PeerNumber - { - get { return _peerNumber; } - set - { - if (value == _peerNumber) return; - - _peerNumber = value; - NotifyPropertyChanged("PeerNumber"); - } - } - - public string PeerUri { get; set; } - - private ePhoneCallStatus _status; - public ePhoneCallStatus Status - { - get { return _status; } - set - { - _status = value; - OffHook = _status == ePhoneCallStatus.PhoneCallStatus_Accepted || - _status == ePhoneCallStatus.PhoneCallStatus_InCall || - _status == ePhoneCallStatus.PhoneCallStatus_Init || - _status == ePhoneCallStatus.PhoneCallStatus_Ringing; - } - } - - public bool OffHook - { - get { return _offHook; } - set - { - if (value == _offHook) return; - - _offHook = value; - NotifyPropertyChanged("OffHook"); - } - } - } - - public enum ePhoneCallStatus - { - PhoneCallStatus_Ringing, - PhoneCallStatus_Terminated, - PhoneCallStatus_Accepted, - PhoneCallStatus_InCall, - PhoneCallStatus_Init, - } - } - - /// - /// zConfiguration class structure - /// - public class zConfiguration - { - public class Sharing - { - [JsonProperty("optimize_video_sharing")] - public bool OptimizeVideoSharing { get; set; } - } - - public class Camera : NotifiableObject - { - private bool _mute; - - public bool Mute - { - get { return _mute; } - set - { - Debug.Console(1, "Camera Mute response received: {0}", value); - - if (value == _mute) return; - - _mute = value; - NotifyPropertyChanged("Mute"); - } - } - } - - public class Microphone : NotifiableObject - { - private bool _mute; - - public bool Mute - { - get - { - return _mute; - } - set - { - if(value != _mute) - { - _mute = value; - NotifyPropertyChanged("Mute"); - } - } - } - } - - public enum eLayoutStyle - { - Gallery, - Speaker, - Strip, - ShareAll - } - - public enum eLayoutSize - { - Off, - Size1, - Size2, - Size3, - Strip - } - - public enum eLayoutPosition - { - Center, - Up, - Right, - UpRight, - Down, - DownRight, - Left, - UpLeft, - DownLeft - } - - public class Layout:NotifiableObject - { - public bool ShareThumb { get; set; } - public eLayoutStyle Style { get; set; } - public eLayoutSize Size { get; set; } - - private eLayoutPosition _position; - public eLayoutPosition Position { - get { return _position; } - set - { - _position = value; - NotifyPropertyChanged("Position"); - } } - } - - public class Lock - { - public bool Enable { get; set; } - } - - public class ClosedCaption - { - public bool Visible { get; set; } - public int FontSize { get; set; } - } - - public class MuteUserOnEntry - { - public bool Enable { get; set; } - } - - public class Call - { - public Sharing Sharing { get; set; } - public Camera Camera { get; set; } - public Microphone Microphone { get; set; } - public Layout Layout { get; set; } - public Lock Lock { get; set; } - public MuteUserOnEntry MuteUserOnEntry { get; set; } - public ClosedCaption ClosedCaption { get; set; } - - - public Call() - { - Sharing = new Sharing(); - Camera = new Camera(); - Microphone = new Microphone(); - Layout = new Layout(); - Lock = new Lock(); - MuteUserOnEntry = new MuteUserOnEntry(); - ClosedCaption = new ClosedCaption(); - } - } - - public class Audio - { - public Input Input { get; set; } - public Output Output { get; set; } - - public Audio() - { - Input = new Input(); - Output = new Output(); - } - } - - public class Input : Output - { - [JsonProperty("reduce_reverb")] - public bool ReduceReverb { get; set; } - } - - public class Output : NotifiableObject - { - private int _volume; - - [JsonProperty("volume")] - public int Volume - { - get - { - return _volume; - } - set - { - if (value != _volume) - { - _volume = value; - NotifyPropertyChanged("Volume"); - } - } - } - [JsonProperty("selectedId")] - public string SelectedId { get; set; } - [JsonProperty("is_sap_disabled")] - public bool IsSapDisabled { get; set; } - } - - public class Video : NotifiableObject - { - private bool _hideConfSelfVideo; - - [JsonProperty("hide_conf_self_video")] - public bool HideConfSelfVideo - { - get - { - return _hideConfSelfVideo; - } - set - { - if (value != _hideConfSelfVideo) - { - _hideConfSelfVideo = value; - NotifyPropertyChanged("HideConfSelfVideo"); - } - } - } - - public VideoCamera Camera { get; set; } - - public Video() - { - Camera = new VideoCamera(); - } - } - - public class VideoCamera : NotifiableObject - { - private string _selectedId; - - [JsonProperty("selectedId")] - public string SelectedId { - get - { - return _selectedId; - } - set - { - if (value != _selectedId) - { - _selectedId = value; - NotifyPropertyChanged("SelectedId"); - } - } - - } - public bool Mirror { get; set; } - } - - public class Client - { - public string appVersion { get; set; } - public string deviceSystem { get; set; } - } - - } - - /// - /// zCommand class structure - /// - public class zCommand - { - public class BookingsListResult - { - [JsonProperty("accessRole")] - public string AccessRole { get; set; } - [JsonProperty("calendarChangeKey")] - public string CalendarChangeKey { get; set; } - [JsonProperty("calendarID")] - public string CalendarId { get; set; } - [JsonProperty("checkIn")] - public bool CheckIn { get; set; } - [JsonProperty("creatorEmail")] - public string CreatorEmail { get; set; } - [JsonProperty("creatorName")] - public string CreatorName { get; set; } - [JsonProperty("endTime")] - public DateTime EndTime { get; set; } - [JsonProperty("hostName")] - public string HostName { get; set; } - [JsonProperty("isInstantMeeting")] - public bool IsInstantMeeting { get; set; } - [JsonProperty("isPrivate")] - public bool IsPrivate { get; set; } - [JsonProperty("location")] - public string Location { get; set; } - [JsonProperty("meetingName")] - public string MeetingName { get; set; } - [JsonProperty("meetingNumber")] - public string MeetingNumber { get; set; } - [JsonProperty("scheduledFrom")] - public string ScheduledFrom { get; set; } - [JsonProperty("startTime")] - public DateTime StartTime { get; set; } - [JsonProperty("third_party")] - public ThirdParty ThirdParty { get; set; } - } - - public static List GetGenericMeetingsFromBookingResult(List bookings, - int minutesBeforeMeetingStart) - { - var rv = GetGenericMeetingsFromBookingResult(bookings); - - foreach (var meeting in rv) - { - meeting.MinutesBeforeMeeting = minutesBeforeMeetingStart; - } - - return rv; - } - /// - /// Extracts the necessary meeting values from the Zoom bookings response and converts them to the generic class - /// - /// - /// - public static List GetGenericMeetingsFromBookingResult(List bookings) - { - var meetings = new List(); - - if (Debug.Level > 0) - { - Debug.Console(1, "Meetings List:\n"); - } - - foreach (var b in bookings) - { - var meeting = new Meeting(); - - if (b.MeetingNumber != null) - meeting.Id = b.MeetingNumber; - if (b.CreatorName != null) - meeting.Organizer = b.CreatorName; - if (b.MeetingName != null) - meeting.Title = b.MeetingName; - //if (b.Agenda != null) - // meeting.Agenda = b.Agenda.Value; - if (b.StartTime != null) - meeting.StartTime = b.StartTime; - if (b.EndTime != null) - meeting.EndTime = b.EndTime; - - meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; - - // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. - var now = DateTime.Now; - if (meeting.StartTime < now && meeting.EndTime < now) - { - Debug.Console(1, "Skipping meeting {0}. Meeting is in the past.", meeting.Title); - continue; - } - - meetings.Add(meeting); - - if (Debug.Level > 0) - { - Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}", meeting.Title, meeting.Id, meeting.Organizer); - Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); - Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); - } - } - - meetings.OrderBy(m => m.StartTime); - - return meetings; - } - - public class HandStatus - { - [JsonProperty("is_raise_hand")] - public bool IsRaiseHand { get; set; } - [JsonProperty("optimize_vis_validideo_sharing")] - public string IsValid { get; set; } - [JsonProperty("time_stamp")] - public string TimeStamp { get; set; } - } - - public class ListParticipant - { - [JsonProperty("audio_status state")] - public string AudioStatusState { get; set; } - [JsonProperty("audio_status type")] - public string AudioStatusType { get; set; } - [JsonProperty("avatar_url")] - public string AvatarUrl { get; set; } - [JsonProperty("camera_status am_i_controlling")] - public bool CameraStatusAmIControlling { get; set; } - [JsonProperty("camera_status can_i_request_control")] - public bool CameraStatusCanIRequestConrol { get; set; } - [JsonProperty("camera_status can_move_camera")] - public bool CameraStatusCanMoveCamera { get; set; } - [JsonProperty("camera_status can_switch_camera")] - public bool CameraStatusCanSwitchCamera { get; set; } - [JsonProperty("camera_status can_zoom_camera")] - public bool CameraStatusCanZoomCamera { get; set; } - [JsonProperty("can_edit_closed_caption")] - public bool CanEditClosedCaption { get; set; } - [JsonProperty("can_record")] - public bool CanRecord { get; set; } - [JsonProperty("event")] - public string Event { get; set; } - [JsonProperty("hand_status")] - public HandStatus HandStatus { get; set; } - [JsonProperty("isCohost")] - public bool IsCohost { get; set; } - [JsonProperty("is_client_support_closed_caption")] - public bool IsClientSupportClosedCaption { get; set; } - [JsonProperty("is_client_support_coHost")] - public bool IsClientSupportCoHost { get; set; } - [JsonProperty("is_host")] - public bool IsHost { get; set; } - [JsonProperty("is_myself")] - public bool IsMyself { get; set; } - [JsonProperty("is_recording")] - public bool IsRecording { get; set; } - [JsonProperty("is_video_can_mute_byHost")] - public bool IsVideoCanMuteByHost { get; set; } - [JsonProperty("is_video_can_unmute_byHost")] - public bool IsVideoCanUnmuteByHost { get; set; } - [JsonProperty("local_recording_disabled")] - public bool LocalRecordingDisabled { get; set; } - [JsonProperty("user_id")] - public int UserId { get; set; } - [JsonProperty("user_name")] - public string UserName { get; set; } - [JsonProperty("user_type")] - public string UserType { get; set; } - [JsonProperty("video_status has_source")] - public bool VideoStatusHasSource { get; set; } - [JsonProperty("video_status is_receiving")] - public bool VideoStatusIsReceiving { get; set; } - [JsonProperty("video_status is_sending")] - public bool VideoStatusIsSending { get; set; } - - public ListParticipant() - { - HandStatus = new HandStatus(); - } - - public static List GetGenericParticipantListFromParticipantsResult( - List participants) - { - return - participants.Select( - p => - new Participant - { - Name = p.UserName, - IsHost = p.IsHost, - CanMuteVideo = p.IsVideoCanMuteByHost, - CanUnmuteVideo = p.IsVideoCanUnmuteByHost, - AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", - VideoMuteFb = p.VideoStatusIsSending - }).ToList(); - } - } - - public class CallinCountryList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class CalloutCountryList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class TollFreeCallinList - { - public int code { get; set; } - public string display_number { get; set; } - public string id { get; set; } - public string name { get; set; } - public string number { get; set; } - } - - public class Info - { - public List callin_country_list { get; set; } - public List callout_country_list { get; set; } - public List toll_free_callin_list { get; set; } - } - - public class ThirdParty - { - public string h323_address { get; set; } - public string meeting_number { get; set; } - public string service_provider { get; set; } - public string sip_address { get; set; } - } - - public class MeetingListItem - { - public string accessRole { get; set; } - public string calendarChangeKey { get; set; } - public string calendarID { get; set; } - public bool checkIn { get; set; } - public string creatorEmail { get; set; } - public string creatorName { get; set; } - public string endTime { get; set; } - public string hostName { get; set; } - public bool isInstantMeeting { get; set; } - public bool isPrivate { get; set; } - public string location { get; set; } - public string meetingName { get; set; } - public string meetingNumber { get; set; } - public string scheduledFrom { get; set; } - public string startTime { get; set; } - public ThirdParty third_party { get; set; } - - public MeetingListItem() - { - third_party = new ThirdParty(); - } - } - - public class InfoResult - { - public Info Info { get; set; } - public bool am_i_original_host { get; set; } - public string default_callin_country { get; set; } - public string dialIn { get; set; } - public string international_url { get; set; } - public string invite_email_content { get; set; } - public string invite_email_subject { get; set; } - public bool is_callin_country_list_available { get; set; } - public bool is_calling_room_system_enabled { get; set; } - public bool is_toll_free_callin_list_available { get; set; } - public bool is_view_only { get; set; } - public bool is_waiting_room { get; set; } - public bool is_webinar { get; set; } - public string meeting_id { get; set; } - public MeetingListItem meeting_list_item { get; set; } - public string meeting_password { get; set; } - public string meeting_type { get; set; } - public int my_userid { get; set; } - public int participant_id { get; set; } - public string real_meeting_id { get; set; } - public string schedule_option { get; set; } - public string schedule_option2 { get; set; } - public string support_callout_type { get; set; } - public string toll_free_number { get; set; } - public string user_type { get; set; } - - public InfoResult() - { - Info = new Info(); - meeting_list_item = new MeetingListItem(); - } - } - - public class Phonebook - { - public List Contacts { get; set; } - public int Limit { get; set; } - public int Offset { get; set; } - } - } + } + } + + /// + /// Represents a response from a ZoomRoom system + /// + public class Response + { + public Status Status { get; set; } + public bool Sync { get; set; } + [JsonProperty("topKey")] + public string TopKey { get; set; } + [JsonProperty("type")] + public string Type { get; set; } + + public Response() + { + Status = new Status(); + } + } + + public class Status + { + [JsonProperty("message")] + public string Message { get; set; } + [JsonProperty("state")] + public string State { get; set; } + } + + + /// + /// zStatus class stucture + /// + public class zStatus + { + public class Login + { + [JsonProperty("ZAAPI Release")] + public string ZAAPIRelease { get; set; } + [JsonProperty("Zoom Room Release")] + public string ZoomRoomRelease { get; set; } + } + + public class SystemUnit + { + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("login_type")] + public string LoginType { get; set; } + [JsonProperty("meeting_number")] + public string MeetingNumber { get; set; } + [JsonProperty("platform")] + public string Platform { get; set; } + [JsonProperty("room_info")] + public RoomInfo RoomInfo { get; set; } + [JsonProperty("room_version")] + public string RoomVersion { get; set; } + + public SystemUnit() + { + RoomInfo = new RoomInfo(); + } + } + + public class RoomInfo + { + [JsonProperty("account_email")] + public string AccountEmail { get; set; } + [JsonProperty("display_version")] + public string DisplayVersion { get; set; } + [JsonProperty("is_auto_answer_enabled")] + public bool AutoAnswerIsEnabled { get; set; } + [JsonProperty("is_auto_answer_selected")] + public bool AutoAnswerIsSelected { get; set; } + [JsonProperty("room_name")] + public string RoomName { get; set; } + } + + public class CloudPbxInfo + { + [JsonProperty("company_number")] + public string CompanyNumber { get; set; } + [JsonProperty("extension")] + public string Extension { get; set; } + [JsonProperty("isValid")] + public bool IsValid { get; set; } + } + + public enum ePresence + { + PRESENCE_OFFLINE, + PRESENCE_ONLINE, + PRESENCE_AWAY, + PRESENCE_BUSY, + PRESENCE_DND + } + + public class Contact + { + [JsonProperty("avatarURL")] + public string AvatarURL { get; set; } + [JsonProperty("cloud_pbx_info")] + public CloudPbxInfo CloudPbxInfo { get; set; } + [JsonProperty("email")] + public string Email { get; set; } + [JsonProperty("firstName")] + public string FirstName { get; set; } + [JsonProperty("index")] + public int Index { get; set; } + [JsonProperty("isLegacy")] + public bool IsLegacy { get; set; } + [JsonProperty("isZoomRoom")] + public bool IsZoomRoom { get; set; } + [JsonProperty("jid")] + public string Jid { get; set; } + [JsonProperty("lastName")] + public string LastName { get; set; } + [JsonProperty("onDesktop")] + public bool OnDesktop { get; set; } + [JsonProperty("onMobile")] + public bool OnMobile { get; set; } + [JsonProperty("phoneNumber")] + public string PhoneNumber { get; set; } + [JsonProperty("presence")] + public ePresence Presence { get; set; } + [JsonProperty("presence_status")] + public int PresenceStatus { get; set; } + [JsonProperty("screenName")] + public string ScreenName { get; set; } + [JsonProperty("sip_phone_number")] + public string SipPhoneNumber { get; set; } + + + public Contact() + { + CloudPbxInfo = new CloudPbxInfo(); + } + } + + /// + /// Used to be able to inplement IInvitableContact on DirectoryContact + /// + public class ZoomDirectoryContact : DirectoryContact, IInvitableContact + { + + } + + public class Phonebook + { + [JsonProperty("Contacts")] + public List Contacts { get; set; } + + public Phonebook() + { + Contacts = new List(); + } + + /// + /// Converts from zStatus.Contact types to generic directory items + /// + /// + public static CodecDirectory ConvertZoomContactsToGeneric(List zoomContacts) + { + var directory = new CodecDirectory(); + + var folders = new List(); + + var roomFolder = new DirectoryFolder(); + + var contactFolder = new DirectoryFolder(); + + var contacts = new List(); + + // Check if there are any zoom rooms + var zoomRooms = zoomContacts.FindAll(c => c.IsZoomRoom); + + if (zoomRooms.Count > 0) + { + // If so, setup a rooms and contacts folder and add them. + roomFolder.Name = "Rooms"; + roomFolder.ParentFolderId = "root"; + roomFolder.FolderId = "rooms"; + + contactFolder.Name = "Contacts"; + contactFolder.ParentFolderId = "root"; + contactFolder.FolderId = "contacts"; + + folders.Add(roomFolder); + folders.Add(contactFolder); + + directory.AddFoldersToDirectory(folders); + } + + try + { + if (zoomContacts.Count == 0) return directory; + { + foreach (Contact c in zoomContacts) + { + var contact = new ZoomDirectoryContact {Name = c.ScreenName, ContactId = c.Jid}; + + if (folders.Count > 0) + { + contact.ParentFolderId = c.IsZoomRoom ? "rooms" : "contacts"; + } + + contacts.Add(contact); + } + + directory.AddContactsToDirectory(contacts); + } + } + catch (Exception e) + { + Debug.Console(1, "Error converting Zoom Phonebook results to generic: {0}", e); + } + + return directory; + } + } + + public enum eCallStatus + { + UNKNOWN, + NOT_IN_MEETING, + CONNECTING_MEETING, + IN_MEETING, + LOGGED_OUT + } + + public class ClosedCaption + { + public bool Available { get; set; } + } + + public class Call : NotifiableObject + { + private eCallStatus _status; + private List _participants; + + public bool IsInCall; + + public eCallStatus Status + { + get + { + return _status; + } + set + { + if (value != _status) + { + _status = value; + IsInCall = _status == eCallStatus.IN_MEETING || _status == eCallStatus.CONNECTING_MEETING; + NotifyPropertyChanged("Status"); + } + } + } + public ClosedCaption ClosedCaption { get; set; } + public List Participants + { + get + { + return _participants; + } + set + { + _participants = value; + NotifyPropertyChanged("Participants"); + } + } + public zEvent.SharingState Sharing { get; set; } + + public CallRecordInfo CallRecordInfo { get; set; } + + private zCommand.InfoResult _info; + + public zCommand.InfoResult Info + { + get + { + return _info; + } + set + { + _info = value; + NotifyPropertyChanged("Info"); + } + } + + public Call() + { + ClosedCaption = new ClosedCaption(); + Participants = new List(); + Sharing = new zEvent.SharingState(); + CallRecordInfo = new CallRecordInfo(); + Info = new zCommand.InfoResult(); + } + } + + public class Capabilities + { + public bool aec_Setting_Stored_In_ZR { get; set; } + public bool can_Dtmf_For_Invite_By_Phone { get; set; } + public bool can_Mute_On_Entry { get; set; } + public bool can_Ringing_In_Pstn_Call { get; set; } + public bool can_Switch_To_Specific_Camera { get; set; } + public bool is_Airhost_Disabled { get; set; } + public bool pstn_Call_In_Local_resentation { get; set; } + public bool support_Claim_Host { get; set; } + public bool support_Out_Room_Display { get; set; } + public bool support_Pin_And_Spotlight { get; set; } + public bool supports_Audio_Checkup { get; set; } + public bool supports_CheckIn { get; set; } + public bool supports_Cloud_PBX { get; set; } + public bool supports_Encrypted_Connection { get; set; } + public bool supports_Expel_User_Permanently { get; set; } + public bool supports_H323_DTMF { get; set; } + public bool supports_Hdmi_Cec_Control { get; set; } + public bool supports_Highly_Reverberant_Room { get; set; } + public bool supports_Loading_Contacts_Dynamically { get; set; } + public bool supports_Loading_Participants_Dynamically { get; set; } + public bool supports_Mic_Record_Test { get; set; } + public bool supports_Multi_Share { get; set; } + public bool supports_ShareCamera { get; set; } + public bool supports_Share_For_Floating_And_Content_Only { get; set; } + public bool supports_Sip_Call_out { get; set; } + public bool supports_Software_Audio_Processing { get; set; } + public bool supports_Web_Settings_Push { get; set; } + } + + public class Sharing : NotifiableObject + { + private string _dispState; + private string _password; + + public string directPresentationPairingCode { get; set; } + /// + /// Laptop client sharing key + /// + public string directPresentationSharingKey { get; set; } + public string dispState + { + get + { + return _dispState; + } + set + { + if (value != _dispState) + { + _dispState = value; + NotifyPropertyChanged("dispState"); + } + } + } + public bool isAirHostClientConnected { get; set; } + public bool isBlackMagicConnected { get; set; } + public bool isBlackMagicDataAvailable { get; set; } + public bool isDirectPresentationConnected { get; set; } + public bool isSharingBlackMagic { get; set; } + /// + /// IOS Airplay code + /// + public string password + { + get + { + return _password; + } + set + { + if (value != _password) + { + _password = value; + NotifyPropertyChanged("password"); + } + } + } + public string serverName { get; set; } + public string wifiName { get; set; } + } + + public class NumberOfScreens + { + [JsonProperty("NumberOfCECScreens")] + public int NumOfCECScreens { get; set; } + [JsonProperty("NumberOfScreens")] + public int NumOfScreens { get; set; } + } + + /// + /// AudioInputLine/AudioOutputLine/VideoCameraLine list item + /// + public class AudioVideoInputOutputLineItem + { + public string Alias { get; set; } + public string Name { get; set; } + public bool Selected { get; set; } + public bool combinedDevice { get; set; } + public string id { get; set; } + public bool manuallySelected { get; set; } + public int numberOfCombinedDevices { get; set; } + public int ptzComId { get; set; } + } + + public class Video + { + public bool Optimizable { get; set; } + } + + public class CameraShare : NotifiableObject + { + private bool _canControlCamera; + private bool _isSharing; + + [JsonProperty("can_Control_Camera")] + public bool CanControlCamera + { + get + { + return _canControlCamera; + } + set + { + if (value != _canControlCamera) + { + _canControlCamera = value; + NotifyPropertyChanged("CanControlCamera"); + } + } + } + public string id { get; set; } + public bool is_Mirrored { get; set; } + [JsonProperty("is_Sharing")] + public bool IsSharing + { + get + { + return _isSharing; + } + set + { + if (value != _isSharing) + { + _isSharing = value; + NotifyPropertyChanged("IsSharing"); + } + } + } + public int pan_Tilt_Speed { get; set; } + + } + + public class Layout + { + public bool can_Adjust_Floating_Video { get; set; } + public bool can_Switch_Floating_Share_Content { get; set; } + public bool can_Switch_Share_On_All_Screens { get; set; } + public bool can_Switch_Speaker_View { get; set; } + public bool can_Switch_Wall_View { get; set; } + public bool is_In_First_Page { get; set; } + public bool is_In_Last_Page { get; set; } + public bool is_supported { get; set; } + public int video_Count_In_Current_Page { get; set; } + public string video_type { get; set; } + } + + public class CallRecordInfo + { + public bool canRecord { get; set; } + public bool emailRequired { get; set; } + public bool amIRecording { get; set; } + public bool meetingIsBeingRecorded { get; set; } + } + } + + /// + /// zEvent Class Structure + /// + public class zEvent + { + public class NeedWaitForHost + { + public bool Wait { get; set; } + } + + public class IncomingCallIndication + { + public string callerJID { get; set; } + public string calleeJID { get; set; } + public string meetingID { get; set; } + public string password { get; set; } + public string meetingOption { get; set; } + public long MeetingNumber { get; set; } + public string callerName { get; set; } + public string avatarURL { get; set; } + public int lifeTime { get; set; } + public bool accepted { get; set; } + } + + public class CallConnectError + { + public int error_code { get; set; } + public string error_message { get; set; } + } + + public class CallDisconnect + { + public bool Successful + { + get + { + return success == "on"; + } + } + + public string success { get; set; } + } + + public class Layout + { + public bool Sharethumb { get; set; } + } + + public class Call + { + public Layout Layout { get; set; } + } + + public class Client + { + public Call Call { get; set; } + } + + public enum eSharingState + { + None, + Connecting, + Sending, + Receiving, + Send_Receiving + } + + public class SharingState : NotifiableObject + { + private bool _paused; + private eSharingState _state; + + public bool IsSharing; + + [JsonProperty("paused")] + public bool Paused + { + get + { + return _paused; + } + set + { + if (value != _paused) + { + _paused = value; + NotifyPropertyChanged("Paused"); + } + } + } + [JsonProperty("state")] + public eSharingState State + { + get + { + return _state; + } + set + { + if (value != _state) + { + _state = value; + IsSharing = _state == eSharingState.Sending; + NotifyPropertyChanged("State"); + } + } + } + } + + public class PinStatusOfScreenNotification + { + [JsonProperty("can_be_pinned")] + public bool CanBePinned { get; set; } + [JsonProperty("can_pin_share")] + public bool CanPinShare { get; set; } + [JsonProperty("pinned_share_source_id")] + public int PinnedShareSourceId { get; set; } + [JsonProperty("pinned_user_id")] + public int PinnedUserId { get; set; } + [JsonProperty("screen_index")] + public int ScreenIndex { get; set; } + [JsonProperty("screen_layout")] + public int ScreenLayout { get; set; } + [JsonProperty("share_source_type")] + public int ShareSourceType { get; set; } + [JsonProperty("why_cannot_pin_share")] + public string WhyCannotPinShare { get; set; } + } + + public class PhoneCallStatus:NotifiableObject + { + private bool _isIncomingCall; + private string _peerDisplayName; + private string _peerNumber; + + private bool _offHook; + + public string CallId { get; set; } + public bool IsIncomingCall { + get { return _isIncomingCall; } + set + { + if(value == _isIncomingCall) return; + + _isIncomingCall = value; + NotifyPropertyChanged("IsIncomingCall"); + } } + + public string PeerDisplayName + { + get { return _peerDisplayName; } + set + { + if (value == _peerDisplayName) return; + _peerDisplayName = value; + NotifyPropertyChanged("PeerDisplayName"); + } + } + + public string PeerNumber + { + get { return _peerNumber; } + set + { + if (value == _peerNumber) return; + + _peerNumber = value; + NotifyPropertyChanged("PeerNumber"); + } + } + + public string PeerUri { get; set; } + + private ePhoneCallStatus _status; + public ePhoneCallStatus Status + { + get { return _status; } + set + { + _status = value; + OffHook = _status == ePhoneCallStatus.PhoneCallStatus_Accepted || + _status == ePhoneCallStatus.PhoneCallStatus_InCall || + _status == ePhoneCallStatus.PhoneCallStatus_Init || + _status == ePhoneCallStatus.PhoneCallStatus_Ringing; + } + } + + public bool OffHook + { + get { return _offHook; } + set + { + if (value == _offHook) return; + + _offHook = value; + NotifyPropertyChanged("OffHook"); + } + } + } + + public enum ePhoneCallStatus + { + PhoneCallStatus_Ringing, + PhoneCallStatus_Terminated, + PhoneCallStatus_Accepted, + PhoneCallStatus_InCall, + PhoneCallStatus_Init, + } + } + + /// + /// zConfiguration class structure + /// + public class zConfiguration + { + public class Sharing + { + [JsonProperty("optimize_video_sharing")] + public bool OptimizeVideoSharing { get; set; } + } + + public class Camera : NotifiableObject + { + private bool _mute; + + public bool Mute + { + get { return _mute; } + set + { + Debug.Console(1, "Camera Mute response received: {0}", value); + + if (value == _mute) return; + + _mute = value; + NotifyPropertyChanged("Mute"); + } + } + } + + public class Microphone : NotifiableObject + { + private bool _mute; + + public bool Mute + { + get + { + return _mute; + } + set + { + if(value != _mute) + { + _mute = value; + NotifyPropertyChanged("Mute"); + } + } + } + } + + public enum eLayoutStyle + { + Gallery, + Speaker, + Strip, + ShareAll + } + + public enum eLayoutSize + { + Off, + Size1, + Size2, + Size3, + Strip + } + + public enum eLayoutPosition + { + Center, + Up, + Right, + UpRight, + Down, + DownRight, + Left, + UpLeft, + DownLeft + } + + public class Layout:NotifiableObject + { + public bool ShareThumb { get; set; } + public eLayoutStyle Style { get; set; } + public eLayoutSize Size { get; set; } + + private eLayoutPosition _position; + public eLayoutPosition Position { + get { return _position; } + set + { + _position = value; + NotifyPropertyChanged("Position"); + } } + } + + public class Lock + { + public bool Enable { get; set; } + } + + public class ClosedCaption + { + public bool Visible { get; set; } + public int FontSize { get; set; } + } + + public class MuteUserOnEntry + { + public bool Enable { get; set; } + } + + public class Call + { + public Sharing Sharing { get; set; } + public Camera Camera { get; set; } + public Microphone Microphone { get; set; } + public Layout Layout { get; set; } + public Lock Lock { get; set; } + public MuteUserOnEntry MuteUserOnEntry { get; set; } + public ClosedCaption ClosedCaption { get; set; } + + + public Call() + { + Sharing = new Sharing(); + Camera = new Camera(); + Microphone = new Microphone(); + Layout = new Layout(); + Lock = new Lock(); + MuteUserOnEntry = new MuteUserOnEntry(); + ClosedCaption = new ClosedCaption(); + } + } + + public class Audio + { + public Input Input { get; set; } + public Output Output { get; set; } + + public Audio() + { + Input = new Input(); + Output = new Output(); + } + } + + public class Input : Output + { + [JsonProperty("reduce_reverb")] + public bool ReduceReverb { get; set; } + } + + public class Output : NotifiableObject + { + private int _volume; + + [JsonProperty("volume")] + public int Volume + { + get + { + return _volume; + } + set + { + if (value != _volume) + { + _volume = value; + NotifyPropertyChanged("Volume"); + } + } + } + [JsonProperty("selectedId")] + public string SelectedId { get; set; } + [JsonProperty("is_sap_disabled")] + public bool IsSapDisabled { get; set; } + } + + public class Video : NotifiableObject + { + private bool _hideConfSelfVideo; + + [JsonProperty("hide_conf_self_video")] + public bool HideConfSelfVideo + { + get + { + return _hideConfSelfVideo; + } + set + { + if (value != _hideConfSelfVideo) + { + _hideConfSelfVideo = value; + NotifyPropertyChanged("HideConfSelfVideo"); + } + } + } + + public VideoCamera Camera { get; set; } + + public Video() + { + Camera = new VideoCamera(); + } + } + + public class VideoCamera : NotifiableObject + { + private string _selectedId; + + [JsonProperty("selectedId")] + public string SelectedId { + get + { + return _selectedId; + } + set + { + if (value != _selectedId) + { + _selectedId = value; + NotifyPropertyChanged("SelectedId"); + } + } + + } + public bool Mirror { get; set; } + } + + public class Client + { + public string appVersion { get; set; } + public string deviceSystem { get; set; } + } + + } + + /// + /// zCommand class structure + /// + public class zCommand + { + public class BookingsListResult + { + [JsonProperty("accessRole")] + public string AccessRole { get; set; } + [JsonProperty("calendarChangeKey")] + public string CalendarChangeKey { get; set; } + [JsonProperty("calendarID")] + public string CalendarId { get; set; } + [JsonProperty("checkIn")] + public bool CheckIn { get; set; } + [JsonProperty("creatorEmail")] + public string CreatorEmail { get; set; } + [JsonProperty("creatorName")] + public string CreatorName { get; set; } + [JsonProperty("endTime")] + public DateTime EndTime { get; set; } + [JsonProperty("hostName")] + public string HostName { get; set; } + [JsonProperty("isInstantMeeting")] + public bool IsInstantMeeting { get; set; } + [JsonProperty("isPrivate")] + public bool IsPrivate { get; set; } + [JsonProperty("location")] + public string Location { get; set; } + [JsonProperty("meetingName")] + public string MeetingName { get; set; } + [JsonProperty("meetingNumber")] + public string MeetingNumber { get; set; } + [JsonProperty("scheduledFrom")] + public string ScheduledFrom { get; set; } + [JsonProperty("startTime")] + public DateTime StartTime { get; set; } + [JsonProperty("third_party")] + public ThirdParty ThirdParty { get; set; } + } + + public static List GetGenericMeetingsFromBookingResult(List bookings, + int minutesBeforeMeetingStart) + { + var rv = GetGenericMeetingsFromBookingResult(bookings); + + foreach (var meeting in rv) + { + meeting.MinutesBeforeMeeting = minutesBeforeMeetingStart; + } + + return rv; + } + /// + /// Extracts the necessary meeting values from the Zoom bookings response and converts them to the generic class + /// + /// + /// + public static List GetGenericMeetingsFromBookingResult(List bookings) + { + var meetings = new List(); + + if (Debug.Level > 0) + { + Debug.Console(1, "Meetings List:\n"); + } + + foreach (var b in bookings) + { + var meeting = new Meeting(); + + if (b.MeetingNumber != null) + meeting.Id = b.MeetingNumber; + if (b.CreatorName != null) + meeting.Organizer = b.CreatorName; + if (b.MeetingName != null) + meeting.Title = b.MeetingName; + //if (b.Agenda != null) + // meeting.Agenda = b.Agenda.Value; + if (b.StartTime != null) + meeting.StartTime = b.StartTime; + if (b.EndTime != null) + meeting.EndTime = b.EndTime; + + meeting.Privacy = b.IsPrivate ? eMeetingPrivacy.Private : eMeetingPrivacy.Public; + + // No meeting.Calls data exists for Zoom Rooms. Leaving out for now. + var now = DateTime.Now; + if (meeting.StartTime < now && meeting.EndTime < now) + { + Debug.Console(1, "Skipping meeting {0}. Meeting is in the past.", meeting.Title); + continue; + } + + meetings.Add(meeting); + + if (Debug.Level > 0) + { + Debug.Console(1, "Title: {0}, ID: {1}, Organizer: {2}", meeting.Title, meeting.Id, meeting.Organizer); + Debug.Console(1, " Start Time: {0}, End Time: {1}, Duration: {2}", meeting.StartTime, meeting.EndTime, meeting.Duration); + Debug.Console(1, " Joinable: {0}\n", meeting.Joinable); + } + } + + meetings.OrderBy(m => m.StartTime); + + return meetings; + } + + public class HandStatus + { + [JsonProperty("is_raise_hand")] + public bool IsRaiseHand { get; set; } + [JsonProperty("optimize_vis_validideo_sharing")] + public string IsValid { get; set; } + [JsonProperty("time_stamp")] + public string TimeStamp { get; set; } + } + + public class ListParticipant + { + [JsonProperty("audio_status state")] + public string AudioStatusState { get; set; } + [JsonProperty("audio_status type")] + public string AudioStatusType { get; set; } + [JsonProperty("avatar_url")] + public string AvatarUrl { get; set; } + [JsonProperty("camera_status am_i_controlling")] + public bool CameraStatusAmIControlling { get; set; } + [JsonProperty("camera_status can_i_request_control")] + public bool CameraStatusCanIRequestConrol { get; set; } + [JsonProperty("camera_status can_move_camera")] + public bool CameraStatusCanMoveCamera { get; set; } + [JsonProperty("camera_status can_switch_camera")] + public bool CameraStatusCanSwitchCamera { get; set; } + [JsonProperty("camera_status can_zoom_camera")] + public bool CameraStatusCanZoomCamera { get; set; } + [JsonProperty("can_edit_closed_caption")] + public bool CanEditClosedCaption { get; set; } + [JsonProperty("can_record")] + public bool CanRecord { get; set; } + [JsonProperty("event")] + public string Event { get; set; } + [JsonProperty("hand_status")] + public HandStatus HandStatus { get; set; } + [JsonProperty("isCohost")] + public bool IsCohost { get; set; } + [JsonProperty("is_client_support_closed_caption")] + public bool IsClientSupportClosedCaption { get; set; } + [JsonProperty("is_client_support_coHost")] + public bool IsClientSupportCoHost { get; set; } + [JsonProperty("is_host")] + public bool IsHost { get; set; } + [JsonProperty("is_myself")] + public bool IsMyself { get; set; } + [JsonProperty("is_recording")] + public bool IsRecording { get; set; } + [JsonProperty("is_video_can_mute_byHost")] + public bool IsVideoCanMuteByHost { get; set; } + [JsonProperty("is_video_can_unmute_byHost")] + public bool IsVideoCanUnmuteByHost { get; set; } + [JsonProperty("local_recording_disabled")] + public bool LocalRecordingDisabled { get; set; } + [JsonProperty("user_id")] + public int UserId { get; set; } + [JsonProperty("user_name")] + public string UserName { get; set; } + [JsonProperty("user_type")] + public string UserType { get; set; } + [JsonProperty("video_status has_source")] + public bool VideoStatusHasSource { get; set; } + [JsonProperty("video_status is_receiving")] + public bool VideoStatusIsReceiving { get; set; } + [JsonProperty("video_status is_sending")] + public bool VideoStatusIsSending { get; set; } + + public ListParticipant() + { + HandStatus = new HandStatus(); + } + + public static List GetGenericParticipantListFromParticipantsResult( + List participants) + { + return + participants.Select( + p => + new Participant + { + Name = p.UserName, + IsHost = p.IsHost, + CanMuteVideo = p.IsVideoCanMuteByHost, + CanUnmuteVideo = p.IsVideoCanUnmuteByHost, + AudioMuteFb = p.AudioStatusState == "AUDIO_MUTED", + VideoMuteFb = p.VideoStatusIsSending + }).ToList(); + } + } + + public class CallinCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class CalloutCountryList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class TollFreeCallinList + { + public int code { get; set; } + public string display_number { get; set; } + public string id { get; set; } + public string name { get; set; } + public string number { get; set; } + } + + public class Info + { + public List callin_country_list { get; set; } + public List callout_country_list { get; set; } + public List toll_free_callin_list { get; set; } + } + + public class ThirdParty + { + public string h323_address { get; set; } + public string meeting_number { get; set; } + public string service_provider { get; set; } + public string sip_address { get; set; } + } + + public class MeetingListItem + { + public string accessRole { get; set; } + public string calendarChangeKey { get; set; } + public string calendarID { get; set; } + public bool checkIn { get; set; } + public string creatorEmail { get; set; } + public string creatorName { get; set; } + public string endTime { get; set; } + public string hostName { get; set; } + public bool isInstantMeeting { get; set; } + public bool isPrivate { get; set; } + public string location { get; set; } + public string meetingName { get; set; } + public string meetingNumber { get; set; } + public string scheduledFrom { get; set; } + public string startTime { get; set; } + public ThirdParty third_party { get; set; } + + public MeetingListItem() + { + third_party = new ThirdParty(); + } + } + + public class InfoResult + { + public Info Info { get; set; } + public bool am_i_original_host { get; set; } + public string default_callin_country { get; set; } + public string dialIn { get; set; } + public string international_url { get; set; } + public string invite_email_content { get; set; } + public string invite_email_subject { get; set; } + public bool is_callin_country_list_available { get; set; } + public bool is_calling_room_system_enabled { get; set; } + public bool is_toll_free_callin_list_available { get; set; } + public bool is_view_only { get; set; } + public bool is_waiting_room { get; set; } + public bool is_webinar { get; set; } + public string meeting_id { get; set; } + public MeetingListItem meeting_list_item { get; set; } + public string meeting_password { get; set; } + public string meeting_type { get; set; } + public int my_userid { get; set; } + public int participant_id { get; set; } + public string real_meeting_id { get; set; } + public string schedule_option { get; set; } + public string schedule_option2 { get; set; } + public string support_callout_type { get; set; } + public string toll_free_number { get; set; } + public string user_type { get; set; } + + public InfoResult() + { + Info = new Info(); + meeting_list_item = new MeetingListItem(); + } + } + + public class Phonebook + { + public List Contacts { get; set; } + public int Limit { get; set; } + public int Offset { get; set; } + } + } } \ No newline at end of file From f0a3b27e3b254b91f192c9d1a5c4b498bb1c807f Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 17 Dec 2020 14:28:27 -0700 Subject: [PATCH 53/89] Adds dummy source and room power on implementation --- .../Room/Config/EssentialsTechRoomConfig.cs | 3 +++ .../Room/Types/EssentialsTechRoom.cs | 26 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs index 292a6b7c..06e67f39 100644 --- a/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsTechRoomConfig.cs @@ -5,6 +5,9 @@ namespace PepperDash.Essentials.Room.Config { public class EssentialsTechRoomConfig { + [JsonProperty("dummySourceKey")] + public string DummySourceKey { get; set; } + [JsonProperty("displays")] public List Displays; diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 515a4355..d0de145d 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -28,8 +28,6 @@ namespace PepperDash.Essentials private Dictionary _currentPresets; private ScheduledEventGroup _roomScheduledEventGroup; - private GenericSource DummySource = new GenericSource("$off", "Dummy Source"); - public EssentialsTechRoom(DeviceConfig config) : base(config) { _config = config.Properties.ToObject(); @@ -243,9 +241,17 @@ namespace PepperDash.Essentials public void RoomPowerOn() { + var dummySource = DeviceManager.GetDeviceForKey(_config.DummySourceKey) as IRoutingOutputs; + + if (dummySource == null) + { + Debug.Console(1, this, "Unable to get source with key: {0}", _config.DummySourceKey); + return; + } + foreach (var display in _displays) { - + RunDirectRoute(dummySource, display.Value); } } @@ -388,7 +394,7 @@ namespace PepperDash.Essentials return; } - if (source.Key.Equals("$off", StringComparison.OrdinalIgnoreCase)) + if (source == null) { dest.ReleaseRoute(); if (dest is IHasPowerControl) @@ -396,11 +402,6 @@ namespace PepperDash.Essentials } else { - if (source == null) - { - Debug.Console(1, this, "Cannot route unknown source '{0}' to {1}", source.Key, dest.Key); - return; - } dest.ReleaseAndMakeRoute(source, eRoutingSignalType.Video); } } @@ -418,11 +419,14 @@ namespace PepperDash.Essentials var source = DeviceManager.GetDeviceForKey(sourceKey) as IRoutingOutputs; + if (source == null || dest == null) + { + Debug.Console(1, this, "Cannot route unknown source or destination '{0}' to {1}", sourceKey, destinationKey); + return; + } RunDirectRoute(source, dest); } - - #endregion } From b2402402d9266e18fa396a356d5d5cbbbff0a9d2 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Thu, 17 Dec 2020 14:48:01 -0700 Subject: [PATCH 54/89] remove dummy device add --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 2 -- .../PepperDashEssentialsBase/Display/DisplayBase.cs | 6 +----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index d0de145d..f0191839 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -48,8 +48,6 @@ namespace PepperDash.Essentials SubscribeToDisplayFeedbacks(); CreateOrUpdateScheduledEvents(); - - DeviceManager.AddDevice(DummySource); } public Dictionary CurrentPresetsFeedbacks { get; private set; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs index e4a2cb26..f1b96ac4 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Display/DisplayBase.cs @@ -81,8 +81,6 @@ namespace PepperDash.Essentials.Core } - - public abstract void PowerOn(); public abstract void PowerOff(); public abstract void PowerToggle(); @@ -99,7 +97,7 @@ namespace PepperDash.Essentials.Core } } - public abstract void ExecuteSwitch(object selector); + public abstract void ExecuteSwitch(object selector); protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) @@ -315,7 +313,5 @@ namespace PepperDash.Essentials.Core var newEvent = NumericSwitchChange; if (newEvent != null) newEvent(this, e); } - - } } \ No newline at end of file From aa61479adc50f992183b8d3cd57407f7400e5511 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 18 Dec 2020 09:16:40 -0700 Subject: [PATCH 55/89] remove device info stuff from DGE for now --- .../Essentials_DM/Endpoints/DGEs/Dge100Controller.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs index d7996554..39e549cc 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/DGEs/Dge100Controller.cs @@ -39,12 +39,12 @@ namespace PepperDash.Essentials.DM.Endpoints.DGEs { _dge = device; _dgeEthernetInfo = _dge.ExtenderEthernetReservedSigs; - _dgeEthernetInfo.DeviceExtenderSigChange += (extender, args) => UpdateDeviceInfo(); + //_dgeEthernetInfo.DeviceExtenderSigChange += (extender, args) => UpdateDeviceInfo(); _dgeEthernetInfo.Use(); DeviceInfo = new DeviceInfo(); - _dge.OnlineStatusChange += (currentDevice, args) => { if (args.DeviceOnLine) UpdateDeviceInfo(); }; + //_dge.OnlineStatusChange += (currentDevice, args) => { if (args.DeviceOnLine) UpdateDeviceInfo(); }; _dc = dc; From 870f2f8fa641d805999b594f8a460deb054adb48 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 18 Dec 2020 14:34:15 -0700 Subject: [PATCH 56/89] properly defines the IsWarming/Cooling FeedbackFuncs and fires feedbacks --- .../Room/Types/EssentialsTechRoom.cs | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index f0191839..a2943528 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -28,6 +28,33 @@ namespace PepperDash.Essentials private Dictionary _currentPresets; private ScheduledEventGroup _roomScheduledEventGroup; + /// + /// + /// + protected override Func IsWarmingFeedbackFunc + { + get + { + return () => + { + return _displays.All(kv => kv.Value.IsWarmingUpFeedback.BoolValue); + }; + } + } + /// + /// + /// + protected override Func IsCoolingFeedbackFunc + { + get + { + return () => + { + return _displays.All(kv => kv.Value.IsCoolingDownFeedback.BoolValue); + }; + } + } + public EssentialsTechRoom(DeviceConfig config) : base(config) { _config = config.Properties.ToObject(); @@ -111,7 +138,12 @@ namespace PepperDash.Essentials foreach (var display in _displays) { display.Value.PowerIsOnFeedback.OutputChange += - (sender, args) => RoomPowerIsOnFeedback.InvokeFireUpdate(); + (sender, args) => + { + RoomPowerIsOnFeedback.InvokeFireUpdate(); + IsWarmingUpFeedback.InvokeFireUpdate(); + IsCoolingDownFeedback.InvokeFireUpdate(); + }; } } @@ -281,16 +313,6 @@ namespace PepperDash.Essentials #region Overrides of EssentialsRoomBase - protected override Func IsWarmingFeedbackFunc - { - get { return () => false; } - } - - protected override Func IsCoolingFeedbackFunc - { - get { return () => false; } - } - protected override Func OnFeedbackFunc { get { return () => RoomPowerIsOn; } From 0a34f48e0a3b45ffbb8dfc0f3b02f2413eab2800 Mon Sep 17 00:00:00 2001 From: Jason Alborough Date: Fri, 18 Dec 2020 17:07:20 -0500 Subject: [PATCH 57/89] Fixes for the static GetPropertyByName method in DeviceJsonApi...now returns a property object rather than a PropertyInfo object --- .../PepperDashEssentialsBase/Devices/DeviceJsonApi.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs index c7bc7c68..51d5882f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceJsonApi.cs @@ -83,13 +83,13 @@ namespace PepperDash.Essentials.Core /// public static object GetPropertyByName(string deviceObjectPath, string propertyName) { - var obj = FindObjectOnPath(deviceObjectPath); - if(obj == null) + var dev = FindObjectOnPath(deviceObjectPath); + if(dev == null) return "{ \"error\":\"No Device\"}"; + + object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null); - CType t = obj.GetType(); - - var prop = t.GetProperty(propertyName); + // var prop = t.GetProperty(propertyName); if (prop != null) { return prop; From 0f924360c11f60e39ec6527ef411908789a8bf77 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Dec 2020 10:51:12 -0700 Subject: [PATCH 58/89] fix issues in LinkToApi --- PepperDashEssentials/Room/Types/EssentialsTechRoom.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index a2943528..0a47933e 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -380,6 +380,8 @@ namespace PepperDash.Essentials feedback.Value.FireUpdate(); } }; + + return; } i = 0; @@ -388,6 +390,8 @@ namespace PepperDash.Essentials var tuner = setTopBox; trilist.SetStringSigAction(joinMap.CurrentPreset.JoinNumber + i, s => _tunerPresets.Dial(s, tuner.Value)); + + i++; } } From 0a43f43f665704ae8edf87f4732a1958508aaee1 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Dec 2020 12:31:00 -0700 Subject: [PATCH 59/89] add ICommunicationMonitor to EiscApiAdvanced --- .../Bridges/BridgeBase.cs | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 86db81ba..33285bff 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -78,7 +78,7 @@ namespace PepperDash.Essentials.Core.Bridges /// /// Bridge API using EISC /// - public class EiscApiAdvanced : BridgeApi + public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor { public EiscApiPropertiesConfig PropertiesConfig { get; private set; } @@ -89,6 +89,7 @@ namespace PepperDash.Essentials.Core.Bridges public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) : base(dc.Key) { + CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000); JoinMaps = new Dictionary(); PropertiesConfig = dc.Properties.ToObject(); @@ -100,6 +101,19 @@ namespace PepperDash.Essentials.Core.Bridges AddPostActivationAction(LinkDevices); AddPostActivationAction(LinkRooms); + AddPostActivationAction(RegisterEisc); + } + + public override bool CustomActivate() + { + CommunicationMonitor.Start(); + return base.CustomActivate(); + } + + public override bool Deactivate() + { + CommunicationMonitor.Stop(); + return base.Deactivate(); } private void LinkDevices() @@ -137,8 +151,6 @@ namespace PepperDash.Essentials.Core.Bridges bridge.LinkToApi(Eisc, d.JoinStart, d.JoinMapKey, this); } } - - RegisterEisc(); } private void RegisterEisc() @@ -182,8 +194,6 @@ namespace PepperDash.Essentials.Core.Bridges rm.LinkToApi(Eisc, room.JoinStart, room.JoinMapKey, this); } - - RegisterEisc(); } /// @@ -324,6 +334,12 @@ namespace PepperDash.Essentials.Core.Bridges Debug.Console(2, this, "Error in Eisc_SigChange handler: {0}", e); } } + + #region Implementation of ICommunicationMonitor + + public StatusMonitorBase CommunicationMonitor { get; private set; } + + #endregion } public class EiscApiPropertiesConfig From cc159e306e1295a2aa277f40dd4e0cc060f39148 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Dec 2020 14:40:19 -0700 Subject: [PATCH 60/89] update Essentials to use PepperDash Core 1.0.43 --- .../PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec | 2 +- packages.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec index 5db86afc..dff167ed 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec @@ -14,7 +14,7 @@ crestron 3series 4series - + diff --git a/packages.config b/packages.config index c138dd1b..67789ca5 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file From ae03b8cd7e1e368619fc32160fc577b89ec0b537 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Dec 2020 16:19:45 -0700 Subject: [PATCH 61/89] fix PresetsList saving to file --- .../PepperDashEssentialsBase/Presets/DevicePresets.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs index 85e5dfdb..d6a9fad0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Presets/DevicePresets.cs @@ -266,14 +266,13 @@ namespace PepperDash.Essentials.Core.Presets try { _fileOps.Enter(); - var json = JsonConvert.SerializeObject(PresetsList); + var pl = new PresetsList {Channels = PresetsList, Name = Name}; + var json = JsonConvert.SerializeObject(pl, Formatting.Indented); using (var file = File.Open(_filePath, FileMode.Truncate)) { file.Write(json, Encoding.UTF8); } - - } finally { From 3b0a5285ab5d8d4f4e99397582a1337eade40a9a Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Mon, 21 Dec 2020 17:00:37 -0700 Subject: [PATCH 62/89] fix order for comm monitor --- .../PepperDashEssentialsBase/Bridges/BridgeBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs index 33285bff..11ede5a1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/BridgeBase.cs @@ -89,7 +89,6 @@ namespace PepperDash.Essentials.Core.Bridges public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) : base(dc.Key) { - CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000); JoinMaps = new Dictionary(); PropertiesConfig = dc.Properties.ToObject(); @@ -99,6 +98,8 @@ namespace PepperDash.Essentials.Core.Bridges Eisc.SigChange += Eisc_SigChange; + CommunicationMonitor = new CrestronGenericBaseCommunicationMonitor(this, Eisc, 120000, 300000); + AddPostActivationAction(LinkDevices); AddPostActivationAction(LinkRooms); AddPostActivationAction(RegisterEisc); From 7196d0aba8edca612d25aa093e37e1ba1b6f7cd9 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 29 Dec 2020 20:20:49 -0700 Subject: [PATCH 63/89] Adds FusionRoomJoinMap and ability to set IPID and JoinMapKey from config --- PepperDashEssentials/ControlSystem.cs | 20 ++- .../EssentialsHuddleVtc1FusionController.cs | 6 +- .../Room/Config/EssentialsRoomConfig.cs | 29 +++ .../Bridges/JoinMaps/AppleTvJoinMap.cs | 4 +- ...lsHuddleSpaceFusionSystemControllerBase.cs | 169 ++++++++++-------- .../Fusion/FusionRoomJoinMap.cs | 122 +++++++++++++ .../JoinMaps/JoinMapBase.cs | 7 +- .../PepperDash_Essentials_Core.csproj | 1 + 8 files changed, 270 insertions(+), 88 deletions(-) create mode 100644 essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 58a545f2..9bec571e 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -432,12 +432,26 @@ namespace PepperDash.Essentials var room = EssentialsRoomConfigHelper.GetRoomObject(roomConfig) as EssentialsRoomBase; if (room != null) { + // default IPID + uint fusionIpId = 0xf1; + + // default to no join map key + string fusionJoinMapKey = string.Empty; + + var fusionConfig = room.Config.Properties["fusion"].ToObject(); + + if (fusionConfig != null) + { + fusionIpId = fusionConfig.IpIdInt; + fusionJoinMapKey = fusionConfig.JoinMapKey; + } + if (room is EssentialsHuddleSpaceRoom) { DeviceManager.AddDevice(room); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleSpaceRoom, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, 0xf1)); + DeviceManager.AddDevice(new Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -449,7 +463,7 @@ namespace PepperDash.Essentials DeviceManager.AddDevice(room); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsHuddleVtc1Room, attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, 0xf1)); + DeviceManager.AddDevice(new EssentialsHuddleVtc1FusionController((EssentialsHuddleVtc1Room)room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge..."); @@ -461,7 +475,7 @@ namespace PepperDash.Essentials Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase(room, 0xF1)); + DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 8d9da386..89059cbe 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -16,8 +16,8 @@ namespace PepperDash.Essentials.Fusion { BooleanSigData CodecIsInCall; - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) - : base(room, ipId) + public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId, string joinMapKey) + : base(room, ipId, joinMapKey) { } @@ -166,7 +166,7 @@ namespace PepperDash.Essentials.Fusion CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); // Room to fusion room Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); diff --git a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs index 79844ce4..201b583c 100644 --- a/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsRoomConfig.cs @@ -177,6 +177,9 @@ namespace PepperDash.Essentials.Room.Config [JsonProperty("volumes")] public EssentialsRoomVolumesConfig Volumes { get; set; } + [JsonProperty("fusion")] + public EssentialsRoomFusionConfig Fusion { get; set; } + [JsonProperty("zeroVolumeWhenSwtichingVolumeDevices")] public bool ZeroVolumeWhenSwtichingVolumeDevices { get; set; } @@ -220,6 +223,32 @@ namespace PepperDash.Essentials.Room.Config } + public class EssentialsRoomFusionConfig + { + public uint IpIdInt + { + get + { + try + { + return Convert.ToUInt32(IpId, 16); + } + catch (Exception) + { + throw new FormatException(string.Format("ERROR:Unable to convert IP ID: {0} to hex. Error:\n{1}", IpId)); + } + + } + } + + [JsonProperty("ipId")] + public string IpId { get; set; } + + [JsonProperty("joinMapKey")] + public string JoinMapKey { get; set; } + + } + public class EssentialsRoomMicrophonePrivacyConfig { [JsonProperty("deviceKey")] diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs index 070e8f61..478f4e29 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Bridges/JoinMaps/AppleTvJoinMap.cs @@ -47,7 +47,7 @@ namespace PepperDash.Essentials.Core.Bridges /// Join this join map will start at /// Type of the child join map public AppleTvJoinMap(uint joinStart, Type type) : base(joinStart, type) - { - } + { + } } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index dcc86635..afaee291 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -16,6 +16,8 @@ namespace PepperDash.Essentials.Core.Fusion { public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider { + FusionRoomJoinMap JoinMap; + private const string RemoteOccupancyXml = "Local{0}"; private readonly bool _guidFileExists; @@ -84,11 +86,19 @@ namespace PepperDash.Essentials.Core.Fusion #endregion - public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId) + public EssentialsHuddleSpaceFusionSystemControllerBase(EssentialsRoomBase room, uint ipId, string joinMapKey) : base(room.Key + "-fusion") { try { + JoinMap = new FusionRoomJoinMap(1); + + if (!string.IsNullOrEmpty(joinMapKey)) + { + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + JoinMap.SetCustomJoinData(customJoins); + } + Room = room; _ipId = ipId; @@ -319,7 +329,7 @@ namespace PepperDash.Essentials.Core.Fusion "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(CreateAsHocMeeting, "FusCreateMeeting", + CrestronConsole.AddNewConsoleCommand(CreateAdHocMeeting, "FusCreateMeeting", "Creates and Ad Hoc meeting for on hour or until the next meeting", ConsoleAccessLevelEnum.AccessOperator); @@ -327,7 +337,7 @@ namespace PepperDash.Essentials.Core.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.CurrentRoomSourceName.JoinNumber, "Display 1 - Current Source", eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; @@ -377,24 +387,24 @@ namespace PepperDash.Essentials.Core.Fusion var response = string.Empty; - var systemReboot = FusionRoom.CreateOffsetBoolSig(74, "Processor - Reboot", eSigIoMask.OutputSigOnly); + var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, "Processor - Reboot", eSigIoMask.OutputSigOnly); systemReboot.OutputSig.SetSigFalseAction( () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); } protected void SetUpEthernetValues() { - _ip1 = FusionRoom.CreateOffsetStringSig(50, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); - _ip2 = FusionRoom.CreateOffsetStringSig(51, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); - _gateway = FusionRoom.CreateOffsetStringSig(52, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); - _hostname = FusionRoom.CreateOffsetStringSig(53, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); - _domain = FusionRoom.CreateOffsetStringSig(54, "Info - Processor - Domain", eSigIoMask.InputSigOnly); - _dns1 = FusionRoom.CreateOffsetStringSig(55, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); - _dns2 = FusionRoom.CreateOffsetStringSig(56, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); - _mac1 = FusionRoom.CreateOffsetStringSig(57, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); - _mac2 = FusionRoom.CreateOffsetStringSig(58, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); - _netMask1 = FusionRoom.CreateOffsetStringSig(59, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); - _netMask2 = FusionRoom.CreateOffsetStringSig(60, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); + _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, "Info - Processor - Domain", eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); } protected void GetProcessorEthernetValues() @@ -447,13 +457,13 @@ namespace PepperDash.Essentials.Core.Fusion protected void GetProcessorInfo() { - _firmware = FusionRoom.CreateOffsetStringSig(61, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); + _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) { for (var i = 0; i < Global.ControlSystem.NumProgramsSupported; i++) { - var join = 62 + i; + var join = JoinMap.ProgramNameStart.JoinNumber + i; var progNum = i + 1; _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); @@ -484,57 +494,60 @@ namespace PepperDash.Essentials.Core.Fusion { if (args.DeviceOnLine) { - CrestronEnvironment.Sleep(200); - - // Send Push Notification Action request: - - const string requestId = "InitialPushRequest"; - - - var actionRequest = - string.Format("\n{0}\n", requestId) + - "RegisterPushModel\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - - Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); - - FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; - - GetCustomProperties(); - - // Request current Fusion Server Time - RequestLocalDateTime(null); - - // Setup timer to request time daily - if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) + CrestronInvoke.BeginInvoke((o) => { - _dailyTimeRequestTimer.Stop(); - _dailyTimeRequestTimer.Dispose(); - } + CrestronEnvironment.Sleep(200); - _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + // Send Push Notification Action request: - _dailyTimeRequestTimer.Reset(86400000, 86400000); + const string requestId = "InitialPushRequest"; + + + var actionRequest = + string.Format("\n{0}\n", requestId) + + "RegisterPushModel\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + + Debug.Console(2, this, "Sending Fusion ActionRequest: \n{0}", actionRequest); + + FusionRoom.ExtenderFusionRoomDataReservedSigs.ActionQuery.StringValue = actionRequest; + + GetCustomProperties(); + + // Request current Fusion Server Time + RequestLocalDateTime(null); + + // Setup timer to request time daily + if (_dailyTimeRequestTimer != null && !_dailyTimeRequestTimer.Disposed) + { + _dailyTimeRequestTimer.Stop(); + _dailyTimeRequestTimer.Dispose(); + } + + _dailyTimeRequestTimer = new CTimer(RequestLocalDateTime, null, 86400000, 86400000); + + _dailyTimeRequestTimer.Reset(86400000, 86400000); + }); } } @@ -641,7 +654,7 @@ namespace PepperDash.Essentials.Core.Fusion /// /// Creates and Ad Hoc meeting with a duration of 1 hour, or until the next meeting if in less than 1 hour. /// - public void CreateAsHocMeeting(string command) + public void CreateAdHocMeeting(string command) { const string requestId = "CreateAdHocMeeting"; @@ -1031,9 +1044,9 @@ namespace PepperDash.Essentials.Core.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source TV " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 5) // We only have five spots + if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots { break; } @@ -1043,9 +1056,9 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source DVD " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 5) // We only have five spots + if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots { break; } @@ -1055,9 +1068,9 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 10) // We only have ten spots??? + if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? { break; } @@ -1180,12 +1193,12 @@ namespace PepperDash.Essentials.Core.Fusion { attrNum = attrNum + touchpanelNum; - if (attrNum > 10) + if (attrNum > JoinMap.XpanelOnlineStart.JoinSpan) { continue; } attrName = "Online - XPanel " + attrNum; - attrNum += 160; + attrNum += JoinMap.XpanelOnlineStart.JoinNumber; touchpanelNum++; } @@ -1193,12 +1206,12 @@ namespace PepperDash.Essentials.Core.Fusion { attrNum = attrNum + xpanelNum; - if (attrNum > 10) + if (attrNum > JoinMap.TouchpanelOnlineStart.JoinSpan) { continue; } attrName = "Online - Touch Panel " + attrNum; - attrNum += 150; + attrNum += JoinMap.TouchpanelOnlineStart.JoinNumber; xpanelNum++; } @@ -1208,12 +1221,12 @@ namespace PepperDash.Essentials.Core.Fusion if (dev is DisplayBase) { attrNum = attrNum + displayNum; - if (attrNum > 10) + if (attrNum > JoinMap.DisplayOnlineStart.JoinSpan) { continue; } attrName = "Online - Display " + attrNum; - attrNum += 170; + attrNum += JoinMap.DisplayOnlineStart.JoinNumber; displayNum++; } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs new file mode 100644 index 00000000..2d220cf9 --- /dev/null +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Essentials.Core.Bridges; + + +namespace PepperDash.Essentials.Core.Fusion +{ + public class FusionRoomJoinMap : JoinMapBaseAdvanced + { + + // Processor Attributes + [JoinName("ProcessorIp1")] + public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor IP Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorIp2")] + public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor IP Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorGateway")] + public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor Gateway Address", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorHostname")] + public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDomain")] + public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDns1")] + public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorDns2")] + public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorMac1")] + public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor MAC Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorMac2")] + public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor MAC Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorNetMask1")] + public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor NetMask Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorNetMask2")] + public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor NetMask Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorFirmware")] + public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor Firmware Version", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProgramNameStart")] + public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10 }, + new JoinMetadata { Description = "Program Names", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("ProcessorReboot")] + public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1 }, + new JoinMetadata { Description = "Processor Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); + + + // Source Attributes + [JoinName("CurrentRoomSourceName")] + public JoinDataComplete CurrentRoomSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 }, + new JoinMetadata { Description = "Current Room Source Name", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("TouchpanelOnlineStart")] + public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10 }, + new JoinMetadata { Description = "Touchpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("XpanelOnlineStart")] + public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 10 }, + new JoinMetadata { Description = "Xpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("DisplayOnlineStart")] + public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10 }, + new JoinMetadata { Description = "Display Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1LaptopSourceStart")] + public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 10 }, + new JoinMetadata { Description = "Display 1 - Source Laptop Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1DiscPlayerSourceStart")] + public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5 }, + new JoinMetadata { Description = "Display 1 - Source Disc Player Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + [JoinName("Display1SetTopBoxSourceStart")] + public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5 }, + new JoinMetadata { Description = "Display 1 - Source TV Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + + + /// + /// Constructor to use when instantiating this Join Map without inheriting from it + /// + /// Join this join map will start at + public FusionRoomJoinMap(uint joinStart) + : base(joinStart, typeof(FusionRoomJoinMap)) + { + + } + + /// + /// Constructor to use when extending this Join map + /// + /// Join this join map will start at + /// Type of the child join map + public FusionRoomJoinMap(uint joinStart, Type type) : base(joinStart, type) + { + } + } +} \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index ad5df2e3..de932918 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -327,7 +327,10 @@ namespace PepperDash.Essentials.Core None = 0, ToSIMPL = 1, FromSIMPL = 2, - ToFromSIMPL = ToSIMPL | FromSIMPL + ToFromSIMPL = ToSIMPL | FromSIMPL, + ToFusion = 4, + FromFusion = 8, + ToFromFusion = ToFusion | FromFusion, } [Flags] @@ -340,7 +343,7 @@ namespace PepperDash.Essentials.Core DigitalAnalog = Digital | Analog, DigitalSerial = Digital | Serial, AnalogSerial = Analog | Serial, - DigitalAnalogSerial = Digital | Analog | Serial + DigitalAnalogSerial = Digital | Analog | Serial, } /// diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 49f4b25a..0443bd7f 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -220,6 +220,7 @@ + From 708ee1a8ffeabcf7e1910be855908135853a31f2 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 30 Dec 2020 17:01:55 -0700 Subject: [PATCH 64/89] #535 Wraps up EssentialsHuddleSpaceFusionSystemControllerBase. Starts on Vtc1FusionController --- .../Fusion/EssentialsHuddleVtc1FusionController.cs | 2 +- ...sentialsHuddleSpaceFusionSystemControllerBase.cs | 10 +++++----- .../Fusion/FusionRoomJoinMap.cs | 13 +++++++++++++ 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 89059cbe..275d7ebe 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -55,7 +55,7 @@ namespace PepperDash.Essentials.Fusion // Map FusionRoom Attributes: // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, "Volume - Fader01", eSigIoMask.InputOutputSig); codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index afaee291..ef30e153 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core.Fusion { public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider { - FusionRoomJoinMap JoinMap; + protected FusionRoomJoinMap JoinMap; private const string RemoteOccupancyXml = "Local{0}"; private readonly bool _guidFileExists; @@ -494,7 +494,7 @@ namespace PepperDash.Essentials.Core.Fusion { if (args.DeviceOnLine) { - CrestronInvoke.BeginInvoke((o) => + CrestronInvoke.BeginInvoke( (o) => { CrestronEnvironment.Sleep(200); @@ -1303,7 +1303,7 @@ namespace PepperDash.Essentials.Core.Fusion FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - MapDisplayToRoomJoins(1, 158, defaultDisplay); + MapDisplayToRoomJoins(1, JoinMap.Display1Start.JoinNumber, defaultDisplay); var deviceConfig = @@ -1359,7 +1359,7 @@ namespace PepperDash.Essentials.Core.Fusion /// /// /// a - protected virtual void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + protected virtual void MapDisplayToRoomJoins(int displayIndex, uint joinOffset, DisplayBase display) { var displayName = string.Format("Display {0} - ", displayIndex); @@ -1370,7 +1370,7 @@ namespace PepperDash.Essentials.Core.Fusion return; } // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, "Volume - Fader01", eSigIoMask.InputOutputSig); defaultDisplayVolume.OutputSig.UserObject = new Action(b => { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs index 2d220cf9..17ed10a8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs @@ -69,12 +69,20 @@ namespace PepperDash.Essentials.Core.Fusion public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1 }, new JoinMetadata { Description = "Processor Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); + // Volume Controls + [JoinName("VolumeFader1")] + public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, + new JoinMetadata { Description = "Volume - Fader 1", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); + + // Source Attributes [JoinName("CurrentRoomSourceName")] public JoinDataComplete CurrentRoomSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 }, new JoinMetadata { Description = "Current Room Source Name", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + // Device Online Status [JoinName("TouchpanelOnlineStart")] public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10 }, new JoinMetadata { Description = "Touchpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); @@ -99,6 +107,11 @@ namespace PepperDash.Essentials.Core.Fusion public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5 }, new JoinMetadata { Description = "Display 1 - Source TV Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + // Display 1 + [JoinName("Display1Start")] + public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 158, JoinSpan = 1 }, + new JoinMetadata { Description = "Display 1 Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + /// /// Constructor to use when instantiating this Join Map without inheriting from it From de4f003c67acca043867a9aac3aee4ff6f887300 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 4 Jan 2021 12:10:12 -0700 Subject: [PATCH 65/89] Swapped out hardcoded joins for join map in Vtc1FusionController --- .../EssentialsHuddleVtc1FusionController.cs | 24 ++++++++--------- ...lsHuddleSpaceFusionSystemControllerBase.cs | 4 +-- ...ntialsHuddleSpaceRoomFusionRoomJoinMap.cs} | 27 ++++++++++++++----- .../PepperDash_Essentials_Core.csproj | 2 +- 4 files changed, 36 insertions(+), 21 deletions(-) rename essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/{FusionRoomJoinMap.cs => EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs} (82%) diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 275d7ebe..8028271e 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -60,14 +60,14 @@ namespace PepperDash.Essentials.Fusion (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); codec.CallStatusChange += new EventHandler(codec_CallStatusChange); // Online status if (codec is ICommunicationMonitor) { var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(122, "Online - VC 1", eSigIoMask.InputSigOnly); + var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, "Online - VC 1", eSigIoMask.InputSigOnly); codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; c.CommunicationMonitor.StatusChange += (o, a) => { @@ -101,10 +101,10 @@ namespace PepperDash.Essentials.Fusion if (codecHasIpInfo) { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, "IP Address - VC", eSigIoMask.InputSigOnly); codecIpAddressSig.InputSig.StringValue = codecIpAddress; - codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, "IP Port - VC", eSigIoMask.InputSigOnly); codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); } @@ -172,7 +172,7 @@ namespace PepperDash.Essentials.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(84, "Display 1 - Current Source", eSigIoMask.InputSigOnly); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.CurrentRoomSourceName.JoinNumber, "Display 1 - Current Source", eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; @@ -197,9 +197,9 @@ namespace PepperDash.Essentials.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs("Display 1 - Source TV " + i, 188 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source TV " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 5) // We only have five spots + if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots break; } @@ -207,7 +207,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, 181 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source DVD " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > 5) // We only have five spots break; @@ -217,9 +217,9 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, 166 + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs("Display 1 - Source Laptop " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; - if (i > 10) // We only have ten spots??? + if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? break; } @@ -283,7 +283,7 @@ namespace PepperDash.Essentials.Fusion if (defaultDisplay is IDisplayUsage) (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - MapDisplayToRoomJoins(1, 158, defaultDisplay); + MapDisplayToRoomJoins(1, JoinMap.Display1Start.JoinNumber, defaultDisplay); var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); @@ -328,7 +328,7 @@ namespace PepperDash.Essentials.Fusion } - protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + protected override void MapDisplayToRoomJoins(int displayIndex, uint joinOffset, DisplayBase display) { string displayName = string.Format("Display {0} - ", displayIndex); diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index ef30e153..2ca546cc 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -16,7 +16,7 @@ namespace PepperDash.Essentials.Core.Fusion { public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider { - protected FusionRoomJoinMap JoinMap; + protected EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap; private const string RemoteOccupancyXml = "Local{0}"; private readonly bool _guidFileExists; @@ -91,7 +91,7 @@ namespace PepperDash.Essentials.Core.Fusion { try { - JoinMap = new FusionRoomJoinMap(1); + JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1); if (!string.IsNullOrEmpty(joinMapKey)) { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs similarity index 82% rename from essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs rename to essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs index 17ed10a8..d388f464 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/FusionRoomJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs @@ -9,7 +9,7 @@ using PepperDash.Essentials.Core.Bridges; namespace PepperDash.Essentials.Core.Fusion { - public class FusionRoomJoinMap : JoinMapBaseAdvanced + public class EssentialsHuddleSpaceRoomFusionRoomJoinMap : JoinMapBaseAdvanced { // Processor Attributes @@ -74,7 +74,22 @@ namespace PepperDash.Essentials.Core.Fusion public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, new JoinMetadata { Description = "Volume - Fader 1", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); + // Codec Info + [JoinName("VcCodecInCall")] + public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1 }, + new JoinMetadata { Description = "VC Codec In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + [JoinName("VcCodecOnline")] + public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1 }, + new JoinMetadata { Description = "VC Codec Online", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + + [JoinName("VcCodecIpAddress")] + public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1 }, + new JoinMetadata { Description = "VC Codec IP Address", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + + [JoinName("VcCodecIpPort")] + public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1 }, + new JoinMetadata { Description = "VC Codec IP Port", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); // Source Attributes [JoinName("CurrentRoomSourceName")] @@ -88,7 +103,7 @@ namespace PepperDash.Essentials.Core.Fusion new JoinMetadata { Description = "Touchpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("XpanelOnlineStart")] - public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 10 }, + public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5 }, new JoinMetadata { Description = "Xpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("DisplayOnlineStart")] @@ -96,7 +111,7 @@ namespace PepperDash.Essentials.Core.Fusion new JoinMetadata { Description = "Display Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("Display1LaptopSourceStart")] - public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 10 }, + public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5 }, new JoinMetadata { Description = "Display 1 - Source Laptop Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); [JoinName("Display1DiscPlayerSourceStart")] @@ -117,8 +132,8 @@ namespace PepperDash.Essentials.Core.Fusion /// Constructor to use when instantiating this Join Map without inheriting from it /// /// Join this join map will start at - public FusionRoomJoinMap(uint joinStart) - : base(joinStart, typeof(FusionRoomJoinMap)) + public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart) + : base(joinStart, typeof(EssentialsHuddleSpaceRoomFusionRoomJoinMap)) { } @@ -128,7 +143,7 @@ namespace PepperDash.Essentials.Core.Fusion /// /// Join this join map will start at /// Type of the child join map - public FusionRoomJoinMap(uint joinStart, Type type) : base(joinStart, type) + public EssentialsHuddleSpaceRoomFusionRoomJoinMap(uint joinStart, Type type) : base(joinStart, type) { } } diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj index 0443bd7f..e97d30f7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.csproj @@ -220,7 +220,7 @@ - + From 4a642b1e3684e17e738a3416a488c17bd1567848 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 4 Jan 2021 16:19:08 -0700 Subject: [PATCH 66/89] Adds UserObject for future use purposes --- .../PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index de932918..3abfb8c7 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -411,6 +411,8 @@ namespace PepperDash.Essentials.Core /// [JsonProperty("joinSpan")] public uint JoinSpan { get; set; } + + } /// @@ -422,6 +424,10 @@ namespace PepperDash.Essentials.Core private JoinData _data; public JoinMetadata Metadata { get; set; } + /// + /// To store some future information as you please + /// + public object UserObject { get; private set; } public JoinDataComplete(JoinData data, JoinMetadata metadata) { From d311b6fef9b1747bc2cb444751da8fe4433174b8 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 5 Jan 2021 18:21:32 -0700 Subject: [PATCH 67/89] Adds AttributeName property to JoinData allowing attribute name to be set from config as well --- .../EssentialsHuddleVtc1FusionController.cs | 22 ++-- ...lsHuddleSpaceFusionSystemControllerBase.cs | 46 ++++---- ...entialsHuddleSpaceRoomFusionRoomJoinMap.cs | 106 +++++++++--------- .../JoinMaps/JoinMapBase.cs | 18 ++- 4 files changed, 101 insertions(+), 91 deletions(-) diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 8028271e..99c07348 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -55,25 +55,25 @@ namespace PepperDash.Essentials.Fusion // Map FusionRoom Attributes: // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, "Volume - Fader01", eSigIoMask.InputOutputSig); + var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.Metadata.Description, eSigIoMask.InputOutputSig); codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, JoinMap.VcCodecInCall.Metadata.Description, eSigIoMask.InputSigOnly); codec.CallStatusChange += new EventHandler(codec_CallStatusChange); // Online status if (codec is ICommunicationMonitor) { var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, "Online - VC 1", eSigIoMask.InputSigOnly); + var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, JoinMap.VcCodecOnline.Metadata.Description, eSigIoMask.InputSigOnly); codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; c.CommunicationMonitor.StatusChange += (o, a) => { codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, "Online - VC 1"); + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, JoinMap.VcCodecOnline.Metadata.Description); } // Codec IP Address @@ -101,10 +101,10 @@ namespace PepperDash.Essentials.Fusion if (codecHasIpInfo) { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, JoinMap.VcCodecIpAddress.Metadata.Description, eSigIoMask.InputSigOnly); codecIpAddressSig.InputSig.StringValue = codecIpAddress; - codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, JoinMap.VcCodecIpPort.Metadata.Description, eSigIoMask.InputSigOnly); codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); } @@ -123,7 +123,7 @@ namespace PepperDash.Essentials.Fusion FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); } - var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Codec", tempAsset.InstanceId); codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); @@ -172,7 +172,7 @@ namespace PepperDash.Essentials.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.CurrentRoomSourceName.JoinNumber, "Display 1 - Current Source", eSigIoMask.InputSigOnly); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.Metadata.Description, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; @@ -197,7 +197,7 @@ namespace PepperDash.Essentials.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs("Display 1 - Source TV " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots break; @@ -207,7 +207,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > 5) // We only have five spots break; @@ -217,7 +217,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.Metadata.Description + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? break; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 2ca546cc..03e1c090 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -93,6 +93,8 @@ namespace PepperDash.Essentials.Core.Fusion { JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1); + CrestronConsole.AddNewConsoleCommand((o) => JoinMap.PrintJoinMapInfo(), string.Format("ptjnmp-{0}", Key), "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator); + if (!string.IsNullOrEmpty(joinMapKey)) { var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); @@ -337,7 +339,7 @@ namespace PepperDash.Essentials.Core.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.CurrentRoomSourceName.JoinNumber, "Display 1 - Current Source", + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.Metadata.Description, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; @@ -387,24 +389,24 @@ namespace PepperDash.Essentials.Core.Fusion var response = string.Empty; - var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, "Processor - Reboot", eSigIoMask.OutputSigOnly); + var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, JoinMap.ProcessorReboot.Metadata.Description, eSigIoMask.OutputSigOnly); systemReboot.OutputSig.SetSigFalseAction( () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); } protected void SetUpEthernetValues() { - _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, "Info - Processor - IP 1", eSigIoMask.InputSigOnly); - _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, "Info - Processor - IP 2", eSigIoMask.InputSigOnly); - _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, "Info - Processor - Gateway", eSigIoMask.InputSigOnly); - _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, "Info - Processor - Hostname", eSigIoMask.InputSigOnly); - _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, "Info - Processor - Domain", eSigIoMask.InputSigOnly); - _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, "Info - Processor - DNS 1", eSigIoMask.InputSigOnly); - _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, "Info - Processor - DNS 2", eSigIoMask.InputSigOnly); - _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, "Info - Processor - MAC 1", eSigIoMask.InputSigOnly); - _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, "Info - Processor - MAC 2", eSigIoMask.InputSigOnly); - _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, "Info - Processor - Net Mask 1", eSigIoMask.InputSigOnly); - _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, "Info - Processor - Net Mask 2", eSigIoMask.InputSigOnly); + _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.Metadata.Description, eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, JoinMap.ProcessorIp2.Metadata.Description, eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, JoinMap.ProcessorGateway.Metadata.Description, eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, JoinMap.ProcessorHostname.Metadata.Description, eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, JoinMap.ProcessorDomain.Metadata.Description, eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, JoinMap.ProcessorDns1.Metadata.Description, eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, JoinMap.ProcessorDns2.Metadata.Description, eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, JoinMap.ProcessorMac1.Metadata.Description, eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, JoinMap.ProcessorMac2.Metadata.Description, eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, JoinMap.ProcessorNetMask1.Metadata.Description, eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.Metadata.Description, eSigIoMask.InputSigOnly); } protected void GetProcessorEthernetValues() @@ -457,7 +459,7 @@ namespace PepperDash.Essentials.Core.Fusion protected void GetProcessorInfo() { - _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, "Info - Processor - Firmware", eSigIoMask.InputSigOnly); + _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.Metadata.Description, eSigIoMask.InputSigOnly); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) { @@ -466,7 +468,7 @@ namespace PepperDash.Essentials.Core.Fusion var join = JoinMap.ProgramNameStart.JoinNumber + i; var progNum = i + 1; _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, - string.Format("Info - Processor - Program {0}", progNum), eSigIoMask.InputSigOnly); + string.Format("{0} {1}", JoinMap.ProgramNameStart.Metadata.Description, progNum), eSigIoMask.InputSigOnly); } } @@ -1044,7 +1046,7 @@ namespace PepperDash.Essentials.Core.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs("Display 1 - Source TV " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.Metadata.Description + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots { @@ -1056,7 +1058,7 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs("Display 1 - Source DVD " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots { @@ -1068,7 +1070,7 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs("Display 1 - Source Laptop " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.Metadata.Description + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? { @@ -1197,7 +1199,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = "Online - XPanel " + attrNum; + attrName = JoinMap.XpanelOnlineStart.Metadata.Description + " " + attrNum; attrNum += JoinMap.XpanelOnlineStart.JoinNumber; touchpanelNum++; @@ -1210,7 +1212,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = "Online - Touch Panel " + attrNum; + attrName = JoinMap.TouchpanelOnlineStart.Metadata.Description + " " + attrNum; attrNum += JoinMap.TouchpanelOnlineStart.JoinNumber; xpanelNum++; @@ -1225,7 +1227,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = "Online - Display " + attrNum; + attrName = JoinMap.DisplayOnlineStart.Metadata.Description + " " + attrNum; attrNum += JoinMap.DisplayOnlineStart.JoinNumber; displayNum++; @@ -1370,7 +1372,7 @@ namespace PepperDash.Essentials.Core.Fusion return; } // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, "Volume - Fader01", + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.Metadata.Description, eSigIoMask.InputOutputSig); defaultDisplayVolume.OutputSig.UserObject = new Action(b => { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs index d388f464..0b61fed2 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs @@ -14,113 +14,113 @@ namespace PepperDash.Essentials.Core.Fusion // Processor Attributes [JoinName("ProcessorIp1")] - public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor IP Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Info - Processor - IP 1" }, + new JoinMetadata { Description = "Info - Processor - IP 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorIp2")] - public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor IP Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = "Info - Processor - IP 2" }, + new JoinMetadata { Description = "Info - Processor - IP 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorGateway")] - public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor Gateway Address", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = "Info - Processor - Gateway" }, + new JoinMetadata { Description = "Info - Processor - Gateway", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorHostname")] - public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = "Info - Processor - Hostname" }, + new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorDomain")] - public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - Domain" }, + new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorDns1")] - public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - DNS 1" }, + new JoinMetadata { Description = "Info - Processor - DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorDns2")] - public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = "Info - Processor - DNS 2" }, + new JoinMetadata { Description = "Info - Processor - DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorMac1")] - public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor MAC Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = "Info - Processor - MAC 1" }, + new JoinMetadata { Description = "Info - Processor - MAC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorMac2")] - public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor MAC Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = "Info - Processor - MAC 2" }, + new JoinMetadata { Description = "Info - Processor - MAC 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorNetMask1")] - public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor NetMask Address 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 1" }, + new JoinMetadata { Description = "Info - Processor - Net Mask 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorNetMask2")] - public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor NetMask Address 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 2" }, + new JoinMetadata { Description = "Info - Processor - Net Mask 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorFirmware")] - public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor Firmware Version", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = "Info - Processor - Firmware" }, + new JoinMetadata { Description = "Info - Processor - Firmware", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProgramNameStart")] - public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10 }, - new JoinMetadata { Description = "Program Names", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = "Info - Processor - Program" }, + new JoinMetadata { Description = "Info - Processor - Program", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorReboot")] - public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1 }, - new JoinMetadata { Description = "Processor Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = "Processor - Reboot" }, + new JoinMetadata { Description = "Processor - Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital }); // Volume Controls [JoinName("VolumeFader1")] - public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1 }, - new JoinMetadata { Description = "Volume - Fader 1", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); + public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Volume - Fader01" }, + new JoinMetadata { Description = "Volume - Fader01", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog }); // Codec Info [JoinName("VcCodecInCall")] - public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1 }, - new JoinMetadata { Description = "VC Codec In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = "Conf - VC 1 In Call" }, + new JoinMetadata { Description = "Conf - VC 1 In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("VcCodecOnline")] - public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1 }, - new JoinMetadata { Description = "VC Codec Online", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = "Online - VC 1" }, + new JoinMetadata { Description = "Online - VC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("VcCodecIpAddress")] - public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1 }, - new JoinMetadata { Description = "VC Codec IP Address", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = "IP Address - VC" }, + new JoinMetadata { Description = "IP Address - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("VcCodecIpPort")] - public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1 }, - new JoinMetadata { Description = "VC Codec IP Port", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = "IP Port - VC" }, + new JoinMetadata { Description = "IP Port - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); // Source Attributes - [JoinName("CurrentRoomSourceName")] - public JoinDataComplete CurrentRoomSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1 }, - new JoinMetadata { Description = "Current Room Source Name", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); + [JoinName("Display1CurrentSourceName")] + public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = "Display 1 - Current Source" }, + new JoinMetadata { Description = "Display 1 - Current Source", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); // Device Online Status [JoinName("TouchpanelOnlineStart")] - public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10 }, - new JoinMetadata { Description = "Touchpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = "Online - Touch Panel" }, + new JoinMetadata { Description = "Online - Touch Panel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("XpanelOnlineStart")] - public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5 }, - new JoinMetadata { Description = "Xpanel Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = "Online - XPanel" }, + new JoinMetadata { Description = "Online - XPanel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("DisplayOnlineStart")] - public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10 }, - new JoinMetadata { Description = "Display Online Start", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = "Online - Display" }, + new JoinMetadata { Description = "Online - Display", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital }); [JoinName("Display1LaptopSourceStart")] - public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5 }, - new JoinMetadata { Description = "Display 1 - Source Laptop Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" }, + new JoinMetadata { Description = "Display 1 - Source Laptop", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); [JoinName("Display1DiscPlayerSourceStart")] - public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5 }, - new JoinMetadata { Description = "Display 1 - Source Disc Player Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" }, + new JoinMetadata { Description = "Display 1 - Source Disc Player", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); [JoinName("Display1SetTopBoxSourceStart")] - public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5 }, - new JoinMetadata { Description = "Display 1 - Source TV Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); + public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5, AttributeName = "Display 1 - Source TV" }, + new JoinMetadata { Description = "Display 1 - Source TV", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital }); // Display 1 [JoinName("Display1Start")] diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 3abfb8c7..4fe2d537 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -266,7 +266,7 @@ namespace PepperDash.Essentials.Core @"Join Number: {0} | JoinSpan: '{1}' | Description: '{2}' | Type: '{3}' | Capabilities: '{4}'", join.Value.JoinNumber, join.Value.JoinSpan, - String.IsNullOrEmpty(join.Value.Metadata.Description) ? join.Value.Metadata.Label: join.Value.Metadata.Description, + String.IsNullOrEmpty(join.Value.Metadata.Description) ? join.Value.Metadata.Label : join.Value.Metadata.Description, join.Value.Metadata.JoinType.ToString(), join.Value.Metadata.JoinCapabilities.ToString()); } @@ -288,7 +288,7 @@ namespace PepperDash.Essentials.Core } else { - Debug.Console(2, "No mathcing key found in join map for: '{0}'", customJoinData.Key); + Debug.Console(2, "No matching key found in join map for: '{0}'", customJoinData.Key); } } @@ -397,7 +397,7 @@ namespace PepperDash.Essentials.Core } /// - /// Data describing the join. Can be + /// Data describing the join. Can be overridden from configuratino /// public class JoinData { @@ -411,8 +411,11 @@ namespace PepperDash.Essentials.Core /// [JsonProperty("joinSpan")] public uint JoinSpan { get; set; } - - + /// + /// Fusion Attribute Name (optional) + /// + [JsonProperty("attributeName")] + public string AttributeName { get; set; } } /// @@ -458,6 +461,11 @@ namespace PepperDash.Essentials.Core get { return _data.JoinSpan; } } + public string AttributeName + { + get { return _data.AttributeName; } + } + public void SetCustomJoinData(JoinData customJoinData) { _data = customJoinData; From 9b62849d9dcb9c3cc1d7d470445f3306b07c8106 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 6 Jan 2021 14:45:40 -0700 Subject: [PATCH 68/89] removes old comment --- .../Fusion/EssentialsHuddleVtc1FusionController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index 99c07348..b071b681 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -179,7 +179,6 @@ namespace PepperDash.Essentials.Fusion FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff", Room.SourceListKey)); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; From 72197e547f0aacf7b55506c0530af238523c506d Mon Sep 17 00:00:00 2001 From: jkdevito Date: Wed, 6 Jan 2021 16:22:54 -0600 Subject: [PATCH 69/89] Updated VideoCodecBase.cs and ZoomRooms.cs to resolve an issue with dialing the currently scheduled meetings. VideoCodecBase.cs changes: 1. Moved the trilist.SetSigFalseActions for DialMeeting 1-3 from the UpdateMeetinsgList mehtod to the LinkVideoCodecScheduleToApi method. - This was necessary to resolve an issue with dialing current meetings. - When the SetSigFalseActions where located in the UpdateMeetingsList, they were using the codec.CodecSchedule.Meetings to dial, which was not updated based on the time of day. 2. Turned the local var currentMeetings into a private list, _currentMeetings, that can be updated by UpdateMeetingsList and then used in LinkVideoCodecScheduleToApi when the trilist actions are executed. 3. Added debug statements to help narrow down the issue and verify the data. ZoomRoom.cs changes: 1. Added debug statement to Dial(Meeting meeting) method to confirm the data passed from LinkVideoCodecScheduleToApi was matching the _currentMeetings list data. --- .../VideoCodec/VideoCodecBase.cs | 2528 +++++++++-------- .../VideoCodec/ZoomRoom/ZoomRoom.cs | 1 + 2 files changed, 1276 insertions(+), 1253 deletions(-) diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs index 0977dea2..6b6d5891 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -1,1257 +1,1279 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharp.Ssh; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharp; -using PepperDash.Core; -using PepperDash.Core.Intersystem; -using PepperDash.Core.Intersystem.Tokens; -using PepperDash.Core.WebApi.Presets; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.Devices; -using PepperDash.Essentials.Core.DeviceTypeInterfaces; -using PepperDash.Essentials.Core.Routing; -using PepperDash.Essentials.Devices.Common.Cameras; -using PepperDash.Essentials.Devices.Common.Codec; -using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; -using PepperDash_Essentials_Core.Bridges.JoinMaps; -using PepperDash_Essentials_Core.DeviceTypeInterfaces; -using Feedback = PepperDash.Essentials.Core.Feedback; - -namespace PepperDash.Essentials.Devices.Common.VideoCodec -{ - public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, - IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced - { - private const int XSigEncoding = 28591; - private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); - protected VideoCodecBase(DeviceConfig config) - : base(config) - { - - StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); - PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); - VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); - MuteFeedback = new BoolFeedback(MuteFeedbackFunc); - SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); - SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); - - InputPorts = new RoutingPortCollection(); - OutputPorts = new RoutingPortCollection(); - - ActiveCalls = new List(); - } - - public IBasicCommunication Communication { get; protected set; } - - /// - /// An internal pseudo-source that is routable and connected to the osd input - /// - public DummyRoutingInputsDevice OsdSource { get; protected set; } - - public BoolFeedback StandbyIsOnFeedback { get; private set; } - - protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } - protected abstract Func VolumeLevelFeedbackFunc { get; } - protected abstract Func MuteFeedbackFunc { get; } - protected abstract Func StandbyIsOnFeedbackFunc { get; } - - public List ActiveCalls { get; set; } - - public bool ShowSelfViewByDefault { get; protected set; } - - protected bool SupportsCameraOff; - protected bool SupportsCameraAutoMode; - - public bool IsReady { get; protected set; } - - public virtual List Feedbacks - { - get - { - return new List +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharp.Ssh; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharp; +using PepperDash.Core; +using PepperDash.Core.Intersystem; +using PepperDash.Core.Intersystem.Tokens; +using PepperDash.Core.WebApi.Presets; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Devices; +using PepperDash.Essentials.Core.DeviceTypeInterfaces; +using PepperDash.Essentials.Core.Routing; +using PepperDash.Essentials.Devices.Common.Cameras; +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec.Interfaces; +using PepperDash_Essentials_Core.Bridges.JoinMaps; +using PepperDash_Essentials_Core.DeviceTypeInterfaces; +using Feedback = PepperDash.Essentials.Core.Feedback; + +namespace PepperDash.Essentials.Devices.Common.VideoCodec +{ + public abstract class VideoCodecBase : ReconfigurableDevice, IRoutingInputsOutputs, + IUsageTracking, IHasDialer, IHasContentSharing, ICodecAudio, iVideoCodecInfo, IBridgeAdvanced + { + private const int XSigEncoding = 28591; + private readonly byte[] _clearBytes = XSigHelpers.ClearOutputs(); + protected VideoCodecBase(DeviceConfig config) + : base(config) + { + + StandbyIsOnFeedback = new BoolFeedback(StandbyIsOnFeedbackFunc); + PrivacyModeIsOnFeedback = new BoolFeedback(PrivacyModeIsOnFeedbackFunc); + VolumeLevelFeedback = new IntFeedback(VolumeLevelFeedbackFunc); + MuteFeedback = new BoolFeedback(MuteFeedbackFunc); + SharingSourceFeedback = new StringFeedback(SharingSourceFeedbackFunc); + SharingContentIsOnFeedback = new BoolFeedback(SharingContentIsOnFeedbackFunc); + + InputPorts = new RoutingPortCollection(); + OutputPorts = new RoutingPortCollection(); + + ActiveCalls = new List(); + } + + public IBasicCommunication Communication { get; protected set; } + + /// + /// An internal pseudo-source that is routable and connected to the osd input + /// + public DummyRoutingInputsDevice OsdSource { get; protected set; } + + public BoolFeedback StandbyIsOnFeedback { get; private set; } + + protected abstract Func PrivacyModeIsOnFeedbackFunc { get; } + protected abstract Func VolumeLevelFeedbackFunc { get; } + protected abstract Func MuteFeedbackFunc { get; } + protected abstract Func StandbyIsOnFeedbackFunc { get; } + + public List ActiveCalls { get; set; } + + public bool ShowSelfViewByDefault { get; protected set; } + + protected bool SupportsCameraOff; + protected bool SupportsCameraAutoMode; + + public bool IsReady { get; protected set; } + + public virtual List Feedbacks + { + get + { + return new List { PrivacyModeIsOnFeedback, SharingSourceFeedback - }; - } - } - - protected abstract Func SharingSourceFeedbackFunc { get; } - protected abstract Func SharingContentIsOnFeedbackFunc { get; } - - #region ICodecAudio Members - - public abstract void PrivacyModeOn(); - public abstract void PrivacyModeOff(); - public abstract void PrivacyModeToggle(); - public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } - - - public BoolFeedback MuteFeedback { get; private set; } - - public abstract void MuteOff(); - - public abstract void MuteOn(); - - public abstract void SetVolume(ushort level); - - public IntFeedback VolumeLevelFeedback { get; private set; } - - public abstract void MuteToggle(); - - public abstract void VolumeDown(bool pressRelease); - - - public abstract void VolumeUp(bool pressRelease); - - #endregion - - #region IHasContentSharing Members - - public abstract void StartSharing(); - public abstract void StopSharing(); - - public bool AutoShareContentWhileInCall { get; protected set; } - - public StringFeedback SharingSourceFeedback { get; private set; } - public BoolFeedback SharingContentIsOnFeedback { get; private set; } - - #endregion - - #region IHasDialer Members - - /// - /// Fires when the status of any active, dialing, or incoming call changes or is new - /// - public event EventHandler CallStatusChange; - - /// - /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected - /// - public bool IsInCall - { - get - { - var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); - return value; - } - } - - public abstract void Dial(string number); - public abstract void EndCall(CodecActiveCallItem call); - public abstract void EndAllCalls(); - public abstract void AcceptCall(CodecActiveCallItem call); - public abstract void RejectCall(CodecActiveCallItem call); - public abstract void SendDtmf(string s); - - #endregion - - #region IRoutingInputsOutputs Members - - public RoutingPortCollection InputPorts { get; private set; } - - public RoutingPortCollection OutputPorts { get; private set; } - - #endregion - - #region IUsageTracking Members - - /// - /// This object can be added by outside users of this class to provide usage tracking - /// for various services - /// - public UsageTracking UsageTracker { get; set; } - - #endregion - - #region iVideoCodecInfo Members - - public VideoCodecInfo CodecInfo { get; protected set; } - - #endregion - - public event EventHandler IsReadyChange; - public abstract void Dial(Meeting meeting); - - public virtual void Dial(IInvitableContact contact) - { - } - - public abstract void ExecuteSwitch(object selector); - - /// - /// Helper method to fire CallStatusChange event with old and new status - /// - protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) - { - call.Status = newStatus; - - OnCallStatusChange(call); - } - - /// - /// - /// - /// - /// - /// - protected virtual void OnCallStatusChange(CodecActiveCallItem item) - { - var handler = CallStatusChange; - if (handler != null) - { - handler(this, new CodecCallStatusItemChangeEventArgs(item)); - } - - if (AutoShareContentWhileInCall) - { - StartSharing(); - } - - if (UsageTracker != null) - { - if (IsInCall && !UsageTracker.UsageTrackingStarted) - { - UsageTracker.StartDeviceUsage(); - } - else if (UsageTracker.UsageTrackingStarted && !IsInCall) - { - UsageTracker.EndDeviceUsage(); - } - } - } - - /// - /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. - /// - protected void SetIsReady() - { - CrestronInvoke.BeginInvoke( (o) => - { - try - { - IsReady = true; - var h = IsReadyChange; - if (h != null) - { - h(this, new EventArgs()); - } - } - catch (Exception e) - { - Debug.Console(2, this, "Error in SetIsReady() : {0}", e); - } - }); - } - - // **** DEBUGGING THINGS **** - /// - /// - /// - public virtual void ListCalls() - { - var sb = new StringBuilder(); - foreach (var c in ActiveCalls) - { - sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); - } - Debug.Console(1, this, "\n{0}\n", sb.ToString()); - } - - public abstract void StandbyActivate(); - - public abstract void StandbyDeactivate(); - - #region Implementation of IBridgeAdvanced - - public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); - - protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, - EiscApiAdvanced bridge) - { - var joinMap = new VideoCodecControllerJoinMap(joinStart); - - var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - - if (customJoins != null) - { - joinMap.SetCustomJoinData(customJoins); - } - - if (bridge != null) - { - bridge.AddJoinMap(Key, joinMap); - } - - Debug.Console(1, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); - - - - LinkVideoCodecDtmfToApi(trilist, joinMap); - - LinkVideoCodecCallControlsToApi(trilist, joinMap); - - LinkVideoCodecContentSharingToApi(trilist, joinMap); - - LinkVideoCodecPrivacyToApi(trilist, joinMap); - - LinkVideoCodecVolumeToApi(trilist, joinMap); - - if (codec is ICommunicationMonitor) - { - LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); - } - - if (codec is IHasCodecCameras) - { - LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); - } - - if (codec is IHasCodecSelfView) - { - LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); - } - - if (codec is IHasCameraAutoMode) - { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); - LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); - } - - if (codec is IHasCameraOff) - { - trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); - LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); - } - - if (codec is IHasCodecLayouts) - { - LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); - } - - if (codec is IHasSelfviewPosition) - { - LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); - } - - if (codec is IHasDirectory) - { - LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); - } - - if (codec is IHasScheduleAwareness) - { - LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); - } - - if (codec is IHasParticipants) - { - LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); - } - - if (codec is IHasFarEndContentStatus) - { - (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); - } - - if (codec is IHasPhoneDialing) - { - LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); - } - - trilist.OnlineStatusChange += (device, args) => - { - if (!args.DeviceOnLine) return; - - if (codec is IHasDirectory) - { - (codec as IHasDirectory).SetCurrentDirectoryToRoot(); - } - - if (codec is IHasScheduleAwareness) - { - (codec as IHasScheduleAwareness).GetSchedule(); - } - - if (codec is IHasParticipants) - { - UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); - } - - if (codec is IHasCameraAutoMode) - { - trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); - - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCodecSelfView) - { - (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraAutoMode) - { - (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); - } - - if (codec is IHasCameraOff) - { - (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); - } - - if (codec is IHasPhoneDialing) - { - (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); - } - - SharingContentIsOnFeedback.FireUpdate(); - - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - }; - } - - private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, - () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); - - trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); - } - - private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); - - codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); - } - - private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); - - codec.CameraIsOffFeedback.OutputChange += (o, a) => - { - if (a.BoolValue) - { - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - - var autoCodec = codec as IHasCameraAutoMode; - - if (autoCodec == null) return; - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); - }; - - if (codec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - - var autoModeCodec = codec as IHasCameraAutoMode; - - if (autoModeCodec == null) return; - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); - } - - private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); - MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); - trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); - trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); - - VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); - - trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); - trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); - - trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); - - } - - private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); - PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); - trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); - trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); - } - - private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); - } - - private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.Participants.ParticipantsListHasChanged += (sender, args) => - { - string participantsXSig; - - if (codec.Participants.CurrentParticipants.Count == 0) - { - participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); - return; - } - - participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); - - trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); - - trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort) codec.Participants.CurrentParticipants.Count); - }; - } - - private string UpdateParticipantsXSig(List currentParticipants) - { - const int maxParticipants = 50; - const int maxDigitals = 5; - const int maxStrings = 1; - const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings * maxParticipants; //15 - var stringIndex = 0; - var meetingIndex = 0; - - var tokenArray = new XSigToken[maxParticipants * offset]; - - foreach (var participant in currentParticipants) - { - if (meetingIndex >= maxParticipants * offset) break; - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - while (meetingIndex < maxParticipants*offset) - { - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); - tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); - tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); - SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); - - SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); - trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); - - trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b); - } - - private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); - - trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => - { - codec.CodecSchedule.MeetingWarningMinutes = i; - }); - - codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); - - codec.CodecSchedule.MeetingEventChange += - (sender, args) => - { - if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) - { - UpdateMeetingsList(codec, trilist, joinMap); - } - }; - } - - private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - var currentTime = DateTime.Now; - var currentMeetings = - codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); - - var meetingsData = UpdateMeetingsListXSig(currentMeetings); - - trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); - - trilist.SetSigFalseAction(joinMap.DialMeeting1.JoinNumber, () => - { - if(codec.CodecSchedule.Meetings[0] != null) - Dial(codec.CodecSchedule.Meetings[0]); - }); - trilist.SetSigFalseAction(joinMap.DialMeeting2.JoinNumber, () => - { - if (codec.CodecSchedule.Meetings[1] != null) - Dial(codec.CodecSchedule.Meetings[1]); - }); - trilist.SetSigFalseAction(joinMap.DialMeeting3.JoinNumber, () => - { - if (codec.CodecSchedule.Meetings[2] != null) - Dial(codec.CodecSchedule.Meetings[2]); - }); - - trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)currentMeetings.Count); - } - - private string UpdateMeetingsListXSig(List meetings) - { - const int maxMeetings = 3; - const int maxDigitals = 2; - const int maxStrings = 7; - const int offset = maxDigitals + maxStrings; - var digitalIndex = maxStrings*maxMeetings; //15 - var stringIndex = 0; - var meetingIndex = 0; - - var tokenArray = new XSigToken[maxMeetings*offset]; - /* - * Digitals - * IsJoinable - 1 - * IsDialable - 2 - * - * Serials - * Organizer - 1 - * Title - 2 - * Start Date - 3 - * Start Time - 4 - * End Date - 5 - * End Time - 6 - */ - - - foreach(var meeting in meetings) - { - var currentTime = DateTime.Now; - - if(meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; - - if (meetingIndex >= maxMeetings*offset) - { - Debug.Console(2, this, "Max Meetings reached"); - break;} - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToShortDateString()); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToShortTimeString()); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToShortDateString()); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToShortTimeString()); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); - - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - while (meetingIndex < maxMeetings*offset) - { - Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", - meetingIndex, maxMeetings*offset); - - //digitals - tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); - tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); - - //serials - tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); - tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); - tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); - - digitalIndex += maxDigitals; - meetingIndex += offset; - stringIndex += maxStrings; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( - trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); - - trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); - - trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); - - trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); - - trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); - - codec.DirectoryResultReturned += (sender, args) => - { - trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort) args.Directory.CurrentDirectoryResults.Count); - - var clearBytes = XSigHelpers.ClearOutputs(); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, - Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); - var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); - - trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); - }; - } - - private void SelectDirectoryEntry(IHasDirectory codec, ushort i) - { - var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; - - if (entry is DirectoryFolder) - { - codec.GetDirectoryFolderContents(entry.FolderId); - return; - } - - var dialableEntry = entry as IInvitableContact; - - if (dialableEntry != null) - { - Dial(dialableEntry); - return; - } - - var entryToDial = entry as DirectoryContact; - - if (entryToDial == null) return; - - Dial(entryToDial.ContactMethods[0].Number); - } - - private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) - { - var contactIndex = 1; - var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; - - foreach(var entry in directory.CurrentDirectoryResults) - { - var arrayIndex = contactIndex - 1; - - if (entry is DirectoryFolder && entry.ParentFolderId == "root") - { - tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); - - contactIndex++; - - continue; - } - - if(isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; - - tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); - - contactIndex++; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, - () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); - - //End All calls for now - trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); - - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - CallStatusChange += (sender, args) => - { - trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); - - Debug.Console(1, this, "Call Direction: {0}", args.CallItem.Direction); - Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); - trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status != eCodecCallStatus.Disconnected); - - if (args.CallItem.Direction == eCodecCallDirection.Incoming) - { - trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); - trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); - } - - trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); - }; - } - - private string UpdateCallStatusXSig() - { - const int maxCalls = 8; - const int maxStrings = 5; - const int offset = 6; - var stringIndex = 0; - var digitalIndex = maxStrings * maxCalls; - var arrayIndex = 0; - - var tokenArray = new XSigToken[maxCalls*offset]; //set array size for number of calls * pieces of info - - foreach (var call in ActiveCalls) - { - if (arrayIndex >= maxCalls * offset) - break; - //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); - - //serials - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); - tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); - tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex++; - } - while (digitalIndex < maxCalls) - { - //digitals - tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); - - //serials - tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); - tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); - tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); - tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty); - tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty); - - arrayIndex += offset; - stringIndex += maxStrings; - digitalIndex++; - } - - return GetXSigString(tokenArray); - } - - private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0")); - trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1")); - trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2")); - trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3")); - trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4")); - trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5")); - trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6")); - trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7")); - trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8")); - trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9")); - trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*")); - trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#")); - } - - private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); - - codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); - } - - private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); - trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); - - codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => - { - var offCodec = codec as IHasCameraOff; - - if (offCodec != null) - { - if (offCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - }; - - var offModeCodec = codec as IHasCameraOff; - - if (offModeCodec != null) - { - if (offModeCodec.CameraIsOffFeedback.BoolValue) - { - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - return; - } - - trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); - trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); - } - - private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, - VideoCodecControllerJoinMap joinMap) - { - trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); - - codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); - } - - private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) - { - //Camera PTZ - trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.TiltUp(); - else camera.TiltStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.TiltDown(); - else camera.TiltStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.PanLeft(); - else camera.PanStop(); - }); - trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.PanRight(); - else camera.PanStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.ZoomIn(); - else camera.ZoomStop(); - }); - - trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => - { - if (codec.SelectedCamera == null) return; - var camera = codec.SelectedCamera as IHasCameraPtzControl; - - if (camera == null) return; - - if (b) camera.ZoomOut(); - else camera.ZoomStop(); - }); - - //Camera Select - trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => - { - if (codec.SelectedCamera == null) return; - - codec.SelectCamera(codec.Cameras[i].Key); - }); - - codec.CameraSelected += (sender, args) => - { - var i = (ushort) codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); - - if (codec is IHasCodecRoomPresets) - { - return; - } - - if (!(args.SelectedCamera is IHasCameraPresets)) - { - return; - } - - var cam = args.SelectedCamera as IHasCameraPresets; - SetCameraPresetNames(cam.Presets); - - (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); - - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, - (a) => - { - cam.PresetSelect(a); - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); - }); - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, - String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - }; - - if (!(codec is IHasCodecRoomPresets)) return; - - var presetCodec = codec as IHasCodecRoomPresets; - - presetCodec.CodecRoomPresetsListHasChanged += - (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); - - //Camera Presets - trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => - { - presetCodec.CodecRoomPresetSelect(i); - - trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); - }); - - trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, - () => - { - presetCodec.CodecRoomPresetStore( - trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); - trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); - }); - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(IEnumerable presets) - { - return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); - } - - private string SetCameraPresetNames(ICollection presets) - { - var i = 1; //start index for xsig; - - var tokenArray = new XSigToken[presets.Count]; - - foreach (var preset in presets) - { - var cameraPreset = new XSigSerialToken(i, preset); - tokenArray[i - 1] = cameraPreset; - i++; - } - - return GetXSigString(tokenArray); - } - - private string GetXSigString(XSigToken[] tokenArray) - { - string returnString; - using (var s = new MemoryStream()) - { - using (var tw = new XSigTokenStreamWriter(s, true)) - { - tw.WriteXSigData(tokenArray); - } - - var xSig = s.ToArray(); - - returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); - } - - return returnString; - } - - #endregion - } - - - /// - /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info - /// - public class CodecPhonebookSyncState : IKeyed - { - private bool _InitialSyncComplete; - - public CodecPhonebookSyncState(string key) - { - Key = key; - - CodecDisconnected(); - } - - public bool InitialSyncComplete - { - get { return _InitialSyncComplete; } - private set - { - if (value == true) - { - var handler = InitialSyncCompleted; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - _InitialSyncComplete = value; - } - } - - public bool InitialPhonebookFoldersWasReceived { get; private set; } - - public bool NumberOfContactsWasReceived { get; private set; } - - public bool PhonebookRootEntriesWasRecieved { get; private set; } - - public bool PhonebookHasFolders { get; private set; } - - public int NumberOfContacts { get; private set; } - - #region IKeyed Members - - public string Key { get; private set; } - - #endregion - - public event EventHandler InitialSyncCompleted; - - public void InitialPhonebookFoldersReceived() - { - InitialPhonebookFoldersWasReceived = true; - - CheckSyncStatus(); - } - - public void PhonebookRootEntriesReceived() - { - PhonebookRootEntriesWasRecieved = true; - - CheckSyncStatus(); - } - - public void SetPhonebookHasFolders(bool value) - { - PhonebookHasFolders = value; - - Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); - } - - public void SetNumberOfContacts(int contacts) - { - NumberOfContacts = contacts; - NumberOfContactsWasReceived = true; - - Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); - - CheckSyncStatus(); - } - - public void CodecDisconnected() - { - InitialPhonebookFoldersWasReceived = false; - PhonebookHasFolders = false; - NumberOfContacts = 0; - NumberOfContactsWasReceived = false; - } - - private void CheckSyncStatus() - { - if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) - { - InitialSyncComplete = true; - Debug.Console(1, this, "Initial Phonebook Sync Complete!"); - } - else - { - InitialSyncComplete = false; - } - } - } + }; + } + } + + protected abstract Func SharingSourceFeedbackFunc { get; } + protected abstract Func SharingContentIsOnFeedbackFunc { get; } + + #region ICodecAudio Members + + public abstract void PrivacyModeOn(); + public abstract void PrivacyModeOff(); + public abstract void PrivacyModeToggle(); + public BoolFeedback PrivacyModeIsOnFeedback { get; private set; } + + + public BoolFeedback MuteFeedback { get; private set; } + + public abstract void MuteOff(); + + public abstract void MuteOn(); + + public abstract void SetVolume(ushort level); + + public IntFeedback VolumeLevelFeedback { get; private set; } + + public abstract void MuteToggle(); + + public abstract void VolumeDown(bool pressRelease); + + + public abstract void VolumeUp(bool pressRelease); + + #endregion + + #region IHasContentSharing Members + + public abstract void StartSharing(); + public abstract void StopSharing(); + + public bool AutoShareContentWhileInCall { get; protected set; } + + public StringFeedback SharingSourceFeedback { get; private set; } + public BoolFeedback SharingContentIsOnFeedback { get; private set; } + + #endregion + + #region IHasDialer Members + + /// + /// Fires when the status of any active, dialing, or incoming call changes or is new + /// + public event EventHandler CallStatusChange; + + /// + /// Returns true when any call is not in state Unknown, Disconnecting, Disconnected + /// + public bool IsInCall + { + get + { + var value = ActiveCalls != null && ActiveCalls.Any(c => c.IsActiveCall); + return value; + } + } + + public abstract void Dial(string number); + public abstract void EndCall(CodecActiveCallItem call); + public abstract void EndAllCalls(); + public abstract void AcceptCall(CodecActiveCallItem call); + public abstract void RejectCall(CodecActiveCallItem call); + public abstract void SendDtmf(string s); + + #endregion + + #region IRoutingInputsOutputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IUsageTracking Members + + /// + /// This object can be added by outside users of this class to provide usage tracking + /// for various services + /// + public UsageTracking UsageTracker { get; set; } + + #endregion + + #region iVideoCodecInfo Members + + public VideoCodecInfo CodecInfo { get; protected set; } + + #endregion + + public event EventHandler IsReadyChange; + public abstract void Dial(Meeting meeting); + + public virtual void Dial(IInvitableContact contact) + { + } + + public abstract void ExecuteSwitch(object selector); + + /// + /// Helper method to fire CallStatusChange event with old and new status + /// + protected void SetNewCallStatusAndFireCallStatusChange(eCodecCallStatus newStatus, CodecActiveCallItem call) + { + call.Status = newStatus; + + OnCallStatusChange(call); + } + + /// + /// + /// + /// + /// + /// + protected virtual void OnCallStatusChange(CodecActiveCallItem item) + { + var handler = CallStatusChange; + if (handler != null) + { + handler(this, new CodecCallStatusItemChangeEventArgs(item)); + } + + if (AutoShareContentWhileInCall) + { + StartSharing(); + } + + if (UsageTracker != null) + { + if (IsInCall && !UsageTracker.UsageTrackingStarted) + { + UsageTracker.StartDeviceUsage(); + } + else if (UsageTracker.UsageTrackingStarted && !IsInCall) + { + UsageTracker.EndDeviceUsage(); + } + } + } + + /// + /// Sets IsReady property and fires the event. Used for dependent classes to sync up their data. + /// + protected void SetIsReady() + { + CrestronInvoke.BeginInvoke((o) => + { + try + { + IsReady = true; + var h = IsReadyChange; + if (h != null) + { + h(this, new EventArgs()); + } + } + catch (Exception e) + { + Debug.Console(2, this, "Error in SetIsReady() : {0}", e); + } + }); + } + + // **** DEBUGGING THINGS **** + /// + /// + /// + public virtual void ListCalls() + { + var sb = new StringBuilder(); + foreach (var c in ActiveCalls) + { + sb.AppendFormat("{0} {1} -- {2} {3}\n", c.Id, c.Number, c.Name, c.Status); + } + Debug.Console(1, this, "\n{0}\n", sb.ToString()); + } + + public abstract void StandbyActivate(); + + public abstract void StandbyDeactivate(); + + #region Implementation of IBridgeAdvanced + + public abstract void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge); + + protected void LinkVideoCodecToApi(VideoCodecBase codec, BasicTriList trilist, uint joinStart, string joinMapKey, + EiscApiAdvanced bridge) + { + var joinMap = new VideoCodecControllerJoinMap(joinStart); + + var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); + + if (customJoins != null) + { + joinMap.SetCustomJoinData(customJoins); + } + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + + Debug.Console(1, this, "Linking to Trilist {0}", trilist.ID.ToString("X")); + + + + LinkVideoCodecDtmfToApi(trilist, joinMap); + + LinkVideoCodecCallControlsToApi(trilist, joinMap); + + LinkVideoCodecContentSharingToApi(trilist, joinMap); + + LinkVideoCodecPrivacyToApi(trilist, joinMap); + + LinkVideoCodecVolumeToApi(trilist, joinMap); + + if (codec is ICommunicationMonitor) + { + LinkVideoCodecCommMonitorToApi(codec as ICommunicationMonitor, trilist, joinMap); + } + + if (codec is IHasCodecCameras) + { + LinkVideoCodecCameraToApi(codec as IHasCodecCameras, trilist, joinMap); + } + + if (codec is IHasCodecSelfView) + { + LinkVideoCodecSelfviewToApi(codec as IHasCodecSelfView, trilist, joinMap); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, SupportsCameraAutoMode); + LinkVideoCodecCameraModeToApi(codec as IHasCameraAutoMode, trilist, joinMap); + } + + if (codec is IHasCameraOff) + { + trilist.SetBool(joinMap.CameraSupportsOffMode.JoinNumber, SupportsCameraOff); + LinkVideoCodecCameraOffToApi(codec as IHasCameraOff, trilist, joinMap); + } + + if (codec is IHasCodecLayouts) + { + LinkVideoCodecCameraLayoutsToApi(codec as IHasCodecLayouts, trilist, joinMap); + } + + if (codec is IHasSelfviewPosition) + { + LinkVideoCodecSelfviewPositionToApi(codec as IHasSelfviewPosition, trilist, joinMap); + } + + if (codec is IHasDirectory) + { + LinkVideoCodecDirectoryToApi(codec as IHasDirectory, trilist, joinMap); + } + + if (codec is IHasScheduleAwareness) + { + LinkVideoCodecScheduleToApi(codec as IHasScheduleAwareness, trilist, joinMap); + } + + if (codec is IHasParticipants) + { + LinkVideoCodecParticipantsToApi(codec as IHasParticipants, trilist, joinMap); + } + + if (codec is IHasFarEndContentStatus) + { + (codec as IHasFarEndContentStatus).ReceivingContent.LinkInputSig(trilist.BooleanInput[joinMap.RecievingContent.JoinNumber]); + } + + if (codec is IHasPhoneDialing) + { + LinkVideoCodecPhoneToApi(codec as IHasPhoneDialing, trilist, joinMap); + } + + trilist.OnlineStatusChange += (device, args) => + { + if (!args.DeviceOnLine) return; + + if (codec is IHasDirectory) + { + (codec as IHasDirectory).SetCurrentDirectoryToRoot(); + } + + if (codec is IHasScheduleAwareness) + { + (codec as IHasScheduleAwareness).GetSchedule(); + } + + if (codec is IHasParticipants) + { + UpdateParticipantsXSig((codec as IHasParticipants).Participants.CurrentParticipants); + } + + if (codec is IHasCameraAutoMode) + { + trilist.SetBool(joinMap.CameraSupportsAutoMode.JoinNumber, true); + + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCodecSelfView) + { + (codec as IHasCodecSelfView).SelfviewIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraAutoMode) + { + (codec as IHasCameraAutoMode).CameraAutoModeIsOnFeedback.FireUpdate(); + } + + if (codec is IHasCameraOff) + { + (codec as IHasCameraOff).CameraIsOffFeedback.FireUpdate(); + } + + if (codec is IHasPhoneDialing) + { + (codec as IHasPhoneDialing).PhoneOffHookFeedback.FireUpdate(); + } + + SharingContentIsOnFeedback.FireUpdate(); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private void LinkVideoCodecPhoneToApi(IHasPhoneDialing codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.PhoneOffHookFeedback.LinkInputSig(trilist.BooleanInput[joinMap.PhoneHookState.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.DialPhone.JoinNumber, + () => codec.DialPhoneCall(trilist.StringOutput[joinMap.PhoneDialString.JoinNumber].StringValue)); + + trilist.SetSigFalseAction(joinMap.HangUpPhone.JoinNumber, codec.EndPhoneCall); + } + + private void LinkVideoCodecSelfviewPositionToApi(IHasSelfviewPosition codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.SelfviewPosition.JoinNumber, codec.SelfviewPipPositionToggle); + + codec.SelfviewPipPositionFeedback.LinkInputSig(trilist.StringInput[joinMap.SelfviewPositionFb.JoinNumber]); + } + + private void LinkVideoCodecCameraOffToApi(IHasCameraOff codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeOff.JoinNumber, codec.CameraOff); + + codec.CameraIsOffFeedback.OutputChange += (o, a) => + { + if (a.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoCodec = codec as IHasCameraAutoMode; + + if (autoCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoCodec.CameraAutoModeIsOnFeedback.BoolValue); + }; + + if (codec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + + var autoModeCodec = codec as IHasCameraAutoMode; + + if (autoModeCodec == null) return; + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !autoModeCodec.CameraAutoModeIsOnFeedback.BoolValue); + } + + private void LinkVideoCodecVolumeToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + MuteFeedback.LinkInputSig(trilist.BooleanInput[joinMap.VolumeMuteOn.JoinNumber]); + MuteFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.VolumeMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.VolumeMuteOn.JoinNumber, MuteOn); + trilist.SetSigFalseAction(joinMap.VolumeMuteOff.JoinNumber, MuteOff); + trilist.SetSigFalseAction(joinMap.VolumeMuteToggle.JoinNumber, MuteToggle); + + VolumeLevelFeedback.LinkInputSig(trilist.UShortInput[joinMap.VolumeLevel.JoinNumber]); + + trilist.SetBoolSigAction(joinMap.VolumeUp.JoinNumber, VolumeUp); + trilist.SetBoolSigAction(joinMap.VolumeDown.JoinNumber, VolumeDown); + + trilist.SetUShortSigAction(joinMap.VolumeLevel.JoinNumber, SetVolume); + + } + + private void LinkVideoCodecPrivacyToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + PrivacyModeIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.MicMuteOn.JoinNumber]); + PrivacyModeIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.MicMuteOff.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.MicMuteOn.JoinNumber, PrivacyModeOn); + trilist.SetSigFalseAction(joinMap.MicMuteOff.JoinNumber, PrivacyModeOff); + trilist.SetSigFalseAction(joinMap.MicMuteToggle.JoinNumber, PrivacyModeToggle); + } + + private void LinkVideoCodecCommMonitorToApi(ICommunicationMonitor codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CommunicationMonitor.IsOnlineFeedback.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + } + + private void LinkVideoCodecParticipantsToApi(IHasParticipants codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.Participants.ParticipantsListHasChanged += (sender, args) => + { + string participantsXSig; + + if (codec.Participants.CurrentParticipants.Count == 0) + { + participantsXSig = Encoding.GetEncoding(XSigEncoding).GetString(_clearBytes, 0, _clearBytes.Length); + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + return; + } + + participantsXSig = UpdateParticipantsXSig(codec.Participants.CurrentParticipants); + + trilist.SetString(joinMap.CurrentParticipants.JoinNumber, participantsXSig); + + trilist.SetUshort(joinMap.ParticipantCount.JoinNumber, (ushort)codec.Participants.CurrentParticipants.Count); + }; + } + + private string UpdateParticipantsXSig(List currentParticipants) + { + const int maxParticipants = 50; + const int maxDigitals = 5; + const int maxStrings = 1; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * maxParticipants; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxParticipants * offset]; + + foreach (var participant in currentParticipants) + { + if (meetingIndex >= maxParticipants * offset) break; + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, participant.AudioMuteFb); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, participant.VideoMuteFb); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, participant.CanMuteVideo); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, participant.CanUnmuteVideo); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, participant.IsHost); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, participant.Name); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxParticipants * offset) + { + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + tokenArray[digitalIndex + 2] = new XSigDigitalToken(digitalIndex + 3, false); + tokenArray[digitalIndex + 3] = new XSigDigitalToken(digitalIndex + 4, false); + tokenArray[digitalIndex + 4] = new XSigDigitalToken(digitalIndex + 5, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecContentSharingToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + SharingContentIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.SourceShareStart.JoinNumber]); + SharingContentIsOnFeedback.LinkComplementInputSig(trilist.BooleanInput[joinMap.SourceShareEnd.JoinNumber]); + + SharingSourceFeedback.LinkInputSig(trilist.StringInput[joinMap.CurrentSource.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.SourceShareStart.JoinNumber, StartSharing); + trilist.SetSigFalseAction(joinMap.SourceShareEnd.JoinNumber, StopSharing); + + trilist.SetBoolSigAction(joinMap.SourceShareAutoStart.JoinNumber, (b) => AutoShareContentWhileInCall = b); + } + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + private List _currentMeetings = new List(); + + private void LinkVideoCodecScheduleToApi(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.UpdateMeetings.JoinNumber, codec.GetSchedule); + + trilist.SetUShortSigAction(joinMap.MinutesBeforeMeetingStart.JoinNumber, (i) => + { + codec.CodecSchedule.MeetingWarningMinutes = i; + }); + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting1.JoinNumber, () => + { + var mtg = 1; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting1.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting2.JoinNumber, () => + { + var mtg = 2; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting2.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + trilist.SetSigFalseAction(joinMap.DialMeeting3.JoinNumber, () => + { + var mtg = 3; + var index = mtg - 1; + Debug.Console(1, this, "Meeting {0} Selected (EISC dig-o{1}) > _currentMeetings[{2}].Id: {3}, Title: {4}", + mtg, joinMap.DialMeeting3.JoinNumber, index, _currentMeetings[index].Id, _currentMeetings[index].Title); + if (_currentMeetings[index] != null) + Dial(_currentMeetings[index]); + }); + + codec.CodecSchedule.MeetingsListHasChanged += (sender, args) => UpdateMeetingsList(codec, trilist, joinMap); + codec.CodecSchedule.MeetingEventChange += (sender, args) => + { + if (args.ChangeType == eMeetingEventChangeType.MeetingStartWarning) + { + UpdateMeetingsList(codec, trilist, joinMap); + } + }; + } + + private void UpdateMeetingsList(IHasScheduleAwareness codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + var currentTime = DateTime.Now; + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + // - changed var currentMeetings >> field _currentMeetings + //_currentMeetings.Clear(); + _currentMeetings = codec.CodecSchedule.Meetings.Where(m => m.StartTime >= currentTime || m.EndTime >= currentTime).ToList(); + + // TODO [ ] 2021-01-06, jkd: Added to debug OBTP dialing issues + // - moved the trilist.SetSigFlaseAction(joinMap.DialMeeting1..3.JoinNumber) lambda's to LinkVideoCodecScheduleToApi + + var meetingsData = UpdateMeetingsListXSig(_currentMeetings); + trilist.SetString(joinMap.Schedule.JoinNumber, meetingsData); + trilist.SetUshort(joinMap.MeetingCount.JoinNumber, (ushort)_currentMeetings.Count); + } + + private string UpdateMeetingsListXSig(List meetings) + { + const int maxMeetings = 3; + const int maxDigitals = 2; + const int maxStrings = 7; + const int offset = maxDigitals + maxStrings; + var digitalIndex = maxStrings * maxMeetings; //15 + var stringIndex = 0; + var meetingIndex = 0; + + var tokenArray = new XSigToken[maxMeetings * offset]; + /* + * Digitals + * IsJoinable - 1 + * IsDialable - 2 + * + * Serials + * Organizer - 1 + * Title - 2 + * Start Date - 3 + * Start Time - 4 + * End Date - 5 + * End Time - 6 + * Id - 7 + */ + + + foreach (var meeting in meetings) + { + var currentTime = DateTime.Now; + + if (meeting.StartTime < currentTime && meeting.EndTime < currentTime) continue; + + if (meetingIndex >= maxMeetings * offset) + { + Debug.Console(2, this, "Max Meetings reached"); + break; + } + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, meeting.Joinable); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, meeting.Id != "0"); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, meeting.Organizer); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, meeting.Title); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, meeting.StartTime.ToShortDateString()); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, meeting.StartTime.ToShortTimeString()); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, meeting.EndTime.ToShortDateString()); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, meeting.EndTime.ToShortTimeString()); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, meeting.Id); + + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + while (meetingIndex < maxMeetings * offset) + { + Debug.Console(2, this, "Clearing unused data. Meeting Index: {0} MaxMeetings * Offset: {1}", + meetingIndex, maxMeetings * offset); + + //digitals + tokenArray[digitalIndex] = new XSigDigitalToken(digitalIndex + 1, false); + tokenArray[digitalIndex + 1] = new XSigDigitalToken(digitalIndex + 2, false); + + //serials + tokenArray[stringIndex] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[stringIndex + 1] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[stringIndex + 2] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[stringIndex + 3] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[stringIndex + 4] = new XSigSerialToken(stringIndex + 5, String.Empty); + tokenArray[stringIndex + 5] = new XSigSerialToken(stringIndex + 6, String.Empty); + tokenArray[stringIndex + 6] = new XSigSerialToken(stringIndex + 7, String.Empty); + + digitalIndex += maxDigitals; + meetingIndex += offset; + stringIndex += maxStrings; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecDirectoryToApi(IHasDirectory codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + codec.CurrentDirectoryResultIsNotDirectoryRoot.LinkComplementInputSig( + trilist.BooleanInput[joinMap.DirectoryIsRoot.JoinNumber]); + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetStringSigAction(joinMap.DirectorySearchString.JoinNumber, codec.SearchDirectory); + + trilist.SetUShortSigAction(joinMap.DirectorySelectRow.JoinNumber, (i) => SelectDirectoryEntry(codec, i)); + + trilist.SetSigFalseAction(joinMap.DirectoryRoot.JoinNumber, codec.SetCurrentDirectoryToRoot); + + trilist.SetSigFalseAction(joinMap.DirectoryFolderBack.JoinNumber, codec.GetDirectoryParentFolderContents); + + codec.DirectoryResultReturned += (sender, args) => + { + trilist.SetUshort(joinMap.DirectoryRowCount.JoinNumber, (ushort)args.Directory.CurrentDirectoryResults.Count); + + var clearBytes = XSigHelpers.ClearOutputs(); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, + Encoding.GetEncoding(XSigEncoding).GetString(clearBytes, 0, clearBytes.Length)); + var directoryXSig = UpdateDirectoryXSig(args.Directory, !codec.CurrentDirectoryResultIsNotDirectoryRoot.BoolValue); + + trilist.SetString(joinMap.DirectoryEntries.JoinNumber, directoryXSig); + }; + } + + private void SelectDirectoryEntry(IHasDirectory codec, ushort i) + { + var entry = codec.CurrentDirectoryResult.CurrentDirectoryResults[i - 1]; + + if (entry is DirectoryFolder) + { + codec.GetDirectoryFolderContents(entry.FolderId); + return; + } + + var dialableEntry = entry as IInvitableContact; + + if (dialableEntry != null) + { + Dial(dialableEntry); + return; + } + + var entryToDial = entry as DirectoryContact; + + if (entryToDial == null) return; + + Dial(entryToDial.ContactMethods[0].Number); + } + + private string UpdateDirectoryXSig(CodecDirectory directory, bool isRoot) + { + var contactIndex = 1; + var tokenArray = new XSigToken[directory.CurrentDirectoryResults.Count]; + + foreach (var entry in directory.CurrentDirectoryResults) + { + var arrayIndex = contactIndex - 1; + + if (entry is DirectoryFolder && entry.ParentFolderId == "root") + { + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, String.Format("[+] {0}", entry.Name)); + + contactIndex++; + + continue; + } + + if (isRoot && String.IsNullOrEmpty(entry.FolderId)) continue; + + tokenArray[arrayIndex] = new XSigSerialToken(contactIndex, entry.Name); + + contactIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecCallControlsToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.ManualDial.JoinNumber, + () => Dial(trilist.StringOutput[joinMap.CurrentDialString.JoinNumber].StringValue)); + + //End All calls for now + trilist.SetSigFalseAction(joinMap.EndCall.JoinNumber, EndAllCalls); + + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + CallStatusChange += (sender, args) => + { + trilist.SetBool(joinMap.HookState.JoinNumber, IsInCall); + + Debug.Console(1, this, "Call Direction: {0}", args.CallItem.Direction); + Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); + trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status != eCodecCallStatus.Disconnected); + + if (args.CallItem.Direction == eCodecCallDirection.Incoming) + { + trilist.SetSigFalseAction(joinMap.IncomingAnswer.JoinNumber, () => AcceptCall(args.CallItem)); + trilist.SetSigFalseAction(joinMap.IncomingReject.JoinNumber, () => RejectCall(args.CallItem)); + } + + trilist.SetString(joinMap.CurrentCallData.JoinNumber, UpdateCallStatusXSig()); + }; + } + + private string UpdateCallStatusXSig() + { + const int maxCalls = 8; + const int maxStrings = 5; + const int offset = 6; + var stringIndex = 0; + var digitalIndex = maxStrings * maxCalls; + var arrayIndex = 0; + + var tokenArray = new XSigToken[maxCalls * offset]; //set array size for number of calls * pieces of info + + foreach (var call in ActiveCalls) + { + if (arrayIndex >= maxCalls * offset) + break; + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, call.IsActiveCall); + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, call.Name ?? String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, call.Number ?? String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, call.Direction.ToString()); + tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, call.Type.ToString()); + tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, call.Status.ToString()); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + while (digitalIndex < maxCalls) + { + //digitals + tokenArray[arrayIndex] = new XSigDigitalToken(digitalIndex + 1, false); + + //serials + tokenArray[arrayIndex + 1] = new XSigSerialToken(stringIndex + 1, String.Empty); + tokenArray[arrayIndex + 2] = new XSigSerialToken(stringIndex + 2, String.Empty); + tokenArray[arrayIndex + 3] = new XSigSerialToken(stringIndex + 3, String.Empty); + tokenArray[arrayIndex + 4] = new XSigSerialToken(stringIndex + 4, String.Empty); + tokenArray[arrayIndex + 5] = new XSigSerialToken(stringIndex + 5, String.Empty); + + arrayIndex += offset; + stringIndex += maxStrings; + digitalIndex++; + } + + return GetXSigString(tokenArray); + } + + private void LinkVideoCodecDtmfToApi(BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.Dtmf0.JoinNumber, () => SendDtmf("0")); + trilist.SetSigFalseAction(joinMap.Dtmf1.JoinNumber, () => SendDtmf("1")); + trilist.SetSigFalseAction(joinMap.Dtmf2.JoinNumber, () => SendDtmf("2")); + trilist.SetSigFalseAction(joinMap.Dtmf3.JoinNumber, () => SendDtmf("3")); + trilist.SetSigFalseAction(joinMap.Dtmf4.JoinNumber, () => SendDtmf("4")); + trilist.SetSigFalseAction(joinMap.Dtmf5.JoinNumber, () => SendDtmf("5")); + trilist.SetSigFalseAction(joinMap.Dtmf6.JoinNumber, () => SendDtmf("6")); + trilist.SetSigFalseAction(joinMap.Dtmf7.JoinNumber, () => SendDtmf("7")); + trilist.SetSigFalseAction(joinMap.Dtmf8.JoinNumber, () => SendDtmf("8")); + trilist.SetSigFalseAction(joinMap.Dtmf9.JoinNumber, () => SendDtmf("9")); + trilist.SetSigFalseAction(joinMap.DtmfStar.JoinNumber, () => SendDtmf("*")); + trilist.SetSigFalseAction(joinMap.DtmfPound.JoinNumber, () => SendDtmf("#")); + } + + private void LinkVideoCodecCameraLayoutsToApi(IHasCodecLayouts codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraLayout.JoinNumber, codec.LocalLayoutToggle); + + codec.LocalLayoutFeedback.LinkInputSig(trilist.StringInput[joinMap.CameraLayoutStringFb.JoinNumber]); + } + + private void LinkVideoCodecCameraModeToApi(IHasCameraAutoMode codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeOn); + trilist.SetSigFalseAction(joinMap.CameraModeManual.JoinNumber, codec.CameraAutoModeOff); + + codec.CameraAutoModeIsOnFeedback.OutputChange += (o, a) => + { + var offCodec = codec as IHasCameraOff; + + if (offCodec != null) + { + if (offCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, a.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !a.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + }; + + var offModeCodec = codec as IHasCameraOff; + + if (offModeCodec != null) + { + if (offModeCodec.CameraIsOffFeedback.BoolValue) + { + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, false); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, true); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + return; + } + + trilist.SetBool(joinMap.CameraModeAuto.JoinNumber, codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeManual.JoinNumber, !codec.CameraAutoModeIsOnFeedback.BoolValue); + trilist.SetBool(joinMap.CameraModeOff.JoinNumber, false); + } + + private void LinkVideoCodecSelfviewToApi(IHasCodecSelfView codec, BasicTriList trilist, + VideoCodecControllerJoinMap joinMap) + { + trilist.SetSigFalseAction(joinMap.CameraSelfView.JoinNumber, codec.SelfViewModeToggle); + + codec.SelfviewIsOnFeedback.LinkInputSig(trilist.BooleanInput[joinMap.CameraSelfView.JoinNumber]); + } + + private void LinkVideoCodecCameraToApi(IHasCodecCameras codec, BasicTriList trilist, VideoCodecControllerJoinMap joinMap) + { + //Camera PTZ + trilist.SetBoolSigAction(joinMap.CameraTiltUp.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltUp(); + else camera.TiltStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraTiltDown.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.TiltDown(); + else camera.TiltStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanLeft.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanLeft(); + else camera.PanStop(); + }); + trilist.SetBoolSigAction(joinMap.CameraPanRight.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.PanRight(); + else camera.PanStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomIn.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomIn(); + else camera.ZoomStop(); + }); + + trilist.SetBoolSigAction(joinMap.CameraZoomOut.JoinNumber, (b) => + { + if (codec.SelectedCamera == null) return; + var camera = codec.SelectedCamera as IHasCameraPtzControl; + + if (camera == null) return; + + if (b) camera.ZoomOut(); + else camera.ZoomStop(); + }); + + //Camera Select + trilist.SetUShortSigAction(joinMap.CameraNumberSelect.JoinNumber, (i) => + { + if (codec.SelectedCamera == null) return; + + codec.SelectCamera(codec.Cameras[i].Key); + }); + + codec.CameraSelected += (sender, args) => + { + var i = (ushort)codec.Cameras.FindIndex((c) => c.Key == args.SelectedCamera.Key); + + if (codec is IHasCodecRoomPresets) + { + return; + } + + if (!(args.SelectedCamera is IHasCameraPresets)) + { + return; + } + + var cam = args.SelectedCamera as IHasCameraPresets; + SetCameraPresetNames(cam.Presets); + + (args.SelectedCamera as IHasCameraPresets).PresetsListHasChanged += (o, eventArgs) => SetCameraPresetNames(cam.Presets); + + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, + (a) => + { + cam.PresetSelect(a); + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, a); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + cam.PresetStore(trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, + String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + }; + + if (!(codec is IHasCodecRoomPresets)) return; + + var presetCodec = codec as IHasCodecRoomPresets; + + presetCodec.CodecRoomPresetsListHasChanged += + (sender, args) => SetCameraPresetNames(presetCodec.NearEndPresets); + + //Camera Presets + trilist.SetUShortSigAction(joinMap.CameraPresetSelect.JoinNumber, (i) => + { + presetCodec.CodecRoomPresetSelect(i); + + trilist.SetUshort(joinMap.CameraPresetSelect.JoinNumber, i); + }); + + trilist.SetSigFalseAction(joinMap.CameraPresetSave.JoinNumber, + () => + { + presetCodec.CodecRoomPresetStore( + trilist.UShortOutput[joinMap.CameraPresetSelect.JoinNumber].UShortValue, String.Empty); + trilist.PulseBool(joinMap.CameraPresetSave.JoinNumber, 3000); + }); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(IEnumerable presets) + { + return SetCameraPresetNames(presets.Select(p => p.Description).ToList()); + } + + private string SetCameraPresetNames(ICollection presets) + { + var i = 1; //start index for xsig; + + var tokenArray = new XSigToken[presets.Count]; + + foreach (var preset in presets) + { + var cameraPreset = new XSigSerialToken(i, preset); + tokenArray[i - 1] = cameraPreset; + i++; + } + + return GetXSigString(tokenArray); + } + + private string GetXSigString(XSigToken[] tokenArray) + { + string returnString; + using (var s = new MemoryStream()) + { + using (var tw = new XSigTokenStreamWriter(s, true)) + { + tw.WriteXSigData(tokenArray); + } + + var xSig = s.ToArray(); + + returnString = Encoding.GetEncoding(XSigEncoding).GetString(xSig, 0, xSig.Length); + } + + return returnString; + } + + #endregion + } + + + /// + /// Used to track the status of syncronizing the phonebook values when connecting to a codec or refreshing the phonebook info + /// + public class CodecPhonebookSyncState : IKeyed + { + private bool _InitialSyncComplete; + + public CodecPhonebookSyncState(string key) + { + Key = key; + + CodecDisconnected(); + } + + public bool InitialSyncComplete + { + get { return _InitialSyncComplete; } + private set + { + if (value == true) + { + var handler = InitialSyncCompleted; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + _InitialSyncComplete = value; + } + } + + public bool InitialPhonebookFoldersWasReceived { get; private set; } + + public bool NumberOfContactsWasReceived { get; private set; } + + public bool PhonebookRootEntriesWasRecieved { get; private set; } + + public bool PhonebookHasFolders { get; private set; } + + public int NumberOfContacts { get; private set; } + + #region IKeyed Members + + public string Key { get; private set; } + + #endregion + + public event EventHandler InitialSyncCompleted; + + public void InitialPhonebookFoldersReceived() + { + InitialPhonebookFoldersWasReceived = true; + + CheckSyncStatus(); + } + + public void PhonebookRootEntriesReceived() + { + PhonebookRootEntriesWasRecieved = true; + + CheckSyncStatus(); + } + + public void SetPhonebookHasFolders(bool value) + { + PhonebookHasFolders = value; + + Debug.Console(1, this, "Phonebook has folders: {0}", PhonebookHasFolders); + } + + public void SetNumberOfContacts(int contacts) + { + NumberOfContacts = contacts; + NumberOfContactsWasReceived = true; + + Debug.Console(1, this, "Phonebook contains {0} contacts.", NumberOfContacts); + + CheckSyncStatus(); + } + + public void CodecDisconnected() + { + InitialPhonebookFoldersWasReceived = false; + PhonebookHasFolders = false; + NumberOfContacts = 0; + NumberOfContactsWasReceived = false; + } + + private void CheckSyncStatus() + { + if (InitialPhonebookFoldersWasReceived && NumberOfContactsWasReceived && PhonebookRootEntriesWasRecieved) + { + InitialSyncComplete = true; + Debug.Console(1, this, "Initial Phonebook Sync Complete!"); + } + else + { + InitialSyncComplete = false; + } + } + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs index fc440e5b..980c7a61 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/ZoomRoom/ZoomRoom.cs @@ -1655,6 +1655,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); SendText(string.Format("zCommand Dial Start meetingNumber: {0}", meeting.Id)); } From 89990971001734da09d7a02a0ea6b15452e20a21 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 6 Jan 2021 16:12:12 -0700 Subject: [PATCH 70/89] Adds additional debug to help with room on/off events --- .../Room/Types/EssentialsTechRoom.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs index 0a47933e..bfa56646 100644 --- a/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs +++ b/PepperDashEssentials/Room/Types/EssentialsTechRoom.cs @@ -261,8 +261,16 @@ namespace PepperDash.Essentials CrestronInvoke.BeginInvoke((o) => { + Debug.Console(2, this, "There are {0} actions to execute for this event.", eventConfig.Actions.Count); + foreach (var a in eventConfig.Actions) { + Debug.Console(2, this, +@"Attempting to run action: +DeviceKey: {0} +MethodName: {1} +Params: {2}" + , a.DeviceKey, a.MethodName, a.Params); DeviceJsonApi.DoDeviceAction(a); } }); @@ -271,6 +279,8 @@ namespace PepperDash.Essentials public void RoomPowerOn() { + Debug.Console(2, this, "Room Powering On"); + var dummySource = DeviceManager.GetDeviceForKey(_config.DummySourceKey) as IRoutingOutputs; if (dummySource == null) @@ -287,6 +297,8 @@ namespace PepperDash.Essentials public void RoomPowerOff() { + Debug.Console(2, this, "Room Powering Off"); + foreach (var display in _displays) { display.Value.PowerOff(); From 3c1ed6e58aac601f4db9442ff8a8f654caa9d547 Mon Sep 17 00:00:00 2001 From: Trevor Payne Date: Thu, 7 Jan 2021 12:41:29 -0600 Subject: [PATCH 71/89] Added debug statements to ExecuteNumericSwitch in DmTx4kz302CController --- .../Endpoints/Transmitters/DmTx4kz302CController.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs index 1e44396c..1c5ce36e 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs @@ -282,6 +282,12 @@ namespace PepperDash.Essentials.DM ExecuteSwitch(eVst.AllDisabled, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); break; } + default: + { + Debug.Console(2, this, "Unable to execute numeric switch to input {0}", input); + break; + } + } @@ -289,6 +295,7 @@ namespace PepperDash.Essentials.DM public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) { + Debug.Console(2, this, "Attempting to switch InputSelector {0}", ((eVst)inputSelector).ToString()); if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) Tx.VideoSource = (eVst)inputSelector; From 82029894e47bc5a06ca07fe21ab3bdc9d1ae4794 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 7 Jan 2021 14:46:49 -0700 Subject: [PATCH 72/89] Changed to use passed in type value for signal type instead of assuming both audio and video --- .../Transmitters/DmTx4kz302CController.cs | 713 +++++++++--------- 1 file changed, 360 insertions(+), 353 deletions(-) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs index 1c5ce36e..fb8f3ac5 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTx4kz302CController.cs @@ -1,44 +1,44 @@ -using Crestron.SimplSharpPro; -using System; -using System.Linq; -//using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.DM; -using Crestron.SimplSharpPro.DM.Endpoints; -using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.DM -{ - using eVst = eX02VideoSourceType; - using eAst = eX02AudioSourceType; - - +using Crestron.SimplSharpPro; +using System; +using System.Linq; +//using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM; +using Crestron.SimplSharpPro.DM.Endpoints; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.DM +{ + using eVst = eX02VideoSourceType; + using eAst = eX02AudioSourceType; + + [Description("Wrapper class for DM-TX-4K-Z-302-C")] - public class DmTx4kz302CController : DmTxControllerBase, ITxRoutingWithFeedback, IHasFeedback, - IIROutputPorts, IComPorts - { - public DmTx4kz302C Tx { get; private set; } - - public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } - public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } - public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } - public RoutingOutputPort DmOut { get; private set; } - public RoutingOutputPort HdmiLoopOut { get; private set; } - - public override StringFeedback ActiveVideoInputFeedback { get; protected set; } - public IntFeedback VideoSourceNumericFeedback { get; protected set; } - public IntFeedback AudioSourceNumericFeedback { get; protected set; } - public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } - public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } - public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } - public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } - public BoolFeedback DisplayPortVideoSyncFeedback { get; protected set; } - - //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } + public class DmTx4kz302CController : DmTxControllerBase, ITxRoutingWithFeedback, IHasFeedback, + IIROutputPorts, IComPorts + { + public DmTx4kz302C Tx { get; private set; } + + public RoutingInputPortWithVideoStatuses HdmiIn1 { get; private set; } + public RoutingInputPortWithVideoStatuses HdmiIn2 { get; private set; } + public RoutingInputPortWithVideoStatuses DisplayPortIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + public RoutingOutputPort HdmiLoopOut { get; private set; } + + public override StringFeedback ActiveVideoInputFeedback { get; protected set; } + public IntFeedback VideoSourceNumericFeedback { get; protected set; } + public IntFeedback AudioSourceNumericFeedback { get; protected set; } + public IntFeedback HdmiIn1HdcpCapabilityFeedback { get; protected set; } + public IntFeedback HdmiIn2HdcpCapabilityFeedback { get; protected set; } + public BoolFeedback Hdmi1VideoSyncFeedback { get; protected set; } + public BoolFeedback Hdmi2VideoSyncFeedback { get; protected set; } + public BoolFeedback DisplayPortVideoSyncFeedback { get; protected set; } + + //public override IntFeedback HdcpSupportAllFeedback { get; protected set; } //public override ushort HdcpSupportCapability { get; protected set; } //IroutingNumericEvent @@ -52,279 +52,286 @@ namespace PepperDash.Essentials.DM { var newEvent = NumericSwitchChange; if (newEvent != null) newEvent(this, e); - } - - /// - /// Helps get the "real" inputs, including when in Auto - /// - public eX02VideoSourceType ActualActiveVideoInput - { - get - { - if (Tx.VideoSourceFeedback != eVst.Auto) - return Tx.VideoSourceFeedback; - if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - return eVst.Hdmi1; - if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) - return eVst.Hdmi2; - - return Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue ? eVst.Vga : eVst.AllDisabled; - } - } - public RoutingPortCollection InputPorts - { - get - { - return new RoutingPortCollection - { - HdmiIn1, - HdmiIn2, - DisplayPortIn, - AnyVideoInput - }; - } - } - public RoutingPortCollection OutputPorts - { - get - { - return new RoutingPortCollection { DmOut, HdmiLoopOut }; - } - } - public DmTx4kz302CController(string key, string name, DmTx4kz302C tx) - : base(key, name, tx) - { - Tx = tx; - - HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, + } + + /// + /// Helps get the "real" inputs, including when in Auto + /// + public eX02VideoSourceType ActualActiveVideoInput + { + get + { + if (Tx.VideoSourceFeedback != eVst.Auto) + return Tx.VideoSourceFeedback; + if (Tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi1; + if (Tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + return eVst.Hdmi2; + + return Tx.DisplayPortInput.SyncDetectedFeedback.BoolValue ? eVst.Vga : eVst.AllDisabled; + } + } + public RoutingPortCollection InputPorts + { + get + { + return new RoutingPortCollection + { + HdmiIn1, + HdmiIn2, + DisplayPortIn, + AnyVideoInput + }; + } + } + public RoutingPortCollection OutputPorts + { + get + { + return new RoutingPortCollection { DmOut, HdmiLoopOut }; + } + } + public DmTx4kz302CController(string key, string name, DmTx4kz302C tx) + : base(key, name, tx) + { + Tx = tx; + + HdmiIn1 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn1, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi1, this, VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[1])) { FeedbackMatchObject = eVst.Hdmi1 - }; - HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, + }; + HdmiIn2 = new RoutingInputPortWithVideoStatuses(DmPortName.HdmiIn2, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.Hdmi, eVst.Hdmi2, this, VideoStatusHelper.GetHdmiInputStatusFuncs(tx.HdmiInputs[2])) { FeedbackMatchObject = eVst.Hdmi2 - }; - DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.DisplayPortIn, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DisplayPort, eVst.DisplayPort, this, + }; + DisplayPortIn = new RoutingInputPortWithVideoStatuses(DmPortName.DisplayPortIn, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.DisplayPort, eVst.DisplayPort, this, VideoStatusHelper.GetDisplayPortInputStatusFuncs(tx.DisplayPortInput)) { FeedbackMatchObject = eVst.DisplayPort - }; - ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", - () => ActualActiveVideoInput.ToString()); - - Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; - Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; - Tx.DisplayPortInput.InputStreamChange += DisplayPortInputStreamChange; - Tx.BaseEvent += Tx_BaseEvent; - Tx.OnlineStatusChange += Tx_OnlineStatusChange; - - VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); - - HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); - - HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpStateFeedback = - new IntFeedback( - () => - tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback - ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback - : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); - - HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; - - Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); - - Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); - - DisplayPortVideoSyncFeedback = new BoolFeedback(() => (bool)tx.DisplayPortInput.SyncDetectedFeedback.BoolValue); - - - var combinedFuncs = new VideoStatusFuncsWrapper - { - HdcpActiveFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), - - HdcpStateFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); - return ActualActiveVideoInput == eVst.Hdmi2 ? tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString() : ""; - }, - - VideoResolutionFeedbackFunc = () => - { - if (ActualActiveVideoInput == eVst.Hdmi1) - return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); - if (ActualActiveVideoInput == eVst.Hdmi2) - return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); - return ActualActiveVideoInput == eVst.Vga ? tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString() : ""; - }, - VideoSyncFeedbackFunc = () => - (ActualActiveVideoInput == eVst.Hdmi1 - && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Hdmi2 - && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) - || (ActualActiveVideoInput == eVst.Vga - && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) - - }; - - AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, - eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); - - DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.DmCat, null, this); - HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, - eRoutingPortConnectionType.Hdmi, null, this); - - - AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, - AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, - AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, - AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, - Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback, DisplayPortVideoSyncFeedback); - - // Set Ports for CEC - HdmiIn1.Port = Tx.HdmiInputs[1]; - HdmiIn2.Port = Tx.HdmiInputs[2]; - HdmiLoopOut.Port = Tx.HdmiOutput; - DmOut.Port = Tx.DmOutput; - } - - void DisplayPortInputStreamChange(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) - { - Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); - - switch (args.EventId) - { - case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: - DisplayPortVideoSyncFeedback.FireUpdate(); - break; - } - } - - - - public override bool CustomActivate() - { - // Link up all of these damned events to the various RoutingPorts via a helper handler - Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); - Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); - - Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); - Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); - - Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); - Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); - - // Base does register and sets up comm monitoring. - return base.CustomActivate(); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) - { - var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); - - if (Hdmi1VideoSyncFeedback != null) - { - Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); - } - if (Hdmi2VideoSyncFeedback != null) - { - Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); - } - if (DisplayPortVideoSyncFeedback != null) - { - DisplayPortVideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input3VideoSyncStatus.JoinNumber]); - } - - LinkDmTxToApi(this, trilist, joinMap, bridge); - } - - public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) - { - Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); - - switch (input) - { - case 0: - { - ExecuteSwitch(eVst.Auto, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 1: - { - ExecuteSwitch(HdmiIn1.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 2: - { - ExecuteSwitch(HdmiIn2.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 3: - { - ExecuteSwitch(DisplayPortIn.Selector, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } - case 4: - { - ExecuteSwitch(eVst.AllDisabled, null, eRoutingSignalType.Audio | eRoutingSignalType.Video); - break; - } + }; + ActiveVideoInputFeedback = new StringFeedback("ActiveVideoInput", + () => ActualActiveVideoInput.ToString()); + + Tx.HdmiInputs[1].InputStreamChange += InputStreamChangeEvent; + Tx.HdmiInputs[2].InputStreamChange += InputStreamChangeEvent; + Tx.DisplayPortInput.InputStreamChange += DisplayPortInputStreamChange; + Tx.BaseEvent += Tx_BaseEvent; + Tx.OnlineStatusChange += Tx_OnlineStatusChange; + + VideoSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + AudioSourceNumericFeedback = new IntFeedback(() => (int)Tx.VideoSourceFeedback); + + HdmiIn1HdcpCapabilityFeedback = new IntFeedback("HdmiIn1HdcpCapability", () => (int)tx.HdmiInputs[1].HdcpCapabilityFeedback); + + HdmiIn2HdcpCapabilityFeedback = new IntFeedback("HdmiIn2HdcpCapability", () => (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpStateFeedback = + new IntFeedback( + () => + tx.HdmiInputs[1].HdcpCapabilityFeedback > tx.HdmiInputs[2].HdcpCapabilityFeedback + ? (int)tx.HdmiInputs[1].HdcpCapabilityFeedback + : (int)tx.HdmiInputs[2].HdcpCapabilityFeedback); + + HdcpSupportCapability = eHdcpCapabilityType.Hdcp2_2Support; + + Hdmi1VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue); + + Hdmi2VideoSyncFeedback = new BoolFeedback(() => (bool)tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue); + + DisplayPortVideoSyncFeedback = new BoolFeedback(() => (bool)tx.DisplayPortInput.SyncDetectedFeedback.BoolValue); + + + var combinedFuncs = new VideoStatusFuncsWrapper + { + HdcpActiveFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].VideoAttributes.HdcpActiveFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].VideoAttributes.HdcpActiveFeedback.BoolValue), + + HdcpStateFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.HdcpStateFeedback.ToString(); + return ActualActiveVideoInput == eVst.Hdmi2 ? tx.HdmiInputs[2].VideoAttributes.HdcpStateFeedback.ToString() : ""; + }, + + VideoResolutionFeedbackFunc = () => + { + if (ActualActiveVideoInput == eVst.Hdmi1) + return tx.HdmiInputs[1].VideoAttributes.GetVideoResolutionString(); + if (ActualActiveVideoInput == eVst.Hdmi2) + return tx.HdmiInputs[2].VideoAttributes.GetVideoResolutionString(); + return ActualActiveVideoInput == eVst.Vga ? tx.DisplayPortInput.VideoAttributes.GetVideoResolutionString() : ""; + }, + VideoSyncFeedbackFunc = () => + (ActualActiveVideoInput == eVst.Hdmi1 + && tx.HdmiInputs[1].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Hdmi2 + && tx.HdmiInputs[2].SyncDetectedFeedback.BoolValue) + || (ActualActiveVideoInput == eVst.Vga + && tx.DisplayPortInput.SyncDetectedFeedback.BoolValue) + + }; + + AnyVideoInput = new RoutingInputPortWithVideoStatuses(DmPortName.AnyVideoIn, + eRoutingSignalType.Audio | eRoutingSignalType.Video, eRoutingPortConnectionType.None, 0, this, combinedFuncs); + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.DmCat, null, this); + HdmiLoopOut = new RoutingOutputPort(DmPortName.HdmiLoopOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this); + + + AddToFeedbackList(ActiveVideoInputFeedback, VideoSourceNumericFeedback, AudioSourceNumericFeedback, + AnyVideoInput.VideoStatus.HasVideoStatusFeedback, AnyVideoInput.VideoStatus.HdcpActiveFeedback, + AnyVideoInput.VideoStatus.HdcpStateFeedback, AnyVideoInput.VideoStatus.VideoResolutionFeedback, + AnyVideoInput.VideoStatus.VideoSyncFeedback, HdmiIn1HdcpCapabilityFeedback, HdmiIn2HdcpCapabilityFeedback, + Hdmi1VideoSyncFeedback, Hdmi2VideoSyncFeedback, DisplayPortVideoSyncFeedback); + + // Set Ports for CEC + HdmiIn1.Port = Tx.HdmiInputs[1]; + HdmiIn2.Port = Tx.HdmiInputs[2]; + HdmiLoopOut.Port = Tx.HdmiOutput; + DmOut.Port = Tx.DmOutput; + } + + void DisplayPortInputStreamChange(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + switch (args.EventId) + { + case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: + DisplayPortVideoSyncFeedback.FireUpdate(); + break; + } + } + + + + public override bool CustomActivate() + { + // Link up all of these damned events to the various RoutingPorts via a helper handler + Tx.HdmiInputs[1].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn1, a.EventId); + Tx.HdmiInputs[1].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn1, a.EventId); + + Tx.HdmiInputs[2].InputStreamChange += (o, a) => FowardInputStreamChange(HdmiIn2, a.EventId); + Tx.HdmiInputs[2].VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(HdmiIn2, a.EventId); + + Tx.DisplayPortInput.InputStreamChange += (o, a) => FowardInputStreamChange(DisplayPortIn, a.EventId); + Tx.DisplayPortInput.VideoAttributes.AttributeChange += (o, a) => ForwardVideoAttributeChange(DisplayPortIn, a.EventId); + + // Base does register and sets up comm monitoring. + return base.CustomActivate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = GetDmTxJoinMap(joinStart, joinMapKey); + + if (Hdmi1VideoSyncFeedback != null) + { + Hdmi1VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input1VideoSyncStatus.JoinNumber]); + } + if (Hdmi2VideoSyncFeedback != null) + { + Hdmi2VideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input2VideoSyncStatus.JoinNumber]); + } + if (DisplayPortVideoSyncFeedback != null) + { + DisplayPortVideoSyncFeedback.LinkInputSig(trilist.BooleanInput[joinMap.Input3VideoSyncStatus.JoinNumber]); + } + + LinkDmTxToApi(this, trilist, joinMap, bridge); + } + + public void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type) + { + Debug.Console(2, this, "Executing Numeric Switch to input {0}.", input); + + switch (input) + { + case 0: + { + ExecuteSwitch(eVst.Auto, null, type); + break; + } + case 1: + { + ExecuteSwitch(HdmiIn1.Selector, null, type); + break; + } + case 2: + { + ExecuteSwitch(HdmiIn2.Selector, null, type); + break; + } + case 3: + { + ExecuteSwitch(DisplayPortIn.Selector, null, type); + break; + } + case 4: + { + ExecuteSwitch(eVst.AllDisabled, null, type); + break; + } default: { Debug.Console(2, this, "Unable to execute numeric switch to input {0}", input); break; - } - - } - - - } - - public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) - { - Debug.Console(2, this, "Attempting to switch InputSelector {0}", ((eVst)inputSelector).ToString()); - if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) - Tx.VideoSource = (eVst)inputSelector; - - // NOTE: It's possible that this particular TX model may not like the AudioSource property being set. - // The SIMPL definition only shows a single analog for AudioVideo Source - if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) - //it doesn't - Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); - //Tx.AudioSource = (eAst)inputSelector; - } - - void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) - { - Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); - - switch (args.EventId) - { - case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: - case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: - case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); - HdcpStateFeedback.FireUpdate(); - break; - case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: - if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); - if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); - break; - } + } + + } + + + } + + public void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType) + { + try + { + Debug.Console(2, this, "Attempting to switch InputSelector {0}", ((eVst)inputSelector).ToString()); + if ((signalType | eRoutingSignalType.Video) == eRoutingSignalType.Video) + Tx.VideoSource = (eVst)inputSelector; + + // NOTE: It's possible that this particular TX model may not like the AudioSource property being set. + // The SIMPL definition only shows a single analog for AudioVideo Source + if ((signalType | eRoutingSignalType.Audio) == eRoutingSignalType.Audio) + //it doesn't + Debug.Console(2, this, "Unable to execute audio-only switch for tx {0}", Key); + //Tx.AudioSource = (eAst)inputSelector; + } + catch (Exception e) + { + Debug.Console(2, this, "Exception in ExecuteSwitch: {0}", e); + } + } + + void InputStreamChangeEvent(EndpointInputStream inputStream, EndpointInputStreamEventArgs args) + { + Debug.Console(2, "{0} event {1} stream {2}", Tx.ToString(), inputStream.ToString(), args.EventId.ToString()); + + switch (args.EventId) + { + case EndpointInputStreamEventIds.HdcpSupportOffFeedbackEventId: + case EndpointInputStreamEventIds.HdcpSupportOnFeedbackEventId: + case EndpointInputStreamEventIds.HdcpCapabilityFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) HdmiIn1HdcpCapabilityFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) HdmiIn2HdcpCapabilityFeedback.FireUpdate(); + HdcpStateFeedback.FireUpdate(); + break; + case EndpointInputStreamEventIds.SyncDetectedFeedbackEventId: + if (inputStream == Tx.HdmiInputs[1]) Hdmi1VideoSyncFeedback.FireUpdate(); + if (inputStream == Tx.HdmiInputs[2]) Hdmi2VideoSyncFeedback.FireUpdate(); + break; + } } void Tx_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args) @@ -363,57 +370,57 @@ namespace PepperDash.Essentials.DM OnSwitchChange(new RoutingNumericEventArgs(1, AudioSourceNumericFeedback.UShortValue, OutputPorts.First(), localInputAudioPort, eRoutingSignalType.Audio)); break; } - } - - /// - /// Relays the input stream change to the appropriate RoutingInputPort. - /// - void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) return; - inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); - } - - /// - /// Relays the VideoAttributes change to a RoutingInputPort - /// - void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) - { - //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds - //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", - // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); - switch (eventId) - { - case VideoAttributeEventIds.HdcpActiveFeedbackEventId: - inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HdcpStateFeedbackEventId: - inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: - case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: - inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); - AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); - break; - } - } - - - #region IIROutputPorts Members - public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } - public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } - #endregion - - #region IComPorts Members - public CrestronCollection ComPorts { get { return Tx.ComPorts; } } - public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } - #endregion - } + } + + /// + /// Relays the input stream change to the appropriate RoutingInputPort. + /// + void FowardInputStreamChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + if (eventId != EndpointInputStreamEventIds.SyncDetectedFeedbackEventId) return; + inputPort.VideoStatus.VideoSyncFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoSyncFeedback.FireUpdate(); + } + + /// + /// Relays the VideoAttributes change to a RoutingInputPort + /// + void ForwardVideoAttributeChange(RoutingInputPortWithVideoStatuses inputPort, int eventId) + { + //// LOCATION: Crestron.SimplSharpPro.DM.VideoAttributeEventIds + //Debug.Console(2, this, "VideoAttributes_AttributeChange event id={0} from {1}", + // args.EventId, (sender as VideoAttributesEnhanced).Owner.GetType()); + switch (eventId) + { + case VideoAttributeEventIds.HdcpActiveFeedbackEventId: + inputPort.VideoStatus.HdcpActiveFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpActiveFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HdcpStateFeedbackEventId: + inputPort.VideoStatus.HdcpStateFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.HdcpStateFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.HorizontalResolutionFeedbackEventId: + case VideoAttributeEventIds.VerticalResolutionFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + case VideoAttributeEventIds.FramesPerSecondFeedbackEventId: + inputPort.VideoStatus.VideoResolutionFeedback.FireUpdate(); + AnyVideoInput.VideoStatus.VideoResolutionFeedback.FireUpdate(); + break; + } + } + + + #region IIROutputPorts Members + public CrestronCollection IROutputPorts { get { return Tx.IROutputPorts; } } + public int NumberOfIROutputPorts { get { return Tx.NumberOfIROutputPorts; } } + #endregion + + #region IComPorts Members + public CrestronCollection ComPorts { get { return Tx.ComPorts; } } + public int NumberOfComPorts { get { return Tx.NumberOfComPorts; } } + #endregion + } } \ No newline at end of file From 91abe4c09a78c904193a285692e6170c7922c22a Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 7 Jan 2021 15:23:35 -0700 Subject: [PATCH 73/89] Updates to IR files and Enter command --- IR Drivers/Comcast X1.ir | Bin 3757 -> 3757 bytes IR Drivers/DirecTV H21.ir | Bin 3854 -> 3872 bytes .../SetTopBox/IRSetTopBoxBase.cs | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/IR Drivers/Comcast X1.ir b/IR Drivers/Comcast X1.ir index bff5afff90cd427859f081cb3ae4b0ec91ce3004..4f6c638e63b27f78ee33fd30f295b789b8b7c2b0 100644 GIT binary patch delta 16 XcmZ20yH<8WD664?xsl<nYguCN_1FC Pby>>wSjsjo%Hab54CfFe delta 41 xcmZ1=*C!{!^1(BR Date: Thu, 7 Jan 2021 19:31:24 -0500 Subject: [PATCH 74/89] Add safety check for casting device into interface (device as IRoutingInputsOutputs).InputPorts will throw Exception on accessing InputPorts property if device do not implement such interface. --- .../PepperDashEssentialsBase/Devices/DeviceManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs index 0e4efa10..55bc523a 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Devices/DeviceManager.cs @@ -360,9 +360,9 @@ namespace PepperDash.Essentials.Core { var device = GetDeviceForKey(s); - if (device == null) return; - var inputPorts = (device as IRoutingInputsOutputs).InputPorts; - var outputPorts = (device as IRoutingInputsOutputs).OutputPorts; + if (device == null) return; + var inputPorts = ((device as IRoutingInputs) != null) ? (device as IRoutingInputs).InputPorts : null; + var outputPorts = ((device as IRoutingOutputs) != null) ? (device as IRoutingOutputs).OutputPorts : null; if (inputPorts != null) { Debug.Console(0, "Device {0} has {1} Input Ports:", s, inputPorts.Count); From 030d69c190f9cfd3127ebbcc327d732d7ac4ce66 Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Tue, 12 Jan 2021 09:20:15 -0500 Subject: [PATCH 75/89] fixes a bug where the system monitor bridge wasn't updating - fires off all program start/stop/register feedbacks when they are created to set initial state --- .../Monitoring/SystemMonitorController.cs | 1352 +++++++++-------- 1 file changed, 679 insertions(+), 673 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs index eadaab12..ec18c995 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/SystemMonitorController.cs @@ -1,106 +1,106 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.Diagnostics; -using PepperDash.Core; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using PepperDash.Essentials.Core.Bridges; - -namespace PepperDash.Essentials.Core.Monitoring -{ - /// - /// Wrapper for the static SystemMonitor class to extend functionality and provide external access - /// to SystemMonitor via APIs - /// - public class SystemMonitorController : EssentialsBridgeableDevice - { - private const long UptimePollTime = 300000; - private CTimer _uptimePollTimer; - - private string _uptime; - private string _lastStart; - - public event EventHandler SystemMonitorPropertiesChanged; - - public Dictionary ProgramStatusFeedbackCollection; - public Dictionary EthernetStatusFeedbackCollection; - - public IntFeedback TimeZoneFeedback { get; protected set; } - public StringFeedback TimeZoneTextFeedback { get; protected set; } - - public StringFeedback IoControllerVersionFeedback { get; protected set; } - public StringFeedback SnmpVersionFeedback { get; protected set; } - public StringFeedback BaCnetAppVersionFeedback { get; protected set; } - public StringFeedback ControllerVersionFeedback { get; protected set; } - - //new feedbacks. Issue #50 - public StringFeedback SerialNumberFeedback { get; protected set; } - public StringFeedback ModelFeedback { get; set; } - - public StringFeedback UptimeFeedback { get; set; } - public StringFeedback LastStartFeedback { get; set; } - - public SystemMonitorController(string key) - : base(key) - { - Debug.Console(2, this, "Adding SystemMonitorController."); - - SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; - - TimeZoneFeedback = new IntFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneNumber); - TimeZoneTextFeedback = new StringFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneName); - - IoControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.IOPVersion); - SnmpVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.SNMPVersion); - BaCnetAppVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.BACNetVersion); - ControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.ControlSystemVersion); - - SerialNumberFeedback = new StringFeedback(() => CrestronEnvironment.SystemInfo.SerialNumber); - ModelFeedback = new StringFeedback(() => InitialParametersClass.ControllerPromptName); - UptimeFeedback = new StringFeedback(() => _uptime); - LastStartFeedback = new StringFeedback(()=> _lastStart); - - ProgramStatusFeedbackCollection = new Dictionary(); - - foreach (var prog in SystemMonitor.ProgramCollection) - { - var program = new ProgramStatusFeedbacks(prog); - ProgramStatusFeedbackCollection.Add(prog.Number, program); - } - - CreateEthernetStatusFeedbacks(); - UpdateEthernetStatusFeeedbacks(); - - _uptimePollTimer = new CTimer(PollUptime,null,0, UptimePollTime); - - SystemMonitor.ProgramChange += SystemMonitor_ProgramChange; - SystemMonitor.TimeZoneInformation.TimeZoneChange += TimeZoneInformation_TimeZoneChange; - CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler; - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler; - } - - private void CrestronEnvironmentOnProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType != eProgramStatusEventType.Stopping) return; - - _uptimePollTimer.Stop(); - _uptimePollTimer.Dispose(); - _uptimePollTimer = null; - } - - private void PollUptime(object obj) - { - var consoleResponse = string.Empty; - - CrestronConsole.SendControlSystemCommand("uptime", ref consoleResponse); - - ParseUptime(consoleResponse); - - UptimeFeedback.FireUpdate(); - LastStartFeedback.FireUpdate(); +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.Diagnostics; +using PepperDash.Core; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.Core.Monitoring +{ + /// + /// Wrapper for the static SystemMonitor class to extend functionality and provide external access + /// to SystemMonitor via APIs + /// + public class SystemMonitorController : EssentialsBridgeableDevice + { + private const long UptimePollTime = 300000; + private CTimer _uptimePollTimer; + + private string _uptime; + private string _lastStart; + + public event EventHandler SystemMonitorPropertiesChanged; + + public Dictionary ProgramStatusFeedbackCollection; + public Dictionary EthernetStatusFeedbackCollection; + + public IntFeedback TimeZoneFeedback { get; protected set; } + public StringFeedback TimeZoneTextFeedback { get; protected set; } + + public StringFeedback IoControllerVersionFeedback { get; protected set; } + public StringFeedback SnmpVersionFeedback { get; protected set; } + public StringFeedback BaCnetAppVersionFeedback { get; protected set; } + public StringFeedback ControllerVersionFeedback { get; protected set; } + + //new feedbacks. Issue #50 + public StringFeedback SerialNumberFeedback { get; protected set; } + public StringFeedback ModelFeedback { get; set; } + + public StringFeedback UptimeFeedback { get; set; } + public StringFeedback LastStartFeedback { get; set; } + + public SystemMonitorController(string key) + : base(key) + { + Debug.Console(2, this, "Adding SystemMonitorController."); + + SystemMonitor.ProgramInitialization.ProgramInitializationUnderUserControl = true; + + TimeZoneFeedback = new IntFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneNumber); + TimeZoneTextFeedback = new StringFeedback(() => SystemMonitor.TimeZoneInformation.TimeZoneName); + + IoControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.IOPVersion); + SnmpVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.SNMPVersion); + BaCnetAppVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.BACNetVersion); + ControllerVersionFeedback = new StringFeedback(() => SystemMonitor.VersionInformation.ControlSystemVersion); + + SerialNumberFeedback = new StringFeedback(() => CrestronEnvironment.SystemInfo.SerialNumber); + ModelFeedback = new StringFeedback(() => InitialParametersClass.ControllerPromptName); + UptimeFeedback = new StringFeedback(() => _uptime); + LastStartFeedback = new StringFeedback(()=> _lastStart); + + ProgramStatusFeedbackCollection = new Dictionary(); + + foreach (var prog in SystemMonitor.ProgramCollection) + { + var program = new ProgramStatusFeedbacks(prog); + ProgramStatusFeedbackCollection.Add(prog.Number, program); + } + + CreateEthernetStatusFeedbacks(); + UpdateEthernetStatusFeeedbacks(); + + _uptimePollTimer = new CTimer(PollUptime,null,0, UptimePollTime); + + SystemMonitor.ProgramChange += SystemMonitor_ProgramChange; + SystemMonitor.TimeZoneInformation.TimeZoneChange += TimeZoneInformation_TimeZoneChange; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironmentOnEthernetEventHandler; + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironmentOnProgramStatusEventHandler; + } + + private void CrestronEnvironmentOnProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) return; + + _uptimePollTimer.Stop(); + _uptimePollTimer.Dispose(); + _uptimePollTimer = null; + } + + private void PollUptime(object obj) + { + var consoleResponse = string.Empty; + + CrestronConsole.SendControlSystemCommand("uptime", ref consoleResponse); + + ParseUptime(consoleResponse); + + UptimeFeedback.FireUpdate(); + LastStartFeedback.FireUpdate(); } private void ParseUptime(string response) @@ -123,94 +123,94 @@ namespace PepperDash.Essentials.Core.Monitoring _uptime = uptimeRaw.Substring(forIndex + 4); } - private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return; - - foreach (var fb in EthernetStatusFeedbackCollection) - { - fb.Value.UpdateEthernetStatus(); - } - } - - private void CreateEthernetStatusFeedbacks() - { - EthernetStatusFeedbackCollection = new Dictionary(); - - Debug.Console(2, "Creating {0} EthernetStatusFeedbacks", InitialParametersClass.NumberOfEthernetInterfaces); - - for (short i = 0; i < InitialParametersClass.NumberOfEthernetInterfaces; i++) - { - Debug.Console(2, "Creating EthernetStatusFeedback for Interface {0}", i); - var ethernetInterface = new EthernetStatusFeedbacks(i); - EthernetStatusFeedbackCollection.Add(i, ethernetInterface); - } - } - - private void UpdateEthernetStatusFeeedbacks() - { - foreach (var iface in EthernetStatusFeedbackCollection) - { - iface.Value.CurrentIpAddressFeedback.FireUpdate(); - iface.Value.CurrentSubnetMaskFeedback.FireUpdate(); - iface.Value.CurrentDefaultGatewayFeedback.FireUpdate(); - iface.Value.StaticIpAddressFeedback.FireUpdate(); - iface.Value.StaticSubnetMaskFeedback.FireUpdate(); - iface.Value.StaticDefaultGatewayFeedback.FireUpdate(); - iface.Value.HostNameFeedback.FireUpdate(); - iface.Value.DnsServerFeedback.FireUpdate(); - iface.Value.DomainFeedback.FireUpdate(); - iface.Value.DhcpStatusFeedback.FireUpdate(); - iface.Value.MacAddressFeedback.FireUpdate(); - } - } - - /// - /// Gets data in separate thread - /// - private void RefreshSystemMonitorData() - { - // this takes a while, launch a new thread - CrestronInvoke.BeginInvoke(UpdateFeedback); - } - - private void UpdateFeedback(object o) - { - TimeZoneFeedback.FireUpdate(); - TimeZoneTextFeedback.FireUpdate(); - IoControllerVersionFeedback.FireUpdate(); - SnmpVersionFeedback.FireUpdate(); - BaCnetAppVersionFeedback.FireUpdate(); - ControllerVersionFeedback.FireUpdate(); - SerialNumberFeedback.FireUpdate(); - ModelFeedback.FireUpdate(); - - OnSystemMonitorPropertiesChanged(); - } - - private void OnSystemMonitorPropertiesChanged() - { - var handler = SystemMonitorPropertiesChanged; - if (handler != null) - { - handler(this, new EventArgs()); - } - } - - public override bool CustomActivate() - { - RefreshSystemMonitorData(); - - return base.CustomActivate(); - } - - public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + private void CrestronEnvironmentOnEthernetEventHandler(EthernetEventArgs ethernetEventArgs) { - var joinMap = new SystemMonitorJoinMap(joinStart); - - var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); - - if (!string.IsNullOrEmpty(joinMapSerialized)) + if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp) return; + + foreach (var fb in EthernetStatusFeedbackCollection) + { + fb.Value.UpdateEthernetStatus(); + } + } + + private void CreateEthernetStatusFeedbacks() + { + EthernetStatusFeedbackCollection = new Dictionary(); + + Debug.Console(2, "Creating {0} EthernetStatusFeedbacks", InitialParametersClass.NumberOfEthernetInterfaces); + + for (short i = 0; i < InitialParametersClass.NumberOfEthernetInterfaces; i++) + { + Debug.Console(2, "Creating EthernetStatusFeedback for Interface {0}", i); + var ethernetInterface = new EthernetStatusFeedbacks(i); + EthernetStatusFeedbackCollection.Add(i, ethernetInterface); + } + } + + private void UpdateEthernetStatusFeeedbacks() + { + foreach (var iface in EthernetStatusFeedbackCollection) + { + iface.Value.CurrentIpAddressFeedback.FireUpdate(); + iface.Value.CurrentSubnetMaskFeedback.FireUpdate(); + iface.Value.CurrentDefaultGatewayFeedback.FireUpdate(); + iface.Value.StaticIpAddressFeedback.FireUpdate(); + iface.Value.StaticSubnetMaskFeedback.FireUpdate(); + iface.Value.StaticDefaultGatewayFeedback.FireUpdate(); + iface.Value.HostNameFeedback.FireUpdate(); + iface.Value.DnsServerFeedback.FireUpdate(); + iface.Value.DomainFeedback.FireUpdate(); + iface.Value.DhcpStatusFeedback.FireUpdate(); + iface.Value.MacAddressFeedback.FireUpdate(); + } + } + + /// + /// Gets data in separate thread + /// + private void RefreshSystemMonitorData() + { + // this takes a while, launch a new thread + CrestronInvoke.BeginInvoke(UpdateFeedback); + } + + private void UpdateFeedback(object o) + { + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + IoControllerVersionFeedback.FireUpdate(); + SnmpVersionFeedback.FireUpdate(); + BaCnetAppVersionFeedback.FireUpdate(); + ControllerVersionFeedback.FireUpdate(); + SerialNumberFeedback.FireUpdate(); + ModelFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + + private void OnSystemMonitorPropertiesChanged() + { + var handler = SystemMonitorPropertiesChanged; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + public override bool CustomActivate() + { + RefreshSystemMonitorData(); + + return base.CustomActivate(); + } + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new SystemMonitorJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) joinMap = JsonConvert.DeserializeObject(joinMapSerialized); if (bridge != null) @@ -220,11 +220,11 @@ namespace PepperDash.Essentials.Core.Monitoring else { Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); - } - - Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); - Debug.Console(2, this, "Linking API starting at join: {0}", joinStart); - + } + + Debug.Console(1, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + Debug.Console(2, this, "Linking API starting at join: {0}", joinStart); + TimeZoneFeedback.LinkInputSig(trilist.UShortInput[joinMap.TimeZone.JoinNumber]); TimeZoneTextFeedback.LinkInputSig(trilist.StringInput[joinMap.TimeZoneName.JoinNumber]); @@ -235,18 +235,18 @@ namespace PepperDash.Essentials.Core.Monitoring SerialNumberFeedback.LinkInputSig(trilist.StringInput[joinMap.SerialNumber.JoinNumber]); ModelFeedback.LinkInputSig(trilist.StringInput[joinMap.Model.JoinNumber]); UptimeFeedback.LinkInputSig(trilist.StringInput[joinMap.Uptime.JoinNumber]); - LastStartFeedback.LinkInputSig(trilist.StringInput[joinMap.LastBoot.JoinNumber]); - - // iterate the program status feedback collection and map all the joins - LinkProgramInfoJoins(this, trilist, joinMap); - - LinkEthernetInfoJoins(this, trilist, joinMap); - } - - private static void LinkEthernetInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, SystemMonitorJoinMap joinMap) + LastStartFeedback.LinkInputSig(trilist.StringInput[joinMap.LastBoot.JoinNumber]); + + // iterate the program status feedback collection and map all the joins + LinkProgramInfoJoins(this, trilist, joinMap); + + LinkEthernetInfoJoins(this, trilist, joinMap); + } + + private static void LinkEthernetInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, SystemMonitorJoinMap joinMap) { - uint ethernetSlotJoinStart = 0; - foreach (var fb in systemMonitorController.EthernetStatusFeedbackCollection) + uint ethernetSlotJoinStart = 0; + foreach (var fb in systemMonitorController.EthernetStatusFeedbackCollection) { fb.Value.CurrentIpAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentIpAddress.JoinNumber]); fb.Value.CurrentSubnetMaskFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.CurrentSubnetMask.JoinNumber]); @@ -258,494 +258,500 @@ namespace PepperDash.Essentials.Core.Monitoring fb.Value.MacAddressFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.MacAddress.JoinNumber]); fb.Value.DomainFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.Domain.JoinNumber]); fb.Value.DnsServerFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DnsServer.JoinNumber]); - fb.Value.DhcpStatusFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DhcpStatus.JoinNumber]); - - ethernetSlotJoinStart += joinMap.EthernetOffsetJoin.JoinNumber; - } - } - - private static void LinkProgramInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, - SystemMonitorJoinMap joinMap) + fb.Value.DhcpStatusFeedback.LinkInputSig(trilist.StringInput[ethernetSlotJoinStart + joinMap.DhcpStatus.JoinNumber]); + + ethernetSlotJoinStart += joinMap.EthernetOffsetJoin.JoinNumber; + } + } + + private static void LinkProgramInfoJoins(SystemMonitorController systemMonitorController, BasicTriList trilist, + SystemMonitorJoinMap joinMap) { - uint programSlotJoinStart = 0; - - foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection) - { + uint programSlotJoinStart = 0; + + foreach (var p in systemMonitorController.ProgramStatusFeedbackCollection) + { var programNumber = p.Value.Program.Number; - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart.JoinNumber, + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStart.JoinNumber, b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Start); p.Value.ProgramStartedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStart.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop.JoinNumber, + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramStop.JoinNumber, b => SystemMonitor.ProgramCollection[programNumber].OperatingState = eProgramOperatingState.Stop); p.Value.ProgramStoppedFeedback.LinkInputSig(trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramStop.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister.JoinNumber, - b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register); + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramRegister.JoinNumber, + b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Register); p.Value.ProgramRegisteredFeedback.LinkInputSig( trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramRegister.JoinNumber]); - trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber, - b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister); + trilist.SetBoolSigAction(programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber, + b => SystemMonitor.ProgramCollection[programNumber].RegistrationState = eProgramRegistrationState.Unregister); p.Value.ProgramUnregisteredFeedback.LinkInputSig( trilist.BooleanInput[programSlotJoinStart + joinMap.ProgramUnregister.JoinNumber]); - p.Value.ProgramNameFeedback.LinkInputSig(trilist.StringInput[programSlotJoinStart + joinMap.ProgramName.JoinNumber]); + p.Value.ProgramNameFeedback.LinkInputSig(trilist.StringInput[programSlotJoinStart + joinMap.ProgramName.JoinNumber]); p.Value.ProgramCompileTimeFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramCompiledTime.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramCompiledTime.JoinNumber]); p.Value.CrestronDataBaseVersionFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramCrestronDatabaseVersion.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramCrestronDatabaseVersion.JoinNumber]); p.Value.EnvironmentVersionFeedback.LinkInputSig( - trilist.StringInput[programSlotJoinStart + joinMap.ProgramEnvironmentVersion.JoinNumber]); + trilist.StringInput[programSlotJoinStart + joinMap.ProgramEnvironmentVersion.JoinNumber]); p.Value.AggregatedProgramInfoFeedback.LinkInputSig( trilist.StringInput[programSlotJoinStart + joinMap.AggregatedProgramInfo.JoinNumber]); - programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinNumber; - } - } - - //// Sets the time zone - //public void SetTimeZone(int timeZone) - //{ - // SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone; - //} - - /// - /// Responds to program change events and triggers the appropriate feedbacks to update - /// - /// - /// - private void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args) - { - Debug.Console(2, this, "Program Change Detected for slot: {0}", sender.Number); - Debug.Console(2, this, "Event Type: {0}", args.EventType); - - var program = ProgramStatusFeedbackCollection[sender.Number]; - - switch (args.EventType) - { - case eProgramChangeEventType.OperatingState: - program.ProgramStartedFeedback.FireUpdate(); - program.ProgramStoppedFeedback.FireUpdate(); - program.ProgramInfo.OperatingState = args.OperatingState; - if (args.OperatingState == eProgramOperatingState.Start) - program.GetProgramInfo(); - else - { - program.AggregatedProgramInfoFeedback.FireUpdate(); - program.OnProgramInfoChanged(); - } - break; - case eProgramChangeEventType.RegistrationState: - program.ProgramRegisteredFeedback.FireUpdate(); - program.ProgramUnregisteredFeedback.FireUpdate(); - program.ProgramInfo.RegistrationState = args.RegistrationState; - program.GetProgramInfo(); - break; - } - } - - /// - /// Responds to time zone changes and updates the appropriate feedbacks - /// - /// - private void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args) - { - Debug.Console(2, this, "Time Zone Change Detected."); - TimeZoneFeedback.FireUpdate(); - TimeZoneTextFeedback.FireUpdate(); - - OnSystemMonitorPropertiesChanged(); - } - - public class EthernetStatusFeedbacks - { - public StringFeedback HostNameFeedback { get; protected set; } - public StringFeedback DnsServerFeedback { get; protected set; } - public StringFeedback DomainFeedback { get; protected set; } - public StringFeedback MacAddressFeedback { get; protected set; } - public StringFeedback DhcpStatusFeedback { get; protected set; } - - public StringFeedback CurrentIpAddressFeedback { get; protected set; } - public StringFeedback CurrentSubnetMaskFeedback { get; protected set; } - public StringFeedback CurrentDefaultGatewayFeedback { get; protected set; } - - public StringFeedback StaticIpAddressFeedback { get; protected set; } - public StringFeedback StaticSubnetMaskFeedback { get; protected set; } - public StringFeedback StaticDefaultGatewayFeedback { get; protected set; } - - public EthernetStatusFeedbacks(short adapterIndex) - { - Debug.Console(2, "Ethernet Information for interface {0}", adapterIndex); - Debug.Console(2, "Adapter Index: {1} Hostname: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Current Router: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPADDRESS, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPMASK, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Static Router: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_ROUTER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} DNS Servers: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} DHCP State: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} Domain Name: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex), adapterIndex); - Debug.Console(2, "Adapter Index: {1} MAC Address: {0}", CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex), adapterIndex); - HostNameFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex)); - - CurrentIpAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); - CurrentDefaultGatewayFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); - CurrentSubnetMaskFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); - StaticIpAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); - StaticDefaultGatewayFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); - StaticSubnetMaskFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); - DomainFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex)); - DnsServerFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex)); - MacAddressFeedback = - new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex)); - - DhcpStatusFeedback = new StringFeedback( - () => - CrestronEthernetHelper.GetEthernetParameter( - CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex)); - } - - public void UpdateEthernetStatus() - { - HostNameFeedback.FireUpdate(); - CurrentIpAddressFeedback.FireUpdate(); - CurrentSubnetMaskFeedback.FireUpdate(); - CurrentDefaultGatewayFeedback.FireUpdate(); - StaticIpAddressFeedback.FireUpdate(); - StaticSubnetMaskFeedback.FireUpdate(); - StaticDefaultGatewayFeedback.FireUpdate(); - DomainFeedback.FireUpdate(); - DnsServerFeedback.FireUpdate(); - MacAddressFeedback.FireUpdate(); - DhcpStatusFeedback.FireUpdate(); - } - } - - - public class ProgramStatusFeedbacks - { - public event EventHandler ProgramInfoChanged; - - public Program Program; - - public ProgramInfo ProgramInfo { get; set; } - - public BoolFeedback ProgramStartedFeedback; - public BoolFeedback ProgramStoppedFeedback; - public BoolFeedback ProgramRegisteredFeedback; - public BoolFeedback ProgramUnregisteredFeedback; - - public StringFeedback ProgramNameFeedback; - public StringFeedback ProgramCompileTimeFeedback; - public StringFeedback CrestronDataBaseVersionFeedback; - // SIMPL windows version - public StringFeedback EnvironmentVersionFeedback; - public StringFeedback AggregatedProgramInfoFeedback; - - public ProgramStatusFeedbacks(Program program) - { - ProgramInfo = new ProgramInfo(program.Number); - - Program = program; - - ProgramInfo.OperatingState = Program.OperatingState; - ProgramInfo.RegistrationState = Program.RegistrationState; - - ProgramStartedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Start); - ProgramStoppedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Stop); - ProgramRegisteredFeedback = - new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Register); - ProgramUnregisteredFeedback = - new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Unregister); - - ProgramNameFeedback = new StringFeedback(() => ProgramInfo.ProgramFile); - ProgramCompileTimeFeedback = new StringFeedback(() => ProgramInfo.CompileTime); - CrestronDataBaseVersionFeedback = new StringFeedback(() => ProgramInfo.CrestronDb); - EnvironmentVersionFeedback = new StringFeedback(() => ProgramInfo.Environment); - - AggregatedProgramInfoFeedback = new StringFeedback(() => JsonConvert.SerializeObject(ProgramInfo)); - - GetProgramInfo(); - } - - /// - /// Retrieves information about a running program - /// - public void GetProgramInfo() - { - CrestronInvoke.BeginInvoke(GetProgramInfo); - } - - private void GetProgramInfo(object o) - { - Debug.Console(2, "Attempting to get program info for slot: {0}", Program.Number); - - string response = null; - - if (Program.RegistrationState == eProgramRegistrationState.Unregister || Program.OperatingState == eProgramOperatingState.Stop) - { - Debug.Console(2, "Program {0} not registered. Setting default values for program information.", - Program.Number); - - ProgramInfo = new ProgramInfo(Program.Number) - { - OperatingState = Program.OperatingState, - RegistrationState = Program.RegistrationState - }; - - return; - } - - var success = CrestronConsole.SendControlSystemCommand( - string.Format("progcomments:{0}", Program.Number), ref response); - - if (!success) - { - Debug.Console(2, "Progcomments Attempt Unsuccessful for slot: {0}", Program.Number); - UpdateFeedbacks(); - return; - } - - if (response.ToLower().Contains("bad or incomplete")) - { - Debug.Console(2, - "Program in slot {0} not running. Setting default ProgramInfo for slot: {0}", - Program.Number); - - // Assume no valid program info. Constructing a new object will wipe all properties - ProgramInfo = new ProgramInfo(Program.Number) - { - OperatingState = Program.OperatingState, - RegistrationState = Program.RegistrationState - }; - - UpdateFeedbacks(); - - return; - } - - - // Shared properteis - ProgramInfo.ProgramFile = ParseConsoleData(response, "Program File", ": ", "\n"); - ProgramInfo.CompilerRevision = ParseConsoleData(response, "Compiler Rev", ": ", "\n"); - ProgramInfo.CompileTime = ParseConsoleData(response, "Compiled On", ": ", "\n"); - ProgramInfo.Include4Dat = ParseConsoleData(response, "Include4.dat", ": ", "\n"); - - - if (ProgramInfo.ProgramFile.Contains(".dll")) - { - // SSP Program - ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ": ", "\n"); - ProgramInfo.ApplicationName = ParseConsoleData(response, "Application Name", ": ", "\n"); - ProgramInfo.ProgramTool = ParseConsoleData(response, "Program Tool", ": ", "\n"); - ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, "Min Firmware Version", ": ", - "\n"); - ProgramInfo.PlugInVersion = ParseConsoleData(response, "PlugInVersion", ": ", "\n"); - } - else if (ProgramInfo.ProgramFile.Contains(".smw")) - { - // SIMPL Windows Program - ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ":", "\n"); - ProgramInfo.SystemName = ParseConsoleData(response, "System Name", ": ", "\n"); - ProgramInfo.CrestronDb = ParseConsoleData(response, "CrestronDB", ": ", "\n"); - ProgramInfo.Environment = ParseConsoleData(response, "Source Env", ": ", "\n"); - ProgramInfo.Programmer = ParseConsoleData(response, "Programmer", ": ", "\n"); - } - Debug.Console(2, "Program info for slot {0} successfully updated", Program.Number); - - UpdateFeedbacks(); - } - - private void UpdateFeedbacks() - { - ProgramNameFeedback.FireUpdate(); - ProgramCompileTimeFeedback.FireUpdate(); - CrestronDataBaseVersionFeedback.FireUpdate(); - EnvironmentVersionFeedback.FireUpdate(); - - AggregatedProgramInfoFeedback.FireUpdate(); - - OnProgramInfoChanged(); - } - - public void OnProgramInfoChanged() - { - //Debug.Console(1, "Firing ProgramInfoChanged for slot: {0}", Program.Number); - var handler = ProgramInfoChanged; - if (handler != null) - { - handler(this, new ProgramInfoEventArgs(ProgramInfo)); - } - } - - private string ParseConsoleData(string data, string line, string startString, string endString) - { - var outputData = ""; - - if (data.Length <= 0) return outputData; - - try - { - //Debug.Console(2, "ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}", data, line, startString, endString); - var linePosition = data.IndexOf(line, StringComparison.Ordinal); - var startPosition = data.IndexOf(startString, linePosition, StringComparison.Ordinal) + - startString.Length; - var endPosition = data.IndexOf(endString, startPosition, StringComparison.Ordinal); - outputData = data.Substring(startPosition, endPosition - startPosition).Trim(); - //Debug.Console(2, "ParseConsoleData Return: {0}", outputData); - } - catch (Exception e) - { - Debug.Console(1, "Error Parsing Console Data:\r{0}", e); - } - - return outputData; - } - } - } - - /// - /// Class for serializing program slot information - /// - public class ProgramInfo - { - // Shared properties - - [JsonProperty("programNumber")] - public uint ProgramNumber { get; private set; } - - [JsonConverter(typeof (StringEnumConverter))] - [JsonProperty("operatingState")] - public eProgramOperatingState OperatingState { get; set; } - - [JsonConverter(typeof (StringEnumConverter))] - [JsonProperty("registrationState")] - public eProgramRegistrationState RegistrationState { get; set; } - - [JsonProperty("programFile")] - public string ProgramFile { get; set; } - - [JsonProperty("friendlyName")] - public string FriendlyName { get; set; } - - [JsonProperty("compilerRevision")] - public string CompilerRevision { get; set; } - - [JsonProperty("compileTime")] - public string CompileTime { get; set; } - - [JsonProperty("include4Dat")] - public string Include4Dat { get; set; } - - // SIMPL Windows properties - [JsonProperty("systemName")] - public string SystemName { get; set; } - - [JsonProperty("crestronDb")] - public string CrestronDb { get; set; } - - [JsonProperty("environment")] - public string Environment { get; set; } - - [JsonProperty("programmer")] - public string Programmer { get; set; } - - - // SSP Properties - [JsonProperty("applicationName")] - public string ApplicationName { get; set; } - - [JsonProperty("programTool")] - public string ProgramTool { get; set; } - - [JsonProperty("minFirmwareVersion")] - public string MinFirmwareVersion { get; set; } - - [JsonProperty("plugInVersion")] - public string PlugInVersion { get; set; } - - public ProgramInfo(uint number) - { - ProgramNumber = number; - - ProgramFile = ""; - FriendlyName = ""; - CompilerRevision = ""; - CompileTime = ""; - Include4Dat = ""; - - SystemName = ""; - CrestronDb = ""; - Environment = ""; - Programmer = ""; - - ApplicationName = ""; - ProgramTool = ""; - MinFirmwareVersion = ""; - PlugInVersion = ""; - } - } - - public class ProgramInfoEventArgs : EventArgs - { - public ProgramInfo ProgramInfo; - - public ProgramInfoEventArgs(ProgramInfo progInfo) - { - ProgramInfo = progInfo; - } - } + programSlotJoinStart = programSlotJoinStart + joinMap.ProgramOffsetJoin.JoinNumber; + } + } + + //// Sets the time zone + //public void SetTimeZone(int timeZone) + //{ + // SystemMonitor.TimeZoneInformation.TimeZoneNumber = timeZone; + //} + + /// + /// Responds to program change events and triggers the appropriate feedbacks to update + /// + /// + /// + private void SystemMonitor_ProgramChange(Program sender, ProgramEventArgs args) + { + Debug.Console(2, this, "Program Change Detected for slot: {0}", sender.Number); + Debug.Console(2, this, "Event Type: {0}", args.EventType); + + var program = ProgramStatusFeedbackCollection[sender.Number]; + + switch (args.EventType) + { + case eProgramChangeEventType.OperatingState: + program.ProgramStartedFeedback.FireUpdate(); + program.ProgramStoppedFeedback.FireUpdate(); + program.ProgramInfo.OperatingState = args.OperatingState; + if (args.OperatingState == eProgramOperatingState.Start) + program.GetProgramInfo(); + else + { + program.AggregatedProgramInfoFeedback.FireUpdate(); + program.OnProgramInfoChanged(); + } + break; + case eProgramChangeEventType.RegistrationState: + program.ProgramRegisteredFeedback.FireUpdate(); + program.ProgramUnregisteredFeedback.FireUpdate(); + program.ProgramInfo.RegistrationState = args.RegistrationState; + program.GetProgramInfo(); + break; + } + } + + /// + /// Responds to time zone changes and updates the appropriate feedbacks + /// + /// + private void TimeZoneInformation_TimeZoneChange(TimeZoneEventArgs args) + { + Debug.Console(2, this, "Time Zone Change Detected."); + TimeZoneFeedback.FireUpdate(); + TimeZoneTextFeedback.FireUpdate(); + + OnSystemMonitorPropertiesChanged(); + } + + public class EthernetStatusFeedbacks + { + public StringFeedback HostNameFeedback { get; protected set; } + public StringFeedback DnsServerFeedback { get; protected set; } + public StringFeedback DomainFeedback { get; protected set; } + public StringFeedback MacAddressFeedback { get; protected set; } + public StringFeedback DhcpStatusFeedback { get; protected set; } + + public StringFeedback CurrentIpAddressFeedback { get; protected set; } + public StringFeedback CurrentSubnetMaskFeedback { get; protected set; } + public StringFeedback CurrentDefaultGatewayFeedback { get; protected set; } + + public StringFeedback StaticIpAddressFeedback { get; protected set; } + public StringFeedback StaticSubnetMaskFeedback { get; protected set; } + public StringFeedback StaticDefaultGatewayFeedback { get; protected set; } + + public EthernetStatusFeedbacks(short adapterIndex) + { + Debug.Console(2, "Ethernet Information for interface {0}", adapterIndex); + Debug.Console(2, "Adapter Index: {1} Hostname: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Current Router: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static IP Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPADDRESS, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static Subnet Mask: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_IPMASK, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Static Router: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_STATIC_ROUTER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} DNS Servers: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} DHCP State: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} Domain Name: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex), adapterIndex); + Debug.Console(2, "Adapter Index: {1} MAC Address: {0}", CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex), adapterIndex); + HostNameFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterIndex)); + + CurrentIpAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); + CurrentDefaultGatewayFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); + CurrentSubnetMaskFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); + StaticIpAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterIndex)); + StaticDefaultGatewayFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterIndex)); + StaticSubnetMaskFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterIndex)); + DomainFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterIndex)); + DnsServerFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterIndex)); + MacAddressFeedback = + new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterIndex)); + + DhcpStatusFeedback = new StringFeedback( + () => + CrestronEthernetHelper.GetEthernetParameter( + CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterIndex)); + } + + public void UpdateEthernetStatus() + { + HostNameFeedback.FireUpdate(); + CurrentIpAddressFeedback.FireUpdate(); + CurrentSubnetMaskFeedback.FireUpdate(); + CurrentDefaultGatewayFeedback.FireUpdate(); + StaticIpAddressFeedback.FireUpdate(); + StaticSubnetMaskFeedback.FireUpdate(); + StaticDefaultGatewayFeedback.FireUpdate(); + DomainFeedback.FireUpdate(); + DnsServerFeedback.FireUpdate(); + MacAddressFeedback.FireUpdate(); + DhcpStatusFeedback.FireUpdate(); + } + } + + + public class ProgramStatusFeedbacks + { + public event EventHandler ProgramInfoChanged; + + public Program Program; + + public ProgramInfo ProgramInfo { get; set; } + + public BoolFeedback ProgramStartedFeedback; + public BoolFeedback ProgramStoppedFeedback; + public BoolFeedback ProgramRegisteredFeedback; + public BoolFeedback ProgramUnregisteredFeedback; + + public StringFeedback ProgramNameFeedback; + public StringFeedback ProgramCompileTimeFeedback; + public StringFeedback CrestronDataBaseVersionFeedback; + // SIMPL windows version + public StringFeedback EnvironmentVersionFeedback; + public StringFeedback AggregatedProgramInfoFeedback; + + public ProgramStatusFeedbacks(Program program) + { + ProgramInfo = new ProgramInfo(program.Number); + + Program = program; + + ProgramInfo.OperatingState = Program.OperatingState; + ProgramInfo.RegistrationState = Program.RegistrationState; + + ProgramStartedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Start); + ProgramStartedFeedback.FireUpdate(); + + ProgramStoppedFeedback = new BoolFeedback(() => Program.OperatingState == eProgramOperatingState.Stop); + ProgramStoppedFeedback.FireUpdate(); + + ProgramRegisteredFeedback = + new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Register); + ProgramRegisteredFeedback.FireUpdate(); + + ProgramUnregisteredFeedback = + new BoolFeedback(() => Program.RegistrationState == eProgramRegistrationState.Unregister); + ProgramUnregisteredFeedback.FireUpdate(); + + ProgramNameFeedback = new StringFeedback(() => ProgramInfo.ProgramFile); + ProgramCompileTimeFeedback = new StringFeedback(() => ProgramInfo.CompileTime); + CrestronDataBaseVersionFeedback = new StringFeedback(() => ProgramInfo.CrestronDb); + EnvironmentVersionFeedback = new StringFeedback(() => ProgramInfo.Environment); + AggregatedProgramInfoFeedback = new StringFeedback(() => JsonConvert.SerializeObject(ProgramInfo)); + + GetProgramInfo(); + } + + /// + /// Retrieves information about a running program + /// + public void GetProgramInfo() + { + CrestronInvoke.BeginInvoke(GetProgramInfo); + } + + private void GetProgramInfo(object o) + { + Debug.Console(2, "Attempting to get program info for slot: {0}", Program.Number); + + string response = null; + + if (Program.RegistrationState == eProgramRegistrationState.Unregister || Program.OperatingState == eProgramOperatingState.Stop) + { + Debug.Console(2, "Program {0} not registered. Setting default values for program information.", + Program.Number); + + ProgramInfo = new ProgramInfo(Program.Number) + { + OperatingState = Program.OperatingState, + RegistrationState = Program.RegistrationState + }; + + return; + } + + var success = CrestronConsole.SendControlSystemCommand( + string.Format("progcomments:{0}", Program.Number), ref response); + + if (!success) + { + Debug.Console(2, "Progcomments Attempt Unsuccessful for slot: {0}", Program.Number); + UpdateFeedbacks(); + return; + } + + if (response.ToLower().Contains("bad or incomplete")) + { + Debug.Console(2, + "Program in slot {0} not running. Setting default ProgramInfo for slot: {0}", + Program.Number); + + // Assume no valid program info. Constructing a new object will wipe all properties + ProgramInfo = new ProgramInfo(Program.Number) + { + OperatingState = Program.OperatingState, + RegistrationState = Program.RegistrationState + }; + + UpdateFeedbacks(); + + return; + } + + + // Shared properteis + ProgramInfo.ProgramFile = ParseConsoleData(response, "Program File", ": ", "\n"); + ProgramInfo.CompilerRevision = ParseConsoleData(response, "Compiler Rev", ": ", "\n"); + ProgramInfo.CompileTime = ParseConsoleData(response, "Compiled On", ": ", "\n"); + ProgramInfo.Include4Dat = ParseConsoleData(response, "Include4.dat", ": ", "\n"); + + + if (ProgramInfo.ProgramFile.Contains(".dll")) + { + // SSP Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ": ", "\n"); + ProgramInfo.ApplicationName = ParseConsoleData(response, "Application Name", ": ", "\n"); + ProgramInfo.ProgramTool = ParseConsoleData(response, "Program Tool", ": ", "\n"); + ProgramInfo.MinFirmwareVersion = ParseConsoleData(response, "Min Firmware Version", ": ", + "\n"); + ProgramInfo.PlugInVersion = ParseConsoleData(response, "PlugInVersion", ": ", "\n"); + } + else if (ProgramInfo.ProgramFile.Contains(".smw")) + { + // SIMPL Windows Program + ProgramInfo.FriendlyName = ParseConsoleData(response, "Friendly Name", ":", "\n"); + ProgramInfo.SystemName = ParseConsoleData(response, "System Name", ": ", "\n"); + ProgramInfo.CrestronDb = ParseConsoleData(response, "CrestronDB", ": ", "\n"); + ProgramInfo.Environment = ParseConsoleData(response, "Source Env", ": ", "\n"); + ProgramInfo.Programmer = ParseConsoleData(response, "Programmer", ": ", "\n"); + } + Debug.Console(2, "Program info for slot {0} successfully updated", Program.Number); + + UpdateFeedbacks(); + } + + private void UpdateFeedbacks() + { + ProgramNameFeedback.FireUpdate(); + ProgramCompileTimeFeedback.FireUpdate(); + CrestronDataBaseVersionFeedback.FireUpdate(); + EnvironmentVersionFeedback.FireUpdate(); + + AggregatedProgramInfoFeedback.FireUpdate(); + + OnProgramInfoChanged(); + } + + public void OnProgramInfoChanged() + { + //Debug.Console(1, "Firing ProgramInfoChanged for slot: {0}", Program.Number); + var handler = ProgramInfoChanged; + if (handler != null) + { + handler(this, new ProgramInfoEventArgs(ProgramInfo)); + } + } + + private string ParseConsoleData(string data, string line, string startString, string endString) + { + var outputData = ""; + + if (data.Length <= 0) return outputData; + + try + { + //Debug.Console(2, "ParseConsoleData Data: {0}, Line {1}, startStirng {2}, endString {3}", data, line, startString, endString); + var linePosition = data.IndexOf(line, StringComparison.Ordinal); + var startPosition = data.IndexOf(startString, linePosition, StringComparison.Ordinal) + + startString.Length; + var endPosition = data.IndexOf(endString, startPosition, StringComparison.Ordinal); + outputData = data.Substring(startPosition, endPosition - startPosition).Trim(); + //Debug.Console(2, "ParseConsoleData Return: {0}", outputData); + } + catch (Exception e) + { + Debug.Console(1, "Error Parsing Console Data:\r{0}", e); + } + + return outputData; + } + } + } + + /// + /// Class for serializing program slot information + /// + public class ProgramInfo + { + // Shared properties + + [JsonProperty("programNumber")] + public uint ProgramNumber { get; private set; } + + [JsonConverter(typeof (StringEnumConverter))] + [JsonProperty("operatingState")] + public eProgramOperatingState OperatingState { get; set; } + + [JsonConverter(typeof (StringEnumConverter))] + [JsonProperty("registrationState")] + public eProgramRegistrationState RegistrationState { get; set; } + + [JsonProperty("programFile")] + public string ProgramFile { get; set; } + + [JsonProperty("friendlyName")] + public string FriendlyName { get; set; } + + [JsonProperty("compilerRevision")] + public string CompilerRevision { get; set; } + + [JsonProperty("compileTime")] + public string CompileTime { get; set; } + + [JsonProperty("include4Dat")] + public string Include4Dat { get; set; } + + // SIMPL Windows properties + [JsonProperty("systemName")] + public string SystemName { get; set; } + + [JsonProperty("crestronDb")] + public string CrestronDb { get; set; } + + [JsonProperty("environment")] + public string Environment { get; set; } + + [JsonProperty("programmer")] + public string Programmer { get; set; } + + + // SSP Properties + [JsonProperty("applicationName")] + public string ApplicationName { get; set; } + + [JsonProperty("programTool")] + public string ProgramTool { get; set; } + + [JsonProperty("minFirmwareVersion")] + public string MinFirmwareVersion { get; set; } + + [JsonProperty("plugInVersion")] + public string PlugInVersion { get; set; } + + public ProgramInfo(uint number) + { + ProgramNumber = number; + + ProgramFile = ""; + FriendlyName = ""; + CompilerRevision = ""; + CompileTime = ""; + Include4Dat = ""; + + SystemName = ""; + CrestronDb = ""; + Environment = ""; + Programmer = ""; + + ApplicationName = ""; + ProgramTool = ""; + MinFirmwareVersion = ""; + PlugInVersion = ""; + } + } + + public class ProgramInfoEventArgs : EventArgs + { + public ProgramInfo ProgramInfo; + + public ProgramInfoEventArgs(ProgramInfo progInfo) + { + ProgramInfo = progInfo; + } + } } \ No newline at end of file From 8ccbed6d812c55da7d4182794368624bfb6a87fe Mon Sep 17 00:00:00 2001 From: Maxim Batourine Date: Sat, 16 Jan 2021 22:57:45 -0500 Subject: [PATCH 76/89] Add hdbasettx device --- .../Endpoints/Transmitters/DmTxHelpers.cs | 10 +- .../Transmitters/HDBaseTTxController.cs | 113 ++++++++++++++++++ 2 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs index fd80ac1f..4d8e41f6 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/DmTxHelpers.cs @@ -122,7 +122,9 @@ namespace PepperDash.Essentials.DM return new DmTx4kz302CController(key, name, new DmTx4kz302C(chassis.Inputs[num])); if (typeName.StartsWith("dmtx401")) return new DmTx401CController(key, name, new DmTx401C(chassis.Inputs[num])); - } + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(chassis.Inputs[num])); + } else { if (typeName.StartsWith("dmtx200")) @@ -145,7 +147,9 @@ namespace PepperDash.Essentials.DM return new DmTx4kz302CController(key, name, new DmTx4kz302C(ipid, chassis.Inputs[num])); if (typeName.StartsWith("dmtx401")) return new DmTx401CController(key, name, new DmTx401C(ipid, chassis.Inputs[num])); - } + if (typeName.StartsWith("hdbasettx")) + return new HDBaseTTxController(key, name, new HDTx3CB(ipid, chassis.Inputs[num])); + } } catch (Exception e) { @@ -355,7 +359,7 @@ namespace PepperDash.Essentials.DM public DmTxControllerFactory() { TypeNames = new List() { "dmtx200c", "dmtx201c", "dmtx201s", "dmtx4k100c", "dmtx4k202c", "dmtx4kz202c", "dmtx4k302c", "dmtx4kz302c", - "dmtx401c", "dmtx401s", "dmtx4k100c1g", "dmtx4kz100c1g" }; + "dmtx401c", "dmtx401s", "dmtx4k100c1g", "dmtx4kz100c1g", "hdbasettx" }; } public override EssentialsDevice BuildDevice(DeviceConfig dc) diff --git a/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs new file mode 100644 index 00000000..800da2a9 --- /dev/null +++ b/essentials-framework/Essentials DM/Essentials_DM/Endpoints/Transmitters/HDBaseTTxController.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.DM.Endpoints.Transmitters; +using Newtonsoft.Json; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Bridges; + +namespace PepperDash.Essentials.DM +{ + /// + /// Controller class for suitable for HDBaseT transmitters + /// + [Description("Wrapper Class for HDBaseT devices based on HDTx3CB class")] + public class HDBaseTTxController: BasicDmTxControllerBase, IRoutingInputsOutputs, IComPorts + { + public RoutingInputPort HdmiIn { get; private set; } + public RoutingOutputPort DmOut { get; private set; } + + public HDBaseTTxController(string key, string name, HDTx3CB tx) + : base(key, name, tx) + { + HdmiIn = new RoutingInputPort(DmPortName.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.Hdmi, null, this) { Port = tx }; + + DmOut = new RoutingOutputPort(DmPortName.DmOut, eRoutingSignalType.Audio | eRoutingSignalType.Video, + eRoutingPortConnectionType.DmCat, null, this); + + InputPorts = new RoutingPortCollection { HdmiIn }; + OutputPorts = new RoutingPortCollection { DmOut }; + } + + #region IRoutingInputs Members + + public RoutingPortCollection InputPorts { get; private set; } + + #endregion + + #region IRoutingOutputs Members + + public RoutingPortCollection OutputPorts { get; private set; } + + #endregion + + #region IComPorts Members + + public CrestronCollection ComPorts { get { return (Hardware as HDTx3CB).ComPorts; } } + public int NumberOfComPorts { get { return (Hardware as HDTx3CB).NumberOfComPorts; } } + + #endregion + + #region CrestronBridgeableBaseDevice abstract overrides + + public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) + { + var joinMap = new HDBaseTTxControllerJoinMap(joinStart); + + var joinMapSerialized = JoinMapHelper.GetSerializedJoinMapForDevice(joinMapKey); + + if (!string.IsNullOrEmpty(joinMapSerialized)) + joinMap = JsonConvert.DeserializeObject(joinMapSerialized); + + + if (bridge != null) + { + bridge.AddJoinMap(Key, joinMap); + } + else + { + Debug.Console(0, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device."); + } + + Debug.Console(1, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X")); + + this.IsOnline.LinkInputSig(trilist.BooleanInput[joinMap.IsOnline.JoinNumber]); + + } + + #endregion + } + + public class HDBaseTTxControllerJoinMap : JoinMapBaseAdvanced + { + [JoinName("IsOnline")] + public JoinDataComplete IsOnline = new JoinDataComplete( + new JoinData + { + JoinNumber = 1, + JoinSpan = 1 + }, + new JoinMetadata + { + Description = "HDBaseT device online feedback", + JoinCapabilities = eJoinCapabilities.ToSIMPL, + JoinType = eJoinType.Digital + }); + + /// + /// Plugin device BridgeJoinMap constructor + /// + /// This will be the join it starts on the EISC bridge + public HDBaseTTxControllerJoinMap(uint joinStart) + : base(joinStart, typeof(HDBaseTTxControllerJoinMap)) + { + } + } +} \ No newline at end of file From 6d66c5adee5208fc410af8b81870f6dc18a1a9ae Mon Sep 17 00:00:00 2001 From: Maxim Batourine Date: Mon, 18 Jan 2021 13:16:19 -0500 Subject: [PATCH 77/89] add HDBaseTTxController to project file --- .../Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj index b1ea1b42..63b20c98 100644 --- a/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj +++ b/essentials-framework/Essentials DM/Essentials_DM/PepperDash_Essentials_DM.csproj @@ -108,6 +108,7 @@ + From 7945bce854c6643c9547ccd42580034887a767d5 Mon Sep 17 00:00:00 2001 From: jkdevito Date: Mon, 18 Jan 2021 15:37:39 -0600 Subject: [PATCH 78/89] Upadted VideoCodecBase LinkVideoCodecCallControlsToApi IncomingCall bridge action to reference "args.CallItem.Status == eCodecCallStatus.Ringing" (previously referencing "args.CallItem.Status != eCodecCallStatus.Disconnected") --- .../VideoCodec/VideoCodecBase.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) 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 6b6d5891..8765b0e1 100644 --- a/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs +++ b/essentials-framework/Essentials Devices Common/Essentials Devices Common/VideoCodec/VideoCodecBase.cs @@ -74,10 +74,10 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec { get { - return new List - { - PrivacyModeIsOnFeedback, - SharingSourceFeedback + return new List + { + PrivacyModeIsOnFeedback, + SharingSourceFeedback }; } } @@ -677,19 +677,19 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec var meetingIndex = 0; var tokenArray = new XSigToken[maxMeetings * offset]; - /* - * Digitals - * IsJoinable - 1 - * IsDialable - 2 - * - * Serials - * Organizer - 1 - * Title - 2 - * Start Date - 3 - * Start Time - 4 - * End Date - 5 - * End Time - 6 - * Id - 7 + /* + * Digitals + * IsJoinable - 1 + * IsDialable - 2 + * + * Serials + * Organizer - 1 + * Title - 2 + * Start Date - 3 + * Start Time - 4 + * End Date - 5 + * End Time - 6 + * Id - 7 */ @@ -848,7 +848,7 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec Debug.Console(1, this, "Call Direction: {0}", args.CallItem.Direction); Debug.Console(1, this, "Call is incoming: {0}", args.CallItem.Direction == eCodecCallDirection.Incoming); - trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status != eCodecCallStatus.Disconnected); + trilist.SetBool(joinMap.IncomingCall.JoinNumber, args.CallItem.Direction == eCodecCallDirection.Incoming && args.CallItem.Status == eCodecCallStatus.Ringing); if (args.CallItem.Direction == eCodecCallDirection.Incoming) { From 52494ca13e5bcc49f0c6446b145c0770ef64b2c4 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 21 Jan 2021 15:04:01 -0700 Subject: [PATCH 79/89] Changes to correct datatype for joinMaps in config --- .gitignore | 1 + .../PepperDashEssentialsBase/Config/BasicConfig.cs | 4 +++- .../Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs | 2 +- .../PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs | 8 ++++---- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 501c2f26..94b7d400 100644 --- a/.gitignore +++ b/.gitignore @@ -388,3 +388,4 @@ MigrationBackup/ # Fody - auto-generated XML schema FodyWeavers.xsd +essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs index 904bfc74..e76bbf9b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs @@ -7,6 +7,8 @@ using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Essentials.Core; +using Newtonsoft.Json.Linq; + namespace PepperDash.Essentials.Core.Config { /// @@ -27,7 +29,7 @@ namespace PepperDash.Essentials.Core.Config public List TieLines { get; set; } [JsonProperty("joinMaps")] - public Dictionary JoinMaps { get; set; } + public Dictionary JoinMaps { get; set; } /// /// Checks SourceLists for a given list and returns it if found. Otherwise, returns null diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs index 0b61fed2..33f2fe1b 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceRoomFusionRoomJoinMap.cs @@ -30,7 +30,7 @@ namespace PepperDash.Essentials.Core.Fusion new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorDomain")] - public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - Domain" }, + public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = "Info - Processor - Domain" }, new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial }); [JoinName("ProcessorDns1")] diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 4fe2d537..52d56dfd 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Core var joinMap = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; - return joinMap; + return joinMap.ToString(); } /// @@ -48,11 +48,11 @@ namespace PepperDash.Essentials.Core if (string.IsNullOrEmpty(joinMapKey)) return null; - var joinMapSerialzed = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; + var joinMapJToken = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; - if (joinMapSerialzed == null) return null; + if (joinMapJToken == null) return null; - var joinMapData = JsonConvert.DeserializeObject>(joinMapSerialzed); + var joinMapData = joinMapJToken.ToObject>(); return joinMapData; } From 9d354fb0ed821a75ff4e514a06bb4bfab207b1f0 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 21 Jan 2021 16:46:21 -0700 Subject: [PATCH 80/89] custom fusion joinmap working. updates to hotfix PD.Core build --- PepperDashEssentials/ControlSystem.cs | 15 +- .../EssentialsHuddleVtc1FusionController.cs | 20 +- .../Config/BasicConfig.cs | 2 +- .../Config/Essentials/ConfigReader.cs | 360 +++++++++--------- ...lsHuddleSpaceFusionSystemControllerBase.cs | 49 +-- .../JoinMaps/JoinMapBase.cs | 37 +- packages.config | 2 +- 7 files changed, 255 insertions(+), 230 deletions(-) diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 9bec571e..7ffb57b5 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -438,12 +438,17 @@ namespace PepperDash.Essentials // default to no join map key string fusionJoinMapKey = string.Empty; - var fusionConfig = room.Config.Properties["fusion"].ToObject(); - - if (fusionConfig != null) + if (room.Config.Properties["fusion"] != null) { - fusionIpId = fusionConfig.IpIdInt; - fusionJoinMapKey = fusionConfig.JoinMapKey; + Debug.Console(2, "Custom Fusion config found. Using custom values"); + + var fusionConfig = room.Config.Properties["fusion"].ToObject(); + + if (fusionConfig != null) + { + fusionIpId = fusionConfig.IpIdInt; + fusionJoinMapKey = fusionConfig.JoinMapKey; + } } if (room is EssentialsHuddleSpaceRoom) diff --git a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs index b071b681..c4a68d4e 100644 --- a/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -55,25 +55,25 @@ namespace PepperDash.Essentials.Fusion // Map FusionRoom Attributes: // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.Metadata.Description, eSigIoMask.InputOutputSig); + var codecVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.AttributeName, eSigIoMask.InputOutputSig); codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, JoinMap.VcCodecInCall.Metadata.Description, eSigIoMask.InputSigOnly); + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecInCall.JoinNumber, JoinMap.VcCodecInCall.AttributeName, eSigIoMask.InputSigOnly); codec.CallStatusChange += new EventHandler(codec_CallStatusChange); // Online status if (codec is ICommunicationMonitor) { var c = codec as ICommunicationMonitor; - var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, JoinMap.VcCodecOnline.Metadata.Description, eSigIoMask.InputSigOnly); + var codecOnline = FusionRoom.CreateOffsetBoolSig(JoinMap.VcCodecOnline.JoinNumber, JoinMap.VcCodecOnline.AttributeName, eSigIoMask.InputSigOnly); codecOnline.InputSig.BoolValue = c.CommunicationMonitor.Status == MonitorStatus.IsOk; c.CommunicationMonitor.StatusChange += (o, a) => { codecOnline.InputSig.BoolValue = a.Status == MonitorStatus.IsOk; }; - Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, JoinMap.VcCodecOnline.Metadata.Description); + Debug.Console(0, this, "Linking '{0}' communication monitor to Fusion '{1}'", codec.Key, JoinMap.VcCodecOnline.AttributeName); } // Codec IP Address @@ -101,10 +101,10 @@ namespace PepperDash.Essentials.Fusion if (codecHasIpInfo) { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, JoinMap.VcCodecIpAddress.Metadata.Description, eSigIoMask.InputSigOnly); + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpAddress.JoinNumber, JoinMap.VcCodecIpAddress.AttributeName, eSigIoMask.InputSigOnly); codecIpAddressSig.InputSig.StringValue = codecIpAddress; - codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, JoinMap.VcCodecIpPort.Metadata.Description, eSigIoMask.InputSigOnly); + codecIpPortSig = FusionRoom.CreateOffsetStringSig(JoinMap.VcCodecIpPort.JoinNumber, JoinMap.VcCodecIpPort.AttributeName, eSigIoMask.InputSigOnly); codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); } @@ -172,7 +172,7 @@ namespace PepperDash.Essentials.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.Metadata.Description, eSigIoMask.InputSigOnly); + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. (Room as EssentialsHuddleVtc1Room).CurrentSourceChange += Room_CurrentSourceInfoChange; @@ -196,7 +196,7 @@ namespace PepperDash.Essentials.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots break; @@ -206,7 +206,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > 5) // We only have five spots break; @@ -216,7 +216,7 @@ namespace PepperDash.Essentials.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.Metadata.Description + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? break; diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs index e76bbf9b..7cbaa5a1 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/BasicConfig.cs @@ -29,7 +29,7 @@ namespace PepperDash.Essentials.Core.Config public List TieLines { get; set; } [JsonProperty("joinMaps")] - public Dictionary JoinMaps { get; set; } + public Dictionary JoinMaps { get; set; } /// /// Checks SourceLists for a given list and returns it if found. Otherwise, returns null diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs index 48a026c2..e99206e8 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Config/Essentials/ConfigReader.cs @@ -1,195 +1,195 @@ -using System; +using System; using System.Linq; using System.Text; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using PepperDash.Core; -using PepperDash.Core.Config; - -namespace PepperDash.Essentials.Core.Config -{ - /// - /// Loads the ConfigObject from the file - /// +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core; +using PepperDash.Core.Config; + +namespace PepperDash.Essentials.Core.Config +{ + /// + /// Loads the ConfigObject from the file + /// public class ConfigReader { public const string LocalConfigPresent = @" *************************************************** ************* Using Local config file ************* -***************************************************"; - - public static EssentialsConfig ConfigObject { get; private set; } - - public static bool LoadConfig2() - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file."); - try - { - // Check for local config file first - var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; - - bool localConfigFound = false; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Local config file: '{0}'", filePath); - - // Check for local config directory first - - var configFiles = GetConfigFiles(filePath); - - if (configFiles != null) - { - if (configFiles.Length > 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); - return false; - } - if(configFiles.Length == 1) - { +***************************************************"; + + public static EssentialsConfig ConfigObject { get; private set; } + + public static bool LoadConfig2() + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading unmerged system/template portal configuration file."); + try + { + // Check for local config file first + var filePath = Global.FilePathPrefix + ConfigWriter.LocalConfigFolder + Global.DirectorySeparator + Global.ConfigFileName; + + bool localConfigFound = false; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Local config file: '{0}'", filePath); + + // Check for local config directory first + + var configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Local Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + if(configFiles.Length == 1) + { localConfigFound = true; - - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Local Configuration file not present.", filePath); - - } - - // Check for Portal Config - if(!localConfigFound) - { - filePath = Global.FilePathPrefix + Global.ConfigFileName; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Portal config file: '{0}'", filePath); - - configFiles = GetConfigFiles(filePath); - - if (configFiles != null) - { - Debug.Console(2, "{0} config files found matching pattern", configFiles.Length); - - if (configFiles.Length > 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****"); - return false; - } - else if (configFiles.Length == 1) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "No config file found."); - return false; - } - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Error, - "ERROR: Portal Configuration file not present. Please load file and reset program."); - return false; - } - } - - // Get the actual file path + + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Local Configuration file not present.", filePath); + + } + + // Check for Portal Config + if(!localConfigFound) + { + filePath = Global.FilePathPrefix + Global.ConfigFileName; + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to load Portal config file: '{0}'", filePath); + + configFiles = GetConfigFiles(filePath); + + if (configFiles != null) + { + Debug.Console(2, "{0} config files found matching pattern", configFiles.Length); + + if (configFiles.Length > 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "****Error: Multiple Portal Configuration files present. Please ensure only a single file exists and reset program.****"); + return false; + } + else if (configFiles.Length == 1) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Found Portal config file: '{0}'", filePath); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "No config file found."); + return false; + } + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Error, + "ERROR: Portal Configuration file not present. Please load file and reset program."); + return false; + } + } + + // Get the actual file path filePath = configFiles[0].FullName; // Generate debug statement if using a local file. if (localConfigFound) { GetLocalFileMessage(filePath); - } - - // Read the file - using (StreamReader fs = new StreamReader(filePath)) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading config file: '{0}'", filePath); - - if (localConfigFound) - { - ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject(); - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Local Config"); - - return true; - } - else - { - var doubleObj = JObject.Parse(fs.ReadToEnd()); - ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); - - // Extract SystemUrl and TemplateUrl into final config output - - if (doubleObj["system_url"] != null) - { - ConfigObject.SystemUrl = doubleObj["system_url"].Value(); - } - - if (doubleObj["template_url"] != null) - { - ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); - } - } - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config"); - - return true; - } - } - catch (Exception e) - { - Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e); - return false; - } - } - - /// - /// Returns all the files from the directory specified. - /// - /// - /// - public static FileInfo[] GetConfigFiles(string filePath) - { - // Get the directory - var dir = Path.GetDirectoryName(filePath); - - if (Directory.Exists(dir)) - { - Debug.Console(1, "Searching in Directory '{0}'", dir); - // Get the directory info - var dirInfo = new DirectoryInfo(dir); - - // Get the file name - var fileName = Path.GetFileName(filePath); - Debug.Console(1, "For Config Files matching: '{0}'", fileName); - - // Get the files that match from the directory - return dirInfo.GetFiles(fileName); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, - "Directory not found: ", dir); - - return null; - } - } - - /// - /// Returns the group for a given device key in config - /// - /// - /// - public static string GetGroupForDeviceKey(string key) - { - var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); - return dev == null ? null : dev.Group; + } + + // Read the file + using (StreamReader fs = new StreamReader(filePath)) + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Loading config file: '{0}'", filePath); + + if (localConfigFound) + { + ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject(); + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Local Config"); + + return true; + } + else + { + var doubleObj = JObject.Parse(fs.ReadToEnd()); + ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject(); + + // Extract SystemUrl and TemplateUrl into final config output + + if (doubleObj["system_url"] != null) + { + ConfigObject.SystemUrl = doubleObj["system_url"].Value(); + } + + if (doubleObj["template_url"] != null) + { + ConfigObject.TemplateUrl = doubleObj["template_url"].Value(); + } + } + + Debug.Console(0, Debug.ErrorLogLevel.Notice, "Successfully Loaded Merged Config"); + + return true; + } + } + catch (Exception e) + { + Debug.Console(0, Debug.ErrorLogLevel.Error, "ERROR: Config load failed: \r{0}", e); + return false; + } + } + + /// + /// Returns all the files from the directory specified. + /// + /// + /// + public static FileInfo[] GetConfigFiles(string filePath) + { + // Get the directory + var dir = Path.GetDirectoryName(filePath); + + if (Directory.Exists(dir)) + { + Debug.Console(1, "Searching in Directory '{0}'", dir); + // Get the directory info + var dirInfo = new DirectoryInfo(dir); + + // Get the file name + var fileName = Path.GetFileName(filePath); + Debug.Console(1, "For Config Files matching: '{0}'", fileName); + + // Get the files that match from the directory + return dirInfo.GetFiles(fileName); + } + else + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, + "Directory not found: ", dir); + + return null; + } + } + + /// + /// Returns the group for a given device key in config + /// + /// + /// + public static string GetGroupForDeviceKey(string key) + { + var dev = ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(key, StringComparison.OrdinalIgnoreCase)); + return dev == null ? null : dev.Group; } private static void GetLocalFileMessage(string filePath) @@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core.Config Debug.Console(2, Debug.ErrorLogLevel.Notice, "Found Local config file: '{0}'", filePath); Debug.Console(0, newDebugString.ToString()); - } - - } + } + + } } \ No newline at end of file diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs index 03e1c090..9574743e 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Fusion/EssentialsHuddleSpaceFusionSystemControllerBase.cs @@ -98,7 +98,10 @@ namespace PepperDash.Essentials.Core.Fusion if (!string.IsNullOrEmpty(joinMapKey)) { var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); - JoinMap.SetCustomJoinData(customJoins); + if (customJoins != null) + { + JoinMap.SetCustomJoinData(customJoins); + } } Room = room; @@ -339,7 +342,7 @@ namespace PepperDash.Essentials.Core.Fusion Room.OnFeedback.LinkInputSig(FusionRoom.SystemPowerOn.InputSig); // Moved to - CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.Metadata.Description, + CurrentRoomSourceNameSig = FusionRoom.CreateOffsetStringSig(JoinMap.Display1CurrentSourceName.JoinNumber, JoinMap.Display1CurrentSourceName.AttributeName, eSigIoMask.InputSigOnly); // Don't think we need to get current status of this as nothing should be alive yet. var hasCurrentSourceInfoChange = Room as IHasCurrentSourceInfoChange; @@ -389,24 +392,24 @@ namespace PepperDash.Essentials.Core.Fusion var response = string.Empty; - var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, JoinMap.ProcessorReboot.Metadata.Description, eSigIoMask.OutputSigOnly); + var systemReboot = FusionRoom.CreateOffsetBoolSig(JoinMap.ProcessorReboot.JoinNumber, JoinMap.ProcessorReboot.AttributeName, eSigIoMask.OutputSigOnly); systemReboot.OutputSig.SetSigFalseAction( () => CrestronConsole.SendControlSystemCommand("reboot", ref response)); } protected void SetUpEthernetValues() { - _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.Metadata.Description, eSigIoMask.InputSigOnly); - _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, JoinMap.ProcessorIp2.Metadata.Description, eSigIoMask.InputSigOnly); - _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, JoinMap.ProcessorGateway.Metadata.Description, eSigIoMask.InputSigOnly); - _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, JoinMap.ProcessorHostname.Metadata.Description, eSigIoMask.InputSigOnly); - _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, JoinMap.ProcessorDomain.Metadata.Description, eSigIoMask.InputSigOnly); - _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, JoinMap.ProcessorDns1.Metadata.Description, eSigIoMask.InputSigOnly); - _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, JoinMap.ProcessorDns2.Metadata.Description, eSigIoMask.InputSigOnly); - _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, JoinMap.ProcessorMac1.Metadata.Description, eSigIoMask.InputSigOnly); - _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, JoinMap.ProcessorMac2.Metadata.Description, eSigIoMask.InputSigOnly); - _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, JoinMap.ProcessorNetMask1.Metadata.Description, eSigIoMask.InputSigOnly); - _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.Metadata.Description, eSigIoMask.InputSigOnly); + _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly); + _ip2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp2.JoinNumber, JoinMap.ProcessorIp2.AttributeName, eSigIoMask.InputSigOnly); + _gateway = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorGateway.JoinNumber, JoinMap.ProcessorGateway.AttributeName, eSigIoMask.InputSigOnly); + _hostname = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorHostname.JoinNumber, JoinMap.ProcessorHostname.AttributeName, eSigIoMask.InputSigOnly); + _domain = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDomain.JoinNumber, JoinMap.ProcessorDomain.AttributeName, eSigIoMask.InputSigOnly); + _dns1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns1.JoinNumber, JoinMap.ProcessorDns1.AttributeName, eSigIoMask.InputSigOnly); + _dns2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorDns2.JoinNumber, JoinMap.ProcessorDns2.AttributeName, eSigIoMask.InputSigOnly); + _mac1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac1.JoinNumber, JoinMap.ProcessorMac1.AttributeName, eSigIoMask.InputSigOnly); + _mac2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorMac2.JoinNumber, JoinMap.ProcessorMac2.AttributeName, eSigIoMask.InputSigOnly); + _netMask1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask1.JoinNumber, JoinMap.ProcessorNetMask1.AttributeName, eSigIoMask.InputSigOnly); + _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly); } protected void GetProcessorEthernetValues() @@ -459,7 +462,7 @@ namespace PepperDash.Essentials.Core.Fusion protected void GetProcessorInfo() { - _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.Metadata.Description, eSigIoMask.InputSigOnly); + _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly); if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) { @@ -468,7 +471,7 @@ namespace PepperDash.Essentials.Core.Fusion var join = JoinMap.ProgramNameStart.JoinNumber + i; var progNum = i + 1; _program[i] = FusionRoom.CreateOffsetStringSig((uint) join, - string.Format("{0} {1}", JoinMap.ProgramNameStart.Metadata.Description, progNum), eSigIoMask.InputSigOnly); + string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly); } } @@ -1046,7 +1049,7 @@ namespace PepperDash.Essentials.Core.Fusion uint i = 1; foreach (var kvp in setTopBoxes) { - TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.Metadata.Description + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots { @@ -1058,7 +1061,7 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in discPlayers) { - TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.Metadata.Description + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots { @@ -1070,7 +1073,7 @@ namespace PepperDash.Essentials.Core.Fusion i = 1; foreach (var kvp in laptops) { - TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.Metadata.Description + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); + TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice); i++; if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? { @@ -1199,7 +1202,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = JoinMap.XpanelOnlineStart.Metadata.Description + " " + attrNum; + attrName = JoinMap.XpanelOnlineStart.AttributeName + " " + attrNum; attrNum += JoinMap.XpanelOnlineStart.JoinNumber; touchpanelNum++; @@ -1212,7 +1215,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = JoinMap.TouchpanelOnlineStart.Metadata.Description + " " + attrNum; + attrName = JoinMap.TouchpanelOnlineStart.AttributeName + " " + attrNum; attrNum += JoinMap.TouchpanelOnlineStart.JoinNumber; xpanelNum++; @@ -1227,7 +1230,7 @@ namespace PepperDash.Essentials.Core.Fusion { continue; } - attrName = JoinMap.DisplayOnlineStart.Metadata.Description + " " + attrNum; + attrName = JoinMap.DisplayOnlineStart.AttributeName + " " + attrNum; attrNum += JoinMap.DisplayOnlineStart.JoinNumber; displayNum++; @@ -1372,7 +1375,7 @@ namespace PepperDash.Essentials.Core.Fusion return; } // Display volume - var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.Metadata.Description, + var defaultDisplayVolume = FusionRoom.CreateOffsetUshortSig(JoinMap.VolumeFader1.JoinNumber, JoinMap.VolumeFader1.AttributeName, eSigIoMask.InputOutputSig); defaultDisplayVolume.OutputSig.UserObject = new Action(b => { diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs index 52d56dfd..b9825749 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/JoinMaps/JoinMapBase.cs @@ -45,16 +45,33 @@ namespace PepperDash.Essentials.Core /// public static Dictionary TryGetJoinMapAdvancedForDevice(string joinMapKey) { - if (string.IsNullOrEmpty(joinMapKey)) + try + { + if (string.IsNullOrEmpty(joinMapKey)) + return null; + + if (!ConfigReader.ConfigObject.JoinMaps.ContainsKey(joinMapKey)) + { + Debug.Console(2, "No Join Map found in config with key: '{0}'", joinMapKey); + return null; + } + + Debug.Console(2, "Attempting to load custom join map with key: {0}", joinMapKey); + + var joinMapJToken = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; + + if (joinMapJToken == null) + return null; + + var joinMapData = joinMapJToken.ToObject>(); + + return joinMapData; + } + catch (Exception e) + { + Debug.Console(2, "Error getting join map for key: '{0}'. Error: {1}", joinMapKey, e); return null; - - var joinMapJToken = ConfigReader.ConfigObject.JoinMaps[joinMapKey]; - - if (joinMapJToken == null) return null; - - var joinMapData = joinMapJToken.ToObject>(); - - return joinMapData; + } } } @@ -266,7 +283,7 @@ namespace PepperDash.Essentials.Core @"Join Number: {0} | JoinSpan: '{1}' | Description: '{2}' | Type: '{3}' | Capabilities: '{4}'", join.Value.JoinNumber, join.Value.JoinSpan, - String.IsNullOrEmpty(join.Value.Metadata.Description) ? join.Value.Metadata.Label : join.Value.Metadata.Description, + String.IsNullOrEmpty(join.Value.AttributeName) ? join.Value.Metadata.Label : join.Value.AttributeName, join.Value.Metadata.JoinType.ToString(), join.Value.Metadata.JoinCapabilities.ToString()); } diff --git a/packages.config b/packages.config index 67789ca5..c7d374b3 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file From 2a5810b671c7c2ce6b56d24e2717588bb84c6abe Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 21 Jan 2021 17:01:43 -0700 Subject: [PATCH 81/89] Updates PD.Core to 1.0.45 --- packages.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.config b/packages.config index c7d374b3..b60e8578 100644 --- a/packages.config +++ b/packages.config @@ -1,3 +1,3 @@ - + \ No newline at end of file From dfc90e58dc3d91b9ea21326149818ef87d39902f Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 21 Jan 2021 17:19:01 -0700 Subject: [PATCH 82/89] Adds EssentialsTechRoomFusionSystemController --- PepperDashEssentials/ControlSystem.cs | 4 +- ...ssentialsTechRoomFusionSystemController.cs | 92 +++++++++++++++++++ .../PepperDashEssentials.csproj | 1 + 3 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 7ffb57b5..74f3c617 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -480,7 +480,7 @@ namespace PepperDash.Essentials Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is EssentialsTechRoom, Attempting to add to DeviceManager with Fusion"); - DeviceManager.AddDevice(new EssentialsHuddleSpaceFusionSystemControllerBase(room, fusionIpId, fusionJoinMapKey)); + DeviceManager.AddDevice(new EssentialsTechRoomFusionSystemController((EssentialsTechRoom)room, fusionIpId, fusionJoinMapKey)); Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Mobile Control Bridge"); @@ -588,7 +588,7 @@ namespace PepperDash.Essentials return ((logoDark != null && logoDark == "system") || (logoLight != null && logoLight == "system") || (logo != null && logo == "system")); } - catch (Exception e) + catch { Debug.Console(1, Debug.ErrorLogLevel.Notice, "Unable to find logo information in any room config"); return false; diff --git a/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs new file mode 100644 index 00000000..a069dbd4 --- /dev/null +++ b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.Fusion; + +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsTechRoomFusionSystemController : EssentialsHuddleSpaceFusionSystemControllerBase + { + public EssentialsTechRoomFusionSystemController(EssentialsTechRoom room, uint ipId, string joinMapKey) + : base(room, ipId, joinMapKey) + { + + } + + protected override void SetUpDisplay() + { + try + { + var displays = (Room as EssentialsTechRoom).Displays; + + foreach (var display in displays.Cast()) + { + display.UsageTracker = new UsageTracking(display) { UsageIsTracked = true }; + display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + + var dispPowerOnAction = new Action(b => + { + if (!b) + { + display.PowerOn(); + } + }); + var dispPowerOffAction = new Action(b => + { + if (!b) + { + display.PowerOff(); + } + }); + + var deviceConfig = ConfigReader.ConfigObject.GetDeviceForKey(display.Key); + + FusionAsset tempAsset; + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), + display.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", + tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + + var defaultTwoWayDisplay = display as IHasPowerControlWithFeedback; + if (defaultTwoWayDisplay != null) + { + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (display is IDisplayUsage) + { + (display as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + } + + defaultTwoWayDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + } + + // Use extension methods + dispAsset.TrySetMakeModel(display); + dispAsset.TryLinkAssetErrorToCommunication(display); + } + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up displays in Fusion: {0}", e); + } + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index 969c1f40..b7df4b0d 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -133,6 +133,7 @@ + From c9fee785f931a090418f91b4422a1a569588d060 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 22 Jan 2021 11:08:35 -0700 Subject: [PATCH 83/89] Static Assets for displays now tested and working --- .../Fusion/EssentialsTechRoomFusionSystemController.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs index a069dbd4..7e465b4d 100644 --- a/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs +++ b/PepperDashEssentials/Fusion/EssentialsTechRoomFusionSystemController.cs @@ -23,10 +23,15 @@ namespace PepperDash.Essentials.Fusion { try { + var displays = (Room as EssentialsTechRoom).Displays; - foreach (var display in displays.Cast()) + Debug.Console(1, this, "Setting up Static Assets for {0} Displays", displays.Count); + + foreach (var display in displays.Values.Cast()) { + Debug.Console(2, this, "Setting up Static Asset for {0}", display.Key); + display.UsageTracker = new UsageTracking(display) { UsageIsTracked = true }; display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; @@ -51,6 +56,7 @@ namespace PepperDash.Essentials.Fusion if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) { + // Used existing asset tempAsset = FusionStaticAssets[deviceConfig.Uid]; } else From 65747b6ad296b57e639b5f58d793ca931e0cb544 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 25 Jan 2021 10:55:27 -0700 Subject: [PATCH 84/89] #560 Adds constructors with priority and fixed debug statments to use error log --- .../Queues/GenericQueue.cs | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index 1f27fe1e..05757213 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -15,23 +15,36 @@ namespace PepperDash_Essentials_Core.Queues protected readonly Thread _worker; protected readonly CEvent _waitHandle = new CEvent(); - private readonly bool _delayEnabled; - private readonly int _delayTime; + private bool _delayEnabled; + private int _delayTime; /// /// If the instance has been disposed. /// public bool Disposed { get; private set; } + /// + /// Constructor with no thread priority + /// + /// + public GenericQueue(string key) + : this(key, Thread.eThreadPriority.NotSet) + { + + } + /// /// Constructor for generic queue with no pacing /// /// Key - public GenericQueue(string key) + public GenericQueue(string key, Thread.eThreadPriority priority) { _key = key; _queue = new CrestronQueue(); - _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running); + _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running) + { + Priority = priority + }; CrestronEnvironment.ProgramStatusEventHandler += programEvent => { @@ -49,11 +62,28 @@ namespace PepperDash_Essentials_Core.Queues /// Pacing in ms between actions public GenericQueue(string key, int pacing) : this(key) + { + SetDelayValues(pacing); + } + + /// + /// Constructor with pacing and priority + /// + /// + /// + /// + public GenericQueue(string key, int pacing, Thread.eThreadPriority priority) + : this(key, priority) + { + SetDelayValues(pacing); + } + + private void SetDelayValues(int pacing) { _delayEnabled = pacing > 0; _delayTime = pacing; } - + /// /// Thread callback /// @@ -83,7 +113,7 @@ namespace PepperDash_Essentials_Core.Queues } catch (Exception ex) { - Debug.ConsoleWithLog(0, this, "Caught an exception in the Queue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace); + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Caught an exception in the Queue {0}\r{1}\r{2}", ex.Message, ex.InnerException, ex.StackTrace); } } else _waitHandle.Wait(); From 42c483f581ce5d1d09102455b0022a317e98f0a9 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 25 Jan 2021 17:56:20 -0700 Subject: [PATCH 85/89] Updates to properly clear errors when there are none and set message to "Room Ok." --- .../Monitoring/StatusMonitorCollection.cs | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs index d9b5b33a..7f985fe0 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Monitoring/StatusMonitorCollection.cs @@ -64,30 +64,34 @@ namespace PepperDash.Essentials.Core initialStatus = MonitorStatus.InWarning; prefix = "2:"; } - else if (InWarning.Count() > 0) + else if (IsOk.Count() > 0) initialStatus = MonitorStatus.IsOk; else initialStatus = MonitorStatus.StatusUnknown; // Build the error message string - if (InError.Count() > 0 || InWarning.Count() > 0) - { - StringBuilder sb = new StringBuilder(prefix); - if (InError.Count() > 0) - { - // Do string splits and joins - sb.Append(string.Format("{0} Errors:", InError.Count())); - foreach (var mon in InError) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - if (InWarning.Count() > 0) - { - sb.Append(string.Format("{0} Warnings:", InWarning.Count())); - foreach (var mon in InWarning) - sb.Append(string.Format("{0}, ", mon.Parent.Key)); - } - Message = sb.ToString(); - } + if (InError.Count() > 0 || InWarning.Count() > 0) + { + StringBuilder sb = new StringBuilder(prefix); + if (InError.Count() > 0) + { + // Do string splits and joins + sb.Append(string.Format("{0} Errors:", InError.Count())); + foreach (var mon in InError) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + if (InWarning.Count() > 0) + { + sb.Append(string.Format("{0} Warnings:", InWarning.Count())); + foreach (var mon in InWarning) + sb.Append(string.Format("{0}, ", mon.Parent.Key)); + } + Message = sb.ToString(); + } + else + { + Message = "Room Ok."; + } // Want to fire even if status doesn't change because the message may. Status = initialStatus; From 24e59e147471db3f154499cf6578328379fd4828 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Tue, 26 Jan 2021 11:05:42 -0700 Subject: [PATCH 86/89] Update PepperDash_Essentials_Core.nuspec --- .../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 dff167ed..e736dcbf 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/PepperDash_Essentials_Core.nuspec @@ -14,7 +14,7 @@ crestron 3series 4series - + From 809639c3f90d30e90865bd4641186a83bb76018d Mon Sep 17 00:00:00 2001 From: Nick Genovese Date: Tue, 26 Jan 2021 13:17:55 -0500 Subject: [PATCH 87/89] Validate method now checks the parameter --- .../PepperDashEssentialsBase/Queues/ComsMessage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs index 92c97248..fce6291d 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/ComsMessage.cs @@ -40,7 +40,7 @@ namespace PepperDash_Essentials_Core.Queues private void Validate(IBasicCommunication coms, object message) { - if (_coms == null) + if (coms == null) throw new ArgumentNullException("coms"); if (message == null) From b7d719607169081b4762f23275e21037283e2ef3 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 26 Jan 2021 12:32:04 -0700 Subject: [PATCH 88/89] Sets the queue size to 25 --- .../PepperDashEssentialsBase/Queues/GenericQueue.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index 05757213..5ca8a793 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -40,7 +40,7 @@ namespace PepperDash_Essentials_Core.Queues public GenericQueue(string key, Thread.eThreadPriority priority) { _key = key; - _queue = new CrestronQueue(); + _queue = new CrestronQueue(25); _worker = new Thread(ProcessQueue, null, Thread.eThreadStartOptions.Running) { Priority = priority From 57641493063023b9fb85a0dbd7a109824145d2c2 Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Wed, 27 Jan 2021 10:48:23 -0700 Subject: [PATCH 89/89] #560 set default queue thread priority to medium --- .../PepperDashEssentialsBase/Queues/GenericQueue.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs index 5ca8a793..b4c6befa 100644 --- a/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs +++ b/essentials-framework/Essentials Core/PepperDashEssentialsBase/Queues/GenericQueue.cs @@ -28,7 +28,7 @@ namespace PepperDash_Essentials_Core.Queues /// /// public GenericQueue(string key) - : this(key, Thread.eThreadPriority.NotSet) + : this(key, Thread.eThreadPriority.MediumPriority) { } @@ -37,6 +37,7 @@ namespace PepperDash_Essentials_Core.Queues /// Constructor for generic queue with no pacing /// /// Key + /// public GenericQueue(string key, Thread.eThreadPriority priority) { _key = key;