diff --git a/src/PepperDash.Essentials.MobileControl/MessageToClients.cs b/src/PepperDash.Essentials.MobileControl/MessageToClients.cs new file mode 100644 index 00000000..fe9c9470 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/MessageToClients.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; +using PepperDash.Core; +using PepperDash.Core.Logging; +using PepperDash.Essentials.AppServer.Messengers; +using PepperDash.Essentials.Core.Queues; +using PepperDash.Essentials.WebSocketServer; +using Serilog.Events; + +namespace PepperDash.Essentials +{ + public class MessageToClients : IQueueMessage + { + private readonly MobileControlWebsocketServer _server; + private readonly object msgToSend; + + public MessageToClients(object msg, MobileControlWebsocketServer server) + { + _server = server; + msgToSend = msg; + } + + public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server) + { + _server = server; + msgToSend = msg; + } + + #region Implementation of IQueueMessage + + public void Dispatch() + { + try + { + if (_server == null) + { + Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null"); + return; + } + + var message = JsonConvert.SerializeObject(msgToSend, Formatting.None, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } }); + + var clientSpecificMessage = msgToSend as MobileControlMessage; + if (clientSpecificMessage.ClientId != null) + { + var clientId = clientSpecificMessage.ClientId; + + _server.LogVerbose("Message TX To client {clientId} Message: {message}", clientId, message); + + _server.SendMessageToClient(clientId, message); + + return; + } + + _server.SendMessageToAllClients(message); + + _server.LogVerbose("Message TX To all clients: {message}", message); + } + catch (ThreadAbortException) + { + //Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace + } + catch (Exception ex) + { + Debug.LogMessage(ex, "Caught an exception in the Transmit Processor"); + } + } + #endregion + } + +} \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs index b17e0490..12a53a48 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs @@ -1,4 +1,10 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Crestron.SimplSharp; using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.Net.Http; using Crestron.SimplSharp.WebScripting; @@ -30,12 +36,6 @@ using PepperDash.Essentials.RoomBridges; using PepperDash.Essentials.Services; using PepperDash.Essentials.WebApiHandlers; using PepperDash.Essentials.WebSocketServer; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; using WebSocketSharp; namespace PepperDash.Essentials @@ -570,7 +570,7 @@ namespace PepperDash.Essentials { this.LogVerbose( "Adding ISetTopBoxControlMessenger for {deviceKey}" - ); + ); var messenger = new ISetTopBoxControlsMessenger( $"{device.Key}-stb-{Key}", @@ -587,7 +587,7 @@ namespace PepperDash.Essentials { this.LogVerbose( "Adding IChannelMessenger for {deviceKey}", device.Key - ); + ); var messenger = new IChannelMessenger( $"{device.Key}-channel-{Key}", @@ -602,7 +602,7 @@ namespace PepperDash.Essentials if (device is IColor colorDevice) { - this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key); + this.LogVerbose("Adding IColorMessenger for {deviceKey}", device.Key); var messenger = new IColorMessenger( $"{device.Key}-color-{Key}", @@ -617,7 +617,7 @@ namespace PepperDash.Essentials if (device is IDPad dPadDevice) { - this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key); + this.LogVerbose("Adding IDPadMessenger for {deviceKey}", device.Key); var messenger = new IDPadMessenger( $"{device.Key}-dPad-{Key}", @@ -632,7 +632,7 @@ namespace PepperDash.Essentials if (device is INumericKeypad nkDevice) { - this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key); + this.LogVerbose("Adding INumericKeyapdMessenger for {deviceKey}", device.Key); var messenger = new INumericKeypadMessenger( $"{device.Key}-numericKeypad-{Key}", @@ -647,7 +647,7 @@ namespace PepperDash.Essentials if (device is IHasPowerControl pcDevice) { - this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key); + this.LogVerbose("Adding IHasPowerControlMessenger for {deviceKey}", device.Key); var messenger = new IHasPowerMessenger( $"{device.Key}-powerControl-{Key}", @@ -681,7 +681,7 @@ namespace PepperDash.Essentials { this.LogVerbose( "Adding ITransportMessenger for {deviceKey}", device.Key - ); + ); var messenger = new ITransportMessenger( $"{device.Key}-transport-{Key}", @@ -1619,12 +1619,12 @@ Mobile Control Direct Server Information: Tokens Defined: {0} Clients Connected: {1} ", - _directServer.UiClients.Count, + _directServer.UiClientContexts.Count, _directServer.ConnectedUiClientsCount ); var clientNo = 1; - foreach (var clientContext in _directServer.UiClients) + foreach (var clientContext in _directServer.UiClientContexts) { var isAlive = false; var duration = "Not Connected"; @@ -2238,7 +2238,7 @@ Mobile Control Direct Server Infromation: { this.LogInformation("-- Warning: Incoming message has no registered handler {type}", message.Type); break; - } + } foreach (var handler in handlers) { diff --git a/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs b/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs index b9ca686b..749e497c 100644 --- a/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs +++ b/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs @@ -1,13 +1,10 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; using PepperDash.Core; -using PepperDash.Core.Logging; using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.Core.Queues; -using PepperDash.Essentials.WebSocketServer; using Serilog.Events; -using System; -using System.Threading; using WebSocketSharp; namespace PepperDash.Essentials @@ -65,66 +62,4 @@ namespace PepperDash.Essentials #endregion } - - - public class MessageToClients : IQueueMessage - { - private readonly MobileControlWebsocketServer _server; - private readonly object msgToSend; - - public MessageToClients(object msg, MobileControlWebsocketServer server) - { - _server = server; - msgToSend = msg; - } - - public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server) - { - _server = server; - msgToSend = msg; - } - - #region Implementation of IQueueMessage - - public void Dispatch() - { - try - { - if (_server == null) - { - Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Server is null"); - return; - } - - var message = JsonConvert.SerializeObject(msgToSend, Formatting.None, - new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } }); - - var clientSpecificMessage = msgToSend as MobileControlMessage; - if (clientSpecificMessage.ClientId != null) - { - var clientId = clientSpecificMessage.ClientId; - - _server.LogVerbose("Message TX To client {clientId} Message: {message}", clientId, message); - - _server.SendMessageToClient(clientId, message); - - return; - } - - _server.SendMessageToAllClients(message); - - _server.LogVerbose("Message TX To all clients: {message}", message); - } - catch (ThreadAbortException) - { - //Swallowing this exception, as it occurs on shutdown and there's no need to print out a scary stack trace - } - catch (Exception ex) - { - Debug.LogMessage(ex, "Caught an exception in the Transmit Processor"); - } - } - #endregion - } - } \ No newline at end of file diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs index c8b97d0b..1b4305af 100644 --- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs +++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs @@ -1,12 +1,12 @@ -using Crestron.SimplSharp.WebScripting; +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp.WebScripting; using Newtonsoft.Json; using PepperDash.Core; using PepperDash.Core.Web.RequestHandlers; using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.WebSocketServer; -using System; -using System.Collections.Generic; -using System.Linq; namespace PepperDash.Essentials.WebApiHandlers { @@ -99,13 +99,13 @@ namespace PepperDash.Essentials.WebApiHandlers public int ServerPort => directServer.Port; [JsonProperty("tokensDefined")] - public int TokensDefined => directServer.UiClients.Count; + public int TokensDefined => directServer.UiClientContexts.Count; [JsonProperty("clientsConnected")] public int ClientsConnected => directServer.ConnectedUiClientsCount; [JsonProperty("clients")] - public List Clients => directServer.UiClients.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList(); + public List Clients => directServer.UiClientContexts.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList(); public MobileControlDirectServer(MobileControlWebsocketServer server) { diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs index 73fdb104..ae8d043a 100644 --- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs +++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs @@ -90,7 +90,7 @@ namespace PepperDash.Essentials.WebApiHandlers - if (!server.UiClients.TryGetValue(request.Token, out UiClientContext clientContext)) + if (!server.UiClientContexts.TryGetValue(request.Token, out UiClientContext clientContext)) { var response = new ClientResponse { @@ -131,7 +131,7 @@ namespace PepperDash.Essentials.WebApiHandlers return; } - server.UiClients.Remove(request.Token); + server.UiClientContexts.Remove(request.Token); server.UpdateSecret(); diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs new file mode 100644 index 00000000..7a6463a0 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs @@ -0,0 +1,39 @@ +using System; +using Newtonsoft.Json; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents the structure of the join response + /// + public class JoinResponse + { + [JsonProperty("clientId")] + public string ClientId { get; set; } + + [JsonProperty("roomKey")] + public string RoomKey { get; set; } + + [JsonProperty("systemUUid")] + public string SystemUuid { get; set; } + + [JsonProperty("roomUUid")] + public string RoomUuid { get; set; } + + [JsonProperty("config")] + public object Config { get; set; } + + [JsonProperty("codeExpires")] + public DateTime CodeExpires { get; set; } + + [JsonProperty("userCode")] + public string UserCode { get; set; } + + [JsonProperty("userAppUrl")] + public string UserAppUrl { get; set; } + + [JsonProperty("enableDebug")] + public bool EnableDebug { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs new file mode 100644 index 00000000..e1e6d476 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs @@ -0,0 +1,22 @@ +using Independentsoft.Exchange; + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents a join token with the associated properties + /// + public class JoinToken + { + public string Code { get; set; } + + public string RoomKey { get; set; } + + public string Uuid { get; set; } + + public string TouchpanelKey { get; set; } = ""; + + public string Token { get; set; } = null; + + public string Id { get; set; } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs index 859a7c7b..ba6a8b0f 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs @@ -1,4 +1,10 @@ -using Crestron.SimplSharp; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using Crestron.SimplSharp; using Crestron.SimplSharp.WebScripting; using Newtonsoft.Json; using PepperDash.Core; @@ -9,12 +15,6 @@ using PepperDash.Essentials.Core.Web; using PepperDash.Essentials.RoomBridges; using PepperDash.Essentials.WebApiHandlers; using Serilog.Events; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Text; using WebSocketSharp; using WebSocketSharp.Net; using WebSocketSharp.Server; @@ -24,6 +24,7 @@ namespace PepperDash.Essentials.WebSocketServer { public class MobileControlWebsocketServer : EssentialsDevice { + private int nextClientId = 0; private readonly string userAppPath = Global.FilePathPrefix + "mcUserApp" + Global.DirectorySeparator; private readonly string localConfigFolderName = "_local-config"; @@ -40,7 +41,9 @@ namespace PepperDash.Essentials.WebSocketServer public HttpServer Server => _server; - public Dictionary UiClients { get; private set; } + public Dictionary UiClientContexts { get; private set; } + + public Dictionary UiClients { get; private set; } = new Dictionary(); private readonly MobileControlSystemController _parent; @@ -60,7 +63,7 @@ namespace PepperDash.Essentials.WebSocketServer private string lanIpAddress => CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter)); - private System.Net.IPAddress csIpAddress; + private System.Net.IPAddress csIpAddress; private System.Net.IPAddress csSubnetMask; @@ -104,7 +107,7 @@ namespace PepperDash.Essentials.WebSocketServer { var count = 0; - foreach (var client in UiClients) + foreach (var client in UiClientContexts) { if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive) { @@ -122,7 +125,7 @@ namespace PepperDash.Essentials.WebSocketServer _parent = parent; // Set the default port to be 50000 plus the slot number of the program - Port = 50000 + (int)Global.ControlSystem.ProgramNumber; + Port = 50000 + (int)Global.ControlSystem.ProgramNumber; if (customPort != 0) { @@ -156,9 +159,9 @@ namespace PepperDash.Essentials.WebSocketServer } try - { + { var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter); - var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId); + var csSubnetMask = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, csAdapterId); var csIpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); this.csSubnetMask = System.Net.IPAddress.Parse(csSubnetMask); @@ -173,7 +176,7 @@ namespace PepperDash.Essentials.WebSocketServer } - UiClients = new Dictionary(); + UiClientContexts = new Dictionary(); //_joinTokens = new Dictionary(); @@ -349,7 +352,7 @@ namespace PepperDash.Essentials.WebSocketServer if (!Directory.Exists($"{userAppPath}{localConfigFolderName}")) { Directory.CreateDirectory($"{userAppPath}{localConfigFolderName}"); - } + } using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigFileName}", FileMode.Create, FileAccess.ReadWrite))) { @@ -358,7 +361,7 @@ namespace PepperDash.Essentials.WebSocketServer this.LogDebug("LAN Adapter ID: {lanAdapterId}", lanAdapterId); - var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); + var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, lanAdapterId); var config = GetApplicationConfig(processorIp); @@ -378,7 +381,7 @@ namespace PepperDash.Essentials.WebSocketServer return; } - if(csAdapterId == -1) + if (csAdapterId == -1) { this.LogDebug("CS LAN Adapter not found"); return; @@ -389,8 +392,8 @@ namespace PepperDash.Essentials.WebSocketServer using (var sw = new StreamWriter(File.Open($"{userAppPath}{localConfigFolderName}{Global.DirectorySeparator}{appConfigCsFileName}", FileMode.Create, FileAccess.ReadWrite))) { // Write the CS application configuration file. Used when a request comes in for the application config from the CS - var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); - + var processorIp = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId); + var config = GetApplicationConfig(processorIp); var contents = JsonConvert.SerializeObject(config, Formatting.Indented); @@ -400,7 +403,7 @@ namespace PepperDash.Essentials.WebSocketServer } private MobileControlApplicationConfig GetApplicationConfig(string processorIp) - { + { try { var config = new MobileControlApplicationConfig @@ -430,10 +433,10 @@ namespace PepperDash.Essentials.WebSocketServer } catch (Exception ex) { - this.LogError(ex, "Error getting application configuration"); + this.LogError(ex, "Error getting application configuration"); return null; - } + } } /// @@ -472,45 +475,39 @@ namespace PepperDash.Essentials.WebSocketServer Debug.LogMessage(LogEventLevel.Information, "Adding token: {0} for room: {1}", this, token.Key, token.Value.RoomKey); - if (UiClients == null) + if (UiClientContexts == null) { Debug.LogMessage(LogEventLevel.Warning, "UiClients is null", this); - UiClients = new Dictionary(); + UiClientContexts = new Dictionary(); } - UiClients.Add(token.Key, new UiClientContext(token.Value)); + UiClientContexts.Add(token.Key, new UiClientContext(token.Value)); } } - if (UiClients.Count > 0) + if (UiClientContexts.Count > 0) { - Debug.LogMessage(LogEventLevel.Information, "Restored {uiClientCount} UiClients from secrets data", this, UiClients.Count); + Debug.LogMessage(LogEventLevel.Information, "Restored {uiClientCount} UiClients from secrets data", this, UiClientContexts.Count); - foreach (var client in UiClients) + foreach (var client in UiClientContexts) { var key = client.Key; var path = _wsPath + key; var roomKey = client.Value.Token.RoomKey; + var token = client.Value.Token; + + var bridge = _parent.GetRoomBridge(roomKey); + + if (bridge == null) + { + this.LogWarning("No bridge found for room key: {0}", this, roomKey); + continue; + } _server.AddWebSocketService(path, () => { - var c = new UiClient(); - Debug.LogMessage(LogEventLevel.Debug, "Constructing UiClient with id: {key}", this, key); - - c.Controller = _parent; - c.RoomKey = roomKey; - UiClients[key].SetClient(c); - return c; + return InitializeUiClient(token, bridge); }); - - - //_server.WebSocketServices.AddService(path, (c) => - //{ - // Debug.Console(2, this, "Constructing UiClient with id: {0}", key); - // c.Controller = _parent; - // c.RoomKey = roomKey; - // UiClients[key].SetClient(c); - //}); } } } @@ -519,7 +516,7 @@ namespace PepperDash.Essentials.WebSocketServer Debug.LogMessage(LogEventLevel.Warning, "No secret found"); } - Debug.LogMessage(LogEventLevel.Debug, "{uiClientCount} UiClients restored from secrets data", this, UiClients.Count); + Debug.LogMessage(LogEventLevel.Debug, "{uiClientCount} UiClients restored from secrets data", this, UiClientContexts.Count); } catch (Exception ex) { @@ -543,7 +540,7 @@ namespace PepperDash.Essentials.WebSocketServer _secret.Tokens.Clear(); - foreach (var uiClientContext in UiClients) + foreach (var uiClientContext in UiClientContexts) { _secret.Tokens.Add(uiClientContext.Key, uiClientContext.Value.Token); } @@ -572,7 +569,7 @@ namespace PepperDash.Essentials.WebSocketServer var values = s.Split(' '); - if(values.Length < 2) + if (values.Length < 2) { CrestronConsole.ConsoleCommandResponse("Invalid number of arguments. Please provide a room key and a grant code"); return; @@ -636,24 +633,50 @@ namespace PepperDash.Essentials.WebSocketServer } } + private UiClient InitializeUiClient(JoinToken token, MobileControlBridgeBase bridge) + { + Debug.LogMessage(LogEventLevel.Verbose, "Constructing UiClient with id: {0}", this, token.Id); + + var c = new UiClient(token.Id) + { + Controller = _parent, + RoomKey = bridge.RoomKey + }; + + UiClients.Add(c.ClientId, c); + + // reset client ID in token for next use + token.Id = null; + + c.Context.WebSocket.OnClose += (sender, e) => + { + if (UiClients.ContainsKey(c.ClientId)) + { + UiClients.Remove(c.ClientId); + } + }; + + return c; + } + public (string, string) GenerateClientToken(MobileControlBridgeBase bridge, string touchPanelKey = "") { var key = Guid.NewGuid().ToString(); var token = new JoinToken { Code = bridge.UserCode, RoomKey = bridge.RoomKey, Uuid = _parent.SystemUuid, TouchpanelKey = touchPanelKey }; - UiClients.Add(key, new UiClientContext(token)); + UiClientContexts.Add(key, new UiClientContext(token)); var path = _wsPath + key; _server.AddWebSocketService(path, () => { - var c = new UiClient(); - Debug.LogMessage(LogEventLevel.Verbose, "Constructing UiClient with id: {0}", this, key); - c.Controller = _parent; - c.RoomKey = bridge.RoomKey; - UiClients[key].SetClient(c); - return c; + if (string.IsNullOrEmpty(token.Id)) + { + token.Id = nextClientId++.ToString(); + } + + return InitializeUiClient(token, bridge); }); Debug.LogMessage(LogEventLevel.Information, "Added new WebSocket UiClient service at path: {path}", this, path); @@ -683,7 +706,7 @@ namespace PepperDash.Essentials.WebSocketServer return; } - foreach (var client in UiClients) + foreach (var client in UiClientContexts) { if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive) { @@ -701,7 +724,7 @@ namespace PepperDash.Essentials.WebSocketServer } } - UiClients.Clear(); + UiClientContexts.Clear(); UpdateSecret(); } @@ -720,9 +743,9 @@ namespace PepperDash.Essentials.WebSocketServer var key = s; - if (UiClients.ContainsKey(key)) + if (UiClientContexts.ContainsKey(key)) { - var uiClientContext = UiClients[key]; + var uiClientContext = UiClientContexts[key]; if (uiClientContext.Client != null && uiClientContext.Client.Context.WebSocket.IsAlive) { @@ -732,7 +755,7 @@ namespace PepperDash.Essentials.WebSocketServer var path = _wsPath + key; if (_server.RemoveWebSocketService(path)) { - UiClients.Remove(key); + UiClientContexts.Remove(key); UpdateSecret(); @@ -756,9 +779,9 @@ namespace PepperDash.Essentials.WebSocketServer { CrestronConsole.ConsoleCommandResponse("Mobile Control UI Client Info:\r"); - CrestronConsole.ConsoleCommandResponse(string.Format("{0} clients found:\r", UiClients.Count)); + CrestronConsole.ConsoleCommandResponse(string.Format("{0} clients found:\r", UiClientContexts.Count)); - foreach (var client in UiClients) + foreach (var client in UiClientContexts) { CrestronConsole.ConsoleCommandResponse(string.Format("RoomKey: {0} Token: {1}\r", client.Value.Token.RoomKey, client.Key)); } @@ -768,7 +791,7 @@ namespace PepperDash.Essentials.WebSocketServer { if (programEventType == eProgramStatusEventType.Stopping) { - foreach (var client in UiClients.Values) + foreach (var client in UiClientContexts.Values) { if (client.Client != null && client.Client.Context.WebSocket.IsAlive) { @@ -907,7 +930,7 @@ namespace PepperDash.Essentials.WebSocketServer this.LogVerbose("Join Room Request with token: {token}", token); - if (UiClients.TryGetValue(token, out UiClientContext clientContext)) + if (UiClientContexts.TryGetValue(token, out UiClientContext clientContext)) { var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey); @@ -916,10 +939,12 @@ namespace PepperDash.Essentials.WebSocketServer res.StatusCode = 200; res.ContentType = "application/json"; + clientContext.Token.Id = nextClientId++.ToString(); + // Construct the response object JoinResponse jRes = new JoinResponse { - ClientId = token, + ClientId = clientContext.Token.Id, RoomKey = bridge.RoomKey, SystemUuid = _parent.SystemUuid, RoomUuid = _parent.SystemUuid, @@ -1137,12 +1162,14 @@ namespace PepperDash.Essentials.WebSocketServer /// public void SendMessageToAllClients(string message) { - foreach (var clientContext in UiClients.Values) + foreach (var client in UiClients.Values) { - if (clientContext.Client != null && clientContext.Client.Context.WebSocket.IsAlive) + if (!client.Context.WebSocket.IsAlive) { - clientContext.Client.Context.WebSocket.Send(message); + continue; } + + client.Context.WebSocket.Send(message); } } @@ -1153,129 +1180,25 @@ namespace PepperDash.Essentials.WebSocketServer /// public void SendMessageToClient(object clientId, string message) { - if (clientId == null) + if (clientId == null || !(clientId is string)) { return; } - if (UiClients.TryGetValue((string)clientId, out UiClientContext clientContext)) - { - if (clientContext.Client != null) - { - var socket = clientContext.Client.Context.WebSocket; - - if (socket.IsAlive) - { - socket.Send(message); - } - } - } - else + if (!UiClients.TryGetValue((string)clientId, out UiClient client)) { this.LogWarning("Unable to find client with ID: {clientId}", clientId); + return; } + + var socket = client.Context.WebSocket; + + if (!socket.IsAlive) + { + return; + } + socket.Send(message); + } } - - /// - /// Class to describe the server version info - /// - public class Version - { - [JsonProperty("serverVersion")] - public string ServerVersion { get; set; } - - [JsonProperty("serverIsRunningOnProcessorHardware")] - public bool ServerIsRunningOnProcessorHardware { get; private set; } - - public Version() - { - ServerIsRunningOnProcessorHardware = true; - } - } - - /// - /// Represents an instance of a UiClient and the associated Token - /// - public class UiClientContext - { - public UiClient Client { get; private set; } - public JoinToken Token { get; private set; } - - public UiClientContext(JoinToken token) - { - Token = token; - } - - public void SetClient(UiClient client) - { - Client = client; - } - - } - - /// - /// Represents the data structure for the grant code and UiClient tokens to be stored in the secrets manager - /// - public class ServerTokenSecrets - { - public string GrantCode { get; set; } - - public Dictionary Tokens { get; set; } - - public ServerTokenSecrets(string grantCode) - { - GrantCode = grantCode; - Tokens = new Dictionary(); - } - } - - /// - /// Represents a join token with the associated properties - /// - public class JoinToken - { - public string Code { get; set; } - - public string RoomKey { get; set; } - - public string Uuid { get; set; } - - public string TouchpanelKey { get; set; } = ""; - - public string Token { get; set; } = null; - } - - /// - /// Represents the structure of the join response - /// - public class JoinResponse - { - [JsonProperty("clientId")] - public string ClientId { get; set; } - - [JsonProperty("roomKey")] - public string RoomKey { get; set; } - - [JsonProperty("systemUUid")] - public string SystemUuid { get; set; } - - [JsonProperty("roomUUid")] - public string RoomUuid { get; set; } - - [JsonProperty("config")] - public object Config { get; set; } - - [JsonProperty("codeExpires")] - public DateTime CodeExpires { get; set; } - - [JsonProperty("userCode")] - public string UserCode { get; set; } - - [JsonProperty("userAppUrl")] - public string UserAppUrl { get; set; } - - [JsonProperty("enableDebug")] - public bool EnableDebug { get; set; } - } } diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs new file mode 100644 index 00000000..fef9bfac --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents the data structure for the grant code and UiClient tokens to be stored in the secrets manager + /// + public class ServerTokenSecrets + { + public string GrantCode { get; set; } + + public Dictionary Tokens { get; set; } + + public ServerTokenSecrets(string grantCode) + { + GrantCode = grantCode; + Tokens = new Dictionary(); + } + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs index eb1cf7a1..7d99695b 100644 --- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs @@ -1,18 +1,18 @@ -using Newtonsoft.Json; +using System; +using System.Text.RegularExpressions; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using PepperDash.Core; using PepperDash.Essentials.AppServer.Messengers; using PepperDash.Essentials.RoomBridges; using Serilog.Events; -using System; -using System.Text.RegularExpressions; using WebSocketSharp; using WebSocketSharp.Server; using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; namespace PepperDash.Essentials.WebSocketServer -{ +{ /// /// Represents the behaviour to associate with a UiClient for WebSocket communication /// @@ -22,7 +22,10 @@ namespace PepperDash.Essentials.WebSocketServer public string RoomKey { get; set; } - private string _clientId; + public string ClientId + { + get; private set; + } private DateTime _connectionTime; @@ -41,9 +44,9 @@ namespace PepperDash.Essentials.WebSocketServer } } - public UiClient() + public UiClient(string clientId) { - + ClientId = clientId; } protected override void OnOpen() @@ -61,8 +64,7 @@ namespace PepperDash.Essentials.WebSocketServer return; } - var clientId = match.Groups[1].Value; - _clientId = clientId; + var clientId = ClientId; if (Controller == null) { @@ -96,7 +98,7 @@ namespace PepperDash.Essentials.WebSocketServer private void Bridge_UserCodeChanged(object sender, EventArgs e) { - SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId); + SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, ClientId); } private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId) diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs new file mode 100644 index 00000000..a97f3828 --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs @@ -0,0 +1,22 @@ +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Represents an instance of a UiClient and the associated Token + /// + public class UiClientContext + { + public UiClient Client { get; private set; } + public JoinToken Token { get; private set; } + + public UiClientContext(JoinToken token) + { + Token = token; + } + + public void SetClient(UiClient client) + { + Client = client; + } + + } +} diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs new file mode 100644 index 00000000..5b62582b --- /dev/null +++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs @@ -0,0 +1,22 @@ +using Newtonsoft.Json; + + +namespace PepperDash.Essentials.WebSocketServer +{ + /// + /// Class to describe the server version info + /// + public class Version + { + [JsonProperty("serverVersion")] + public string ServerVersion { get; set; } + + [JsonProperty("serverIsRunningOnProcessorHardware")] + public bool ServerIsRunningOnProcessorHardware { get; private set; } + + public Version() + { + ServerIsRunningOnProcessorHardware = true; + } + } +}