mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-04-13 12:37:07 +00:00
refactor: Clean up and streamline Debug and WebApiServer classes for improved readability and performance
This commit is contained in:
parent
5c26438a1c
commit
c98b48ff87
7 changed files with 379 additions and 280 deletions
|
|
@ -114,7 +114,7 @@ public static class Debug
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string PepperDashCoreVersion { get; private set; }
|
public static string PepperDashCoreVersion { get; private set; }
|
||||||
|
|
||||||
private static Timer _saveTimer;
|
// private static Timer _saveTimer;
|
||||||
|
|
||||||
|
|
||||||
private const int defaultConsoleDebugTimeoutMin = 120;
|
private const int defaultConsoleDebugTimeoutMin = 120;
|
||||||
|
|
@ -235,7 +235,7 @@ public static class Debug
|
||||||
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
|
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
// CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||||
|
|
||||||
DoNotLoadConfigOnNextBoot = GetDoNotLoadOnNextBoot();
|
DoNotLoadConfigOnNextBoot = GetDoNotLoadOnNextBoot();
|
||||||
|
|
||||||
|
|
@ -249,7 +249,8 @@ public static class Debug
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
LogError(ex, "Exception in Debug static constructor: {message}", ex.Message);
|
// _logger may not have been initialized yet — do not call LogError here.
|
||||||
|
CrestronConsole.PrintLine($"Exception in Debug static constructor: {ex.Message}\r\n{ex.StackTrace}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,26 +338,26 @@ public static class Debug
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Used to save memory when shutting down
|
// /// Used to save memory when shutting down
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="programEventType"></param>
|
// /// <param name="programEventType"></param>
|
||||||
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
// static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||||
{
|
// {
|
||||||
|
|
||||||
if (programEventType == eProgramStatusEventType.Stopping)
|
// if (programEventType == eProgramStatusEventType.Stopping)
|
||||||
{
|
// {
|
||||||
Log.CloseAndFlush();
|
// Log.CloseAndFlush();
|
||||||
|
|
||||||
if (_saveTimer != null)
|
// if (_saveTimer != null)
|
||||||
{
|
// {
|
||||||
_saveTimer.Stop();
|
// _saveTimer.Stop();
|
||||||
_saveTimer = null;
|
// _saveTimer = null;
|
||||||
}
|
// }
|
||||||
LogMessage(LogEventLevel.Information, "Saving debug settings");
|
// LogMessage(LogEventLevel.Information, "Saving debug settings");
|
||||||
SaveMemory();
|
// // SaveMemory();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Callback for console command
|
/// Callback for console command
|
||||||
|
|
@ -632,7 +633,7 @@ public static class Debug
|
||||||
public static void SetDeviceDebugSettings(string deviceKey, object settings)
|
public static void SetDeviceDebugSettings(string deviceKey, object settings)
|
||||||
{
|
{
|
||||||
_contexts.SetDebugSettingsForKey(deviceKey, settings);
|
_contexts.SetDebugSettingsForKey(deviceKey, settings);
|
||||||
SaveMemoryOnTimeout();
|
// SaveMemoryOnTimeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -1005,83 +1006,82 @@ public static class Debug
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Writes the memory object after timeout
|
// /// Writes the memory object after timeout
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
static void SaveMemoryOnTimeout()
|
// static void SaveMemoryOnTimeout()
|
||||||
{
|
// {
|
||||||
LogInformation("Saving debug settings");
|
// LogInformation("Saving debug settings");
|
||||||
if (_saveTimer == null)
|
// if (_saveTimer == null)
|
||||||
{
|
// {
|
||||||
_saveTimer = new Timer(SaveTimeoutMs) { AutoReset = false };
|
// _saveTimer = new Timer(SaveTimeoutMs) { AutoReset = false };
|
||||||
_saveTimer.Elapsed += (s, e) =>
|
// _saveTimer.Elapsed += (s, e) =>
|
||||||
{
|
// {
|
||||||
_saveTimer = null;
|
// _saveTimer = null;
|
||||||
SaveMemory();
|
// SaveMemory();
|
||||||
};
|
// };
|
||||||
_saveTimer.Start();
|
// _saveTimer.Start();
|
||||||
}
|
// }
|
||||||
else
|
// else
|
||||||
{
|
// {
|
||||||
_saveTimer.Stop();
|
// _saveTimer.Stop();
|
||||||
_saveTimer.Interval = SaveTimeoutMs;
|
// _saveTimer.Interval = SaveTimeoutMs;
|
||||||
_saveTimer.Start();
|
// _saveTimer.Start();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Writes the memory - use SaveMemoryOnTimeout
|
// /// Writes the memory - use SaveMemoryOnTimeout
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
static void SaveMemory()
|
// static void SaveMemory()
|
||||||
{
|
// {
|
||||||
//var dir = @"\NVRAM\debug";
|
// //var dir = @"\NVRAM\debug";
|
||||||
//if (!Directory.Exists(dir))
|
// //if (!Directory.Exists(dir))
|
||||||
// Directory.Create(dir);
|
// // Directory.Create(dir);
|
||||||
|
|
||||||
try
|
// try
|
||||||
{
|
// {
|
||||||
var fileName = GetMemoryFileName();
|
// var fileName = GetMemoryFileName();
|
||||||
|
|
||||||
LogInformation("Loading debug settings file from {fileName}", fileName);
|
// LogInformation("Loading debug settings file from {fileName}", fileName);
|
||||||
|
|
||||||
using (var sw = new StreamWriter(fileName))
|
// using (var sw = new StreamWriter(fileName))
|
||||||
{
|
// {
|
||||||
var json = JsonConvert.SerializeObject(_contexts);
|
// var json = JsonConvert.SerializeObject(_contexts);
|
||||||
sw.Write(json);
|
// sw.Write(json);
|
||||||
sw.Flush();
|
// sw.Flush();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
catch (Exception ex)
|
// catch (Exception ex)
|
||||||
{
|
// {
|
||||||
ErrorLog.Error("Exception saving debug settings: {message}", ex);
|
// LogError("Exception saving debug settings: {message}", ex);
|
||||||
CrestronConsole.PrintLine("Exception saving debug settings: {message}", ex.Message);
|
// return;
|
||||||
return;
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
///
|
// ///
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
static void LoadMemory()
|
// static void LoadMemory()
|
||||||
{
|
// {
|
||||||
var file = GetMemoryFileName();
|
// var file = GetMemoryFileName();
|
||||||
if (File.Exists(file))
|
// if (File.Exists(file))
|
||||||
{
|
// {
|
||||||
using (var sr = new StreamReader(file))
|
// using (var sr = new StreamReader(file))
|
||||||
{
|
// {
|
||||||
var json = sr.ReadToEnd();
|
// var json = sr.ReadToEnd();
|
||||||
_contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);
|
// _contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);
|
||||||
|
|
||||||
if (_contexts != null)
|
// if (_contexts != null)
|
||||||
{
|
// {
|
||||||
LogMessage(LogEventLevel.Debug, "Debug memory restored from file");
|
// LogMessage(LogEventLevel.Debug, "Debug memory restored from file");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
_contexts = new DebugContextCollection();
|
// _contexts = new DebugContextCollection();
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper to get the file path for this app's debug memory
|
/// Helper to get the file path for this app's debug memory
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
extern alias NewtonsoftJson;
|
extern alias NewtonsoftJson;
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Crestron.SimplSharp;
|
using System.Security.Authentication;
|
||||||
using Org.BouncyCastle.Asn1.X509;
|
using System.Security.Cryptography;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using Crestron.SimplSharp;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Configuration;
|
using Serilog.Configuration;
|
||||||
using Serilog.Core;
|
using Serilog.Core;
|
||||||
|
|
@ -11,10 +13,8 @@ using Serilog.Formatting;
|
||||||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
||||||
using Serilog.Formatting.Json;
|
using Serilog.Formatting.Json;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Authentication;
|
|
||||||
using WebSocketSharp;
|
using WebSocketSharp;
|
||||||
using WebSocketSharp.Server;
|
using WebSocketSharp.Server;
|
||||||
using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2;
|
|
||||||
using WebSocketSharp.Net;
|
using WebSocketSharp.Net;
|
||||||
|
|
||||||
namespace PepperDash.Core;
|
namespace PepperDash.Core;
|
||||||
|
|
@ -33,15 +33,18 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
private readonly string _path = "/debug/join/";
|
private readonly string _path = "/debug/join/";
|
||||||
private const string _certificateName = "selfCres";
|
private const string _certificateName = "selfCres";
|
||||||
private const string _certificatePassword = "cres12345";
|
private const string _certificatePassword = "cres12345";
|
||||||
|
private static string CertPath =>
|
||||||
|
$"{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}{_certificateName}.pfx";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the port number on which the HTTPS server is currently running.
|
/// Gets the port number on which the HTTPS server is currently running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Port
|
public int Port
|
||||||
{ get
|
{
|
||||||
|
get
|
||||||
{
|
{
|
||||||
|
|
||||||
if(_httpsServer == null) return 0;
|
if (_httpsServer == null) return 0;
|
||||||
return _httpsServer.Port;
|
return _httpsServer.Port;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,8 +58,10 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (_httpsServer == null) return "";
|
if (_httpsServer == null || !_httpsServer.IsListening) return "";
|
||||||
return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}";
|
var service = _httpsServer.WebSocketServices[_path];
|
||||||
|
if (service == null) return "";
|
||||||
|
return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{service.Path}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +88,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
|
|
||||||
_textFormatter = formatProvider ?? new JsonFormatter();
|
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||||
|
|
||||||
if (!File.Exists($"\\user\\{_certificateName}.pfx"))
|
if (!File.Exists(CertPath))
|
||||||
CreateCert();
|
CreateCert();
|
||||||
|
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += type =>
|
CrestronEnvironment.ProgramStatusEventHandler += type =>
|
||||||
|
|
@ -97,29 +102,65 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
|
|
||||||
private static void CreateCert()
|
private static void CreateCert()
|
||||||
{
|
{
|
||||||
|
// NOTE: This method is called from the constructor, which is itself called during Debug's static
|
||||||
|
// constructor before _logger is assigned. Do NOT call any Debug.Log* methods here — use
|
||||||
|
// CrestronConsole.PrintLine only, to avoid a NullReferenceException that would poison the Debug type.
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var utility = new BouncyCertificate();
|
|
||||||
|
|
||||||
var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
|
var ipAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
|
||||||
var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
|
var hostName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
|
||||||
var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
|
var domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0);
|
||||||
|
|
||||||
Debug.LogInformation("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress);
|
CrestronConsole.PrintLine(string.Format("CreateCert: DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress));
|
||||||
|
|
||||||
var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), [string.Format("{0}.{1}", hostName, domainName), ipAddress], [KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth]);
|
var subjectName = string.Format("CN={0}.{1}", hostName, domainName);
|
||||||
|
var fqdn = string.Format("{0}.{1}", hostName, domainName);
|
||||||
|
|
||||||
//Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested
|
using var rsa = RSA.Create(2048);
|
||||||
|
|
||||||
|
var request = new CertificateRequest(
|
||||||
|
subjectName,
|
||||||
|
rsa,
|
||||||
|
HashAlgorithmName.SHA256,
|
||||||
|
RSASignaturePadding.Pkcs1);
|
||||||
|
|
||||||
|
// Subject Key Identifier
|
||||||
|
request.CertificateExtensions.Add(
|
||||||
|
new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
|
||||||
|
|
||||||
|
// Extended Key Usage: server + client auth
|
||||||
|
request.CertificateExtensions.Add(
|
||||||
|
new X509EnhancedKeyUsageExtension(
|
||||||
|
new OidCollection
|
||||||
|
{
|
||||||
|
new Oid("1.3.6.1.5.5.7.3.1"), // id-kp-serverAuth
|
||||||
|
new Oid("1.3.6.1.5.5.7.3.2") // id-kp-clientAuth
|
||||||
|
},
|
||||||
|
false));
|
||||||
|
|
||||||
|
// Subject Alternative Names: DNS + IP
|
||||||
|
var sanBuilder = new SubjectAlternativeNameBuilder();
|
||||||
|
sanBuilder.AddDnsName(fqdn);
|
||||||
|
if (System.Net.IPAddress.TryParse(ipAddress, out var ip))
|
||||||
|
sanBuilder.AddIpAddress(ip);
|
||||||
|
request.CertificateExtensions.Add(sanBuilder.Build());
|
||||||
|
|
||||||
|
var notBefore = DateTimeOffset.UtcNow;
|
||||||
|
var notAfter = notBefore.AddYears(2);
|
||||||
|
|
||||||
|
using var cert = request.CreateSelfSigned(notBefore, notAfter);
|
||||||
|
|
||||||
var separator = Path.DirectorySeparatorChar;
|
var separator = Path.DirectorySeparatorChar;
|
||||||
|
var outputPath = string.Format("{0}user{1}{2}.pfx", separator, separator, _certificateName);
|
||||||
|
|
||||||
utility.CertificatePassword = _certificatePassword;
|
var pfxBytes = cert.Export(X509ContentType.Pfx, _certificatePassword);
|
||||||
utility.WriteCertificate(certificate, @$"{separator}user{separator}", _certificateName);
|
File.WriteAllBytes(outputPath, pfxBytes);
|
||||||
|
|
||||||
|
CrestronConsole.PrintLine(string.Format("CreateCert: Certificate written to {0}", outputPath));
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogError(ex, "WSS CreateCert Failed: {0}", ex.Message);
|
CrestronConsole.PrintLine(string.Format("WSS CreateCert Failed: {0}\r\n{1}", ex.Message, ex.StackTrace));
|
||||||
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,7 +193,26 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
Debug.LogInformation("Starting Websocket Server on port: {0}", port);
|
Debug.LogInformation("Starting Websocket Server on port: {0}", port);
|
||||||
|
|
||||||
|
|
||||||
Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword);
|
Start(port, CertPath, _certificatePassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static X509Certificate2 LoadOrRecreateCert(string certPath, string certPassword)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// EphemeralKeySet is required on Linux/OpenSSL (Crestron 4-series) to avoid
|
||||||
|
// key-container persistence failures, and avoids the private key export restriction.
|
||||||
|
return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Cert is stale or was generated by an incompatible library (e.g. old BouncyCastle output).
|
||||||
|
// Delete it, regenerate with the BCL path, and retry once.
|
||||||
|
CrestronConsole.PrintLine(string.Format("SSL cert load failed ({0}); regenerating...", ex.Message));
|
||||||
|
try { File.Delete(certPath); } catch { }
|
||||||
|
CreateCert();
|
||||||
|
return new X509Certificate2(certPath, certPassword, X509KeyStorageFlags.EphemeralKeySet);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Start(int port, string certPath = "", string certPassword = "")
|
private void Start(int port, string certPath = "", string certPassword = "")
|
||||||
|
|
@ -165,7 +225,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
{
|
{
|
||||||
Debug.LogInformation("Assigning SSL Configuration");
|
Debug.LogInformation("Assigning SSL Configuration");
|
||||||
|
|
||||||
_httpsServer.SslConfiguration.ServerCertificate = new X509Certificate2(certPath, certPassword);
|
_httpsServer.SslConfiguration.ServerCertificate = LoadOrRecreateCert(certPath, certPassword);
|
||||||
_httpsServer.SslConfiguration.ClientCertificateRequired = false;
|
_httpsServer.SslConfiguration.ClientCertificateRequired = false;
|
||||||
_httpsServer.SslConfiguration.CheckCertificateRevocation = false;
|
_httpsServer.SslConfiguration.CheckCertificateRevocation = false;
|
||||||
_httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;
|
_httpsServer.SslConfiguration.EnabledSslProtocols = SslProtocols.Tls12;
|
||||||
|
|
@ -180,36 +240,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
_httpsServer.AddWebSocketService<DebugClient>(_path);
|
_httpsServer.AddWebSocketService<DebugClient>(_path);
|
||||||
Debug.LogInformation("Assigning Log Info");
|
Debug.LogInformation("Assigning Log Info");
|
||||||
_httpsServer.Log.Level = LogLevel.Trace;
|
_httpsServer.Log.Level = LogLevel.Trace;
|
||||||
_httpsServer.Log.Output = (d, s) =>
|
_httpsServer.Log.Output = WriteWebSocketInternalLog;
|
||||||
{
|
|
||||||
uint level;
|
|
||||||
|
|
||||||
switch(d.Level)
|
|
||||||
{
|
|
||||||
case WebSocketSharp.LogLevel.Fatal:
|
|
||||||
level = 3;
|
|
||||||
break;
|
|
||||||
case WebSocketSharp.LogLevel.Error:
|
|
||||||
level = 2;
|
|
||||||
break;
|
|
||||||
case WebSocketSharp.LogLevel.Warn:
|
|
||||||
level = 1;
|
|
||||||
break;
|
|
||||||
case WebSocketSharp.LogLevel.Info:
|
|
||||||
level = 0;
|
|
||||||
break;
|
|
||||||
case WebSocketSharp.LogLevel.Debug:
|
|
||||||
level = 4;
|
|
||||||
break;
|
|
||||||
case WebSocketSharp.LogLevel.Trace:
|
|
||||||
level = 5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
level = 4;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Debug.LogInformation("{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s);
|
|
||||||
};
|
|
||||||
Debug.LogInformation("Starting");
|
Debug.LogInformation("Starting");
|
||||||
|
|
||||||
_httpsServer.Start();
|
_httpsServer.Start();
|
||||||
|
|
@ -219,6 +250,8 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
{
|
{
|
||||||
Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
|
Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
|
||||||
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||||
|
// Null out the server so callers can detect failure via IsRunning / Url null guards.
|
||||||
|
_httpsServer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,10 +263,69 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
||||||
public void StopServer()
|
public void StopServer()
|
||||||
{
|
{
|
||||||
Debug.LogInformation("Stopping Websocket Server");
|
Debug.LogInformation("Stopping Websocket Server");
|
||||||
_httpsServer?.Stop();
|
|
||||||
|
|
||||||
_httpsServer = null;
|
try
|
||||||
|
{
|
||||||
|
if (_httpsServer == null || !_httpsServer.IsListening)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent close-sequence internal websocket logs from re-entering the logging pipeline.
|
||||||
|
_httpsServer.Log.Output = (d, s) => { };
|
||||||
|
|
||||||
|
var serviceHost = _httpsServer.WebSocketServices[_path];
|
||||||
|
|
||||||
|
if (serviceHost == null)
|
||||||
|
{
|
||||||
|
_httpsServer.Stop();
|
||||||
|
_httpsServer = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceHost.Sessions.Broadcast("Server is stopping");
|
||||||
|
|
||||||
|
foreach (var session in serviceHost.Sessions.Sessions)
|
||||||
|
{
|
||||||
|
if (session?.Context?.WebSocket != null && session.Context.WebSocket.IsAlive)
|
||||||
|
{
|
||||||
|
session.Context.WebSocket.Close(1001, "Server is stopping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpsServer.Stop();
|
||||||
|
|
||||||
|
_httpsServer = null;
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError(ex, "WebSocket Failed to stop gracefully {0}", ex.Message);
|
||||||
|
Debug.LogVerbose("Stack Trace\r\n{0}", ex.StackTrace);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void WriteWebSocketInternalLog(LogData data, string supplemental)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message = string.IsNullOrWhiteSpace(data.Message) ? "<none>" : data.Message;
|
||||||
|
var details = string.IsNullOrWhiteSpace(supplemental) ? string.Empty : string.Format(" | details: {0}", supplemental);
|
||||||
|
|
||||||
|
// Use direct console output to avoid recursive log sink calls.
|
||||||
|
CrestronConsole.PrintLine(string.Format("WS[{0}] {1} | message: {2}{3}", data.Level, data.Date, message, details));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Never throw from websocket log callback.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
extern alias NewtonsoftJson;
|
using System;
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.WebScripting;
|
using Crestron.SimplSharp.WebScripting;
|
||||||
using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
|
|
||||||
using JsonConvert = NewtonsoftJson::Newtonsoft.Json.JsonConvert;
|
|
||||||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
|
||||||
using PepperDash.Core.Web.RequestHandlers;
|
using PepperDash.Core.Web.RequestHandlers;
|
||||||
using PepperDash.Core.Logging;
|
using PepperDash.Core.Logging;
|
||||||
|
|
||||||
|
|
@ -76,10 +71,13 @@ public class WebApiServer : IKeyName
|
||||||
Name = string.IsNullOrEmpty(name) ? DefaultName : name;
|
Name = string.IsNullOrEmpty(name) ? DefaultName : name;
|
||||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||||
|
|
||||||
|
this.LogInformation("Creating Web API Server with Key: {Key}, Name: {Name}, BasePath: {BasePath}", Key, Name, BasePath);
|
||||||
|
|
||||||
if (_server == null) _server = new HttpCwsServer(BasePath);
|
if (_server == null) _server = new HttpCwsServer(BasePath);
|
||||||
|
|
||||||
_server.setProcessName(Key);
|
_server.setProcessName(Key);
|
||||||
_server.HttpRequestHandler = new DefaultRequestHandler();
|
_server.HttpRequestHandler = new DefaultRequestHandler();
|
||||||
|
_server.ReceivedRequestEvent += ReceivedRequestEventHandler;
|
||||||
|
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||||
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
|
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
|
||||||
|
|
@ -104,8 +102,12 @@ public class WebApiServer : IKeyName
|
||||||
/// <param name="ethernetEventArgs"></param>
|
/// <param name="ethernetEventArgs"></param>
|
||||||
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
||||||
{
|
{
|
||||||
// Re-enable the server if the link comes back up and the status should be connected
|
if (ethernetEventArgs.EthernetEventType != eEthernetEventType.LinkUp)
|
||||||
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered)
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsRegistered)
|
||||||
{
|
{
|
||||||
this.LogInformation("Ethernet link up. Server is already registered.");
|
this.LogInformation("Ethernet link up. Server is already registered.");
|
||||||
return;
|
return;
|
||||||
|
|
@ -116,14 +118,14 @@ public class WebApiServer : IKeyName
|
||||||
Start();
|
Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Initialize method
|
// /// Initialize method
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
public void Initialize(string key, string basePath)
|
// public void Initialize(string key, string basePath)
|
||||||
{
|
// {
|
||||||
Key = key;
|
// Key = key;
|
||||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
// BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a route to CWS
|
/// Adds a route to CWS
|
||||||
|
|
@ -214,12 +216,10 @@ public class WebApiServer : IKeyName
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsRegistered = _server.Unregister() == false;
|
var unregistered = _server.Unregister();
|
||||||
|
IsRegistered = !unregistered;
|
||||||
|
|
||||||
this.LogDebug("Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful");
|
this.LogDebug("Stopping server, unregistration {0}", unregistered ? "was successful" : "failed");
|
||||||
|
|
||||||
_server.Dispose();
|
|
||||||
_server = null;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
@ -240,13 +240,12 @@ public class WebApiServer : IKeyName
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented);
|
var req = args.Context?.Request;
|
||||||
this.LogVerbose("RecieveRequestEventHandler Context:\x0d\x0a{0}", j);
|
this.LogVerbose("ReceivedRequestEventHandler: {Method} {Path}", req?.HttpMethod, req?.Path);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.LogException(ex, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
|
this.LogException(ex, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
|
||||||
this.LogVerbose("ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -123,68 +123,48 @@ namespace PepperDash.Essentials.Core.Config;
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Loading config file: '{0}'", filePath);
|
Debug.LogMessage(LogEventLevel.Information, "Loading config file: '{0}'", filePath);
|
||||||
|
|
||||||
|
var fileContents = fs.ReadToEnd();
|
||||||
|
|
||||||
if (localConfigFound)
|
if (localConfigFound)
|
||||||
{
|
{
|
||||||
ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject<EssentialsConfig>();
|
ConfigObject = JObject.Parse(fileContents).ToObject<EssentialsConfig>();
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
|
||||||
if (localConfigFound)
|
return ConfigObject != null;
|
||||||
{
|
|
||||||
ConfigObject = JObject.Parse(fs.ReadToEnd()).ToObject<EssentialsConfig>();
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var parsedConfig = JObject.Parse(fs.ReadToEnd());
|
|
||||||
|
|
||||||
// Check if it's a v2 config (check for "version" node)
|
|
||||||
// this means it's already merged by the Portal API
|
|
||||||
// from the v2 config tool
|
|
||||||
var isV2Config = parsedConfig["versions"] != null;
|
|
||||||
|
|
||||||
if (isV2Config)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
|
|
||||||
ConfigObject = parsedConfig.ToObject<EssentialsConfig>();
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract SystemUrl and TemplateUrl into final config output
|
|
||||||
ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject<EssentialsConfig>();
|
|
||||||
|
|
||||||
if (parsedConfig["system_url"] != null)
|
|
||||||
{
|
|
||||||
ConfigObject.SystemUrl = parsedConfig["system_url"].Value<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedConfig["template_url"] != null)
|
|
||||||
{
|
|
||||||
ConfigObject.TemplateUrl = parsedConfig["template_url"].Value<string>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Merged Config");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var doubleObj = JObject.Parse(fs.ReadToEnd());
|
var parsedConfig = JObject.Parse(fileContents);
|
||||||
ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject<EssentialsConfig>();
|
|
||||||
|
|
||||||
// Extract SystemUrl and TemplateUrl into final config output
|
// Check if it's a v2 config (check for "version" node)
|
||||||
|
// this means it's already merged by the Portal API
|
||||||
|
// from the v2 config tool
|
||||||
|
var isV2Config = parsedConfig["versions"] != null;
|
||||||
|
|
||||||
if (doubleObj["system_url"] != null)
|
if (isV2Config)
|
||||||
{
|
{
|
||||||
ConfigObject.SystemUrl = doubleObj["system_url"].Value<string>();
|
Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
|
||||||
|
ConfigObject = parsedConfig.ToObject<EssentialsConfig>();
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
|
||||||
|
return ConfigObject != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doubleObj["template_url"] != null)
|
// Extract SystemUrl and TemplateUrl into final config output
|
||||||
|
ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject<EssentialsConfig>();
|
||||||
|
|
||||||
|
if (ConfigObject == null)
|
||||||
{
|
{
|
||||||
ConfigObject.TemplateUrl = doubleObj["template_url"].Value<string>();
|
Debug.LogMessage(LogEventLevel.Warning, "Config merge produced a null ConfigObject.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedConfig["system_url"] != null)
|
||||||
|
{
|
||||||
|
ConfigObject.SystemUrl = parsedConfig["system_url"].Value<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedConfig["template_url"] != null)
|
||||||
|
{
|
||||||
|
ConfigObject.TemplateUrl = parsedConfig["template_url"].Value<string>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.WebScripting;
|
using Crestron.SimplSharp.WebScripting;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
|
@ -26,10 +27,6 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber)
|
? string.Format("/app{0:00}/api", InitialParametersClass.ApplicationNumber)
|
||||||
: "/api";
|
: "/api";
|
||||||
|
|
||||||
private const int DebugTrace = 0;
|
|
||||||
private const int DebugInfo = 1;
|
|
||||||
private const int DebugVerbose = 2;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the BasePath
|
/// Gets or sets the BasePath
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -230,7 +227,7 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
|
|
||||||
_server.Start();
|
_server.Start();
|
||||||
|
|
||||||
GetPaths();
|
PrintPaths();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -240,11 +237,11 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
|
|
||||||
_server.Start();
|
_server.Start();
|
||||||
|
|
||||||
GetPaths();
|
PrintPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Print the available pahts
|
/// Print the available paths
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <example>
|
/// <example>
|
||||||
/// http(s)://{ipaddress}/cws/{basePath}
|
/// http(s)://{ipaddress}/cws/{basePath}
|
||||||
|
|
@ -253,7 +250,7 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GetPaths method
|
/// GetPaths method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void GetPaths()
|
public void PrintPaths()
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
|
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
|
||||||
|
|
||||||
|
|
@ -267,7 +264,7 @@ public class EssentialsWebApi : EssentialsDevice
|
||||||
? $"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}"
|
? $"https://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}"
|
||||||
: $"https://{currentIp}/cws{BasePath}";
|
: $"https://{currentIp}/cws{BasePath}";
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Server:{path:l}", path);
|
Debug.LogMessage(LogEventLevel.Information, this, "Server: {path:l}", path);
|
||||||
|
|
||||||
var routeCollection = _server.GetRouteCollection();
|
var routeCollection = _server.GetRouteCollection();
|
||||||
if (routeCollection == null)
|
if (routeCollection == null)
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,23 @@ using PepperDash.Core.Web.RequestHandlers;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using System;
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Web.RequestHandlers;
|
namespace PepperDash.Essentials.Core.Web.RequestHandlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a DebugSessionRequestHandler
|
||||||
|
/// </summary>
|
||||||
public class DebugSessionRequestHandler : WebApiBaseRequestHandler
|
public class DebugSessionRequestHandler : WebApiBaseRequestHandler
|
||||||
{
|
{
|
||||||
private readonly DebugWebsocketSink _sink = new DebugWebsocketSink();
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public DebugSessionRequestHandler()
|
public DebugSessionRequestHandler()
|
||||||
: base(true)
|
: base(true)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets details for a debug session
|
/// Gets details for a debug session
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -41,21 +46,30 @@ public class DebugSessionRequestHandler : WebApiBaseRequestHandler
|
||||||
|
|
||||||
var port = 0;
|
var port = 0;
|
||||||
|
|
||||||
if (!_sink.IsRunning)
|
if (!Debug.WebsocketSink.IsRunning)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Starting WS Server");
|
Debug.LogMessage(LogEventLevel.Information, "Starting WS Server");
|
||||||
// Generate a random port within a specified range
|
// Generate a random port within a specified range
|
||||||
port = new Random().Next(65435, 65535);
|
port = new Random().Next(65435, 65535);
|
||||||
// Start the WS Server
|
// Start the WS Server
|
||||||
_sink.StartServerAndSetPort(port);
|
Debug.WebsocketSink.StartServerAndSetPort(port);
|
||||||
Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);
|
Debug.SetWebSocketMinimumDebugLevel(Serilog.Events.LogEventLevel.Verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = _sink.Url;
|
if (!Debug.WebsocketSink.IsRunning)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.StatusDescription = "Internal Server Error";
|
||||||
|
context.Response.Write(JsonConvert.SerializeObject(new { error = "Failed to start WebSocket debug server. Check logs for details." }), false);
|
||||||
|
context.Response.End();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = Debug.WebsocketSink.Url;
|
||||||
|
|
||||||
object data = new
|
object data = new
|
||||||
{
|
{
|
||||||
url = _sink.Url
|
url = Debug.WebsocketSink.Url
|
||||||
};
|
};
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Debug Session URL: {0}", url);
|
Debug.LogMessage(LogEventLevel.Information, "Debug Session URL: {0}", url);
|
||||||
|
|
@ -82,7 +96,8 @@ public class DebugSessionRequestHandler : WebApiBaseRequestHandler
|
||||||
/// <param name="context"></param>
|
/// <param name="context"></param>
|
||||||
protected override void HandlePost(HttpCwsContext context)
|
protected override void HandlePost(HttpCwsContext context)
|
||||||
{
|
{
|
||||||
_sink.StopServer();
|
|
||||||
|
Task.Run(() => Debug.WebsocketSink.StopServer());
|
||||||
|
|
||||||
context.Response.StatusCode = 200;
|
context.Response.StatusCode = 200;
|
||||||
context.Response.StatusDescription = "OK";
|
context.Response.StatusDescription = "OK";
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ using System.Threading;
|
||||||
using Timeout = Crestron.SimplSharp.Timeout;
|
using Timeout = Crestron.SimplSharp.Timeout;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using PepperDash.Essentials.Core.Web;
|
||||||
|
|
||||||
namespace PepperDash.Essentials;
|
namespace PepperDash.Essentials;
|
||||||
|
|
||||||
|
|
@ -56,7 +57,15 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogError(e, "FATAL INITIALIZE ERROR. System is in an inconsistent state");
|
try
|
||||||
|
{
|
||||||
|
Debug.LogError(e, "FATAL INITIALIZE ERROR. System is in an inconsistent state");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Debug may not be initialized (e.g. its own static ctor failed); fall back to console.
|
||||||
|
CrestronConsole.PrintLine($"FATAL INITIALIZE ERROR. System is in an inconsistent state\r\n{e.Message}\r\n{e.StackTrace}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,6 +161,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts,
|
CrestronConsole.AddNewConsoleCommand(DeviceManager.GetRoutingPorts,
|
||||||
"getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator);
|
"getroutingports", "Reports all routing ports, if any. Requires a device key", ConsoleAccessLevelEnum.AccessOperator);
|
||||||
|
|
||||||
|
DeviceManager.AddDevice(new EssentialsWebApi("essentialsWebApi", "Essentials Web API"));
|
||||||
|
|
||||||
if (!Debug.DoNotLoadConfigOnNextBoot)
|
if (!Debug.DoNotLoadConfigOnNextBoot)
|
||||||
{
|
{
|
||||||
|
|
@ -260,10 +270,10 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
PluginLoader.LoadPlugins();
|
PluginLoader.LoadPlugins();
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Folder structure verified. Loading config...");
|
Debug.LogMessage(LogEventLevel.Information, "Folder structure verified. Loading config...");
|
||||||
if (!ConfigReader.LoadConfig2())
|
if (!ConfigReader.LoadConfig2() || ConfigReader.ConfigObject == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Essentials Load complete with errors");
|
Debug.LogMessage(LogEventLevel.Warning, "Unable to load config file. Please ensure a valid config file is present and restart the program.");
|
||||||
return;
|
// return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Load();
|
Load();
|
||||||
|
|
@ -385,43 +395,49 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
new Core.Monitoring.SystemMonitorController("systemMonitor"));
|
new Core.Monitoring.SystemMonitorController("systemMonitor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var devConf in ConfigReader.ConfigObject.Devices)
|
if (ConfigReader.ConfigObject is not null)
|
||||||
{
|
{
|
||||||
IKeyed newDev = null;
|
Debug.LogMessage(LogEventLevel.Warning, "LoadDevices: ConfigObject is null. Cannot load devices.");
|
||||||
|
|
||||||
try
|
foreach (var devConf in ConfigReader.ConfigObject.Devices)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Creating device '{deviceKey:l}', type '{deviceType:l}'", devConf.Key, devConf.Type);
|
IKeyed newDev = null;
|
||||||
// Skip this to prevent unnecessary warnings
|
|
||||||
if (devConf.Key == "processor")
|
try
|
||||||
{
|
{
|
||||||
var prompt = Global.ControlSystem.ControllerPrompt;
|
Debug.LogMessage(LogEventLevel.Information, "Creating device '{deviceKey:l}', type '{deviceType:l}'", devConf.Key, devConf.Type);
|
||||||
|
// Skip this to prevent unnecessary warnings
|
||||||
|
if (devConf.Key == "processor")
|
||||||
|
{
|
||||||
|
var prompt = Global.ControlSystem.ControllerPrompt;
|
||||||
|
|
||||||
var typeMatch = string.Equals(devConf.Type, prompt, StringComparison.OrdinalIgnoreCase) ||
|
var typeMatch = string.Equals(devConf.Type, prompt, StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(devConf.Type, prompt.Replace("-", ""), StringComparison.OrdinalIgnoreCase);
|
string.Equals(devConf.Type, prompt.Replace("-", ""), StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (!typeMatch)
|
if (!typeMatch)
|
||||||
Debug.LogMessage(LogEventLevel.Information,
|
Debug.LogMessage(LogEventLevel.Information,
|
||||||
"WARNING: Config file defines processor type as '{deviceType:l}' but actual processor is '{processorType:l}'! Some ports may not be available",
|
"WARNING: Config file defines processor type as '{deviceType:l}' but actual processor is '{processorType:l}'! Some ports may not be available",
|
||||||
devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper());
|
devConf.Type.ToUpper(), Global.ControlSystem.ControllerPrompt.ToUpper());
|
||||||
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (newDev == null)
|
||||||
|
newDev = Core.DeviceFactory.GetDevice(devConf);
|
||||||
|
|
||||||
|
if (newDev != null)
|
||||||
|
DeviceManager.AddDevice(newDev);
|
||||||
|
else
|
||||||
|
Debug.LogMessage(LogEventLevel.Information, "ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.", devConf.Type, devConf.Key);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(e, "ERROR: Creating device {deviceKey:l}. Skipping device.", args: new[] { devConf.Key });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (newDev == null)
|
|
||||||
newDev = Core.DeviceFactory.GetDevice(devConf);
|
|
||||||
|
|
||||||
if (newDev != null)
|
|
||||||
DeviceManager.AddDevice(newDev);
|
|
||||||
else
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "ERROR: Cannot load unknown device type '{deviceType:l}', key '{deviceKey:l}'.", devConf.Type, devConf.Key);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Debug.LogMessage(e, "ERROR: Creating device {deviceKey:l}. Skipping device.", args: new[] { devConf.Key });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Debug.LogMessage(LogEventLevel.Information, "All Devices Loaded.");
|
Debug.LogMessage(LogEventLevel.Information, "All Devices Loaded.");
|
||||||
|
|
||||||
|
|
@ -438,7 +454,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
|
|
||||||
var tlc = TieLineCollection.Default;
|
var tlc = TieLineCollection.Default;
|
||||||
|
|
||||||
if (ConfigReader.ConfigObject.TieLines == null)
|
if (ConfigReader.ConfigObject?.TieLines == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -459,7 +475,7 @@ public class ControlSystem : CrestronControlSystem, ILoadConfig
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LoadRooms()
|
public void LoadRooms()
|
||||||
{
|
{
|
||||||
if (ConfigReader.ConfigObject.Rooms == null)
|
if (ConfigReader.ConfigObject?.Rooms == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Notice: Configuration contains no rooms - Is this intentional? This may be a valid configuration.");
|
Debug.LogMessage(LogEventLevel.Information, "Notice: Configuration contains no rooms - Is this intentional? This may be a valid configuration.");
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue