mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-04-12 12:06:58 +00:00
refactor: Refactor DeviceManager and related classes to improve thread safety and performance
- Replaced CCriticalSection with lock statements in DeviceManager for better thread management. - Updated AddDevice and RemoveDevice methods to use Monitor for locking. - Enhanced event handling for device activation and registration. - Modified FileIO class to utilize Task for asynchronous file operations instead of CrestronInvoke. - Improved feedback mechanisms in FeedbackBase and SystemMonitorController using Task.Run. - Refactored GenericQueue to remove Crestron threading dependencies and utilize System.Threading. - Updated BlueJeansPc and VideoCodecBase classes to use Task for asynchronous operations. - Cleaned up unnecessary critical sections and improved code documentation across various files.
This commit is contained in:
parent
426ef4ad6b
commit
346a5e9e57
23 changed files with 998 additions and 912 deletions
|
|
@ -2,7 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using System.Timers;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
|
@ -20,7 +20,7 @@ public class CommunicationStreamDebugging
|
|||
/// <summary>
|
||||
/// Timer to disable automatically if not manually disabled
|
||||
/// </summary>
|
||||
private CTimer DebugExpiryPeriod;
|
||||
private Timer DebugExpiryPeriod;
|
||||
|
||||
/// <summary>
|
||||
/// The current debug setting
|
||||
|
|
@ -93,7 +93,9 @@ public class CommunicationStreamDebugging
|
|||
|
||||
StopDebugTimer();
|
||||
|
||||
DebugExpiryPeriod = new CTimer((o) => DisableDebugging(), _DebugTimeoutInMs);
|
||||
DebugExpiryPeriod = new Timer(_DebugTimeoutInMs) { AutoReset = false };
|
||||
DebugExpiryPeriod.Elapsed += (s, e) => DisableDebugging();
|
||||
DebugExpiryPeriod.Start();
|
||||
|
||||
if ((setting & eStreamDebuggingSetting.Rx) == eStreamDebuggingSetting.Rx)
|
||||
RxStreamDebuggingIsEnabled = true;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -277,7 +279,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
/// <summary>
|
||||
/// Queue lock
|
||||
/// </summary>
|
||||
CCriticalSection DequeueLock = new CCriticalSection();
|
||||
private readonly object _dequeueLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
|
||||
|
|
@ -696,9 +698,8 @@ 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)
|
||||
{
|
||||
var gotLock = DequeueLock.TryEnter();
|
||||
if (gotLock)
|
||||
CrestronInvoke.BeginInvoke((o) => DequeueEvent());
|
||||
if (Monitor.TryEnter(_dequeueLock))
|
||||
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
|
||||
|
|
@ -708,7 +709,7 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||
/// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
|
||||
/// It will dequeue items as they are enqueued automatically.
|
||||
/// </summary>
|
||||
void DequeueEvent()
|
||||
|
|
@ -730,11 +731,8 @@ public class GenericSecureTcpIpClient : Device, ISocketStatusWithStreamDebugging
|
|||
{
|
||||
this.LogError(e, "DequeueEvent error: {0}", e.Message);
|
||||
}
|
||||
// Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
|
||||
if (DequeueLock != null)
|
||||
{
|
||||
DequeueLock.Leave();
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
void HeartbeatStart()
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -271,7 +273,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
/// <summary>
|
||||
/// Queue lock
|
||||
/// </summary>
|
||||
CCriticalSection DequeueLock = new CCriticalSection();
|
||||
private readonly object _dequeueLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
|
||||
|
|
@ -655,9 +657,8 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
//Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
|
||||
if (handler != null)
|
||||
{
|
||||
var gotLock = DequeueLock.TryEnter();
|
||||
if (gotLock)
|
||||
CrestronInvoke.BeginInvoke((o) => DequeueEvent());
|
||||
if (Monitor.TryEnter(_dequeueLock))
|
||||
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
|
||||
|
|
@ -667,7 +668,7 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||
/// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
|
||||
/// It will dequeue items as they are enqueued automatically.
|
||||
/// </summary>
|
||||
void DequeueEvent()
|
||||
|
|
@ -689,11 +690,8 @@ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
|
|||
{
|
||||
this.LogError("DequeueEvent error: {0}", e.Message, e);
|
||||
}
|
||||
// Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
|
||||
if (DequeueLock != null)
|
||||
{
|
||||
DequeueLock.Leave();
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
void HeartbeatStart()
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
/*PepperDash Technology Corp.
|
||||
JAG
|
||||
Copyright: 2017
|
||||
------------------------------------
|
||||
***Notice of Ownership and Copyright***
|
||||
The material in which this notice appears is the property of PepperDash Technology Corporation,
|
||||
which claims copyright under the laws of the United States of America in the entire body of material
|
||||
and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
|
||||
of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
|
||||
PepperDash Technology Corporation reserves all rights under applicable laws.
|
||||
------------------------------------ */
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -69,12 +60,17 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// <summary>
|
||||
/// Server listen lock
|
||||
/// </summary>
|
||||
CCriticalSection ServerCCSection = new CCriticalSection();
|
||||
private readonly object _serverLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Queue lock
|
||||
/// </summary>
|
||||
CCriticalSection DequeueLock = new CCriticalSection();
|
||||
private readonly object _dequeueLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast lock
|
||||
/// </summary>
|
||||
private readonly object _broadcastLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Receive Queue size. Defaults to 20. Will set to 20 if QueueSize property is less than 20. Use constructor or set queue size property before
|
||||
|
|
@ -399,18 +395,19 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// </summary>
|
||||
public void Listen()
|
||||
{
|
||||
ServerCCSection.Enter();
|
||||
lock (_serverLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Port < 1 || Port > 65535)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
|
||||
this.LogError("Server '{0}': Invalid port", Key);
|
||||
ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
|
||||
this.LogError("Server '{0}': No Shared Key set", Key);
|
||||
ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
|
||||
return;
|
||||
}
|
||||
|
|
@ -434,22 +431,21 @@ public class GenericSecureTcpIpServer : Device
|
|||
SocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
||||
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error starting WaitForConnectionAsync {0}", status);
|
||||
this.LogError("Error starting WaitForConnectionAsync {0}", status);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerStopped = false;
|
||||
}
|
||||
OnServerStateChange(SecureServer.State);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
|
||||
ServerCCSection.Leave();
|
||||
this.LogInformation("Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerCCSection.Leave();
|
||||
ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
|
||||
this.LogException(ex, "{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -459,18 +455,18 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
||||
this.LogVerbose("Stopping Listener");
|
||||
if (SecureServer != null)
|
||||
{
|
||||
SecureServer.Stop();
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
|
||||
this.LogVerbose("Server State: {0}", SecureServer.State);
|
||||
OnServerStateChange(SecureServer.State);
|
||||
}
|
||||
ServerStopped = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
||||
this.LogException(ex, "Error stopping server. Error: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -483,11 +479,11 @@ public class GenericSecureTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
SecureServer.Disconnect(client);
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
||||
this.LogVerbose("Disconnected client index: {0}", client);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
||||
this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", client, ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
|
|
@ -495,7 +491,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// </summary>
|
||||
public void DisconnectAllClientsForShutdown()
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
|
||||
this.LogInformation("Disconnecting All Clients");
|
||||
if (SecureServer != null)
|
||||
{
|
||||
SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;
|
||||
|
|
@ -507,17 +503,17 @@ public class GenericSecureTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
SecureServer.Disconnect(i);
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
||||
this.LogInformation("Disconnected client index: {0}", i);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
||||
this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", i, ex.Message);
|
||||
}
|
||||
}
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
|
||||
this.LogInformation("Server Status: {0}", SecureServer.ServerSocketStatus);
|
||||
}
|
||||
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
||||
this.LogInformation("Disconnected All Clients");
|
||||
ConnectedClientsIndexes.Clear();
|
||||
|
||||
if (!ProgramIsStopping)
|
||||
|
|
@ -535,8 +531,8 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// <param name="text"></param>
|
||||
public void BroadcastText(string text)
|
||||
{
|
||||
CCriticalSection CCBroadcast = new CCriticalSection();
|
||||
CCBroadcast.Enter();
|
||||
lock (_broadcastLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ConnectedClientsIndexes.Count > 0)
|
||||
|
|
@ -552,13 +548,12 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
}
|
||||
}
|
||||
CCBroadcast.Leave();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CCBroadcast.Leave();
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
|
||||
this.LogException(ex, "Error Broadcasting messages from server. Error: {0}", ex.Message);
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -579,7 +574,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||
this.LogException(ex, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -603,7 +598,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
}
|
||||
Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
this.LogDebug("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
// Return Heartbeat
|
||||
SendTextToClient(HeartbeatStringToMatch, clientIndex);
|
||||
return remainingText;
|
||||
|
|
@ -618,13 +613,13 @@ public class GenericSecureTcpIpServer : Device
|
|||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
}
|
||||
Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
this.LogInformation("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
|
||||
this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
|
@ -636,11 +631,11 @@ public class GenericSecureTcpIpServer : Device
|
|||
/// <returns></returns>
|
||||
public string GetClientIPAddress(uint clientIndex)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
|
||||
this.LogInformation("GetClientIPAddress Index: {0}", clientIndex);
|
||||
if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
|
||||
{
|
||||
var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
|
||||
this.LogInformation("GetClientIPAddress IPAddreess: {0}", ipa);
|
||||
return ipa;
|
||||
|
||||
}
|
||||
|
|
@ -663,7 +658,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
clientIndex = (uint)o;
|
||||
address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
|
||||
this.LogInformation("Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
|
||||
address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
|
||||
|
||||
if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
|
|
@ -703,7 +698,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
// 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)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
|
||||
this.LogInformation("SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
|
||||
|
||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
||||
ConnectedClientsIndexes.Remove(clientIndex);
|
||||
|
|
@ -725,12 +720,12 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||
this.LogException(ex, "Error in Socket Status Change Callback. Error: {0}", ex.Message);
|
||||
}
|
||||
//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.
|
||||
CrestronInvoke.BeginInvoke(o => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)), null);
|
||||
Task.Run(() => onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -745,7 +740,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
|
||||
this.LogInformation("ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
|
||||
server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
|
||||
clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
||||
if (clientIndex != 0)
|
||||
|
|
@ -765,7 +760,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
|
||||
server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
this.LogInformation("Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -784,19 +779,19 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
|
||||
this.LogError("Client attempt faulty.");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||
this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex.Message);
|
||||
}
|
||||
|
||||
// Rearm the listner
|
||||
SocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
|
||||
if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||
{
|
||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Socket status connect callback status {0}", status);
|
||||
this.LogError("Socket status connect callback status {0}", status);
|
||||
if (status == SocketErrorCodes.SOCKET_CONNECTION_IN_PROGRESS)
|
||||
{
|
||||
// There is an issue where on a failed negotiation we need to stop and start the server. This should still leave connected clients intact.
|
||||
|
|
@ -833,7 +828,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
if (received != SharedKey)
|
||||
{
|
||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||
this.LogWarning("Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||
mySecureTCPServer.SendData(clientIndex, b, b.Length);
|
||||
mySecureTCPServer.Disconnect(clientIndex);
|
||||
|
||||
|
|
@ -844,7 +839,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
||||
mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
||||
OnServerClientReadyForCommunications(clientIndex);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||
this.LogInformation("Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
||||
{
|
||||
|
|
@ -857,7 +852,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
||||
this.LogException(ex, "Error Receiving data: {0}. Error: {1}", received, ex.Message);
|
||||
}
|
||||
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
||||
|
|
@ -865,9 +860,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)
|
||||
{
|
||||
var gotLock = DequeueLock.TryEnter();
|
||||
if (gotLock)
|
||||
CrestronInvoke.BeginInvoke((o) => DequeueEvent());
|
||||
if (Monitor.TryEnter(_dequeueLock))
|
||||
Task.Run(() => DequeueEvent());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -877,7 +871,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||
/// This method gets spooled up in its own thread an protected by a lock to prevent multiple threads from running concurrently.
|
||||
/// It will dequeue items as they are enqueued automatically.
|
||||
/// </summary>
|
||||
void DequeueEvent()
|
||||
|
|
@ -899,11 +893,8 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
this.LogError(e, "DequeueEvent error");
|
||||
}
|
||||
// Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
|
||||
if (DequeueLock != null)
|
||||
{
|
||||
DequeueLock.Leave();
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -974,7 +965,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
if (MonitorClient != null)
|
||||
MonitorClient.Disconnect();
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
|
||||
this.LogInformation("Program stopping. Closing server");
|
||||
KillServer();
|
||||
}
|
||||
}
|
||||
|
|
@ -1015,7 +1006,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
//MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
|
||||
MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
|
||||
this.LogInformation("Starting monitor check");
|
||||
|
||||
MonitorClient.Connect();
|
||||
// From here MonitorCLient either connects or hangs, MonitorClient will call back
|
||||
|
|
@ -1042,7 +1033,7 @@ public class GenericSecureTcpIpServer : Device
|
|||
{
|
||||
if (args.IsReady)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
|
||||
this.LogInformation("Monitor client connection success. Disconnecting in 2s");
|
||||
MonitorClientTimer.Stop();
|
||||
MonitorClientTimer = null;
|
||||
MonitorClientFailureCount = 0;
|
||||
|
|
@ -1063,13 +1054,13 @@ public class GenericSecureTcpIpServer : Device
|
|||
StopMonitorClient();
|
||||
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||
this.LogWarning("Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
||||
StartMonitorClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error,
|
||||
this.LogError(
|
||||
"\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
|
||||
MonitorClientMaxFailureCount);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using Org.BouncyCastle.Utilities;
|
||||
|
|
@ -135,8 +136,6 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
|
||||
CTimer ReconnectTimer;
|
||||
|
||||
//Lock object to prevent simulatneous connect/disconnect operations
|
||||
//private CCriticalSection connectLock = new CCriticalSection();
|
||||
private SemaphoreSlim connectLock = new SemaphoreSlim(1);
|
||||
|
||||
private bool DisconnectLogged = false;
|
||||
|
|
@ -435,7 +434,7 @@ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoR
|
|||
/// </summary>
|
||||
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o =>
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
|
||||
this.LogError("Disconnected by remote");
|
||||
|
|
|
|||
|
|
@ -4,17 +4,21 @@ using System;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using Timer = System.Timers.Timer;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
|
||||
using Required = NewtonsoftJson::Newtonsoft.Json.Required;
|
||||
using PepperDash.Core.Logging;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Core;
|
||||
|
||||
/// <summary>
|
||||
/// A class to handle basic TCP/IP communications with a server
|
||||
/// </summary>
|
||||
public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
|
||||
{
|
||||
private const string SplusKey = "Uninitialized TcpIpClient";
|
||||
/// <summary>
|
||||
|
|
@ -22,44 +26,44 @@ namespace PepperDash.Core;
|
|||
/// </summary>
|
||||
public CommunicationStreamDebugging StreamDebugging { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fires when data is received from the server and returns it as a Byte array
|
||||
/// </summary>
|
||||
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
||||
/// <summary>
|
||||
/// Fires when data is received from the server and returns it as a Byte array
|
||||
/// </summary>
|
||||
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Fires when data is received from the server and returns it as text
|
||||
/// </summary>
|
||||
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
||||
/// <summary>
|
||||
/// Fires when data is received from the server and returns it as text
|
||||
/// </summary>
|
||||
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||
|
||||
|
||||
private string _hostname;
|
||||
private string _hostname;
|
||||
|
||||
/// <summary>
|
||||
/// Address of server
|
||||
/// </summary>
|
||||
public string Hostname
|
||||
{
|
||||
get
|
||||
{
|
||||
return _hostname;
|
||||
}
|
||||
get
|
||||
{
|
||||
return _hostname;
|
||||
}
|
||||
|
||||
set
|
||||
set
|
||||
{
|
||||
_hostname = value;
|
||||
if (_client != null)
|
||||
{
|
||||
_hostname = value;
|
||||
if (_client != null)
|
||||
{
|
||||
_client.AddressClientConnectedTo = _hostname;
|
||||
}
|
||||
_client.AddressClientConnectedTo = _hostname;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port on server
|
||||
|
|
@ -81,19 +85,19 @@ namespace PepperDash.Core;
|
|||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The actual client class
|
||||
/// </summary>
|
||||
private TCPClient _client;
|
||||
/// <summary>
|
||||
/// The actual client class
|
||||
/// </summary>
|
||||
private TCPClient _client;
|
||||
|
||||
/// <summary>
|
||||
/// Bool showing if socket is connected
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
/// <summary>
|
||||
/// Bool showing if socket is connected
|
||||
/// </summary>
|
||||
public bool IsConnected
|
||||
{
|
||||
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// S+ helper for IsConnected
|
||||
/// </summary>
|
||||
|
|
@ -102,15 +106,15 @@ namespace PepperDash.Core;
|
|||
get { return (ushort)(IsConnected ? 1 : 0); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// _client socket status Read only
|
||||
/// </summary>
|
||||
public SocketStatus ClientStatus
|
||||
{
|
||||
get
|
||||
/// <summary>
|
||||
/// _client socket status Read only
|
||||
/// </summary>
|
||||
public SocketStatus ClientStatus
|
||||
{
|
||||
get
|
||||
{
|
||||
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
|
||||
}
|
||||
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -122,26 +126,26 @@ namespace PepperDash.Core;
|
|||
get { return (ushort)ClientStatus; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Status text shows the message associated with socket status
|
||||
/// </summary>
|
||||
public string ClientStatusText { get { return ClientStatus.ToString(); } }
|
||||
/// </summary>
|
||||
public string ClientStatusText { get { return ClientStatus.ToString(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Ushort representation of client status
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Ushort representation of client status
|
||||
/// </summary>
|
||||
[Obsolete]
|
||||
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
|
||||
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
|
||||
|
||||
/// <summary>
|
||||
/// Connection failure reason
|
||||
/// </summary>
|
||||
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
|
||||
/// <summary>
|
||||
/// Connection failure reason
|
||||
/// </summary>
|
||||
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// S+ helper for AutoReconnect
|
||||
|
|
@ -152,29 +156,29 @@ namespace PepperDash.Core;
|
|||
set { AutoReconnect = value == 1; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
/// <summary>
|
||||
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set only when the disconnect method is called
|
||||
/// </summary>
|
||||
bool DisconnectCalledByUser;
|
||||
/// <summary>
|
||||
/// Set only when the disconnect method is called
|
||||
/// </summary>
|
||||
bool DisconnectCalledByUser;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Connected
|
||||
{
|
||||
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Connected
|
||||
{
|
||||
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
|
||||
}
|
||||
|
||||
//Lock object to prevent simulatneous connect/disconnect operations
|
||||
private CCriticalSection connectLock = new CCriticalSection();
|
||||
private readonly object _connectLock = new();
|
||||
|
||||
// private Timer for auto reconnect
|
||||
private CTimer RetryTimer;
|
||||
private Timer RetryTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
|
|
@ -183,9 +187,9 @@ namespace PepperDash.Core;
|
|||
/// <param name="address"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="bufferSize"></param>
|
||||
public GenericTcpIpClient(string key, string address, int port, int bufferSize)
|
||||
: base(key)
|
||||
{
|
||||
public GenericTcpIpClient(string key, string address, int port, int bufferSize)
|
||||
: base(key)
|
||||
{
|
||||
StreamDebugging = new CommunicationStreamDebugging(key);
|
||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
|
|
@ -193,10 +197,7 @@ namespace PepperDash.Core;
|
|||
Port = port;
|
||||
BufferSize = bufferSize;
|
||||
|
||||
RetryTimer = new CTimer(o =>
|
||||
{
|
||||
Reconnect();
|
||||
}, Timeout.Infinite);
|
||||
SetupRetryTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -211,28 +212,30 @@ namespace PepperDash.Core;
|
|||
AutoReconnectIntervalMs = 5000;
|
||||
BufferSize = 2000;
|
||||
|
||||
RetryTimer = new CTimer(o =>
|
||||
{
|
||||
Reconnect();
|
||||
}, Timeout.Infinite);
|
||||
SetupRetryTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor for S+
|
||||
/// </summary>
|
||||
public GenericTcpIpClient()
|
||||
: base(SplusKey)
|
||||
: base(SplusKey)
|
||||
{
|
||||
StreamDebugging = new CommunicationStreamDebugging(SplusKey);
|
||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
BufferSize = 2000;
|
||||
|
||||
RetryTimer = new CTimer(o =>
|
||||
{
|
||||
Reconnect();
|
||||
}, Timeout.Infinite);
|
||||
}
|
||||
SetupRetryTimer();
|
||||
}
|
||||
|
||||
private void SetupRetryTimer()
|
||||
{
|
||||
RetryTimer = new Timer { AutoReset = false, Enabled = false };
|
||||
RetryTimer.Elapsed += (s, e) => Reconnect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Just to help S+ set the key
|
||||
|
|
@ -249,7 +252,7 @@ namespace PepperDash.Core;
|
|||
{
|
||||
if (programEventType == eProgramStatusEventType.Stopping)
|
||||
{
|
||||
Debug.Console(1, this, "Program stopping. Closing connection");
|
||||
this.LogInformation("Program stopping. Closing connection");
|
||||
Deactivate();
|
||||
}
|
||||
}
|
||||
|
|
@ -258,42 +261,41 @@ namespace PepperDash.Core;
|
|||
///
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override bool Deactivate()
|
||||
{
|
||||
public override bool Deactivate()
|
||||
{
|
||||
RetryTimer.Stop();
|
||||
RetryTimer.Dispose();
|
||||
if (_client != null)
|
||||
{
|
||||
_client.SocketStatusChange -= this.Client_SocketStatusChange;
|
||||
_client.SocketStatusChange -= this.Client_SocketStatusChange;
|
||||
DisconnectClient();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to connect to the server
|
||||
/// </summary>
|
||||
public void Connect()
|
||||
{
|
||||
public void Connect()
|
||||
{
|
||||
if (string.IsNullOrEmpty(Hostname))
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
|
||||
this.LogWarning("GenericTcpIpClient '{0}': No address set", Key);
|
||||
return;
|
||||
}
|
||||
if (Port < 1 || Port > 65535)
|
||||
{
|
||||
{
|
||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", Key);
|
||||
this.LogWarning("GenericTcpIpClient '{0}': Invalid port", Key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
lock (_connectLock)
|
||||
{
|
||||
connectLock.Enter();
|
||||
if (IsConnected)
|
||||
{
|
||||
Debug.Console(1, this, "Connection already connected. Exiting Connect()");
|
||||
this.LogInformation("Connection already connected. Exiting Connect()");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -306,11 +308,7 @@ namespace PepperDash.Core;
|
|||
_client.ConnectToServerAsync(ConnectToServerCallback);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Reconnect()
|
||||
{
|
||||
|
|
@ -318,44 +316,34 @@ namespace PepperDash.Core;
|
|||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
lock (_connectLock)
|
||||
{
|
||||
connectLock.Enter();
|
||||
if (IsConnected || DisconnectCalledByUser == true)
|
||||
{
|
||||
Debug.Console(1, this, "Reconnect no longer needed. Exiting Reconnect()");
|
||||
this.LogInformation("Reconnect no longer needed. Exiting Reconnect()");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Attempting reconnect now");
|
||||
this.LogInformation("Attempting reconnect now");
|
||||
_client.ConnectToServerAsync(ConnectToServerCallback);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to disconnect the client
|
||||
/// </summary>
|
||||
public void Disconnect()
|
||||
{
|
||||
try
|
||||
public void Disconnect()
|
||||
{
|
||||
lock (_connectLock)
|
||||
{
|
||||
connectLock.Enter();
|
||||
DisconnectCalledByUser = true;
|
||||
|
||||
// Stop trying reconnects, if we are
|
||||
RetryTimer.Stop();
|
||||
DisconnectClient();
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Does the actual disconnect business
|
||||
|
|
@ -364,7 +352,7 @@ namespace PepperDash.Core;
|
|||
{
|
||||
if (_client != null)
|
||||
{
|
||||
Debug.Console(1, this, "Disconnecting client");
|
||||
this.LogInformation("Disconnecting client");
|
||||
if (IsConnected)
|
||||
_client.DisconnectFromServer();
|
||||
}
|
||||
|
|
@ -374,50 +362,47 @@ namespace PepperDash.Core;
|
|||
/// Callback method for connection attempt
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
void ConnectToServerCallback(TCPClient c)
|
||||
{
|
||||
void ConnectToServerCallback(TCPClient c)
|
||||
{
|
||||
if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
|
||||
this.LogInformation("Server connection result: {0}", c.ClientStatus);
|
||||
WaitAndTryReconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
|
||||
this.LogInformation("Server connection result: {0}", c.ClientStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects, waits and attemtps to connect again
|
||||
/// </summary>
|
||||
void WaitAndTryReconnect()
|
||||
{
|
||||
CrestronInvoke.BeginInvoke(o =>
|
||||
void WaitAndTryReconnect()
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
try
|
||||
lock (_connectLock)
|
||||
{
|
||||
connectLock.Enter();
|
||||
if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)
|
||||
{
|
||||
DisconnectClient();
|
||||
Debug.Console(1, this, "Attempting reconnect, status={0}", _client.ClientStatus);
|
||||
RetryTimer.Reset(AutoReconnectIntervalMs);
|
||||
this.LogInformation("Attempting reconnect, status={0}", _client.ClientStatus);
|
||||
RetryTimer.Stop();
|
||||
RetryTimer.Interval = AutoReconnectIntervalMs;
|
||||
RetryTimer.Start();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
connectLock.Leave();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recieves incoming data
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="numBytes"></param>
|
||||
void Receive(TCPClient client, int numBytes)
|
||||
{
|
||||
void Receive(TCPClient client, int numBytes)
|
||||
{
|
||||
if (client != null)
|
||||
{
|
||||
if (numBytes > 0)
|
||||
|
|
@ -428,7 +413,7 @@ namespace PepperDash.Core;
|
|||
{
|
||||
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));
|
||||
}
|
||||
|
|
@ -439,135 +424,135 @@ namespace PepperDash.Core;
|
|||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
client.ReceiveDataAsync(Receive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
// Check debug level before processing byte array
|
||||
/// <summary>
|
||||
/// General send method
|
||||
/// </summary>
|
||||
public void SendText(string text)
|
||||
{
|
||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||
// Check debug level before processing byte array
|
||||
if (StreamDebugging.TxStreamDebuggingIsEnabled)
|
||||
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
this.LogInformation("Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
|
||||
if (_client != null)
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SendEscapedText method
|
||||
/// </summary>
|
||||
public void SendEscapedText(string text)
|
||||
{
|
||||
var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
|
||||
{
|
||||
var hex = s.Groups[1].Value;
|
||||
return ((char)Convert.ToByte(hex, 16)).ToString();
|
||||
});
|
||||
SendText(unescapedText);
|
||||
}
|
||||
/// <summary>
|
||||
/// SendEscapedText method
|
||||
/// </summary>
|
||||
public void SendEscapedText(string text)
|
||||
{
|
||||
var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
|
||||
{
|
||||
var hex = s.Groups[1].Value;
|
||||
return ((char)Convert.ToByte(hex, 16)).ToString();
|
||||
});
|
||||
SendText(unescapedText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends Bytes to the server
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
public void SendBytes(byte[] bytes)
|
||||
{
|
||||
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 (_client != null)
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
_client.SendData(bytes, bytes.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Socket Status Change Handler
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <param name="clientSocketStatus"></param>
|
||||
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
|
||||
{
|
||||
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
|
||||
{
|
||||
if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
|
||||
this.LogDebug("Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
|
||||
WaitAndTryReconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
|
||||
_client.ReceiveDataAsync(Receive);
|
||||
this.LogDebug("Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
|
||||
_client.ReceiveDataAsync(Receive);
|
||||
}
|
||||
|
||||
var handler = ConnectionChange;
|
||||
if (handler != null)
|
||||
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
|
||||
}
|
||||
var handler = ConnectionChange;
|
||||
if (handler != null)
|
||||
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration properties for TCP/SSH Connections
|
||||
/// </summary>
|
||||
public class TcpSshPropertiesConfig
|
||||
{
|
||||
public class TcpSshPropertiesConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Address to connect to
|
||||
/// </summary>
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string Address { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public string Address { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Port to connect to
|
||||
/// </summary>
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public int Port { get; set; }
|
||||
|
||||
[JsonProperty(Required = Required.Always)]
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Username credential
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
public string Username { get; set; }
|
||||
/// <summary>
|
||||
/// Passord credential
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defaults to 32768
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
/// <summary>
|
||||
/// Defaults to 32768
|
||||
/// </summary>
|
||||
public int BufferSize { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnect
|
||||
/// </summary>
|
||||
public bool AutoReconnect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnectIntervalMs
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the AutoReconnectIntervalMs
|
||||
/// </summary>
|
||||
public int AutoReconnectIntervalMs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When true, turns off echo for the SSH session
|
||||
/// </summary>
|
||||
[JsonProperty("disableSshEcho")]
|
||||
public bool DisableSshEcho { get; set; }
|
||||
/// <summary>
|
||||
/// When true, turns off echo for the SSH session
|
||||
/// </summary>
|
||||
[JsonProperty("disableSshEcho")]
|
||||
public bool DisableSshEcho { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public TcpSshPropertiesConfig()
|
||||
{
|
||||
BufferSize = 32768;
|
||||
AutoReconnect = true;
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
public TcpSshPropertiesConfig()
|
||||
{
|
||||
BufferSize = 32768;
|
||||
AutoReconnect = true;
|
||||
AutoReconnectIntervalMs = 5000;
|
||||
Username = "";
|
||||
Password = "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronSockets;
|
||||
using PepperDash.Core.Logging;
|
||||
|
|
@ -61,9 +62,14 @@ public class GenericTcpIpServer : Device
|
|||
#region Properties/Variables
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Server listen lock
|
||||
/// </summary>
|
||||
CCriticalSection ServerCCSection = new CCriticalSection();
|
||||
object _serverLock = new();
|
||||
|
||||
/// <summary>
|
||||
/// Broadcast lock
|
||||
/// </summary>
|
||||
private readonly object _broadcastLock = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -365,12 +371,12 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
else
|
||||
{
|
||||
ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
|
||||
this.LogError("Could not initialize server with key: {0}", serverConfigObject.Key);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
|
||||
this.LogError("Could not initialize server with key: {0}", serverConfigObject.Key);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -379,19 +385,18 @@ public class GenericTcpIpServer : Device
|
|||
/// </summary>
|
||||
public void Listen()
|
||||
{
|
||||
ServerCCSection.Enter();
|
||||
lock (_serverLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Port < 1 || Port > 65535)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
|
||||
ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
|
||||
this.LogError("Server '{0}': Invalid port", Key);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
|
||||
ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
|
||||
this.LogError("Server '{0}': No Shared Key set", Key);
|
||||
return;
|
||||
}
|
||||
if (IsListening)
|
||||
|
|
@ -417,18 +422,15 @@ public class GenericTcpIpServer : Device
|
|||
ServerStopped = false;
|
||||
myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
OnServerStateChange(myTcpServer.State);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
|
||||
this.LogInformation("TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
|
||||
|
||||
// StartMonitorClient();
|
||||
|
||||
|
||||
ServerCCSection.Leave();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ServerCCSection.Leave();
|
||||
ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
|
||||
this.LogException(ex, "Error with Dynamic Server: {0}", ex.Message);
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -438,18 +440,18 @@ public class GenericTcpIpServer : Device
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
||||
this.LogDebug("Stopping Listener");
|
||||
if (myTcpServer != null)
|
||||
{
|
||||
myTcpServer.Stop();
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
|
||||
this.LogDebug("Server State: {0}", myTcpServer.State);
|
||||
OnServerStateChange(myTcpServer.State);
|
||||
}
|
||||
ServerStopped = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
||||
this.LogException(ex, "Error stopping server. Error: {0}", ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -462,11 +464,11 @@ public class GenericTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
myTcpServer.Disconnect(client);
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
||||
this.LogVerbose("Disconnected client index: {0}", client);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
||||
this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", client, ex.Message);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
|
|
@ -474,7 +476,7 @@ public class GenericTcpIpServer : Device
|
|||
/// </summary>
|
||||
public void DisconnectAllClientsForShutdown()
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
|
||||
this.LogInformation("Disconnecting All Clients");
|
||||
if (myTcpServer != null)
|
||||
{
|
||||
myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
|
||||
|
|
@ -486,17 +488,17 @@ public class GenericTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
myTcpServer.Disconnect(i);
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
||||
this.LogVerbose("Disconnected client index: {0}", i);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
||||
this.LogException(ex, "Error Disconnecting client index: {0}. Error: {1}", i, ex.Message);
|
||||
}
|
||||
}
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
|
||||
this.LogVerbose("Server Status: {0}", myTcpServer.ServerSocketStatus);
|
||||
}
|
||||
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
||||
this.LogInformation("Disconnected All Clients");
|
||||
ConnectedClientsIndexes.Clear();
|
||||
|
||||
if (!ProgramIsStopping)
|
||||
|
|
@ -514,8 +516,8 @@ public class GenericTcpIpServer : Device
|
|||
/// <param name="text"></param>
|
||||
public void BroadcastText(string text)
|
||||
{
|
||||
CCriticalSection CCBroadcast = new CCriticalSection();
|
||||
CCBroadcast.Enter();
|
||||
lock (_broadcastLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ConnectedClientsIndexes.Count > 0)
|
||||
|
|
@ -531,13 +533,12 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
}
|
||||
}
|
||||
CCBroadcast.Leave();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CCBroadcast.Leave();
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
|
||||
this.LogException(ex, "Error Broadcasting messages from server. Error: {0}", ex.Message);
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -558,7 +559,7 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||
this.LogException(ex, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -582,7 +583,7 @@ public class GenericTcpIpServer : Device
|
|||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
}
|
||||
Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
|
||||
// Return Heartbeat
|
||||
SendTextToClient(HeartbeatStringToMatch, clientIndex);
|
||||
return remainingText;
|
||||
|
|
@ -597,13 +598,13 @@ public class GenericTcpIpServer : Device
|
|||
CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
|
||||
HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
|
||||
}
|
||||
Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
this.LogVerbose("Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
|
||||
this.LogException(ex, "Error checking heartbeat: {0}", ex.Message);
|
||||
}
|
||||
return received;
|
||||
}
|
||||
|
|
@ -615,11 +616,11 @@ public class GenericTcpIpServer : Device
|
|||
/// <returns>IP address of the client</returns>
|
||||
public string GetClientIPAddress(uint clientIndex)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
|
||||
this.LogVerbose("GetClientIPAddress Index: {0}", clientIndex);
|
||||
if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
|
||||
{
|
||||
var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
|
||||
this.LogVerbose("GetClientIPAddress IPAddreess: {0}", ipa);
|
||||
return ipa;
|
||||
|
||||
}
|
||||
|
|
@ -642,7 +643,7 @@ public class GenericTcpIpServer : Device
|
|||
clientIndex = (uint)o;
|
||||
address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
|
||||
this.LogWarning("Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
|
||||
address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
|
||||
|
||||
if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
|
|
@ -678,7 +679,7 @@ public class GenericTcpIpServer : Device
|
|||
try
|
||||
{
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
this.LogInformation("SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
{
|
||||
if (ConnectedClientsIndexes.Contains(clientIndex))
|
||||
|
|
@ -697,7 +698,7 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||
this.LogException(ex, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||
}
|
||||
onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
||||
}
|
||||
|
|
@ -714,7 +715,7 @@ public class GenericTcpIpServer : Device
|
|||
{
|
||||
try
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
|
||||
this.LogDebug("ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
|
||||
server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
|
||||
clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
||||
if (clientIndex != 0)
|
||||
|
|
@ -734,10 +735,11 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
|
||||
server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
this.LogDebug("Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.LogDebug("Client at index {0} is ready for communications", clientIndex);
|
||||
OnServerClientReadyForCommunications(clientIndex);
|
||||
}
|
||||
if (HeartbeatRequired)
|
||||
|
|
@ -753,7 +755,7 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
|
||||
this.LogError("Client attempt faulty.");
|
||||
if (!ServerStopped)
|
||||
{
|
||||
server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
|
|
@ -763,7 +765,7 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||
this.LogException(ex, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||
}
|
||||
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
||||
// server.State,
|
||||
|
|
@ -771,7 +773,7 @@ public class GenericTcpIpServer : Device
|
|||
// ServerStopped);
|
||||
if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Waiting for next connection");
|
||||
this.LogDebug("Waiting for next connection");
|
||||
server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||
|
||||
}
|
||||
|
|
@ -802,7 +804,7 @@ public class GenericTcpIpServer : Device
|
|||
if (received != SharedKey)
|
||||
{
|
||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||
this.LogWarning("Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||
myTCPServer.SendData(clientIndex, b, b.Length);
|
||||
myTCPServer.Disconnect(clientIndex);
|
||||
return;
|
||||
|
|
@ -812,7 +814,7 @@ public class GenericTcpIpServer : Device
|
|||
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
||||
myTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
||||
OnServerClientReadyForCommunications(clientIndex);
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||
this.LogDebug("Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||
}
|
||||
|
||||
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
||||
|
|
@ -820,7 +822,7 @@ public class GenericTcpIpServer : Device
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
||||
this.LogException(ex, "Error Receiving data: {0}. Error: {1}", received, ex);
|
||||
}
|
||||
if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||
myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
|
||||
|
|
@ -901,7 +903,7 @@ public class GenericTcpIpServer : Device
|
|||
if (MonitorClient != null)
|
||||
MonitorClient.Disconnect();
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
|
||||
this.LogInformation("Program stopping. Closing server");
|
||||
KillServer();
|
||||
}
|
||||
}
|
||||
|
|
@ -942,7 +944,7 @@ public class GenericTcpIpServer : Device
|
|||
//MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
|
||||
MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
|
||||
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
|
||||
this.LogDebug("Starting monitor check");
|
||||
|
||||
MonitorClient.Connect();
|
||||
// From here MonitorCLient either connects or hangs, MonitorClient will call back
|
||||
|
|
@ -969,7 +971,7 @@ public class GenericTcpIpServer : Device
|
|||
{
|
||||
if (args.IsReady)
|
||||
{
|
||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
|
||||
this.LogInformation("Monitor client connection success. Disconnecting in 2s");
|
||||
MonitorClientTimer.Stop();
|
||||
MonitorClientTimer = null;
|
||||
MonitorClientFailureCount = 0;
|
||||
|
|
@ -990,13 +992,13 @@ public class GenericTcpIpServer : Device
|
|||
StopMonitorClient();
|
||||
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||
this.LogWarning("Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
||||
StartMonitorClient();
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Console(2, this, Debug.ErrorLogLevel.Error,
|
||||
this.LogError(
|
||||
"\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
|
||||
MonitorClientMaxFailureCount);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
extern alias NewtonsoftJson;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
using JsonProperty = NewtonsoftJson::Newtonsoft.Json.JsonPropertyAttribute;
|
||||
|
||||
|
|
@ -9,62 +10,61 @@ namespace PepperDash.Core.Logging;
|
|||
/// <summary>
|
||||
/// Class to persist current Debug settings across program restarts
|
||||
/// </summary>
|
||||
public class DebugContextCollection
|
||||
{
|
||||
public class DebugContextCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// To prevent threading issues with the DeviceDebugSettings collection
|
||||
/// </summary>
|
||||
private readonly CCriticalSection _deviceDebugSettingsLock;
|
||||
private readonly object _deviceDebugSettingsLock = new();
|
||||
|
||||
[JsonProperty("items")] private readonly Dictionary<string, DebugContextItem> _items;
|
||||
[JsonProperty("items")]
|
||||
private readonly Dictionary<string, DebugContextItem> _items = new Dictionary<string, DebugContextItem>();
|
||||
|
||||
/// <summary>
|
||||
/// Collection of the debug settings for each device where the dictionary key is the device key
|
||||
/// </summary>
|
||||
[JsonProperty("deviceDebugSettings")]
|
||||
private Dictionary<string, object> DeviceDebugSettings { get; set; }
|
||||
private Dictionary<string, object> DeviceDebugSettings { get; set; } = new Dictionary<string, object>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public DebugContextCollection()
|
||||
{
|
||||
_deviceDebugSettingsLock = new CCriticalSection();
|
||||
DeviceDebugSettings = new Dictionary<string, object>();
|
||||
_items = new Dictionary<string, DebugContextItem>();
|
||||
}
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public DebugContextCollection()
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Sets the level of a given context item, and adds that item if it does not
|
||||
/// exist
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <summary>
|
||||
/// SetLevel method
|
||||
/// </summary>
|
||||
public void SetLevel(string contextKey, int level)
|
||||
{
|
||||
if (level < 0 || level > 2)
|
||||
return;
|
||||
GetOrCreateItem(contextKey).Level = level;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a level or creates it if not existing
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetOrCreateItem method
|
||||
/// </summary>
|
||||
public DebugContextItem GetOrCreateItem(string contextKey)
|
||||
{
|
||||
if (!_items.ContainsKey(contextKey))
|
||||
_items[contextKey] = new DebugContextItem { Level = 0 };
|
||||
return _items[contextKey];
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the level of a given context item, and adds that item if it does not
|
||||
/// exist
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <param name="level"></param>
|
||||
/// <summary>
|
||||
/// SetLevel method
|
||||
/// </summary>
|
||||
public void SetLevel(string contextKey, int level)
|
||||
{
|
||||
if (level < 0 || level > 2)
|
||||
return;
|
||||
GetOrCreateItem(contextKey).Level = level;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a level or creates it if not existing
|
||||
/// </summary>
|
||||
/// <param name="contextKey"></param>
|
||||
/// <returns></returns>
|
||||
/// <summary>
|
||||
/// GetOrCreateItem method
|
||||
/// </summary>
|
||||
public DebugContextItem GetOrCreateItem(string contextKey)
|
||||
{
|
||||
if (!_items.ContainsKey(contextKey))
|
||||
_items[contextKey] = new DebugContextItem { Level = 0 };
|
||||
return _items[contextKey];
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -75,10 +75,8 @@ namespace PepperDash.Core.Logging;
|
|||
/// <returns></returns>
|
||||
public void SetDebugSettingsForKey(string deviceKey, object settings)
|
||||
{
|
||||
try
|
||||
lock (_deviceDebugSettingsLock)
|
||||
{
|
||||
_deviceDebugSettingsLock.Enter();
|
||||
|
||||
if (DeviceDebugSettings.ContainsKey(deviceKey))
|
||||
{
|
||||
DeviceDebugSettings[deviceKey] = settings;
|
||||
|
|
@ -86,10 +84,6 @@ namespace PepperDash.Core.Logging;
|
|||
else
|
||||
DeviceDebugSettings.Add(deviceKey, settings);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_deviceDebugSettingsLock.Leave();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -101,22 +95,22 @@ namespace PepperDash.Core.Logging;
|
|||
{
|
||||
return DeviceDebugSettings[deviceKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains information about
|
||||
/// </summary>
|
||||
public class DebugContextItem
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information about
|
||||
/// </summary>
|
||||
public class DebugContextItem
|
||||
{
|
||||
/// <summary>
|
||||
/// The level of debug messages to print
|
||||
/// </summary>
|
||||
[JsonProperty("level")]
|
||||
public int Level { get; set; }
|
||||
[JsonProperty("level")]
|
||||
public int Level { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Property to tell the program not to intitialize when it boots, if desired
|
||||
/// </summary>
|
||||
[JsonProperty("doNotLoadOnNextBoot")]
|
||||
public bool DoNotLoadOnNextBoot { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -3,287 +3,250 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Formatting = NewtonsoftJson::Newtonsoft.Json.Formatting;
|
||||
using JsonConvert = NewtonsoftJson::Newtonsoft.Json.JsonConvert;
|
||||
using JObject = NewtonsoftJson::Newtonsoft.Json.Linq.JObject;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using PepperDash.Core.Logging;
|
||||
|
||||
namespace PepperDash.Core.Web;
|
||||
|
||||
/// <summary>
|
||||
/// Web API server
|
||||
/// </summary>
|
||||
public class WebApiServer : IKeyName
|
||||
{
|
||||
private const string SplusKey = "Uninitialized Web API Server";
|
||||
private const string DefaultName = "Web API Server";
|
||||
private const string DefaultBasePath = "/api";
|
||||
|
||||
private readonly object _serverLock = new();
|
||||
private HttpCwsServer _server;
|
||||
|
||||
/// <summary>
|
||||
/// Web API server
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
public class WebApiServer : IKeyName
|
||||
public string Key { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the BasePath
|
||||
/// </summary>
|
||||
public string BasePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IsRegistered
|
||||
/// </summary>
|
||||
public bool IsRegistered { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for S+. Make sure to set necessary properties using init method
|
||||
/// </summary>
|
||||
public WebApiServer()
|
||||
: this(SplusKey, DefaultName, null)
|
||||
{
|
||||
private const string SplusKey = "Uninitialized Web API Server";
|
||||
private const string DefaultName = "Web API Server";
|
||||
private const string DefaultBasePath = "/api";
|
||||
}
|
||||
|
||||
private const uint DebugTrace = 0;
|
||||
private const uint DebugInfo = 1;
|
||||
private const uint DebugVerbose = 2;
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="basePath"></param>
|
||||
public WebApiServer(string key, string basePath)
|
||||
: this(key, DefaultName, basePath)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly CCriticalSection _serverLock = new CCriticalSection();
|
||||
private HttpCwsServer _server;
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="basePath"></param>
|
||||
public WebApiServer(string key, string name, string basePath)
|
||||
{
|
||||
Key = key;
|
||||
Name = string.IsNullOrEmpty(name) ? DefaultName : name;
|
||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Key
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
if (_server == null) _server = new HttpCwsServer(BasePath);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name
|
||||
/// </summary>
|
||||
public string Name { get; private set; }
|
||||
_server.setProcessName(Key);
|
||||
_server.HttpRequestHandler = new DefaultRequestHandler();
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the BasePath
|
||||
/// </summary>
|
||||
public string BasePath { get; private set; }
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the IsRegistered
|
||||
/// </summary>
|
||||
public bool IsRegistered { get; private set; }
|
||||
/// <summary>
|
||||
/// Program status event handler
|
||||
/// </summary>
|
||||
/// <param name="programEventType"></param>
|
||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
{
|
||||
if (programEventType != eProgramStatusEventType.Stopping) return;
|
||||
|
||||
/// <summary>
|
||||
/// Http request handler
|
||||
/// </summary>
|
||||
//public IHttpCwsHandler HttpRequestHandler
|
||||
//{
|
||||
// get { return _server.HttpRequestHandler; }
|
||||
// set
|
||||
// {
|
||||
// if (_server == null) return;
|
||||
// _server.HttpRequestHandler = value;
|
||||
// }
|
||||
//}
|
||||
this.LogInformation("Program stopping. stopping server");
|
||||
|
||||
/// <summary>
|
||||
/// Received request event handler
|
||||
/// </summary>
|
||||
//public event EventHandler<HttpCwsRequestEventArgs> ReceivedRequestEvent
|
||||
//{
|
||||
// add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); }
|
||||
// remove { _server.ReceivedRequestEvent -= new HttpCwsRequestEventHandler(value); }
|
||||
//}
|
||||
Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for S+. Make sure to set necessary properties using init method
|
||||
/// </summary>
|
||||
public WebApiServer()
|
||||
: this(SplusKey, DefaultName, null)
|
||||
/// <summary>
|
||||
/// Ethernet event handler
|
||||
/// </summary>
|
||||
/// <param name="ethernetEventArgs"></param>
|
||||
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
||||
{
|
||||
// Re-enable the server if the link comes back up and the status should be connected
|
||||
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered)
|
||||
{
|
||||
this.LogInformation("Ethernet link up. Server is already registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="basePath"></param>
|
||||
public WebApiServer(string key, string basePath)
|
||||
: this(key, DefaultName, basePath)
|
||||
this.LogInformation("Ethernet link up. Starting server");
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key, string basePath)
|
||||
{
|
||||
Key = key;
|
||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to CWS
|
||||
/// </summary>
|
||||
public void AddRoute(HttpCwsRoute route)
|
||||
{
|
||||
if (route == null)
|
||||
{
|
||||
this.LogWarning("Failed to add route, route parameter is null");
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="basePath"></param>
|
||||
public WebApiServer(string key, string name, string basePath)
|
||||
_server.Routes.Add(route);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a route from CWS
|
||||
/// </summary>
|
||||
/// <param name="route"></param>
|
||||
/// <summary>
|
||||
/// RemoveRoute method
|
||||
/// </summary>
|
||||
public void RemoveRoute(HttpCwsRoute route)
|
||||
{
|
||||
if (route == null)
|
||||
{
|
||||
Key = key;
|
||||
Name = string.IsNullOrEmpty(name) ? DefaultName : name;
|
||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||
|
||||
if (_server == null) _server = new HttpCwsServer(BasePath);
|
||||
|
||||
_server.setProcessName(Key);
|
||||
_server.HttpRequestHandler = new DefaultRequestHandler();
|
||||
|
||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
|
||||
this.LogWarning("Failed to remove route, route parameter is null");
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Program status event handler
|
||||
/// </summary>
|
||||
/// <param name="programEventType"></param>
|
||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||
{
|
||||
if (programEventType != eProgramStatusEventType.Stopping) return;
|
||||
_server.Routes.Remove(route);
|
||||
}
|
||||
|
||||
Debug.Console(DebugInfo, this, "Program stopping. stopping server");
|
||||
/// <summary>
|
||||
/// GetRouteCollection method
|
||||
/// </summary>
|
||||
public HttpCwsRouteCollection GetRouteCollection()
|
||||
{
|
||||
return _server.Routes;
|
||||
}
|
||||
|
||||
Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ethernet event handler
|
||||
/// </summary>
|
||||
/// <param name="ethernetEventArgs"></param>
|
||||
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
||||
{
|
||||
// Re-enable the server if the link comes back up and the status should be connected
|
||||
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp && IsRegistered)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Ethernet link up. Server is alreedy registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Console(DebugInfo, this, "Ethernet link up. Starting server");
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize method
|
||||
/// </summary>
|
||||
public void Initialize(string key, string basePath)
|
||||
{
|
||||
Key = key;
|
||||
BasePath = string.IsNullOrEmpty(basePath) ? DefaultBasePath : basePath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a route to CWS
|
||||
/// </summary>
|
||||
public void AddRoute(HttpCwsRoute route)
|
||||
{
|
||||
if (route == null)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Failed to add route, route parameter is null");
|
||||
return;
|
||||
}
|
||||
|
||||
_server.Routes.Add(route);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a route from CWS
|
||||
/// </summary>
|
||||
/// <param name="route"></param>
|
||||
/// <summary>
|
||||
/// RemoveRoute method
|
||||
/// </summary>
|
||||
public void RemoveRoute(HttpCwsRoute route)
|
||||
{
|
||||
if (route == null)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Failed to remote route, orute parameter is null");
|
||||
return;
|
||||
}
|
||||
|
||||
_server.Routes.Remove(route);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// GetRouteCollection method
|
||||
/// </summary>
|
||||
public HttpCwsRouteCollection GetRouteCollection()
|
||||
{
|
||||
return _server.Routes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts CWS instance
|
||||
/// </summary>
|
||||
public void Start()
|
||||
/// <summary>
|
||||
/// Starts CWS instance
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
lock (_serverLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
_serverLock.Enter();
|
||||
|
||||
if (_server == null)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Server is null, unable to start");
|
||||
this.LogDebug("Server is null, unable to start");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsRegistered)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Server has already been started");
|
||||
this.LogDebug("Server has already been started");
|
||||
return;
|
||||
}
|
||||
|
||||
IsRegistered = _server.Register();
|
||||
|
||||
Debug.Console(DebugInfo, this, "Starting server, registration {0}", IsRegistered ? "was successful" : "failed");
|
||||
this.LogDebug("Starting server, registration {0}", IsRegistered ? "was successful" : "failed");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Start Exception Message: {0}", ex.Message);
|
||||
Debug.Console(DebugVerbose, this, "Start Exception StackTrace: {0}", ex.StackTrace);
|
||||
if (ex.InnerException != null)
|
||||
Debug.Console(DebugVerbose, this, "Start Exception InnerException: {0}", ex.InnerException);
|
||||
this.LogException(ex, "Start Exception Message: {0}", ex.Message);
|
||||
this.LogVerbose("Start Exception StackTrace: {0}", ex.StackTrace);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serverLock.Leave();
|
||||
}
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop method
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
/// <summary>
|
||||
/// Stop method
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
lock (_serverLock)
|
||||
{
|
||||
try
|
||||
{
|
||||
_serverLock.Enter();
|
||||
|
||||
if (_server == null)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Server is null or has already been stopped");
|
||||
this.LogDebug("Server is null or has already been stopped");
|
||||
return;
|
||||
}
|
||||
|
||||
IsRegistered = _server.Unregister() == false;
|
||||
|
||||
Debug.Console(DebugInfo, this, "Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful");
|
||||
this.LogDebug("Stopping server, unregistration {0}", IsRegistered ? "failed" : "was successful");
|
||||
|
||||
_server.Dispose();
|
||||
_server = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "Server Stop Exception Message: {0}", ex.Message);
|
||||
Debug.Console(DebugVerbose, this, "Server Stop Exception StackTrace: {0}", ex.StackTrace);
|
||||
if (ex.InnerException != null)
|
||||
Debug.Console(DebugVerbose, this, "Server Stop Exception InnerException: {0}", ex.InnerException);
|
||||
this.LogException(ex, "Server Stop Exception Message: {0}", ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_serverLock.Leave();
|
||||
}
|
||||
}
|
||||
} // end lock
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Received request handler
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is here for development and testing
|
||||
/// </remarks>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)
|
||||
/// <summary>
|
||||
/// Received request handler
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is here for development and testing
|
||||
/// </remarks>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="args"></param>
|
||||
public void ReceivedRequestEventHandler(object sender, HttpCwsRequestEventArgs args)
|
||||
{
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented);
|
||||
Debug.Console(DebugVerbose, this, "RecieveRequestEventHandler Context:\x0d\x0a{0}", j);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.Console(DebugInfo, this, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
|
||||
Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace);
|
||||
if (ex.InnerException != null)
|
||||
Debug.Console(DebugVerbose, this, "ReceivedRequestEventHandler Exception InnerException: {0}", ex.InnerException);
|
||||
}
|
||||
var j = JsonConvert.SerializeObject(args.Context, Formatting.Indented);
|
||||
this.LogVerbose("RecieveRequestEventHandler Context:\x0d\x0a{0}", j);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.LogException(ex, "ReceivedRequestEventHandler Exception Message: {0}", ex.Message);
|
||||
this.LogVerbose("ReceivedRequestEventHandler Exception StackTrace: {0}", ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue