diff --git a/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs b/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs
index beddd2f6..b974a9aa 100644
--- a/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs
+++ b/src/PepperDash.Essentials.MobileControl/ClientSpecificUpdateRequest.cs
@@ -3,10 +3,15 @@ using System;
namespace PepperDash.Essentials
{
///
- /// Represents a ClientSpecificUpdateRequest
+ /// Send an update request for a specific client
///
+ [Obsolete]
public class ClientSpecificUpdateRequest
{
+ ///
+ /// Initialize an instance of the class.
+ ///
+ ///
public ClientSpecificUpdateRequest(Action action)
{
ResponseMethod = action;
diff --git a/src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs b/src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs
index 2d191c89..abeef999 100644
--- a/src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs
+++ b/src/PepperDash.Essentials.MobileControl/IDelayedConfiguration.cs
@@ -7,8 +7,9 @@ namespace PepperDash.Essentials
///
public interface IDelayedConfiguration
{
-
-
+ ///
+ /// Event triggered when the configuration is ready. Used when Mobile Control is interacting with a SIMPL program.
+ ///
event EventHandler ConfigurationIsReady;
}
}
\ No newline at end of file
diff --git a/src/PepperDash.Essentials.MobileControl/MessageToClients.cs b/src/PepperDash.Essentials.MobileControl/MessageToClients.cs
new file mode 100644
index 00000000..e1aa5eb5
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl/MessageToClients.cs
@@ -0,0 +1,90 @@
+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
+{
+ ///
+ /// Represents a MessageToClients
+ ///
+ public class MessageToClients : IQueueMessage
+ {
+ private readonly MobileControlWebsocketServer _server;
+ private readonly object msgToSend;
+
+ ///
+ /// Message to send to Direct Server Clients
+ ///
+ /// message object to send
+ /// WebSocket server instance
+ public MessageToClients(object msg, MobileControlWebsocketServer server)
+ {
+ _server = server;
+ msgToSend = msg;
+ }
+
+ ///
+ /// Message to send to Direct Server Clients
+ ///
+ /// message object to send
+ /// WebSocket server instance
+ public MessageToClients(DeviceStateMessageBase msg, MobileControlWebsocketServer server)
+ {
+ _server = server;
+ msgToSend = msg;
+ }
+
+ #region Implementation of IQueueMessage
+
+ ///
+ /// Dispatch method
+ ///
+ 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}", 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/MobileControlAction.cs b/src/PepperDash.Essentials.MobileControl/MobileControlAction.cs
index 2e402dfb..16a9bd40 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlAction.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlAction.cs
@@ -1,6 +1,6 @@
-using Newtonsoft.Json.Linq;
+using System;
+using Newtonsoft.Json.Linq;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
-using System;
namespace PepperDash.Essentials
{
@@ -10,12 +10,20 @@ namespace PepperDash.Essentials
public class MobileControlAction : IMobileControlAction
{
///
- /// Gets or sets the Messenger
+ /// Gets the Messenger
///
public IMobileControlMessenger Messenger { get; private set; }
+ ///
+ /// Action to execute when this path is matched
+ ///
public Action Action { get; private set; }
+ ///
+ /// Initialize an instance of the class
+ ///
+ /// Messenger associated with this action
+ /// Action to take when this path is matched
public MobileControlAction(IMobileControlMessenger messenger, Action handler)
{
Messenger = messenger;
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlDeviceFactory.cs b/src/PepperDash.Essentials.MobileControl/MobileControlDeviceFactory.cs
index c4aa673b..6147b14e 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlDeviceFactory.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlDeviceFactory.cs
@@ -1,28 +1,25 @@
-using PepperDash.Core;
-using PepperDash.Core.Logging;
+using System;
+using System.Collections.Generic;
+using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
-using Serilog.Events;
-using System;
-using System.Collections.Generic;
-using System.Linq;
namespace PepperDash.Essentials
{
///
- /// Represents a MobileControlDeviceFactory
+ /// Factory to create a Mobile Control System Controller
///
public class MobileControlDeviceFactory : EssentialsDeviceFactory
{
+ ///
+ /// Create the factory for a Mobile Control System Controller
+ ///
public MobileControlDeviceFactory()
{
TypeNames = new List { "appserver", "mobilecontrol", "webserver" };
}
- ///
- /// BuildDevice method
- ///
///
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs b/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs
index 7183ec84..d0415287 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlEssentialsConfig.cs
@@ -6,29 +6,35 @@ using PepperDash.Essentials.Core.Config;
namespace PepperDash.Essentials
{
///
- /// Represents a MobileControlEssentialsConfig
+ /// Configuration class for sending data to Mobile Control Edge or a client using the Direct Server
///
public class MobileControlEssentialsConfig : EssentialsConfig
{
+ ///
+ /// Current versions for the system
+ ///
[JsonProperty("runtimeInfo")]
public MobileControlRuntimeInfo RuntimeInfo { get; set; }
+ ///
+ /// Create Configuration for Mobile Control. Used as part of the data sent to a client
+ ///
+ /// The base configuration
public MobileControlEssentialsConfig(EssentialsConfig config)
: base()
{
- // TODO: Consider using Reflection to iterate properties
- this.Devices = config.Devices;
- this.Info = config.Info;
- this.JoinMaps = config.JoinMaps;
- this.Rooms = config.Rooms;
- this.SourceLists = config.SourceLists;
- this.DestinationLists = config.DestinationLists;
- this.SystemUrl = config.SystemUrl;
- this.TemplateUrl = config.TemplateUrl;
- this.TieLines = config.TieLines;
+ Devices = config.Devices;
+ Info = config.Info;
+ JoinMaps = config.JoinMaps;
+ Rooms = config.Rooms;
+ SourceLists = config.SourceLists;
+ DestinationLists = config.DestinationLists;
+ SystemUrl = config.SystemUrl;
+ TemplateUrl = config.TemplateUrl;
+ TieLines = config.TieLines;
- if (this.Info == null)
- this.Info = new InfoConfig();
+ if (Info == null)
+ Info = new InfoConfig();
RuntimeInfo = new MobileControlRuntimeInfo();
}
@@ -46,15 +52,21 @@ namespace PepperDash.Essentials
[JsonProperty("pluginVersion")]
public string PluginVersion { get; set; }
+ ///
+ /// Essentials Version
+ ///
[JsonProperty("essentialsVersion")]
public string EssentialsVersion { get; set; }
+ ///
+ /// PepperDash Core Version
+ ///
[JsonProperty("pepperDashCoreVersion")]
public string PepperDashCoreVersion { get; set; }
///
- /// Gets or sets the EssentialsPlugins
+ /// List of Plugins loaded on this system
///
[JsonProperty("essentialsPlugins")]
public List EssentialsPlugins { get; set; }
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs b/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs
index 393b3385..4f3c5433 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs
@@ -7,10 +7,13 @@ using PepperDash.Essentials.Core;
namespace PepperDash.Essentials
{
///
- /// Represents a MobileControlFactory
+ /// Factory class for the Mobile Control App Controller
///
public class MobileControlFactory
{
+ ///
+ /// Create an instance of the class.
+ ///
public MobileControlFactory()
{
var assembly = Assembly.GetExecutingAssembly();
diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
index d6a40ede..01bf3579 100644
--- a/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
+++ b/src/PepperDash.Essentials.MobileControl/MobileControlSystemController.cs
@@ -91,10 +91,16 @@ namespace PepperDash.Essentials
///
public MobileControlApiService ApiService { get; private set; }
+ ///
+ /// Get Room Bridges associated with this controller
+ ///
public List RoomBridges => _roomBridges;
private readonly MobileControlWebsocketServer _directServer;
+ ///
+ /// Get the Direct Server instance associated with this controller
+ ///
public MobileControlWebsocketServer DirectServer => _directServer;
private readonly CCriticalSection _wsCriticalSection = new CCriticalSection();
@@ -104,10 +110,16 @@ namespace PepperDash.Essentials
///
public string SystemUrl; //set only from SIMPL Bridge!
+ ///
+ /// True if the Mobile Control Edge Server Websocket is connected
+ ///
public bool Connected => _wsClient2 != null && _wsClient2.IsAlive;
private IEssentialsRoomCombiner _roomCombiner;
+ ///
+ /// Gets the SystemUuid from configuration or SIMPL Bridge
+ ///
public string SystemUuid
{
get
@@ -169,6 +181,9 @@ namespace PepperDash.Essentials
private DateTime _lastAckMessage;
+ ///
+ /// Gets the LastAckMessage timestamp
+ ///
public DateTime LastAckMessage => _lastAckMessage;
private CTimer _pingTimer;
@@ -177,11 +192,11 @@ namespace PepperDash.Essentials
private LogLevel _wsLogLevel = LogLevel.Error;
///
- ///
+ /// Initializes a new instance of the class.
///
- ///
- ///
- ///
+ /// The unique key for this controller.
+ /// The name of the controller.
+ /// The configuration settings for the controller.
public MobileControlSystemController(string key, string name, MobileControlConfig config)
: base(key, name)
{
@@ -1192,6 +1207,9 @@ namespace PepperDash.Essentials
///
public string Host { get; private set; }
+ ///
+ /// Gets the configured Client App URL
+ ///
public string ClientAppUrl => Config.ClientAppUrl;
private void OnRoomCombinationScenarioChanged(
@@ -1203,7 +1221,7 @@ namespace PepperDash.Essentials
}
///
- /// CheckForDeviceMessenger method
+ /// Checks if a device messenger exists for the given key.
///
public bool CheckForDeviceMessenger(string key)
{
@@ -1211,13 +1229,13 @@ namespace PepperDash.Essentials
}
///
- /// AddDeviceMessenger method
+ /// Add the provided messenger to the messengers collection
///
public void AddDeviceMessenger(IMobileControlMessenger messenger)
{
if (_messengers.ContainsKey(messenger.Key))
{
- this.LogWarning("Messenger with key {messengerKey) already added", messenger.Key);
+ this.LogWarning("Messenger with key {messengerKey} already added", messenger.Key);
return;
}
@@ -1291,9 +1309,6 @@ namespace PepperDash.Essentials
messenger.RegisterWithAppServer(this);
}
- ///
- /// Initialize method
- ///
///
public override void Initialize()
{
@@ -1338,7 +1353,7 @@ namespace PepperDash.Essentials
#region IMobileControl Members
///
- /// GetAppServer method
+ /// Gets the App Server instance
///
public static IMobileControl GetAppServer()
{
@@ -1356,16 +1371,10 @@ namespace PepperDash.Essentials
}
}
- ///
- /// Generates the url and creates the websocket client
- ///
private bool CreateWebsocket()
{
- if (_wsClient2 != null)
- {
- _wsClient2.Close();
- _wsClient2 = null;
- }
+ _wsClient2?.Close();
+ _wsClient2 = null;
if (string.IsNullOrEmpty(SystemUuid))
{
@@ -1382,33 +1391,13 @@ namespace PepperDash.Essentials
{
Log =
{
- Output = (data, message) =>
- {
- switch (data.Level)
- {
- case LogLevel.Trace:
- this.LogVerbose(data.Message);
- break;
- case LogLevel.Debug:
- this.LogDebug(data.Message);
- break;
- case LogLevel.Info:
- this.LogInformation(data.Message);
- break;
- case LogLevel.Warn:
- this.LogWarning(data.Message);
- break;
- case LogLevel.Error:
- this.LogError(data.Message);
- break;
- case LogLevel.Fatal:
- this.LogFatal(data.Message);
- break;
- }
- }
+ Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this)
}
};
+ // setting to trace to let level be controlled by appdebug
+ _wsClient2.Log.Level = LogLevel.Trace;
+
_wsClient2.SslConfiguration.EnabledSslProtocols =
System.Security.Authentication.SslProtocols.Tls11
| System.Security.Authentication.SslProtocols.Tls12;
@@ -1422,7 +1411,7 @@ namespace PepperDash.Essentials
}
///
- /// LinkSystemMonitorToAppServer method
+ /// Link the System Monitor to this App server
///
public void LinkSystemMonitorToAppServer()
{
@@ -1449,14 +1438,6 @@ namespace PepperDash.Essentials
private void SetWebsocketDebugLevel(string cmdparameters)
{
- // if (CrestronEnvironment.ProgramCompatibility == eCrestronSeries.Series4)
- // {
- // this.LogInformation(
- // "Setting websocket log level not currently allowed on 4 series."
- // );
- // return; // Web socket log level not currently allowed in series4
- // }
-
if (string.IsNullOrEmpty(cmdparameters))
{
this.LogInformation("Current Websocket debug level: {webSocketDebugLevel}", _wsLogLevel);
@@ -1494,10 +1475,6 @@ namespace PepperDash.Essentials
}
}
- ///
- /// Sends message to server to indicate the system is shutting down
- ///
- ///
private void CrestronEnvironment_ProgramStatusEventHandler(
eProgramStatusEventType programEventType
)
@@ -1530,6 +1507,9 @@ namespace PepperDash.Essentials
}
}
+ ///
+ /// Get action paths for the current actions
+ ///
public List<(string, string)> GetActionDictionaryPaths()
{
var paths = new List<(string, string)>();
@@ -1602,24 +1582,24 @@ namespace PepperDash.Essentials
}
}
+ ///
+ /// Get the room bridge with the provided key
+ ///
+ /// The key of the room bridge
public MobileControlBridgeBase GetRoomBridge(string key)
{
return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));
}
///
- /// GetRoomMessenger method
+ /// Get the room messenger with the provided key
///
+ /// The Key of the rooom messenger
public IMobileControlRoomMessenger GetRoomMessenger(string key)
{
return _roomBridges.FirstOrDefault((r) => r.RoomKey.Equals(key));
}
- ///
- ///
- ///
- ///
- ///
private void Bridge_ConfigurationIsReady(object sender, EventArgs e)
{
this.LogDebug("Bridge ready. Registering");
@@ -1640,10 +1620,6 @@ namespace PepperDash.Essentials
}
}
- ///
- ///
- ///
- ///
private void ReconnectToServerTimerCallback(object o)
{
this.LogDebug("Attempting to reconnect to server...");
@@ -1651,9 +1627,6 @@ namespace PepperDash.Essentials
ConnectWebsocketClient();
}
- ///
- /// Verifies system connection with servers
- ///
private void AuthorizeSystem(string code)
{
if (
@@ -1698,9 +1671,6 @@ namespace PepperDash.Essentials
});
}
- ///
- /// Dumps info in response to console command.
- ///
private void ShowInfo()
{
var url = Config != null ? Host : "No config";
@@ -1766,38 +1736,37 @@ namespace PepperDash.Essentials
"\r\n UI Client Info:\r\n" +
" Tokens Defined: {0}\r\n" +
" Clients Connected: {1}\r\n",
- _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";
-
- if (clientContext.Value.Client != null)
- {
- isAlive = clientContext.Value.Client.Context.WebSocket.IsAlive;
- duration = clientContext.Value.Client.ConnectedDuration.ToString();
- }
+ var clients = _directServer.UiClients.Values.Where(c => c.Token == clientContext.Value.Token.Token);
CrestronConsole.ConsoleCommandResponse(
- "\r\nClient {0}:\r\n" +
- "Room Key: {1}\r\n" +
- "Touchpanel Key: {6}\r\n" +
- "Token: {2}\r\n" +
- "Client URL: {3}\r\n" +
- "Connected: {4}\r\n" +
- "Duration: {5}\r\n",
- clientNo,
- clientContext.Value.Token.RoomKey,
- clientContext.Key,
- string.Format("{0}{1}", _directServer.UserAppUrlPrefix, clientContext.Key),
- isAlive,
- duration,
- clientContext.Value.Token.TouchpanelKey
+ $"\r\nClient {clientNo}:\r\n" +
+ $" Room Key: {clientContext.Value.Token.RoomKey}\r\n" +
+ $" Touchpanel Key: {clientContext.Value.Token.TouchpanelKey}\r\n" +
+ $" Token: {clientContext.Key}\r\n" +
+ $" Client URL: {_directServer.UserAppUrlPrefix}{clientContext.Key}\r\n" +
+ $" Clients:\r\n"
);
+
+ if (!clients.Any())
+ {
+ CrestronConsole.ConsoleCommandResponse(" No clients connected");
+ }
+ foreach (var client in clients)
+ {
+ CrestronConsole.ConsoleCommandResponse(
+ $" ID: {client.Id}\r\n" +
+ $" Connected: {client.Context.WebSocket.IsAlive}\r\n" +
+ $" Duration: {(client.Context.WebSocket.IsAlive ? client.ConnectedDuration.TotalSeconds.ToString() : "Not Connected")}\r\n"
+ );
+ }
+
clientNo++;
}
}
@@ -1811,7 +1780,7 @@ namespace PepperDash.Essentials
}
///
- /// RegisterSystemToServer method
+ /// Register this system to the Mobile Control Edge Server
///
public void RegisterSystemToServer()
{
@@ -1835,9 +1804,6 @@ namespace PepperDash.Essentials
ConnectWebsocketClient();
}
- ///
- /// Connects the Websocket Client
- ///
private void ConnectWebsocketClient()
{
try
@@ -1878,9 +1844,6 @@ namespace PepperDash.Essentials
}
}
- ///
- /// Attempts to connect the websocket
- ///
private void TryConnect()
{
try
@@ -1910,9 +1873,6 @@ namespace PepperDash.Essentials
}
}
- ///
- /// Gracefully handles conect failures by reconstructing the ws client and starting the reconnect timer
- ///
private void HandleConnectFailure()
{
_wsClient2 = null;
@@ -1944,11 +1904,6 @@ namespace PepperDash.Essentials
StartServerReconnectTimer();
}
- ///
- ///
- ///
- ///
- ///
private void HandleOpen(object sender, EventArgs e)
{
StopServerReconnectTimer();
@@ -1957,11 +1912,6 @@ namespace PepperDash.Essentials
SendMessageObject(new MobileControlMessage { Type = "hello" });
}
- ///
- ///
- ///
- ///
- ///
private void HandleMessage(object sender, MessageEventArgs e)
{
if (e.IsPing)
@@ -1978,11 +1928,6 @@ namespace PepperDash.Essentials
}
}
- ///
- ///
- ///
- ///
- ///
private void HandleError(object sender, ErrorEventArgs e)
{
this.LogError("Websocket error {0}", e.Message);
@@ -1991,11 +1936,6 @@ namespace PepperDash.Essentials
StartServerReconnectTimer();
}
- ///
- ///
- ///
- ///
- ///
private void HandleClose(object sender, CloseEventArgs e)
{
this.LogDebug(
@@ -2016,9 +1956,6 @@ namespace PepperDash.Essentials
StartServerReconnectTimer();
}
- ///
- /// After a "hello" from the server, sends config and stuff
- ///
private void SendInitialMessage()
{
this.LogInformation("Sending initial join message");
@@ -2045,7 +1982,7 @@ namespace PepperDash.Essentials
}
///
- /// GetConfigWithPluginVersion method
+ /// Get the Essentials configuration with version data
///
public MobileControlEssentialsConfig GetConfigWithPluginVersion()
{
@@ -2080,8 +2017,13 @@ namespace PepperDash.Essentials
}
///
- /// SetClientUrl method
+ /// Set the Client URL for a given room
///
+ /// new App URL
+ /// room key. Default is null
+ ///
+ /// If roomKey is null, the URL will be set for the entire system.
+ ///
public void SetClientUrl(string path, string roomKey = null)
{
var message = new MobileControlMessage
@@ -2097,9 +2039,6 @@ namespace PepperDash.Essentials
/// Sends any object type to server
///
///
- ///
- /// SendMessageObject method
- ///
public void SendMessageObject(IMobileControlMessage o)
{
@@ -2123,8 +2062,9 @@ namespace PepperDash.Essentials
///
- /// SendMessageObjectToDirectClient method
+ /// Send a message to a client using the Direct Server
///
+ /// object to send
public void SendMessageObjectToDirectClient(object o)
{
if (
@@ -2137,10 +2077,6 @@ namespace PepperDash.Essentials
}
}
-
- ///
- /// Disconnects the Websocket Client and stops the heartbeat timer
- ///
private void CleanUpWebsocketClient()
{
if (_wsClient2 == null)
@@ -2198,9 +2134,6 @@ namespace PepperDash.Essentials
}
}
- ///
- ///
- ///
private void StartServerReconnectTimer()
{
StopServerReconnectTimer();
@@ -2211,9 +2144,6 @@ namespace PepperDash.Essentials
this.LogDebug("Reconnect Timer Started.");
}
- ///
- /// Does what it says
- ///
private void StopServerReconnectTimer()
{
if (_serverReconnectTimer == null)
@@ -2224,10 +2154,6 @@ namespace PepperDash.Essentials
_serverReconnectTimer = null;
}
- ///
- /// Resets reconnect timer and updates usercode
- ///
- ///
private void HandleHeartBeat(JToken content)
{
SendMessageObject(new MobileControlMessage { Type = "/system/heartbeatAck" });
@@ -2337,16 +2263,13 @@ namespace PepperDash.Essentials
}
///
- /// HandleClientMessage method
+ /// Enqueue an incoming message for processing
///
public void HandleClientMessage(string message)
{
_receiveQueue.Enqueue(new ProcessStringMessage(message, ParseStreamRx));
}
- ///
- ///
- ///
private void ParseStreamRx(string messageText)
{
if (string.IsNullOrEmpty(messageText))
@@ -2414,10 +2337,33 @@ namespace PepperDash.Essentials
foreach (var handler in handlers)
{
- Task.Run(
- () =>
- handler.Action(message.Type, message.ClientId, message.Content)
- );
+ Task.Run(async () =>
+ {
+ try
+ {
+ handler.Action(message.Type, message.ClientId, message.Content);
+ }
+ catch (Exception ex)
+ {
+ this.LogError(
+ "Exception in handler for message type {type}, ClientId {clientId}",
+ message.Type,
+ message.ClientId
+ );
+ this.LogDebug(ex, "Stack Trace: ");
+ }
+ }).ContinueWith(task =>
+ {
+ if (task.IsFaulted && task.Exception != null)
+ {
+ this.LogError(
+ "Unhandled exception in Task for message type {type}, ClientId {clientId}",
+ message.Type,
+ message.ClientId
+ );
+ this.LogDebug(task.Exception.GetBaseException(), "Stack Trace: ");
+ }
+ }, TaskContinuationOptions.OnlyOnFaulted);
}
break;
@@ -2433,10 +2379,6 @@ namespace PepperDash.Essentials
}
}
- ///
- ///
- ///
- ///
private void TestHttpRequest(string s)
{
{
diff --git a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlBridgeBase.cs b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlBridgeBase.cs
index ac1a851c..634255fe 100644
--- a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlBridgeBase.cs
+++ b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlBridgeBase.cs
@@ -1,23 +1,35 @@
-using PepperDash.Core;
+using System;
+using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer.Messengers;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
-using System;
namespace PepperDash.Essentials.RoomBridges
{
///
- ///
+ /// Base class for a Mobile Control Bridge that's used to control a room
///
public abstract class MobileControlBridgeBase : MessengerBase, IMobileControlRoomMessenger
{
+ ///
+ /// Triggered when the user Code changes
+ ///
public event EventHandler UserCodeChanged;
+ ///
+ /// Triggered when a user should be prompted for the new code
+ ///
public event EventHandler UserPromptedForCode;
+ ///
+ /// Triggered when a client joins to control this room
+ ///
public event EventHandler ClientJoined;
+ ///
+ /// Triggered when the App URL for this room changes
+ ///
public event EventHandler AppUrlChanged;
///
@@ -49,15 +61,32 @@ namespace PepperDash.Essentials.RoomBridges
///
public string McServerUrl { get; private set; }
+ ///
+ /// Room Name
+ ///
public abstract string RoomName { get; }
+ ///
+ /// Room key
+ ///
public abstract string RoomKey { get; }
+ ///
+ /// Create an instance of the class
+ ///
+ /// The unique key for this bridge
+ /// The message path for this bridge
protected MobileControlBridgeBase(string key, string messagePath)
: base(key, messagePath)
{
}
+ ///
+ /// Create an instance of the class
+ ///
+ /// The unique key for this bridge
+ /// The message path for this bridge
+ /// The device associated with this bridge
protected MobileControlBridgeBase(string key, string messagePath, IKeyName device)
: base(key, messagePath, device)
{
@@ -110,6 +139,10 @@ namespace PepperDash.Essentials.RoomBridges
SetUserCode(code);
}
+ ///
+ /// Update the App Url with the provided URL
+ ///
+ /// The new App URL
public virtual void UpdateAppUrl(string url)
{
AppUrl = url;
@@ -137,16 +170,25 @@ namespace PepperDash.Essentials.RoomBridges
OnUserCodeChanged();
}
+ ///
+ /// Trigger the UserCodeChanged event
+ ///
protected void OnUserCodeChanged()
{
UserCodeChanged?.Invoke(this, new EventArgs());
}
+ ///
+ /// Trigger the UserPromptedForCode event
+ ///
protected void OnUserPromptedForCode()
{
UserPromptedForCode?.Invoke(this, new EventArgs());
}
+ ///
+ /// Trigger the ClientJoined event
+ ///
protected void OnClientJoined()
{
ClientJoined?.Invoke(this, new EventArgs());
diff --git a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs
index f0463f27..742251db 100644
--- a/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs
+++ b/src/PepperDash.Essentials.MobileControl/RoomBridges/MobileControlEssentialsRoomBridge.cs
@@ -41,24 +41,37 @@ namespace PepperDash.Essentials.RoomBridges
///
public string DefaultRoomKey { get; private set; }
///
- ///
+ /// Gets the name of the room
///
public override string RoomName
{
get { return Room.Name; }
}
+ ///
+ /// Gets the key of the room
+ ///
public override string RoomKey
{
get { return Room.Key; }
}
+ ///
+ /// Initializes a new instance of the class with the specified room
+ ///
+ /// The essentials room to bridge
public MobileControlEssentialsRoomBridge(IEssentialsRoom room) :
this($"mobileControlBridge-{room.Key}", room.Key, room)
{
Room = room;
}
+ ///
+ /// Initializes a new instance of the class with the specified parameters
+ ///
+ /// The unique key for this bridge
+ /// The key of the room to bridge
+ /// The essentials room to bridge
public MobileControlEssentialsRoomBridge(string key, string roomKey, IEssentialsRoom room) : base(key, $"/room/{room.Key}", room as Device)
{
DefaultRoomKey = roomKey;
@@ -66,7 +79,9 @@ namespace PepperDash.Essentials.RoomBridges
AddPreActivationAction(GetRoom);
}
-
+ ///
+ /// Registers all message handling actions with the AppServer for this room bridge
+ ///
protected override void RegisterActions()
{
// we add actions to the messaging system with a path, and a related action. Custom action
@@ -284,6 +299,9 @@ namespace PepperDash.Essentials.RoomBridges
Room = tempRoom;
}
+ ///
+ /// Handles user code changes and generates QR code URL
+ ///
protected override void UserCodeChange()
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Server user code changed: {userCode}", this, UserCode);
@@ -807,18 +825,33 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("configuration", NullValueHandling = NullValueHandling.Ignore)]
public RoomConfiguration Configuration { get; set; }
+ ///
+ /// Gets or sets the activity mode of the room
+ ///
[JsonProperty("activityMode", NullValueHandling = NullValueHandling.Ignore)]
public int? ActivityMode { get; set; }
+ ///
+ /// Gets or sets whether advanced sharing is active
+ ///
[JsonProperty("advancedSharingActive", NullValueHandling = NullValueHandling.Ignore)]
public bool? AdvancedSharingActive { get; set; }
+ ///
+ /// Gets or sets whether the room is powered on
+ ///
[JsonProperty("isOn", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsOn { get; set; }
+ ///
+ /// Gets or sets whether the room is warming up
+ ///
[JsonProperty("isWarmingUp", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsWarmingUp { get; set; }
+ ///
+ /// Gets or sets whether the room is cooling down
+ ///
[JsonProperty("isCoolingDown", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsCoolingDown { get; set; }
@@ -834,9 +867,15 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("share", NullValueHandling = NullValueHandling.Ignore)]
public ShareState Share { get; set; }
+ ///
+ /// Gets or sets the volume controls collection
+ ///
[JsonProperty("volumes", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary Volumes { get; set; }
+ ///
+ /// Gets or sets whether the room is in a call
+ ///
[JsonProperty("isInCall", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsInCall { get; set; }
}
@@ -853,9 +892,15 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("currentShareText", NullValueHandling = NullValueHandling.Ignore)]
public string CurrentShareText { get; set; }
+ ///
+ /// Gets or sets whether sharing is enabled
+ ///
[JsonProperty("enabled", NullValueHandling = NullValueHandling.Ignore)]
public bool? Enabled { get; set; }
+ ///
+ /// Gets or sets whether content is currently being shared
+ ///
[JsonProperty("isSharing", NullValueHandling = NullValueHandling.Ignore)]
public bool? IsSharing { get; set; }
}
@@ -865,24 +910,45 @@ namespace PepperDash.Essentials.RoomBridges
///
public class RoomConfiguration
{
+ ///
+ /// Gets or sets whether the room has video conferencing capabilities
+ ///
[JsonProperty("hasVideoConferencing", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasVideoConferencing { get; set; }
+ ///
+ /// Gets or sets whether the video codec is a Zoom Room
+ ///
[JsonProperty("videoCodecIsZoomRoom", NullValueHandling = NullValueHandling.Ignore)]
public bool? VideoCodecIsZoomRoom { get; set; }
+ ///
+ /// Gets or sets whether the room has audio conferencing capabilities
+ ///
[JsonProperty("hasAudioConferencing", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasAudioConferencing { get; set; }
+ ///
+ /// Gets or sets whether the room has environmental controls (lighting, shades, etc.)
+ ///
[JsonProperty("hasEnvironmentalControls", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasEnvironmentalControls { get; set; }
+ ///
+ /// Gets or sets whether the room has camera controls
+ ///
[JsonProperty("hasCameraControls", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasCameraControls { get; set; }
+ ///
+ /// Gets or sets whether the room has set-top box controls
+ ///
[JsonProperty("hasSetTopBoxControls", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasSetTopBoxControls { get; set; }
+ ///
+ /// Gets or sets whether the room has routing controls
+ ///
[JsonProperty("hasRoutingControls", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasRoutingControls { get; set; }
@@ -949,6 +1015,9 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("defaultDisplayKey", NullValueHandling = NullValueHandling.Ignore)]
public string DefaultDisplayKey { get; set; }
+ ///
+ /// Gets or sets the destinations dictionary keyed by destination type
+ ///
[JsonProperty("destinations", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary Destinations { get; set; }
@@ -959,9 +1028,15 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("environmentalDevices", NullValueHandling = NullValueHandling.Ignore)]
public List EnvironmentalDevices { get; set; }
+ ///
+ /// Gets or sets the source list for the room
+ ///
[JsonProperty("sourceList", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary SourceList { get; set; }
+ ///
+ /// Gets or sets the destination list for the room
+ ///
[JsonProperty("destinationList", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary DestinationList { get; set; }
@@ -972,6 +1047,9 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("audioControlPointList", NullValueHandling = NullValueHandling.Ignore)]
public AudioControlPointListItem AudioControlPointList { get; set; }
+ ///
+ /// Gets or sets the camera list for the room
+ ///
[JsonProperty("cameraList", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary CameraList { get; set; }
@@ -1004,9 +1082,15 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("uiBehavior", NullValueHandling = NullValueHandling.Ignore)]
public EssentialsRoomUiBehaviorConfig UiBehavior { get; set; }
+ ///
+ /// Gets or sets whether the room supports advanced sharing features
+ ///
[JsonProperty("supportsAdvancedSharing", NullValueHandling = NullValueHandling.Ignore)]
public bool? SupportsAdvancedSharing { get; set; }
+ ///
+ /// Gets or sets whether the user can change the share mode
+ ///
[JsonProperty("userCanChangeShareMode", NullValueHandling = NullValueHandling.Ignore)]
public bool? UserCanChangeShareMode { get; set; }
@@ -1017,6 +1101,9 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("roomCombinerKey", NullValueHandling = NullValueHandling.Ignore)]
public string RoomCombinerKey { get; set; }
+ ///
+ /// Initializes a new instance of the class
+ ///
public RoomConfiguration()
{
Destinations = new Dictionary();
@@ -1046,6 +1133,11 @@ namespace PepperDash.Essentials.RoomBridges
[JsonProperty("deviceType", NullValueHandling = NullValueHandling.Ignore)]
public eEnvironmentalDeviceTypes DeviceType { get; private set; }
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// The device key
+ /// The environmental device type
public EnvironmentalDeviceConfiguration(string key, eEnvironmentalDeviceTypes type)
{
DeviceKey = key;
@@ -1054,14 +1146,29 @@ namespace PepperDash.Essentials.RoomBridges
}
///
- /// Enumeration of eEnvironmentalDeviceTypes values
+ /// Enumeration of environmental device types
///
public enum eEnvironmentalDeviceTypes
{
+ ///
+ /// No environmental device type specified
+ ///
None,
+ ///
+ /// Lighting device type
+ ///
Lighting,
+ ///
+ /// Shade device type
+ ///
Shade,
+ ///
+ /// Shade controller device type
+ ///
ShadeController,
+ ///
+ /// Relay device type
+ ///
Relay,
}
diff --git a/src/PepperDash.Essentials.MobileControl/Services/MobileControlApiService.cs b/src/PepperDash.Essentials.MobileControl/Services/MobileControlApiService.cs
index 4a84f8f1..508fb776 100644
--- a/src/PepperDash.Essentials.MobileControl/Services/MobileControlApiService.cs
+++ b/src/PepperDash.Essentials.MobileControl/Services/MobileControlApiService.cs
@@ -1,18 +1,22 @@
-using PepperDash.Core;
-using System;
+using System;
using System.Net.Http;
using System.Threading.Tasks;
+using PepperDash.Core;
namespace PepperDash.Essentials.Services
{
///
- /// Represents a MobileControlApiService
+ /// Service for interacting with a Mobile Control Edge server instance
///
public class MobileControlApiService
{
private readonly HttpClient _client;
+ ///
+ /// Create an instance of the class.
+ ///
+ /// Mobile Control Edge API URL
public MobileControlApiService(string apiUrl)
{
var handler = new HttpClientHandler
@@ -24,6 +28,13 @@ namespace PepperDash.Essentials.Services
_client = new HttpClient(handler);
}
+ ///
+ /// Send authorization request to Mobile Control Edge Server
+ ///
+ /// Mobile Control Edge API URL
+ /// Grant code for authorization
+ /// System UUID for authorization
+ /// Authorization response
public async Task SendAuthorizationRequest(string apiUrl, string grantCode, string systemUuid)
{
try
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs
index e5ba76cf..c7efbf4c 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITheme.cs
@@ -7,8 +7,15 @@ namespace PepperDash.Essentials.Touchpanel
///
public interface ITheme : IKeyed
{
+ ///
+ /// Current theme
+ ///
string Theme { get; }
+ ///
+ /// Set the theme with the given value
+ ///
+ /// The theme to set
void UpdateTheme(string theme);
}
}
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControl.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControl.cs
index 3beb92c5..055ad80c 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControl.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControl.cs
@@ -8,12 +8,24 @@ namespace PepperDash.Essentials.Touchpanel
///
public interface ITswAppControl : IKeyed
{
+ ///
+ /// Updates when the Zoom Room Control Application opens or closes
+ ///
BoolFeedback AppOpenFeedback { get; }
+ ///
+ /// Hide the Zoom App and show the User Control Application
+ ///
void HideOpenApp();
+ ///
+ /// Close the Zoom App and show the User Control Application
+ ///
void CloseOpenApp();
+ ///
+ /// Open the Zoom App
+ ///
void OpenApp();
}
@@ -22,10 +34,19 @@ namespace PepperDash.Essentials.Touchpanel
///
public interface ITswZoomControl : IKeyed
{
+ ///
+ /// Updates when Zoom has an incoming call
+ ///
BoolFeedback ZoomIncomingCallFeedback { get; }
+ ///
+ /// Updates when Zoom is in a call
+ ///
BoolFeedback ZoomInCallFeedback { get; }
+ ///
+ /// End a Zoom Call
+ ///
void EndZoomCall();
}
}
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControlMessenger.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControlMessenger.cs
index f9581c74..d4c55f87 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControlMessenger.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswAppControlMessenger.cs
@@ -7,17 +7,24 @@ using PepperDash.Essentials.AppServer.Messengers;
namespace PepperDash.Essentials.Touchpanel
{
///
- /// Represents a ITswAppControlMessenger
+ /// Messenger for controlling the Zoom App on a TSW Panel that supports the Zoom Room Control Application
///
public class ITswAppControlMessenger : MessengerBase
{
private readonly ITswAppControl _appControl;
+ ///
+ /// Create an instance of the class.
+ ///
+ /// The key for this messenger
+ /// The message path for this messenger
+ /// The device for this messenger
public ITswAppControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
{
_appControl = device as ITswAppControl;
}
+ ///
protected override void RegisterActions()
{
if (_appControl == null)
@@ -43,14 +50,14 @@ namespace PepperDash.Essentials.Touchpanel
};
}
- private void SendFullStatus()
+ private void SendFullStatus(string id = null)
{
var message = new TswAppStateMessage
{
AppOpen = _appControl.AppOpenFeedback.BoolValue,
};
- PostStatusMessage(message);
+ PostStatusMessage(message, id);
}
}
@@ -59,6 +66,9 @@ namespace PepperDash.Essentials.Touchpanel
///
public class TswAppStateMessage : DeviceStateMessageBase
{
+ ///
+ /// True if the Zoom app is open on a TSW panel
+ ///
[JsonProperty("appOpen", NullValueHandling = NullValueHandling.Ignore)]
public bool? AppOpen { get; set; }
}
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswZoomControlMessenger.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswZoomControlMessenger.cs
index 48c7fb7f..bbf4030e 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswZoomControlMessenger.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ITswZoomControlMessenger.cs
@@ -8,17 +8,24 @@ using PepperDash.Essentials.AppServer.Messengers;
namespace PepperDash.Essentials.Touchpanel
{
///
- /// Represents a ITswZoomControlMessenger
+ /// Messenger to handle Zoom status and control for a TSW panel that supports the Zoom Application
///
public class ITswZoomControlMessenger : MessengerBase
{
private readonly ITswZoomControl _zoomControl;
+ ///
+ /// Create an instance of the class for the given device
+ ///
+ /// The key for this messenger
+ /// The message path for this messenger
+ /// The device for this messenger
public ITswZoomControlMessenger(string key, string messagePath, Device device) : base(key, messagePath, device)
{
_zoomControl = device as ITswZoomControl;
}
+ ///
protected override void RegisterActions()
{
if (_zoomControl == null)
@@ -27,7 +34,9 @@ namespace PepperDash.Essentials.Touchpanel
return;
}
- AddAction($"/fullStatus", (id, context) => SendFullStatus());
+ AddAction($"/fullStatus", (id, context) => SendFullStatus(id));
+
+ AddAction($"/zoomStatus", (id, content) => SendFullStatus(id));
AddAction($"/endCall", (id, context) => _zoomControl.EndZoomCall());
@@ -53,7 +62,7 @@ namespace PepperDash.Essentials.Touchpanel
};
}
- private void SendFullStatus()
+ private void SendFullStatus(string id = null)
{
var message = new TswZoomStateMessage
{
@@ -61,7 +70,7 @@ namespace PepperDash.Essentials.Touchpanel
IncomingCall = _zoomControl?.ZoomIncomingCallFeedback.BoolValue
};
- PostStatusMessage(message);
+ PostStatusMessage(message, id);
}
}
@@ -70,9 +79,16 @@ namespace PepperDash.Essentials.Touchpanel
///
public class TswZoomStateMessage : DeviceStateMessageBase
{
+ ///
+ /// True if the panel is in a Zoom call
+ ///
[JsonProperty("inCall", NullValueHandling = NullValueHandling.Ignore)]
public bool? InCall { get; set; }
+ ///
+ /// True if there is an incoming Zoom call
+ ///
+
[JsonProperty("incomingCall", NullValueHandling = NullValueHandling.Ignore)]
public bool? IncomingCall { get; set; }
}
diff --git a/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs b/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs
index 089665d9..0f02bc59 100644
--- a/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs
+++ b/src/PepperDash.Essentials.MobileControl/Touchpanel/ThemeMessenger.cs
@@ -7,17 +7,24 @@ using PepperDash.Essentials.AppServer.Messengers;
namespace PepperDash.Essentials.Touchpanel
{
///
- /// Represents a ThemeMessenger
+ /// Messenger to save the current theme (light/dark) and send to a device
///
public class ThemeMessenger : MessengerBase
{
private readonly ITheme _tpDevice;
+ ///
+ /// Create an instance of the class
+ ///
+ /// The key for this messenger
+ /// The path for this messenger
+ /// The device for this messenger
public ThemeMessenger(string key, string path, ITheme device) : base(key, path, device as Device)
{
_tpDevice = device;
}
+ ///
protected override void RegisterActions()
{
AddAction("/fullStatus", (id, content) =>
diff --git a/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs b/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs
index 38f7944f..06595a9d 100644
--- a/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs
+++ b/src/PepperDash.Essentials.MobileControl/TransmitMessage.cs
@@ -1,13 +1,9 @@
-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
@@ -20,12 +16,22 @@ namespace PepperDash.Essentials
private readonly WebSocket _ws;
private readonly object msgToSend;
+ ///
+ /// Initialize a message to send
+ ///
+ /// message object to send
+ /// WebSocket instance
public TransmitMessage(object msg, WebSocket ws)
{
_ws = ws;
msgToSend = msg;
}
+ ///
+ /// Initialize a message to send
+ ///
+ /// message object to send
+ /// WebSocket instance
public TransmitMessage(DeviceStateMessageBase msg, WebSocket ws)
{
_ws = ws;
@@ -43,13 +49,13 @@ namespace PepperDash.Essentials
{
if (_ws == null)
{
- Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Websocket client is null");
+ Debug.LogWarning("Cannot send message. Websocket client is null");
return;
}
if (!_ws.IsAlive)
{
- Debug.LogMessage(LogEventLevel.Warning, "Cannot send message. Websocket client is not connected");
+ Debug.LogWarning("Cannot send message. Websocket client is not connected");
return;
}
@@ -57,83 +63,14 @@ namespace PepperDash.Essentials
var message = JsonConvert.SerializeObject(msgToSend, Formatting.None,
new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore, Converters = { new IsoDateTimeConverter() } });
- Debug.LogMessage(LogEventLevel.Verbose, "Message TX: {0}", null, message);
+ Debug.LogVerbose("Message TX: {0}", message);
_ws.Send(message);
-
-
}
catch (Exception ex)
{
- Debug.LogMessage(ex, "Caught an exception in the Transmit Processor");
- }
- }
- #endregion
- }
-
-
-
- ///
- /// Represents a MessageToClients
- ///
- 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
-
- ///
- /// Dispatch method
- ///
- 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");
+ Debug.LogError("Caught an exception in the Transmit Processor: {message}", ex.Message);
+ Debug.LogDebug(ex, "Stack Trace: ");
}
}
#endregion
diff --git a/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs b/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs
index ab899167..6c510d14 100644
--- a/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs
+++ b/src/PepperDash.Essentials.MobileControl/UserCodeChanged.cs
@@ -3,12 +3,19 @@ using System;
namespace PepperDash.Essentials
{
///
- /// Represents a UserCodeChanged
+ /// Defines the action to take when the User code changes
///
public class UserCodeChanged
{
+ ///
+ /// Action to take when the User Code changes
+ ///
public Action UpdateUserCode { get; private set; }
+ ///
+ /// create an instance of the class
+ ///
+ /// action to take when the User Code changes
public UserCodeChanged(Action updateMethod)
{
UpdateUserCode = updateMethod;
diff --git a/src/PepperDash.Essentials.MobileControl/Utilities.cs b/src/PepperDash.Essentials.MobileControl/Utilities.cs
new file mode 100644
index 00000000..8c2abf3e
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl/Utilities.cs
@@ -0,0 +1,97 @@
+using PepperDash.Core;
+using PepperDash.Core.Logging;
+using WebSocketSharp;
+
+namespace PepperDash.Essentials
+{
+ ///
+ /// Utility functions for logging and other common tasks.
+ ///
+ public static class Utilities
+ {
+ private static int nextClientId = 0;
+
+ ///
+ /// Get the next unique client ID
+ ///
+ /// Client ID
+ public static int GetNextClientId()
+ {
+ nextClientId++;
+ return nextClientId;
+ }
+ ///
+ /// Converts a WebSocketServer LogData object to Essentials logging calls.
+ ///
+ /// The LogData object to convert.
+ /// The log message.
+ /// The device associated with the log message.
+ public static void ConvertWebsocketLog(LogData data, string message, IKeyed device = null)
+ {
+
+ switch (data.Level)
+ {
+ case LogLevel.Trace:
+ if (device == null)
+ {
+ Debug.LogVerbose(message);
+ }
+ else
+ {
+ device.LogVerbose(message);
+ }
+ break;
+ case LogLevel.Debug:
+ if (device == null)
+ {
+ Debug.LogDebug(message);
+ }
+ else
+ {
+ device.LogDebug(message);
+ }
+ break;
+ case LogLevel.Info:
+ if (device == null)
+ {
+ Debug.LogInformation(message);
+ }
+ else
+ {
+ device.LogInformation(message);
+ }
+ break;
+ case LogLevel.Warn:
+ if (device == null)
+ {
+ Debug.LogWarning(message);
+ }
+ else
+ {
+ device.LogWarning(message);
+ }
+ break;
+ case LogLevel.Error:
+ if (device == null)
+ {
+ Debug.LogError(message);
+ }
+ else
+ {
+ device.LogError(message);
+ }
+ break;
+ case LogLevel.Fatal:
+ if (device == null)
+ {
+ Debug.LogFatal(message);
+ }
+ else
+ {
+ device.LogFatal(message);
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl/Volumes.cs b/src/PepperDash.Essentials.MobileControl/Volumes.cs
index 84accd26..44febbfc 100644
--- a/src/PepperDash.Essentials.MobileControl/Volumes.cs
+++ b/src/PepperDash.Essentials.MobileControl/Volumes.cs
@@ -15,15 +15,17 @@ namespace PepperDash.Essentials
[JsonProperty("master", NullValueHandling = NullValueHandling.Ignore)]
public Volume Master { get; set; }
+ ///
+ /// Aux Faders as configured in the room
+ ///
[JsonProperty("auxFaders", NullValueHandling = NullValueHandling.Ignore)]
public Dictionary AuxFaders { get; set; }
+ ///
+ /// Count of aux faders for this system
+ ///
[JsonProperty("numberOfAuxFaders", NullValueHandling = NullValueHandling.Ignore)]
public int? NumberOfAuxFaders { get; set; }
-
- public Volumes()
- {
- }
}
///
@@ -31,16 +33,21 @@ namespace PepperDash.Essentials
///
public class Volume
{
-
///
/// Gets or sets the Key
///
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
public string Key { get; set; }
+ ///
+ /// Level for this volume object
+ ///
[JsonProperty("level", NullValueHandling = NullValueHandling.Ignore)]
public int? Level { get; set; }
+ ///
+ /// True if this volume control is muted
+ ///
[JsonProperty("muted", NullValueHandling = NullValueHandling.Ignore)]
public bool? Muted { get; set; }
@@ -51,12 +58,21 @@ namespace PepperDash.Essentials
[JsonProperty("label", NullValueHandling = NullValueHandling.Ignore)]
public string Label { get; set; }
+ ///
+ /// True if this volume object has mute control
+ ///
[JsonProperty("hasMute", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasMute { get; set; }
+ ///
+ /// True if this volume object has Privacy mute control
+ ///
[JsonProperty("hasPrivacyMute", NullValueHandling = NullValueHandling.Ignore)]
public bool? HasPrivacyMute { get; set; }
+ ///
+ /// True if the privacy mute is muted
+ ///
[JsonProperty("privacyMuted", NullValueHandling = NullValueHandling.Ignore)]
public bool? PrivacyMuted { get; set; }
@@ -68,6 +84,15 @@ namespace PepperDash.Essentials
[JsonProperty("muteIcon", NullValueHandling = NullValueHandling.Ignore)]
public string MuteIcon { get; set; }
+ ///
+ /// Create an instance of the class
+ ///
+ /// The key for this volume object
+ /// The level for this volume object
+ /// True if this volume control is muted
+ /// The label for this volume object
+ /// True if this volume object has mute control
+ /// The mute icon for this volume object
public Volume(string key, int level, bool muted, string label, bool hasMute, string muteIcon)
: this(key)
{
@@ -78,18 +103,32 @@ namespace PepperDash.Essentials
MuteIcon = muteIcon;
}
+ ///
+ /// Create an instance of the class
+ ///
+ /// The key for this volume object
+ /// The level for this volume object
public Volume(string key, int level)
: this(key)
{
Level = level;
}
+ ///
+ /// Create an instance of the class
+ ///
+ /// The key for this volume object
+ /// True if this volume control is muted
public Volume(string key, bool muted)
: this(key)
{
Muted = muted;
}
+ ///
+ /// Create an instance of the class
+ ///
+ /// The key for this volume object
public Volume(string key)
{
Key = key;
diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs
index 84d0318a..1c204e8e 100644
--- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/ActionPathsHandler.cs
@@ -12,11 +12,20 @@ namespace PepperDash.Essentials.WebApiHandlers
public class ActionPathsHandler : WebApiBaseRequestHandler
{
private readonly MobileControlSystemController mcController;
+
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public ActionPathsHandler(MobileControlSystemController controller) : base(true)
{
mcController = controller;
}
+ ///
+ /// Handle a request to get the action paths
+ ///
+ /// Request Context
protected override void HandleGet(HttpCwsContext context)
{
var response = JsonConvert.SerializeObject(new ActionPathsResponse(mcController));
@@ -37,9 +46,16 @@ namespace PepperDash.Essentials.WebApiHandlers
[JsonIgnore]
private readonly MobileControlSystemController mcController;
+ ///
+ /// Registered action paths for this system
+ ///
[JsonProperty("actionPaths")]
public List ActionPaths => mcController.GetActionDictionaryPaths().Select((path) => new ActionPath { MessengerKey = path.Item1, Path = path.Item2 }).ToList();
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public ActionPathsResponse(MobileControlSystemController mcController)
{
this.mcController = mcController;
diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileAuthRequestHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileAuthRequestHandler.cs
index 352c56e5..5d9e9766 100644
--- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileAuthRequestHandler.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileAuthRequestHandler.cs
@@ -1,10 +1,10 @@
-using Crestron.SimplSharp.WebScripting;
+using System;
+using System.Threading.Tasks;
+using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Web.RequestHandlers;
using PepperDash.Essentials.Core.Web;
-using System;
-using System.Threading.Tasks;
namespace PepperDash.Essentials.WebApiHandlers
{
@@ -15,11 +15,20 @@ namespace PepperDash.Essentials.WebApiHandlers
{
private readonly MobileControlSystemController mcController;
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public MobileAuthRequestHandler(MobileControlSystemController controller) : base(true)
{
mcController = controller;
}
+ ///
+ /// Handle authorization request for this processor
+ ///
+ /// request context
+ /// Task
protected override async Task HandlePost(HttpCwsContext context)
{
try
diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs
index d123dbcd..676fd3c8 100644
--- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/MobileInfoHandler.cs
@@ -1,26 +1,35 @@
-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
{
///
- /// Represents a MobileInfoHandler
+ /// Represents a MobileInfoHandler. Used with the Essentials CWS API
///
public class MobileInfoHandler : WebApiBaseRequestHandler
{
private readonly MobileControlSystemController mcController;
+
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public MobileInfoHandler(MobileControlSystemController controller) : base(true)
{
mcController = controller;
}
+ ///
+ /// Get Mobile Control Information
+ ///
+ ///
protected override void HandleGet(HttpCwsContext context)
{
try
@@ -50,14 +59,22 @@ namespace PepperDash.Essentials.WebApiHandlers
[JsonIgnore]
private readonly MobileControlSystemController mcController;
+ ///
+ /// Edge Server. Null if edge server is disabled
+ ///
[JsonProperty("edgeServer", NullValueHandling = NullValueHandling.Ignore)]
public MobileControlEdgeServer EdgeServer => mcController.Config.EnableApiServer ? new MobileControlEdgeServer(mcController) : null;
-
+ ///
+ /// Direct server. Null if the direct server is disabled
+ ///
[JsonProperty("directServer", NullValueHandling = NullValueHandling.Ignore)]
public MobileControlDirectServer DirectServer => mcController.Config.DirectServer.EnableDirectServer ? new MobileControlDirectServer(mcController.DirectServer) : null;
-
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public InformationResponse(MobileControlSystemController controller)
{
mcController = controller;
@@ -72,24 +89,46 @@ namespace PepperDash.Essentials.WebApiHandlers
[JsonIgnore]
private readonly MobileControlSystemController mcController;
+ ///
+ /// Mobile Control Edge Server address for this system
+ ///
[JsonProperty("serverAddress")]
public string ServerAddress => mcController.Config == null ? "No Config" : mcController.Host;
+ ///
+ /// System Name for this system
+ ///
[JsonProperty("systemName")]
public string SystemName => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].RoomName : "No Config";
+ ///
+ /// System URL for this system
+ ///
[JsonProperty("systemUrl")]
public string SystemUrl => ConfigReader.ConfigObject.SystemUrl;
+ ///
+ /// User code to use in MC UI for this system
+ ///
[JsonProperty("userCode")]
public string UserCode => mcController.RoomBridges.Count > 0 ? mcController.RoomBridges[0].UserCode : "Not available";
+ ///
+ /// True if connected to edge server
+ ///
[JsonProperty("connected")]
public bool Connected => mcController.Connected;
+ ///
+ /// Seconds since last comms with edge server
+ ///
[JsonProperty("secondsSinceLastAck")]
public int SecondsSinceLastAck => (DateTime.Now - mcController.LastAckMessage).Seconds;
+ ///
+ /// Create an instance of the class.
+ ///
+ /// controller to use for this
public MobileControlEdgeServer(MobileControlSystemController controller)
{
mcController = controller;
@@ -104,21 +143,43 @@ namespace PepperDash.Essentials.WebApiHandlers
[JsonIgnore]
private readonly MobileControlWebsocketServer directServer;
+ ///
+ /// URL to use to interact with this server
+ ///
[JsonProperty("userAppUrl")]
public string UserAppUrl => $"{directServer.UserAppUrlPrefix}/[insert_client_token]";
+ ///
+ /// TCP/IP Port this server is configured to use
+ ///
[JsonProperty("serverPort")]
public int ServerPort => directServer.Port;
+ ///
+ /// Count of defined tokens for this server
+ ///
[JsonProperty("tokensDefined")]
- public int TokensDefined => directServer.UiClients.Count;
+ public int TokensDefined => directServer.UiClientContexts.Count;
+ ///
+ /// Count of connected clients
+ ///
[JsonProperty("clientsConnected")]
public int ClientsConnected => directServer.ConnectedUiClientsCount;
+ ///
+ /// List of tokens and connected clients for this server
+ ///
[JsonProperty("clients")]
- public List Clients => directServer.UiClients.Select((c, i) => { return new MobileControlDirectClient(c, i, directServer.UserAppUrlPrefix); }).ToList();
+ public List Clients => directServer.UiClientContexts
+ .Select(context => (context, clients: directServer.UiClients.Where(client => client.Value.Token == context.Value.Token.Token).Select(c => c.Value).ToList()))
+ .Select((clientTuple, i) => new MobileControlDirectClient(clientTuple.clients, clientTuple.context, i, directServer.UserAppUrlPrefix))
+ .ToList();
+ ///
+ /// Create an instance of the class.
+ ///
+ ///
public MobileControlDirectServer(MobileControlWebsocketServer server)
{
directServer = server;
@@ -142,33 +203,85 @@ namespace PepperDash.Essentials.WebApiHandlers
[JsonIgnore]
private readonly string urlPrefix;
+ ///
+ /// Client number for this client
+ ///
[JsonProperty("clientNumber")]
public string ClientNumber => $"{clientNumber}";
+ ///
+ /// Room Key for this client
+ ///
[JsonProperty("roomKey")]
public string RoomKey => context.Token.RoomKey;
+ ///
+ /// Touchpanel Key, if defined, for this client
+ ///
[JsonProperty("touchpanelKey")]
public string TouchpanelKey => context.Token.TouchpanelKey;
+ ///
+ /// URL for this client
+ ///
[JsonProperty("url")]
public string Url => $"{urlPrefix}{Key}";
+ ///
+ /// Token for this client
+ ///
[JsonProperty("token")]
public string Token => Key;
- [JsonProperty("connected")]
- public bool Connected => context.Client != null && context.Client.Context.WebSocket.IsAlive;
+ private readonly List clients;
- [JsonProperty("duration")]
- public double Duration => context.Client == null ? 0 : context.Client.ConnectedDuration.TotalSeconds;
+ ///
+ /// List of status for all connected UI Clients
+ ///
+ [JsonProperty("clientStatus")]
+ public List ClientStatus => clients.Select(c => new ClientStatus(c)).ToList();
- public MobileControlDirectClient(KeyValuePair clientContext, int index, string urlPrefix)
+ ///
+ /// Create an instance of the class.
+ ///
+ /// List of Websocket Clients
+ /// Context for the client
+ /// Index of the client
+ /// URL prefix for the client
+ public MobileControlDirectClient(List clients, KeyValuePair context, int index, string urlPrefix)
{
- context = clientContext.Value;
- Key = clientContext.Key;
+ this.context = context.Value;
+ Key = context.Key;
clientNumber = index;
this.urlPrefix = urlPrefix;
+ this.clients = clients;
+ }
+ }
+
+ ///
+ /// Report the status of a UiClient
+ ///
+ public class ClientStatus
+ {
+ private readonly UiClient client;
+
+ ///
+ /// True if client is connected
+ ///
+ public bool Connected => client != null && client.Context.WebSocket.IsAlive;
+
+ ///
+ /// Get the time this client has been connected
+ ///
+ public double Duration => client == null ? 0 : client.ConnectedDuration.TotalSeconds;
+
+ ///
+ /// Create an instance of the class for the specified client
+ ///
+ /// client to report on
+ public ClientStatus(UiClient client)
+ {
+ this.client = client;
}
}
}
diff --git a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs
index e45fcc39..2f972ddf 100644
--- a/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebApiHandlers/UiClientHandler.cs
@@ -14,11 +14,20 @@ namespace PepperDash.Essentials.WebApiHandlers
public class UiClientHandler : WebApiBaseRequestHandler
{
private readonly MobileControlWebsocketServer server;
+
+ ///
+ /// Essentials CWS API handler for the MC Direct Server
+ ///
+ /// Direct Server instance
public UiClientHandler(MobileControlWebsocketServer directServer) : base(true)
{
server = directServer;
}
+ ///
+ /// Create a client for the Direct Server
+ ///
+ /// HTTP Context for this request
protected override void HandlePost(HttpCwsContext context)
{
var req = context.Request;
@@ -65,6 +74,10 @@ namespace PepperDash.Essentials.WebApiHandlers
res.End();
}
+ ///
+ /// Handle DELETE request for a Client
+ ///
+ ///
protected override void HandleDelete(HttpCwsContext context)
{
var req = context.Request;
@@ -93,7 +106,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
{
@@ -134,7 +147,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/ConnectionClosedEventArgs.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ConnectionClosedEventArgs.cs
new file mode 100644
index 00000000..617cc6d6
--- /dev/null
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ConnectionClosedEventArgs.cs
@@ -0,0 +1,24 @@
+using System;
+
+namespace PepperDash.Essentials.WebSocketServer
+{
+ ///
+ /// Event Args for ConnectionClosed event
+ ///
+ public class ConnectionClosedEventArgs : EventArgs
+ {
+ ///
+ /// Client ID that is being closed
+ ///
+ public string ClientId { get; private set; }
+
+ ///
+ /// Initialize an instance of the class.
+ ///
+ /// client that's closing
+ public ConnectionClosedEventArgs(string clientId)
+ {
+ ClientId = clientId;
+ }
+ }
+}
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs
index ba214db4..1529d0fe 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinResponse.cs
@@ -17,9 +17,15 @@ namespace PepperDash.Essentials.WebSocketServer
[JsonProperty("clientId")]
public string ClientId { get; set; }
+ ///
+ /// Room Key for this client
+ ///
[JsonProperty("roomKey")]
public string RoomKey { get; set; }
+ ///
+ /// System UUID for this system
+ ///
[JsonProperty("systemUUid")]
public string SystemUuid { get; set; }
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs
index b3ea3c7c..63fdcdd4 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/JoinToken.cs
@@ -5,15 +5,28 @@ namespace PepperDash.Essentials.WebSocketServer
///
public class JoinToken
{
+ ///
+ /// Unique client ID for a client that is joining
+ ///
+ public string Id { get; set; }
///
/// Gets or sets the Code
///
public string Code { get; set; }
+ ///
+ /// Room Key this token is associated with
+ ///
public string RoomKey { get; set; }
+ ///
+ /// Unique ID for this token
+ ///
public string Uuid { get; set; }
+ ///
+ /// Touchpanel Key this token is associated with, if this is a touch panel token
+ ///
public string TouchpanelKey { get; set; } = "";
///
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
index d15185b0..a30c0bd2 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/MobileControlWebsocketServer.cs
@@ -10,6 +10,7 @@ using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharp.WebScripting;
using Newtonsoft.Json;
+using Org.BouncyCastle.Crypto.Prng;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
@@ -56,7 +57,14 @@ namespace PepperDash.Essentials.WebSocketServer
///
/// Gets the collection of UI client contexts
///
- public Dictionary UiClients { get; private set; }
+ public Dictionary UiClientContexts { get; private set; }
+
+ private readonly Dictionary uiClients = new Dictionary();
+
+ ///
+ /// Gets the collection of UI clients
+ ///
+ public ReadOnlyDictionary UiClients => new ReadOnlyDictionary(uiClients);
private readonly MobileControlSystemController _parent;
@@ -127,17 +135,7 @@ namespace PepperDash.Essentials.WebSocketServer
{
get
{
- var count = 0;
-
- foreach (var client in UiClients)
- {
- if (client.Value.Client != null && client.Value.Client.Context.WebSocket.IsAlive)
- {
- count++;
- }
- }
-
- return count;
+ return uiClients.Values.Where(c => c.Context.WebSocket.IsAlive).Count();
}
}
@@ -202,7 +200,7 @@ namespace PepperDash.Essentials.WebSocketServer
}
- UiClients = new Dictionary();
+ UiClientContexts = new Dictionary();
//_joinTokens = new Dictionary();
@@ -277,30 +275,10 @@ namespace PepperDash.Essentials.WebSocketServer
};
}
- _server.Log.Output = (data, message) =>
- {
- switch (data.Level)
- {
- case LogLevel.Trace:
- this.LogVerbose(data.Message);
- break;
- case LogLevel.Debug:
- this.LogDebug(data.Message);
- break;
- case LogLevel.Info:
- this.LogInformation(data.Message);
- break;
- case LogLevel.Warn:
- this.LogWarning(data.Message);
- break;
- case LogLevel.Error:
- this.LogError(data.Message);
- break;
- case LogLevel.Fatal:
- this.LogFatal(data.Message);
- break;
- }
- };
+ _server.Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
+
+ // setting to trace to allow logging level to be controlled by appdebug
+ _server.Log.Level = LogLevel.Trace;
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
@@ -326,6 +304,9 @@ namespace PepperDash.Essentials.WebSocketServer
}
}
+ ///
+ /// Set the internal logging level for the Websocket Server
+ ///
public void SetWebsocketLogLevel(LogLevel level)
{
CrestronConsole.ConsoleCommandResponse($"Setting direct server debug level to {level}", level.ToString());
@@ -554,20 +535,20 @@ namespace PepperDash.Essentials.WebSocketServer
this.LogInformation("Adding token: {key} for room: {roomKey}", token.Key, token.Value.RoomKey);
- if (UiClients == null)
+ if (UiClientContexts == null)
{
- 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)
{
- this.LogInformation("Restored {uiClientCount} UiClients from secrets data", UiClients.Count);
+ this.LogInformation("Restored {uiClientCount} UiClients from secrets data", UiClientContexts.Count);
- foreach (var client in UiClients)
+ foreach (var client in UiClientContexts)
{
var key = client.Key;
var path = _wsPath + key;
@@ -575,13 +556,8 @@ namespace PepperDash.Essentials.WebSocketServer
_server.AddWebSocketService(path, () =>
{
- var c = new UiClient($"uiclient-{key}-{roomKey}");
- this.LogDebug("Constructing UiClient with id: {key}", key);
-
- c.Controller = _parent;
- c.RoomKey = roomKey;
- UiClients[key].SetClient(c);
- return c;
+ this.LogInformation("Building a UiClient with ID {id}", client.Value.Token.Id);
+ return BuildUiClient(roomKey, client.Value.Token, key);
});
}
}
@@ -591,7 +567,7 @@ namespace PepperDash.Essentials.WebSocketServer
this.LogWarning("No secret found");
}
- this.LogDebug("{uiClientCount} UiClients restored from secrets data", UiClients.Count);
+ this.LogDebug("{uiClientCount} UiClients restored from secrets data", UiClientContexts.Count);
}
catch (Exception ex)
{
@@ -616,7 +592,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);
}
@@ -725,21 +701,17 @@ namespace PepperDash.Essentials.WebSocketServer
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($"uiclient-{key}-{bridge.RoomKey}");
- this.LogVerbose("Constructing UiClient with id: {key}", key);
- c.Controller = _parent;
- c.RoomKey = bridge.RoomKey;
- UiClients[key].SetClient(c);
- return c;
+ this.LogInformation("Building a UiClient with ID {id}", token.Id);
+ return BuildUiClient(bridge.RoomKey, token, key);
});
- this.LogInformation("Added new WebSocket UiClient service at path: {path}", path);
+ this.LogInformation("Added new WebSocket UiClient for path: {path}", path);
this.LogInformation("Token: {@token}", token);
this.LogVerbose("{serviceCount} websocket services present", _server.WebSocketServices.Count);
@@ -749,6 +721,44 @@ namespace PepperDash.Essentials.WebSocketServer
return (key, path);
}
+ private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
+ {
+ var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token);
+ this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id);
+ c.Controller = _parent;
+ c.RoomKey = roomKey;
+
+ if (uiClients.ContainsKey(token.Id))
+ {
+ this.LogWarning("removing client with duplicate id {id}", token.Id);
+ uiClients.Remove(token.Id);
+ }
+ uiClients.Add(token.Id, c);
+ // UiClients[key].SetClient(c);
+ c.ConnectionClosed += (o, a) => uiClients.Remove(a.ClientId);
+ token.Id = null;
+ return c;
+ }
+
+ ///
+ /// Prints out the session data for each path
+ ///
+ public void PrintSessionData()
+ {
+ foreach (var path in _server.WebSocketServices.Paths)
+ {
+ this.LogInformation("Path: {path}", path);
+ this.LogInformation(" Session Count: {sessionCount}", _server.WebSocketServices[path].Sessions.Count);
+ this.LogInformation(" Active Session Count: {activeSessionCount}", _server.WebSocketServices[path].Sessions.ActiveIDs.Count());
+ this.LogInformation(" Inactive Session Count: {inactiveSessionCount}", _server.WebSocketServices[path].Sessions.InactiveIDs.Count());
+ this.LogInformation(" Active Clients:");
+ foreach (var session in _server.WebSocketServices[path].Sessions.IDs)
+ {
+ this.LogInformation(" Client ID: {id}", (_server.WebSocketServices[path].Sessions[session] as UiClient)?.Id);
+ }
+ }
+ }
+
///
/// Removes all clients from the server
///
@@ -766,7 +776,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)
{
@@ -784,7 +794,7 @@ namespace PepperDash.Essentials.WebSocketServer
}
}
- UiClients.Clear();
+ UiClientContexts.Clear();
UpdateSecret();
}
@@ -803,9 +813,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)
{
@@ -815,7 +825,7 @@ namespace PepperDash.Essentials.WebSocketServer
var path = _wsPath + key;
if (_server.RemoveWebSocketService(path))
{
- UiClients.Remove(key);
+ UiClientContexts.Remove(key);
UpdateSecret();
@@ -839,9 +849,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));
}
@@ -853,9 +863,9 @@ namespace PepperDash.Essentials.WebSocketServer
{
foreach (var client in UiClients.Values)
{
- if (client.Client != null && client.Client.Context.WebSocket.IsAlive)
+ if (client != null && client.Context.WebSocket.IsAlive)
{
- client.Client.Context.WebSocket.Close(CloseStatusCode.Normal, "Server Shutting Down");
+ client.Context.WebSocket.Close(CloseStatusCode.Normal, "Server Shutting Down");
}
}
@@ -990,77 +1000,81 @@ namespace PepperDash.Essentials.WebSocketServer
this.LogVerbose("Join Room Request with token: {token}", token);
+ byte[] body;
- if (UiClients.TryGetValue(token, out UiClientContext clientContext))
- {
- var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey);
-
- if (bridge != null)
- {
- res.StatusCode = 200;
- res.ContentType = "application/json";
-
- var devices = DeviceManager.GetDevices();
- Dictionary deviceInterfaces = new Dictionary();
-
- foreach (var device in devices)
- {
- var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List();
- deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo
- {
- Key = device.Key,
- Name = device is IKeyName ? (device as IKeyName).Name : "",
- Interfaces = interfaces
- });
- }
-
- // Construct the response object
- JoinResponse jRes = new JoinResponse
- {
- ClientId = token,
- RoomKey = bridge.RoomKey,
- SystemUuid = _parent.SystemUuid,
- RoomUuid = _parent.SystemUuid,
- Config = _parent.GetConfigWithPluginVersion(),
- CodeExpires = new DateTime().AddYears(1),
- UserCode = bridge.UserCode,
- UserAppUrl = string.Format("http://{0}:{1}/mc/app",
- CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
- Port),
- EnableDebug = false,
- DeviceInterfaceSupport = deviceInterfaces
- };
-
- // Serialize to JSON and convert to Byte[]
- var json = JsonConvert.SerializeObject(jRes);
- var body = Encoding.UTF8.GetBytes(json);
- res.ContentLength64 = body.LongLength;
-
- // Send the response
- res.Close(body, true);
- }
- else
- {
- var message = string.Format("Unable to find bridge with key: {0}", clientContext.Token.RoomKey);
- res.StatusCode = 404;
- res.ContentType = "application/json";
- this.LogVerbose("{message}", message);
- var body = Encoding.UTF8.GetBytes(message);
- res.ContentLength64 = body.LongLength;
- res.Close(body, true);
-
- }
- }
- else
+ if (!UiClientContexts.TryGetValue(token, out UiClientContext clientContext))
{
var message = "Token invalid or has expired";
res.StatusCode = 401;
res.ContentType = "application/json";
this.LogVerbose("{message}", message);
- var body = Encoding.UTF8.GetBytes(message);
+ body = Encoding.UTF8.GetBytes(message);
res.ContentLength64 = body.LongLength;
res.Close(body, true);
+ return;
}
+
+ var bridge = _parent.GetRoomBridge(clientContext.Token.RoomKey);
+
+ if (bridge == null)
+ {
+ var message = string.Format("Unable to find bridge with key: {0}", clientContext.Token.RoomKey);
+ res.StatusCode = 404;
+ res.ContentType = "application/json";
+ this.LogVerbose("{message}", message);
+ body = Encoding.UTF8.GetBytes(message);
+ res.ContentLength64 = body.LongLength;
+ res.Close(body, true);
+ return;
+ }
+
+ res.StatusCode = 200;
+ res.ContentType = "application/json";
+
+ var devices = DeviceManager.GetDevices();
+ Dictionary deviceInterfaces = new Dictionary();
+
+ foreach (var device in devices)
+ {
+ var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List();
+
+ deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo
+ {
+ Key = device.Key,
+ Name = (device as IKeyName)?.Name ?? "",
+ Interfaces = interfaces
+ });
+ }
+
+ var clientId = $"{Utilities.GetNextClientId()}";
+ clientContext.Token.Id = clientId;
+
+ this.LogVerbose("Assigning ClientId: {clientId}", clientId);
+
+ // Construct the response object
+ JoinResponse jRes = new JoinResponse
+ {
+ ClientId = clientId,
+ RoomKey = bridge.RoomKey,
+ SystemUuid = _parent.SystemUuid,
+ RoomUuid = _parent.SystemUuid,
+ Config = _parent.GetConfigWithPluginVersion(),
+ CodeExpires = new DateTime().AddYears(1),
+ UserCode = bridge.UserCode,
+ UserAppUrl = string.Format("http://{0}:{1}/mc/app",
+ CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
+ Port),
+ EnableDebug = false,
+ DeviceInterfaceSupport = deviceInterfaces
+ };
+
+ // Serialize to JSON and convert to Byte[]
+ var json = JsonConvert.SerializeObject(jRes);
+ body = Encoding.UTF8.GetBytes(json);
+ res.ContentLength64 = body.LongLength;
+
+ // Send the response
+ res.Close(body, true);
}
///
@@ -1242,12 +1256,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);
}
}
@@ -1266,17 +1282,16 @@ namespace PepperDash.Essentials.WebSocketServer
return;
}
- if (UiClients.TryGetValue((string)clientId, out UiClientContext clientContext))
+ if (uiClients.TryGetValue((string)clientId, out var client))
{
- if (clientContext.Client != null)
- {
- var socket = clientContext.Client.Context.WebSocket;
+ var socket = client.Context.WebSocket;
- if (socket.IsAlive)
- {
- socket.Send(message);
- }
+ if (!socket.IsAlive)
+ {
+ this.LogError("Unable to send message to client {id}. Client is disconnected: {message}", clientId, message);
+ return;
}
+ socket.Send(message);
}
else
{
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs
index 3fa2fb0c..ad9a1d66 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/ServerTokenSecrets.cs
@@ -13,8 +13,15 @@ namespace PepperDash.Essentials.WebSocketServer
///
public string GrantCode { get; set; }
+ ///
+ /// Gets or sets the Tokens for this server
+ ///
public Dictionary Tokens { get; set; }
+ ///
+ /// Initialize a new instance of the class with the provided grant code
+ ///
+ /// The grant code for this server
public ServerTokenSecrets(string grantCode)
{
GrantCode = grantCode;
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs
index 3cdd183b..e4e8a47d 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClient.cs
@@ -1,5 +1,4 @@
using System;
-using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
@@ -22,6 +21,16 @@ namespace PepperDash.Essentials.WebSocketServer
///
public string Key { get; private set; }
+ ///
+ /// Client ID used by client for this connection
+ ///
+ public string Id { get; private set; }
+
+ ///
+ /// Token associated with this client
+ ///
+ public string Token { get; private set; }
+
///
/// Gets or sets the mobile control system controller that handles this client's messages
///
@@ -32,11 +41,6 @@ namespace PepperDash.Essentials.WebSocketServer
///
public string RoomKey { get; set; }
- ///
- /// The unique identifier for this client instance
- ///
- private string _clientId;
-
///
/// The timestamp when this client connection was established
///
@@ -60,13 +64,22 @@ namespace PepperDash.Essentials.WebSocketServer
}
}
+ ///
+ /// Triggered when this client closes it's connection
+ ///
+ public event EventHandler ConnectionClosed;
+
///
/// Initializes a new instance of the UiClient class with the specified key
///
/// The unique key to identify this client
- public UiClient(string key)
+ /// The client ID used by the client for this connection
+ /// The token associated with this client
+ public UiClient(string key, string id, string token)
{
Key = key;
+ Id = id;
+ Token = token;
}
///
@@ -74,19 +87,10 @@ namespace PepperDash.Essentials.WebSocketServer
{
base.OnOpen();
- var url = Context.WebSocket.Url;
- this.LogInformation("New WebSocket Connection from: {url}", url);
+ _connectionTime = DateTime.Now;
- var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)");
-
- if (!match.Success)
- {
- _connectionTime = DateTime.Now;
- return;
- }
-
- var clientId = match.Groups[1].Value;
- _clientId = clientId;
+ Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
+ Log.Level = LogLevel.Trace;
if (Controller == null)
{
@@ -99,7 +103,7 @@ namespace PepperDash.Essentials.WebSocketServer
Type = "/system/clientJoined",
Content = JToken.FromObject(new
{
- clientId,
+ clientId = Id,
roomKey = RoomKey,
})
};
@@ -110,7 +114,7 @@ namespace PepperDash.Essentials.WebSocketServer
if (bridge == null) return;
- SendUserCodeToClient(bridge, clientId);
+ SendUserCodeToClient(bridge, Id);
bridge.UserCodeChanged -= Bridge_UserCodeChanged;
bridge.UserCodeChanged += Bridge_UserCodeChanged;
@@ -125,7 +129,7 @@ namespace PepperDash.Essentials.WebSocketServer
/// Event arguments
private void Bridge_UserCodeChanged(object sender, EventArgs e)
{
- SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId);
+ SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, Id);
}
///
@@ -172,13 +176,15 @@ namespace PepperDash.Essentials.WebSocketServer
foreach (var messenger in Controller.Messengers)
{
- messenger.Value.UnsubscribeClient(_clientId);
+ messenger.Value.UnsubscribeClient(Id);
}
foreach (var messenger in Controller.DefaultMessengers)
{
- messenger.Value.UnsubscribeClient(_clientId);
+ messenger.Value.UnsubscribeClient(Id);
}
+
+ ConnectionClosed?.Invoke(this, new ConnectionClosedEventArgs(Id));
}
///
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs
index 6782306f..ffc09e90 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/UiClientContext.cs
@@ -14,6 +14,10 @@ namespace PepperDash.Essentials.WebSocketServer
///
public JoinToken Token { get; private set; }
+ ///
+ /// Initialize an instance of the class with the provided token
+ ///
+ /// token for this client
public UiClientContext(JoinToken token)
{
Token = token;
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs
index 5552e29b..9380e54d 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/Version.cs
@@ -8,12 +8,25 @@ namespace PepperDash.Essentials.WebSocketServer
///
public class Version
{
+ ///
+ /// Server version this Websocket is connected to
+ ///
[JsonProperty("serverVersion")]
public string ServerVersion { get; set; }
+ ///
+ /// True if the server is on a processor
+ ///
+
[JsonProperty("serverIsRunningOnProcessorHardware")]
public bool ServerIsRunningOnProcessorHardware { get; private set; }
+ ///
+ /// Initialize an instance of the class
+ ///
+ ///
+ /// The property is set to true by default.
+ ///
public Version()
{
ServerIsRunningOnProcessorHardware = true;
diff --git a/src/PepperDash.Essentials.MobileControl/WebSocketServer/WebSocketServerSecretProvider.cs b/src/PepperDash.Essentials.MobileControl/WebSocketServer/WebSocketServerSecretProvider.cs
index cf125d29..3dd67bd8 100644
--- a/src/PepperDash.Essentials.MobileControl/WebSocketServer/WebSocketServerSecretProvider.cs
+++ b/src/PepperDash.Essentials.MobileControl/WebSocketServer/WebSocketServerSecretProvider.cs
@@ -13,25 +13,28 @@ namespace PepperDash.Essentials.WebSocketServer
}
///
- /// Represents a WebSocketServerSecret
+ /// Stores a secret value using the provided secret store provider
///
public class WebSocketServerSecret : ISecret
{
///
- /// Gets or sets the Provider
+ /// Gets the Secret Provider associated with this secret
///
public ISecretProvider Provider { get; private set; }
///
- /// Gets or sets the Key
+ /// Gets the Key associated with this secret
///
public string Key { get; private set; }
///
- /// Gets or sets the Value
+ /// Gets the Value associated with this secret
///
public object Value { get; private set; }
+ ///
+ /// Initialize and instance of the class
+ ///
public WebSocketServerSecret(string key, object value, ISecretProvider provider)
{
Key = key;