mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-04-12 12:06:58 +00:00
Refator: Refactor timer implementation across multiple classes to use System.Timers.Timer instead of CTimer for improved consistency and performance.
- Updated RelayControlledShade to utilize Timer for output pulsing. - Refactored MockVC to replace CTimer with Timer for call status simulation. - Modified VideoCodecBase to enhance documentation and improve feedback handling. - Removed obsolete IHasCamerasMessenger and updated related classes to use IHasCamerasWithControls. - Adjusted PressAndHoldHandler to implement Timer for button hold actions. - Enhanced logging throughout MobileControl and RoomBridges for better debugging and information tracking. - Cleaned up unnecessary comments and improved exception handling in various classes.
This commit is contained in:
parent
b4d53dbe0e
commit
7076eafc21
56 changed files with 1343 additions and 2197 deletions
|
|
@ -3,8 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -183,7 +182,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
}
|
||||
|
||||
// private Timer for auto reconnect
|
||||
private CTimer RetryTimer;
|
||||
private Timer RetryTimer;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -266,12 +265,12 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
/// </summary>
|
||||
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
|
||||
|
||||
CTimer HeartbeatSendTimer;
|
||||
CTimer HeartbeatAckTimer;
|
||||
Timer HeartbeatSendTimer;
|
||||
Timer HeartbeatAckTimer;
|
||||
|
||||
// Used to force disconnection on a dead connect attempt
|
||||
CTimer ConnectFailTimer;
|
||||
CTimer WaitForSharedKey;
|
||||
Timer ConnectFailTimer;
|
||||
Timer WaitForSharedKey;
|
||||
private int ConnectionCount;
|
||||
|
||||
bool ProgramIsStopping;
|
||||
|
|
@ -493,7 +492,8 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
|
||||
//var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||
|
||||
ConnectFailTimer = new CTimer(o =>
|
||||
ConnectFailTimer = new Timer(30000) { AutoReset = false };
|
||||
ConnectFailTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
|
||||
if (IsTryingToConnect)
|
||||
|
|
@ -507,7 +507,8 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
//SecureClient.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
}
|
||||
}, 30000);
|
||||
};
|
||||
ConnectFailTimer.Start();
|
||||
|
||||
this.LogVerbose("Making Connection Count:{0}", ConnectionCount);
|
||||
_client.ConnectToServerAsync(o =>
|
||||
|
|
@ -528,16 +529,16 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
if (SharedKeyRequired)
|
||||
{
|
||||
WaitingForSharedKeyResponse = true;
|
||||
WaitForSharedKey = new CTimer(timer =>
|
||||
WaitForSharedKey = new Timer(15000) { AutoReset = false };
|
||||
WaitForSharedKey.Elapsed += (s, e) =>
|
||||
{
|
||||
|
||||
this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
|
||||
// Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
|
||||
// This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
|
||||
o.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
//OnClientReadyForcommunications(false); // Should send false event
|
||||
}, 15000);
|
||||
};
|
||||
WaitForSharedKey.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -637,7 +638,9 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
}
|
||||
if (AutoReconnectTriggered != null)
|
||||
AutoReconnectTriggered(this, new EventArgs());
|
||||
RetryTimer = new CTimer(o => Connect(), rndTime);
|
||||
RetryTimer = new Timer(rndTime) { AutoReset = false };
|
||||
RetryTimer.Elapsed += (s, e) => Connect();
|
||||
RetryTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -698,11 +701,11 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
//Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
|
||||
if (handler != null)
|
||||
{
|
||||
if (Monitor.TryEnter(_dequeueLock))
|
||||
Task.Run(() => DequeueEvent());
|
||||
if (System.Threading.Monitor.TryEnter(_dequeueLock))
|
||||
System.Threading.Tasks.Task.Run(() => DequeueEvent());
|
||||
}
|
||||
}
|
||||
else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
|
||||
}
|
||||
else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
|
||||
{
|
||||
client.DisconnectFromServer();
|
||||
}
|
||||
|
|
@ -732,7 +735,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
this.LogError(e, "DequeueEvent error: {0}", e.Message);
|
||||
}
|
||||
// Make sure to release the lock in case an exception above stops this thread, or we won't be able to restart it.
|
||||
Monitor.Exit(_dequeueLock);
|
||||
System.Threading.Monitor.Exit(_dequeueLock);
|
||||
}
|
||||
|
||||
void HeartbeatStart()
|
||||
|
|
@ -743,11 +746,15 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
if (HeartbeatSendTimer == null)
|
||||
{
|
||||
|
||||
HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
|
||||
HeartbeatSendTimer = new Timer(HeartbeatInterval) { AutoReset = true };
|
||||
HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
|
||||
HeartbeatSendTimer.Start();
|
||||
}
|
||||
if (HeartbeatAckTimer == null)
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -791,11 +798,15 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
{
|
||||
if (HeartbeatAckTimer != null)
|
||||
{
|
||||
HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
|
||||
HeartbeatAckTimer.Stop();
|
||||
HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
|
||||
return remainingText;
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ using System.Text;
|
|||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -223,7 +224,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
/// <summary>
|
||||
/// private Timer for auto reconnect
|
||||
/// </summary>
|
||||
CTimer RetryTimer;
|
||||
System.Timers.Timer RetryTimer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -255,13 +256,13 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
/// </summary>
|
||||
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
|
||||
|
||||
CTimer HeartbeatSendTimer;
|
||||
CTimer HeartbeatAckTimer;
|
||||
System.Timers.Timer HeartbeatSendTimer;
|
||||
System.Timers.Timer HeartbeatAckTimer;
|
||||
/// <summary>
|
||||
/// Used to force disconnection on a dead connect attempt
|
||||
/// </summary>
|
||||
CTimer ConnectFailTimer;
|
||||
CTimer WaitForSharedKey;
|
||||
System.Timers.Timer ConnectFailTimer;
|
||||
System.Timers.Timer WaitForSharedKey;
|
||||
private int ConnectionCount;
|
||||
/// <summary>
|
||||
/// Internal secure client
|
||||
|
|
@ -457,7 +458,8 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
|
||||
//var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||
|
||||
ConnectFailTimer = new CTimer(o =>
|
||||
ConnectFailTimer = new System.Timers.Timer(30000) { AutoReset = false };
|
||||
ConnectFailTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
|
||||
if (IsTryingToConnect)
|
||||
|
|
@ -471,7 +473,8 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
//SecureClient.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
}
|
||||
}, 30000);
|
||||
};
|
||||
ConnectFailTimer.Start();
|
||||
|
||||
this.LogVerbose("Making Connection Count:{0}", ConnectionCount);
|
||||
Client.ConnectToServerAsync(o =>
|
||||
|
|
@ -492,16 +495,16 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
if (SharedKeyRequired)
|
||||
{
|
||||
WaitingForSharedKeyResponse = true;
|
||||
WaitForSharedKey = new CTimer(timer =>
|
||||
WaitForSharedKey = new System.Timers.Timer(15000) { AutoReset = false };
|
||||
WaitForSharedKey.Elapsed += (s, e) =>
|
||||
{
|
||||
|
||||
this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
|
||||
// Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
|
||||
// This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
|
||||
o.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
//OnClientReadyForcommunications(false); // Should send false event
|
||||
}, 15000);
|
||||
};
|
||||
WaitForSharedKey.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -596,7 +599,9 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
if (AutoReconnectTriggered != null)
|
||||
AutoReconnectTriggered(this, new EventArgs());
|
||||
RetryTimer = new CTimer(o => Connect(), rndTime);
|
||||
RetryTimer = new System.Timers.Timer(rndTime) { AutoReset = false };
|
||||
RetryTimer.Elapsed += (s, e) => Connect();
|
||||
RetryTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -702,11 +707,15 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
if (HeartbeatSendTimer == null)
|
||||
{
|
||||
|
||||
HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
|
||||
HeartbeatSendTimer = new System.Timers.Timer(HeartbeatInterval) { AutoReset = true };
|
||||
HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
|
||||
HeartbeatSendTimer.Start();
|
||||
}
|
||||
if (HeartbeatAckTimer == null)
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new System.Timers.Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -750,11 +759,15 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
if (HeartbeatAckTimer != null)
|
||||
{
|
||||
HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
|
||||
HeartbeatAckTimer.Stop();
|
||||
HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new System.Timers.Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
|
||||
return remainingText;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -92,7 +91,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// <summary>
|
||||
/// Timer to operate the bandaid monitor client in a loop.
|
||||
/// </summary>
|
||||
CTimer MonitorClientTimer;
|
||||
Timer MonitorClientTimer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
|
@ -259,7 +258,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
public string HeartbeatStringToMatch { get; set; }
|
||||
|
||||
//private timers for Heartbeats per client
|
||||
Dictionary<uint, CTimer> HeartbeatTimerDictionary = new Dictionary<uint, CTimer>();
|
||||
Dictionary<uint, Timer> HeartbeatTimerDictionary = new Dictionary<uint, Timer>();
|
||||
|
||||
//flags to show the secure server is waiting for client at index to send the shared key
|
||||
List<uint> WaitingForSharedKey = new List<uint>();
|
||||
|
|
@ -592,11 +591,17 @@ public class GenericSecureTcpIpServer : Device
|
|||
if (noDelimiter.Contains(HeartbeatStringToMatch))
|
||||
{
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
|
||||
HeartbeatTimerDictionary[clientIndex].Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
this.LogDebug("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
// Return Heartbeat
|
||||
|
|
@ -607,11 +612,17 @@ public class GenericSecureTcpIpServer : Device
|
|||
else
|
||||
{
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
|
||||
HeartbeatTimerDictionary[clientIndex].Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
this.LogInformation("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
}
|
||||
|
|
@ -665,7 +676,6 @@ public class GenericSecureTcpIpServer : Device
|
|||
SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
|
||||
|
||||
var discoResult = SecureServer.Disconnect(clientIndex);
|
||||
//Debug.Console(1, this, "{0}", discoResult);
|
||||
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
|
|
@ -694,8 +704,6 @@ public class GenericSecureTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
|
||||
|
||||
// Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
this.LogInformation("SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
|
||||
|
|
@ -725,7 +733,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
//Use a thread for this event so that the server state updates to listening while this event is processed. Listening must be added to the server state
|
||||
//after every client connection so that the server can check and see if it is at max clients. Due to this the event fires and server listening enum bit flag
|
||||
//is not set. Putting in a thread allows the state to update before this event processes so that the subscribers to this event get accurate isListening in the event.
|
||||
Task.Run(() => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)));
|
||||
System.Threading.Tasks.Task.Run(() => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -770,7 +778,10 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -860,8 +871,8 @@ public class GenericSecureTcpIpServer : Device
|
|||
//Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
|
||||
if (handler != null)
|
||||
{
|
||||
if (Monitor.TryEnter(_dequeueLock))
|
||||
Task.Run(() => DequeueEvent());
|
||||
if (System.Threading.Monitor.TryEnter(_dequeueLock))
|
||||
System.Threading.Tasks.Task.Run(() => DequeueEvent());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -894,7 +905,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
this.LogError(e, "DequeueEvent error");
|
||||
}
|
||||
// Make sure to release the lock in case an exception above stops this thread, or we won't be able to restart it.
|
||||
Monitor.Exit(_dequeueLock);
|
||||
System.Threading.Monitor.Exit(_dequeueLock);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -991,7 +1002,9 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
return;
|
||||
}
|
||||
MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
|
||||
MonitorClientTimer = new Timer(60000) { AutoReset = false };
|
||||
MonitorClientTimer.Elapsed += (s, e) => RunMonitorClient();
|
||||
MonitorClientTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
using PepperDash.Core.Logging;
|
||||
using Renci.SshNet;
|
||||
using Renci.SshNet.Common;
|
||||
|
|
@ -134,9 +131,9 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
|
||||
ShellStream TheStream;
|
||||
|
||||
CTimer ReconnectTimer;
|
||||
Timer ReconnectTimer;
|
||||
|
||||
private SemaphoreSlim connectLock = new SemaphoreSlim(1);
|
||||
private System.Threading.SemaphoreSlim connectLock = new System.Threading.SemaphoreSlim(1);
|
||||
|
||||
private bool DisconnectLogged = false;
|
||||
|
||||
|
|
@ -155,13 +152,14 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
Password = password;
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
|
||||
ReconnectTimer = new CTimer(o =>
|
||||
ReconnectTimer = new Timer { AutoReset = false, Enabled = false };
|
||||
ReconnectTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
if (ConnectEnabled)
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -173,13 +171,14 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
|
||||
ReconnectTimer = new CTimer(o =>
|
||||
ReconnectTimer = new Timer { AutoReset = false, Enabled = false };
|
||||
ReconnectTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
if (ConnectEnabled)
|
||||
{
|
||||
Connect();
|
||||
}
|
||||
}, System.Threading.Timeout.Infinite);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -265,7 +264,6 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
catch (SshConnectionException e)
|
||||
{
|
||||
var ie = e.InnerException; // The details are inside!!
|
||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||
|
||||
if (ie is SocketException)
|
||||
{
|
||||
|
|
@ -289,7 +287,9 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Interval = AutoReconnectIntervalMs;
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
}
|
||||
catch (SshOperationTimeoutException ex)
|
||||
|
|
@ -301,19 +301,22 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Interval = AutoReconnectIntervalMs;
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
|
||||
this.LogException(e, "Unhandled exception on connect");
|
||||
DisconnectLogged = true;
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
if (AutoReconnect)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Interval = AutoReconnectIntervalMs;
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -434,7 +437,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
/// </summary>
|
||||
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
System.Threading.Tasks.Task.Run(() =>
|
||||
{
|
||||
if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
|
||||
this.LogError("Disconnected by remote");
|
||||
|
|
@ -452,7 +455,9 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
if (AutoReconnect && ConnectEnabled)
|
||||
{
|
||||
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Reset(AutoReconnectIntervalMs);
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Interval = AutoReconnectIntervalMs;
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -497,7 +502,8 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -531,7 +537,8 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
|
||||
|
||||
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
|
||||
ReconnectTimer.Reset();
|
||||
ReconnectTimer.Stop();
|
||||
ReconnectTimer.Start();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -11,10 +11,9 @@ PepperDash Technology Corporation reserves all rights under applicable laws.
|
|||
------------------------------------ */
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -210,7 +209,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
/// <summary>
|
||||
/// private Timer for auto reconnect
|
||||
/// </summary>
|
||||
CTimer RetryTimer;
|
||||
Timer RetryTimer;
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -237,13 +236,13 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
/// </summary>
|
||||
public int HeartbeatInterval = 50000;
|
||||
|
||||
CTimer HeartbeatSendTimer;
|
||||
CTimer HeartbeatAckTimer;
|
||||
Timer HeartbeatSendTimer;
|
||||
Timer HeartbeatAckTimer;
|
||||
/// <summary>
|
||||
/// Used to force disconnection on a dead connect attempt
|
||||
/// </summary>
|
||||
CTimer ConnectFailTimer;
|
||||
CTimer WaitForSharedKey;
|
||||
Timer ConnectFailTimer;
|
||||
Timer WaitForSharedKey;
|
||||
private int ConnectionCount;
|
||||
/// <summary>
|
||||
/// Internal secure client
|
||||
|
|
@ -303,7 +302,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
|
||||
this.LogInformation("Program stopping. Closing Client connection");
|
||||
ProgramIsStopping = true;
|
||||
Disconnect();
|
||||
}
|
||||
|
|
@ -316,17 +315,17 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
public void Connect()
|
||||
{
|
||||
ConnectionCount++;
|
||||
Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
|
||||
this.LogDebug("Attempting connect Count:{0}", ConnectionCount);
|
||||
|
||||
|
||||
if (IsConnected)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
|
||||
this.LogInformation("Already connected. Ignoring.");
|
||||
return;
|
||||
}
|
||||
if (IsTryingToConnect)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
|
||||
this.LogInformation("Already trying to connect. Ignoring.");
|
||||
return;
|
||||
}
|
||||
try
|
||||
|
|
@ -339,17 +338,17 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
if (string.IsNullOrEmpty(Hostname))
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
|
||||
this.LogWarning("DynamicTcpClient: No address set");
|
||||
return;
|
||||
}
|
||||
if (Port < 1 || Port > 65535)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
|
||||
this.LogWarning("DynamicTcpClient: Invalid port");
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
|
||||
this.LogWarning("DynamicTcpClient: No Shared Key set");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -370,9 +369,10 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
|
||||
//var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
|
||||
|
||||
ConnectFailTimer = new CTimer(o =>
|
||||
ConnectFailTimer = new Timer(30000) { AutoReset = false };
|
||||
ConnectFailTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
|
||||
this.LogError("Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
|
||||
if (IsTryingToConnect)
|
||||
{
|
||||
IsTryingToConnect = false;
|
||||
|
|
@ -384,12 +384,13 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
//SecureClient.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
}
|
||||
}, 30000);
|
||||
};
|
||||
ConnectFailTimer.Start();
|
||||
|
||||
Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
|
||||
this.LogDebug("Making Connection Count:{0}", ConnectionCount);
|
||||
Client.ConnectToServerAsync(o =>
|
||||
{
|
||||
Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
|
||||
this.LogDebug("ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
|
||||
|
||||
if (ConnectFailTimer != null)
|
||||
{
|
||||
|
|
@ -399,22 +400,22 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
|
||||
if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
|
||||
this.LogVerbose("Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
|
||||
o.ReceiveDataAsync(Receive);
|
||||
|
||||
if (SharedKeyRequired)
|
||||
{
|
||||
WaitingForSharedKeyResponse = true;
|
||||
WaitForSharedKey = new CTimer(timer =>
|
||||
WaitForSharedKey = new Timer(15000) { AutoReset = false };
|
||||
WaitForSharedKey.Elapsed += (s, e) =>
|
||||
{
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
|
||||
// Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
|
||||
this.LogWarning("Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
|
||||
// This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
|
||||
o.DisconnectFromServer();
|
||||
//CheckClosedAndTryReconnect();
|
||||
//OnClientReadyForcommunications(false); // Should send false event
|
||||
}, 15000);
|
||||
};
|
||||
WaitForSharedKey.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -428,14 +429,15 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
|
||||
this.LogWarning("Connect attempt failed {0}", o.ClientStatus);
|
||||
CheckClosedAndTryReconnect();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
|
||||
this.LogException(ex, "Client connection exception: {0}", ex.Message);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
IsTryingToConnect = false;
|
||||
CheckClosedAndTryReconnect();
|
||||
}
|
||||
|
|
@ -472,7 +474,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
if (Client != null)
|
||||
{
|
||||
//SecureClient.DisconnectFromServer();
|
||||
Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
|
||||
this.LogVerbose("Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
|
||||
Client.SocketStatusChange -= Client_SocketStatusChange;
|
||||
Client.Dispose();
|
||||
Client = null;
|
||||
|
|
@ -494,20 +496,22 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
if (Client != null)
|
||||
{
|
||||
Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
|
||||
this.LogVerbose("Cleaning up remotely closed/failed connection.");
|
||||
Cleanup();
|
||||
}
|
||||
if (!DisconnectCalledByUser && AutoReconnect)
|
||||
{
|
||||
var halfInterval = AutoReconnectIntervalMs / 2;
|
||||
var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
|
||||
Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
|
||||
this.LogVerbose("Attempting reconnect in {0} ms, randomized", rndTime);
|
||||
if (RetryTimer != null)
|
||||
{
|
||||
RetryTimer.Stop();
|
||||
RetryTimer = null;
|
||||
}
|
||||
RetryTimer = new CTimer(o => Connect(), rndTime);
|
||||
RetryTimer = new Timer(rndTime) { AutoReset = false };
|
||||
RetryTimer.Elapsed += (s, e) => Connect();
|
||||
RetryTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,18 +530,18 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
|
||||
str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
||||
Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
|
||||
this.LogVerbose("Client Received:\r--------\r{0}\r--------", str);
|
||||
if (!string.IsNullOrEmpty(checkHeartbeat(str)))
|
||||
{
|
||||
if (SharedKeyRequired && str == "SharedKey:")
|
||||
{
|
||||
Debug.Console(2, this, "Server asking for shared key, sending");
|
||||
this.LogVerbose("Server asking for shared key, sending");
|
||||
SendText(SharedKey + "\n");
|
||||
}
|
||||
else if (SharedKeyRequired && str == "Shared Key Match")
|
||||
{
|
||||
StopWaitForSharedKeyTimer();
|
||||
Debug.Console(2, this, "Shared key confirmed. Ready for communication");
|
||||
this.LogVerbose("Shared key confirmed. Ready for communication");
|
||||
OnClientReadyForcommunications(true); // Successful key exchange
|
||||
}
|
||||
else
|
||||
|
|
@ -553,7 +557,8 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
|
||||
this.LogException(ex, "Error receiving data: {1}. Error: {0}", ex.Message, str);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
|
|
@ -564,15 +569,19 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
if (HeartbeatEnabled)
|
||||
{
|
||||
Debug.Console(2, this, "Starting Heartbeat");
|
||||
this.LogVerbose("Starting Heartbeat");
|
||||
if (HeartbeatSendTimer == null)
|
||||
{
|
||||
|
||||
HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
|
||||
HeartbeatSendTimer = new Timer(HeartbeatInterval) { AutoReset = true };
|
||||
HeartbeatSendTimer.Elapsed += (s, e) => SendHeartbeat(null);
|
||||
HeartbeatSendTimer.Start();
|
||||
}
|
||||
if (HeartbeatAckTimer == null)
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -582,13 +591,13 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
|
||||
if (HeartbeatSendTimer != null)
|
||||
{
|
||||
Debug.Console(2, this, "Stoping Heartbeat Send");
|
||||
this.LogVerbose("Stoping Heartbeat Send");
|
||||
HeartbeatSendTimer.Stop();
|
||||
HeartbeatSendTimer = null;
|
||||
}
|
||||
if (HeartbeatAckTimer != null)
|
||||
{
|
||||
Debug.Console(2, this, "Stoping Heartbeat Ack");
|
||||
this.LogVerbose("Stoping Heartbeat Ack");
|
||||
HeartbeatAckTimer.Stop();
|
||||
HeartbeatAckTimer = null;
|
||||
}
|
||||
|
|
@ -597,7 +606,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
void SendHeartbeat(object notused)
|
||||
{
|
||||
this.SendText(HeartbeatString);
|
||||
Debug.Console(2, this, "Sending Heartbeat");
|
||||
this.LogVerbose("Sending Heartbeat");
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -616,13 +625,17 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
if (HeartbeatAckTimer != null)
|
||||
{
|
||||
HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
|
||||
HeartbeatAckTimer.Stop();
|
||||
HeartbeatAckTimer.Interval = HeartbeatInterval * 2;
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
|
||||
HeartbeatAckTimer = new Timer(HeartbeatInterval * 2) { AutoReset = true };
|
||||
HeartbeatAckTimer.Elapsed += (s, e) => HeartbeatAckTimerFail(null);
|
||||
HeartbeatAckTimer.Start();
|
||||
}
|
||||
Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
|
||||
this.LogVerbose("Heartbeat Received: {0}, from Server", HeartbeatString);
|
||||
return remainingText;
|
||||
}
|
||||
}
|
||||
|
|
@ -630,7 +643,8 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
|
||||
this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
|
@ -644,7 +658,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
|
||||
if (IsConnected)
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
|
||||
this.LogWarning("Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
|
||||
SendText("Heartbeat not received by server, closing connection");
|
||||
CheckClosedAndTryReconnect();
|
||||
}
|
||||
|
|
@ -652,7 +666,8 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
|
||||
this.LogException(ex, "Heartbeat timeout Error on Client: {0}, {1}", Key, ex.Message);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -685,14 +700,15 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
// HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
|
||||
if (n <= 0)
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
|
||||
this.LogWarning("[{0}] Sent zero bytes. Was there an error?", this.Key);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
|
||||
this.LogException(ex, "Error sending text: {1}. Error: {0}", ex.Message, text);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -711,7 +727,8 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
|
||||
this.LogException(ex, "Error sending bytes. Error: {0}", ex.Message);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -730,7 +747,7 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
try
|
||||
{
|
||||
Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
|
||||
this.LogVerbose("Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
|
||||
|
||||
OnConnectionChange();
|
||||
|
||||
|
|
@ -744,7 +761,8 @@ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
|
||||
this.LogException(ex, "Error in socket status change callback. Error: {0}", ex.Message);
|
||||
this.LogVerbose("Stack Trace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -80,7 +80,7 @@ public class GenericTcpIpServer : Device
|
|||
/// <summary>
|
||||
/// Timer to operate the bandaid monitor client in a loop.
|
||||
/// </summary>
|
||||
CTimer MonitorClientTimer;
|
||||
Timer MonitorClientTimer;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
|
|
@ -250,7 +250,7 @@ public class GenericTcpIpServer : Device
|
|||
public string HeartbeatStringToMatch { get; set; }
|
||||
|
||||
//private timers for Heartbeats per client
|
||||
Dictionary<uint, CTimer> HeartbeatTimerDictionary = new Dictionary<uint, CTimer>();
|
||||
Dictionary<uint, Timer> HeartbeatTimerDictionary = new Dictionary<uint, Timer>();
|
||||
|
||||
//flags to show the secure server is waiting for client at index to send the shared key
|
||||
List<uint> WaitingForSharedKey = new List<uint>();
|
||||
|
|
@ -577,11 +577,17 @@ public class GenericTcpIpServer : Device
|
|||
if (noDelimiter.Contains(HeartbeatStringToMatch))
|
||||
{
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
|
||||
HeartbeatTimerDictionary[clientIndex].Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
// Return Heartbeat
|
||||
|
|
@ -592,11 +598,17 @@ public class GenericTcpIpServer : Device
|
|||
else
|
||||
{
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
|
||||
{
|
||||
HeartbeatTimerDictionary[clientIndex].Stop();
|
||||
HeartbeatTimerDictionary[clientIndex].Interval = HeartbeatRequiredIntervalMs;
|
||||
HeartbeatTimerDictionary[clientIndex].Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
}
|
||||
|
|
@ -650,7 +662,6 @@ public class GenericTcpIpServer : Device
|
|||
SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
|
||||
|
||||
var discoResult = myTcpServer.Disconnect(clientIndex);
|
||||
//Debug.Console(1, this, "{0}", discoResult);
|
||||
|
||||
if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
|
|
@ -661,7 +672,8 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
|
||||
this.LogException(ex, "Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message);
|
||||
this.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -746,7 +758,10 @@ public class GenericTcpIpServer : Device
|
|||
{
|
||||
if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
|
||||
{
|
||||
HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
|
||||
var heartbeatTimer = new Timer(HeartbeatRequiredIntervalMs) { AutoReset = false };
|
||||
heartbeatTimer.Elapsed += (s, e) => HeartbeatTimer_CallbackFunction(clientIndex);
|
||||
heartbeatTimer.Start();
|
||||
HeartbeatTimerDictionary.Add(clientIndex, heartbeatTimer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -765,9 +780,9 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||
this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex.Message);
|
||||
this.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||
}
|
||||
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
||||
// server.State,
|
||||
// MaxClients,
|
||||
// ServerStopped);
|
||||
|
|
@ -929,7 +944,9 @@ public class GenericTcpIpServer : Device
|
|||
{
|
||||
return;
|
||||
}
|
||||
MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
|
||||
MonitorClientTimer = new Timer(60000) { AutoReset = false };
|
||||
MonitorClientTimer.Elapsed += (s, e) => RunMonitorClient();
|
||||
MonitorClientTimer.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
if (programEventType != eProgramStatusEventType.Stopping)
|
||||
return;
|
||||
|
||||
Debug.Console(1, this, "Program stopping. Disabling Server");
|
||||
this.LogInformation("Program stopping. Disabling Server");
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
|
|
@ -199,20 +199,20 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
|
||||
if (string.IsNullOrEmpty(Hostname))
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
|
||||
this.LogWarning("GenericUdpServer '{0}': No address set", Key);
|
||||
return;
|
||||
}
|
||||
if (Port < 1 || Port > 65535)
|
||||
{
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key);
|
||||
this.LogWarning("GenericUdpServer '{0}': Invalid port", Key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var status = Server.EnableUDPServer(Hostname, Port);
|
||||
|
||||
Debug.Console(2, this, "SocketErrorCode: {0}", status);
|
||||
this.LogVerbose("SocketErrorCode: {0}", status);
|
||||
if (status == SocketErrorCodes.SOCKET_OK)
|
||||
IsConnected = true;
|
||||
|
||||
|
|
@ -247,7 +247,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
/// <param name="numBytes"></param>
|
||||
void Receive(UDPServer server, int numBytes)
|
||||
{
|
||||
Debug.Console(2, this, "Received {0} bytes", numBytes);
|
||||
this.LogVerbose("Received {0} bytes", numBytes);
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -263,13 +263,13 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
if (dataRecivedExtra != null)
|
||||
dataRecivedExtra(this, new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));
|
||||
|
||||
Debug.Console(2, this, "Bytes: {0}", bytes.ToString());
|
||||
this.LogVerbose("Bytes: {0}", bytes.ToString());
|
||||
var bytesHandler = BytesReceived;
|
||||
if (bytesHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
{
|
||||
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
|
||||
}
|
||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||
}
|
||||
|
|
@ -277,7 +277,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
if (textHandler != null)
|
||||
{
|
||||
if (StreamDebugging.RxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
|
||||
this.LogInformation("Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
|
||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
||||
}
|
||||
}
|
||||
|
|
@ -302,7 +302,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
if (IsConnected && Server != null)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
this.LogVerbose("Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
|
||||
Server.SendData(bytes, bytes.Length);
|
||||
}
|
||||
|
|
@ -315,7 +315,7 @@ public class GenericUdpServer : Device, ISocketStatusWithStreamDebugging
|
|||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||
|
||||
if (IsConnected && Server != null)
|
||||
Server.SendData(bytes, bytes.Length);
|
||||
|
|
|
|||
|
|
@ -155,7 +155,6 @@ namespace PepperDash.Core.Config;
|
|||
else
|
||||
merged.Add(global, template[global]);
|
||||
|
||||
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
|
||||
return merged;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using PepperDash.Core.Logging;
|
||||
using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
|
@ -91,7 +92,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
if (Master != null)
|
||||
Master.AddChild(this);
|
||||
else
|
||||
Debug.Console(1, "JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
|
||||
this.LogWarning("JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -107,7 +108,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
/// </summary>
|
||||
public void SetBoolPath(ushort index, string path)
|
||||
{
|
||||
Debug.Console(1, "JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
|
||||
this.LogDebug("JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
|
||||
if (path == null || path.Trim() == string.Empty) return;
|
||||
BoolPaths[index] = path;
|
||||
}
|
||||
|
|
@ -117,7 +118,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
/// </summary>
|
||||
public void SetUshortPath(ushort index, string path)
|
||||
{
|
||||
Debug.Console(1, "JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
|
||||
this.LogDebug("JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
|
||||
if (path == null || path.Trim() == string.Empty) return;
|
||||
UshortPaths[index] = path;
|
||||
}
|
||||
|
|
@ -127,7 +128,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
/// </summary>
|
||||
public void SetStringPath(ushort index, string path)
|
||||
{
|
||||
Debug.Console(1, "JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
|
||||
this.LogDebug("JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
|
||||
if (path == null || path.Trim() == string.Empty) return;
|
||||
StringPaths[index] = path;
|
||||
}
|
||||
|
|
@ -140,13 +141,13 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
{
|
||||
if (!LinkedToObject)
|
||||
{
|
||||
Debug.Console(1, this, "Not linked to object in file. Skipping");
|
||||
this.LogDebug("Not linked to object in file. Skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SetAllPathsDelegate == null)
|
||||
{
|
||||
Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring ProcessAll");
|
||||
this.LogDebug("No SetAllPathsDelegate set. Ignoring ProcessAll");
|
||||
return;
|
||||
}
|
||||
SetAllPathsDelegate();
|
||||
|
|
@ -206,11 +207,11 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
bool Process(string path, out string response)
|
||||
{
|
||||
path = GetFullPath(path);
|
||||
Debug.Console(1, "JSON Child[{0}] Processing {1}", Key, path);
|
||||
this.LogDebug("JSON Child[{0}] Processing {1}", Key, path);
|
||||
response = "";
|
||||
if (Master == null)
|
||||
{
|
||||
Debug.Console(1, "JSONChild[{0}] cannot process without Master attached", Key);
|
||||
this.LogWarning("JSONChild[{0}] cannot process without Master attached", Key);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +234,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
response = (t.HasValues ? t.Children().Count() : 0).ToString();
|
||||
else
|
||||
response = (string)t;
|
||||
Debug.Console(1, " ='{0}'", response);
|
||||
this.LogDebug(" ='{0}'", response);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -259,13 +260,13 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
{
|
||||
if (!LinkedToObject)
|
||||
{
|
||||
Debug.Console(1, this, "Not linked to object in file. Skipping");
|
||||
this.LogDebug("Not linked to object in file. Skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SetAllPathsDelegate == null)
|
||||
{
|
||||
Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
|
||||
this.LogDebug("No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
|
||||
return;
|
||||
}
|
||||
SetAllPathsDelegate();
|
||||
|
|
@ -327,7 +328,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
var path = GetFullPath(keyPath);
|
||||
try
|
||||
{
|
||||
Debug.Console(1, "JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
|
||||
this.LogDebug("JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
|
||||
|
||||
//var token = Master.JsonObject.SelectToken(path);
|
||||
//if (token != null) // The path exists in the file
|
||||
|
|
@ -335,7 +336,7 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Console(1, "JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
|
||||
this.LogDebug("JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Text;
|
|||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using PepperDash.Core.Logging;
|
||||
using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
|
||||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
||||
using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
|
||||
|
|
@ -129,7 +130,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
var fileName = Path.GetFileName(Filepath);
|
||||
|
||||
OnStringChange(string.Format("Checking '{0}' for '{1}'", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "Checking '{0}' for '{1}'", fileDirectory, fileName);
|
||||
this.LogInformation("Checking '{0}' for '{1}'", fileDirectory, fileName);
|
||||
|
||||
if (Directory.Exists(fileDirectory))
|
||||
{
|
||||
|
|
@ -143,7 +144,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
var msg = string.Format("JSON file not found: {0}", Filepath);
|
||||
OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
|
||||
CrestronConsole.PrintLine(msg);
|
||||
ErrorLog.Error(msg);
|
||||
this.LogError(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -152,18 +153,18 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
ActualFilePath = actualFile.FullName;
|
||||
OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
|
||||
OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "Actual JSON file is {0}", ActualFilePath);
|
||||
this.LogInformation("Actual JSON file is {0}", ActualFilePath);
|
||||
|
||||
Filename = actualFile.Name;
|
||||
OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);
|
||||
OnStringChange(string.Format("JSON Filename is {0}", Filename), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "JSON Filename is {0}", Filename);
|
||||
this.LogInformation("JSON Filename is {0}", Filename);
|
||||
|
||||
|
||||
FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator);
|
||||
OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);
|
||||
OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "JSON File Path is {0}", FilePathName);
|
||||
this.LogInformation("JSON File Path is {0}", FilePathName);
|
||||
|
||||
var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
|
||||
|
|
@ -176,7 +177,7 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
else
|
||||
{
|
||||
OnStringChange(string.Format("'{0}' not found", fileDirectory), 0, JsonToSimplConstants.StringValueChange);
|
||||
Debug.Console(1, "'{0}' not found", fileDirectory);
|
||||
this.LogError("'{0}' not found", fileDirectory);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
|
|
@ -184,12 +185,12 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
var msg = string.Format("EvaluateFile Exception: Message\r{0}", e.Message);
|
||||
OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
|
||||
CrestronConsole.PrintLine(msg);
|
||||
ErrorLog.Error(msg);
|
||||
this.LogException(e, "EvaluateFile Exception: {0}", e.Message);
|
||||
|
||||
var stackTrace = string.Format("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
|
||||
OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);
|
||||
CrestronConsole.PrintLine(stackTrace);
|
||||
ErrorLog.Error(stackTrace);
|
||||
this.LogVerbose("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -213,63 +214,31 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
// Make each child update their values into master object
|
||||
foreach (var child in Children)
|
||||
{
|
||||
Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
|
||||
this.LogInformation("Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
|
||||
child.UpdateInputsForMaster();
|
||||
}
|
||||
|
||||
if (UnsavedValues == null || UnsavedValues.Count == 0)
|
||||
{
|
||||
Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
|
||||
this.LogInformation("Master [{0}] No updated values to save. Skipping", UniqueID);
|
||||
return;
|
||||
}
|
||||
lock (FileLock)
|
||||
{
|
||||
Debug.Console(1, "Saving");
|
||||
this.LogInformation("Saving");
|
||||
foreach (var path in UnsavedValues.Keys)
|
||||
{
|
||||
var tokenToReplace = JsonObject.SelectToken(path);
|
||||
if (tokenToReplace != null)
|
||||
{// It's found
|
||||
tokenToReplace.Replace(UnsavedValues[path]);
|
||||
Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
|
||||
this.LogInformation("JSON Master[{0}] Updating '{1}'", UniqueID, path);
|
||||
}
|
||||
else // No token. Let's make one
|
||||
{
|
||||
//http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
|
||||
Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
|
||||
this.LogWarning("JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
|
||||
|
||||
// JContainer jpart = JsonObject;
|
||||
// // walk down the path and find where it goes
|
||||
//#warning Does not handle arrays.
|
||||
// foreach (var part in path.Split('.'))
|
||||
// {
|
||||
|
||||
// var openPos = part.IndexOf('[');
|
||||
// if (openPos > -1)
|
||||
// {
|
||||
// openPos++; // move to number
|
||||
// var closePos = part.IndexOf(']');
|
||||
// var arrayName = part.Substring(0, openPos - 1); // get the name
|
||||
// var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos));
|
||||
|
||||
// // Check if the array itself exists and add the item if so
|
||||
// if (jpart[arrayName] != null)
|
||||
// {
|
||||
// var arrayObj = jpart[arrayName] as JArray;
|
||||
// var item = arrayObj[index];
|
||||
// if (item == null)
|
||||
// arrayObj.Add(new JObject());
|
||||
// }
|
||||
|
||||
// Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW");
|
||||
// continue;
|
||||
// }
|
||||
// // Build the
|
||||
// if (jpart[part] == null)
|
||||
// jpart.Add(new JProperty(part, new JObject()));
|
||||
// jpart = jpart[part] as JContainer;
|
||||
// }
|
||||
// jpart.Replace(UnsavedValues[path]);
|
||||
}
|
||||
}
|
||||
using (StreamWriter sw = new StreamWriter(ActualFilePath))
|
||||
|
|
@ -282,11 +251,13 @@ public class JsonToSimplFileMaster : JsonToSimplMaster
|
|||
catch (Exception e)
|
||||
{
|
||||
string err = string.Format("Error writing JSON file:\r{0}", e);
|
||||
Debug.Console(0, err);
|
||||
ErrorLog.Warn(err);
|
||||
this.LogException(e, "Error writing JSON file: {0}", e.Message);
|
||||
this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core.Logging;
|
||||
using Renci.SshNet.Messages;
|
||||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
||||
using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
|
||||
|
||||
|
|
@ -74,7 +76,8 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Console(0, this, "JSON parsing failed:\r{0}", e);
|
||||
this.LogException(e, "JSON parsing failed:\r{0}", e.Message);
|
||||
this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,36 +92,36 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
// Make each child update their values into master object
|
||||
foreach (var child in Children)
|
||||
{
|
||||
Debug.Console(1, this, "Master. checking child [{0}] for updates to save", child.Key);
|
||||
this.LogDebug("Master. checking child [{0}] for updates to save", child.Key);
|
||||
child.UpdateInputsForMaster();
|
||||
}
|
||||
|
||||
if (UnsavedValues == null || UnsavedValues.Count == 0)
|
||||
{
|
||||
Debug.Console(1, this, "Master. No updated values to save. Skipping");
|
||||
this.LogDebug("Master. No updated values to save. Skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (WriteLock)
|
||||
{
|
||||
Debug.Console(1, this, "Saving");
|
||||
this.LogDebug("Saving");
|
||||
foreach (var path in UnsavedValues.Keys)
|
||||
{
|
||||
var tokenToReplace = JsonObject.SelectToken(path);
|
||||
if (tokenToReplace != null)
|
||||
{// It's found
|
||||
tokenToReplace.Replace(UnsavedValues[path]);
|
||||
Debug.Console(1, this, "Master Updating '{0}'", path);
|
||||
this.LogDebug("Master Updating '{0}'", path);
|
||||
}
|
||||
else // No token. Let's make one
|
||||
{
|
||||
Debug.Console(1, "Master Cannot write value onto missing property: '{0}'", path);
|
||||
this.LogDebug("Master Cannot write value onto missing property: '{0}'", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SaveCallback != null)
|
||||
SaveCallback(JsonObject.ToString());
|
||||
else
|
||||
Debug.Console(0, this, "WARNING: No save callback defined.");
|
||||
this.LogDebug("WARNING: No save callback defined.");
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
|||
using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
|
||||
using JsonSerializationException = NewtonsoftJson::Newtonsoft.Json.JsonSerializationException;
|
||||
using JsonTextReader = NewtonsoftJson::Newtonsoft.Json.JsonTextReader;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
|
|
@ -142,11 +143,10 @@ namespace PepperDash.Core.JsonToSimpl;
|
|||
{
|
||||
if (UnsavedValues.ContainsKey(path))
|
||||
{
|
||||
Debug.Console(0, "Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\r Ingoring. Please ensure that path does not exist on multiple modules.", UniqueID, path);
|
||||
this.LogWarning("Master[{0}] WARNING - Attempt to add duplicate value for path '{1}'.\r Ingoring. Please ensure that path does not exist on multiple modules.", UniqueID, path);
|
||||
}
|
||||
else
|
||||
UnsavedValues.Add(path, value);
|
||||
//Debug.Console(0, "Master[{0}] Unsaved size={1}", UniqueID, UnsavedValues.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Crestron.SimplSharp.CrestronIO;
|
|||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
||||
using JValue = NewtonsoftJson::Newtonsoft.Json.Linq.JValue;
|
||||
using PepperDash.Core.Config;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core.JsonToSimpl;
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
// If the portal file is xyz.json, then
|
||||
// the file we want to check for first will be called xyz.local.json
|
||||
var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json");
|
||||
Debug.Console(0, this, "Checking for local file {0}", localFilepath);
|
||||
this.LogInformation("Checking for local file {0}", localFilepath);
|
||||
var actualLocalFile = GetActualFileInfoFromPath(localFilepath);
|
||||
|
||||
if (actualLocalFile != null)
|
||||
|
|
@ -73,7 +74,7 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
// and create the local.
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
|
||||
this.LogInformation("Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
|
||||
var actualPortalFile = GetActualFileInfoFromPath(portalFilepath);
|
||||
if (actualPortalFile != null)
|
||||
{
|
||||
|
|
@ -86,14 +87,13 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
else
|
||||
{
|
||||
var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath);
|
||||
Debug.Console(1, this, msg);
|
||||
ErrorLog.Error(msg);
|
||||
this.LogError(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we should have a local file. Do it.
|
||||
Debug.Console(1, "Reading local JSON file {0}", ActualFilePath);
|
||||
this.LogInformation("Reading local JSON file {0}", ActualFilePath);
|
||||
|
||||
string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
|
||||
|
||||
|
|
@ -107,8 +107,7 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
catch (Exception e)
|
||||
{
|
||||
var msg = string.Format("JSON parsing failed:\r{0}", e);
|
||||
CrestronConsole.PrintLine(msg);
|
||||
ErrorLog.Error(msg);
|
||||
this.LogError(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -149,30 +148,30 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
// Make each child update their values into master object
|
||||
foreach (var child in Children)
|
||||
{
|
||||
Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
|
||||
this.LogInformation("Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
|
||||
child.UpdateInputsForMaster();
|
||||
}
|
||||
|
||||
if (UnsavedValues == null || UnsavedValues.Count == 0)
|
||||
{
|
||||
Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
|
||||
this.LogInformation("Master [{0}] No updated values to save. Skipping", UniqueID);
|
||||
return;
|
||||
}
|
||||
lock (FileLock)
|
||||
{
|
||||
Debug.Console(1, "Saving");
|
||||
this.LogInformation("Saving");
|
||||
foreach (var path in UnsavedValues.Keys)
|
||||
{
|
||||
var tokenToReplace = JsonObject.SelectToken(path);
|
||||
if (tokenToReplace != null)
|
||||
{// It's found
|
||||
tokenToReplace.Replace(UnsavedValues[path]);
|
||||
Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
|
||||
this.LogInformation("JSON Master[{0}] Updating '{1}'", UniqueID, path);
|
||||
}
|
||||
else // No token. Let's make one
|
||||
{
|
||||
//http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
|
||||
Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
|
||||
this.LogWarning("JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -186,8 +185,8 @@ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
|
|||
catch (Exception e)
|
||||
{
|
||||
string err = string.Format("Error writing JSON file:\r{0}", e);
|
||||
Debug.Console(0, err);
|
||||
ErrorLog.Warn(err);
|
||||
this.LogException(e, "Error writing JSON file: {0}", e.Message);
|
||||
this.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ using System.Threading.Tasks;
|
|||
|
||||
namespace PepperDash.Core.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// Enriches log events with Crestron-specific context properties, such as the application name based on the device platform.
|
||||
/// </summary>
|
||||
public class CrestronEnricher : ILogEventEnricher
|
||||
{
|
||||
static readonly string _appName;
|
||||
|
|
@ -27,6 +30,11 @@ public class CrestronEnricher : ILogEventEnricher
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Enriches the log event with Crestron-specific properties.
|
||||
/// </summary>
|
||||
/// <param name="logEvent"></param>
|
||||
/// <param name="propertyFactory"></param>
|
||||
public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
|
||||
{
|
||||
var property = propertyFactory.CreateProperty("App", _appName);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Timers;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronDataStore;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
|
|
@ -44,24 +45,32 @@ public static class Debug
|
|||
|
||||
private static ILogger _logger;
|
||||
|
||||
private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch consoleLoggingLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch websocketLoggingLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch errorLogLevelSwitch;
|
||||
|
||||
private static readonly LoggingLevelSwitch _fileLevelSwitch;
|
||||
private static readonly LoggingLevelSwitch fileLevelSwitch;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum log level for messages to be sent to the console sink
|
||||
/// </summary>
|
||||
public static LogEventLevel WebsocketMinimumLogLevel
|
||||
{
|
||||
get { return _websocketLoggingLevelSwitch.MinimumLevel; }
|
||||
get { return websocketLoggingLevelSwitch.MinimumLevel; }
|
||||
}
|
||||
|
||||
private static readonly DebugWebsocketSink _websocketSink;
|
||||
private static readonly DebugWebsocketSink websocketSink;
|
||||
|
||||
/// <summary>
|
||||
/// The DebugWebsocketSink instance used for sending log messages to connected websocket clients.
|
||||
/// This is exposed publicly in case there is a need to call methods on the sink directly, such as SendMessageToClients.
|
||||
/// For general logging purposes, use the LogMessage and LogError methods in this class which will send messages to all configured sinks including the websocket sink.
|
||||
/// </summary>
|
||||
public static DebugWebsocketSink WebsocketSink
|
||||
{
|
||||
get { return _websocketSink; }
|
||||
get { return websocketSink; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -95,6 +104,9 @@ public static class Debug
|
|||
|
||||
private const int SaveTimeoutMs = 30000;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the code is running on an appliance or not. Used to determine file paths and other appliance vs server differences
|
||||
/// </summary>
|
||||
public static bool IsRunningOnAppliance = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -102,7 +114,12 @@ public static class Debug
|
|||
/// </summary>
|
||||
public static string PepperDashCoreVersion { get; private set; }
|
||||
|
||||
private static CTimer _saveTimer;
|
||||
private static Timer _saveTimer;
|
||||
|
||||
|
||||
private const int defaultConsoleDebugTimeoutMin = 120;
|
||||
|
||||
private static Timer consoleDebugTimer;
|
||||
|
||||
/// <summary>
|
||||
/// When true, the IncludedExcludedKeys dict will contain keys to include.
|
||||
|
|
@ -118,6 +135,10 @@ public static class Debug
|
|||
|
||||
private static LoggerConfiguration _loggerConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// The default logger configuration used by the Debug class. Can be used as a base for creating custom logger configurations.
|
||||
/// If changes are made to this configuration after initialization, call ResetLoggerConfiguration to have those changes reflected in the logger.
|
||||
/// </summary>
|
||||
public static LoggerConfiguration LoggerConfiguration => _loggerConfiguration;
|
||||
|
||||
static Debug()
|
||||
|
|
@ -126,6 +147,13 @@ public static class Debug
|
|||
{
|
||||
CrestronDataStoreStatic.InitCrestronDataStore();
|
||||
|
||||
consoleDebugTimer = new Timer(defaultConsoleDebugTimeoutMin * 60000) { AutoReset = false };
|
||||
consoleDebugTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
SetDebugLevel(LogEventLevel.Information);
|
||||
CrestronConsole.ConsoleCommandResponse($"Console debug level reset to {LogEventLevel.Information} after timeout of {defaultConsoleDebugTimeoutMin} minutes");
|
||||
};
|
||||
|
||||
var defaultConsoleLevel = GetStoredLogEventLevel(LevelStoreKey);
|
||||
|
||||
var defaultWebsocketLevel = GetStoredLogEventLevel(WebSocketLevelStoreKey);
|
||||
|
|
@ -134,15 +162,15 @@ public static class Debug
|
|||
|
||||
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
|
||||
|
||||
_consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
|
||||
|
||||
_websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
|
||||
|
||||
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
|
||||
|
||||
_fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||
fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
|
||||
|
||||
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||
websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
|
||||
|
||||
var logFilePath = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ?
|
||||
$@"{Directory.GetApplicationRootDirectory()}{Path.DirectorySeparatorChar}user{Path.DirectorySeparatorChar}debug{Path.DirectorySeparatorChar}app{InitialParametersClass.ApplicationNumber}{Path.DirectorySeparatorChar}global-log.log" :
|
||||
|
|
@ -158,14 +186,14 @@ public static class Debug
|
|||
.MinimumLevel.Verbose()
|
||||
.Enrich.FromLogContext()
|
||||
.Enrich.With(new CrestronEnricher())
|
||||
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
|
||||
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch)
|
||||
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
|
||||
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: consoleLoggingLevelSwitch)
|
||||
.WriteTo.Sink(websocketSink, levelSwitch: websocketLoggingLevelSwitch)
|
||||
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: errorLogLevelSwitch)
|
||||
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
|
||||
rollingInterval: RollingInterval.Day,
|
||||
restrictedToMinimumLevel: LogEventLevel.Debug,
|
||||
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
|
||||
levelSwitch: _fileLevelSwitch
|
||||
levelSwitch: fileLevelSwitch
|
||||
);
|
||||
|
||||
// Instantiate the root logger
|
||||
|
|
@ -214,9 +242,9 @@ public static class Debug
|
|||
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));
|
||||
|
||||
_consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
|
||||
consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
|
||||
{
|
||||
LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", consoleLoggingLevelSwitch.MinimumLevel);
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
|
@ -238,6 +266,12 @@ public static class Debug
|
|||
return doNotLoad;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the LoggerConfiguration used by the Debug class.
|
||||
/// This allows for changing logger settings such as sinks and output templates.
|
||||
/// After calling this method, the new configuration will be used for all subsequent log messages.
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public static void UpdateLoggerConfiguration(LoggerConfiguration config)
|
||||
{
|
||||
_loggerConfiguration = config;
|
||||
|
|
@ -245,6 +279,9 @@ public static class Debug
|
|||
_logger = config.CreateLogger();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LoggerConfiguration to the default configuration defined in this class.
|
||||
/// </summary>
|
||||
public static void ResetLoggerConfiguration()
|
||||
{
|
||||
_loggerConfiguration = _defaultLoggerConfiguration;
|
||||
|
|
@ -332,7 +369,11 @@ public static class Debug
|
|||
if (levelString.Trim() == "?")
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
|
||||
"Used to set the minimum level of debug messages to be printed to the console:\r\n" +
|
||||
"[LogLevel] [TimeoutInMinutes]\r\n" +
|
||||
"If TimeoutInMinutes is not provided, it will default to 120 minutes. If provided, the level will reset to Information after the timeout period elapses.\r\n" +
|
||||
"LogLevel can be either a number from 0-5 or a log level name. If using a number, the mapping is as follows:\r\n" +
|
||||
$"{_logLevels[0]} = 0\r\n" +
|
||||
$"{_logLevels[1]} = 1\r\n" +
|
||||
$"{_logLevels[2]} = 2\r\n" +
|
||||
|
|
@ -344,10 +385,22 @@ public static class Debug
|
|||
|
||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", consoleLoggingLevelSwitch.MinimumLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
// split on space to allow for potential future addition of timeout parameter without breaking existing command usage
|
||||
var parts = Regex.Split(levelString.Trim(), @"\s+");
|
||||
levelString = parts[0];
|
||||
|
||||
if (parts.Length > 1 && long.TryParse(parts[1], out var timeout))
|
||||
{
|
||||
timeout = Math.Max(timeout, 1); // enforce minimum timeout of 1 minute
|
||||
consoleDebugTimer.Interval = timeout * 60000;
|
||||
}
|
||||
|
||||
// first try to parse as int for backward compatibility with existing usage of numeric levels
|
||||
|
||||
if (int.TryParse(levelString, out var levelInt))
|
||||
{
|
||||
if (levelInt < 0 || levelInt > 5)
|
||||
|
|
@ -377,7 +430,8 @@ public static class Debug
|
|||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"> Valid values 0-5</param>
|
||||
public static void SetDebugLevel(uint level)
|
||||
/// <param name="timeout"> Timeout in minutes</param>
|
||||
public static void SetDebugLevel(uint level, int timeout = defaultConsoleDebugTimeoutMin)
|
||||
{
|
||||
if (!_logLevels.TryGetValue(level, out var logLevel))
|
||||
{
|
||||
|
|
@ -385,18 +439,27 @@ public static class Debug
|
|||
|
||||
CrestronConsole.ConsoleCommandResponse($"{level} not valid. Setting level to {logLevel}");
|
||||
|
||||
SetDebugLevel(logLevel);
|
||||
SetDebugLevel(logLevel, timeout);
|
||||
}
|
||||
|
||||
SetDebugLevel(logLevel);
|
||||
SetDebugLevel(logLevel, timeout);
|
||||
}
|
||||
|
||||
public static void SetDebugLevel(LogEventLevel level)
|
||||
/// <summary>
|
||||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"> The log level to set</param>
|
||||
/// <param name="timeout"> Timeout in minutes</param>
|
||||
public static void SetDebugLevel(LogEventLevel level, int timeout = defaultConsoleDebugTimeoutMin)
|
||||
{
|
||||
_consoleLoggingLevelSwitch.MinimumLevel = level;
|
||||
consoleDebugTimer.Stop();
|
||||
consoleDebugTimer.Interval = timeout * 60000;
|
||||
consoleDebugTimer.Start();
|
||||
|
||||
consoleLoggingLevelSwitch.MinimumLevel = level;
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n",
|
||||
InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel);
|
||||
InitialParametersClass.ApplicationNumber, consoleLoggingLevelSwitch.MinimumLevel);
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
|
||||
|
||||
|
|
@ -408,40 +471,52 @@ public static class Debug
|
|||
CrestronConsole.PrintLine($"Error saving console debug level setting: {err}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the debug level for the websocket sink
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_websocketLoggingLevelSwitch.MinimumLevel = level;
|
||||
websocketLoggingLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", websocketLoggingLevelSwitch.MinimumLevel);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum debug level for the error log sink
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
public static void SetErrorLogMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
errorLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", websocketLoggingLevelSwitch.MinimumLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the minimum debug level for the file sink
|
||||
/// </summary>
|
||||
public static void SetFileMinimumDebugLevel(LogEventLevel level)
|
||||
{
|
||||
_errorLogLevelSwitch.MinimumLevel = level;
|
||||
errorLogLevelSwitch.MinimumLevel = level;
|
||||
|
||||
var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
|
||||
|
||||
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||
LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
|
||||
|
||||
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
|
||||
LogMessage(LogEventLevel.Information, "File debug level set to {0}", websocketLoggingLevelSwitch.MinimumLevel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -626,21 +701,45 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with at the specified level.
|
||||
/// </summary>
|
||||
public static void LogMessage(LogEventLevel level, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(level, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with at the specified level and exception.
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogMessage(LogEventLevel level, Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(level, ex, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with at the specified level and device context.
|
||||
/// </summary> <param name="level"></param>
|
||||
/// <param name="keyed"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogMessage(LogEventLevel level, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
LogMessage(level, message, keyed, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with at the specified level, exception, and device context.
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="device"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogMessage(LogEventLevel level, Exception ex, IKeyed device, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", device?.Key))
|
||||
|
|
@ -650,6 +749,13 @@ public static class Debug
|
|||
}
|
||||
|
||||
#region Explicit methods for logging levels
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Verbose level and device context.
|
||||
/// </summary>
|
||||
/// <param name="keyed"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogVerbose(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -658,6 +764,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Verbose level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogVerbose(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -666,16 +775,25 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Verbose level.
|
||||
/// </summary>
|
||||
public static void LogVerbose(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Verbose, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Verbose level and exception.
|
||||
/// </summary>
|
||||
public static void LogVerbose(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Verbose, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Debug level and device context.
|
||||
/// </summary>
|
||||
public static void LogDebug(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -684,6 +802,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Debug level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogDebug(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -692,16 +813,25 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Debug level.
|
||||
/// </summary>
|
||||
public static void LogDebug(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Debug, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Debug level and exception.
|
||||
/// </summary>
|
||||
public static void LogDebug(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Debug, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Information level and device context.
|
||||
/// </summary>
|
||||
public static void LogInformation(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -710,6 +840,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Information level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogInformation(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -718,16 +851,25 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Information level.
|
||||
/// </summary>
|
||||
public static void LogInformation(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Information, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Information level and exception.
|
||||
/// </summary>
|
||||
public static void LogInformation(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Information, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Warning level and device context.
|
||||
/// </summary>
|
||||
public static void LogWarning(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -736,6 +878,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Warning level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogWarning(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -744,16 +889,25 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Warning level.
|
||||
/// </summary>
|
||||
public static void LogWarning(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Warning, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Warning level and exception.
|
||||
/// </summary>
|
||||
public static void LogWarning(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Warning, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Error level and device context.
|
||||
/// </summary>
|
||||
public static void LogError(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -762,6 +916,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Error level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogError(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -770,16 +927,25 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Error level.
|
||||
/// </summary>
|
||||
public static void LogError(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Error, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Error level and exception.
|
||||
/// </summary>
|
||||
public static void LogError(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Error, ex, null, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Fatal level and device context.
|
||||
/// </summary>
|
||||
public static void LogFatal(IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -788,6 +954,9 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Fatal level, exception, and device context.
|
||||
/// </summary>
|
||||
public static void LogFatal(Exception ex, IKeyed keyed, string message, params object[] args)
|
||||
{
|
||||
using (LogContext.PushProperty("Key", keyed?.Key))
|
||||
|
|
@ -796,11 +965,19 @@ public static class Debug
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Fatal level.
|
||||
/// </summary>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="args"></param>
|
||||
public static void LogFatal(string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Fatal, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log a message with Fatal level and exception.
|
||||
/// </summary>
|
||||
public static void LogFatal(Exception ex, string message, params object[] args)
|
||||
{
|
||||
_logger.Write(LogEventLevel.Fatal, ex, null, message, args);
|
||||
|
|
@ -828,127 +1005,28 @@ public static class Debug
|
|||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
||||
/// Uses CrestronConsole.PrintLine.
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="format">Console format string</param>
|
||||
/// <param name="items">Object parameters</param>
|
||||
[Obsolete("Use LogMessage methods. Will be removed in 2.2.0 and later versions")]
|
||||
public static void Console(uint level, string format, params object[] items)
|
||||
{
|
||||
|
||||
LogMessage(level, 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));
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs to Console when at-level, and all messages to error log, including device key
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
LogMessage(level, dev, format, items);
|
||||
|
||||
//if (Level >= level)
|
||||
// Console(level, "[{0}] {1}", dev.Key, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
|
||||
/// Uses CrestronConsole.PrintLine.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
LogMessage(level, dev, format, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs to Console when at-level, and all messages to error log
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
LogMessage(level, format, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
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);
|
||||
// CrestronLogger.WriteToLog(str, level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
LogMessage(level, dev, format, items);
|
||||
|
||||
// var str = string.Format(format, items);
|
||||
// CrestronLogger.WriteToLog(string.Format("[{0}] {1}", dev.Key, str), level);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints to log and error log
|
||||
/// </summary>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="str"></param>
|
||||
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
|
||||
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
||||
{
|
||||
switch (errorLogLevel)
|
||||
{
|
||||
case ErrorLogLevel.Error:
|
||||
LogMessage(LogEventLevel.Error, str);
|
||||
break;
|
||||
case ErrorLogLevel.Warning:
|
||||
LogMessage(LogEventLevel.Warning, str);
|
||||
break;
|
||||
case ErrorLogLevel.Notice:
|
||||
LogMessage(LogEventLevel.Information, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the memory object after timeout
|
||||
/// </summary>
|
||||
static void SaveMemoryOnTimeout()
|
||||
{
|
||||
Console(0, "Saving debug settings");
|
||||
LogInformation("Saving debug settings");
|
||||
if (_saveTimer == null)
|
||||
_saveTimer = new CTimer(o =>
|
||||
{
|
||||
_saveTimer = new Timer(SaveTimeoutMs) { AutoReset = false };
|
||||
_saveTimer.Elapsed += (s, e) =>
|
||||
{
|
||||
_saveTimer = null;
|
||||
SaveMemory();
|
||||
}, SaveTimeoutMs);
|
||||
};
|
||||
_saveTimer.Start();
|
||||
}
|
||||
else
|
||||
_saveTimer.Reset(SaveTimeoutMs);
|
||||
{
|
||||
_saveTimer.Stop();
|
||||
_saveTimer.Interval = SaveTimeoutMs;
|
||||
_saveTimer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -964,7 +1042,7 @@ public static class Debug
|
|||
{
|
||||
var fileName = GetMemoryFileName();
|
||||
|
||||
LogMessage(LogEventLevel.Information, "Loading debug settings file from {fileName}", fileName);
|
||||
LogInformation("Loading debug settings file from {fileName}", fileName);
|
||||
|
||||
using (var sw = new StreamWriter(fileName))
|
||||
{
|
||||
|
|
@ -1018,27 +1096,4 @@ public static class Debug
|
|||
|
||||
return string.Format("{0}{1}user{1}debugSettings{1}{2}.json", Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Error level to for message to be logged at
|
||||
/// </summary>
|
||||
public enum ErrorLogLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Error
|
||||
/// </summary>
|
||||
Error,
|
||||
/// <summary>
|
||||
/// Warning
|
||||
/// </summary>
|
||||
Warning,
|
||||
/// <summary>
|
||||
/// Notice
|
||||
/// </summary>
|
||||
Notice,
|
||||
/// <summary>
|
||||
/// None
|
||||
/// </summary>
|
||||
None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,283 +0,0 @@
|
|||
extern alias NewtonsoftJson;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
|
||||
using JsonConvert = NewtonsoftJson::Newtonsoft.Json.JsonConvert;
|
||||
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a debugging context
|
||||
/// </summary>
|
||||
public class DebugContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
|
||||
///// <summary>
|
||||
///// The name of the file containing the current debug settings.
|
||||
///// </summary>
|
||||
//string FileName = string.Format(@"\nvram\debug\app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
||||
|
||||
DebugContextSaveData SaveData;
|
||||
|
||||
int SaveTimeoutMs = 30000;
|
||||
|
||||
CTimer SaveTimer;
|
||||
|
||||
|
||||
static List<DebugContext> Contexts = new List<DebugContext>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates or gets a debug context
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public static DebugContext GetDebugContext(string key)
|
||||
{
|
||||
var context = Contexts.FirstOrDefault(c => c.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
if (context == null)
|
||||
{
|
||||
context = new DebugContext(key);
|
||||
Contexts.Add(context);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Do not use. For S+ access.
|
||||
/// </summary>
|
||||
public DebugContext() { }
|
||||
|
||||
DebugContext(string key)
|
||||
{
|
||||
Key = key;
|
||||
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);
|
||||
}
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||
|
||||
LoadMemory();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to save memory when shutting down
|
||||
/// </summary>
|
||||
/// <param name="programEventType"></param>
|
||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
{
|
||||
if (programEventType == eProgramStatusEventType.Stopping)
|
||||
{
|
||||
if (SaveTimer != null)
|
||||
{
|
||||
SaveTimer.Stop();
|
||||
SaveTimer = null;
|
||||
}
|
||||
Console(0, "Saving debug settings");
|
||||
SaveMemory();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback for console command
|
||||
/// </summary>
|
||||
/// <param name="levelString"></param>
|
||||
public void SetDebugFromConsole(string levelString)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", SaveData.Level);
|
||||
return;
|
||||
}
|
||||
|
||||
SetDebugLevel(Convert.ToInt32(levelString));
|
||||
}
|
||||
catch
|
||||
{
|
||||
CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the debug level
|
||||
/// </summary>
|
||||
/// <param name="level"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>
|
||||
public void SetDebugLevel(int level)
|
||||
{
|
||||
if (level <= 2)
|
||||
{
|
||||
SaveData.Level = level;
|
||||
SaveMemoryOnTimeout();
|
||||
|
||||
CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
|
||||
InitialParametersClass.ApplicationNumber, SaveData.Level);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
||||
/// Uses CrestronConsole.PrintLine.
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="format">Console format string</param>
|
||||
/// <param name="items">Object parameters</param>
|
||||
public void Console(uint level, string format, params object[] items)
|
||||
{
|
||||
if (SaveData.Level >= level)
|
||||
CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber,
|
||||
string.Format(format, items));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a device Key to the beginning of a message
|
||||
/// </summary>
|
||||
public void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||
{
|
||||
if (SaveData.Level >= level)
|
||||
Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="dev"></param>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="items"></param>
|
||||
public void Console(uint level, IKeyed dev, Debug.ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
if (SaveData.Level >= level)
|
||||
{
|
||||
var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
|
||||
Console(level, str);
|
||||
LogError(errorLogLevel, str);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="level"></param>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="format"></param>
|
||||
/// <param name="items"></param>
|
||||
public void Console(uint level, Debug.ErrorLogLevel errorLogLevel,
|
||||
string format, params object[] items)
|
||||
{
|
||||
if (SaveData.Level >= level)
|
||||
{
|
||||
var str = string.Format(format, items);
|
||||
Console(level, str);
|
||||
LogError(errorLogLevel, str);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="errorLogLevel"></param>
|
||||
/// <param name="str"></param>
|
||||
public void LogError(Debug.ErrorLogLevel errorLogLevel, string str)
|
||||
{
|
||||
string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||
switch (errorLogLevel)
|
||||
{
|
||||
case Debug.ErrorLogLevel.Error:
|
||||
ErrorLog.Error(msg);
|
||||
break;
|
||||
case Debug.ErrorLogLevel.Warning:
|
||||
ErrorLog.Warn(msg);
|
||||
break;
|
||||
case Debug.ErrorLogLevel.Notice:
|
||||
ErrorLog.Notice(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the memory object after timeout
|
||||
/// </summary>
|
||||
void SaveMemoryOnTimeout()
|
||||
{
|
||||
if (SaveTimer == null)
|
||||
SaveTimer = new CTimer(o =>
|
||||
{
|
||||
SaveTimer = null;
|
||||
SaveMemory();
|
||||
}, SaveTimeoutMs);
|
||||
else
|
||||
SaveTimer.Reset(SaveTimeoutMs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the memory - use SaveMemoryOnTimeout
|
||||
/// </summary>
|
||||
void SaveMemory()
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(GetMemoryFileName()))
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(SaveData);
|
||||
sw.Write(json);
|
||||
sw.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
void LoadMemory()
|
||||
{
|
||||
var file = GetMemoryFileName();
|
||||
if (File.Exists(file))
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(file))
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<DebugContextSaveData>(sr.ReadToEnd());
|
||||
if (data != null)
|
||||
{
|
||||
SaveData = data;
|
||||
Debug.Console(1, "Debug memory restored from file");
|
||||
return;
|
||||
}
|
||||
else
|
||||
SaveData = new DebugContextSaveData();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to get the file path for this app's debug memory
|
||||
/// </summary>
|
||||
string GetMemoryFileName()
|
||||
{
|
||||
return string.Format(@"\NVRAM\debugSettings\program{0}-{1}", InitialParametersClass.ApplicationNumber, Key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class DebugContextSaveData
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public int Level { get; set; }
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
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);
|
||||
|
||||
CrestronConsole.PrintLine(string.Format("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress));
|
||||
Debug.LogInformation("DomainName: {0} | HostName: {1} | {1}.{0}@{2}", domainName, hostName, ipAddress);
|
||||
|
||||
var certificate = utility.CreateSelfSignedCertificate(string.Format("CN={0}.{1}", hostName, domainName), [string.Format("{0}.{1}", hostName, domainName), ipAddress], [KeyPurposeID.id_kp_serverAuth, KeyPurposeID.id_kp_clientAuth]);
|
||||
|
||||
|
|
@ -118,8 +118,8 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
//Debug.Console(0, "WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
|
||||
CrestronConsole.PrintLine("WSS CreateCert Failed\r\n{0}\r\n{1}", ex.Message, ex.StackTrace);
|
||||
Debug.LogError(ex, "WSS CreateCert Failed: {0}", ex.Message);
|
||||
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
/// <param name="port">The port number on which the WebSocket server will listen. Must be a valid, non-negative port number.</param>
|
||||
public void StartServerAndSetPort(int port)
|
||||
{
|
||||
Debug.Console(0, "Starting Websocket Server on port: {0}", port);
|
||||
Debug.LogInformation("Starting Websocket Server on port: {0}", port);
|
||||
|
||||
|
||||
Start(port, $"\\user\\{_certificateName}.pfx", _certificatePassword);
|
||||
|
|
@ -163,7 +163,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
|
||||
if (!string.IsNullOrWhiteSpace(certPath))
|
||||
{
|
||||
Debug.Console(0, "Assigning SSL Configuration");
|
||||
Debug.LogInformation("Assigning SSL Configuration");
|
||||
|
||||
_httpsServer.SslConfiguration.ServerCertificate = new X509Certificate2(certPath, certPassword);
|
||||
_httpsServer.SslConfiguration.ClientCertificateRequired = false;
|
||||
|
|
@ -172,13 +172,13 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
//this is just to test, you might want to actually validate
|
||||
_httpsServer.SslConfiguration.ClientCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) =>
|
||||
{
|
||||
Debug.Console(0, "HTTPS ClientCerticateValidation Callback triggered");
|
||||
Debug.LogInformation("HTTPS ClientCerticateValidation Callback triggered");
|
||||
return true;
|
||||
};
|
||||
}
|
||||
Debug.Console(0, "Adding Debug Client Service");
|
||||
Debug.LogInformation("Adding Debug Client Service");
|
||||
_httpsServer.AddWebSocketService<DebugClient>(_path);
|
||||
Debug.Console(0, "Assigning Log Info");
|
||||
Debug.LogInformation("Assigning Log Info");
|
||||
_httpsServer.Log.Level = LogLevel.Trace;
|
||||
_httpsServer.Log.Output = (d, s) =>
|
||||
{
|
||||
|
|
@ -208,17 +208,17 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
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.LogInformation("{1} {0}\rCaller:{2}\rMessage:{3}\rs:{4}", d.Level.ToString(), d.Date.ToString(), d.Caller.ToString(), d.Message, s);
|
||||
};
|
||||
Debug.Console(0, "Starting");
|
||||
Debug.LogInformation("Starting");
|
||||
|
||||
_httpsServer.Start();
|
||||
Debug.Console(0, "Ready");
|
||||
Debug.LogInformation("Ready");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(0, "WebSocket Failed to start {0}", ex.Message);
|
||||
Debug.LogError(ex, "WebSocket Failed to start {0}", ex.Message);
|
||||
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -229,7 +229,7 @@ public class DebugWebsocketSink : ILogEventSink, IKeyed
|
|||
/// calling this method, the server will no longer accept or process incoming connections.</remarks>
|
||||
public void StopServer()
|
||||
{
|
||||
Debug.Console(0, "Stopping Websocket Server");
|
||||
Debug.LogInformation("Stopping Websocket Server");
|
||||
_httpsServer?.Stop();
|
||||
|
||||
_httpsServer = null;
|
||||
|
|
@ -291,20 +291,18 @@ public class DebugClient : WebSocketBehavior
|
|||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DebugClient"/> class.
|
||||
/// </summary>
|
||||
/// <remarks>This constructor creates a new <see cref="DebugClient"/> instance and logs its
|
||||
/// creation using the <see cref="Debug.Console(int, string)"/> method with a debug level of 0.</remarks>
|
||||
public DebugClient()
|
||||
{
|
||||
Debug.Console(0, "DebugClient Created");
|
||||
Debug.LogInformation("DebugClient Created");
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
|
||||
var url = Context.WebSocket.Url;
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Notice, "New WebSocket Connection from: {0}", url);
|
||||
Debug.LogInformation("New WebSocket Connection from: {0}", url);
|
||||
|
||||
_connectionTime = DateTime.Now;
|
||||
}
|
||||
|
|
@ -314,7 +312,7 @@ public class DebugClient : WebSocketBehavior
|
|||
{
|
||||
base.OnMessage(e);
|
||||
|
||||
Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
|
||||
Debug.LogVerbose("WebSocket UiClient Message: {0}", e.Data);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -322,8 +320,7 @@ public class DebugClient : WebSocketBehavior
|
|||
{
|
||||
base.OnClose(e);
|
||||
|
||||
Debug.Console(0, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
|
||||
|
||||
Debug.LogDebug("WebSocket UiClient Closing: {0} reason: {1}", e.Code, e.Reason);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -331,6 +328,7 @@ public class DebugClient : WebSocketBehavior
|
|||
{
|
||||
base.OnError(e);
|
||||
|
||||
Debug.Console(2, Debug.ErrorLogLevel.Notice, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
|
||||
Debug.LogError(e.Exception, "WebSocket UiClient Error: {0} message: {1}", e.Exception, e.Message);
|
||||
Debug.LogVerbose("Stack Trace:\r{0}", e.Exception.StackTrace);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,246 +1,252 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using System.Timers;
|
||||
|
||||
namespace PepperDash.Core.PasswordManagement;
|
||||
|
||||
/// <summary>
|
||||
/// Allows passwords to be stored and managed
|
||||
/// </summary>
|
||||
public class PasswordManager
|
||||
public class PasswordManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Public dictionary of known passwords
|
||||
/// </summary>
|
||||
public static Dictionary<uint, string> Passwords = new Dictionary<uint, string>();
|
||||
/// <summary>
|
||||
/// Private dictionary, used when passwords are updated
|
||||
/// </summary>
|
||||
private Dictionary<uint, string> _passwords = new Dictionary<uint, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Timer used to wait until password changes have stopped before updating the dictionary
|
||||
/// </summary>
|
||||
Timer PasswordTimer;
|
||||
/// <summary>
|
||||
/// Timer length
|
||||
/// </summary>
|
||||
public long PasswordTimerElapsedMs = 5000;
|
||||
|
||||
/// <summary>
|
||||
/// Boolean event
|
||||
/// </summary>
|
||||
public event EventHandler<BoolChangeEventArgs> BoolChange;
|
||||
/// <summary>
|
||||
/// Ushort event
|
||||
/// </summary>
|
||||
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
|
||||
/// <summary>
|
||||
/// String event
|
||||
/// </summary>
|
||||
public event EventHandler<StringChangeEventArgs> StringChange;
|
||||
/// <summary>
|
||||
/// Event to notify clients of an updated password at the specified index (uint)
|
||||
/// </summary>
|
||||
public static event EventHandler<StringChangeEventArgs> PasswordChange;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public PasswordManager()
|
||||
{
|
||||
/// <summary>
|
||||
/// Public dictionary of known passwords
|
||||
/// </summary>
|
||||
public static Dictionary<uint, string> Passwords = new Dictionary<uint, string>();
|
||||
/// <summary>
|
||||
/// Private dictionary, used when passwords are updated
|
||||
/// </summary>
|
||||
private Dictionary<uint, string> _passwords = new Dictionary<uint, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Timer used to wait until password changes have stopped before updating the dictionary
|
||||
/// </summary>
|
||||
CTimer PasswordTimer;
|
||||
/// <summary>
|
||||
/// Timer length
|
||||
/// </summary>
|
||||
public long PasswordTimerElapsedMs = 5000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Boolean event
|
||||
/// </summary>
|
||||
public event EventHandler<BoolChangeEventArgs> BoolChange;
|
||||
/// <summary>
|
||||
/// Ushort event
|
||||
/// </summary>
|
||||
public event EventHandler<UshrtChangeEventArgs> UshrtChange;
|
||||
/// <summary>
|
||||
/// String event
|
||||
/// </summary>
|
||||
public event EventHandler<StringChangeEventArgs> StringChange;
|
||||
/// <summary>
|
||||
/// Event to notify clients of an updated password at the specified index (uint)
|
||||
/// </summary>
|
||||
public static event EventHandler<StringChangeEventArgs> PasswordChange;
|
||||
/// <summary>
|
||||
/// Initialize password manager
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
{
|
||||
if (Passwords == null)
|
||||
Passwords = new Dictionary<uint, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public PasswordManager()
|
||||
if (_passwords == null)
|
||||
_passwords = new Dictionary<uint, string>();
|
||||
|
||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates password stored in the dictonary
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <summary>
|
||||
/// UpdatePassword method
|
||||
/// </summary>
|
||||
public void UpdatePassword(ushort key, string password)
|
||||
{
|
||||
// validate the parameters
|
||||
if (key > 0 && string.IsNullOrEmpty(password))
|
||||
{
|
||||
|
||||
Debug.LogDebug("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password);
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize password manager
|
||||
/// </summary>
|
||||
public void Initialize()
|
||||
try
|
||||
{
|
||||
if (Passwords == null)
|
||||
Passwords = new Dictionary<uint, string>();
|
||||
// if key exists, update the value
|
||||
if (_passwords.ContainsKey(key))
|
||||
_passwords[key] = password;
|
||||
// else add the key & value
|
||||
else
|
||||
_passwords.Add(key, password);
|
||||
|
||||
if (_passwords == null)
|
||||
_passwords = new Dictionary<uint, string>();
|
||||
Debug.LogDebug("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]);
|
||||
|
||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates password stored in the dictonary
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <summary>
|
||||
/// UpdatePassword method
|
||||
/// </summary>
|
||||
public void UpdatePassword(ushort key, string password)
|
||||
{
|
||||
// validate the parameters
|
||||
if (key > 0 && string.IsNullOrEmpty(password))
|
||||
if (PasswordTimer == null)
|
||||
{
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password));
|
||||
return;
|
||||
PasswordTimer = new Timer(PasswordTimerElapsedMs) { AutoReset = false };
|
||||
PasswordTimer.Elapsed += (s, e) => PasswordTimerElapsed(s, e);
|
||||
PasswordTimer.Start();
|
||||
Debug.LogDebug("PasswordManager.UpdatePassword: Timer Started");
|
||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// if key exists, update the value
|
||||
if(_passwords.ContainsKey(key))
|
||||
_passwords[key] = password;
|
||||
// else add the key & value
|
||||
else
|
||||
_passwords.Add(key, password);
|
||||
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]));
|
||||
|
||||
if (PasswordTimer == null)
|
||||
{
|
||||
PasswordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
|
||||
OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
|
||||
}
|
||||
else
|
||||
{
|
||||
PasswordTimer.Reset(PasswordTimerElapsedMs);
|
||||
Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
|
||||
Debug.Console(1, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CTimer callback function
|
||||
/// </summary>
|
||||
private void PasswordTimerElapsed()
|
||||
{
|
||||
try
|
||||
else
|
||||
{
|
||||
PasswordTimer.Stop();
|
||||
Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: CTimer Stopped"));
|
||||
OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
|
||||
foreach (var pw in _passwords)
|
||||
PasswordTimer.Interval = PasswordTimerElapsedMs;
|
||||
PasswordTimer.Start();
|
||||
Debug.LogDebug("PasswordManager.UpdatePassword: Timer Reset");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
|
||||
Debug.LogError(e, msg);
|
||||
Debug.LogVerbose("Stack Trace:\r{0}", e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Timer callback function
|
||||
/// </summary>
|
||||
private void PasswordTimerElapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
PasswordTimer.Stop();
|
||||
Debug.LogDebug("PasswordManager.PasswordTimerElapsed: Timer Stopped");
|
||||
OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
|
||||
foreach (var pw in _passwords)
|
||||
{
|
||||
// if key exists, continue
|
||||
if (Passwords.ContainsKey(pw.Key))
|
||||
{
|
||||
// if key exists, continue
|
||||
if (Passwords.ContainsKey(pw.Key))
|
||||
Debug.LogDebug("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value);
|
||||
if (Passwords[pw.Key] != _passwords[pw.Key])
|
||||
{
|
||||
Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value));
|
||||
if (Passwords[pw.Key] != _passwords[pw.Key])
|
||||
{
|
||||
Passwords[pw.Key] = _passwords[pw.Key];
|
||||
Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]));
|
||||
OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
|
||||
}
|
||||
}
|
||||
// else add the key & value
|
||||
else
|
||||
{
|
||||
Passwords.Add(pw.Key, pw.Value);
|
||||
Passwords[pw.Key] = _passwords[pw.Key];
|
||||
Debug.LogDebug("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]);
|
||||
OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
|
||||
}
|
||||
}
|
||||
OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", e);
|
||||
Debug.Console(1, msg);
|
||||
// else add the key & value
|
||||
else
|
||||
{
|
||||
Passwords.Add(pw.Key, pw.Value);
|
||||
}
|
||||
}
|
||||
OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to change the default timer value, (default 5000ms/5s)
|
||||
/// </summary>
|
||||
/// <param name="time"></param>
|
||||
/// <summary>
|
||||
/// PasswordTimerMs method
|
||||
/// </summary>
|
||||
public void PasswordTimerMs(ushort time)
|
||||
catch (Exception ex)
|
||||
{
|
||||
PasswordTimerElapsedMs = Convert.ToInt64(time);
|
||||
var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", ex.Message);
|
||||
Debug.LogError(ex, msg);
|
||||
Debug.LogVerbose("Stack Trace:\r{0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper method for debugging to see what passwords are in the lists
|
||||
/// </summary>
|
||||
public void ListPasswords()
|
||||
{
|
||||
Debug.Console(0, "PasswordManager.ListPasswords:\r");
|
||||
foreach (var pw in Passwords)
|
||||
Debug.Console(0, "Passwords[{0}]: {1}\r", pw.Key, pw.Value);
|
||||
Debug.Console(0, "\n");
|
||||
foreach (var pw in _passwords)
|
||||
Debug.Console(0, "_passwords[{0}]: {1}\r", pw.Key, pw.Value);
|
||||
}
|
||||
/// <summary>
|
||||
/// Method to change the default timer value, (default 5000ms/5s)
|
||||
/// </summary>
|
||||
/// <param name="time"></param>
|
||||
/// <summary>
|
||||
/// PasswordTimerMs method
|
||||
/// </summary>
|
||||
public void PasswordTimerMs(ushort time)
|
||||
{
|
||||
PasswordTimerElapsedMs = Convert.ToInt64(time);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected boolean change event handler
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnBoolChange(bool state, ushort index, ushort type)
|
||||
{
|
||||
var handler = BoolChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new BoolChangeEventArgs(state, type);
|
||||
args.Index = index;
|
||||
BoolChange(this, args);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Helper method for debugging to see what passwords are in the lists
|
||||
/// </summary>
|
||||
public void ListPasswords()
|
||||
{
|
||||
Debug.LogInformation("PasswordManager.ListPasswords:\r");
|
||||
foreach (var pw in Passwords)
|
||||
Debug.LogInformation("Passwords[{0}]: {1}\r", pw.Key, pw.Value);
|
||||
Debug.LogInformation("\n");
|
||||
foreach (var pw in _passwords)
|
||||
Debug.LogInformation("_passwords[{0}]: {1}\r", pw.Key, pw.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected ushort change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnUshrtChange(ushort value, ushort index, ushort type)
|
||||
/// <summary>
|
||||
/// Protected boolean change event handler
|
||||
/// </summary>
|
||||
/// <param name="state"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnBoolChange(bool state, ushort index, ushort type)
|
||||
{
|
||||
var handler = BoolChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var handler = UshrtChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new UshrtChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
UshrtChange(this, args);
|
||||
}
|
||||
var args = new BoolChangeEventArgs(state, type);
|
||||
args.Index = index;
|
||||
BoolChange(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected string change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnStringChange(string value, ushort index, ushort type)
|
||||
/// <summary>
|
||||
/// Protected ushort change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnUshrtChange(ushort value, ushort index, ushort type)
|
||||
{
|
||||
var handler = UshrtChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var handler = StringChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new StringChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
StringChange(this, args);
|
||||
}
|
||||
var args = new UshrtChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
UshrtChange(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected password change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnPasswordChange(string value, ushort index, ushort type)
|
||||
/// <summary>
|
||||
/// Protected string change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnStringChange(string value, ushort index, ushort type)
|
||||
{
|
||||
var handler = StringChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var handler = PasswordChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new StringChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
PasswordChange(this, args);
|
||||
}
|
||||
var args = new StringChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
StringChange(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Protected password change event handler
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="type"></param>
|
||||
protected void OnPasswordChange(string value, ushort index, ushort type)
|
||||
{
|
||||
var handler = PasswordChange;
|
||||
if (handler != null)
|
||||
{
|
||||
var args = new StringChangeEventArgs(value, type);
|
||||
args.Index = index;
|
||||
PasswordChange(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue