diff --git a/CLZ Builds/PepperDash_Core.clz b/CLZ Builds/PepperDash_Core.clz index f3a2ec2..0ef4596 100644 Binary files a/CLZ Builds/PepperDash_Core.clz and b/CLZ Builds/PepperDash_Core.clz differ diff --git a/CLZ Builds/PepperDash_Core.dll b/CLZ Builds/PepperDash_Core.dll index 43d7667..9ec42f6 100644 Binary files a/CLZ Builds/PepperDash_Core.dll and b/CLZ Builds/PepperDash_Core.dll differ diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs index 58f46c8..3047a60 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs @@ -669,6 +669,8 @@ namespace PepperDash.Core } if (ClientReadyAfterKeyExchange.Contains(clientIndex)) ClientReadyAfterKeyExchange.Remove(clientIndex); + if (WaitingForSharedKey.Contains(clientIndex)) + WaitingForSharedKey.Remove(clientIndex); } } catch (Exception ex) @@ -781,31 +783,33 @@ namespace PepperDash.Core Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received); mySecureTCPServer.SendData(clientIndex, b, b.Length); mySecureTCPServer.Disconnect(clientIndex); - WaitingForSharedKey.Remove(clientIndex); + return; } - if (mySecureTCPServer.NumberOfClientsConnected > 0) - mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback); + WaitingForSharedKey.Remove(clientIndex); byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match"); mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null); OnServerClientReadyForCommunications(clientIndex); Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex); - return; + } - //var address = mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex); - //Debug.Console(1, this, "Secure Server Listening on Port: {0}, client IP: {1}, Client Index: {4}, NumberOfBytesReceived: {2}, Received: {3}\r\n", - // mySecureTCPServer.PortNumber.ToString(), address , numberOfBytesReceived.ToString(), received, clientIndex.ToString()); - if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received))) - onTextReceived(received, clientIndex); + else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received))) + onTextReceived(received, clientIndex); } catch (Exception ex) { Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex); } + if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED) + mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback); } - if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED) - mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback); + else + { + // If numberOfBytesReceived <= 0 + mySecureTCPServer.Disconnect(clientIndex); + } + } #endregion diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs index 3d1101a..6af5d7b 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs @@ -174,8 +174,8 @@ namespace PepperDash.Core if (Client != null) { Debug.Console(1, this, "Program stopping. Closing connection"); - Client.Disconnect(); - Client.Dispose(); + Disconnect(); + //Client.Dispose(); } } } diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs index 2ef2845..7dfb682 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs @@ -387,13 +387,16 @@ namespace PepperDash.Core myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5); // myTcpServer.HandshakeTimeout = 30; - myTcpServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(TcpServer_SocketStatusChange); } else { KillServer(); myTcpServer.PortNumber = Port; } + + myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange; + myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange; + ServerStopped = false; myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback); OnServerStateChange(myTcpServer.State); @@ -670,6 +673,8 @@ namespace PepperDash.Core } if (ClientReadyAfterKeyExchange.Contains(clientIndex)) ClientReadyAfterKeyExchange.Remove(clientIndex); + if (WaitingForSharedKey.Contains(clientIndex)) + WaitingForSharedKey.Remove(clientIndex); } } catch (Exception ex) @@ -763,50 +768,51 @@ namespace PepperDash.Core /// /// /// - void TcpServerReceivedDataAsyncCallback(TCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived) + void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived) { - if (numberOfBytesReceived > 0) - { - string received = "Nothing"; - try - { - byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex); - received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived); - if (WaitingForSharedKey.Contains(clientIndex)) - { - received = received.Replace("\r", ""); - received = received.Replace("\n", ""); - if (received != SharedKey) - { - byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting"); - Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received); - mySecureTCPServer.SendData(clientIndex, b, b.Length); - mySecureTCPServer.Disconnect(clientIndex); - WaitingForSharedKey.Remove(clientIndex); - return; - } - if (mySecureTCPServer.NumberOfClientsConnected > 0) - mySecureTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback); - WaitingForSharedKey.Remove(clientIndex); - byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match"); - mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null); - OnServerClientReadyForCommunications(clientIndex); - Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex); - return; - } - //var address = mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex); - //Debug.Console(1, this, "Secure Server Listening on Port: {0}, client IP: {1}, Client Index: {4}, NumberOfBytesReceived: {2}, Received: {3}\r\n", - // mySecureTCPServer.PortNumber.ToString(), address , numberOfBytesReceived.ToString(), received, clientIndex.ToString()); - if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received))) - onTextReceived(received, clientIndex); - } - catch (Exception ex) - { - Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex); - } - } - if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED) - mySecureTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback); + if (numberOfBytesReceived > 0) + { + string received = "Nothing"; + try + { + byte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex); + received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived); + if (WaitingForSharedKey.Contains(clientIndex)) + { + received = received.Replace("\r", ""); + received = received.Replace("\n", ""); + if (received != SharedKey) + { + byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting"); + Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received); + myTCPServer.SendData(clientIndex, b, b.Length); + myTCPServer.Disconnect(clientIndex); + return; + } + + WaitingForSharedKey.Remove(clientIndex); + byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match"); + myTCPServer.SendDataAsync(clientIndex, success, success.Length, null); + OnServerClientReadyForCommunications(clientIndex); + Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex); + } + + else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received))) + onTextReceived(received, clientIndex); + } + catch (Exception ex) + { + Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex); + } + if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED) + myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback); + } + else + { + // If numberOfBytesReceived <= 0 + myTCPServer.Disconnect(); + } + } #endregion diff --git a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs b/Pepperdash Core/Pepperdash Core/Logging/Debug.cs index cf92ff0..21f0121 100644 --- a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs +++ b/Pepperdash Core/Pepperdash Core/Logging/Debug.cs @@ -1,419 +1,421 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using Crestron.SimplSharp; -using Crestron.SimplSharp.Reflection; -using Crestron.SimplSharp.CrestronLogger; -using Crestron.SimplSharp.CrestronIO; -using Newtonsoft.Json; -using PepperDash.Core.DebugThings; - - -namespace PepperDash.Core -{ - public static class Debug - { - /// - /// 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. - /// - public static string FilePathPrefix = @"\nvram\debug\"; - - /// - /// The name of the file containing the current debug settings. - /// - public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber); - - public static int Level { get; private set; } - - static DebugContextCollection Contexts; - - static int SaveTimeoutMs = 30000; - - static CTimer SaveTimer; - - /// - /// When true, the IncludedExcludedKeys dict will contain keys to include. - /// When false (default), IncludedExcludedKeys will contain keys to exclude. - /// - static bool ExcludeAllMode; - - //static bool ExcludeNoKeyMessages; - - static Dictionary IncludedExcludedKeys; - - static Debug() - { - // Get the assembly version and print it to console and the log - var version = Assembly.GetExecutingAssembly().GetName().Version; - - var versionString = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); - - var msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, versionString); - - CrestronConsole.PrintLine(msg); - - LogError(ErrorLogLevel.Notice, msg); - - IncludedExcludedKeys = new Dictionary(); - - //CrestronDataStoreStatic.InitCrestronDataStore(); - if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro) - { - // Add command to console - CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug", - "appdebug:P [0-2]: Sets the application's console debug message level", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog", - "appdebuglog:P [all] Use \"all\" for full log.", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear", - "appdebugclear:P Clears the current custom log", - ConsoleAccessLevelEnum.AccessOperator); - CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter", - "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator); - } - - CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; - - LoadMemory(); - Level = Contexts.GetOrCreateItem("DEFAULT").Level; - - try - { - if (InitialParametersClass.NumberOfRemovableDrives > 0) - { - CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives); - CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages. - } - else - CrestronConsole.PrintLine("No RM Drive(s) Present."); - } - catch (Exception e) - { - - CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e); - } - } - - /// - /// Used to save memory when shutting down - /// - /// - static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType == eProgramStatusEventType.Stopping) - { - if (SaveTimer != null) - { - SaveTimer.Stop(); - SaveTimer = null; - } - Console(0, "Saving debug settings"); - SaveMemory(); - } - } - - /// - /// Callback for console command - /// - /// - public static void SetDebugFromConsole(string levelString) - { - try - { - if (string.IsNullOrEmpty(levelString.Trim())) - { - CrestronConsole.PrintLine("AppDebug level = {0}", Level); - return; - } - - SetDebugLevel(Convert.ToInt32(levelString)); - } - catch - { - CrestronConsole.PrintLine("Usage: appdebug:P [0-2]"); - } - } - - public static void SetDebugFilterFromConsole(string items) - { - var str = items.Trim(); - if (str == "?") - { - 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 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" + - "(nokey settings are independent of all other settings)"); - return; - } - var keys = Regex.Split(str, @"\s*"); - foreach (var keyToken in keys) - { - var lkey = keyToken.ToLower(); - if (lkey == "+all") - { - IncludedExcludedKeys.Clear(); - ExcludeAllMode = false; - } - else if (lkey == "-all") - { - IncludedExcludedKeys.Clear(); - ExcludeAllMode = true; - } - //else if (lkey == "+nokey") - //{ - // ExcludeNoKeyMessages = false; - //} - //else if (lkey == "-nokey") - //{ - // ExcludeNoKeyMessages = true; - //} - else - { - string key = null; ; - if (lkey.StartsWith("-")) - { - key = lkey.Substring(1); - // if in exclude all mode, we need to remove this from the inclusions - if (ExcludeAllMode) - { - if (IncludedExcludedKeys.ContainsKey(key)) - IncludedExcludedKeys.Remove(key); - } - // otherwise include all mode, add to the exclusions - else - { - IncludedExcludedKeys[key] = new object(); - } - } - else if (lkey.StartsWith("+")) - { - key = lkey.Substring(1); - // if in exclude all mode, we need to add this as inclusion - if (ExcludeAllMode) - { - - IncludedExcludedKeys[key] = new object(); - } - // otherwise include all mode, remove this from exclusions - else - { - if (IncludedExcludedKeys.ContainsKey(key)) - IncludedExcludedKeys.Remove(key); - } - } - } - } - } - - - /// - /// Sets the debug level - /// - /// Valid values 0 (no debug), 1 (critical), 2 (all messages) - public static void SetDebugLevel(int level) - { - if (level <= 2) - { - Level = level; - Contexts.GetOrCreateItem("DEFAULT").Level = level; - SaveMemoryOnTimeout(); - - CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}", - InitialParametersClass.ApplicationNumber, Level); - - //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level); - //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) - // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); - } - } - - /// - /// - /// - public static void ShowDebugLog(string s) - { - var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all"); - foreach (var l in loglist) - CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine); - } - - /// - /// Prints message to console if current debug level is equal to or higher than the level of this message. - /// Uses CrestronConsole.PrintLine. - /// - /// - /// Console format string - /// Object parameters - public static void Console(uint level, string format, params object[] items) - { - if (Level >= level) - CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber, - string.Format(format, items)); - } - - /// - /// Logs to Console when at-level, and all messages to error log, including device key - /// - 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)); - } - - public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, - string format, params object[] items) - { - var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items)); - LogError(errorLogLevel, str); - if (Level >= level) - { - Console(level, str); - } - } - - /// - /// 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) - { - var str = string.Format(format, items); - LogError(errorLogLevel, str); - if (Level >= level) - { - Console(level, str); - } - } - - /// - /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at - /// or above the level provided, then the output will be written to both console and the log. Otherwise - /// it will only be written to the log. - /// - /// - /// - /// - 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); - CrestronLogger.WriteToLog(str, level); - } - - /// - /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at - /// or above the level provided, then the output will be written to both console and the log. Otherwise - /// it will only be written to the log. - /// - /// - /// - /// String.format string - /// Parameters for substitution in the format string. - 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); - } - - /// - /// Prints to log and error log - /// - /// - /// - public static void LogError(ErrorLogLevel errorLogLevel, string str) - { - string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); - switch (errorLogLevel) - { - case ErrorLogLevel.Error: - ErrorLog.Error(msg); - break; - case ErrorLogLevel.Warning: - ErrorLog.Warn(msg); - break; - case ErrorLogLevel.Notice: - ErrorLog.Notice(msg); - break; - } - } - - /// - /// Writes the memory object after timeout - /// - static void SaveMemoryOnTimeout() - { - if (SaveTimer == null) - SaveTimer = new CTimer(o => - { - SaveTimer = null; - SaveMemory(); - }, SaveTimeoutMs); - else - SaveTimer.Reset(SaveTimeoutMs); - } - - /// - /// Writes the memory - use SaveMemoryOnTimeout - /// - static void SaveMemory() - { - //var dir = @"\NVRAM\debug"; - //if (!Directory.Exists(dir)) - // Directory.Create(dir); - - using (StreamWriter sw = new StreamWriter(GetMemoryFileName())) - { - var json = JsonConvert.SerializeObject(Contexts); - sw.Write(json); - sw.Flush(); - } - } - - /// - /// - /// - static void LoadMemory() - { - var file = GetMemoryFileName(); - if (File.Exists(file)) - { - using (StreamReader sr = new StreamReader(file)) - { - var json = sr.ReadToEnd(); - Contexts = JsonConvert.DeserializeObject(json); - - if (Contexts != null) - { - Debug.Console(1, "Debug memory restored from file"); - return; - } - } - } - - Contexts = new DebugContextCollection(); - } - - /// - /// Helper to get the file path for this app's debug memory - /// - static string GetMemoryFileName() - { - return string.Format(@"\NVRAM\debugSettings\program{0}", InitialParametersClass.ApplicationNumber); - } - - public enum ErrorLogLevel - { - Error, Warning, Notice, None - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Crestron.SimplSharp; +using Crestron.SimplSharp.Reflection; +using Crestron.SimplSharp.CrestronLogger; +using Crestron.SimplSharp.CrestronIO; +using Newtonsoft.Json; +using PepperDash.Core.DebugThings; + + +namespace PepperDash.Core +{ + public static class Debug + { + /// + /// 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. + /// + public static string FilePathPrefix = @"\nvram\debug\"; + + /// + /// The name of the file containing the current debug settings. + /// + public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber); + + public static int Level { get; private set; } + + static DebugContextCollection Contexts; + + static int SaveTimeoutMs = 30000; + + public static string PepperDashCoreVersion { get; private set; } + + static CTimer SaveTimer; + + /// + /// When true, the IncludedExcludedKeys dict will contain keys to include. + /// When false (default), IncludedExcludedKeys will contain keys to exclude. + /// + static bool ExcludeAllMode; + + //static bool ExcludeNoKeyMessages; + + static Dictionary IncludedExcludedKeys; + + static Debug() + { + // Get the assembly version and print it to console and the log + var version = Assembly.GetExecutingAssembly().GetName().Version; + + PepperDashCoreVersion = string.Format("{0}.{1}.{2}", version.Major, version.Minor, version.Build); + + var msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, PepperDashCoreVersion); + + CrestronConsole.PrintLine(msg); + + LogError(ErrorLogLevel.Notice, msg); + + IncludedExcludedKeys = new Dictionary(); + + //CrestronDataStoreStatic.InitCrestronDataStore(); + if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro) + { + // Add command to console + CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug", + "appdebug:P [0-2]: Sets the application's console debug message level", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog", + "appdebuglog:P [all] Use \"all\" for full log.", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear", + "appdebugclear:P Clears the current custom log", + ConsoleAccessLevelEnum.AccessOperator); + CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter", + "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator); + } + + CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; + + LoadMemory(); + Level = Contexts.GetOrCreateItem("DEFAULT").Level; + + try + { + if (InitialParametersClass.NumberOfRemovableDrives > 0) + { + CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives); + CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages. + } + else + CrestronConsole.PrintLine("No RM Drive(s) Present."); + } + catch (Exception e) + { + + CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e); + } + } + + /// + /// Used to save memory when shutting down + /// + /// + static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType == eProgramStatusEventType.Stopping) + { + if (SaveTimer != null) + { + SaveTimer.Stop(); + SaveTimer = null; + } + Console(0, "Saving debug settings"); + SaveMemory(); + } + } + + /// + /// Callback for console command + /// + /// + public static void SetDebugFromConsole(string levelString) + { + try + { + if (string.IsNullOrEmpty(levelString.Trim())) + { + CrestronConsole.PrintLine("AppDebug level = {0}", Level); + return; + } + + SetDebugLevel(Convert.ToInt32(levelString)); + } + catch + { + CrestronConsole.PrintLine("Usage: appdebug:P [0-2]"); + } + } + + public static void SetDebugFilterFromConsole(string items) + { + var str = items.Trim(); + if (str == "?") + { + 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 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" + + "(nokey settings are independent of all other settings)"); + return; + } + var keys = Regex.Split(str, @"\s*"); + foreach (var keyToken in keys) + { + var lkey = keyToken.ToLower(); + if (lkey == "+all") + { + IncludedExcludedKeys.Clear(); + ExcludeAllMode = false; + } + else if (lkey == "-all") + { + IncludedExcludedKeys.Clear(); + ExcludeAllMode = true; + } + //else if (lkey == "+nokey") + //{ + // ExcludeNoKeyMessages = false; + //} + //else if (lkey == "-nokey") + //{ + // ExcludeNoKeyMessages = true; + //} + else + { + string key = null; ; + if (lkey.StartsWith("-")) + { + key = lkey.Substring(1); + // if in exclude all mode, we need to remove this from the inclusions + if (ExcludeAllMode) + { + if (IncludedExcludedKeys.ContainsKey(key)) + IncludedExcludedKeys.Remove(key); + } + // otherwise include all mode, add to the exclusions + else + { + IncludedExcludedKeys[key] = new object(); + } + } + else if (lkey.StartsWith("+")) + { + key = lkey.Substring(1); + // if in exclude all mode, we need to add this as inclusion + if (ExcludeAllMode) + { + + IncludedExcludedKeys[key] = new object(); + } + // otherwise include all mode, remove this from exclusions + else + { + if (IncludedExcludedKeys.ContainsKey(key)) + IncludedExcludedKeys.Remove(key); + } + } + } + } + } + + + /// + /// Sets the debug level + /// + /// Valid values 0 (no debug), 1 (critical), 2 (all messages) + public static void SetDebugLevel(int level) + { + if (level <= 2) + { + Level = level; + Contexts.GetOrCreateItem("DEFAULT").Level = level; + SaveMemoryOnTimeout(); + + CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}", + InitialParametersClass.ApplicationNumber, Level); + + //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level); + //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) + // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err); + } + } + + /// + /// + /// + public static void ShowDebugLog(string s) + { + var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all"); + foreach (var l in loglist) + CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine); + } + + /// + /// Prints message to console if current debug level is equal to or higher than the level of this message. + /// Uses CrestronConsole.PrintLine. + /// + /// + /// Console format string + /// Object parameters + public static void Console(uint level, string format, params object[] items) + { + if (Level >= level) + CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber, + string.Format(format, items)); + } + + /// + /// Logs to Console when at-level, and all messages to error log, including device key + /// + 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)); + } + + public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel, + string format, params object[] items) + { + var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items)); + LogError(errorLogLevel, str); + if (Level >= level) + { + Console(level, str); + } + } + + /// + /// 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) + { + var str = string.Format(format, items); + LogError(errorLogLevel, str); + if (Level >= level) + { + Console(level, str); + } + } + + /// + /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at + /// or above the level provided, then the output will be written to both console and the log. Otherwise + /// it will only be written to the log. + /// + /// + /// + /// + 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); + CrestronLogger.WriteToLog(str, level); + } + + /// + /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at + /// or above the level provided, then the output will be written to both console and the log. Otherwise + /// it will only be written to the log. + /// + /// + /// + /// String.format string + /// Parameters for substitution in the format string. + 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); + } + + /// + /// Prints to log and error log + /// + /// + /// + public static void LogError(ErrorLogLevel errorLogLevel, string str) + { + string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str); + switch (errorLogLevel) + { + case ErrorLogLevel.Error: + ErrorLog.Error(msg); + break; + case ErrorLogLevel.Warning: + ErrorLog.Warn(msg); + break; + case ErrorLogLevel.Notice: + ErrorLog.Notice(msg); + break; + } + } + + /// + /// Writes the memory object after timeout + /// + static void SaveMemoryOnTimeout() + { + if (SaveTimer == null) + SaveTimer = new CTimer(o => + { + SaveTimer = null; + SaveMemory(); + }, SaveTimeoutMs); + else + SaveTimer.Reset(SaveTimeoutMs); + } + + /// + /// Writes the memory - use SaveMemoryOnTimeout + /// + static void SaveMemory() + { + //var dir = @"\NVRAM\debug"; + //if (!Directory.Exists(dir)) + // Directory.Create(dir); + + using (StreamWriter sw = new StreamWriter(GetMemoryFileName())) + { + var json = JsonConvert.SerializeObject(Contexts); + sw.Write(json); + sw.Flush(); + } + } + + /// + /// + /// + static void LoadMemory() + { + var file = GetMemoryFileName(); + if (File.Exists(file)) + { + using (StreamReader sr = new StreamReader(file)) + { + var json = sr.ReadToEnd(); + Contexts = JsonConvert.DeserializeObject(json); + + if (Contexts != null) + { + Debug.Console(1, "Debug memory restored from file"); + return; + } + } + } + + Contexts = new DebugContextCollection(); + } + + /// + /// Helper to get the file path for this app's debug memory + /// + static string GetMemoryFileName() + { + return string.Format(@"\NVRAM\debugSettings\program{0}", InitialParametersClass.ApplicationNumber); + } + + public enum ErrorLogLevel + { + Error, Warning, Notice, None + } + } } \ No newline at end of file