diff --git a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
index eeba5772e..cfbf57852 100644
--- a/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
+++ b/src/PepperDash.Core/Logging/DebugWebsocketSink.cs
@@ -72,6 +72,20 @@ public string Url
///
public bool IsRunning { get => _httpsServer?.IsListening ?? false; }
+ ///
+ /// Gets a value indicating whether there are active WebSocket connections.
+ ///
+ public bool HasActiveConnections
+ {
+ get
+ {
+ if (_httpsServer == null || !_httpsServer.IsListening) return false;
+ var service = _httpsServer.WebSocketServices[_path];
+ if (service == null) return false;
+ return service.Sessions.Count > 0;
+ }
+ }
+
private readonly ITextFormatter _textFormatter;
@@ -217,6 +231,8 @@ public void StartServerAndSetPort(int port)
{
Debug.LogInformation("Starting Websocket Server on port: {0}", port);
+
+
Start(port, CertPath, _certificatePassword);
}
diff --git a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
index 1eeb27809..59c662dc2 100644
--- a/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
+++ b/src/PepperDash.Essentials.Core/Web/RequestHandlers/DebugSessionRequestHandler.cs
@@ -17,7 +17,10 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
/// Represents a DebugSessionRequestHandler
///
public class DebugSessionRequestHandler : WebApiBaseRequestHandler
- {
+ {
+ private CTimer _portForwardTimeoutTimer;
+ private readonly object _timerLock = new object();
+
///
/// Constructor
///
@@ -48,6 +51,7 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
var port = 0;
+ string csIp = null;
if (!Debug.WebsocketSink.IsRunning)
{
@@ -57,15 +61,18 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
// Start the WS Server
Debug.WebsocketSink.StartServerAndSetPort(port);
Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);
+ }
- // Attempt to forward the port to the CS LAN
- try
- {
- var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(
- EthernetAdapterType.EthernetCSAdapter);
- var csIp = CrestronEthernetHelper.GetEthernetParameter(
- CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
+ // Attempt to get the CS LAN IP and forward the port
+ try
+ {
+ var csAdapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(
+ EthernetAdapterType.EthernetCSAdapter);
+ csIp = CrestronEthernetHelper.GetEthernetParameter(
+ CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, csAdapterId);
+ if (port > 0)
+ {
var result = CrestronEthernetHelper.AddPortForwarding(
(ushort)port, (ushort)port, csIp,
CrestronEthernetHelper.ePortMapTransport.TCP);
@@ -77,26 +84,29 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
else
{
Debug.LogMessage(LogEventLevel.Information, "Port {0} forwarded to CS LAN for debug websocket", port);
+ StartPortForwardTimeout(port, csIp);
}
}
- catch (ArgumentException)
- {
- Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding");
- }
- catch (Exception ex)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Error automatically forwarding debug websocket port to CS LAN: {0}", ex.Message);
- }
+ }
+ catch (ArgumentException)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "This processor does not have a CS LAN adapter; skipping port forwarding");
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Error automatically forwarding debug websocket port to CS LAN: {0}", ex.Message);
}
var url = Debug.WebsocketSink.Url;
- object data = new
+ var data = new
{
- url = Debug.WebsocketSink.Url
+ url = Debug.WebsocketSink.Url,
+ fallbackUrl = csIp != null ? url.Replace(csIp, ip) : null
};
Debug.LogMessage(LogEventLevel.Information, "Debug Session URL: {0}", url);
+ Debug.LogMessage(LogEventLevel.Information, "Fallback Debug Session URL: {0}", data.fallbackUrl);
// Return the port number with the full url of the WS Server
var res = JsonConvert.SerializeObject(data);
@@ -120,6 +130,8 @@ protected override void HandleGet(Crestron.SimplSharp.WebScripting.HttpCwsContex
///
protected override void HandlePost(HttpCwsContext context)
{
+ CancelPortForwardTimeout();
+
var port = Debug.WebsocketSink.Port;
Debug.WebsocketSink.StopServer();
@@ -168,5 +180,55 @@ protected override void HandlePost(HttpCwsContext context)
Debug.LogMessage(LogEventLevel.Information, "Websocket Debug Session Stopped");
}
+ private void StartPortForwardTimeout(int port, string csIp)
+ {
+ lock (_timerLock)
+ {
+ _portForwardTimeoutTimer?.Dispose();
+ _portForwardTimeoutTimer = new CTimer(_ =>
+ {
+ if (Debug.WebsocketSink.HasActiveConnections)
+ {
+ Debug.LogMessage(LogEventLevel.Debug, "Debug websocket has active connections; keeping port forward");
+ return;
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "No debug websocket connection within 30 seconds; removing port forward for port {0}", port);
+
+ try
+ {
+ var result = CrestronEthernetHelper.RemovePortForwarding(
+ (ushort)port, (ushort)port, csIp,
+ CrestronEthernetHelper.ePortMapTransport.TCP);
+
+ if (result != CrestronEthernetHelper.PortForwardingUserPatRetCodes.NoErr)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding on timeout: {0}", result);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Port forwarding for port {0} removed due to timeout", port);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Error removing port forwarding on timeout: {0}", ex.Message);
+ }
+ }, 30000);
+ }
+ }
+
+ ///
+ /// Cancels the port forward timeout timer if a session is being explicitly stopped.
+ ///
+ private void CancelPortForwardTimeout()
+ {
+ lock (_timerLock)
+ {
+ _portForwardTimeoutTimer?.Dispose();
+ _portForwardTimeoutTimer = null;
+ }
+ }
+
}
}
diff --git a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
index 3031f4baa..eb3afec3b 100644
--- a/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
+++ b/src/PepperDash.Essentials.MobileControl.Messengers/Messengers/MessengerBase.cs
@@ -264,6 +264,9 @@ protected void PostStatusMessage(DeviceStateMessageBase message, string clientId
message.Name = _device.Name;
+ message.MessageBasePath = MessagePath;
+
+
var token = JToken.FromObject(message);
PostStatusMessage(token, MessagePath, clientId);