diff --git a/PepperDashEssentials/AppServer/CotijaSystemController.cs b/PepperDashEssentials/AppServer/CotijaSystemController.cs index 70beec0e..00f3ef5b 100644 --- a/PepperDashEssentials/AppServer/CotijaSystemController.cs +++ b/PepperDashEssentials/AppServer/CotijaSystemController.cs @@ -15,7 +15,6 @@ using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Room.Cotija; namespace PepperDash.Essentials @@ -24,6 +23,8 @@ namespace PepperDash.Essentials { WebSocketClient WSClient; + //bool LinkUp; + /// /// Prevents post operations from stomping on each other and getting lost /// @@ -83,12 +84,33 @@ namespace PepperDash.Essentials CrestronConsole.AddNewConsoleCommand(TestHttpRequest, "mobilehttprequest", "Tests an HTTP get to URL given", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "showactionpaths", "Prints the paths in teh Action Dictionary", ConsoleAccessLevelEnum.AccessOperator); - + CrestronConsole.AddNewConsoleCommand(PrintActionDictionaryPaths, "mobileshowactionpaths", + "Prints the paths in the Action Dictionary", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => ConnectWebsocketClient(), "mobileconnect", + "Forces connect of websocket", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CleanUpWebsocketClient(), "mobiledisco", + "Disconnects websocket", ConsoleAccessLevelEnum.AccessOperator); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); + } + /// + /// + /// + /// + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs args) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Ethernet status change, port {0}: {1}", + args.EthernetAdapter, args.EthernetEventType); + + if (args.EthernetEventType == eEthernetEventType.LinkDown && WSClient != null && args.EthernetAdapter == WSClient.EthernetAdapter) + { + CleanUpWebsocketClient(); + } + } + /// /// Sends message to server to indicate the system is shutting down /// @@ -97,11 +119,7 @@ namespace PepperDash.Essentials { if (programEventType == eProgramStatusEventType.Stopping && WSClient.Connected) { - SendMessageToServer(JObject.FromObject( new - { - type = "/system/close" - })); - + CleanUpWebsocketClient(); } } @@ -158,6 +176,7 @@ namespace PepperDash.Essentials else { Debug.Console(0, this, "Adding room bridge and sending configuration"); + SystemUuid = ConfigReader.ConfigObject.SystemUuid; RegisterSystemToServer(); } } @@ -170,6 +189,7 @@ namespace PepperDash.Essentials void bridge_ConfigurationIsReady(object sender, EventArgs e) { Debug.Console(1, this, "Bridge ready. Registering"); + SystemUuid = ConfigReader.ConfigObject.SystemUuid; // send the configuration object to the server RegisterSystemToServer(); } @@ -189,6 +209,8 @@ namespace PepperDash.Essentials /// void AuthorizeSystem(string code) { + SystemUuid = ConfigReader.ConfigObject.SystemUuid; + if (string.IsNullOrEmpty(SystemUuid)) { CrestronConsole.ConsoleCommandResponse("System does not have a UUID. Please ensure proper portal-format configuration is loaded and restart."); @@ -221,6 +243,7 @@ namespace PepperDash.Essentials if (r.Code == 200) { Debug.Console(0, "System authorized, sending config."); +#warning This registration may need to wait for config ready. Maybe. RegisterSystemToServer(); } else if (r.Code == 404) @@ -281,58 +304,104 @@ namespace PepperDash.Essentials /// URL of the server, including the port number, if not 80. Format: "serverUrlOrIp:port" void RegisterSystemToServer() { - - - var ready = RegisterLockEvent.Wait(20000); - if (!ready) - { - Debug.Console(1, this, "RegisterSystemToServer failed to enter after 20 seconds. Ignoring"); - return; - } - RegisterLockEvent.Reset(); - - try - { - var confObject = ConfigReader.ConfigObject; - confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; - var version = Assembly.GetExecutingAssembly().GetName().Version; - confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - - string postBody = JsonConvert.SerializeObject(confObject); - SystemUuid = confObject.SystemUuid; - - if (string.IsNullOrEmpty(postBody)) - { - Debug.Console(1, this, "ERROR: Config body is empty. Cannot register with server."); - } - else - { - var regClient = new HttpClient(); - regClient.Verbose = true; - regClient.KeepAlive = true; - - string url = string.Format("http://{0}/api/system/join/{1}", Config.ServerUrl, SystemUuid); - Debug.Console(1, this, "Joining server at {0}", url); - - HttpClientRequest request = new HttpClientRequest(); - request.Url.Parse(url); - request.RequestType = RequestType.Post; - request.Header.SetHeaderValue("Content-Type", "application/json"); - request.ContentString = postBody; - - var err = regClient.DispatchAsync(request, RegistrationConnectionCallback); - } - - } - catch (Exception e) - { - Debug.Console(0, this, "ERROR: Initilizing Room: {0}", e); - RegisterLockEvent.Set(); - StartReconnectTimer(); - } - + ConnectWebsocketClient(); } + /// + /// Connects the Websocket Client + /// + /// + void ConnectWebsocketClient() + { + + + Debug.Console(1, this, "Initializing Stream client to server."); + + if (WSClient != null) + { + Debug.Console(1, this, "Cleaning up previous socket"); + CleanUpWebsocketClient(); + } + + WSClient = new WebSocketClient(); + WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid); + WSClient.ConnectionCallBack = Websocket_ConnectCallback; + WSClient.ConnectAsync(); + } + + /// + /// + /// + /// + /// + int Websocket_ConnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code) + { + if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS) + { + StopServerReconnectTimer(); + Debug.Console(1, this, "Websocket connected"); + WSClient.DisconnectCallBack = Websocket_DisconnectCallback; + WSClient.SendCallBack = Websocket_SendCallback; + WSClient.ReceiveCallBack = Websocket_ReceiveCallback; + WSClient.ReceiveAsync(); + SendMessageObjectToServer(new + { + type = "hello" + }); + } + else + { + if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_HTTP_HANDSHAKE_TOKEN_ERROR) + { + // This is the case when app server is running behind a websever and app server is down + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed. Check that app server is running behind web server"); + } + else if (code == WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SOCKET_CONNECTION_FAILED) + { + // this will be the case when webserver is unreachable + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failed"); + } + else + { + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Web socket connection failure: {0}", code); + } + StartServerReconnectTimer(); + } + + return 0; + } + + /// + /// After a "hello" from the server, sends config and stuff + /// + void SendInitialMessage() + { + Debug.Console(1, this, "Sending initial join message"); + var confObject = ConfigReader.ConfigObject; + confObject.Info.RuntimeInfo.AppName = Assembly.GetExecutingAssembly().GetName().Name; + var version = Assembly.GetExecutingAssembly().GetName().Version; + confObject.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + + var msg = new + { + type = "join", + content = new + { + config = confObject + } + }; + SendMessageObjectToServer(msg); + } + + /// + /// Sends any object type to server + /// + /// + public void SendMessageObjectToServer(object o) + { + SendMessageToServer(JObject.FromObject(o)); + } + /// /// Sends a message to the server from a room /// @@ -340,78 +409,67 @@ namespace PepperDash.Essentials /// object to be serialized and sent in post body public void SendMessageToServer(JObject o) { - if (WSClient != null && WSClient.Connected) { string message = JsonConvert.SerializeObject(o, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); Debug.Console(1, this, "Message TX: {0}", message); var messageBytes = System.Text.Encoding.UTF8.GetBytes(message); - WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); - //WSClient.SendAsync(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); + var result = WSClient.Send(messageBytes, (uint)messageBytes.Length, WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME); + if (result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS) + { + Debug.Console(1, this, "Socket send result error: {0}", result); + } } - + else if (!WSClient.Connected) + { + Debug.Console(1, this, "Cannot send. Not connected {0}"); + } } /// /// Disconnects the SSE Client and stops the heartbeat timer /// /// - void DisconnectStreamClient(string command) + void CleanUpWebsocketClient() { - //if(SseClient != null) - // SseClient.Disconnect(); - - if (WSClient != null && WSClient.Connected) - WSClient.Disconnect(); - - if (ServerHeartbeatCheckTimer != null) - { - ServerHeartbeatCheckTimer.Stop(); - - ServerHeartbeatCheckTimer = null; - } + Debug.Console(1, this, "Disconnecting websocket"); + if (WSClient != null) + { + WSClient.SendCallBack = null; + WSClient.ReceiveCallBack = null; + WSClient.ConnectionCallBack = null; + WSClient.DisconnectCallBack = null; + if (WSClient.Connected) + { + WSClient.Disconnect(); + } + WSClient = null; + } } - - /// - /// The callback that fires when we get a response from our registration attempt - /// - /// - /// - void RegistrationConnectionCallback(HttpClientResponse resp, HTTP_CALLBACK_ERROR err) - { - CheckHttpDebug(resp, err); - Debug.Console(1, this, "RegistrationConnectionCallback: {0}", err); - try - { - if (resp != null && resp.Code == 200) - { - if(ServerReconnectTimer != null) - { - ServerReconnectTimer.Stop(); - ServerReconnectTimer = null; - } - // Success here! - ConnectStreamClient(); - } - else - { - if (resp != null) - Debug.Console(1, this, "Response from server: {0}\n{1}", resp.Code, err); - else - { - Debug.Console(1, this, "Null response received from server."); - } - StartReconnectTimer(); - } - } - catch (Exception e) - { - Debug.Console(1, this, "Error Initializing Stream Client: {0}", e); - StartReconnectTimer(); - } - RegisterLockEvent.Set(); - } + /// + /// + /// + /// + /// + void StartServerReconnectTimer() + { + StopServerReconnectTimer(); + ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, ServerReconnectInterval); + Debug.Console(1, this, "Reconnect Timer Started."); + } + + /// + /// Does what it says + /// + void StopServerReconnectTimer() + { + if (ServerReconnectTimer != null) + { + ServerReconnectTimer.Stop(); + ServerReconnectTimer = null; + } + } /// /// Executes when we don't get a heartbeat message in time. Triggers reconnect. @@ -425,23 +483,8 @@ namespace PepperDash.Essentials ServerHeartbeatCheckTimer.Stop(); ServerHeartbeatCheckTimer = null; } - StartReconnectTimer(); - } - - /// - /// - /// - /// - /// - void StartReconnectTimer() - { - // Start the reconnect timer - if (ServerReconnectTimer == null) - { - ServerReconnectTimer = new CTimer(ReconnectToServerTimerCallback, null, ServerReconnectInterval, ServerReconnectInterval); - Debug.Console(1, this, "Reconnect Timer Started."); - } - ServerReconnectTimer.Reset(ServerReconnectInterval, ServerReconnectInterval); + CleanUpWebsocketClient(); + StartServerReconnectTimer(); } /// @@ -451,37 +494,42 @@ namespace PepperDash.Essentials /// void ResetOrStartHearbeatTimer() { - if (ServerHeartbeatCheckTimer == null) - { - ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval); - - Debug.Console(1, this, "Heartbeat Timer Started."); - } - - ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval); - } - - - /// - /// Connects the SSE Client - /// - /// - void ConnectStreamClient() - { - Debug.Console(0, this, "Initializing Stream client to server."); - - if (WSClient == null) + if (ServerHeartbeatCheckTimer == null) { - WSClient = new WebSocketClient(); + ServerHeartbeatCheckTimer = new CTimer(HeartbeatExpiredTimerCallback, null, ServerHeartbeatInterval, ServerHeartbeatInterval); + Debug.Console(1, this, "Heartbeat Timer Started."); + } + else + { + ServerHeartbeatCheckTimer.Reset(ServerHeartbeatInterval, ServerHeartbeatInterval); } - WSClient.URL = string.Format("wss://{0}/system/join/{1}", Config.ServerUrl, this.SystemUuid); - WSClient.Connect(); - Debug.Console(0, this, "Websocket connected"); - WSClient.ReceiveCallBack = WebsocketReceiveCallback; - //WSClient.SendCallBack = WebsocketSendCallback; - WSClient.ReceiveAsync(); } + /// + /// Waits two and goes again + /// + void ReconnectStreamClient() + { + new CTimer(o => ConnectWebsocketClient(), 2000); + } + + /// + /// + /// + /// + /// + int Websocket_DisconnectCallback(WebSocketClient.WEBSOCKET_RESULT_CODES code, object o) + { + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Websocket disconnected with code: {0}", code); + + if (ServerHeartbeatCheckTimer != null) + ServerHeartbeatCheckTimer.Stop(); + // Start the reconnect timer + StartServerReconnectTimer(); + return 0; + } + + /// /// Resets reconnect timer and updates usercode /// @@ -514,12 +562,26 @@ namespace PepperDash.Essentials { if (HttpDebugEnabled) { - Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------"); - Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl.ToString()); - Debug.Console(0, this, "HTTP Response 'error' {0}", e); - Debug.Console(0, this, "HTTP Response code: {0}", r.Code); - Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString); - Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------"); + try + { + Debug.Console(0, this, "------ Begin HTTP Debug ---------------------------------------"); + if (r != null) + { + Debug.Console(0, this, "HTTP Response URL: {0}", r.ResponseUrl != null ? r.ResponseUrl.ToString() : "NONE"); + Debug.Console(0, this, "HTTP Response code: {0}", r.Code); + Debug.Console(0, this, "HTTP Response content: \r{0}", r.ContentString); + } + else + { + Debug.Console(0, this, "No HTTP response"); + } + Debug.Console(0, this, "HTTP Response 'error' {0}", e); + Debug.Console(0, this, "------ End HTTP Debug -----------------------------------------"); + } + catch (Exception ex) + { + Debug.Console(0, this, "HttpDebugError: {0}", ex); + } } } @@ -530,14 +592,28 @@ namespace PepperDash.Essentials /// /// /// - int WebsocketReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode, + int Websocket_ReceiveCallback(byte[] data, uint length, WebSocketClient.WEBSOCKET_PACKET_TYPES opcode, WebSocketClient.WEBSOCKET_RESULT_CODES err) { - var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length); - if(rx.Length > 0) - ParseStreamRx(rx); - WSClient.ReceiveAsync(); - return 1; + if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__TEXT_FRAME) + { + var rx = System.Text.Encoding.UTF8.GetString(data, 0, (int)length); + if (rx.Length > 0) + ParseStreamRx(rx); + WSClient.ReceiveAsync(); + } + + else if (opcode == WebSocketClient.WEBSOCKET_PACKET_TYPES.LWS_WS_OPCODE_07__CLOSE) + { + Debug.Console(1, this, "Websocket disconnect received from remote"); + CleanUpWebsocketClient(); + } + else + { + Debug.Console(1, this, "websocket rx opcode/err {0}/{1}", opcode, err); + WSClient.ReceiveAsync(); + } + return 0; } /// @@ -545,11 +621,11 @@ namespace PepperDash.Essentials /// /// /// - int WebsocketSendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result) + int Websocket_SendCallback(Crestron.SimplSharp.CrestronWebSocketClient.WebSocketClient.WEBSOCKET_RESULT_CODES result) { - Debug.Console(1, this, "SendCallback result: {0}", result); - - return 1; + if(result != WebSocketClient.WEBSOCKET_RESULT_CODES.WEBSOCKET_CLIENT_SUCCESS) + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SendCallback questionable result: {0}", result); + return 1; } /// @@ -562,7 +638,7 @@ namespace PepperDash.Essentials if(string.IsNullOrEmpty(message)) return; - Debug.Console(1, this, "Message RX: '{0}'", message); + Debug.Console(1, this, "Message RX: {0}", message); try { var messageObj = JObject.Parse(message); @@ -571,6 +647,7 @@ namespace PepperDash.Essentials if (type == "hello") { + SendInitialMessage(); ResetOrStartHearbeatTimer(); } else if (type == "/system/heartbeat") @@ -579,11 +656,11 @@ namespace PepperDash.Essentials } else if (type == "close") { - WSClient.Disconnect(); + Debug.Console(1, this, "Received close message from server."); + // DisconnectWebsocketClient(); - ServerHeartbeatCheckTimer.Stop(); - // Start the reconnect timer - StartReconnectTimer(); + if (ServerHeartbeatCheckTimer != null) + ServerHeartbeatCheckTimer.Stop(); } else { @@ -620,7 +697,7 @@ namespace PepperDash.Essentials } case "held": { - if (!PushedActions.ContainsKey(type)) + if (PushedActions.ContainsKey(type)) { PushedActions[type].Reset(ButtonHeartbeatInterval, ButtonHeartbeatInterval); } diff --git a/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs new file mode 100644 index 00000000..882ad317 --- /dev/null +++ b/PepperDashEssentials/AppServer/Messengers/AtcDdvc01Messenger.cs @@ -0,0 +1,167 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.EthernetCommunication; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Devices.Common.Codec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + public class Ddvc01AtcMessenger : MessengerBase + { + BasicTriList EISC; + + const uint BDialHangup = 221; + const uint BIncomingAnswer = 251; + const uint BIncomingReject = 252; + const uint BSpeedDial1 = 241; + const uint BSpeedDial2 = 242; + const uint BSpeedDial3 = 243; + const uint BSpeedDial4 = 244; + + /// + /// 201 + /// + const uint SCurrentDialString = 201; + /// + /// 211 + /// + const uint SCurrentCallString = 211; + /// + /// 221 + /// + const uint SHookState = 221; + + /// + /// + /// + Dictionary DTMFMap = new Dictionary + { + { "1", 201 }, + { "2", 202 }, + { "3", 203 }, + { "4", 204 }, + { "5", 205 }, + { "6", 206 }, + { "7", 207 }, + { "8", 208 }, + { "9", 209 }, + { "0", 210 }, + { "*", 211 }, + { "#", 212 }, + }; + + CodecActiveCallItem CurrentCallItem; + + + /// + /// + /// + /// + /// + public Ddvc01AtcMessenger(BasicTriList eisc, string messagePath) + : base(messagePath) + { + EISC = eisc; + + CurrentCallItem = new CodecActiveCallItem(); + CurrentCallItem.Type = eCodecCallType.Audio; + CurrentCallItem.Id = "-audio-"; + } + + /// + /// + /// + void SendFullStatus() + { + this.PostStatusMessage(new + { + calls = GetCurrentCallList(), + callStatus = EISC.GetString(SHookState), + currentCallString = EISC.GetString(SCurrentCallString), + currentDialString = EISC.GetString(SCurrentDialString), + }); + } + + /// + /// + /// + /// + protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController) + { + Action send = this.PostStatusMessage; + EISC.SetStringSigAction(SCurrentDialString, s => send(new { currentDialString = s })); + + EISC.SetStringSigAction(SHookState, s => + { + CurrentCallItem.Status = (eCodecCallStatus)Enum.Parse(typeof(eCodecCallStatus), s, true); + GetCurrentCallList(); + send(new + { + calls = GetCurrentCallList(), + callStatus = s + }); + }); + + EISC.SetStringSigAction(SCurrentCallString, s => + { + CurrentCallItem.Name = s; + CurrentCallItem.Number = s; + send(new + { + calls = GetCurrentCallList(), + currentCallString = s + }); + }); + + // Add press and holds using helper + Action addPHAction = (s, u) => + AppServerController.AddAction(MessagePath + s, new PressAndHoldAction(b => EISC.SetBool(u, b))); + + // Add straight pulse calls + Action addAction = (s, u) => + AppServerController.AddAction(MessagePath + s, new Action(() => EISC.PulseBool(u, 100))); + addAction("/endCall", BDialHangup); + addAction("/incomingAnswer", BIncomingAnswer); + addAction("/incomingReject", BIncomingReject); + addAction("/speedDial1", BSpeedDial1); + addAction("/speedDial2", BSpeedDial2); + addAction("/speedDial3", BSpeedDial3); + addAction("/speedDial4", BSpeedDial4); + + // Get status + AppServerController.AddAction(MessagePath + "/fullStatus", new Action(SendFullStatus)); + // Dial on string + AppServerController.AddAction(MessagePath + "/dial", new Action(s => EISC.SetString(SCurrentDialString, s))); + // Pulse DTMF + AppServerController.AddAction(MessagePath + "/dtmf", new Action(s => + { + if (DTMFMap.ContainsKey(s)) + { + EISC.PulseBool(DTMFMap[s], 100); + } + })); + } + + /// + /// Turns the + /// + /// + List GetCurrentCallList() + { + if (CurrentCallItem.Status == eCodecCallStatus.Disconnected) + { + return new List(); + } + else + { + return new List() { CurrentCallItem }; + } + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs new file mode 100644 index 00000000..125bf83a --- /dev/null +++ b/PepperDashEssentials/AppServer/Messengers/MessengerBase.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Crestron.SimplSharp; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using PepperDash.Essentials.Devices.Common.Codec; +using PepperDash.Essentials.Devices.Common.VideoCodec; + +namespace PepperDash.Essentials.AppServer.Messengers +{ + /// + /// Provides a messaging bridge for a VideoCodecBase + /// + public abstract class MessengerBase + { + /// + /// + /// + public CotijaSystemController AppServerController { get; private set; } + + public string MessagePath { get; private set; } + + /// + /// + /// + /// + public MessengerBase(string messagePath) + { + if (string.IsNullOrEmpty(messagePath)) + throw new ArgumentException("messagePath must not be empty or null"); + + MessagePath = messagePath; + } + + + /// + /// Registers this messenger with appserver controller + /// + /// + public void RegisterWithAppServer(CotijaSystemController appServerController) + { + if (appServerController == null) + throw new ArgumentNullException("appServerController"); + + AppServerController = appServerController; + CustomRegisterWithAppServer(AppServerController); + } + + /// + /// Implemented in extending classes. Wire up API calls and feedback here + /// + /// + abstract protected void CustomRegisterWithAppServer(CotijaSystemController appServerController); + + /// + /// Helper for posting status message + /// + /// The contents of the content object + protected void PostStatusMessage(object contentObject) + { + AppServerController.SendMessageToServer(JObject.FromObject(new + { + type = MessagePath, + content = contentObject + })); + } + } +} \ No newline at end of file diff --git a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs index 67180b35..37437b5d 100644 --- a/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs +++ b/PepperDashEssentials/AppServer/Messengers/VideoCodecBaseMessenger.cs @@ -15,29 +15,22 @@ namespace PepperDash.Essentials.AppServer.Messengers /// /// Provides a messaging bridge for a VideoCodecBase /// - public class VideoCodecBaseMessenger + public class VideoCodecBaseMessenger : MessengerBase { /// /// /// public VideoCodecBase Codec { get; private set; } - public CotijaSystemController AppServerController { get; private set; } - - public string MessagePath { get; private set; } - /// /// /// /// - public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath) + public VideoCodecBaseMessenger(VideoCodecBase codec, string messagePath) : base(messagePath) { if (codec == null) throw new ArgumentNullException("codec"); - if (string.IsNullOrEmpty(messagePath)) - throw new ArgumentException("messagePath must not be empty or null"); - MessagePath = messagePath; Codec = codec; codec.CallStatusChange += new EventHandler(codec_CallStatusChange); codec.IsReadyChange += new EventHandler(codec_IsReadyChange); @@ -77,16 +70,11 @@ namespace PepperDash.Essentials.AppServer.Messengers } /// - /// Registers this codec's messaging with an app server controller + /// Called from base's RegisterWithAppServer method /// /// - public void RegisterWithAppServer(CotijaSystemController appServerController) + protected override void CustomRegisterWithAppServer(CotijaSystemController appServerController) { - if (appServerController == null) - throw new ArgumentNullException("appServerController"); - - AppServerController = appServerController; - appServerController.AddAction("/device/videoCodec/isReady", new Action(SendIsReady)); appServerController.AddAction("/device/videoCodec/fullStatus", new Action(SendVtcFullMessageObject)); appServerController.AddAction("/device/videoCodec/dial", new Action(s => Codec.Dial(s))); @@ -242,18 +230,5 @@ namespace PepperDash.Essentials.AppServer.Messengers hasDirectory = Codec is IHasDirectory }); } - - /// - /// Helper for posting status message - /// - /// The contents of the content object - void PostStatusMessage(object contentObject) - { - AppServerController.SendMessageToServer(JObject.FromObject(new - { - type = MessagePath, - content = contentObject - })); - } } } \ No newline at end of file diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs index 2ccb03d9..f02d6098 100644 --- a/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs +++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaDdvc01RoomBridge.cs @@ -10,6 +10,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; +using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.Core; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Room.Config; @@ -26,6 +27,14 @@ namespace PepperDash.Essentials.Room.Cotija /// public const uint RoomIsOn = 301; + /// + /// 41 + /// + public const uint PromptForCode = 41; + /// + /// 42 + /// + public const uint ClientJoined = 42; /// /// 51 /// @@ -60,14 +69,15 @@ namespace PepperDash.Essentials.Room.Cotija /// 63 /// public const uint ShutdownStart = 63; - - - /// /// 72 /// public const uint SourceHasChanged = 72; /// + /// 261 - The start of the range of speed dial visibles + /// + public const uint SpeedDialVisibleStartJoin = 261; + /// /// 501 /// public const uint ConfigIsReady = 501; @@ -92,6 +102,16 @@ namespace PepperDash.Essentials.Room.Cotija /// 71 /// public const uint SelectedSourceKey = 71; + + /// + /// 241 + /// + public const uint SpeedDialNameStartJoin = 241; + + /// + /// 251 + /// + public const uint SpeedDialNumberStartJoin = 251; /// /// 501 @@ -145,6 +165,8 @@ namespace PepperDash.Essentials.Room.Cotija CotijaDdvc01DeviceBridge SourceBridge; + Ddvc01AtcMessenger AtcMessenger; + /// /// @@ -182,6 +204,9 @@ namespace PepperDash.Essentials.Room.Cotija SetupFunctions(); SetupFeedbacks(); + AtcMessenger = new Ddvc01AtcMessenger(EISC, "/device/audioCodec"); + AtcMessenger.RegisterWithAppServer(Parent); + EISC.SigChange += EISC_SigChange; EISC.OnlineStatusChange += (o, a) => { @@ -216,8 +241,6 @@ namespace PepperDash.Essentials.Room.Cotija } }, "mobilebridgedump", "Dumps DDVC01 bridge EISC data b,u,s", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => LoadConfigValues(), "loadddvc", "", ConsoleAccessLevelEnum.AccessOperator); - return base.CustomActivate(); } @@ -227,6 +250,9 @@ namespace PepperDash.Essentials.Room.Cotija /// void SetupFunctions() { +#warning need join numbers for these + Parent.AddAction(@"/room/room1/promptForCode", new Action(() => EISC.PulseBool(BoolJoin.PromptForCode))); + Parent.AddAction(@"/room/room1/clientJoined", new Action(() => EISC.PulseBool(BoolJoin.ClientJoined))); Parent.AddAction(@"/room/room1/status", new Action(SendFullStatus)); @@ -238,6 +264,10 @@ namespace PepperDash.Essentials.Room.Cotija Parent.AddAction(@"/room/room1/defaultsource", new Action(() => EISC.PulseBool(BoolJoin.ActivitySharePress))); + Parent.AddAction(@"/room/room1/activityVideo", new Action(() => + EISC.PulseBool(BoolJoin.ActivityVideoCallPress))); + Parent.AddAction(@"/room/room1/activityPhone", new Action(() => + EISC.PulseBool(BoolJoin.ActivityPhoneCallPress))); Parent.AddAction(@"/room/room1/volumes/master/level", new Action(u => EISC.SetUshort(UshortJoin.MasterVolumeLevel, u))); @@ -250,20 +280,26 @@ namespace PepperDash.Essentials.Room.Cotija EISC.PulseBool(BoolJoin.ShutdownEnd))); Parent.AddAction(@"/room/room1/shutdownCancel", new Action(() => EISC.PulseBool(BoolJoin.ShutdownCancel))); - - - // Source Device (Current Source)' - - SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary(); - - var prefix = @"/device/currentSource/"; - - foreach (var item in sourceJoinMap) - { - Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(item.Value, b))); - } } + /// + /// + /// + /// + void SetupSourceFunctions(string devKey) + { + SourceDeviceMapDictionary sourceJoinMap = new SourceDeviceMapDictionary(); + + var prefix = string.Format("/device/{0}/", devKey); + + foreach (var item in sourceJoinMap) + { + var join = item.Value; + Parent.AddAction(string.Format("{0}{1}", prefix, item.Key), new PressAndHoldAction(b => EISC.SetBool(join, b))); + } + } + + /// /// Links feedbacks to whatever is gonna happen! /// @@ -345,11 +381,11 @@ namespace PepperDash.Essentials.Room.Cotija var version = Assembly.GetExecutingAssembly().GetName().Version; co.Info.RuntimeInfo.AssemblyVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - //Room - if (co.Rooms == null) - co.Rooms = new List(); - var rm = new DeviceConfig(); + //if (co.Rooms == null) + // always start fresh in case simpl changed + co.Rooms = new List(); + var rm = new EssentialsRoomConfig(); if (co.Rooms.Count == 0) { Debug.Console(0, this, "Adding room to config"); @@ -388,6 +424,11 @@ namespace PepperDash.Essentials.Room.Cotija var name = EISC.StringOutput[i + 1].StringValue; rmProps.SpeedDials.Add(new DDVC01SpeedDial { Number = num, Name = name}); } + + // This MAY need a check + rmProps.AudioCodecKey = "audioCodec"; + rmProps.VideoCodecKey = null; // "videoCodec"; + // volume control names var volCount = EISC.UShortOutput[701].UShortValue; @@ -403,7 +444,10 @@ namespace PepperDash.Essentials.Room.Cotija co.Devices = new List(); // clear out previous DDVC devices - co.Devices.RemoveAll(d => d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase)); + co.Devices.RemoveAll(d => + d.Key.StartsWith("source-", StringComparison.OrdinalIgnoreCase) + || d.Key.Equals("audioCodec", StringComparison.OrdinalIgnoreCase) + || d.Key.Equals("videoCodec", StringComparison.OrdinalIgnoreCase)); rmProps.SourceListKey = "default"; rm.Properties = JToken.FromObject(rmProps); @@ -422,6 +466,7 @@ namespace PepperDash.Essentials.Room.Cotija break; var icon = EISC.StringOutput[651 + i].StringValue; var key = EISC.StringOutput[671 + i].StringValue; + var type = EISC.StringOutput[701 + i].StringValue; Debug.Console(0, this, "Adding source {0} '{1}'", key, name); @@ -430,6 +475,7 @@ namespace PepperDash.Essentials.Room.Cotija Name = name, Order = (int)i + 1, SourceKey = key, + Type = eSourceListItemType.Route }; newSl.Add(key, newSLI); @@ -447,10 +493,50 @@ namespace PepperDash.Essentials.Room.Cotija Type = type }; co.Devices.Add(devConf); + + if (group.ToLower().StartsWith("settopbox")) // Add others here as needed + { + SetupSourceFunctions(key); + } } co.SourceLists.Add("default", newSl); + // build "audioCodec" config if we need + if (!string.IsNullOrEmpty(rmProps.AudioCodecKey)) + { + var acFavs = new List(); + for (uint i = 0; i < 4; i++) + { + if (!EISC.GetBool(BoolJoin.SpeedDialVisibleStartJoin + i)) + { + break; + } + acFavs.Add(new PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem() + { + Name = EISC.GetString(StringJoin.SpeedDialNameStartJoin + i), + Number = EISC.GetString(StringJoin.SpeedDialNumberStartJoin + i), + Type = PepperDash.Essentials.Devices.Common.Codec.eCodecCallType.Audio + }); + } + + var acProps = new + { + favorites = acFavs + }; + + var acStr = "audioCodec"; + var acConf = new DeviceConfig() + { + Group = acStr, + Key = acStr, + Name = acStr, + Type = acStr, + Properties = JToken.FromObject(acProps) + }; + co.Devices.Add(acConf); + } + Debug.Console(0, this, "******* CONFIG FROM DDVC: \r{0}", JsonConvert.SerializeObject(ConfigReader.ConfigObject, Formatting.Indented)); var handler = ConfigurationIsReady; @@ -462,6 +548,9 @@ namespace PepperDash.Essentials.Room.Cotija ConfigIsLoaded = true; } + /// + /// + /// void SendFullStatus() { if (ConfigIsLoaded) @@ -585,86 +674,5 @@ namespace PepperDash.Essentials.Room.Cotija EISC.StringInput[StringJoin.UserCodeToSystem].StringValue = UserCode; EISC.StringInput[StringJoin.ServerUrl].StringValue = Parent.Config.ClientAppUrl; } - - /// - /// - /// - /// - /// - void SourceChange(string oldKey, string newKey) - { - /* Example message - * { -   "type":"/room/status", -   "content": { -     "selectedSourceKey": "off", -   } - } - */ - //if (type == ChangeType.WillChange) - //{ - // // Disconnect from previous source - - // if (info != null) - // { - // var previousDev = info.SourceDevice; - - // // device type interfaces - // if (previousDev is ISetTopBoxControls) - // (previousDev as ISetTopBoxControls).UnlinkActions(Parent); - // // common interfaces - // if (previousDev is IChannel) - // (previousDev as IChannel).UnlinkActions(Parent); - // if (previousDev is IColor) - // (previousDev as IColor).UnlinkActions(Parent); - // if (previousDev is IDPad) - // (previousDev as IDPad).UnlinkActions(Parent); - // if (previousDev is IDvr) - // (previousDev as IDvr).UnlinkActions(Parent); - // if (previousDev is INumericKeypad) - // (previousDev as INumericKeypad).UnlinkActions(Parent); - // if (previousDev is IPower) - // (previousDev as IPower).UnlinkActions(Parent); - // if (previousDev is ITransport) - // (previousDev as ITransport).UnlinkActions(Parent); - // } - - - // var huddleRoom = room as EssentialsHuddleSpaceRoom; - // JObject roomStatus = new JObject(); - // roomStatus.Add("selectedSourceKey", huddleRoom.CurrentSourceInfoKey); - - // JObject message = new JObject(); - - // message.Add("type", "/room/status/"); - // message.Add("content", roomStatus); - - // Parent.PostToServer(message); - //} - //else - //{ - // if (info != null) - // { - // var dev = info.SourceDevice; - - // if (dev is ISetTopBoxControls) - // (dev as ISetTopBoxControls).LinkActions(Parent); - // if (dev is IChannel) - // (dev as IChannel).LinkActions(Parent); - // if (dev is IColor) - // (dev as IColor).LinkActions(Parent); - // if (dev is IDPad) - // (dev as IDPad).LinkActions(Parent); - // if (dev is IDvr) - // (dev as IDvr).LinkActions(Parent); - // if (dev is INumericKeypad) - // (dev as INumericKeypad).LinkActions(Parent); - // if (dev is IPower) - // (dev as IPower).LinkActions(Parent); - // if (dev is ITransport) - // (dev as ITransport).LinkActions(Parent); - // } - //} - } } } \ No newline at end of file diff --git a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs index 24f2340a..35acefc6 100644 --- a/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs +++ b/PepperDashEssentials/AppServer/RoomBridges/CotijaEssentialsHuddleSpaceRoomBridge.cs @@ -74,7 +74,7 @@ namespace PepperDash.Essentials { Parent.AddAction(string.Format(@"/room/{0}/volumes/master/level", Room.Key), new Action(u => (volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback).SetVolume(u))); - Parent.AddAction(string.Format(@"/room/{0}/volumes/master/mute", Room.Key), new Action(() => + Parent.AddAction(string.Format(@"/room/{0}/volumes/master/muteToggle", Room.Key), new Action(() => volumeRoom.CurrentVolumeControls.MuteToggle())); volumeRoom.CurrentVolumeDeviceChange += new EventHandler(Room_CurrentVolumeDeviceChange); @@ -82,7 +82,7 @@ namespace PepperDash.Essentials var currentVolumeDevice = volumeRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; if (currentVolumeDevice != null) { - currentVolumeDevice.MuteFeedback.OutputChange += VolumeLevelFeedback_OutputChange; + currentVolumeDevice.MuteFeedback.OutputChange += MuteFeedback_OutputChange; currentVolumeDevice.VolumeLevelFeedback.OutputChange += VolumeLevelFeedback_OutputChange; } } @@ -470,8 +470,7 @@ namespace PepperDash.Essentials }; } return vtc; - } - + } } /// diff --git a/PepperDashEssentials/ControlSystem.cs b/PepperDashEssentials/ControlSystem.cs index 8c28a48c..660d8311 100644 --- a/PepperDashEssentials/ControlSystem.cs +++ b/PepperDashEssentials/ControlSystem.cs @@ -42,6 +42,10 @@ namespace PepperDash.Essentials // ConsoleAccessLevelEnum.AccessOperator); // CrestronConsole.AddNewConsoleCommand(S => { ConfigWriter.WriteConfigFile(null); }, "writeconfig", "writes the current config to a file", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => + { + Debug.Console(0, Debug.ErrorLogLevel.Notice, "CONSOLE MESSAGE: {0}", s); + }, "appdebugmessage", "Writes message to log", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(s => { diff --git a/PepperDashEssentials/ControlSystem.cs.orig b/PepperDashEssentials/ControlSystem.cs.orig deleted file mode 100644 index bd9590c4..00000000 --- a/PepperDashEssentials/ControlSystem.cs.orig +++ /dev/null @@ -1,367 +0,0 @@ -using System; -using System.Linq; -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.CrestronThread; -using PepperDash.Core; -using PepperDash.Core.PortalSync; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Devices.Common; -using PepperDash.Essentials.DM; -using PepperDash.Essentials.Fusion; -using PepperDash.Essentials.Room.Cotija; - -namespace PepperDash.Essentials -{ - public class ControlSystem : CrestronControlSystem - { - PepperDashPortalSyncClient PortalSync; - HttpLogoServer LogoServer; - - public ControlSystem() - : base() - { - Thread.MaxNumberOfUserThreads = 400; - Global.ControlSystem = this; - DeviceManager.Initialize(this); - } - - /// - /// Git 'er goin' - /// - public override void InitializeSystem() - { -<<<<<<< HEAD - DeterminePlatform(); - - //CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file", - // ConsoleAccessLevelEnum.AccessOperator); -======= - CrestronConsole.AddNewConsoleCommand(s => GoWithLoad(), "go", "Loads configuration file", - ConsoleAccessLevelEnum.AccessOperator); ->>>>>>> 600b9f11ff1bbc186f7c2a2945955731b3523b3c - - CrestronConsole.AddNewConsoleCommand(s => - { - foreach (var tl in TieLineCollection.Default) - CrestronConsole.ConsoleCommandResponse(" {0}\r", tl); - }, - "listtielines", "Prints out all tie lines", ConsoleAccessLevelEnum.AccessOperator); - - CrestronConsole.AddNewConsoleCommand(s => - { - CrestronConsole.ConsoleCommandResponse - ("Current running configuration. This is the merged system and template configuration"); - CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject - (ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented)); - }, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator); - - CrestronConsole.AddNewConsoleCommand(s => - { - CrestronConsole.ConsoleCommandResponse("This system can be found at the following URLs:\r" + - "System URL: {0}\r" + - "Template URL: {1}", ConfigReader.ConfigObject.SystemUrl, ConfigReader.ConfigObject.TemplateUrl); - }, "portalinfo", "Shows portal URLS from configuration", ConsoleAccessLevelEnum.AccessOperator); - - //GoWithLoad(); - } - - /// - /// Determines if the program is running on a processor (appliance) or server (XiO Edge). - /// - /// Sets Global.FilePathPrefix based on platform - /// - public void DeterminePlatform() - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Determining Platform...."); - - string filePathPrefix; - - var dirSeparator = Global.DirectorySeparator; - - var version = Crestron.SimplSharp.Reflection.Assembly.GetExecutingAssembly().GetName().Version; - - var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - - string directoryPrefix; - - //directoryPrefix = Crestron.SimplSharp.CrestronIO.Directory.GetApplicationRootDirectory(); -#warning ^ For use with beta Include4.dat for XiO Edge - directoryPrefix = ""; - - if (CrestronEnvironment.DevicePlatform != eDevicePlatform.Server) - { - filePathPrefix = directoryPrefix + dirSeparator + "NVRAM" - + dirSeparator + string.Format("program{0}", InitialParametersClass.ApplicationNumber) + dirSeparator; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on 3-series Appliance", versionString); - } - else - { - filePathPrefix = directoryPrefix + dirSeparator + "User" + dirSeparator; - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials v{0} on XiO Edge Server", versionString); - } - - Global.SetFilePathPrefix(filePathPrefix); - } - - /// - /// Do it, yo - /// - public void GoWithLoad() - { - try - { - CrestronConsole.AddNewConsoleCommand(EnablePortalSync, "portalsync", "Loads Portal Sync", - ConsoleAccessLevelEnum.AccessOperator); - - //PortalSync = new PepperDashPortalSyncClient(); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Starting Essentials load from configuration"); - - var filesReady = SetupFilesystem(); - if (filesReady) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Folder structure verified. Loading config..."); - if (!ConfigReader.LoadConfig2()) - return; - - Load(); - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Essentials load complete\r" + - "-------------------------------------------------------------"); - } - else - { - Debug.Console(0, - "------------------------------------------------\r" + - "------------------------------------------------\r" + - "------------------------------------------------\r" + - "Essentials file structure setup completed.\r" + - "Please load config, sgd and ir files and\r" + - "restart program.\r" + - "------------------------------------------------\r" + - "------------------------------------------------\r" + - "------------------------------------------------"); - } - } - catch (Exception e) - { - Debug.Console(0, "FATAL INITIALIZE ERROR. System is in an inconsistent state:\r{0}", e); - } - - } - - /// - /// Verifies filesystem is set up. IR, SGD, and program1 folders - /// - bool SetupFilesystem() - { - Debug.Console(0, "Verifying and/or creating folder structure"); - var configDir = Global.FilePathPrefix; - var configExists = Directory.Exists(configDir); - if (!configExists) - Directory.Create(configDir); - - var irDir = Global.FilePathPrefix + "ir"; - if (!Directory.Exists(irDir)) - Directory.Create(irDir); - - var sgdDir = Global.FilePathPrefix + "sgd"; - if (!Directory.Exists(sgdDir)) - Directory.Create(sgdDir); - - return configExists; - } - - public void EnablePortalSync(string s) - { - if (s.ToLower() == "enable") - { - CrestronConsole.ConsoleCommandResponse("Portal Sync features enabled"); - PortalSync = new PepperDashPortalSyncClient(); - } - } - - public void TearDown() - { - Debug.Console(0, "Tearing down existing system"); - DeviceManager.DeactivateAll(); - - TieLineCollection.Default.Clear(); - - foreach (var key in DeviceManager.GetDevices()) - DeviceManager.RemoveDevice(key); - - Debug.Console(0, "Tear down COMPLETE"); - } - - /// - /// - /// - void Load() - { - LoadDevices(); - LoadTieLines(); - LoadRooms(); - LoadLogoServer(); - - DeviceManager.ActivateAll(); - } - - - /// - /// Reads all devices from config and adds them to DeviceManager - /// - public void LoadDevices() - { - foreach (var devConf in ConfigReader.ConfigObject.Devices) - { - - try - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Creating device '{0}'", devConf.Key); - // Skip this to prevent unnecessary warnings - if (devConf.Key == "processor") - continue; - - // Try local factory first - var newDev = DeviceFactory.GetDevice(devConf); - - // Then associated library factories - if (newDev == null) - newDev = PepperDash.Essentials.Devices.Common.DeviceFactory.GetDevice(devConf); - if (newDev == null) - newDev = PepperDash.Essentials.DM.DeviceFactory.GetDevice(devConf); - if (newDev == null) - newDev = PepperDash.Essentials.Devices.Displays.DisplayDeviceFactory.GetDevice(devConf); - - if (newDev != null) - DeviceManager.AddDevice(newDev); - else - Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Cannot load unknown device type '{0}', key '{1}'.", devConf.Type, devConf.Key); - } - catch (Exception e) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "ERROR: Creating device {0}. Skipping device. \r{1}", devConf.Key, e); - } - } - Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Devices Loaded."); - - } - - /// - /// Helper method to load tie lines. This should run after devices have loaded - /// - public void LoadTieLines() - { - // In the future, we can't necessarily just clear here because devices - // might be making their own internal sources/tie lines - - var tlc = TieLineCollection.Default; - //tlc.Clear(); - if (ConfigReader.ConfigObject.TieLines == null) - { - return; - } - - foreach (var tieLineConfig in ConfigReader.ConfigObject.TieLines) - { - var newTL = tieLineConfig.GetTieLine(); - if (newTL != null) - tlc.Add(newTL); - } - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Tie Lines Loaded."); - - } - - /// - /// Reads all rooms from config and adds them to DeviceManager - /// - public void LoadRooms() - { - if (ConfigReader.ConfigObject.Rooms == null) - { - Debug.Console(0, Debug.ErrorLogLevel.Warning, "WARNING: Configuration contains no rooms"); - return; - } - - foreach (var roomConfig in ConfigReader.ConfigObject.Rooms) - { - var room = roomConfig.GetRoomObject(); - if (room != null) - { - 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 EssentialsHuddleSpaceFusionSystemControllerBase((EssentialsHuddleSpaceRoom)room, 0xf1)); - - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Attempting to build Cotija Bridge..."); - // Cotija bridge - var bridge = new CotijaEssentialsHuddleSpaceRoomBridge(room as EssentialsHuddleSpaceRoom); - AddBridgePostActivationHelper(bridge); // Lets things happen later when all devices are present - DeviceManager.AddDevice(bridge); - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Cotija Bridge Added..."); - } - else if (room is EssentialsHuddleVtc1Room) - { - 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)); - } - else - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "Room is NOT EssentialsHuddleSpaceRoom, attempting to add to DeviceManager w/o Fusion"); - DeviceManager.AddDevice(room); - } - - } - else - Debug.Console(0, Debug.ErrorLogLevel.Notice, "WARNING: Cannot create room from config, key '{0}'", roomConfig.Key); - } - - Debug.Console(0, Debug.ErrorLogLevel.Notice, "All Rooms Loaded."); - - } - - /// - /// Helps add the post activation steps that link bridges to main controller - /// - /// - void AddBridgePostActivationHelper(CotijaBridgeBase bridge) - { - bridge.AddPostActivationAction(() => - { - var parent = DeviceManager.AllDevices.FirstOrDefault(d => d.Key == "appServer") as CotijaSystemController; - if (parent == null) - { - Debug.Console(0, bridge, "ERROR: Cannot connect bridge. System controller not present"); - } - Debug.Console(0, bridge, "Linking to parent controller"); - bridge.AddParent(parent); - parent.AddBridge(bridge); - }); - } - - /// - /// Fires up a logo server if not already running - /// - void LoadLogoServer() - { - try - { - LogoServer = new HttpLogoServer(8080, Global.FilePathPrefix + "html" + Global.DirectorySeparator + "logo"); - } - catch (Exception) - { - Debug.Console(0, Debug.ErrorLogLevel.Notice, "NOTICE: Logo server cannot be started. Likely already running in another program"); - } - } - } -} diff --git a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs index 14148a12..c79d49d7 100644 --- a/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs +++ b/PepperDashEssentials/OTHER/Fusion/EssentialsHuddleVtc1FusionController.cs @@ -1,348 +1,348 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronIO; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.DeviceSupport; -using Crestron.SimplSharpPro.Fusion; - - -using PepperDash.Core; -using PepperDash.Essentials; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Devices.Common; -using PepperDash.Essentials.Devices.Common.Occupancy; - -namespace PepperDash.Essentials.Fusion -{ - public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase - { - BooleanSigData CodecIsInCall; - - public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) - : base(room, ipId) - { - - } - - /// - /// Called in base class constructor before RVI and GUID files are built - /// - protected override void ExecuteCustomSteps() - { - SetUpCodec(); - } - - /// - /// Creates a static asset for the codec and maps the joins to the main room symbol - /// - void SetUpCodec() - { - try - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - if (codec == null) - { - Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); - return; - } - - codec.UsageTracker = new UsageTracking(codec); - codec.UsageTracker.UsageIsTracked = true; - codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; - - var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); - var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); - - // Map FusionRoom Attributes: - - // Codec volume - var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); - codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); - (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); - - // In Call Status - CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); - 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); - 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"); - } - - // Codec IP Address - bool codecHasIpInfo = false; - var codecComm = codec.Communication; - - string codecIpAddress = string.Empty; - int codecIpPort = 0; - - StringSigData codecIpAddressSig; - StringSigData codecIpPortSig; - - if(codecComm is GenericSshClient) - { - codecIpAddress = (codecComm as GenericSshClient).Hostname; - codecIpPort = (codecComm as GenericSshClient).Port; - codecHasIpInfo = true; - } - else if (codecComm is GenericTcpIpClient) - { - codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; - codecIpPort = (codecComm as GenericTcpIpClient).Port; - codecHasIpInfo = true; - } - - if (codecHasIpInfo) - { - codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); - codecIpAddressSig.InputSig.StringValue = codecIpAddress; - - codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); - codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); - } - - var tempAsset = new FusionAsset(); - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; - codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; - codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); - - // TODO: Map relevant attributes on asset symbol - - codecAsset.TrySetMakeModel(codec); - codecAsset.TryLinkAssetErrorToCommunication(codec); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); - } - } - - void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) - { - var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; - - CodecIsInCall.InputSig.BoolValue = codec.IsInCall; - } - - // These methods are overridden because they access the room class which is of a different type - - protected override void CreateSymbolAndBasicSigs(uint ipId) - { - Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); - - FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); - FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); - - FusionRoom.Register(); - - FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; - - FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; - FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; - FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; - - CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(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); - - // 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 EssentialsHuddleVtc1Room).CurrentSingleSourceChange += Room_CurrentSourceInfoChange; - - - FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); - FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff")); - // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); - - - CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; - } - - protected override void SetUpSources() - { - // Sources - var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).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 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) - { - var usageDevice = kvp.Value.SourceDevice as IUsageTracking; - - if (usageDevice != null) - { - usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); - usageDevice.UsageTracker.UsageIsTracked = true; - usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - } - - } - else - { - Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", - (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); - } - } - - protected override void SetUpDisplay() - { - try - { - //Setup Display Usage Monitoring - - var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); - - // Consider updating this in multiple display systems - - foreach (DisplayBase display in displays) - { - display.UsageTracker = new UsageTracking(display); - display.UsageTracker.UsageIsTracked = true; - display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); - } - - var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; - if (defaultDisplay == null) - { - Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); - return; - } - - var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); - var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); - - // Display to fusion room sigs - FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; - FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); - if (defaultDisplay is IDisplayUsage) - (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); - - - - MapDisplayToRoomJoins(1, 158, defaultDisplay); - - - var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); - - //Check for existing asset in GUIDs collection - - var tempAsset = new FusionAsset(); - - if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) - { - tempAsset = FusionStaticAssets[deviceConfig.Uid]; - } - else - { - // Create a new asset - tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); - FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); - } - - var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); - dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; - dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; - defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); - // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); - // Use extension methods - dispAsset.TrySetMakeModel(defaultDisplay); - dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); - } - catch (Exception e) - { - Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); - } - - } - - protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) - { - string displayName = string.Format("Display {0} - ", displayIndex); - - - if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) - { - // Power on - var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Power Off - var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); - defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; - display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); - - // Current Source - var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); - defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"); }); ; - } - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronIO; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.DeviceSupport; +using Crestron.SimplSharpPro.Fusion; + + +using PepperDash.Core; +using PepperDash.Essentials; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Devices.Common; +using PepperDash.Essentials.Devices.Common.Occupancy; + +namespace PepperDash.Essentials.Fusion +{ + public class EssentialsHuddleVtc1FusionController : EssentialsHuddleSpaceFusionSystemControllerBase + { + BooleanSigData CodecIsInCall; + + public EssentialsHuddleVtc1FusionController(EssentialsHuddleVtc1Room room, uint ipId) + : base(room, ipId) + { + + } + + /// + /// Called in base class constructor before RVI and GUID files are built + /// + protected override void ExecuteCustomSteps() + { + SetUpCodec(); + } + + /// + /// Creates a static asset for the codec and maps the joins to the main room symbol + /// + void SetUpCodec() + { + try + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + if (codec == null) + { + Debug.Console(1, this, "Cannot link codec to Fusion because codec is null"); + return; + } + + codec.UsageTracker = new UsageTracking(codec); + codec.UsageTracker.UsageIsTracked = true; + codec.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded; + + var codecPowerOnAction = new Action(b => { if (!b) codec.StandbyDeactivate(); }); + var codecPowerOffAction = new Action(b => { if (!b) codec.StandbyActivate(); }); + + // Map FusionRoom Attributes: + + // Codec volume + var codecVolume = FusionRoom.CreateOffsetUshortSig(50, "Volume - Fader01", eSigIoMask.InputOutputSig); + codecVolume.OutputSig.UserObject = new Action(b => (codec as IBasicVolumeWithFeedback).SetVolume(b)); + (codec as IBasicVolumeWithFeedback).VolumeLevelFeedback.LinkInputSig(codecVolume.InputSig); + + // In Call Status + CodecIsInCall = FusionRoom.CreateOffsetBoolSig(69, "Conf - VC 1 In Call", eSigIoMask.InputSigOnly); + 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); + 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"); + } + + // Codec IP Address + bool codecHasIpInfo = false; + var codecComm = codec.Communication; + + string codecIpAddress = string.Empty; + int codecIpPort = 0; + + StringSigData codecIpAddressSig; + StringSigData codecIpPortSig; + + if(codecComm is GenericSshClient) + { + codecIpAddress = (codecComm as GenericSshClient).Hostname; + codecIpPort = (codecComm as GenericSshClient).Port; + codecHasIpInfo = true; + } + else if (codecComm is GenericTcpIpClient) + { + codecIpAddress = (codecComm as GenericTcpIpClient).Hostname; + codecIpPort = (codecComm as GenericTcpIpClient).Port; + codecHasIpInfo = true; + } + + if (codecHasIpInfo) + { + codecIpAddressSig = FusionRoom.CreateOffsetStringSig(121, "IP Address - VC", eSigIoMask.InputSigOnly); + codecIpAddressSig.InputSig.StringValue = codecIpAddress; + + codecIpPortSig = FusionRoom.CreateOffsetStringSig(150, "IP Port - VC", eSigIoMask.InputSigOnly); + codecIpPortSig.InputSig.StringValue = codecIpPort.ToString(); + } + + var tempAsset = new FusionAsset(); + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(c => c.Key.Equals(codec.Key)); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), codec.Name, "Codec", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var codecAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + codecAsset.PowerOn.OutputSig.UserObject = codecPowerOnAction; + codecAsset.PowerOff.OutputSig.UserObject = codecPowerOffAction; + codec.StandbyIsOnFeedback.LinkComplementInputSig(codecAsset.PowerOn.InputSig); + + // TODO: Map relevant attributes on asset symbol + + codecAsset.TrySetMakeModel(codec); + codecAsset.TryLinkAssetErrorToCommunication(codec); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up codec in Fusion: {0}", e); + } + } + + void codec_CallStatusChange(object sender, PepperDash.Essentials.Devices.Common.Codec.CodecCallStatusItemChangeEventArgs e) + { + var codec = (Room as EssentialsHuddleVtc1Room).VideoCodec; + + CodecIsInCall.InputSig.BoolValue = codec.IsInCall; + } + + // These methods are overridden because they access the room class which is of a different type + + protected override void CreateSymbolAndBasicSigs(uint ipId) + { + Debug.Console(1, this, "Creating Fusion Room symbol with GUID: {0}", RoomGuid); + + FusionRoom = new FusionRoom(ipId, Global.ControlSystem, Room.Name, RoomGuid); + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.Use(); + FusionRoom.ExtenderFusionRoomDataReservedSigs.Use(); + + FusionRoom.Register(); + + FusionRoom.FusionStateChange += FusionRoom_FusionStateChange; + + FusionRoom.ExtenderRoomViewSchedulingDataReservedSigs.DeviceExtenderSigChange += FusionRoomSchedule_DeviceExtenderSigChange; + FusionRoom.ExtenderFusionRoomDataReservedSigs.DeviceExtenderSigChange += ExtenderFusionRoomDataReservedSigs_DeviceExtenderSigChange; + FusionRoom.OnlineStatusChange += FusionRoom_OnlineStatusChange; + + CrestronConsole.AddNewConsoleCommand(RequestFullRoomSchedule, "FusReqRoomSchedule", "Requests schedule of the room for the next 24 hours", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ModifyMeetingEndTimeConsoleHelper, "FusReqRoomSchMod", "Ends or extends a meeting by the specified time", ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(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); + + // 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 EssentialsHuddleVtc1Room).CurrentSingleSourceChange += Room_CurrentSourceInfoChange; + + + FusionRoom.SystemPowerOn.OutputSig.SetSigFalseAction((Room as EssentialsHuddleVtc1Room).PowerOnToDefaultOrLastSource); + FusionRoom.SystemPowerOff.OutputSig.SetSigFalseAction(() => (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff")); + // NO!! room.RoomIsOn.LinkComplementInputSig(FusionRoom.SystemPowerOff.InputSig); + + + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + protected override void SetUpSources() + { + // Sources + var dict = ConfigReader.ConfigObject.GetSourceListForKey((Room as EssentialsHuddleVtc1Room).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 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) + { + var usageDevice = kvp.Value.SourceDevice as IUsageTracking; + + if (usageDevice != null) + { + usageDevice.UsageTracker = new UsageTracking(usageDevice as Device); + usageDevice.UsageTracker.UsageIsTracked = true; + usageDevice.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + } + + } + else + { + Debug.Console(1, this, "WARNING: Config source list '{0}' not found for room '{1}'", + (Room as EssentialsHuddleVtc1Room).SourceListKey, Room.Key); + } + } + + protected override void SetUpDisplay() + { + try + { + //Setup Display Usage Monitoring + + var displays = DeviceManager.AllDevices.Where(d => d is DisplayBase); + + // Consider updating this in multiple display systems + + foreach (DisplayBase display in displays) + { + display.UsageTracker = new UsageTracking(display); + display.UsageTracker.UsageIsTracked = true; + display.UsageTracker.DeviceUsageEnded += new EventHandler(UsageTracker_DeviceUsageEnded); + } + + var defaultDisplay = (Room as EssentialsHuddleVtc1Room).DefaultDisplay as DisplayBase; + if (defaultDisplay == null) + { + Debug.Console(1, this, "Cannot link null display to Fusion because default display is null"); + return; + } + + var dispPowerOnAction = new Action(b => { if (!b) defaultDisplay.PowerOn(); }); + var dispPowerOffAction = new Action(b => { if (!b) defaultDisplay.PowerOff(); }); + + // Display to fusion room sigs + FusionRoom.DisplayPowerOn.OutputSig.UserObject = dispPowerOnAction; + FusionRoom.DisplayPowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(FusionRoom.DisplayPowerOn.InputSig); + if (defaultDisplay is IDisplayUsage) + (defaultDisplay as IDisplayUsage).LampHours.LinkInputSig(FusionRoom.DisplayUsage.InputSig); + + + + MapDisplayToRoomJoins(1, 158, defaultDisplay); + + + var deviceConfig = ConfigReader.ConfigObject.Devices.FirstOrDefault(d => d.Key.Equals(defaultDisplay.Key)); + + //Check for existing asset in GUIDs collection + + var tempAsset = new FusionAsset(); + + if (FusionStaticAssets.ContainsKey(deviceConfig.Uid)) + { + tempAsset = FusionStaticAssets[deviceConfig.Uid]; + } + else + { + // Create a new asset + tempAsset = new FusionAsset(FusionRoomGuids.GetNextAvailableAssetNumber(FusionRoom), defaultDisplay.Name, "Display", ""); + FusionStaticAssets.Add(deviceConfig.Uid, tempAsset); + } + + var dispAsset = FusionRoom.CreateStaticAsset(tempAsset.SlotNumber, tempAsset.Name, "Display", tempAsset.InstanceId); + dispAsset.PowerOn.OutputSig.UserObject = dispPowerOnAction; + dispAsset.PowerOff.OutputSig.UserObject = dispPowerOffAction; + defaultDisplay.PowerIsOnFeedback.LinkInputSig(dispAsset.PowerOn.InputSig); + // NO!! display.PowerIsOn.LinkComplementInputSig(dispAsset.PowerOff.InputSig); + // Use extension methods + dispAsset.TrySetMakeModel(defaultDisplay); + dispAsset.TryLinkAssetErrorToCommunication(defaultDisplay); + } + catch (Exception e) + { + Debug.Console(1, this, "Error setting up display in Fusion: {0}", e); + } + + } + + protected override void MapDisplayToRoomJoins(int displayIndex, int joinOffset, DisplayBase display) + { + string displayName = string.Format("Display {0} - ", displayIndex); + + + if (display == (Room as EssentialsHuddleVtc1Room).DefaultDisplay) + { + // Power on + var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOn(); }); + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Power Off + var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", eSigIoMask.InputOutputSig); + defaultDisplayPowerOn.OutputSig.UserObject = new Action(b => { if (!b) display.PowerOff(); }); ; + display.PowerIsOnFeedback.LinkInputSig(defaultDisplayPowerOn.InputSig); + + // Current Source + var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, displayName + "Source None", eSigIoMask.InputOutputSig); + defaultDisplaySourceNone.OutputSig.UserObject = new Action(b => { if (!b) (Room as EssentialsHuddleVtc1Room).RunRouteAction("roomOff"); }); ; + } + } + } } \ No newline at end of file diff --git a/PepperDashEssentials/PepperDashEssentials.csproj b/PepperDashEssentials/PepperDashEssentials.csproj index e38f2f70..26305d2f 100644 --- a/PepperDashEssentials/PepperDashEssentials.csproj +++ b/PepperDashEssentials/PepperDashEssentials.csproj @@ -108,6 +108,8 @@ + + diff --git a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs index 83303582..16e49933 100644 --- a/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs +++ b/PepperDashEssentials/Room/Config/EssentialsHuddleVtc1PropertiesConfig.cs @@ -20,5 +20,7 @@ namespace PepperDash.Essentials.Room.Config public string DefaultSourceItem { get; set; } [JsonProperty("videoCodecKey")] public string VideoCodecKey { get; set; } + [JsonProperty("audioCodecKey")] + public string AudioCodecKey { get; set; } } } \ No newline at end of file diff --git a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs index f1b86477..9a2b7a31 100644 --- a/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs +++ b/PepperDashEssentials/UIDrivers/EssentialsHuddle/EssentialsHuddlePanelAvFunctionsDriver.cs @@ -1,1076 +1,1076 @@ -using System; -using System.Collections.Generic; -using Crestron.SimplSharp; -using Crestron.SimplSharpPro; -using Crestron.SimplSharpPro.UI; - -using PepperDash.Core; -using PepperDash.Essentials.Core; -using PepperDash.Essentials.Core.Config; -using PepperDash.Essentials.Core.SmartObjects; -using PepperDash.Essentials.Core.PageManagers; - -namespace PepperDash.Essentials -{ - /// - /// - /// - public class EssentialsHuddlePanelAvFunctionsDriver : PanelDriverBase, IAVDriver - { - CrestronTouchpanelPropertiesConfig Config; - - public enum UiDisplayMode - { - PresentationMode, AudioSetup - } - - /// - /// Whether volume ramping from this panel will show the volume - /// gauge popup. - /// - public bool ShowVolumeGauge { get; set; } - - /// - /// The amount of time that the volume buttons stays on screen, in ms - /// - public uint VolumeButtonPopupTimeout - { - get { return VolumeButtonsPopupFeedback.TimeoutMs; } - set { VolumeButtonsPopupFeedback.TimeoutMs = value; } - } - - /// - /// The amount of time that the volume gauge stays on screen, in ms - /// - public uint VolumeGaugePopupTimeout - { - get { return VolumeGaugeFeedback.TimeoutMs; } - set { VolumeGaugeFeedback.TimeoutMs = value; } - } - - /// - /// - /// - public uint PowerOffTimeout { get; set; } - - /// - /// - /// - public string DefaultRoomKey - { - get { return _DefaultRoomKey; } - set - { - _DefaultRoomKey = value; - //CurrentRoom = DeviceManager.GetDeviceForKey(value) as EssentialsHuddleSpaceRoom; - } - } - string _DefaultRoomKey; - - /// - /// Indicates that the SetHeaderButtons method has completed successfully - /// - public bool HeaderButtonsAreSetUp { get; private set; } - - /// - /// - /// - public EssentialsHuddleSpaceRoom CurrentRoom - { - get { return _CurrentRoom; } - set - { - SetCurrentRoom(value); - } - } - EssentialsHuddleSpaceRoom _CurrentRoom; - - /// - /// - /// - //uint CurrentInterlockedModalJoin; - - /// - /// For hitting feedback - /// - BoolInputSig ShareButtonSig; - BoolInputSig EndMeetingButtonSig; - - /// - /// Controls the extended period that the volume gauge shows on-screen, - /// as triggered by Volume up/down operations - /// - BoolFeedbackPulseExtender VolumeGaugeFeedback; - - /// - /// Controls the period that the volume buttons show on non-hard-button - /// interfaces - /// - BoolFeedbackPulseExtender VolumeButtonsPopupFeedback; - - /// - /// The parent driver for this - /// - PanelDriverBase Parent; - - /// - /// All children attached to this driver. For hiding and showing as a group. - /// - List ChildDrivers = new List(); - - List CurrentDisplayModeSigsInUse = new List(); - - //// Important smart objects - - /// - /// Smart Object 3200 - /// - SubpageReferenceList SourcesSrl; - - /// - /// Smart Object 15022 - /// - SubpageReferenceList ActivityFooterSrl; - - /// - /// Tracks which audio page group the UI is in - /// - UiDisplayMode CurrentDisplayMode; - - /// - /// The AV page mangagers that have been used, to keep them alive for later - /// - Dictionary PageManagers = new Dictionary(); - - /// - /// Current page manager running for a source - /// - PageManager CurrentSourcePageManager; - - /// - /// Will auto-timeout a power off - /// - CTimer PowerOffTimer; - - ModalDialog PowerDownModal; - - public JoinedSigInterlock PopupInterlock { get; private set; } - - /// - /// The driver for the tech page. Lazy getter for memory usage - /// - PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver TechDriver - { - get - { - if (_TechDriver == null) - _TechDriver = new PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver(TriList, CurrentRoom.PropertiesConfig.Tech); - return _TechDriver; - } - } - PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver _TechDriver; - - - /// - /// Controls timeout of notification ribbon timer - /// - CTimer RibbonTimer; - - /// - /// Constructor - /// - public EssentialsHuddlePanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) - : base(parent.TriList) - { - Config = config; - Parent = parent; - PopupInterlock = new JoinedSigInterlock(TriList); - - SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); - ActivityFooterSrl = new SubpageReferenceList(TriList, 15022, 3, 3, 3); - ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); - - SetupActivityFooterWhenRoomOff(); - - ShowVolumeGauge = true; - - // One-second pulse extender for volume gauge - VolumeGaugeFeedback = new BoolFeedbackPulseExtender(1500); - VolumeGaugeFeedback.Feedback - .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeGaugePopupVisible]); - - VolumeButtonsPopupFeedback = new BoolFeedbackPulseExtender(4000); - VolumeButtonsPopupFeedback.Feedback - .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeButtonPopupVisible]); - - PowerOffTimeout = 30000; - - TriList.StringInput[UIStringJoin.StartActivityText].StringValue = - "Tap Share to begin"; - } - - /// - /// - /// - public override void Show() - { - if (CurrentRoom == null) - { - Debug.Console(1, "ERROR: AVUIFunctionsDriver, Cannot show. No room assigned"); - return; - } - - var roomConf = CurrentRoom.PropertiesConfig; - - if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Habanero) - { - TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => - PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); - } - else if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Verbose) - { - // room name on join 1, concat phone and sip on join 2, no button method - //var addr = roomConf.Addresses; - //if (addr == null) // protect from missing values by using default empties - // addr = new EssentialsRoomAddressPropertiesConfig(); - //// empty string when either missing, pipe when both showing - //TriList.SetString(UIStringJoin.RoomAddressPipeText, - // (string.IsNullOrEmpty(addr.PhoneNumber.Trim()) - // || string.IsNullOrEmpty(addr.SipAddress.Trim())) ? "" : " | "); - //TriList.SetString(UIStringJoin.RoomPhoneText, addr.PhoneNumber); - //TriList.SetString(UIStringJoin.RoomSipText, addr.SipAddress); - } - - TriList.SetBool(UIBoolJoin.DateAndTimeVisible, Config.ShowDate && Config.ShowTime); - TriList.SetBool(UIBoolJoin.DateOnlyVisible, Config.ShowDate && !Config.ShowTime); - TriList.SetBool(UIBoolJoin.TimeOnlyVisible, !Config.ShowDate && Config.ShowTime); - - TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true); - TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = true; - - // Default to showing rooms/sources now. - if (CurrentRoom.OnFeedback.BoolValue) - { - TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); - SetupActivityFooterWhenRoomOn(); - } - else - { - TriList.SetBool(UIBoolJoin.StartPageVisible, true); - TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); - SetupActivityFooterWhenRoomOff(); - } - ShowCurrentDisplayModeSigsInUse(); - - // Attach actions - TriList.SetSigFalseAction(UIBoolJoin.VolumeButtonPopupPress, VolumeButtonsTogglePress); - - // Generic "close" button for popup modals - TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); - - // Volume related things - TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => CurrentRoom.SetDefaultLevels()); - TriList.SetString(UIStringJoin.AdvancedVolumeSlider1Text, "Room"); - - //TriList.SetSigFalseAction(UIBoolJoin.RoomHeaderButtonPress, () => - // ShowInterlockedModal(UIBoolJoin.RoomHeaderPageVisible)); - - - //if(TriList is CrestronApp) - // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; - //else - // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; - - // power-related functions - // Note: some of these are not directly-related to the huddle space UI, but are held over - // in case - TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, EndMeetingPress); - - TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => - { - if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) - (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); - }); - - base.Show(); - } - - /// - /// - /// - public void EndMeetingPress() - { - if (!CurrentRoom.OnFeedback.BoolValue - || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) - return; - - CurrentRoom.StartShutdown(eShutdownType.Manual); - } - - /// - /// Reveals the tech page and puts away anything that's in the way. - /// - public void ShowTech() - { - PopupInterlock.HideAndClear(); - TechDriver.Show(); - } - - /// - /// - /// - void ShowLogo() - { - if (CurrentRoom.LogoUrl == null) - { - TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); - TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); - } - else - { - TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); - TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); - TriList.SetString(UIStringJoin.LogoUrl, _CurrentRoom.LogoUrl); - } - } - - /// - /// - /// - void HideLogo() - { - TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); - TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); - } - - /// - /// - /// - public override void Hide() - { - HideAndClearCurrentDisplayModeSigsInUse(); - TriList.BooleanInput[UIBoolJoin.TopBarHabaneroDynamicVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - //TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; - VolumeButtonsPopupFeedback.ClearNow(); - //CancelPowerOff(); - - base.Hide(); - } - - /// - /// Reveals a message on the notification ribbon until cleared - /// - /// Text to display - /// Time in ms to display. 0 to keep on screen - public void ShowNotificationRibbon(string message, int timeout) - { - TriList.SetString(UIStringJoin.NotificationRibbonText, message); - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, true); - if (timeout > 0) - { - if (RibbonTimer != null) - RibbonTimer.Stop(); - RibbonTimer = new CTimer(o => - { - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); - RibbonTimer = null; - }, timeout); - } - } - - /// - /// Hides the notification ribbon - /// - public void HideNotificationRibbon() - { - TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); - if (RibbonTimer != null) - { - RibbonTimer.Stop(); - RibbonTimer = null; - } - } - - /// - /// Shows the various "modes" that this driver controls. Presentation, Setup page - /// - /// - public void ShowMode(UiDisplayMode mode) - { - //Clear whatever is showing now. - HideAndClearCurrentDisplayModeSigsInUse(); - CurrentDisplayMode = mode; - switch (mode) - { - case UiDisplayMode.PresentationMode: - // show start page or staging... - if (CurrentRoom.OnFeedback.BoolValue) - { - TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - } - // Date/time - if (Config.ShowDate && Config.ShowTime) - { - TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = Config.ShowDate; - TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = Config.ShowTime; - } - - ShowCurrentDisplayModeSigsInUse(); - break; - } - } - - /// - /// When the room is off, set the footer SRL - /// - void SetupActivityFooterWhenRoomOff() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, - b => { if (!b) ShareButtonPressed(); })); - ActivityFooterSrl.Count = 1; - TriList.UShortInput[UIUshortJoin.PresentationStagingCaretMode].UShortValue = 0; - ShareButtonSig.BoolValue = false; - } - - /// - /// Sets up the footer SRL for when the room is on - /// - void SetupActivityFooterWhenRoomOn() - { - ActivityFooterSrl.Clear(); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, - 0, null)); - ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, - 4, b => { if (!b) PowerButtonPressed(); })); - ActivityFooterSrl.Count = 2; - TriList.UShortInput[UIUshortJoin.PresentationStagingCaretMode].UShortValue = 1; - EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(2, 1); - ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; - } - - /// - /// Attached to activity list share button - /// - void ShareButtonPressed() - { - ShareButtonSig.BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; - // Run default source when room is off and share is pressed - if (!CurrentRoom.OnFeedback.BoolValue) - CurrentRoom.RunDefaultPresentRoute(); - } - - - /// - /// Shows all sigs that are in CurrentDisplayModeSigsInUse - /// - void ShowCurrentDisplayModeSigsInUse() - { - foreach (var sig in CurrentDisplayModeSigsInUse) - sig.BoolValue = true; - } - - /// - /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array - /// - void HideAndClearCurrentDisplayModeSigsInUse() - { - foreach (var sig in CurrentDisplayModeSigsInUse) - sig.BoolValue = false; - CurrentDisplayModeSigsInUse.Clear(); - } - - /// - /// Send the UI back depending on location, not used in huddle UI - /// - public override void BackButtonPressed() - { - switch (CurrentDisplayMode) - { - case UiDisplayMode.PresentationMode: - //CancelReturnToSourceTimer(); - BackToHome(); - break; - } - } - - /// - /// - /// - void BackToHome() - { - Hide(); - Parent.Show(); - } - - /// - /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them - /// - void ShowCurrentSource() - { - if (CurrentRoom.CurrentSourceInfo == null) - return; - - var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; - PageManager pm = null; - // If we need a page manager, get an appropriate one - if (uiDev != null) - { - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - // Got an existing page manager, get it - if (PageManagers.ContainsKey(uiDev)) - pm = PageManagers[uiDev]; - // Otherwise make an apporiate one - else if (uiDev is ISetTopBoxControls) - //pm = new SetTopBoxMediumPageManager(uiDev as ISetTopBoxControls, TriList); - pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); - else if (uiDev is IDiscPlayerControls) - pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); - else - pm = new DefaultPageManager(uiDev, TriList); - PageManagers[uiDev] = pm; - CurrentSourcePageManager = pm; - pm.Show(); - } - } - - /// - /// Called from button presses on source, where We can assume we want - /// to change to the proper screen. - /// - /// The key name of the route to run - void UiSelectSource(string key) - { - // Run the route and when it calls back, show the source - CurrentRoom.RunRouteAction(key, null); - } - - /// - /// - /// - public void PowerButtonPressed() - { - if (!CurrentRoom.OnFeedback.BoolValue - || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) - return; - - CurrentRoom.StartShutdown(eShutdownType.Manual); - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) - { - // Do we need to check where the UI is? No? - var timer = CurrentRoom.ShutdownPromptTimer; - EndMeetingButtonSig.BoolValue = true; - ShareButtonSig.BoolValue = false; - - if (CurrentRoom.ShutdownType == eShutdownType.Manual || CurrentRoom.ShutdownType == eShutdownType.Vacancy) - { - PowerDownModal = new ModalDialog(TriList); - var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); - - // Attach timer things to modal - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; - - // respond to offs by cancelling dialog - var onFb = CurrentRoom.OnFeedback; - EventHandler offHandler = null; - offHandler = (o, a) => - { - if (!onFb.BoolValue) - { - EndMeetingButtonSig.BoolValue = false; - PowerDownModal.HideDialog(); - onFb.OutputChange -= offHandler; - //gauge.OutputChange -= gaugeHandler; - } - }; - onFb.OutputChange += offHandler; - - PowerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, - but => - { - if (but != 2) // any button except for End cancels - timer.Cancel(); - else - timer.Finish(); - }); - } - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) - { - EndMeetingButtonSig.BoolValue = false; - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - } - - /// - /// - /// - /// - /// - void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) - { - if (PowerDownModal != null) - PowerDownModal.HideDialog(); - EndMeetingButtonSig.BoolValue = false; - ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; - - CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; - CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; - } - - void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) - { - - var message = string.Format("Meeting will end in {0} seconds", (sender as StringFeedback).StringValue); - TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; - } - - void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) - { - var value = (ushort)((sender as IntFeedback).UShortValue * 65535 / 100); - TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; - } - - /// - /// - /// - void CancelPowerOffTimer() - { - if (PowerOffTimer != null) - { - PowerOffTimer.Stop(); - PowerOffTimer = null; - } - } - - /// - /// - /// - void VolumeButtonsTogglePress() - { - if (VolumeButtonsPopupFeedback.BoolValue) - VolumeButtonsPopupFeedback.ClearNow(); - else - { - // Trigger the popup - VolumeButtonsPopupFeedback.BoolValue = true; - VolumeButtonsPopupFeedback.BoolValue = false; - } - } - - /// - /// - /// - /// - public void VolumeUpPress(bool state) - { - // extend timeouts - if (ShowVolumeGauge) - VolumeGaugeFeedback.BoolValue = state; - VolumeButtonsPopupFeedback.BoolValue = state; - if (CurrentRoom.CurrentVolumeControls != null) - CurrentRoom.CurrentVolumeControls.VolumeUp(state); - } - - /// - /// - /// - /// - public void VolumeDownPress(bool state) - { - // extend timeouts - if (ShowVolumeGauge) - VolumeGaugeFeedback.BoolValue = state; - VolumeButtonsPopupFeedback.BoolValue = state; - if (CurrentRoom.CurrentVolumeControls != null) - CurrentRoom.CurrentVolumeControls.VolumeDown(state); - } - - - /// - /// Helper for property setter. Sets the panel to the given room, latching up all functionality - /// - public void RefreshCurrentRoom(EssentialsHuddleSpaceRoom room) - { - if (_CurrentRoom != null) - { - // Disconnect current room - _CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange; - ClearAudioDeviceConnections(); - _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange; - DisconnectSource(_CurrentRoom.CurrentSourceInfo); - _CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; - _CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; - _CurrentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; - - _CurrentRoom.OnFeedback.OutputChange -= CurrentRoom_OnFeedback_OutputChange; - _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; - _CurrentRoom.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; - } - - _CurrentRoom = room; - - if (_CurrentRoom != null) - { - // get the source list config and set up the source list - var config = ConfigReader.ConfigObject.SourceLists; - if (config.ContainsKey(_CurrentRoom.SourceListKey)) - { - var srcList = config[_CurrentRoom.SourceListKey]; - // Setup sources list - uint i = 1; // counter for UI list - foreach (var kvp in srcList) - { - var srcConfig = kvp.Value; - if (!srcConfig.IncludeInSourceList) // Skip sources marked this way - continue; - - var actualSource = DeviceManager.GetDeviceForKey(srcConfig.SourceKey) as Device; - if (actualSource == null) - { - Debug.Console(1, "Cannot assign missing source '{0}' to source UI list", - srcConfig.SourceKey); - continue; - } - var routeKey = kvp.Key; - var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, - b => { if (!b) UiSelectSource(routeKey); }); - SourcesSrl.AddItem(item); // add to the SRL - item.RegisterForSourceChange(_CurrentRoom); - } - SourcesSrl.Count = (ushort)(i - 1); - } - // Name and logo - TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; - if (_CurrentRoom.LogoUrl == null) - { - TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = false; - } - else - { - TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = true; - TriList.StringInput[UIStringJoin.LogoUrl].StringValue = _CurrentRoom.LogoUrl; - } - - // Shutdown timer - _CurrentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; - _CurrentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; - _CurrentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; - - // Link up all the change events from the room - _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; - CurrentRoom_SyncOnFeedback(); - _CurrentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; - _CurrentRoom.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; - - _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; - RefreshAudioDeviceConnections(); - _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; - RefreshSourceInfo(); - - (Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom); - } - else - { - // Clear sigs that need to be - TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; - } - } - - void SetCurrentRoom(EssentialsHuddleSpaceRoom room) - { - if (_CurrentRoom == room) return; - // Disconnect current (probably never called) - - room.ConfigChanged -= room_ConfigChanged; - room.ConfigChanged += room_ConfigChanged; - - RefreshCurrentRoom(room); - } - - /// - /// Fires when room config of current room has changed. Meant to refresh room values to propegate any updates to UI - /// - /// - /// - void room_ConfigChanged(object sender, EventArgs e) - { - RefreshCurrentRoom(_CurrentRoom); - } - - /// - /// For room on/off changes - /// - void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) - { - CurrentRoom_SyncOnFeedback(); - } - - void CurrentRoom_SyncOnFeedback() - { - var value = _CurrentRoom.OnFeedback.BoolValue; - //Debug.Console(2, CurrentRoom, "UI: Is on event={0}", value); - TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; - - if (value) //ON - { - SetupActivityFooterWhenRoomOn(); - TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; - - } - else - { - SetupActivityFooterWhenRoomOff(); - ShowLogo(); - TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; - TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; - TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; - } - } - - /// - /// - /// - void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) - { - if (CurrentRoom.IsWarmingUpFeedback.BoolValue) - { - ShowNotificationRibbon("Room is powering on. Please wait...", 0); - } - else - { - ShowNotificationRibbon("Room is powered on. Welcome.", 2000); - } - } - - - void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) - { - if (CurrentRoom.IsCoolingDownFeedback.BoolValue) - { - ShowNotificationRibbon("Room is powering off. Please wait.", 0); - } - else - { - HideNotificationRibbon(); - } - } - - /// - /// Hides source for provided source info - /// - /// - void DisconnectSource(SourceListItem previousInfo) - { - if (previousInfo == null) return; - - // Hide whatever is showing - if (IsVisible) - { - if (CurrentSourcePageManager != null) - { - CurrentSourcePageManager.Hide(); - CurrentSourcePageManager = null; - } - } - - if (previousInfo == null) return; - var previousDev = previousInfo.SourceDevice; - - // device type interfaces - if (previousDev is ISetTopBoxControls) - (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); - // common interfaces - if (previousDev is IChannel) - (previousDev as IChannel).UnlinkButtons(TriList); - if (previousDev is IColor) - (previousDev as IColor).UnlinkButtons(TriList); - if (previousDev is IDPad) - (previousDev as IDPad).UnlinkButtons(TriList); - if (previousDev is IDvr) - (previousDev as IDvr).UnlinkButtons(TriList); - if (previousDev is INumericKeypad) - (previousDev as INumericKeypad).UnlinkButtons(TriList); - if (previousDev is IPower) - (previousDev as IPower).UnlinkButtons(TriList); - if (previousDev is ITransport) - (previousDev as ITransport).UnlinkButtons(TriList); - //if (previousDev is IRadio) - // (previousDev as IRadio).UnlinkButtons(this); - } - - /// - /// Refreshes and shows the room's current source - /// - void RefreshSourceInfo() - { - var routeInfo = CurrentRoom.CurrentSourceInfo; - // This will show off popup too - if (this.IsVisible) - ShowCurrentSource(); - - if (routeInfo == null)// || !CurrentRoom.OnFeedback.BoolValue) - { - // Check for power off and insert "Room is off" - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; - this.Hide(); - Parent.Show(); - return; - } - else if (CurrentRoom.CurrentSourceInfo != null) - { - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" - } - else - { - TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "---"; - TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Blank"; - } - - // Connect controls - if (routeInfo.SourceDevice != null) - ConnectControlDeviceMethods(routeInfo.SourceDevice); - } - - /// - /// Attach the source to the buttons and things - /// - void ConnectControlDeviceMethods(Device dev) - { - if(dev is ISetTopBoxControls) - (dev as ISetTopBoxControls).LinkButtons(TriList); - if (dev is IChannel) - (dev as IChannel).LinkButtons(TriList); - if (dev is IColor) - (dev as IColor).LinkButtons(TriList); - if (dev is IDPad) - (dev as IDPad).LinkButtons(TriList); - if (dev is IDvr) - (dev as IDvr).LinkButtons(TriList); - if (dev is INumericKeypad) - (dev as INumericKeypad).LinkButtons(TriList); - if (dev is IPower) - (dev as IPower).LinkButtons(TriList); - if (dev is ITransport) - (dev as ITransport).LinkButtons(TriList); - //if (dev is IRadio) - // (dev as IRadio).LinkButtons(this); // +++++++++++++ Make part of this into page manager - - //if (dev is ICustomFunctions) - //{ - // var custBridge = (dev as ICustomFunctions).GetCustomBridge(); - // custBridge.Link(this.Remote); - } - - /// - /// Detaches the buttons and feedback from the room's current audio device - /// - void ClearAudioDeviceConnections() - { - TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); - TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); - TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); - - var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; - if (fDev != null) - { - TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); - fDev.VolumeLevelFeedback.UnlinkInputSig( - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); - } - } - - /// - /// Attaches the buttons and feedback to the room's current audio device - /// - void RefreshAudioDeviceConnections() - { - var dev = CurrentRoom.CurrentVolumeControls; - if (dev != null) // connect buttons - { - TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); - TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); - TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); - } - - var fbDev = dev as IBasicVolumeWithFeedback; - if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; - else - { - // slider - TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); - // feedbacks - fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); - fbDev.VolumeLevelFeedback.LinkInputSig( - TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); - } - } - - /// - /// Handler for when the room's volume control device changes - /// - void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) - { - if (args.Type == ChangeType.WillChange) - ClearAudioDeviceConnections(); - else // did change - RefreshAudioDeviceConnections(); - } - - /// - /// Handles source change - /// - void CurrentRoom_SourceInfoChange(EssentialsRoomBase room, - SourceListItem info, ChangeType change) - { - if (change == ChangeType.WillChange) - DisconnectSource(info); - else - RefreshSourceInfo(); - } - } +using System; +using System.Collections.Generic; +using Crestron.SimplSharp; +using Crestron.SimplSharpPro; +using Crestron.SimplSharpPro.UI; + +using PepperDash.Core; +using PepperDash.Essentials.Core; +using PepperDash.Essentials.Core.Config; +using PepperDash.Essentials.Core.SmartObjects; +using PepperDash.Essentials.Core.PageManagers; + +namespace PepperDash.Essentials +{ + /// + /// + /// + public class EssentialsHuddlePanelAvFunctionsDriver : PanelDriverBase, IAVDriver + { + CrestronTouchpanelPropertiesConfig Config; + + public enum UiDisplayMode + { + PresentationMode, AudioSetup + } + + /// + /// Whether volume ramping from this panel will show the volume + /// gauge popup. + /// + public bool ShowVolumeGauge { get; set; } + + /// + /// The amount of time that the volume buttons stays on screen, in ms + /// + public uint VolumeButtonPopupTimeout + { + get { return VolumeButtonsPopupFeedback.TimeoutMs; } + set { VolumeButtonsPopupFeedback.TimeoutMs = value; } + } + + /// + /// The amount of time that the volume gauge stays on screen, in ms + /// + public uint VolumeGaugePopupTimeout + { + get { return VolumeGaugeFeedback.TimeoutMs; } + set { VolumeGaugeFeedback.TimeoutMs = value; } + } + + /// + /// + /// + public uint PowerOffTimeout { get; set; } + + /// + /// + /// + public string DefaultRoomKey + { + get { return _DefaultRoomKey; } + set + { + _DefaultRoomKey = value; + //CurrentRoom = DeviceManager.GetDeviceForKey(value) as EssentialsHuddleSpaceRoom; + } + } + string _DefaultRoomKey; + + /// + /// Indicates that the SetHeaderButtons method has completed successfully + /// + public bool HeaderButtonsAreSetUp { get; private set; } + + /// + /// + /// + public EssentialsHuddleSpaceRoom CurrentRoom + { + get { return _CurrentRoom; } + set + { + SetCurrentRoom(value); + } + } + EssentialsHuddleSpaceRoom _CurrentRoom; + + /// + /// + /// + //uint CurrentInterlockedModalJoin; + + /// + /// For hitting feedback + /// + BoolInputSig ShareButtonSig; + BoolInputSig EndMeetingButtonSig; + + /// + /// Controls the extended period that the volume gauge shows on-screen, + /// as triggered by Volume up/down operations + /// + BoolFeedbackPulseExtender VolumeGaugeFeedback; + + /// + /// Controls the period that the volume buttons show on non-hard-button + /// interfaces + /// + BoolFeedbackPulseExtender VolumeButtonsPopupFeedback; + + /// + /// The parent driver for this + /// + PanelDriverBase Parent; + + /// + /// All children attached to this driver. For hiding and showing as a group. + /// + List ChildDrivers = new List(); + + List CurrentDisplayModeSigsInUse = new List(); + + //// Important smart objects + + /// + /// Smart Object 3200 + /// + SubpageReferenceList SourcesSrl; + + /// + /// Smart Object 15022 + /// + SubpageReferenceList ActivityFooterSrl; + + /// + /// Tracks which audio page group the UI is in + /// + UiDisplayMode CurrentDisplayMode; + + /// + /// The AV page mangagers that have been used, to keep them alive for later + /// + Dictionary PageManagers = new Dictionary(); + + /// + /// Current page manager running for a source + /// + PageManager CurrentSourcePageManager; + + /// + /// Will auto-timeout a power off + /// + CTimer PowerOffTimer; + + ModalDialog PowerDownModal; + + public JoinedSigInterlock PopupInterlock { get; private set; } + + /// + /// The driver for the tech page. Lazy getter for memory usage + /// + PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver TechDriver + { + get + { + if (_TechDriver == null) + _TechDriver = new PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver(TriList, CurrentRoom.PropertiesConfig.Tech); + return _TechDriver; + } + } + PepperDash.Essentials.UIDrivers.EssentialsHuddleTechPageDriver _TechDriver; + + + /// + /// Controls timeout of notification ribbon timer + /// + CTimer RibbonTimer; + + /// + /// Constructor + /// + public EssentialsHuddlePanelAvFunctionsDriver(PanelDriverBase parent, CrestronTouchpanelPropertiesConfig config) + : base(parent.TriList) + { + Config = config; + Parent = parent; + PopupInterlock = new JoinedSigInterlock(TriList); + + SourcesSrl = new SubpageReferenceList(TriList, 3200, 3, 3, 3); + ActivityFooterSrl = new SubpageReferenceList(TriList, 15022, 3, 3, 3); + ShareButtonSig = ActivityFooterSrl.BoolInputSig(1, 1); + + SetupActivityFooterWhenRoomOff(); + + ShowVolumeGauge = true; + + // One-second pulse extender for volume gauge + VolumeGaugeFeedback = new BoolFeedbackPulseExtender(1500); + VolumeGaugeFeedback.Feedback + .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeGaugePopupVisible]); + + VolumeButtonsPopupFeedback = new BoolFeedbackPulseExtender(4000); + VolumeButtonsPopupFeedback.Feedback + .LinkInputSig(TriList.BooleanInput[UIBoolJoin.VolumeButtonPopupVisible]); + + PowerOffTimeout = 30000; + + TriList.StringInput[UIStringJoin.StartActivityText].StringValue = + "Tap Share to begin"; + } + + /// + /// + /// + public override void Show() + { + if (CurrentRoom == null) + { + Debug.Console(1, "ERROR: AVUIFunctionsDriver, Cannot show. No room assigned"); + return; + } + + var roomConf = CurrentRoom.PropertiesConfig; + + if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Habanero) + { + TriList.SetSigFalseAction(UIBoolJoin.HeaderRoomButtonPress, () => + PopupInterlock.ShowInterlockedWithToggle(UIBoolJoin.RoomHeaderPageVisible)); + } + else if (Config.HeaderStyle.ToLower() == CrestronTouchpanelPropertiesConfig.Verbose) + { + // room name on join 1, concat phone and sip on join 2, no button method + //var addr = roomConf.Addresses; + //if (addr == null) // protect from missing values by using default empties + // addr = new EssentialsRoomAddressPropertiesConfig(); + //// empty string when either missing, pipe when both showing + //TriList.SetString(UIStringJoin.RoomAddressPipeText, + // (string.IsNullOrEmpty(addr.PhoneNumber.Trim()) + // || string.IsNullOrEmpty(addr.SipAddress.Trim())) ? "" : " | "); + //TriList.SetString(UIStringJoin.RoomPhoneText, addr.PhoneNumber); + //TriList.SetString(UIStringJoin.RoomSipText, addr.SipAddress); + } + + TriList.SetBool(UIBoolJoin.DateAndTimeVisible, Config.ShowDate && Config.ShowTime); + TriList.SetBool(UIBoolJoin.DateOnlyVisible, Config.ShowDate && !Config.ShowTime); + TriList.SetBool(UIBoolJoin.TimeOnlyVisible, !Config.ShowDate && Config.ShowTime); + + TriList.SetBool(UIBoolJoin.TopBarHabaneroDynamicVisible, true); + TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = true; + + // Default to showing rooms/sources now. + if (CurrentRoom.OnFeedback.BoolValue) + { + TriList.SetBool(UIBoolJoin.TapToBeginVisible, false); + SetupActivityFooterWhenRoomOn(); + } + else + { + TriList.SetBool(UIBoolJoin.StartPageVisible, true); + TriList.SetBool(UIBoolJoin.TapToBeginVisible, true); + SetupActivityFooterWhenRoomOff(); + } + ShowCurrentDisplayModeSigsInUse(); + + // Attach actions + TriList.SetSigFalseAction(UIBoolJoin.VolumeButtonPopupPress, VolumeButtonsTogglePress); + + // Generic "close" button for popup modals + TriList.SetSigFalseAction(UIBoolJoin.InterlockedModalClosePress, PopupInterlock.HideAndClear); + + // Volume related things + TriList.SetSigFalseAction(UIBoolJoin.VolumeDefaultPress, () => CurrentRoom.SetDefaultLevels()); + TriList.SetString(UIStringJoin.AdvancedVolumeSlider1Text, "Room"); + + //TriList.SetSigFalseAction(UIBoolJoin.RoomHeaderButtonPress, () => + // ShowInterlockedModal(UIBoolJoin.RoomHeaderPageVisible)); + + + //if(TriList is CrestronApp) + // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = false; + //else + // TriList.BooleanInput[UIBoolJoin.GearButtonVisible].BoolValue = true; + + // power-related functions + // Note: some of these are not directly-related to the huddle space UI, but are held over + // in case + TriList.SetSigFalseAction(UIBoolJoin.ShowPowerOffPress, EndMeetingPress); + + TriList.SetSigFalseAction(UIBoolJoin.DisplayPowerTogglePress, () => + { + if (CurrentRoom != null && CurrentRoom.DefaultDisplay is IPower) + (CurrentRoom.DefaultDisplay as IPower).PowerToggle(); + }); + + base.Show(); + } + + /// + /// + /// + public void EndMeetingPress() + { + if (!CurrentRoom.OnFeedback.BoolValue + || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) + return; + + CurrentRoom.StartShutdown(eShutdownType.Manual); + } + + /// + /// Reveals the tech page and puts away anything that's in the way. + /// + public void ShowTech() + { + PopupInterlock.HideAndClear(); + TechDriver.Show(); + } + + /// + /// + /// + void ShowLogo() + { + if (CurrentRoom.LogoUrl == null) + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, true); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); + } + else + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, true); + TriList.SetString(UIStringJoin.LogoUrl, _CurrentRoom.LogoUrl); + } + } + + /// + /// + /// + void HideLogo() + { + TriList.SetBool(UIBoolJoin.LogoDefaultVisible, false); + TriList.SetBool(UIBoolJoin.LogoUrlVisible, false); + } + + /// + /// + /// + public override void Hide() + { + HideAndClearCurrentDisplayModeSigsInUse(); + TriList.BooleanInput[UIBoolJoin.TopBarHabaneroDynamicVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.ActivityFooterVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + //TriList.BooleanInput[UIBoolJoin.StagingPageVisible].BoolValue = false; + VolumeButtonsPopupFeedback.ClearNow(); + //CancelPowerOff(); + + base.Hide(); + } + + /// + /// Reveals a message on the notification ribbon until cleared + /// + /// Text to display + /// Time in ms to display. 0 to keep on screen + public void ShowNotificationRibbon(string message, int timeout) + { + TriList.SetString(UIStringJoin.NotificationRibbonText, message); + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, true); + if (timeout > 0) + { + if (RibbonTimer != null) + RibbonTimer.Stop(); + RibbonTimer = new CTimer(o => + { + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); + RibbonTimer = null; + }, timeout); + } + } + + /// + /// Hides the notification ribbon + /// + public void HideNotificationRibbon() + { + TriList.SetBool(UIBoolJoin.NotificationRibbonVisible, false); + if (RibbonTimer != null) + { + RibbonTimer.Stop(); + RibbonTimer = null; + } + } + + /// + /// Shows the various "modes" that this driver controls. Presentation, Setup page + /// + /// + public void ShowMode(UiDisplayMode mode) + { + //Clear whatever is showing now. + HideAndClearCurrentDisplayModeSigsInUse(); + CurrentDisplayMode = mode; + switch (mode) + { + case UiDisplayMode.PresentationMode: + // show start page or staging... + if (CurrentRoom.OnFeedback.BoolValue) + { + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.TapToBeginVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + } + // Date/time + if (Config.ShowDate && Config.ShowTime) + { + TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.DateAndTimeVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.DateOnlyVisible].BoolValue = Config.ShowDate; + TriList.BooleanInput[UIBoolJoin.TimeOnlyVisible].BoolValue = Config.ShowTime; + } + + ShowCurrentDisplayModeSigsInUse(); + break; + } + } + + /// + /// When the room is off, set the footer SRL + /// + void SetupActivityFooterWhenRoomOff() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, 0, + b => { if (!b) ShareButtonPressed(); })); + ActivityFooterSrl.Count = 1; + TriList.UShortInput[UIUshortJoin.PresentationStagingCaretMode].UShortValue = 0; + ShareButtonSig.BoolValue = false; + } + + /// + /// Sets up the footer SRL for when the room is on + /// + void SetupActivityFooterWhenRoomOn() + { + ActivityFooterSrl.Clear(); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(1, ActivityFooterSrl, + 0, null)); + ActivityFooterSrl.AddItem(new SubpageReferenceListActivityItem(2, ActivityFooterSrl, + 4, b => { if (!b) PowerButtonPressed(); })); + ActivityFooterSrl.Count = 2; + TriList.UShortInput[UIUshortJoin.PresentationStagingCaretMode].UShortValue = 1; + EndMeetingButtonSig = ActivityFooterSrl.BoolInputSig(2, 1); + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + } + + /// + /// Attached to activity list share button + /// + void ShareButtonPressed() + { + ShareButtonSig.BoolValue = true; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = true; + // Run default source when room is off and share is pressed + if (!CurrentRoom.OnFeedback.BoolValue) + CurrentRoom.RunDefaultPresentRoute(); + } + + + /// + /// Shows all sigs that are in CurrentDisplayModeSigsInUse + /// + void ShowCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = true; + } + + /// + /// Hides all CurrentDisplayModeSigsInUse sigs and clears the array + /// + void HideAndClearCurrentDisplayModeSigsInUse() + { + foreach (var sig in CurrentDisplayModeSigsInUse) + sig.BoolValue = false; + CurrentDisplayModeSigsInUse.Clear(); + } + + /// + /// Send the UI back depending on location, not used in huddle UI + /// + public override void BackButtonPressed() + { + switch (CurrentDisplayMode) + { + case UiDisplayMode.PresentationMode: + //CancelReturnToSourceTimer(); + BackToHome(); + break; + } + } + + /// + /// + /// + void BackToHome() + { + Hide(); + Parent.Show(); + } + + /// + /// Loads the appropriate Sigs into CurrentDisplayModeSigsInUse and shows them + /// + void ShowCurrentSource() + { + if (CurrentRoom.CurrentSourceInfo == null) + return; + + var uiDev = CurrentRoom.CurrentSourceInfo.SourceDevice as IUiDisplayInfo; + PageManager pm = null; + // If we need a page manager, get an appropriate one + if (uiDev != null) + { + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + // Got an existing page manager, get it + if (PageManagers.ContainsKey(uiDev)) + pm = PageManagers[uiDev]; + // Otherwise make an apporiate one + else if (uiDev is ISetTopBoxControls) + //pm = new SetTopBoxMediumPageManager(uiDev as ISetTopBoxControls, TriList); + pm = new SetTopBoxThreePanelPageManager(uiDev as ISetTopBoxControls, TriList); + else if (uiDev is IDiscPlayerControls) + pm = new DiscPlayerMediumPageManager(uiDev as IDiscPlayerControls, TriList); + else + pm = new DefaultPageManager(uiDev, TriList); + PageManagers[uiDev] = pm; + CurrentSourcePageManager = pm; + pm.Show(); + } + } + + /// + /// Called from button presses on source, where We can assume we want + /// to change to the proper screen. + /// + /// The key name of the route to run + void UiSelectSource(string key) + { + // Run the route and when it calls back, show the source + CurrentRoom.RunRouteAction(key, null); + } + + /// + /// + /// + public void PowerButtonPressed() + { + if (!CurrentRoom.OnFeedback.BoolValue + || CurrentRoom.ShutdownPromptTimer.IsRunningFeedback.BoolValue) + return; + + CurrentRoom.StartShutdown(eShutdownType.Manual); + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasStarted(object sender, EventArgs e) + { + // Do we need to check where the UI is? No? + var timer = CurrentRoom.ShutdownPromptTimer; + EndMeetingButtonSig.BoolValue = true; + ShareButtonSig.BoolValue = false; + + if (CurrentRoom.ShutdownType == eShutdownType.Manual || CurrentRoom.ShutdownType == eShutdownType.Vacancy) + { + PowerDownModal = new ModalDialog(TriList); + var message = string.Format("Meeting will end in {0} seconds", CurrentRoom.ShutdownPromptSeconds); + + // Attach timer things to modal + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange += ShutdownPromptTimer_PercentFeedback_OutputChange; + + // respond to offs by cancelling dialog + var onFb = CurrentRoom.OnFeedback; + EventHandler offHandler = null; + offHandler = (o, a) => + { + if (!onFb.BoolValue) + { + EndMeetingButtonSig.BoolValue = false; + PowerDownModal.HideDialog(); + onFb.OutputChange -= offHandler; + //gauge.OutputChange -= gaugeHandler; + } + }; + onFb.OutputChange += offHandler; + + PowerDownModal.PresentModalDialog(2, "End Meeting", "Power", message, "Cancel", "End Meeting Now", true, true, + but => + { + if (but != 2) // any button except for End cancels + timer.Cancel(); + else + timer.Finish(); + }); + } + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_HasFinished(object sender, EventArgs e) + { + EndMeetingButtonSig.BoolValue = false; + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange -= ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + /// + /// + /// + /// + /// + void ShutdownPromptTimer_WasCancelled(object sender, EventArgs e) + { + if (PowerDownModal != null) + PowerDownModal.HideDialog(); + EndMeetingButtonSig.BoolValue = false; + ShareButtonSig.BoolValue = CurrentRoom.OnFeedback.BoolValue; + + CurrentRoom.ShutdownPromptTimer.TimeRemainingFeedback.OutputChange += ShutdownPromptTimer_TimeRemainingFeedback_OutputChange; + CurrentRoom.ShutdownPromptTimer.PercentFeedback.OutputChange -= ShutdownPromptTimer_PercentFeedback_OutputChange; + } + + void ShutdownPromptTimer_TimeRemainingFeedback_OutputChange(object sender, EventArgs e) + { + + var message = string.Format("Meeting will end in {0} seconds", (sender as StringFeedback).StringValue); + TriList.StringInput[ModalDialog.MessageTextJoin].StringValue = message; + } + + void ShutdownPromptTimer_PercentFeedback_OutputChange(object sender, EventArgs e) + { + var value = (ushort)((sender as IntFeedback).UShortValue * 65535 / 100); + TriList.UShortInput[ModalDialog.TimerGaugeJoin].UShortValue = value; + } + + /// + /// + /// + void CancelPowerOffTimer() + { + if (PowerOffTimer != null) + { + PowerOffTimer.Stop(); + PowerOffTimer = null; + } + } + + /// + /// + /// + void VolumeButtonsTogglePress() + { + if (VolumeButtonsPopupFeedback.BoolValue) + VolumeButtonsPopupFeedback.ClearNow(); + else + { + // Trigger the popup + VolumeButtonsPopupFeedback.BoolValue = true; + VolumeButtonsPopupFeedback.BoolValue = false; + } + } + + /// + /// + /// + /// + public void VolumeUpPress(bool state) + { + // extend timeouts + if (ShowVolumeGauge) + VolumeGaugeFeedback.BoolValue = state; + VolumeButtonsPopupFeedback.BoolValue = state; + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeUp(state); + } + + /// + /// + /// + /// + public void VolumeDownPress(bool state) + { + // extend timeouts + if (ShowVolumeGauge) + VolumeGaugeFeedback.BoolValue = state; + VolumeButtonsPopupFeedback.BoolValue = state; + if (CurrentRoom.CurrentVolumeControls != null) + CurrentRoom.CurrentVolumeControls.VolumeDown(state); + } + + + /// + /// Helper for property setter. Sets the panel to the given room, latching up all functionality + /// + public void RefreshCurrentRoom(EssentialsHuddleSpaceRoom room) + { + if (_CurrentRoom != null) + { + // Disconnect current room + _CurrentRoom.CurrentVolumeDeviceChange -= this.CurrentRoom_CurrentAudioDeviceChange; + ClearAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange -= this.CurrentRoom_SourceInfoChange; + DisconnectSource(_CurrentRoom.CurrentSourceInfo); + _CurrentRoom.ShutdownPromptTimer.HasStarted -= ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished -= ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled -= ShutdownPromptTimer_WasCancelled; + + _CurrentRoom.OnFeedback.OutputChange -= CurrentRoom_OnFeedback_OutputChange; + _CurrentRoom.IsWarmingUpFeedback.OutputChange -= CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange -= IsCoolingDownFeedback_OutputChange; + } + + _CurrentRoom = room; + + if (_CurrentRoom != null) + { + // get the source list config and set up the source list + var config = ConfigReader.ConfigObject.SourceLists; + if (config.ContainsKey(_CurrentRoom.SourceListKey)) + { + var srcList = config[_CurrentRoom.SourceListKey]; + // Setup sources list + uint i = 1; // counter for UI list + foreach (var kvp in srcList) + { + var srcConfig = kvp.Value; + if (!srcConfig.IncludeInSourceList) // Skip sources marked this way + continue; + + var actualSource = DeviceManager.GetDeviceForKey(srcConfig.SourceKey) as Device; + if (actualSource == null) + { + Debug.Console(1, "Cannot assign missing source '{0}' to source UI list", + srcConfig.SourceKey); + continue; + } + var routeKey = kvp.Key; + var item = new SubpageReferenceListSourceItem(i++, SourcesSrl, srcConfig, + b => { if (!b) UiSelectSource(routeKey); }); + SourcesSrl.AddItem(item); // add to the SRL + item.RegisterForSourceChange(_CurrentRoom); + } + SourcesSrl.Count = (ushort)(i - 1); + } + // Name and logo + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = _CurrentRoom.Name; + if (_CurrentRoom.LogoUrl == null) + { + TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = false; + } + else + { + TriList.BooleanInput[UIBoolJoin.LogoDefaultVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.LogoUrlVisible].BoolValue = true; + TriList.StringInput[UIStringJoin.LogoUrl].StringValue = _CurrentRoom.LogoUrl; + } + + // Shutdown timer + _CurrentRoom.ShutdownPromptTimer.HasStarted += ShutdownPromptTimer_HasStarted; + _CurrentRoom.ShutdownPromptTimer.HasFinished += ShutdownPromptTimer_HasFinished; + _CurrentRoom.ShutdownPromptTimer.WasCancelled += ShutdownPromptTimer_WasCancelled; + + // Link up all the change events from the room + _CurrentRoom.OnFeedback.OutputChange += CurrentRoom_OnFeedback_OutputChange; + CurrentRoom_SyncOnFeedback(); + _CurrentRoom.IsWarmingUpFeedback.OutputChange += CurrentRoom_IsWarmingFeedback_OutputChange; + _CurrentRoom.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange; + + _CurrentRoom.CurrentVolumeDeviceChange += CurrentRoom_CurrentAudioDeviceChange; + RefreshAudioDeviceConnections(); + _CurrentRoom.CurrentSingleSourceChange += CurrentRoom_SourceInfoChange; + RefreshSourceInfo(); + + (Parent as EssentialsPanelMainInterfaceDriver).HeaderDriver.SetupHeaderButtons(this, CurrentRoom); + } + else + { + // Clear sigs that need to be + TriList.StringInput[UIStringJoin.CurrentRoomName].StringValue = "Select a room"; + } + } + + void SetCurrentRoom(EssentialsHuddleSpaceRoom room) + { + if (_CurrentRoom == room) return; + // Disconnect current (probably never called) + + room.ConfigChanged -= room_ConfigChanged; + room.ConfigChanged += room_ConfigChanged; + + RefreshCurrentRoom(room); + } + + /// + /// Fires when room config of current room has changed. Meant to refresh room values to propegate any updates to UI + /// + /// + /// + void room_ConfigChanged(object sender, EventArgs e) + { + RefreshCurrentRoom(_CurrentRoom); + } + + /// + /// For room on/off changes + /// + void CurrentRoom_OnFeedback_OutputChange(object sender, EventArgs e) + { + CurrentRoom_SyncOnFeedback(); + } + + void CurrentRoom_SyncOnFeedback() + { + var value = _CurrentRoom.OnFeedback.BoolValue; + //Debug.Console(2, CurrentRoom, "UI: Is on event={0}", value); + TriList.BooleanInput[UIBoolJoin.RoomIsOn].BoolValue = value; + + if (value) //ON + { + SetupActivityFooterWhenRoomOn(); + TriList.BooleanInput[UIBoolJoin.SelectASourceVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = true; + + } + else + { + SetupActivityFooterWhenRoomOff(); + ShowLogo(); + TriList.BooleanInput[UIBoolJoin.StartPageVisible].BoolValue = true; + TriList.BooleanInput[UIBoolJoin.VolumeSingleMute1Visible].BoolValue = false; + TriList.BooleanInput[UIBoolJoin.SourceStagingBarVisible].BoolValue = false; + } + } + + /// + /// + /// + void CurrentRoom_IsWarmingFeedback_OutputChange(object sender, EventArgs e) + { + if (CurrentRoom.IsWarmingUpFeedback.BoolValue) + { + ShowNotificationRibbon("Room is powering on. Please wait...", 0); + } + else + { + ShowNotificationRibbon("Room is powered on. Welcome.", 2000); + } + } + + + void IsCoolingDownFeedback_OutputChange(object sender, EventArgs e) + { + if (CurrentRoom.IsCoolingDownFeedback.BoolValue) + { + ShowNotificationRibbon("Room is powering off. Please wait.", 0); + } + else + { + HideNotificationRibbon(); + } + } + + /// + /// Hides source for provided source info + /// + /// + void DisconnectSource(SourceListItem previousInfo) + { + if (previousInfo == null) return; + + // Hide whatever is showing + if (IsVisible) + { + if (CurrentSourcePageManager != null) + { + CurrentSourcePageManager.Hide(); + CurrentSourcePageManager = null; + } + } + + if (previousInfo == null) return; + var previousDev = previousInfo.SourceDevice; + + // device type interfaces + if (previousDev is ISetTopBoxControls) + (previousDev as ISetTopBoxControls).UnlinkButtons(TriList); + // common interfaces + if (previousDev is IChannel) + (previousDev as IChannel).UnlinkButtons(TriList); + if (previousDev is IColor) + (previousDev as IColor).UnlinkButtons(TriList); + if (previousDev is IDPad) + (previousDev as IDPad).UnlinkButtons(TriList); + if (previousDev is IDvr) + (previousDev as IDvr).UnlinkButtons(TriList); + if (previousDev is INumericKeypad) + (previousDev as INumericKeypad).UnlinkButtons(TriList); + if (previousDev is IPower) + (previousDev as IPower).UnlinkButtons(TriList); + if (previousDev is ITransport) + (previousDev as ITransport).UnlinkButtons(TriList); + //if (previousDev is IRadio) + // (previousDev as IRadio).UnlinkButtons(this); + } + + /// + /// Refreshes and shows the room's current source + /// + void RefreshSourceInfo() + { + var routeInfo = CurrentRoom.CurrentSourceInfo; + // This will show off popup too + if (this.IsVisible) + ShowCurrentSource(); + + if (routeInfo == null)// || !CurrentRoom.OnFeedback.BoolValue) + { + // Check for power off and insert "Room is off" + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "Room is off"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Power"; + this.Hide(); + Parent.Show(); + return; + } + else if (CurrentRoom.CurrentSourceInfo != null) + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = routeInfo.PreferredName; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = routeInfo.Icon; // defaults to "blank" + } + else + { + TriList.StringInput[UIStringJoin.CurrentSourceName].StringValue = "---"; + TriList.StringInput[UIStringJoin.CurrentSourceIcon].StringValue = "Blank"; + } + + // Connect controls + if (routeInfo.SourceDevice != null) + ConnectControlDeviceMethods(routeInfo.SourceDevice); + } + + /// + /// Attach the source to the buttons and things + /// + void ConnectControlDeviceMethods(Device dev) + { + if(dev is ISetTopBoxControls) + (dev as ISetTopBoxControls).LinkButtons(TriList); + if (dev is IChannel) + (dev as IChannel).LinkButtons(TriList); + if (dev is IColor) + (dev as IColor).LinkButtons(TriList); + if (dev is IDPad) + (dev as IDPad).LinkButtons(TriList); + if (dev is IDvr) + (dev as IDvr).LinkButtons(TriList); + if (dev is INumericKeypad) + (dev as INumericKeypad).LinkButtons(TriList); + if (dev is IPower) + (dev as IPower).LinkButtons(TriList); + if (dev is ITransport) + (dev as ITransport).LinkButtons(TriList); + //if (dev is IRadio) + // (dev as IRadio).LinkButtons(this); // +++++++++++++ Make part of this into page manager + + //if (dev is ICustomFunctions) + //{ + // var custBridge = (dev as ICustomFunctions).GetCustomBridge(); + // custBridge.Link(this.Remote); + } + + /// + /// Detaches the buttons and feedback from the room's current audio device + /// + void ClearAudioDeviceConnections() + { + TriList.ClearBoolSigAction(UIBoolJoin.VolumeUpPress); + TriList.ClearBoolSigAction(UIBoolJoin.VolumeDownPress); + TriList.ClearBoolSigAction(UIBoolJoin.Volume1ProgramMutePressAndFB); + + var fDev = CurrentRoom.CurrentVolumeControls as IBasicVolumeWithFeedback; + if (fDev != null) + { + TriList.ClearUShortSigAction(UIUshortJoin.VolumeSlider1Value); + fDev.VolumeLevelFeedback.UnlinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Attaches the buttons and feedback to the room's current audio device + /// + void RefreshAudioDeviceConnections() + { + var dev = CurrentRoom.CurrentVolumeControls; + if (dev != null) // connect buttons + { + TriList.SetBoolSigAction(UIBoolJoin.VolumeUpPress, VolumeUpPress); + TriList.SetBoolSigAction(UIBoolJoin.VolumeDownPress, VolumeDownPress); + TriList.SetSigFalseAction(UIBoolJoin.Volume1ProgramMutePressAndFB, dev.MuteToggle); + } + + var fbDev = dev as IBasicVolumeWithFeedback; + if (fbDev == null) // this should catch both IBasicVolume and IBasicVolumeWithFeeback + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value].UShortValue = 0; + else + { + // slider + TriList.SetUShortSigAction(UIUshortJoin.VolumeSlider1Value, fbDev.SetVolume); + // feedbacks + fbDev.MuteFeedback.LinkInputSig(TriList.BooleanInput[UIBoolJoin.Volume1ProgramMutePressAndFB]); + fbDev.VolumeLevelFeedback.LinkInputSig( + TriList.UShortInput[UIUshortJoin.VolumeSlider1Value]); + } + } + + /// + /// Handler for when the room's volume control device changes + /// + void CurrentRoom_CurrentAudioDeviceChange(object sender, VolumeDeviceChangeEventArgs args) + { + if (args.Type == ChangeType.WillChange) + ClearAudioDeviceConnections(); + else // did change + RefreshAudioDeviceConnections(); + } + + /// + /// Handles source change + /// + void CurrentRoom_SourceInfoChange(EssentialsRoomBase room, + SourceListItem info, ChangeType change) + { + if (change == ChangeType.WillChange) + DisconnectSource(info); + else + RefreshSourceInfo(); + } + } } \ No newline at end of file