From 6174400b7e447a80e48c20ba93f99cb5c4a3ec9e Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 27 Oct 2023 15:13:46 -0600 Subject: [PATCH 01/36] Updates merge to ignore uid and merge on key only for devices --- src/Pepperdash Core/Config/PortalConfigReader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Config/PortalConfigReader.cs b/src/Pepperdash Core/Config/PortalConfigReader.cs index 803875f..fdc9040 100644 --- a/src/Pepperdash Core/Config/PortalConfigReader.cs +++ b/src/Pepperdash Core/Config/PortalConfigReader.cs @@ -85,7 +85,7 @@ public static JObject MergeConfigs(JObject doubleConfig) merged.Add("info", template["info"]); merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray, - system["devices"] as JArray, "uid", "devices")); + system["devices"] as JArray, "key", "devices")); if (system["rooms"] == null) merged.Add("rooms", template["rooms"]); From 2f1180512c18e31962b8434bf13e4ca2067737fd Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 30 Oct 2023 17:24:47 -0600 Subject: [PATCH 02/36] feat: Initial implementation of DebugWebsocketSink --- src/Pepperdash Core/CoreInterfaces.cs | 20 ++++-- src/Pepperdash Core/Device.cs | 12 +++- src/Pepperdash Core/Logging/Debug.cs | 70 ++++++++++++++++++- .../Logging/DebugWebsocketSink.cs | 42 +++++++++++ src/Pepperdash Core/PepperDash_Core.csproj | 4 ++ 5 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 src/Pepperdash Core/Logging/DebugWebsocketSink.cs diff --git a/src/Pepperdash Core/CoreInterfaces.cs b/src/Pepperdash Core/CoreInterfaces.cs index 3a5df42..84d2486 100644 --- a/src/Pepperdash Core/CoreInterfaces.cs +++ b/src/Pepperdash Core/CoreInterfaces.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Crestron.SimplSharp; +using Serilog; namespace PepperDash.Core { @@ -15,16 +16,27 @@ public interface IKeyed /// Unique Key /// string Key { get; } - } + } /// - /// Named Keyed device interface. Forces the devie to have a Unique Key and a name. + /// Named Keyed device interface. Forces the device to have a Unique Key and a name. /// public interface IKeyName : IKeyed - { + { /// /// Isn't it obvious :) /// string Name { get; } - } + } + + /// + /// Logging interface for IKeyName + /// + public interface IKeyNameWithLogging : IKeyName + { + /// + /// Logger for the device + /// + ILogger Logger { get; } + } } \ No newline at end of file diff --git a/src/Pepperdash Core/Device.cs b/src/Pepperdash Core/Device.cs index 55d4c15..2ce3b10 100644 --- a/src/Pepperdash Core/Device.cs +++ b/src/Pepperdash Core/Device.cs @@ -1,6 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Serilog; +using Serilog.Core; +using Serilog.Sinks.SystemConsole; namespace PepperDash.Core { @@ -8,8 +11,12 @@ namespace PepperDash.Core /// /// The core event and status-bearing class that most if not all device and connectors can derive from. /// - public class Device : IKeyName + public class Device : IKeyNameWithLogging { + public ILogger Logger { get; private set; } + + private static LoggingLevelSwitch _loggingLevelSwitch; + /// /// Unique Key /// @@ -52,6 +59,9 @@ public Device(string key) if (key.Contains('.')) Debug.Console(0, this, "WARNING: Device name's should not include '.'"); Name = ""; + _loggingLevelSwitch = new LoggingLevelSwitch(); + + Logger = Serilog.Log.ForContext("Key", Key); } /// diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index a0af198..09dd7b3 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -8,15 +8,47 @@ using Crestron.SimplSharp.CrestronIO; using Full.Newtonsoft.Json; using PepperDash.Core.DebugThings; - +using Serilog; +using Serilog.Core; +using Serilog.Events; +using Serilog.Formatting.Display; +using System.Runtime.Serialization; namespace PepperDash.Core { + public enum eDebugLevel + { + Information = 0, + Warning = 1, + Error = 2, + Fatal = 3, + Debug = 4, + Verbose = 5, + } + /// /// Contains debug commands for use in various situations /// public static class Debug { + private static Dictionary> _logActions = new Dictionary>() + { + {0, (s) => _logger.Information(s) }, + {1, (s) => _logger.Warning(s) }, + {2, (s) => _logger.Error(s) }, + {3, (s) => _logger.Fatal(s) }, + {4, (s) => _logger.Debug(s) }, + {5, (s) => _logger.Verbose(s) }, + }; + + private static Logger _logger; + + private static LoggingLevelSwitch _consoleLoggingLevelSwitch; + + private static LoggingLevelSwitch _websocketLoggingLevelSwitch; + + private static DebugWebsocketSink _websocketSink; + /// /// Describes the folder location where a given program stores it's debug level memory. By default, the /// file written will be named appNdebug where N is 1-10. @@ -67,6 +99,19 @@ public static class Debug static Debug() { + _consoleLoggingLevelSwitch = new LoggingLevelSwitch(); + _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); + + // Instantiate the root logger + _logger = new LoggerConfiguration() + .WriteTo.Logger(lc => lc + .WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) + .WriteTo.Sink(new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) + .WriteTo.File(@"\user\debug\global-log-{Date}.txt" + , rollingInterval: RollingInterval.Day + , restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug) + .CreateLogger(); + // Get the assembly version and print it to console and the log GetVersion(); @@ -165,6 +210,7 @@ private static void GetVersion() /// static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) { + if (programEventType == eProgramStatusEventType.Stopping) { if (_saveTimer != null) @@ -199,6 +245,16 @@ public static void SetDebugFromConsole(string levelString) } } + public static void SetConsoleDebugLevel(LogEventLevel level) + { + _consoleLoggingLevelSwitch.MinimumLevel = level; + } + + public static void SetWebSocketDebugLevel(LogEventLevel level) + { + + } + /// /// Callback for console command /// @@ -233,7 +289,7 @@ public static void SetDebugFilterFromConsole(string items) CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " + "+all: at beginning puts filter into 'default include' mode\r" + " All keys that follow will be excluded from output.\r" + - "-all: at beginning puts filter into 'default excluse all' mode.\r" + + "-all: at beginning puts filter into 'default exclude all' mode.\r" + " All keys that follow will be the only keys that are shown\r" + "+nokey: Enables messages with no key (default)\r" + "-nokey: Disables messages with no key.\r" + @@ -368,6 +424,12 @@ public static void ShowDebugLog(string s) CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine); } + + private static void Log(uint level, string format, params object[] items) + { + + } + /// /// Prints message to console if current debug level is equal to or higher than the level of this message. /// Uses CrestronConsole.PrintLine. @@ -410,6 +472,10 @@ public static void Console(uint level, IKeyed dev, string format, params object[ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, string format, params object[] items) { + var logDevice = dev as IKeyNameWithLogging; + + + var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items)); if (errorLogLevel != ErrorLogLevel.None) { diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs new file mode 100644 index 0000000..c335759 --- /dev/null +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Serilog; +using Serilog.Core; +using Serilog.Events; +using Serilog.Configuration; +using WebSocketSharp.Server; + +namespace PepperDash.Core +{ + public class DebugWebsocketSink : ILogEventSink + { + public WebSocketServer WSSV { get; private set; } + + private readonly IFormatProvider _formatProvider; + + public DebugWebsocketSink() + { + WSSV = new WebSocketServer(); + + } + + public void Emit(LogEvent logEvent) + { + var message = logEvent.RenderMessage(_formatProvider); + WSSV.WebSocketServices.Broadcast(message); + } + } + + public static class DebugWebsocketSinkExtensions + { + public static LoggerConfiguration DebugWebsocketSink( + this LoggerSinkConfiguration loggerConfiguration, + IFormatProvider formatProvider = null) + { + return loggerConfiguration.Sink(new DebugWebsocketSink()); + } + } +} diff --git a/src/Pepperdash Core/PepperDash_Core.csproj b/src/Pepperdash Core/PepperDash_Core.csproj index 845577b..3100767 100644 --- a/src/Pepperdash Core/PepperDash_Core.csproj +++ b/src/Pepperdash Core/PepperDash_Core.csproj @@ -34,6 +34,10 @@ Full + + + + From d43965b82aead8176d5bf534b6304fcee7f0325a Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 30 Oct 2023 18:28:30 -0600 Subject: [PATCH 03/36] Adds log methods --- src/Pepperdash Core/CoreInterfaces.cs | 10 ----- src/Pepperdash Core/Device.cs | 9 +---- src/Pepperdash Core/Logging/Debug.cs | 57 ++++++++++++++++----------- 3 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/Pepperdash Core/CoreInterfaces.cs b/src/Pepperdash Core/CoreInterfaces.cs index 84d2486..c1432c2 100644 --- a/src/Pepperdash Core/CoreInterfaces.cs +++ b/src/Pepperdash Core/CoreInterfaces.cs @@ -29,14 +29,4 @@ public interface IKeyName : IKeyed string Name { get; } } - /// - /// Logging interface for IKeyName - /// - public interface IKeyNameWithLogging : IKeyName - { - /// - /// Logger for the device - /// - ILogger Logger { get; } - } } \ No newline at end of file diff --git a/src/Pepperdash Core/Device.cs b/src/Pepperdash Core/Device.cs index 2ce3b10..98c4293 100644 --- a/src/Pepperdash Core/Device.cs +++ b/src/Pepperdash Core/Device.cs @@ -11,11 +11,8 @@ namespace PepperDash.Core /// /// The core event and status-bearing class that most if not all device and connectors can derive from. /// - public class Device : IKeyNameWithLogging + public class Device : IKeyName { - public ILogger Logger { get; private set; } - - private static LoggingLevelSwitch _loggingLevelSwitch; /// /// Unique Key @@ -58,10 +55,6 @@ public Device(string key) Key = key; if (key.Contains('.')) Debug.Console(0, this, "WARNING: Device name's should not include '.'"); Name = ""; - - _loggingLevelSwitch = new LoggingLevelSwitch(); - - Logger = Serilog.Log.ForContext("Key", Key); } /// diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 09dd7b3..4a36a15 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -31,15 +31,15 @@ public enum eDebugLevel /// public static class Debug { - private static Dictionary> _logActions = new Dictionary>() - { - {0, (s) => _logger.Information(s) }, - {1, (s) => _logger.Warning(s) }, - {2, (s) => _logger.Error(s) }, - {3, (s) => _logger.Fatal(s) }, - {4, (s) => _logger.Debug(s) }, - {5, (s) => _logger.Verbose(s) }, - }; + //private static Dictionary> _logActions = new Dictionary>() + //{ + // {0, (s) => _logger.Information(s) }, + // {1, (s) => _logger.Warning(s) }, + // {2, (s) => _logger.Error(s) }, + // {3, (s) => _logger.Fatal(s) }, + // {4, (s) => _logger.Debug(s) }, + // {5, (s) => _logger.Verbose(s) }, + //}; private static Logger _logger; @@ -49,6 +49,11 @@ public static class Debug private static DebugWebsocketSink _websocketSink; + public static DebugWebsocketSink WebsocketSink + { + get { return _websocketSink; } + } + /// /// Describes the folder location where a given program stores it's debug level memory. By default, the /// file written will be named appNdebug where N is 1-10. @@ -447,13 +452,10 @@ public static void Console(uint level, string format, params object[] items) return; } - if(Level < level) - { - return; - } + _logger.Write((LogEventLevel)level, format, items); - CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber, - string.Format(format, items)); + //CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber, + // string.Format(format, items)); } /// @@ -461,8 +463,12 @@ public static void Console(uint level, string format, params object[] items) /// public static void Console(uint level, IKeyed dev, string format, params object[] items) { - if (Level >= level) - Console(level, "[{0}] {1}", dev.Key, string.Format(format, items)); + var log = _logger.ForContext("Key", dev.Key); + + log.Write((LogEventLevel)level, format, items); + + //if (Level >= level) + // Console(level, "[{0}] {1}", dev.Key, string.Format(format, items)); } /// @@ -472,19 +478,22 @@ public static void Console(uint level, IKeyed dev, string format, params object[ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, string format, params object[] items) { - var logDevice = dev as IKeyNameWithLogging; - - var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items)); if (errorLogLevel != ErrorLogLevel.None) { LogError(errorLogLevel, str); } - if (Level >= level) - { - Console(level, str); - } + + var log = _logger.ForContext("Key", dev.Key); + + + log.Write((LogEventLevel)level, format, items); + + //if (Level >= level) + //{ + // Console(level, str); + //} } /// From 90e1dc78ac5e3900d1af75dbc54de6a2e4b8c757 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 31 Oct 2023 13:06:22 -0600 Subject: [PATCH 04/36] Adds webserver classes from development branch into 2.0 branch --- .../RequestHandlers/DefaultRequestHandler.cs | 16 + .../WebApiBaseRequestHandler.cs | 165 ++++++++++ src/Pepperdash Core/Web/WebApiServer.cs | 284 ++++++++++++++++++ 3 files changed, 465 insertions(+) create mode 100644 src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs create mode 100644 src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs create mode 100644 src/Pepperdash Core/Web/WebApiServer.cs diff --git a/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs b/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs new file mode 100644 index 0000000..57fa1de --- /dev/null +++ b/src/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs @@ -0,0 +1,16 @@ + +namespace PepperDash.Core.Web.RequestHandlers +{ + /// + /// Web API default request handler + /// + public class DefaultRequestHandler : WebApiBaseRequestHandler + { + /// + /// Constructor + /// + public DefaultRequestHandler() + : base(true) + { } + } +} diff --git a/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs new file mode 100644 index 0000000..caa6a7c --- /dev/null +++ b/src/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using Crestron.SimplSharp.WebScripting; + +namespace PepperDash.Core.Web.RequestHandlers +{ + /// + /// CWS Base Handler, implements IHttpCwsHandler + /// + public abstract class WebApiBaseRequestHandler : IHttpCwsHandler + { + private readonly Dictionary> _handlers; + protected readonly bool EnableCors; + + /// + /// Constructor + /// + protected WebApiBaseRequestHandler(bool enableCors) + { + EnableCors = enableCors; + + _handlers = new Dictionary> + { + {"CONNECT", HandleConnect}, + {"DELETE", HandleDelete}, + {"GET", HandleGet}, + {"HEAD", HandleHead}, + {"OPTIONS", HandleOptions}, + {"PATCH", HandlePatch}, + {"POST", HandlePost}, + {"PUT", HandlePut}, + {"TRACE", HandleTrace} + }; + } + + /// + /// Constructor + /// + protected WebApiBaseRequestHandler() + : this(false) + { + } + + /// + /// Handles CONNECT method requests + /// + /// + protected virtual void HandleConnect(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles DELETE method requests + /// + /// + protected virtual void HandleDelete(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles GET method requests + /// + /// + protected virtual void HandleGet(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles HEAD method requests + /// + /// + protected virtual void HandleHead(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles OPTIONS method requests + /// + /// + protected virtual void HandleOptions(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PATCH method requests + /// + /// + protected virtual void HandlePatch(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles POST method requests + /// + /// + protected virtual void HandlePost(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles PUT method requests + /// + /// + protected virtual void HandlePut(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Handles TRACE method requests + /// + /// + protected virtual void HandleTrace(HttpCwsContext context) + { + context.Response.StatusCode = 501; + context.Response.StatusDescription = "Not Implemented"; + context.Response.End(); + } + + /// + /// Process request + /// + /// + public void ProcessRequest(HttpCwsContext context) + { + Action handler; + + if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler)) + { + return; + } + + if (EnableCors) + { + context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); + context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); + } + + handler(context); + } + } +} \ No newline at end of file diff --git a/src/Pepperdash Core/Web/WebApiServer.cs b/src/Pepperdash Core/Web/WebApiServer.cs new file mode 100644 index 0000000..17c737a --- /dev/null +++ b/src/Pepperdash Core/Web/WebApiServer.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Crestron.SimplSharp; +using Crestron.SimplSharp.WebScripting; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PepperDash.Core.Web.RequestHandlers; + +namespace PepperDash.Core.Web +{ + /// + /// Web API server + /// + public class WebApiServer : IKeyName + { + private const string SplusKey = "Uninitialized Web API Server"; + private const string DefaultName = "Web API Server"; + private const string DefaultBasePath = "/api"; + + private const uint DebugTrace = 0; + private const uint DebugInfo = 1; + private const uint DebugVerbose = 2; + + private readonly CCriticalSection _serverLock = new CCriticalSection(); + private HttpCwsServer _server; + + /// + /// Web API server key + /// + public string Key { get; private set; } + + /// + /// Web API server name + /// + public string Name { get; private set; } + + /// + /// CWS base path, will default to "/api" if not set via initialize method + /// + public string BasePath { get; private set; } + + /// + /// Indicates CWS is registered with base path + /// + public bool IsRegistered { get; private set; } + + /// + /// Http request handler + /// + //public IHttpCwsHandler HttpRequestHandler + //{ + // get { return _server.HttpRequestHandler; } + // set + // { + // if (_server == null) return; + // _server.HttpRequestHandler = value; + // } + //} + + /// + /// Received request event handler + /// + //public event EventHandler ReceivedRequestEvent + //{ + // add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); } + // remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); } + //} + + /// + /// Constructor for S+. Make sure to set necessary properties using init method + /// + public WebApiServer() + : this(SplusKey, DefaultName, null) + { + } + + /// + /// Constructor + /// + /// + /// + public WebApiServer(string key, string basePath) + : this(key, DefaultName, basePath) + { + } + + /// + /// Constructor + /// + /// + /// + /// + public WebApiServer(string key, string name, string basePath) + { + Key = key; + Name = string.IsNullOrEmpty(name) ? DefaultName : name; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + + if (_server == null) _server = new HttpCwsServer(BasePath); + + _server.setProcessName(Key); + _server.HttpRequestHandler = new DefaultRequestHandler(); + + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; + } + + /// + /// Program status event handler + /// + /// + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType != eProgramStatusEventType.Stopping) return; + + Debug.Console(DebugInfo, this, "Program stopping. stopping server"); + + Stop(); + } + + /// + /// Ethernet event handler + /// + /// + 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 && IsRegistered) + { + Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered."); + return; + } + + Debug.Console(DebugInfo, this, "Ethernet link up. Starting server"); + + Start(); + } + + /// + /// Initializes CWS class + /// + public void Initialize(string key, string basePath) + { + Key = key; + BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath; + } + + /// + /// Adds a route to CWS + /// + public void AddRoute(HttpCwsRoute route) + { + if (route == null) + { + Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null"); + return; + } + + _server.Routes.Add(route); + + } + + /// + /// Removes a route from CWS + /// + /// + public void RemoveRoute(HttpCwsRoute route) + { + if (route == null) + { + Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null"); + return; + } + + _server.Routes.Remove(route); + } + + /// + /// Returns a list of the current routes + /// + public HttpCwsRouteCollection GetRouteCollection() + { + return _server.Routes; + } + + /// + /// Starts CWS instance + /// + public void Start() + { + try + { + _serverLock.Enter(); + + if (_server == null) + { + Debug.Console(DebugInfo, this, "Server is null, unable to start"); + return; + } + + if (IsRegistered) + { + Debug.Console(DebugInfo, this, "Server has already been started"); + return; + } + + IsRegistered = _server.Register(); + + Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed"); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Stop CWS instance + /// + public void Stop() + { + try + { + _serverLock.Enter(); + + if (_server == null) + { + Debug.Console(DebugInfo, this, "Server is null or has already been stopped"); + return; + } + + IsRegistered = _server.Unregister() == false; + + Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful"); + + _server.Dispose(); + _server = null; + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException); + } + finally + { + _serverLock.Leave(); + } + } + + /// + /// Received request handler + /// + /// + /// This is here for development and testing + /// + /// + /// + public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args) + { + try + { + var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented); + Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j); + } + catch (Exception ex) + { + Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message); + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace); + if (ex.InnerException != null) + Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException); + } + } + } +} \ No newline at end of file From c9f3d77e0b39c5b0a2b73aaa494eac09a8be9d3f Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 1 Nov 2023 09:39:46 -0600 Subject: [PATCH 05/36] Adds back in crestron console print statements when running on appliance --- src/Pepperdash Core/Logging/Debug.cs | 36 +++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 4a36a15..e60c047 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -11,8 +11,6 @@ using Serilog; using Serilog.Core; using Serilog.Events; -using Serilog.Formatting.Display; -using System.Runtime.Serialization; namespace PepperDash.Core { @@ -85,6 +83,8 @@ public static DebugWebsocketSink WebsocketSink private const int SaveTimeoutMs = 30000; + private static bool _runningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; + /// /// Version for the currently loaded PepperDashCore dll /// @@ -104,7 +104,7 @@ public static DebugWebsocketSink WebsocketSink static Debug() { - _consoleLoggingLevelSwitch = new LoggingLevelSwitch(); + _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); // Instantiate the root logger @@ -218,6 +218,8 @@ static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventTyp if (programEventType == eProgramStatusEventType.Stopping) { + _logger.CloseAndFlush(); + if (_saveTimer != null) { _saveTimer.Stop(); @@ -430,11 +432,6 @@ public static void ShowDebugLog(string s) } - private static void Log(uint level, string format, params object[] items) - { - - } - /// /// Prints message to console if current debug level is equal to or higher than the level of this message. /// Uses CrestronConsole.PrintLine. @@ -454,8 +451,13 @@ public static void Console(uint level, string format, params object[] items) _logger.Write((LogEventLevel)level, format, items); - //CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber, - // string.Format(format, items)); + if (_runningOnAppliance) + { + CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), + InitialParametersClass.ApplicationNumber, + level, + string.Format(format, items)); + } } /// @@ -467,8 +469,8 @@ public static void Console(uint level, IKeyed dev, string format, params object[ log.Write((LogEventLevel)level, format, items); - //if (Level >= level) - // Console(level, "[{0}] {1}", dev.Key, string.Format(format, items)); + if (Level >= level) + Console(level, "[{0}] {1}", dev.Key, string.Format(format, items)); } /// @@ -490,10 +492,10 @@ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, log.Write((LogEventLevel)level, format, items); - //if (Level >= level) - //{ - // Console(level, str); - //} + if (Level >= level) + { + Console(level, str); + } } /// @@ -546,7 +548,7 @@ public static void ConsoleWithLog(uint level, IKeyed dev, string format, params public static void LogError(ErrorLogLevel errorLogLevel, string str) { - var msg = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str); + var msg = _runningOnAppliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str); switch (errorLogLevel) { case ErrorLogLevel.Error: From baa54e40b289d33e7db524bb4b680411cdaed069 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 1 Nov 2023 09:43:47 -0600 Subject: [PATCH 06/36] Gets rid of CloseAndFlush temporarily --- src/Pepperdash Core/Logging/Debug.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index e60c047..f2beed2 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -218,7 +218,7 @@ static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventTyp if (programEventType == eProgramStatusEventType.Stopping) { - _logger.CloseAndFlush(); + //_logger.CloseAndFlush(); if (_saveTimer != null) { From 0266edda05e6edc2be200d6579616342698dd20f Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 16:56:51 -0700 Subject: [PATCH 07/36] feat: exposes methods to control wssv --- src/Pepperdash Core/Logging/Debug.cs | 13 ++++++++----- .../Logging/DebugWebsocketSink.cs | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index f2beed2..fe7138b 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -109,9 +109,9 @@ static Debug() // Instantiate the root logger _logger = new LoggerConfiguration() - .WriteTo.Logger(lc => lc - .WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) - .WriteTo.Sink(new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) + //.WriteTo.Logger(lc => lc + //.WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) + .WriteTo.Sink(new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.File(@"\user\debug\global-log-{Date}.txt" , rollingInterval: RollingInterval.Day , restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug) @@ -218,7 +218,7 @@ static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventTyp if (programEventType == eProgramStatusEventType.Stopping) { - //_logger.CloseAndFlush(); + Log.CloseAndFlush(); if (_saveTimer != null) { @@ -370,9 +370,12 @@ public static void SetDebugFilterFromConsole(string items) /// Valid values 0 (no debug), 1 (critical), 2 (all messages) public static void SetDebugLevel(int level) { - if (level <= 2) + _consoleLoggingLevelSwitch.MinimumLevel = (LogEventLevel)level; + + if (level <= 5) { Level = level; + _contexts.GetOrCreateItem("DEFAULT").Level = level; SaveMemoryOnTimeout(); diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index c335759..165cb80 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -13,20 +13,33 @@ namespace PepperDash.Core { public class DebugWebsocketSink : ILogEventSink { - public WebSocketServer WSSV { get; private set; } + private WebSocketServer _wssv { get; private set; } private readonly IFormatProvider _formatProvider; public DebugWebsocketSink() { - WSSV = new WebSocketServer(); + _wssv = new WebSocketServer(); } public void Emit(LogEvent logEvent) { + if (_wssv == null || !_wssv.IsListening) return; + var message = logEvent.RenderMessage(_formatProvider); - WSSV.WebSocketServices.Broadcast(message); + _wssv.WebSocketServices.Broadcast(message); + } + + public void StartServerAndSetPort(int port) + { + _wssv = new WebSocketServer(port); + _wssv.Start(); + } + + public void StopServer() + { + _wssv.Stop(); } } From 93c7dbe812dbcfbbb9f9371ee35fcc1889439b27 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 17:58:44 -0700 Subject: [PATCH 08/36] fix: fixes accessiblity level --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 165cb80..8f5f85c 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -13,7 +13,7 @@ namespace PepperDash.Core { public class DebugWebsocketSink : ILogEventSink { - private WebSocketServer _wssv { get; private set; } + private WebSocketServer _wssv; private readonly IFormatProvider _formatProvider; From 95312a827b671b4daa39e77e85ac6d0a862c687a Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 18:17:00 -0700 Subject: [PATCH 09/36] feat: exposes Port and IsListening properties --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 8f5f85c..c6d44c7 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -15,6 +15,10 @@ public class DebugWebsocketSink : ILogEventSink { private WebSocketServer _wssv; + public int Port { get { return _wssv.Port; } } + + public bool IsListening { get { return _wssv.IsListening; } } + private readonly IFormatProvider _formatProvider; public DebugWebsocketSink() @@ -33,6 +37,7 @@ public void Emit(LogEvent logEvent) public void StartServerAndSetPort(int port) { + Port = port; _wssv = new WebSocketServer(port); _wssv.Start(); } From a876de73f11bc660387fc7f6489d8695ef072bb0 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 18:19:19 -0700 Subject: [PATCH 10/36] fix: fixes Port issue --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index c6d44c7..7bed359 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -37,7 +37,6 @@ public void Emit(LogEvent logEvent) public void StartServerAndSetPort(int port) { - Port = port; _wssv = new WebSocketServer(port); _wssv.Start(); } From b845e427e21d5c454ce3a514789b33e0d2b8c454 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 18:31:28 -0700 Subject: [PATCH 11/36] fix: adds null checks --- .../Logging/DebugWebsocketSink.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 7bed359..2d52fde 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -15,9 +15,22 @@ public class DebugWebsocketSink : ILogEventSink { private WebSocketServer _wssv; - public int Port { get { return _wssv.Port; } } + public int Port + { get + { + + if(_wssv == null) return 0; + return _wssv.Port; + } + } - public bool IsListening { get { return _wssv.IsListening; } } + public bool IsListening + { get + { + if (_wssv == null) return false; + return _wssv.IsListening; + } + } private readonly IFormatProvider _formatProvider; From 12ef858ed34a785e4edec0b504f7c9a92f074d88 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 18:43:09 -0700 Subject: [PATCH 12/36] feat: adds status messages for wssv --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 2d52fde..912863e 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -50,12 +50,14 @@ public void Emit(LogEvent logEvent) public void StartServerAndSetPort(int port) { + Debug.Console(0, "Starting Websocket Server on port: {0}", port); _wssv = new WebSocketServer(port); _wssv.Start(); } public void StopServer() - { + { + Debug.Console(0, "Stopping Websocket Server"); _wssv.Stop(); } } From 2223e09283c842e50194420cf8dd41b3cd9f2724 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 19:18:25 -0700 Subject: [PATCH 13/36] fix: Assigns a value to the sink local var --- src/Pepperdash Core/Logging/Debug.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index fe7138b..3ee1e55 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -111,7 +111,7 @@ static Debug() _logger = new LoggerConfiguration() //.WriteTo.Logger(lc => lc //.WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) - .WriteTo.Sink(new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) + .WriteTo.Sink(_websocketSink = new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.File(@"\user\debug\global-log-{Date}.txt" , rollingInterval: RollingInterval.Day , restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug) From 75176666143aa94cf03aa972475dee7fcf5ddd37 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 6 Nov 2023 19:24:03 -0700 Subject: [PATCH 14/36] fix: better way to instantiate websocket sink --- src/Pepperdash Core/Logging/Debug.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 3ee1e55..1089fb0 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -106,12 +106,13 @@ static Debug() { _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); + _websocketSink = new DebugWebsocketSink(); // Instantiate the root logger _logger = new LoggerConfiguration() //.WriteTo.Logger(lc => lc //.WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) - .WriteTo.Sink(_websocketSink = new DebugWebsocketSink(), levelSwitch: _websocketLoggingLevelSwitch) + .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.File(@"\user\debug\global-log-{Date}.txt" , rollingInterval: RollingInterval.Day , restrictedToMinimumLevel: Serilog.Events.LogEventLevel.Debug) From a82e53ce2bb2062f6cc32671e55bd7b8729c9b2b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Nov 2023 12:08:50 -0700 Subject: [PATCH 15/36] feat: Adds WebSocketBehaviour --- .../Logging/DebugWebsocketSink.cs | 125 ++++++++++++++++-- src/Pepperdash Core/PepperDash_Core.csproj | 1 + 2 files changed, 114 insertions(+), 12 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 912863e..3ff5cd8 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -8,27 +8,33 @@ using Serilog.Events; using Serilog.Configuration; using WebSocketSharp.Server; +using Crestron.SimplSharp; +using System.Net.Http; +using System.Text.RegularExpressions; +using WebSocketSharp; namespace PepperDash.Core { public class DebugWebsocketSink : ILogEventSink { - private WebSocketServer _wssv; + private HttpServer _server; + + private string _path = "/join"; public int Port { get { - if(_wssv == null) return 0; - return _wssv.Port; + if(_server == null) return 0; + return _server.Port; } } public bool IsListening { get { - if (_wssv == null) return false; - return _wssv.IsListening; + if (_server == null) return false; + return _server.IsListening; } } @@ -36,29 +42,35 @@ public bool IsListening public DebugWebsocketSink() { - _wssv = new WebSocketServer(); - + CrestronEnvironment.ProgramStatusEventHandler += type => + { + if (type == eProgramStatusEventType.Stopping) + { + StopServer(); + } + }; } public void Emit(LogEvent logEvent) { - if (_wssv == null || !_wssv.IsListening) return; + if (_server == null || !_server.IsListening) return; var message = logEvent.RenderMessage(_formatProvider); - _wssv.WebSocketServices.Broadcast(message); + _server.WebSocketServices.Broadcast(message); } public void StartServerAndSetPort(int port) { Debug.Console(0, "Starting Websocket Server on port: {0}", port); - _wssv = new WebSocketServer(port); - _wssv.Start(); + _server = new HttpServer(port); + _server.AddWebSocketService(_path); + _server.Start(); } public void StopServer() { Debug.Console(0, "Stopping Websocket Server"); - _wssv.Stop(); + _server.Stop(); } } @@ -71,4 +83,93 @@ public static LoggerConfiguration DebugWebsocketSink( return loggerConfiguration.Sink(new DebugWebsocketSink()); } } + + public class DebugClient : WebSocketBehavior + { + private DateTime _connectionTime; + + public TimeSpan ConnectedDuration + { + get + { + if (Context.WebSocket.IsAlive) + { + return DateTime.Now - _connectionTime; + } + else + { + return new TimeSpan(0); + } + } + } + + public DebugClient() + { + + } + + protected override void OnOpen() + { + base.OnOpen(); + + var url = Context.WebSocket.Url; + Debug.Console(2, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); + + //var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)"); + + //if (match.Success) + //{ + // var clientId = match.Groups[1].Value; + + // // Inform controller of client joining + // if (Controller != null) + // { + // var clientJoined = new MobileControlResponseMessage + // { + // Type = "/system/roomKey", + // ClientId = clientId, + // Content = RoomKey, + // }; + + // Controller.SendMessageObjectToDirectClient(clientJoined); + + // var bridge = Controller.GetRoomBridge(RoomKey); + + // SendUserCodeToClient(bridge, clientId); + + // bridge.UserCodeChanged += (sender, args) => SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, clientId); + // } + // else + // { + // Debug.Console(2, "WebSocket UiClient Controller is null"); + // } + //} + + _connectionTime = DateTime.Now; + + // TODO: Future: Check token to see if there's already an open session using that token and reject/close the session + } + + protected override void OnMessage(MessageEventArgs e) + { + base.OnMessage(e); + + Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data); + } + + protected override void OnClose(CloseEventArgs e) + { + base.OnClose(e); + + Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason); + + } + + protected override void OnError(ErrorEventArgs e) + { + base.OnError(e); + + Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message); + } + } } diff --git a/src/Pepperdash Core/PepperDash_Core.csproj b/src/Pepperdash Core/PepperDash_Core.csproj index 3100767..2cdf3af 100644 --- a/src/Pepperdash Core/PepperDash_Core.csproj +++ b/src/Pepperdash Core/PepperDash_Core.csproj @@ -14,6 +14,7 @@ https://github.com/PepperDash/PepperDashCore crestron;4series; $(Version) + $(Version) ../../package From 072d77a369833497b0857443737c118a0e00e76f Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Tue, 7 Nov 2023 20:56:29 -0700 Subject: [PATCH 16/36] feat: adds BouncyCertificate --- .../Logging/DebugWebsocketSink.cs | 183 ++++++--- src/Pepperdash Core/PepperDash_Core.csproj | 1 + src/Pepperdash Core/Web/BouncyCertificate.cs | 360 ++++++++++++++++++ 3 files changed, 492 insertions(+), 52 deletions(-) create mode 100644 src/Pepperdash Core/Web/BouncyCertificate.cs diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 3ff5cd8..7cf7c8f 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -9,39 +9,43 @@ using Serilog.Configuration; using WebSocketSharp.Server; using Crestron.SimplSharp; -using System.Net.Http; -using System.Text.RegularExpressions; using WebSocketSharp; +using System.Security.Authentication; +using WebSocketSharp.Net; +using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; +using System.IO; +using Org.BouncyCastle.Asn1.X509; namespace PepperDash.Core { public class DebugWebsocketSink : ILogEventSink { - private HttpServer _server; - + private HttpServer _httpsServer; + private string _path = "/join"; + private const string _certificateName = "selfCres"; + private const string _certificatePassword = "cres12345"; public int Port { get { - if(_server == null) return 0; - return _server.Port; + if(_httpsServer == null) return 0; + return _httpsServer.Port; } } - public bool IsListening - { get - { - if (_server == null) return false; - return _server.IsListening; - } - } + public bool IsRunning { get => _httpsServer?.IsListening ?? false; } + private readonly IFormatProvider _formatProvider; public DebugWebsocketSink() { + + if (!File.Exists($"\\user\\{_certificateName}.pfx")) + CreateCert(null); + CrestronEnvironment.ProgramStatusEventHandler += type => { if (type == eProgramStatusEventType.Stopping) @@ -51,26 +55,133 @@ public DebugWebsocketSink() }; } + private void CreateCert(string[] args) + { + try + { + Debug.Console(0,$"CreateCert Creating Utility"); + //var utility = new CertificateUtility(); + var utility = new BouncyCertificate(); + Debug.Console(0, $"CreateCert Calling CreateCert"); + //utility.CreateCert(); + 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 domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); + + Debug.Console(0, $"DomainName: {domainName} | HostName: {hostName} | {hostName}.{domainName}@{ipAddress}"); + + var certificate = utility.CreateSelfSignedCertificate($"CN={hostName}.{domainName}", new[] { $"{hostName}.{domainName}", ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth }); + //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested + //Debug.Print($"CreateCert Storing Certificate To My.LocalMachine"); + //utility.AddCertToStore(certificate, StoreName.My, StoreLocation.LocalMachine); + Debug.Console(0, $"CreateCert Saving Cert to \\user\\"); + utility.CertificatePassword = _certificatePassword; + utility.WriteCertificate(certificate, @"\user\", _certificateName); + Debug.Console(0, $"CreateCert Ending CreateCert"); + } + catch (Exception ex) + { + Debug.Console(0, $"WSS CreateCert Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); + } + } + public void Emit(LogEvent logEvent) { - if (_server == null || !_server.IsListening) return; + if (_httpsServer == null || !_httpsServer.IsListening) return; var message = logEvent.RenderMessage(_formatProvider); - _server.WebSocketServices.Broadcast(message); + _httpsServer.WebSocketServices.Broadcast(message); } public void StartServerAndSetPort(int port) { Debug.Console(0, "Starting Websocket Server on port: {0}", port); - _server = new HttpServer(port); - _server.AddWebSocketService(_path); - _server.Start(); + + + Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @"\html\wss"); + } + + private void Start(int port, string certPath = "", string certPassword = "", string rootPath = @"\html") + { + try + { + _httpsServer = new HttpServer(port, true) + { + RootPath = rootPath + }; + + if (!string.IsNullOrWhiteSpace(certPath)) + { + Debug.Console(0, "Assigning SSL Configuration"); + _httpsServer.SslConfiguration = new ServerSslConfiguration(new X509Certificate2(certPath, certPassword)) + { + ClientCertificateRequired = false, + CheckCertificateRevocation = false, + EnabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, + //this is just to test, you might want to actually validate + ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => + { + Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered"); + return true; + } + }; + } + Debug.Console(0, "Adding Debug Client Service"); + _httpsServer.AddWebSocketService("/echo"); + Debug.Console(0, "Assigning Log Info"); + _httpsServer.Log.Level = LogLevel.Trace; + _httpsServer.Log.Output = delegate + { + //Debug.Print(DebugLevel.WebSocket, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); + }; + Debug.Console(0, "Starting"); + + //_httpsServer.OnGet += (sender, e) => + //{ + // Debug.Console(0, $"OnGet requesting {e.Request}"); + // var req = e.Request; + // var res = e.Response; + + // var path = req.RawUrl; + + // if (path == "/") + // path += "index.html"; + + // var localPath = Path.Combine(rootPath, path.Substring(1)); + + // byte[] contents; + // if (File.Exists(localPath)) + // contents = File.ReadAllBytes(localPath); + // else + // { + // e.Response.StatusCode = 404; + // contents = Encoding.UTF8.GetBytes("Path not found " + e.Request.RawUrl); + // } + + // var extention = Path.GetExtension(path); + // if (!_contentTypes.TryGetValue(extention, out var contentType)) + // contentType = "text/html"; + + // res.ContentLength64 = contents.LongLength; + + // res.Close(contents, true); + //}; + + _httpsServer.Start(); + Debug.Console(0, "Ready"); + } + catch (Exception ex) + { + Debug.Console(0, "WebSocket Failed to start {0}", ex.Message); + } } public void StopServer() { Debug.Console(0, "Stopping Websocket Server"); - _server.Stop(); + _httpsServer?.Stop(); + + _httpsServer = null; } } @@ -115,39 +226,7 @@ protected override void OnOpen() var url = Context.WebSocket.Url; Debug.Console(2, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); - //var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)"); - - //if (match.Success) - //{ - // var clientId = match.Groups[1].Value; - - // // Inform controller of client joining - // if (Controller != null) - // { - // var clientJoined = new MobileControlResponseMessage - // { - // Type = "/system/roomKey", - // ClientId = clientId, - // Content = RoomKey, - // }; - - // Controller.SendMessageObjectToDirectClient(clientJoined); - - // var bridge = Controller.GetRoomBridge(RoomKey); - - // SendUserCodeToClient(bridge, clientId); - - // bridge.UserCodeChanged += (sender, args) => SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, clientId); - // } - // else - // { - // Debug.Console(2, "WebSocket UiClient Controller is null"); - // } - //} - _connectionTime = DateTime.Now; - - // TODO: Future: Check token to see if there's already an open session using that token and reject/close the session } protected override void OnMessage(MessageEventArgs e) @@ -165,7 +244,7 @@ protected override void OnClose(CloseEventArgs e) } - protected override void OnError(ErrorEventArgs e) + protected override void OnError(WebSocketSharp.ErrorEventArgs e) { base.OnError(e); diff --git a/src/Pepperdash Core/PepperDash_Core.csproj b/src/Pepperdash Core/PepperDash_Core.csproj index 2cdf3af..db72046 100644 --- a/src/Pepperdash Core/PepperDash_Core.csproj +++ b/src/Pepperdash Core/PepperDash_Core.csproj @@ -31,6 +31,7 @@ + Full diff --git a/src/Pepperdash Core/Web/BouncyCertificate.cs b/src/Pepperdash Core/Web/BouncyCertificate.cs new file mode 100644 index 0000000..c969235 --- /dev/null +++ b/src/Pepperdash Core/Web/BouncyCertificate.cs @@ -0,0 +1,360 @@ +using Crestron.SimplSharp; + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Prng; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.Pkcs; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; +using Org.BouncyCastle.X509; +using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; +using X509KeyStorageFlags = System.Security.Cryptography.X509Certificates.X509KeyStorageFlags; +using X509ContentType = System.Security.Cryptography.X509Certificates.X509ContentType; +using System.Text; +using Org.BouncyCastle.Crypto.Operators; +using System.Numerics; +using System.Security.Cryptography.X509Certificates; +using BigInteger = Org.BouncyCastle.Math.BigInteger; +using X509Certificate = Org.BouncyCastle.X509.X509Certificate; + +namespace PepperDash.Core +{ + /// + /// Taken From https://github.com/rlipscombe/bouncy-castle-csharp/ + /// + internal class BouncyCertificate + { + public string CertificatePassword { get; set; } = "password"; + public X509Certificate2 LoadCertificate(string issuerFileName, string password) + { + // We need to pass 'Exportable', otherwise we can't get the private key. + var issuerCertificate = new X509Certificate2(issuerFileName, password, X509KeyStorageFlags.Exportable); + return issuerCertificate; + } + + public X509Certificate2 IssueCertificate(string subjectName, X509Certificate2 issuerCertificate, string[] subjectAlternativeNames, KeyPurposeID[] usages) + { + // It's self-signed, so these are the same. + var issuerName = issuerCertificate.Subject; + + var random = GetSecureRandom(); + var subjectKeyPair = GenerateKeyPair(random, 2048); + + var issuerKeyPair = DotNetUtilities.GetKeyPair(issuerCertificate.PrivateKey); + + var serialNumber = GenerateSerialNumber(random); + var issuerSerialNumber = new BigInteger(issuerCertificate.GetSerialNumber()); + + const bool isCertificateAuthority = false; + var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber, + subjectAlternativeNames, issuerName, issuerKeyPair, + issuerSerialNumber, isCertificateAuthority, + usages); + return ConvertCertificate(certificate, subjectKeyPair, random); + } + + public X509Certificate2 CreateCertificateAuthorityCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages) + { + // It's self-signed, so these are the same. + var issuerName = subjectName; + + var random = GetSecureRandom(); + var subjectKeyPair = GenerateKeyPair(random, 2048); + + // It's self-signed, so these are the same. + var issuerKeyPair = subjectKeyPair; + + var serialNumber = GenerateSerialNumber(random); + var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number. + + const bool isCertificateAuthority = true; + var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber, + subjectAlternativeNames, issuerName, issuerKeyPair, + issuerSerialNumber, isCertificateAuthority, + usages); + return ConvertCertificate(certificate, subjectKeyPair, random); + } + + public X509Certificate2 CreateSelfSignedCertificate(string subjectName, string[] subjectAlternativeNames, KeyPurposeID[] usages) + { + // It's self-signed, so these are the same. + var issuerName = subjectName; + + var random = GetSecureRandom(); + var subjectKeyPair = GenerateKeyPair(random, 2048); + + // It's self-signed, so these are the same. + var issuerKeyPair = subjectKeyPair; + + var serialNumber = GenerateSerialNumber(random); + var issuerSerialNumber = serialNumber; // Self-signed, so it's the same serial number. + + const bool isCertificateAuthority = false; + var certificate = GenerateCertificate(random, subjectName, subjectKeyPair, serialNumber, + subjectAlternativeNames, issuerName, issuerKeyPair, + issuerSerialNumber, isCertificateAuthority, + usages); + return ConvertCertificate(certificate, subjectKeyPair, random); + } + + private SecureRandom GetSecureRandom() + { + // Since we're on Windows, we'll use the CryptoAPI one (on the assumption + // that it might have access to better sources of entropy than the built-in + // Bouncy Castle ones): + var randomGenerator = new CryptoApiRandomGenerator(); + var random = new SecureRandom(randomGenerator); + return random; + } + + private X509Certificate GenerateCertificate(SecureRandom random, + string subjectName, + AsymmetricCipherKeyPair subjectKeyPair, + BigInteger subjectSerialNumber, + string[] subjectAlternativeNames, + string issuerName, + AsymmetricCipherKeyPair issuerKeyPair, + BigInteger issuerSerialNumber, + bool isCertificateAuthority, + KeyPurposeID[] usages) + { + var certificateGenerator = new X509V3CertificateGenerator(); + + certificateGenerator.SetSerialNumber(subjectSerialNumber); + + var issuerDN = new X509Name(issuerName); + certificateGenerator.SetIssuerDN(issuerDN); + + // Note: The subject can be omitted if you specify a subject alternative name (SAN). + var subjectDN = new X509Name(subjectName); + certificateGenerator.SetSubjectDN(subjectDN); + + // Our certificate needs valid from/to values. + var notBefore = DateTime.UtcNow.Date; + var notAfter = notBefore.AddYears(2); + + certificateGenerator.SetNotBefore(notBefore); + certificateGenerator.SetNotAfter(notAfter); + + // The subject's public key goes in the certificate. + certificateGenerator.SetPublicKey(subjectKeyPair.Public); + + AddAuthorityKeyIdentifier(certificateGenerator, issuerDN, issuerKeyPair, issuerSerialNumber); + AddSubjectKeyIdentifier(certificateGenerator, subjectKeyPair); + //AddBasicConstraints(certificateGenerator, isCertificateAuthority); + + if (usages != null && usages.Any()) + AddExtendedKeyUsage(certificateGenerator, usages); + + if (subjectAlternativeNames != null && subjectAlternativeNames.Any()) + AddSubjectAlternativeNames(certificateGenerator, subjectAlternativeNames); + + // Set the signature algorithm. This is used to generate the thumbprint which is then signed + // with the issuer's private key. We'll use SHA-256, which is (currently) considered fairly strong. + const string signatureAlgorithm = "SHA256WithRSA"; + + // The certificate is signed with the issuer's private key. + ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, issuerKeyPair.Private, random); + var certificate = certificateGenerator.Generate(signatureFactory); + return certificate; + } + + /// + /// The certificate needs a serial number. This is used for revocation, + /// and usually should be an incrementing index (which makes it easier to revoke a range of certificates). + /// Since we don't have anywhere to store the incrementing index, we can just use a random number. + /// + /// + /// + private BigInteger GenerateSerialNumber(SecureRandom random) + { + var serialNumber = + BigIntegers.CreateRandomInRange( + BigInteger.One, BigInteger.ValueOf(Int64.MaxValue), random); + return serialNumber; + } + + /// + /// Generate a key pair. + /// + /// The random number generator. + /// The key length in bits. For RSA, 2048 bits should be considered the minimum acceptable these days. + /// + private AsymmetricCipherKeyPair GenerateKeyPair(SecureRandom random, int strength) + { + var keyGenerationParameters = new KeyGenerationParameters(random, strength); + + var keyPairGenerator = new RsaKeyPairGenerator(); + keyPairGenerator.Init(keyGenerationParameters); + var subjectKeyPair = keyPairGenerator.GenerateKeyPair(); + return subjectKeyPair; + } + + /// + /// Add the Authority Key Identifier. According to http://www.alvestrand.no/objectid/2.5.29.35.html, this + /// identifies the public key to be used to verify the signature on this certificate. + /// In a certificate chain, this corresponds to the "Subject Key Identifier" on the *issuer* certificate. + /// The Bouncy Castle documentation, at http://www.bouncycastle.org/wiki/display/JA1/X.509+Public+Key+Certificate+and+Certification+Request+Generation, + /// shows how to create this from the issuing certificate. Since we're creating a self-signed certificate, we have to do this slightly differently. + /// + /// + /// + /// + /// + private void AddAuthorityKeyIdentifier(X509V3CertificateGenerator certificateGenerator, + X509Name issuerDN, + AsymmetricCipherKeyPair issuerKeyPair, + BigInteger issuerSerialNumber) + { + var authorityKeyIdentifierExtension = + new AuthorityKeyIdentifier( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(issuerKeyPair.Public), + new GeneralNames(new GeneralName(issuerDN)), + issuerSerialNumber); + certificateGenerator.AddExtension( + X509Extensions.AuthorityKeyIdentifier.Id, false, authorityKeyIdentifierExtension); + } + + /// + /// Add the "Subject Alternative Names" extension. Note that you have to repeat + /// the value from the "Subject Name" property. + /// + /// + /// + private void AddSubjectAlternativeNames(X509V3CertificateGenerator certificateGenerator, + IEnumerable subjectAlternativeNames) + { + var subjectAlternativeNamesExtension = + new DerSequence( + subjectAlternativeNames.Select(name => new GeneralName(GeneralName.DnsName, name)) + .ToArray()); + certificateGenerator.AddExtension( + X509Extensions.SubjectAlternativeName.Id, false, subjectAlternativeNamesExtension); + } + + /// + /// Add the "Extended Key Usage" extension, specifying (for example) "server authentication". + /// + /// + /// + private void AddExtendedKeyUsage(X509V3CertificateGenerator certificateGenerator, KeyPurposeID[] usages) + { + certificateGenerator.AddExtension( + X509Extensions.ExtendedKeyUsage.Id, false, new ExtendedKeyUsage(usages)); + } + + /// + /// Add the "Basic Constraints" extension. + /// + /// + /// + private void AddBasicConstraints(X509V3CertificateGenerator certificateGenerator, + bool isCertificateAuthority) + { + certificateGenerator.AddExtension( + X509Extensions.BasicConstraints.Id, true, new BasicConstraints(isCertificateAuthority)); + } + + /// + /// Add the Subject Key Identifier. + /// + /// + /// + private void AddSubjectKeyIdentifier(X509V3CertificateGenerator certificateGenerator, + AsymmetricCipherKeyPair subjectKeyPair) + { + var subjectKeyIdentifierExtension = + new SubjectKeyIdentifier( + SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public)); + certificateGenerator.AddExtension( + X509Extensions.SubjectKeyIdentifier.Id, false, subjectKeyIdentifierExtension); + } + + private X509Certificate2 ConvertCertificate(X509Certificate certificate, + AsymmetricCipherKeyPair subjectKeyPair, + SecureRandom random) + { + // Now to convert the Bouncy Castle certificate to a .NET certificate. + // See http://web.archive.org/web/20100504192226/http://www.fkollmann.de/v2/post/Creating-certificates-using-BouncyCastle.aspx + // ...but, basically, we create a PKCS12 store (a .PFX file) in memory, and add the public and private key to that. + var store = new Pkcs12Store(); + + // What Bouncy Castle calls "alias" is the same as what Windows terms the "friendly name". + string friendlyName = certificate.SubjectDN.ToString(); + + // Add the certificate. + var certificateEntry = new X509CertificateEntry(certificate); + store.SetCertificateEntry(friendlyName, certificateEntry); + + // Add the private key. + store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), new[] { certificateEntry }); + + // Convert it to an X509Certificate2 object by saving/loading it from a MemoryStream. + // It needs a password. Since we'll remove this later, it doesn't particularly matter what we use. + + var stream = new MemoryStream(); + store.Save(stream, CertificatePassword.ToCharArray(), random); + + var convertedCertificate = + new X509Certificate2(stream.ToArray(), + CertificatePassword, + X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable); + return convertedCertificate; + } + + public void WriteCertificate(X509Certificate2 certificate, string outputDirectory, string certName) + { + // This password is the one attached to the PFX file. Use 'null' for no password. + // Create PFX (PKCS #12) with private key + try + { + var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword); + File.WriteAllBytes($"{Path.Combine(outputDirectory, certName)}.pfx", pfx); + } + catch (Exception ex) + { + CrestronConsole.PrintLine($"Failed to write x509 cert pfx\r\n{ex.Message}"); + } + // Create Base 64 encoded CER (public key only) + using (var writer = new StreamWriter($"{Path.Combine(outputDirectory, certName)}.cer", false)) + { + try + { + var contents = $"-----BEGIN CERTIFICATE-----\r\n{Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)}\r\n-----END CERTIFICATE-----"; + writer.Write(contents); + } + catch (Exception ex) + { + CrestronConsole.PrintLine($"Failed to write x509 cert cer\r\n{ex.Message}"); + } + } + } + public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X509Certificates.StoreName st, System.Security.Cryptography.X509Certificates.StoreLocation sl) + { + bool bRet = false; + + try + { + var store = new System.Security.Cryptography.X509Certificates.X509Store(st, sl); + store.Open(System.Security.Cryptography.X509Certificates.OpenFlags.ReadWrite); + store.Add(cert); + + store.Close(); + bRet = true; + } + catch (Exception ex) + { + CrestronConsole.PrintLine($"AddCertToStore Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); + } + + return bRet; + } + } +} \ No newline at end of file From 846abe24d4ee48a50b98676030c782a633056637 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 15 Nov 2023 16:50:24 -0700 Subject: [PATCH 17/36] fix: fixed debug message formatting --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 7cf7c8f..4c6c356 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -59,29 +59,29 @@ private void CreateCert(string[] args) { try { - Debug.Console(0,$"CreateCert Creating Utility"); + Debug.Console(0,"CreateCert Creating Utility"); //var utility = new CertificateUtility(); var utility = new BouncyCertificate(); - Debug.Console(0, $"CreateCert Calling CreateCert"); + Debug.Console(0, "CreateCert Calling CreateCert"); //utility.CreateCert(); 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 domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - Debug.Console(0, $"DomainName: {domainName} | HostName: {hostName} | {hostName}.{domainName}@{ipAddress}"); + Debug.Console(0, "DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress); var certificate = utility.CreateSelfSignedCertificate($"CN={hostName}.{domainName}", new[] { $"{hostName}.{domainName}", ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth }); //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested //Debug.Print($"CreateCert Storing Certificate To My.LocalMachine"); //utility.AddCertToStore(certificate, StoreName.My, StoreLocation.LocalMachine); - Debug.Console(0, $"CreateCert Saving Cert to \\user\\"); + Debug.Console(0, "CreateCert Saving Cert to \\user\\"); utility.CertificatePassword = _certificatePassword; utility.WriteCertificate(certificate, @"\user\", _certificateName); - Debug.Console(0, $"CreateCert Ending CreateCert"); + Debug.Console(0, "CreateCert Ending CreateCert"); } catch (Exception ex) { - Debug.Console(0, $"WSS CreateCert Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); + Debug.Console(0, "WSS CreateCert Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); } } From 7872468af2517ffd43ffc617298e07ad4d95fced Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 15 Nov 2023 17:14:13 -0700 Subject: [PATCH 18/36] fix: more string formatting updates --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 4 ++-- src/Pepperdash Core/Web/BouncyCertificate.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 4c6c356..fad13e2 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -70,7 +70,7 @@ private void CreateCert(string[] args) Debug.Console(0, "DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress); - var certificate = utility.CreateSelfSignedCertificate($"CN={hostName}.{domainName}", new[] { $"{hostName}.{domainName}", ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth }); + var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), new[] { string.Format("{0}.{1}", hostName, domainName), ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth }); //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested //Debug.Print($"CreateCert Storing Certificate To My.LocalMachine"); //utility.AddCertToStore(certificate, StoreName.My, StoreLocation.LocalMachine); @@ -81,7 +81,7 @@ private void CreateCert(string[] args) } catch (Exception ex) { - Debug.Console(0, "WSS CreateCert Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); + Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); } } diff --git a/src/Pepperdash Core/Web/BouncyCertificate.cs b/src/Pepperdash Core/Web/BouncyCertificate.cs index c969235..67c129f 100644 --- a/src/Pepperdash Core/Web/BouncyCertificate.cs +++ b/src/Pepperdash Core/Web/BouncyCertificate.cs @@ -316,23 +316,23 @@ public void WriteCertificate(X509Certificate2 certificate, string outputDirector try { var pfx = certificate.Export(X509ContentType.Pfx, CertificatePassword); - File.WriteAllBytes($"{Path.Combine(outputDirectory, certName)}.pfx", pfx); + File.WriteAllBytes(string.Format("{0}.pfx", Path.Combine(outputDirectory, certName)), pfx); } catch (Exception ex) { - CrestronConsole.PrintLine($"Failed to write x509 cert pfx\r\n{ex.Message}"); + CrestronConsole.PrintLine(string.Format("Failed to write x509 cert pfx\r\n{0}", ex.Message)); } // Create Base 64 encoded CER (public key only) using (var writer = new StreamWriter($"{Path.Combine(outputDirectory, certName)}.cer", false)) { try { - var contents = $"-----BEGIN CERTIFICATE-----\r\n{Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)}\r\n-----END CERTIFICATE-----"; + var contents = string.Format("-----BEGIN CERTIFICATE-----\r\n{0}\r\n-----END CERTIFICATE-----", Convert.ToBase64String(certificate.Export(X509ContentType.Cert), Base64FormattingOptions.InsertLineBreaks)); writer.Write(contents); } catch (Exception ex) { - CrestronConsole.PrintLine($"Failed to write x509 cert cer\r\n{ex.Message}"); + CrestronConsole.PrintLine(string.Format("Failed to write x509 cert cer\r\n{0}", ex.Message)); } } } @@ -351,7 +351,7 @@ public bool AddCertToStore(X509Certificate2 cert, System.Security.Cryptography.X } catch (Exception ex) { - CrestronConsole.PrintLine($"AddCertToStore Failed\r\n{ex.Message}\r\n{ex.StackTrace}"); + CrestronConsole.PrintLine(string.Format("AddCertToStore Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace)); } return bRet; From 0e7672dc3a3dcc2f13f490f62591a32fd56cad91 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 16 Nov 2023 13:17:37 -0700 Subject: [PATCH 19/36] fix: swaps debug statements in CreateCert method for printlines as logger hasn't been constructed yet when it gets called. --- .../Logging/DebugWebsocketSink.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index fad13e2..202f472 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -59,29 +59,35 @@ private void CreateCert(string[] args) { try { - Debug.Console(0,"CreateCert Creating Utility"); + //Debug.Console(0,"CreateCert Creating Utility"); + CrestronConsole.PrintLine("CreateCert Creating Utility"); //var utility = new CertificateUtility(); var utility = new BouncyCertificate(); - Debug.Console(0, "CreateCert Calling CreateCert"); + //Debug.Console(0, "CreateCert Calling CreateCert"); + CrestronConsole.PrintLine("CreateCert Calling CreateCert"); //utility.CreateCert(); 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 domainName = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, 0); - Debug.Console(0, "DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress); + //Debug.Console(0, "DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress); + CrestronConsole.PrintLine(string.Format("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress)); var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), new[] { string.Format("{0}.{1}", hostName, domainName), ipAddress }, new[] { KeyPurposeID.IdKPServerAuth, KeyPurposeID.IdKPClientAuth }); //Crestron fails to let us do this...perhaps it should be done through their Dll's but haven't tested //Debug.Print($"CreateCert Storing Certificate To My.LocalMachine"); //utility.AddCertToStore(certificate, StoreName.My, StoreLocation.LocalMachine); - Debug.Console(0, "CreateCert Saving Cert to \\user\\"); + //Debug.Console(0, "CreateCert Saving Cert to \\user\\"); + CrestronConsole.PrintLine("CreateCert Saving Cert to \\user\\"); utility.CertificatePassword = _certificatePassword; utility.WriteCertificate(certificate, @"\user\", _certificateName); - Debug.Console(0, "CreateCert Ending CreateCert"); + //Debug.Console(0, "CreateCert Ending CreateCert"); + CrestronConsole.PrintLine("CreateCert Ending CreateCert"); } catch (Exception ex) { - Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); + //Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace); + CrestronConsole.PrintLine(string.Format("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace)); } } From 3ce284750aa444dc15a24769f68004e848b570be Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 16 Nov 2023 15:11:29 -0700 Subject: [PATCH 20/36] fix: updates path on service --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 202f472..7d35bd2 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -133,7 +133,7 @@ private void Start(int port, string certPath = "", string certPassword = "", str }; } Debug.Console(0, "Adding Debug Client Service"); - _httpsServer.AddWebSocketService("/echo"); + _httpsServer.AddWebSocketService("/debug/join"); Debug.Console(0, "Assigning Log Info"); _httpsServer.Log.Level = LogLevel.Trace; _httpsServer.Log.Output = delegate From eece4dece16cc1a9cb1721a860152a64cee80813 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 12:59:49 -0700 Subject: [PATCH 21/36] fix: adds debug statement to log output --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 7d35bd2..f054419 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -97,6 +97,14 @@ public void Emit(LogEvent logEvent) var message = logEvent.RenderMessage(_formatProvider); _httpsServer.WebSocketServices.Broadcast(message); + + foreach(var service in _httpsServer.WebSocketServices.Hosts) + { + foreach (var session in service.Sessions.Sessions) + { + + } + } } public void StartServerAndSetPort(int port) @@ -136,9 +144,9 @@ private void Start(int port, string certPath = "", string certPassword = "", str _httpsServer.AddWebSocketService("/debug/join"); Debug.Console(0, "Assigning Log Info"); _httpsServer.Log.Level = LogLevel.Trace; - _httpsServer.Log.Output = delegate + _httpsServer.Log.Output = (d, s) => { - //Debug.Print(DebugLevel.WebSocket, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); + Debug.Console(0, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); }; Debug.Console(0, "Starting"); @@ -222,7 +230,7 @@ public TimeSpan ConnectedDuration public DebugClient() { - + Debug.Console(0, "DebugClient Created"); } protected override void OnOpen() From 40f540e23a47075c972787dac2e9215f50259a4d Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 13:11:46 -0700 Subject: [PATCH 22/36] fix: fix rootPath for wss server --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index f054419..9ad30d6 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -112,10 +112,10 @@ public void StartServerAndSetPort(int port) Debug.Console(0, "Starting Websocket Server on port: {0}", port); - Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @"\html\wss"); + Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @""); } - private void Start(int port, string certPath = "", string certPassword = "", string rootPath = @"\html") + private void Start(int port, string certPath = "", string certPassword = "", string rootPath = @"/html") { try { From 19032a0f6d3623f948f341176ba1e5398dfd76f4 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 13:40:55 -0700 Subject: [PATCH 23/36] fix: Adds url property and utilizes private _path for consistency --- .../Logging/DebugWebsocketSink.cs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 9ad30d6..ad21b49 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -22,7 +22,7 @@ public class DebugWebsocketSink : ILogEventSink { private HttpServer _httpsServer; - private string _path = "/join"; + private string _path = "/debug/join/"; private const string _certificateName = "selfCres"; private const string _certificatePassword = "cres12345"; @@ -35,6 +35,15 @@ public int Port } } + public string Url + { + get + { + if (_httpsServer == null) return ""; + return $"wss://{_httpsServer.Address}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}"; + } + } + public bool IsRunning { get => _httpsServer?.IsListening ?? false; } @@ -112,17 +121,15 @@ public void StartServerAndSetPort(int port) Debug.Console(0, "Starting Websocket Server on port: {0}", port); - Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @""); + Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @"/"); } private void Start(int port, string certPath = "", string certPassword = "", string rootPath = @"/html") { try { - _httpsServer = new HttpServer(port, true) - { - RootPath = rootPath - }; + _httpsServer = new HttpServer(port, true); + if (!string.IsNullOrWhiteSpace(certPath)) { @@ -141,7 +148,7 @@ private void Start(int port, string certPath = "", string certPassword = "", str }; } Debug.Console(0, "Adding Debug Client Service"); - _httpsServer.AddWebSocketService("/debug/join"); + _httpsServer.AddWebSocketService(_path); Debug.Console(0, "Assigning Log Info"); _httpsServer.Log.Level = LogLevel.Trace; _httpsServer.Log.Output = (d, s) => From 6c51ce5b959bdca4a8e65923809286f5411a7862 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 14:02:43 -0700 Subject: [PATCH 24/36] fix: adds IP from ethernet helper class --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index ad21b49..c11ee55 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -40,7 +40,7 @@ public string Url get { if (_httpsServer == null) return ""; - return $"wss://{_httpsServer.Address}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}"; + return $"wss://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{_httpsServer.Port}{_httpsServer.WebSocketServices[_path].Path}"; } } From 29c3fe22b1a5767f05b8957eeb1db0ba063b8f14 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 14:55:30 -0700 Subject: [PATCH 25/36] fix: adds text formatter --- src/Pepperdash Core/Logging/Debug.cs | 3 +- .../Logging/DebugWebsocketSink.cs | 30 +++++++++---------- src/Pepperdash Core/PepperDash_Core.csproj | 5 ++-- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 1089fb0..4746755 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -11,6 +11,7 @@ using Serilog; using Serilog.Core; using Serilog.Events; +using Serilog.Formatting.Compact; namespace PepperDash.Core { @@ -106,7 +107,7 @@ static Debug() { _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); - _websocketSink = new DebugWebsocketSink(); + _websocketSink = new DebugWebsocketSink(new CompactJsonFormatter()); // Instantiate the root logger _logger = new LoggerConfiguration() diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index c11ee55..3cb0d49 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -15,6 +15,9 @@ using X509Certificate2 = System.Security.Cryptography.X509Certificates.X509Certificate2; using System.IO; using Org.BouncyCastle.Asn1.X509; +using Serilog.Formatting; +using Newtonsoft.Json.Linq; +using Serilog.Formatting.Json; namespace PepperDash.Core { @@ -47,11 +50,13 @@ public string Url public bool IsRunning { get => _httpsServer?.IsListening ?? false; } - private readonly IFormatProvider _formatProvider; + private readonly ITextFormatter _textFormatter; - public DebugWebsocketSink() + public DebugWebsocketSink(ITextFormatter formatProvider) { + _textFormatter = formatProvider ?? new JsonFormatter(); + if (!File.Exists($"\\user\\{_certificateName}.pfx")) CreateCert(null); @@ -104,16 +109,11 @@ public void Emit(LogEvent logEvent) { if (_httpsServer == null || !_httpsServer.IsListening) return; - var message = logEvent.RenderMessage(_formatProvider); - _httpsServer.WebSocketServices.Broadcast(message); + var sw = new StringWriter(); + _textFormatter.Format(logEvent, sw); + + _httpsServer.WebSocketServices.Broadcast(sw.ToString()); - foreach(var service in _httpsServer.WebSocketServices.Hosts) - { - foreach (var session in service.Sessions.Sessions) - { - - } - } } public void StartServerAndSetPort(int port) @@ -121,10 +121,10 @@ public void StartServerAndSetPort(int port) Debug.Console(0, "Starting Websocket Server on port: {0}", port); - Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword, @"/"); + Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword); } - private void Start(int port, string certPath = "", string certPassword = "", string rootPath = @"/html") + private void Start(int port, string certPath = "", string certPassword = "") { try { @@ -210,9 +210,9 @@ public static class DebugWebsocketSinkExtensions { public static LoggerConfiguration DebugWebsocketSink( this LoggerSinkConfiguration loggerConfiguration, - IFormatProvider formatProvider = null) + ITextFormatter formatProvider = null) { - return loggerConfiguration.Sink(new DebugWebsocketSink()); + return loggerConfiguration.Sink(new DebugWebsocketSink(formatProvider)); } } diff --git a/src/Pepperdash Core/PepperDash_Core.csproj b/src/Pepperdash Core/PepperDash_Core.csproj index db72046..26d13f2 100644 --- a/src/Pepperdash Core/PepperDash_Core.csproj +++ b/src/Pepperdash Core/PepperDash_Core.csproj @@ -36,8 +36,9 @@ Full - - + + + From fac210676cfcb404a3465ea137ed8d252d6729c5 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 15:40:00 -0700 Subject: [PATCH 26/36] fix: switches formatters --- src/Pepperdash Core/Logging/Debug.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 4746755..a0ea176 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -12,6 +12,7 @@ using Serilog.Core; using Serilog.Events; using Serilog.Formatting.Compact; +using Serilog.Formatting.Json; namespace PepperDash.Core { @@ -107,7 +108,7 @@ static Debug() { _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); - _websocketSink = new DebugWebsocketSink(new CompactJsonFormatter()); + _websocketSink = new DebugWebsocketSink(new JsonFormatter()); // Instantiate the root logger _logger = new LoggerConfiguration() @@ -470,12 +471,13 @@ public static void Console(uint level, string format, params object[] items) /// public static void Console(uint level, IKeyed dev, string format, params object[] items) { - var log = _logger.ForContext("Key", dev.Key); + var log = _logger.ForContext("key", dev.Key); + var message = string.Format(format, items); - log.Write((LogEventLevel)level, format, items); + log.Write((LogEventLevel)level, message); if (Level >= level) - Console(level, "[{0}] {1}", dev.Key, string.Format(format, items)); + Console(level, "[{0}] {1}", dev.Key, message); } /// From 6c32f371faaf42eda81113fbb55f7e14ff8622f8 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Mon, 20 Nov 2023 16:27:13 -0700 Subject: [PATCH 27/36] fix: makes logging format consistent --- src/Pepperdash Core/Logging/Debug.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index a0ea176..e8bebe8 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -455,7 +455,9 @@ public static void Console(uint level, string format, params object[] items) return; } - _logger.Write((LogEventLevel)level, format, items); + var message = string.Format(format, items); + + _logger.Write((LogEventLevel)level, message); if (_runningOnAppliance) { @@ -471,7 +473,7 @@ public static void Console(uint level, string format, params object[] items) /// public static void Console(uint level, IKeyed dev, string format, params object[] items) { - var log = _logger.ForContext("key", dev.Key); + var log = _logger.ForContext("Key", dev.Key); var message = string.Format(format, items); log.Write((LogEventLevel)level, message); @@ -495,9 +497,9 @@ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, } var log = _logger.ForContext("Key", dev.Key); + var message = string.Format(format, items); - - log.Write((LogEventLevel)level, format, items); + log.Write((LogEventLevel)level, message); if (Level >= level) { From 15024b57f2a5d1c73cb9a0069117a711241a4b2e Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Nov 2023 14:03:01 -0700 Subject: [PATCH 28/36] feat: adds console sink for Serilog --- src/Pepperdash Core/Logging/Debug.cs | 78 +++++++++++-------- .../Logging/DebugConsoleSink.cs | 50 ++++++++++++ .../Logging/DebugWebsocketSink.cs | 31 -------- 3 files changed, 97 insertions(+), 62 deletions(-) create mode 100644 src/Pepperdash Core/Logging/DebugConsoleSink.cs diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index e8bebe8..dbd8e68 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -85,7 +85,7 @@ public static DebugWebsocketSink WebsocketSink private const int SaveTimeoutMs = 30000; - private static bool _runningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; + public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance; /// /// Version for the currently loaded PepperDashCore dll @@ -108,12 +108,13 @@ static Debug() { _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); - _websocketSink = new DebugWebsocketSink(new JsonFormatter()); + _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); // Instantiate the root logger _logger = new LoggerConfiguration() //.WriteTo.Logger(lc => lc //.WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) + .WriteTo.Sink(new DebugConsoleSink(new JsonFormatter(renderMessage: true)), levelSwitch: _consoleLoggingLevelSwitch) .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.File(@"\user\debug\global-log-{Date}.txt" , rollingInterval: RollingInterval.Day @@ -438,6 +439,18 @@ public static void ShowDebugLog(string s) } + private static void LogMessage(uint level, string message) + { + _logger.Write((LogEventLevel)level, message); + } + + private static void LogMessage(uint level, string message, IKeyed keyed) + { + var log = _logger.ForContext("Key", keyed.Key); + log.Write((LogEventLevel)level, message); + } + + /// /// Prints message to console if current debug level is equal to or higher than the level of this message. /// Uses CrestronConsole.PrintLine. @@ -459,13 +472,13 @@ public static void Console(uint level, string format, params object[] items) _logger.Write((LogEventLevel)level, message); - if (_runningOnAppliance) - { - CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), - InitialParametersClass.ApplicationNumber, - level, - string.Format(format, items)); - } + //if (IsRunningOnAppliance) + //{ + // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), + // InitialParametersClass.ApplicationNumber, + // level, + // string.Format(format, items)); + //} } /// @@ -473,13 +486,10 @@ public static void Console(uint level, string format, params object[] items) /// public static void Console(uint level, IKeyed dev, string format, params object[] items) { - var log = _logger.ForContext("Key", dev.Key); - var message = string.Format(format, items); + LogMessage(level, string.Format(format, items), dev); - log.Write((LogEventLevel)level, message); - - if (Level >= level) - Console(level, "[{0}] {1}", dev.Key, message); + //if (Level >= level) + // Console(level, "[{0}] {1}", dev.Key, message); } /// @@ -496,15 +506,17 @@ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, LogError(errorLogLevel, str); } - var log = _logger.ForContext("Key", dev.Key); - var message = string.Format(format, items); + LogMessage(level, str, dev); - log.Write((LogEventLevel)level, message); + //var log = _logger.ForContext("Key", dev.Key); + //var message = string.Format(format, items); - if (Level >= level) - { - Console(level, str); - } + //log.Write((LogEventLevel)level, message); + + //if (Level >= level) + //{ + // Console(level, str); + //} } /// @@ -518,10 +530,12 @@ public static void Console(uint level, ErrorLogLevel errorLogLevel, { LogError(errorLogLevel, str); } - if (Level >= level) - { - Console(level, str); - } + + LogMessage(level, str); + //if (Level >= level) + //{ + // Console(level, str); + //} } /// @@ -532,8 +546,9 @@ public static void Console(uint level, ErrorLogLevel errorLogLevel, public static void ConsoleWithLog(uint level, string format, params object[] items) { var str = string.Format(format, items); - if (Level >= level) - CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); + //if (Level >= level) + // CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); + LogMessage(level, str); CrestronLogger.WriteToLog(str, level); } @@ -545,8 +560,9 @@ public static void ConsoleWithLog(uint level, string format, params object[] ite public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items) { var str = string.Format(format, items); - if (Level >= level) - ConsoleWithLog(level, "[{0}] {1}", dev.Key, str); + + LogMessage(level, str, dev); + CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level); } /// @@ -557,7 +573,7 @@ public static void ConsoleWithLog(uint level, IKeyed dev, string format, params public static void LogError(ErrorLogLevel errorLogLevel, string str) { - var msg = _runningOnAppliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str); + var msg = IsRunningOnAppliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str); switch (errorLogLevel) { case ErrorLogLevel.Error: diff --git a/src/Pepperdash Core/Logging/DebugConsoleSink.cs b/src/Pepperdash Core/Logging/DebugConsoleSink.cs new file mode 100644 index 0000000..9af051c --- /dev/null +++ b/src/Pepperdash Core/Logging/DebugConsoleSink.cs @@ -0,0 +1,50 @@ +using Crestron.SimplSharp; +using Serilog.Configuration; +using Serilog; +using Serilog.Core; +using Serilog.Events; +using Serilog.Formatting; +using Serilog.Formatting.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace PepperDash.Core +{ + internal class DebugConsoleSink : ILogEventSink + { + private readonly ITextFormatter _textFormatter; + + public void Emit(LogEvent logEvent) + { + if (!Debug.IsRunningOnAppliance) return; + + CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", logEvent.Timestamp, + InitialParametersClass.ApplicationNumber, + logEvent.Level, + logEvent.RenderMessage()); + } + + public DebugConsoleSink(ITextFormatter formatProvider) + { + + _textFormatter = formatProvider ?? new JsonFormatter(); + + } + + } + + public static class DebugConsoleSinkExtensions + { + public static LoggerConfiguration DebugConsoleSink( + this LoggerSinkConfiguration loggerConfiguration, + ITextFormatter formatProvider = null) + { + return loggerConfiguration.Sink(new DebugConsoleSink(formatProvider)); + } + } + +} diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 3cb0d49..3dd2606 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -157,37 +157,6 @@ private void Start(int port, string certPath = "", string certPassword = "") }; Debug.Console(0, "Starting"); - //_httpsServer.OnGet += (sender, e) => - //{ - // Debug.Console(0, $"OnGet requesting {e.Request}"); - // var req = e.Request; - // var res = e.Response; - - // var path = req.RawUrl; - - // if (path == "/") - // path += "index.html"; - - // var localPath = Path.Combine(rootPath, path.Substring(1)); - - // byte[] contents; - // if (File.Exists(localPath)) - // contents = File.ReadAllBytes(localPath); - // else - // { - // e.Response.StatusCode = 404; - // contents = Encoding.UTF8.GetBytes("Path not found " + e.Request.RawUrl); - // } - - // var extention = Path.GetExtension(path); - // if (!_contentTypes.TryGetValue(extention, out var contentType)) - // contentType = "text/html"; - - // res.ContentLength64 = contents.LongLength; - - // res.Close(contents, true); - //}; - _httpsServer.Start(); Debug.Console(0, "Ready"); } From 3debb30d53b9b9f4f5787eeb1aba755dfbd06eac Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Nov 2023 16:44:52 -0700 Subject: [PATCH 29/36] fix: consolidates log messages --- src/Pepperdash Core/Logging/Debug.cs | 80 ++++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index dbd8e68..5a2d15c 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -79,7 +79,7 @@ public static DebugWebsocketSink WebsocketSink /// /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal /// - public static bool DoNotLoadOnNextBoot { get; private set; } + public static bool DoNotLoadConfigOnNextBoot { get; private set; } private static DebugContextCollection _contexts; @@ -107,6 +107,10 @@ public static DebugWebsocketSink WebsocketSink static Debug() { _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Information); + _consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) => + { + Debug.Console(0, "Console debug level set to {0}", _consoleLoggingLevelSwitch.MinimumLevel); + }; _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); @@ -167,9 +171,9 @@ static Debug() var context = _contexts.GetOrCreateItem("DEFAULT"); Level = context.Level; - DoNotLoadOnNextBoot = context.DoNotLoadOnNextBoot; + DoNotLoadConfigOnNextBoot = context.DoNotLoadOnNextBoot; - if(DoNotLoadOnNextBoot) + if(DoNotLoadConfigOnNextBoot) CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber)); try @@ -244,7 +248,7 @@ public static void SetDebugFromConsole(string levelString) { if (string.IsNullOrEmpty(levelString.Trim())) { - CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", Level); + CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch); return; } @@ -252,7 +256,7 @@ public static void SetDebugFromConsole(string levelString) } catch { - CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-2]"); + CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]"); } } @@ -276,11 +280,11 @@ public static void SetDoNotLoadOnNextBootFromConsole(string stateString) { if (string.IsNullOrEmpty(stateString.Trim())) { - CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadOnNextBoot); + CrestronConsole.ConsoleCommandResponse("DoNotLoadOnNextBoot = {0}", DoNotLoadConfigOnNextBoot); return; } - SetDoNotLoadOnNextBoot(Boolean.Parse(stateString)); + SetDoNotLoadConfigOnNextBoot(Boolean.Parse(stateString)); } catch { @@ -376,20 +380,16 @@ public static void SetDebugLevel(int level) { _consoleLoggingLevelSwitch.MinimumLevel = (LogEventLevel)level; - if (level <= 5) - { - Level = level; - - _contexts.GetOrCreateItem("DEFAULT").Level = level; - SaveMemoryOnTimeout(); + //if (level <= 5) + //{ - CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}", - InitialParametersClass.ApplicationNumber, Level); + // CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}", + // InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); - //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level); - //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); - } + // //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level); + // //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + // // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); + //} } /// @@ -418,14 +418,14 @@ public static object GetDeviceDebugSettingsForKey(string deviceKey) /// Sets the flag to prevent application starting on next boot /// /// - public static void SetDoNotLoadOnNextBoot(bool state) + public static void SetDoNotLoadConfigOnNextBoot(bool state) { - DoNotLoadOnNextBoot = state; + DoNotLoadConfigOnNextBoot = state; _contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state; SaveMemoryOnTimeout(); - CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Start on Next Boot set to {1}", - InitialParametersClass.ApplicationNumber, DoNotLoadOnNextBoot); + CrestronConsole.ConsoleCommandResponse("[Application {0}], Do Not Load Config on Next Boot set to {1}", + InitialParametersClass.ApplicationNumber, DoNotLoadConfigOnNextBoot); } /// @@ -439,15 +439,15 @@ public static void ShowDebugLog(string s) } - private static void LogMessage(uint level, string message) + private static void LogMessage(uint level, string format, params object[] items) { - _logger.Write((LogEventLevel)level, message); + _logger.Write((LogEventLevel)level, format, items); } - private static void LogMessage(uint level, string message, IKeyed keyed) + private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items) { var log = _logger.ForContext("Key", keyed.Key); - log.Write((LogEventLevel)level, message); + log.Write((LogEventLevel)level, format, items); } @@ -460,6 +460,9 @@ private static void LogMessage(uint level, string message, IKeyed keyed) /// Object parameters public static void Console(uint level, string format, params object[] items) { + + LogMessage(level, format, items); + if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) { var logString = string.Format("[level {0}] {1}", level, string.Format(format, items)); @@ -468,10 +471,6 @@ public static void Console(uint level, string format, params object[] items) return; } - var message = string.Format(format, items); - - _logger.Write((LogEventLevel)level, message); - //if (IsRunningOnAppliance) //{ // CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", DateTime.Now.ToString("HH:mm:ss.fff"), @@ -486,7 +485,7 @@ public static void Console(uint level, string format, params object[] items) /// public static void Console(uint level, IKeyed dev, string format, params object[] items) { - LogMessage(level, string.Format(format, items), dev); + LogMessage(level, dev, format, items); //if (Level >= level) // Console(level, "[{0}] {1}", dev.Key, message); @@ -506,7 +505,7 @@ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, LogError(errorLogLevel, str); } - LogMessage(level, str, dev); + LogMessage(level, dev, format, items); //var log = _logger.ForContext("Key", dev.Key); //var message = string.Format(format, items); @@ -519,9 +518,9 @@ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, //} } - /// - /// Logs to Console when at-level, and all messages to error log - /// + /// + /// Logs to Console when at-level, and all messages to error log + /// public static void Console(uint level, ErrorLogLevel errorLogLevel, string format, params object[] items) { @@ -531,7 +530,7 @@ public static void Console(uint level, ErrorLogLevel errorLogLevel, LogError(errorLogLevel, str); } - LogMessage(level, str); + LogMessage(level, format, items); //if (Level >= level) //{ // Console(level, str); @@ -545,10 +544,11 @@ public static void Console(uint level, ErrorLogLevel errorLogLevel, /// public static void ConsoleWithLog(uint level, string format, params object[] items) { + LogMessage(level, format, items); + var str = string.Format(format, items); //if (Level >= level) // CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); - LogMessage(level, str); CrestronLogger.WriteToLog(str, level); } @@ -559,9 +559,9 @@ public static void ConsoleWithLog(uint level, string format, params object[] ite /// public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items) { - var str = string.Format(format, items); + LogMessage(level, dev, format, items); - LogMessage(level, str, dev); + var str = string.Format(format, items); CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level); } From 65800193bb420b346dcad95467c793b5f81b0768 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 22 Nov 2023 17:31:36 -0700 Subject: [PATCH 30/36] feat: adds methods to get/set websocket min log level --- src/Pepperdash Core/Logging/Debug.cs | 10 ++++++++-- src/Pepperdash Core/Logging/DebugConsoleSink.cs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 5a2d15c..ed5a2ed 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -11,7 +11,6 @@ using Serilog; using Serilog.Core; using Serilog.Events; -using Serilog.Formatting.Compact; using Serilog.Formatting.Json; namespace PepperDash.Core @@ -47,6 +46,11 @@ public static class Debug private static LoggingLevelSwitch _websocketLoggingLevelSwitch; + public static LogEventLevel WebsocketMinimumLogLevel + { + get { return _websocketLoggingLevelSwitch.MinimumLevel; } + } + private static DebugWebsocketSink _websocketSink; public static DebugWebsocketSink WebsocketSink @@ -265,9 +269,11 @@ public static void SetConsoleDebugLevel(LogEventLevel level) _consoleLoggingLevelSwitch.MinimumLevel = level; } - public static void SetWebSocketDebugLevel(LogEventLevel level) + public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) { + _websocketLoggingLevelSwitch.MinimumLevel = level; + Console(0, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); } /// diff --git a/src/Pepperdash Core/Logging/DebugConsoleSink.cs b/src/Pepperdash Core/Logging/DebugConsoleSink.cs index 9af051c..e991429 100644 --- a/src/Pepperdash Core/Logging/DebugConsoleSink.cs +++ b/src/Pepperdash Core/Logging/DebugConsoleSink.cs @@ -22,7 +22,7 @@ public void Emit(LogEvent logEvent) { if (!Debug.IsRunningOnAppliance) return; - CrestronConsole.PrintLine("[{0}]App {1} Lvl {2}:{3}", logEvent.Timestamp, + CrestronConsole.PrintLine("[{0}][App {1}][Lvl {2}]: {3}", logEvent.Timestamp, InitialParametersClass.ApplicationNumber, logEvent.Level, logEvent.RenderMessage()); From 5b2453c2cc2140dbe1be376fdb871b48419f4e0c Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 23 Nov 2023 11:28:25 -0700 Subject: [PATCH 31/36] feat: improments to setting debug level from console --- src/Pepperdash Core/Config/PortalConfigReader.cs | 2 +- src/Pepperdash Core/Logging/Debug.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Pepperdash Core/Config/PortalConfigReader.cs b/src/Pepperdash Core/Config/PortalConfigReader.cs index fdc9040..9ae9a0d 100644 --- a/src/Pepperdash Core/Config/PortalConfigReader.cs +++ b/src/Pepperdash Core/Config/PortalConfigReader.cs @@ -123,7 +123,7 @@ public static JObject MergeConfigs(JObject doubleConfig) else merged.Add("global", template["global"]); - Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); + //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); return merged; } diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index ed5a2ed..eeccef5 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -250,9 +250,21 @@ public static void SetDebugFromConsole(string levelString) { try { + if (levelString.Trim() == "?") + { + CrestronConsole.ConsoleCommandResponse( +$@"Used to set the minimum level of debug messages to be printed to the console: +{eDebugLevel.Information.ToString()} = {eDebugLevel.Information} +{eDebugLevel.Warning.ToString()} = {eDebugLevel.Warning} +{eDebugLevel.Error.ToString()} = {eDebugLevel.Error} +{eDebugLevel.Fatal.ToString()} = {eDebugLevel.Fatal} +{eDebugLevel.Debug.ToString()} = {eDebugLevel.Debug} +{eDebugLevel.Verbose.ToString()} = {eDebugLevel.Verbose}"); + } + if (string.IsNullOrEmpty(levelString.Trim())) { - CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch); + CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel); return; } From 1abffd82cabec3f6a8dbcea732fa5415288fca6d Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 23 Nov 2023 12:11:36 -0700 Subject: [PATCH 32/36] fix: reworks how the console debug level is set --- .../JsonToSimpl/JsonToSimplFileMaster.cs | 2 +- .../JsonToSimplPortalFileMaster.cs | 2 +- src/Pepperdash Core/Logging/Debug.cs | 82 ++++++++++--------- 3 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs b/src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs index 521cecc..011c63e 100644 --- a/src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs +++ b/src/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs @@ -197,7 +197,7 @@ public void EvaluateFile(string filepath) /// Sets the debug level /// /// - public void setDebugLevel(int level) + public void setDebugLevel(uint level) { Debug.SetDebugLevel(level); } diff --git a/src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs b/src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs index 8a85b33..fa0bc94 100644 --- a/src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs +++ b/src/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs @@ -134,7 +134,7 @@ FileInfo GetActualFileInfoFromPath(string path) /// /// /// - public void setDebugLevel(int level) + public void setDebugLevel(uint level) { Debug.SetDebugLevel(level); } diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index eeccef5..8341f6e 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -12,6 +12,8 @@ using Serilog.Core; using Serilog.Events; using Serilog.Formatting.Json; +using Crestron.SimplSharp.CrestronDataStore; +using System.Linq; namespace PepperDash.Core { @@ -30,15 +32,15 @@ public enum eDebugLevel /// public static class Debug { - //private static Dictionary> _logActions = new Dictionary>() - //{ - // {0, (s) => _logger.Information(s) }, - // {1, (s) => _logger.Warning(s) }, - // {2, (s) => _logger.Error(s) }, - // {3, (s) => _logger.Fatal(s) }, - // {4, (s) => _logger.Debug(s) }, - // {5, (s) => _logger.Verbose(s) }, - //}; + private static Dictionary _logLevels = new Dictionary() + { + {0, LogEventLevel.Information }, + {1, LogEventLevel.Warning }, + {2, LogEventLevel.Error }, + {3, LogEventLevel.Fatal }, + {4, LogEventLevel.Debug }, + {5, LogEventLevel.Verbose }, + }; private static Logger _logger; @@ -157,7 +159,7 @@ static Debug() "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug", - "appdebug:P [0-2]: Sets the application's console debug message level", + "appdebug:P [0-5]: Sets the application's console debug message level", ConsoleAccessLevelEnum.AccessOperator); CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog", "appdebuglog:P [all] Use \"all\" for full log.", @@ -252,14 +254,15 @@ public static void SetDebugFromConsole(string levelString) { if (levelString.Trim() == "?") { - CrestronConsole.ConsoleCommandResponse( +CrestronConsole.ConsoleCommandResponse( $@"Used to set the minimum level of debug messages to be printed to the console: -{eDebugLevel.Information.ToString()} = {eDebugLevel.Information} -{eDebugLevel.Warning.ToString()} = {eDebugLevel.Warning} -{eDebugLevel.Error.ToString()} = {eDebugLevel.Error} -{eDebugLevel.Fatal.ToString()} = {eDebugLevel.Fatal} -{eDebugLevel.Debug.ToString()} = {eDebugLevel.Debug} -{eDebugLevel.Verbose.ToString()} = {eDebugLevel.Verbose}"); +{eDebugLevel.Information} = 0 +{eDebugLevel.Warning} = 1 +{eDebugLevel.Error} = 2 +{eDebugLevel.Fatal} = 3 +{eDebugLevel.Debug} = 4 +{eDebugLevel.Verbose} = 5"); + return; } if (string.IsNullOrEmpty(levelString.Trim())) @@ -268,7 +271,10 @@ public static void SetDebugFromConsole(string levelString) return; } - SetDebugLevel(Convert.ToInt32(levelString)); + var level = Convert.ToUInt32(levelString); + + if (_logLevels.ContainsKey(level)) + SetDebugLevel(level); } catch { @@ -276,15 +282,33 @@ public static void SetDebugFromConsole(string levelString) } } - public static void SetConsoleDebugLevel(LogEventLevel level) + /// + /// Sets the debug level + /// + /// Valid values 0-5 + public static void SetDebugLevel(uint level) { - _consoleLoggingLevelSwitch.MinimumLevel = level; + if (_logLevels.ContainsKey(level)) + _consoleLoggingLevelSwitch.MinimumLevel = _logLevels[level]; + + CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}", + InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); + + var err = CrestronDataStoreStatic.SetLocalUintValue("ConsoleDebugLevel", level); + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); } public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) { _websocketLoggingLevelSwitch.MinimumLevel = level; + var levelInt = _logLevels.FirstOrDefault((l) => l.Value.Equals(level)).Key; + + var err = CrestronDataStoreStatic.SetLocalUintValue("WebsocketDebugLevel", levelInt); + if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + Console(0, "Error saving websocket debug level setting: {0}", err); + Console(0, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel); } @@ -390,25 +414,7 @@ public static void SetDebugFilterFromConsole(string items) } - /// - /// Sets the debug level - /// - /// Valid values 0 (no debug), 1 (critical), 2 (all messages) - public static void SetDebugLevel(int level) - { - _consoleLoggingLevelSwitch.MinimumLevel = (LogEventLevel)level; - //if (level <= 5) - //{ - - // CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}", - // InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel); - - // //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level); - // //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - // // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); - //} - } /// /// sets the settings for a device or creates a new entry From 5931c1fb780cb8118393f7e1b311b86246f6496b Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 23 Nov 2023 12:34:19 -0700 Subject: [PATCH 33/36] fix: fixes setting log level in LogMessage --- src/Pepperdash Core/Logging/Debug.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index 8341f6e..d05763c 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -465,13 +465,20 @@ public static void ShowDebugLog(string s) private static void LogMessage(uint level, string format, params object[] items) { - _logger.Write((LogEventLevel)level, format, items); + if (!_logLevels.ContainsKey(level)) return; + + var logLevel = _logLevels[level]; + _logger.Write(logLevel, format, items); } private static void LogMessage(uint level, IKeyed keyed, string format, params object[] items) { + if (!_logLevels.ContainsKey(level)) return; + + var logLevel = _logLevels[level]; + var log = _logger.ForContext("Key", keyed.Key); - log.Write((LogEventLevel)level, format, items); + log.Write(logLevel, format, items); } From 58fcd91fa27990eb44908bf4157171bcee509bf1 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Thu, 23 Nov 2023 15:33:22 -0700 Subject: [PATCH 34/36] fix: minor adjustments to debug statement levels --- src/Pepperdash Core/Logging/DebugWebsocketSink.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index 3dd2606..fc73597 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -214,7 +214,7 @@ protected override void OnOpen() base.OnOpen(); var url = Context.WebSocket.Url; - Debug.Console(2, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url); _connectionTime = DateTime.Now; } @@ -230,7 +230,7 @@ protected override void OnClose(CloseEventArgs e) { base.OnClose(e); - Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason); + Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason); } From 9001c6c369000fa871fad7773c723e8116baa1b5 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Fri, 24 Nov 2023 11:52:37 -0700 Subject: [PATCH 35/36] feat: sets debug level on output log messages from websocket server --- .../Logging/DebugWebsocketSink.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs index fc73597..f24a585 100644 --- a/src/Pepperdash Core/Logging/DebugWebsocketSink.cs +++ b/src/Pepperdash Core/Logging/DebugWebsocketSink.cs @@ -153,7 +153,34 @@ private void Start(int port, string certPath = "", string certPassword = "") _httpsServer.Log.Level = LogLevel.Trace; _httpsServer.Log.Output = (d, s) => { - Debug.Console(0, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); + 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.Console(level, "{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s); }; Debug.Console(0, "Starting"); From def22cfe916f75e867dab9607d0cb27d420bf758 Mon Sep 17 00:00:00 2001 From: Neil Dorin Date: Wed, 29 Nov 2023 12:14:56 -0700 Subject: [PATCH 36/36] fix: sets overall min log level in logger config --- src/Pepperdash Core/Logging/Debug.cs | 32 +++++++++------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/Pepperdash Core/Logging/Debug.cs b/src/Pepperdash Core/Logging/Debug.cs index d05763c..a33002c 100644 --- a/src/Pepperdash Core/Logging/Debug.cs +++ b/src/Pepperdash Core/Logging/Debug.cs @@ -17,16 +17,6 @@ namespace PepperDash.Core { - public enum eDebugLevel - { - Information = 0, - Warning = 1, - Error = 2, - Fatal = 3, - Debug = 4, - Verbose = 5, - } - /// /// Contains debug commands for use in various situations /// @@ -117,13 +107,12 @@ static Debug() { Debug.Console(0, "Console debug level set to {0}", _consoleLoggingLevelSwitch.MinimumLevel); }; - _websocketLoggingLevelSwitch = new LoggingLevelSwitch(); + _websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: LogEventLevel.Verbose); _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); // Instantiate the root logger _logger = new LoggerConfiguration() - //.WriteTo.Logger(lc => lc - //.WriteTo.Console(levelSwitch: _consoleLoggingLevelSwitch)) + .MinimumLevel.Verbose() .WriteTo.Sink(new DebugConsoleSink(new JsonFormatter(renderMessage: true)), levelSwitch: _consoleLoggingLevelSwitch) .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch) .WriteTo.File(@"\user\debug\global-log-{Date}.txt" @@ -254,14 +243,14 @@ public static void SetDebugFromConsole(string levelString) { if (levelString.Trim() == "?") { -CrestronConsole.ConsoleCommandResponse( -$@"Used to set the minimum level of debug messages to be printed to the console: -{eDebugLevel.Information} = 0 -{eDebugLevel.Warning} = 1 -{eDebugLevel.Error} = 2 -{eDebugLevel.Fatal} = 3 -{eDebugLevel.Debug} = 4 -{eDebugLevel.Verbose} = 5"); + CrestronConsole.ConsoleCommandResponse( + $@"Used to set the minimum level of debug messages to be printed to the console: +{_logLevels[0]} = 0 +{_logLevels[1]} = 1 +{_logLevels[2]} = 2 +{_logLevels[3]} = 3 +{_logLevels[4]} = 4 +{_logLevels[5]} = 5"); return; } @@ -302,7 +291,6 @@ public static void SetDebugLevel(uint level) public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) { _websocketLoggingLevelSwitch.MinimumLevel = level; - var levelInt = _logLevels.FirstOrDefault((l) => l.Value.Equals(level)).Key; var err = CrestronDataStoreStatic.SetLocalUintValue("WebsocketDebugLevel", levelInt);