mirror of
https://github.com/PepperDash/PepperDashCore.git
synced 2026-02-15 04:34:46 +00:00
Merged in bugfix/PDC-18_AddS+ConstructorToEventArgs (pull request #24)
PDC-18_AddS+ConstructorToEventArgs Approved-by: Neil Dorin <ndorin@pepperdash.com>
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -23,12 +23,14 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
public ISocketStatus Client { get; private set; }
|
public ISocketStatus Client { get; private set; }
|
||||||
|
|
||||||
public GenericSocketStatusChageEventArgs() { }
|
|
||||||
|
|
||||||
public GenericSocketStatusChageEventArgs(ISocketStatus client)
|
public GenericSocketStatusChageEventArgs(ISocketStatus client)
|
||||||
{
|
{
|
||||||
Client = client;
|
Client = client;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericSocketStatusChageEventArgs() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
|
public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
|
||||||
@@ -36,12 +38,14 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
public ServerState State { get; private set; }
|
public ServerState State { get; private set; }
|
||||||
|
|
||||||
public GenericTcpServerStateChangedEventArgs() { }
|
|
||||||
|
|
||||||
public GenericTcpServerStateChangedEventArgs(ServerState state)
|
public GenericTcpServerStateChangedEventArgs(ServerState state)
|
||||||
{
|
{
|
||||||
State = state;
|
State = state;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericTcpServerStateChangedEventArgs() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
|
public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
|
||||||
@@ -50,8 +54,6 @@ namespace PepperDash.Core
|
|||||||
public object Socket { get; private set; }
|
public object Socket { get; private set; }
|
||||||
public uint ReceivedFromClientIndex { get; private set; }
|
public uint ReceivedFromClientIndex { get; private set; }
|
||||||
public SocketStatus ClientStatus { get; set; }
|
public SocketStatus ClientStatus { get; set; }
|
||||||
|
|
||||||
public GenericTcpServerSocketStatusChangeEventArgs() { }
|
|
||||||
|
|
||||||
public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
|
public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
|
||||||
{
|
{
|
||||||
@@ -65,6 +67,10 @@ namespace PepperDash.Core
|
|||||||
ReceivedFromClientIndex = clientIndex;
|
ReceivedFromClientIndex = clientIndex;
|
||||||
ClientStatus = clientStatus;
|
ClientStatus = clientStatus;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericTcpServerSocketStatusChangeEventArgs() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
|
||||||
@@ -82,6 +88,10 @@ namespace PepperDash.Core
|
|||||||
Text = text;
|
Text = text;
|
||||||
ReceivedFromClientIndex = clientIndex;
|
ReceivedFromClientIndex = clientIndex;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericTcpServerCommMethodReceiveTextArgs() { }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
|
public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
|
||||||
@@ -91,6 +101,29 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
IsReady = isReady;
|
IsReady = isReady;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenericUdpConnectedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public ushort UConnected;
|
||||||
|
public bool Connected;
|
||||||
|
|
||||||
|
public GenericUdpConnectedEventArgs() { }
|
||||||
|
|
||||||
|
public GenericUdpConnectedEventArgs(ushort uconnected)
|
||||||
|
{
|
||||||
|
UConnected = uconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenericUdpConnectedEventArgs(bool connected)
|
||||||
|
{
|
||||||
|
Connected = connected;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,15 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;
|
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;
|
||||||
|
|
||||||
|
public event EventHandler AutoReconnectTriggered;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
|
||||||
|
/// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;
|
||||||
|
|
||||||
|
|
||||||
public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange;
|
public event EventHandler<GenericTcpServerSocketStatusChangeEventArgs> ConnectionChange;
|
||||||
|
|
||||||
|
|
||||||
@@ -209,8 +218,20 @@ namespace PepperDash.Core
|
|||||||
get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
|
get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
|
||||||
set { HeartbeatEnabled = value == 1; }
|
set { HeartbeatEnabled = value == 1; }
|
||||||
}
|
}
|
||||||
public string HeartbeatString = "heartbeat";
|
|
||||||
public int HeartbeatInterval = 50000;
|
public string HeartbeatString { get; set; }
|
||||||
|
//public int HeartbeatInterval = 50000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
|
||||||
|
/// </summary>
|
||||||
|
public int HeartbeatInterval { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simpl+ Heartbeat Analog value in seconds
|
||||||
|
/// </summary>
|
||||||
|
public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
|
||||||
|
|
||||||
CTimer HeartbeatSendTimer;
|
CTimer HeartbeatSendTimer;
|
||||||
CTimer HeartbeatAckTimer;
|
CTimer HeartbeatAckTimer;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -226,6 +247,24 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
bool ProgramIsStopping;
|
bool ProgramIsStopping;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue lock
|
||||||
|
/// </summary>
|
||||||
|
CCriticalSection DequeueLock = new CCriticalSection();
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
/// calling initialize.
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiveQueueSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
|
||||||
|
/// calling initialize.
|
||||||
|
/// </summary>
|
||||||
|
private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
@@ -244,12 +283,24 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
//base class constructor
|
//base class constructor
|
||||||
public GenericSecureTcpIpClient_ForServer()
|
public GenericSecureTcpIpClient_ForServer()
|
||||||
: base("Uninitialized DynamicTcpClient")
|
: base("Uninitialized Secure Tcp Client For Server")
|
||||||
{
|
{
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
AutoReconnectIntervalMs = 5000;
|
AutoReconnectIntervalMs = 5000;
|
||||||
BufferSize = 2000;
|
BufferSize = 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contstructor that sets all properties by calling the initialize method with a config object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverConfigObject"></param>
|
||||||
|
public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)
|
||||||
|
: base(key)
|
||||||
|
{
|
||||||
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
|
Initialize(clientConfigObject);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
@@ -262,6 +313,43 @@ namespace PepperDash.Core
|
|||||||
Key = key;
|
Key = key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientConfigObject"></param>
|
||||||
|
public void Initialize(TcpClientConfigObject clientConfigObject)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (clientConfigObject != null)
|
||||||
|
{
|
||||||
|
var TcpSshProperties = clientConfigObject.Control.TcpSshProperties;
|
||||||
|
Hostname = TcpSshProperties.Address;
|
||||||
|
AutoReconnect = TcpSshProperties.AutoReconnect;
|
||||||
|
AutoReconnectIntervalMs = TcpSshProperties.AutoReconnectIntervalMs > 1000 ?
|
||||||
|
TcpSshProperties.AutoReconnectIntervalMs : 5000;
|
||||||
|
SharedKey = clientConfigObject.SharedKey;
|
||||||
|
SharedKeyRequired = clientConfigObject.SharedKeyRequired;
|
||||||
|
HeartbeatEnabled = clientConfigObject.HeartbeatRequired;
|
||||||
|
HeartbeatRequiredIntervalInSeconds = clientConfigObject.HeartbeatRequiredIntervalInSeconds > 0 ?
|
||||||
|
clientConfigObject.HeartbeatRequiredIntervalInSeconds : (ushort)15;
|
||||||
|
HeartbeatString = string.IsNullOrEmpty(clientConfigObject.HeartbeatStringToMatch) ? "heartbeat" : clientConfigObject.HeartbeatStringToMatch;
|
||||||
|
Port = TcpSshProperties.Port;
|
||||||
|
BufferSize = TcpSshProperties.BufferSize > 2000 ? TcpSshProperties.BufferSize : 2000;
|
||||||
|
ReceiveQueueSize = clientConfigObject.ReceiveQueueSize > 20 ? clientConfigObject.ReceiveQueueSize : 20;
|
||||||
|
MessageQueue = new CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs>(ReceiveQueueSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorLog.Error("Could not initialize client with key: {0}", Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ErrorLog.Error("Could not initialize client with key: {0}", Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles closing this up when the program shuts down
|
/// Handles closing this up when the program shuts down
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -328,7 +416,8 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
Client = new SecureTCPClient(Hostname, Port, BufferSize);
|
Client = new SecureTCPClient(Hostname, Port, BufferSize);
|
||||||
Client.SocketStatusChange += Client_SocketStatusChange;
|
Client.SocketStatusChange += Client_SocketStatusChange;
|
||||||
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
|
if (HeartbeatEnabled)
|
||||||
|
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
|
||||||
Client.AddressClientConnectedTo = Hostname;
|
Client.AddressClientConnectedTo = Hostname;
|
||||||
Client.PortNumber = Port;
|
Client.PortNumber = Port;
|
||||||
// SecureClient = c;
|
// SecureClient = c;
|
||||||
@@ -381,6 +470,15 @@ namespace PepperDash.Core
|
|||||||
//OnClientReadyForcommunications(false); // Should send false event
|
//OnClientReadyForcommunications(false); // Should send false event
|
||||||
}, 15000);
|
}, 15000);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
|
||||||
|
//required this is called by the shared key being negotiated
|
||||||
|
if (IsReadyForCommunication == false)
|
||||||
|
{
|
||||||
|
OnClientReadyForcommunications(true); // Key not required
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -463,6 +561,8 @@ namespace PepperDash.Core
|
|||||||
RetryTimer.Stop();
|
RetryTimer.Stop();
|
||||||
RetryTimer = null;
|
RetryTimer = null;
|
||||||
}
|
}
|
||||||
|
if(AutoReconnectTriggered != null)
|
||||||
|
AutoReconnectTriggered(this, new EventArgs());
|
||||||
RetryTimer = new CTimer(o => Connect(), rndTime);
|
RetryTimer = new CTimer(o => Connect(), rndTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -477,7 +577,7 @@ namespace PepperDash.Core
|
|||||||
if (numBytes > 0)
|
if (numBytes > 0)
|
||||||
{
|
{
|
||||||
string str = string.Empty;
|
string str = string.Empty;
|
||||||
|
var handler = TextReceivedQueueInvoke;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
|
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
|
||||||
@@ -499,20 +599,18 @@ namespace PepperDash.Core
|
|||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Shared key confirmed. Ready for communication");
|
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Shared key confirmed. Ready for communication");
|
||||||
OnClientReadyForcommunications(true); // Successful key exchange
|
OnClientReadyForcommunications(true); // Successful key exchange
|
||||||
}
|
}
|
||||||
else if (SharedKeyRequired == false && IsReadyForCommunication == false)
|
|
||||||
{
|
|
||||||
OnClientReadyForcommunications(true); // Key not required
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
//var bytesHandler = BytesReceived;
|
//var bytesHandler = BytesReceived;
|
||||||
//if (bytesHandler != null)
|
//if (bytesHandler != null)
|
||||||
// bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
// bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||||
var textHandler = TextReceived;
|
var textHandler = TextReceived;
|
||||||
if (textHandler != null)
|
if (textHandler != null)
|
||||||
textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
|
textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,9 +618,51 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
|
Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
|
||||||
}
|
}
|
||||||
|
if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||||
|
client.ReceiveDataAsync(Receive);
|
||||||
|
|
||||||
|
//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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //JAG added this as I believe the error return is 0 bytes like the server. See help when hover on ReceiveAsync
|
||||||
|
{
|
||||||
|
client.DisconnectFromServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||||
|
/// It will dequeue items as they are enqueued automatically.
|
||||||
|
/// </summary>
|
||||||
|
void DequeueEvent()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
|
||||||
|
var message = MessageQueue.Dequeue();
|
||||||
|
var handler = TextReceivedQueueInvoke;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Console(0, "DequeueEvent error: {0}\r", 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();
|
||||||
}
|
}
|
||||||
if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
|
|
||||||
client.ReceiveDataAsync(Receive);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeartbeatStart()
|
void HeartbeatStart()
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ namespace PepperDash.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;
|
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceived;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event for Receiving text. Once subscribed to this event the receive callback will start a thread that dequeues the messages and invokes the event on a new thread.
|
||||||
|
/// It is not recommended to use both the TextReceived event and the TextReceivedQueueInvoke event.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<GenericTcpServerCommMethodReceiveTextArgs> TextReceivedQueueInvoke;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Event for client connection socket status change
|
/// Event for client connection socket status change
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,10 +62,26 @@ namespace PepperDash.Core
|
|||||||
#region Properties/Variables
|
#region Properties/Variables
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// Server listen lock
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CCriticalSection ServerCCSection = new CCriticalSection();
|
CCriticalSection ServerCCSection = new CCriticalSection();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue lock
|
||||||
|
/// </summary>
|
||||||
|
CCriticalSection DequeueLock = new CCriticalSection();
|
||||||
|
|
||||||
|
/// <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
|
||||||
|
/// calling initialize.
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiveQueueSize { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue to temporarily store received messages with the source IP and Port info. Defaults to size 20. Use constructor or set queue size property before
|
||||||
|
/// calling initialize.
|
||||||
|
/// </summary>
|
||||||
|
private CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs> MessageQueue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A bandaid client that monitors whether the server is reachable
|
/// A bandaid client that monitors whether the server is reachable
|
||||||
@@ -145,7 +167,9 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
get { return (ushort)(IsListening ? 1 : 0); }
|
get { return (ushort)(IsListening ? 1 : 0); }
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65
|
||||||
|
/// </summary>
|
||||||
public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
|
public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of clients currently connected.
|
/// Number of clients currently connected.
|
||||||
@@ -269,7 +293,7 @@ namespace PepperDash.Core
|
|||||||
/// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
|
/// constructor S+ Does not accept a key. Use initialze with key to set the debug key on this device. If using with + make sure to set all properties manually.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public GenericSecureTcpIpServer()
|
public GenericSecureTcpIpServer()
|
||||||
: base("Uninitialized Dynamic TCP Server")
|
: base("Uninitialized Secure TCP Server")
|
||||||
{
|
{
|
||||||
HeartbeatRequiredIntervalInSeconds = 15;
|
HeartbeatRequiredIntervalInSeconds = 15;
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
@@ -282,7 +306,7 @@ namespace PepperDash.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key"></param>
|
/// <param name="key"></param>
|
||||||
public GenericSecureTcpIpServer(string key)
|
public GenericSecureTcpIpServer(string key)
|
||||||
: base("Uninitialized Dynamic TCP Server")
|
: base("Uninitialized Secure TCP Server")
|
||||||
{
|
{
|
||||||
HeartbeatRequiredIntervalInSeconds = 15;
|
HeartbeatRequiredIntervalInSeconds = 15;
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
@@ -292,11 +316,11 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contstructor that sets all properties by calling the initialize method with a config object.
|
/// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="serverConfigObject"></param>
|
/// <param name="serverConfigObject"></param>
|
||||||
public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
|
public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
|
||||||
: base("Uninitialized Dynamic TCP Server")
|
: base("Uninitialized Secure TCP Server")
|
||||||
{
|
{
|
||||||
HeartbeatRequiredIntervalInSeconds = 15;
|
HeartbeatRequiredIntervalInSeconds = 15;
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
@@ -345,7 +369,8 @@ namespace PepperDash.Core
|
|||||||
HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
|
HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
|
||||||
HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
|
HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
|
||||||
BufferSize = serverConfigObject.BufferSize;
|
BufferSize = serverConfigObject.BufferSize;
|
||||||
|
ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;
|
||||||
|
MessageQueue = new CrestronQueue<GenericTcpServerCommMethodReceiveTextArgs>(ReceiveQueueSize);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -384,13 +409,14 @@ namespace PepperDash.Core
|
|||||||
if (SecureServer == null)
|
if (SecureServer == null)
|
||||||
{
|
{
|
||||||
SecureServer = new SecureTCPServer(Port, MaxClients);
|
SecureServer = new SecureTCPServer(Port, MaxClients);
|
||||||
SecureServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
|
if(HeartbeatRequired)
|
||||||
|
SecureServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
|
||||||
SecureServer.HandshakeTimeout = 30;
|
SecureServer.HandshakeTimeout = 30;
|
||||||
SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
|
SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
KillServer();
|
//KillServer(); Remove this to be able to reactivate listener if it stops itself due to max clients without disconnecting connected clients.
|
||||||
SecureServer.PortNumber = Port;
|
SecureServer.PortNumber = Port;
|
||||||
}
|
}
|
||||||
ServerStopped = false;
|
ServerStopped = false;
|
||||||
@@ -415,25 +441,21 @@ namespace PepperDash.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopListening()
|
public void StopListening()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
||||||
if (SecureServer != null)
|
if (SecureServer != null)
|
||||||
{
|
{
|
||||||
SecureServer.Stop();
|
SecureServer.Stop();
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
|
||||||
//SecureServer = null;
|
OnServerStateChange(SecureServer.State);
|
||||||
}
|
}
|
||||||
|
ServerStopped = true;
|
||||||
ServerStopped = true;
|
}
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server Stopped");
|
catch (Exception ex)
|
||||||
|
{
|
||||||
OnServerStateChange(SecureServer.State);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -445,11 +467,11 @@ namespace PepperDash.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
SecureServer.Disconnect(client);
|
SecureServer.Disconnect(client);
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -469,17 +491,17 @@ namespace PepperDash.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
SecureServer.Disconnect(i);
|
SecureServer.Disconnect(i);
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
||||||
ConnectedClientsIndexes.Clear();
|
ConnectedClientsIndexes.Clear();
|
||||||
|
|
||||||
if (!ProgramIsStopping)
|
if (!ProgramIsStopping)
|
||||||
@@ -510,7 +532,7 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
|
SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
|
||||||
if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||||
Debug.Console(0, error.ToString());
|
Debug.Console(2, error.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -541,7 +563,7 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,13 +691,18 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
||||||
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
||||||
|
if (WaitingForSharedKey.Contains(clientIndex))
|
||||||
|
WaitingForSharedKey.Remove(clientIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
//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);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -739,7 +766,7 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
||||||
// server.State,
|
// server.State,
|
||||||
@@ -767,6 +794,7 @@ namespace PepperDash.Core
|
|||||||
if (numberOfBytesReceived > 0)
|
if (numberOfBytesReceived > 0)
|
||||||
{
|
{
|
||||||
string received = "Nothing";
|
string received = "Nothing";
|
||||||
|
var handler = TextReceivedQueueInvoke;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
||||||
@@ -781,31 +809,74 @@ namespace PepperDash.Core
|
|||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||||
mySecureTCPServer.SendData(clientIndex, b, b.Length);
|
mySecureTCPServer.SendData(clientIndex, b, b.Length);
|
||||||
mySecureTCPServer.Disconnect(clientIndex);
|
mySecureTCPServer.Disconnect(clientIndex);
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mySecureTCPServer.NumberOfClientsConnected > 0)
|
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
WaitingForSharedKey.Remove(clientIndex);
|
||||||
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
||||||
mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
||||||
OnServerClientReadyForCommunications(clientIndex);
|
OnServerClientReadyForCommunications(clientIndex);
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
//var address = mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
||||||
//Debug.Console(1, this, "Secure Server Listening on Port: {0}, client IP: {1}, Client Index: {4}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
|
{
|
||||||
// mySecureTCPServer.PortNumber.ToString(), address , numberOfBytesReceived.ToString(), received, clientIndex.ToString());
|
|
||||||
if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
|
||||||
onTextReceived(received, clientIndex);
|
onTextReceived(received, clientIndex);
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(received, clientIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
||||||
|
}
|
||||||
|
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||||
|
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
||||||
|
|
||||||
|
//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 (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
else
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
|
{
|
||||||
|
mySecureTCPServer.Disconnect(clientIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||||
|
/// It will dequeue items as they are enqueued automatically.
|
||||||
|
/// </summary>
|
||||||
|
void DequeueEvent()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
|
||||||
|
var message = MessageQueue.Dequeue();
|
||||||
|
var handler = TextReceivedQueueInvoke;
|
||||||
|
if (handler != null)
|
||||||
|
{
|
||||||
|
handler(this, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Console(2, "DequeueEvent error: {0}\r", 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -965,13 +1036,13 @@ namespace PepperDash.Core
|
|||||||
StopMonitorClient();
|
StopMonitorClient();
|
||||||
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||||
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
||||||
StartMonitorClient();
|
StartMonitorClient();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error,
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error,
|
||||||
"\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
|
"\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
|
||||||
MonitorClientMaxFailureCount);
|
MonitorClientMaxFailureCount);
|
||||||
|
|
||||||
|
|||||||
@@ -174,8 +174,8 @@ namespace PepperDash.Core
|
|||||||
if (Client != null)
|
if (Client != null)
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Program stopping. Closing connection");
|
Debug.Console(1, this, "Program stopping. Closing connection");
|
||||||
Client.Disconnect();
|
Disconnect();
|
||||||
Client.Dispose();
|
//Client.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,27 @@ namespace PepperDash.Core
|
|||||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||||
|
|
||||||
|
|
||||||
|
private string _Hostname { get; set;}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Address of server
|
/// Address of server
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Hostname { get; set; }
|
public string Hostname {
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _Hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_Hostname = value;
|
||||||
|
if (Client != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
Client.AddressClientConnectedTo = _Hostname;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Port on server
|
/// Port on server
|
||||||
@@ -233,10 +250,13 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
if (Client == null)
|
if (Client == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
Client = new TCPClient(Hostname, Port, BufferSize);
|
Client = new TCPClient(Hostname, Port, BufferSize);
|
||||||
Client.SocketStatusChange += Client_SocketStatusChange;
|
Client.SocketStatusChange += Client_SocketStatusChange;
|
||||||
}
|
}
|
||||||
DisconnectCalledByUser = false;
|
DisconnectCalledByUser = false;
|
||||||
|
|
||||||
Client.ConnectToServerAsync(ConnectToServerCallback); // (null);
|
Client.ConnectToServerAsync(ConnectToServerCallback); // (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +372,6 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public class TcpSshPropertiesConfig
|
public class TcpSshPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty(Required = Required.Always)]
|
[JsonProperty(Required = Required.Always)]
|
||||||
|
|||||||
@@ -328,7 +328,8 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
Client = new TCPClient(Hostname, Port, BufferSize);
|
Client = new TCPClient(Hostname, Port, BufferSize);
|
||||||
Client.SocketStatusChange += Client_SocketStatusChange;
|
Client.SocketStatusChange += Client_SocketStatusChange;
|
||||||
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
|
if(HeartbeatEnabled)
|
||||||
|
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
|
||||||
Client.AddressClientConnectedTo = Hostname;
|
Client.AddressClientConnectedTo = Hostname;
|
||||||
Client.PortNumber = Port;
|
Client.PortNumber = Port;
|
||||||
// SecureClient = c;
|
// SecureClient = c;
|
||||||
@@ -381,6 +382,15 @@ namespace PepperDash.Core
|
|||||||
//OnClientReadyForcommunications(false); // Should send false event
|
//OnClientReadyForcommunications(false); // Should send false event
|
||||||
}, 15000);
|
}, 15000);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
|
||||||
|
//required this is called by the shared key being negotiated
|
||||||
|
if (IsReadyForCommunication == false)
|
||||||
|
{
|
||||||
|
OnClientReadyForcommunications(true); // Key not required
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -485,7 +495,6 @@ namespace PepperDash.Core
|
|||||||
Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
|
Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
|
||||||
if (!string.IsNullOrEmpty(checkHeartbeat(str)))
|
if (!string.IsNullOrEmpty(checkHeartbeat(str)))
|
||||||
{
|
{
|
||||||
|
|
||||||
if (SharedKeyRequired && str == "SharedKey:")
|
if (SharedKeyRequired && str == "SharedKey:")
|
||||||
{
|
{
|
||||||
Debug.Console(1, this, "Server asking for shared key, sending");
|
Debug.Console(1, this, "Server asking for shared key, sending");
|
||||||
@@ -494,19 +503,11 @@ namespace PepperDash.Core
|
|||||||
else if (SharedKeyRequired && str == "Shared Key Match")
|
else if (SharedKeyRequired && str == "Shared Key Match")
|
||||||
{
|
{
|
||||||
StopWaitForSharedKeyTimer();
|
StopWaitForSharedKeyTimer();
|
||||||
|
|
||||||
|
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Shared key confirmed. Ready for communication");
|
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Shared key confirmed. Ready for communication");
|
||||||
OnClientReadyForcommunications(true); // Successful key exchange
|
OnClientReadyForcommunications(true); // Successful key exchange
|
||||||
}
|
}
|
||||||
else if (SharedKeyRequired == false && IsReadyForCommunication == false)
|
|
||||||
{
|
|
||||||
OnClientReadyForcommunications(true); // Key not required
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
//var bytesHandler = BytesReceived;
|
//var bytesHandler = BytesReceived;
|
||||||
//if (bytesHandler != null)
|
//if (bytesHandler != null)
|
||||||
// bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
// bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||||
@@ -698,6 +699,7 @@ namespace PepperDash.Core
|
|||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
|
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
|
||||||
|
|
||||||
OnConnectionChange();
|
OnConnectionChange();
|
||||||
|
|
||||||
// The client could be null or disposed by this time...
|
// The client could be null or disposed by this time...
|
||||||
if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -384,16 +384,20 @@ namespace PepperDash.Core
|
|||||||
if (myTcpServer == null)
|
if (myTcpServer == null)
|
||||||
{
|
{
|
||||||
myTcpServer = new TCPServer(Port, MaxClients);
|
myTcpServer = new TCPServer(Port, MaxClients);
|
||||||
myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
|
if(HeartbeatRequired)
|
||||||
|
myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
|
||||||
|
|
||||||
// myTcpServer.HandshakeTimeout = 30;
|
// myTcpServer.HandshakeTimeout = 30;
|
||||||
myTcpServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(TcpServer_SocketStatusChange);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
KillServer();
|
KillServer();
|
||||||
myTcpServer.PortNumber = Port;
|
myTcpServer.PortNumber = Port;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
|
||||||
|
myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
|
||||||
|
|
||||||
ServerStopped = false;
|
ServerStopped = false;
|
||||||
myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
|
||||||
OnServerStateChange(myTcpServer.State);
|
OnServerStateChange(myTcpServer.State);
|
||||||
@@ -418,22 +422,18 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
|
||||||
if (myTcpServer != null)
|
if (myTcpServer != null)
|
||||||
{
|
{
|
||||||
myTcpServer.Stop();
|
myTcpServer.Stop();
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
|
||||||
//SecureServer = null;
|
OnServerStateChange(myTcpServer.State);
|
||||||
}
|
}
|
||||||
|
ServerStopped = true;
|
||||||
ServerStopped = true;
|
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server Stopped");
|
|
||||||
|
|
||||||
OnServerStateChange(myTcpServer.State);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,11 +446,11 @@ namespace PepperDash.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
myTcpServer.Disconnect(client);
|
myTcpServer.Disconnect(client);
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -470,17 +470,17 @@ namespace PepperDash.Core
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
myTcpServer.Disconnect(i);
|
myTcpServer.Disconnect(i);
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
|
||||||
ConnectedClientsIndexes.Clear();
|
ConnectedClientsIndexes.Clear();
|
||||||
|
|
||||||
if (!ProgramIsStopping)
|
if (!ProgramIsStopping)
|
||||||
@@ -511,7 +511,7 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
|
SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
|
||||||
if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
|
||||||
Debug.Console(0, error.ToString());
|
Debug.Console(2, error.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,11 +670,13 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
if (ClientReadyAfterKeyExchange.Contains(clientIndex))
|
||||||
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
ClientReadyAfterKeyExchange.Remove(clientIndex);
|
||||||
|
if (WaitingForSharedKey.Contains(clientIndex))
|
||||||
|
WaitingForSharedKey.Remove(clientIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
|
||||||
}
|
}
|
||||||
@@ -740,7 +742,7 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
|
||||||
}
|
}
|
||||||
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
//Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
|
||||||
// server.State,
|
// server.State,
|
||||||
@@ -763,50 +765,51 @@ namespace PepperDash.Core
|
|||||||
/// <param name="mySecureTCPServer"></param>
|
/// <param name="mySecureTCPServer"></param>
|
||||||
/// <param name="clientIndex"></param>
|
/// <param name="clientIndex"></param>
|
||||||
/// <param name="numberOfBytesReceived"></param>
|
/// <param name="numberOfBytesReceived"></param>
|
||||||
void TcpServerReceivedDataAsyncCallback(TCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
|
void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
|
||||||
{
|
{
|
||||||
if (numberOfBytesReceived > 0)
|
if (numberOfBytesReceived > 0)
|
||||||
{
|
{
|
||||||
string received = "Nothing";
|
string received = "Nothing";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
byte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
|
||||||
received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
|
received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
|
||||||
if (WaitingForSharedKey.Contains(clientIndex))
|
if (WaitingForSharedKey.Contains(clientIndex))
|
||||||
{
|
{
|
||||||
received = received.Replace("\r", "");
|
received = received.Replace("\r", "");
|
||||||
received = received.Replace("\n", "");
|
received = received.Replace("\n", "");
|
||||||
if (received != SharedKey)
|
if (received != SharedKey)
|
||||||
{
|
{
|
||||||
byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
|
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);
|
Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
|
||||||
mySecureTCPServer.SendData(clientIndex, b, b.Length);
|
myTCPServer.SendData(clientIndex, b, b.Length);
|
||||||
mySecureTCPServer.Disconnect(clientIndex);
|
myTCPServer.Disconnect(clientIndex);
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
if (mySecureTCPServer.NumberOfClientsConnected > 0)
|
WaitingForSharedKey.Remove(clientIndex);
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
|
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
||||||
WaitingForSharedKey.Remove(clientIndex);
|
myTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
||||||
byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
|
OnServerClientReadyForCommunications(clientIndex);
|
||||||
mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
|
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
||||||
OnServerClientReadyForCommunications(clientIndex);
|
}
|
||||||
Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Client with index {0} provided the shared key and successfully connected to the server", clientIndex);
|
|
||||||
return;
|
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
||||||
}
|
onTextReceived(received, clientIndex);
|
||||||
//var address = mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
|
}
|
||||||
//Debug.Console(1, this, "Secure Server Listening on Port: {0}, client IP: {1}, Client Index: {4}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
|
catch (Exception ex)
|
||||||
// mySecureTCPServer.PortNumber.ToString(), address , numberOfBytesReceived.ToString(), received, clientIndex.ToString());
|
{
|
||||||
if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
||||||
onTextReceived(received, clientIndex);
|
}
|
||||||
}
|
if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
||||||
catch (Exception ex)
|
myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
|
||||||
{
|
}
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
|
else
|
||||||
}
|
{
|
||||||
}
|
// If numberOfBytesReceived <= 0
|
||||||
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
|
myTCPServer.Disconnect();
|
||||||
mySecureTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -966,13 +969,13 @@ namespace PepperDash.Core
|
|||||||
StopMonitorClient();
|
StopMonitorClient();
|
||||||
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
|
||||||
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
|
||||||
StartMonitorClient();
|
StartMonitorClient();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.Console(0, this, Debug.ErrorLogLevel.Error,
|
Debug.Console(2, this, Debug.ErrorLogLevel.Error,
|
||||||
"\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
|
"\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
|
||||||
MonitorClientMaxFailureCount);
|
MonitorClientMaxFailureCount);
|
||||||
|
|
||||||
|
|||||||
@@ -1,231 +1,351 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronSockets;
|
using Crestron.SimplSharp.CrestronSockets;
|
||||||
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
|
||||||
namespace PepperDash.Core
|
|
||||||
{
|
|
||||||
public class GenericUdpServer : Device, IBasicCommunication
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
/// <summary>
|
public class GenericUdpServer : Device, IBasicCommunication
|
||||||
///
|
{
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
///
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
|
||||||
///
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
///
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
|
||||||
///
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
/// This event will fire when a message is dequeued that includes the source IP and Port info if needed to determine the source of the received data.
|
||||||
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
/// </summary>
|
||||||
|
public event EventHandler<GenericUdpReceiveTextExtraArgs> DataRecievedExtra;
|
||||||
public SocketStatus ClientStatus
|
|
||||||
{
|
/// <summary>
|
||||||
get
|
/// Queue to temporarily store received messages with the source IP and Port info
|
||||||
{
|
/// </summary>
|
||||||
return Server.ServerStatus;
|
private CrestronQueue<GenericUdpReceiveTextExtraArgs> MessageQueue;
|
||||||
}
|
|
||||||
}
|
/// <summary>
|
||||||
|
///
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Address of server
|
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
|
||||||
/// </summary>
|
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
|
||||||
public string Hostname { get; set; }
|
|
||||||
|
public event EventHandler<GenericUdpConnectedEventArgs> UpdateConnectionStatus;
|
||||||
/// <summary>
|
|
||||||
/// Port on server
|
public SocketStatus ClientStatus
|
||||||
/// </summary>
|
{
|
||||||
public int Port { get; set; }
|
get
|
||||||
|
{
|
||||||
/// <summary>
|
return Server.ServerStatus;
|
||||||
/// Another damn S+ helper because S+ seems to treat large port nums as signed ints
|
}
|
||||||
/// which screws up things
|
}
|
||||||
/// </summary>
|
|
||||||
public ushort UPort
|
public ushort UStatus
|
||||||
{
|
{
|
||||||
get { return Convert.ToUInt16(Port); }
|
get { return (ushort)Server.ServerStatus; }
|
||||||
set { Port = Convert.ToInt32(value); }
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
CCriticalSection DequeueLock;
|
||||||
/// Indicates that the UDP Server is enabled
|
/// <summary>
|
||||||
/// </summary>
|
/// Address of server
|
||||||
public bool IsConnected
|
/// </summary>
|
||||||
{
|
public string Hostname { get; set; }
|
||||||
get;
|
|
||||||
private set;
|
/// <summary>
|
||||||
}
|
/// IP Address of the sender of the last recieved message
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
|
||||||
/// Defaults to 2000
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
public int BufferSize { get; set; }
|
/// Port on server
|
||||||
|
/// </summary>
|
||||||
public UDPServer Server { get; private set; }
|
public int Port { get; set; }
|
||||||
|
|
||||||
public GenericUdpServer(string key, string address, int port, int buffefSize)
|
/// <summary>
|
||||||
: base(key)
|
/// Another damn S+ helper because S+ seems to treat large port nums as signed ints
|
||||||
{
|
/// which screws up things
|
||||||
Hostname = address;
|
/// </summary>
|
||||||
Port = port;
|
public ushort UPort
|
||||||
BufferSize = buffefSize;
|
{
|
||||||
|
get { return Convert.ToUInt16(Port); }
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
set { Port = Convert.ToInt32(value); }
|
||||||
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
/// Indicates that the UDP Server is enabled
|
||||||
{
|
/// </summary>
|
||||||
// Re-enable the server if the link comes back up and the status should be connected
|
public bool IsConnected
|
||||||
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
|
{
|
||||||
&& IsConnected)
|
get;
|
||||||
{
|
private set;
|
||||||
Connect();
|
}
|
||||||
}
|
|
||||||
}
|
public ushort UIsConnected
|
||||||
|
{
|
||||||
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
get { return IsConnected ? (ushort)1 : (ushort)0; }
|
||||||
{
|
}
|
||||||
if (programEventType == eProgramStatusEventType.Stopping)
|
|
||||||
{
|
/// <summary>
|
||||||
Debug.Console(1, this, "Program stopping. Disabling Server");
|
/// Defaults to 2000
|
||||||
Disconnect();
|
/// </summary>
|
||||||
}
|
public int BufferSize { get; set; }
|
||||||
}
|
|
||||||
|
public UDPServer Server { get; private set; }
|
||||||
/// <summary>
|
|
||||||
/// Enables the UDP Server
|
/// <summary>
|
||||||
/// </summary>
|
/// Constructor for S+. Make sure to set key, address, port, and buffersize using init method
|
||||||
public void Connect()
|
/// </summary>
|
||||||
{
|
public GenericUdpServer()
|
||||||
if (Server == null)
|
: base("Uninitialized Udp Server")
|
||||||
{
|
{
|
||||||
Server = new UDPServer();
|
BufferSize = 5000;
|
||||||
|
DequeueLock = new CCriticalSection();
|
||||||
}
|
MessageQueue = new CrestronQueue<GenericUdpReceiveTextExtraArgs>();
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(Hostname))
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
{
|
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
|
||||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
|
}
|
||||||
return;
|
|
||||||
}
|
public GenericUdpServer(string key, string address, int port, int buffefSize)
|
||||||
if (Port < 1 || Port > 65535)
|
: base(key)
|
||||||
{
|
{
|
||||||
{
|
Hostname = address;
|
||||||
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key);
|
Port = port;
|
||||||
return;
|
BufferSize = buffefSize;
|
||||||
}
|
|
||||||
}
|
DequeueLock = new CCriticalSection();
|
||||||
|
MessageQueue = new CrestronQueue<GenericUdpReceiveTextExtraArgs>();
|
||||||
var status = Server.EnableUDPServer(Hostname, Port);
|
|
||||||
|
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
|
||||||
Debug.Console(2, this, "SocketErrorCode: {0}", status);
|
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
|
||||||
if (status == SocketErrorCodes.SOCKET_OK)
|
}
|
||||||
IsConnected = true;
|
|
||||||
|
public void Initialize(string key, string address, ushort port)
|
||||||
// Start receiving data
|
{
|
||||||
Server.ReceiveDataAsync(Receive);
|
Key = key;
|
||||||
}
|
Hostname = address;
|
||||||
|
UPort = port;
|
||||||
/// <summary>
|
}
|
||||||
/// Disabled the UDP Server
|
|
||||||
/// </summary>
|
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
|
||||||
public void Disconnect()
|
{
|
||||||
{
|
// Re-enable the server if the link comes back up and the status should be connected
|
||||||
if(Server != null)
|
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
|
||||||
Server.DisableUDPServer();
|
&& IsConnected)
|
||||||
|
{
|
||||||
IsConnected = false;
|
Connect();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||||
/// Recursive method to receive data
|
{
|
||||||
/// </summary>
|
if (programEventType == eProgramStatusEventType.Stopping)
|
||||||
/// <param name="server"></param>
|
{
|
||||||
/// <param name="numBytes"></param>
|
Debug.Console(1, this, "Program stopping. Disabling Server");
|
||||||
void Receive(UDPServer server, int numBytes)
|
Disconnect();
|
||||||
{
|
}
|
||||||
Debug.Console(2, this, "Received {0} bytes", numBytes);
|
}
|
||||||
|
|
||||||
if (numBytes > 0)
|
/// <summary>
|
||||||
{
|
/// Enables the UDP Server
|
||||||
var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();
|
/// </summary>
|
||||||
|
public void Connect()
|
||||||
Debug.Console(2, this, "Bytes: {0}", bytes.ToString());
|
{
|
||||||
var bytesHandler = BytesReceived;
|
if (Server == null)
|
||||||
if (bytesHandler != null)
|
{
|
||||||
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
Server = new UDPServer();
|
||||||
else
|
}
|
||||||
Debug.Console(2, this, "bytesHandler is null");
|
|
||||||
var textHandler = TextReceived;
|
if (string.IsNullOrEmpty(Hostname))
|
||||||
if (textHandler != null)
|
{
|
||||||
{
|
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
|
||||||
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
return;
|
||||||
Debug.Console(2, this, "RX: {0}", str);
|
}
|
||||||
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
if (Port < 1 || Port > 65535)
|
||||||
}
|
{
|
||||||
else
|
{
|
||||||
Debug.Console(2, this, "textHandler is null");
|
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key);
|
||||||
}
|
return;
|
||||||
server.ReceiveDataAsync(Receive);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
var status = Server.EnableUDPServer(Hostname, Port);
|
||||||
/// General send method
|
|
||||||
/// </summary>
|
Debug.Console(2, this, "SocketErrorCode: {0}", status);
|
||||||
/// <param name="text"></param>
|
if (status == SocketErrorCodes.SOCKET_OK)
|
||||||
public void SendText(string text)
|
IsConnected = true;
|
||||||
{
|
|
||||||
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
var handler = UpdateConnectionStatus;
|
||||||
|
if (handler != null)
|
||||||
if (IsConnected && Server != null)
|
handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
|
||||||
{
|
|
||||||
Debug.Console(2, this, "TX: {0}", text);
|
// Start receiving data
|
||||||
Server.SendData(bytes, bytes.Length);
|
Server.ReceiveDataAsync(Receive);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
public void SendBytes(byte[] bytes)
|
/// Disabled the UDP Server
|
||||||
{
|
/// </summary>
|
||||||
//if (Debug.Level == 2)
|
public void Disconnect()
|
||||||
// Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
{
|
||||||
if (IsConnected && Server != null)
|
if(Server != null)
|
||||||
Server.SendData(bytes, bytes.Length);
|
Server.DisableUDPServer();
|
||||||
}
|
|
||||||
|
IsConnected = false;
|
||||||
|
|
||||||
|
var handler = UpdateConnectionStatus;
|
||||||
}
|
if (handler != null)
|
||||||
|
handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
|
||||||
public class UdpServerPropertiesConfig
|
}
|
||||||
{
|
|
||||||
[JsonProperty(Required = Required.Always)]
|
|
||||||
public string Address { get; set; }
|
/// <summary>
|
||||||
|
/// Recursive method to receive data
|
||||||
[JsonProperty(Required = Required.Always)]
|
/// </summary>
|
||||||
public int Port { get; set; }
|
/// <param name="server"></param>
|
||||||
|
/// <param name="numBytes"></param>
|
||||||
/// <summary>
|
void Receive(UDPServer server, int numBytes)
|
||||||
/// Defaults to 32768
|
{
|
||||||
/// </summary>
|
Debug.Console(2, this, "Received {0} bytes", numBytes);
|
||||||
public int BufferSize { get; set; }
|
|
||||||
|
if (numBytes > 0)
|
||||||
public UdpServerPropertiesConfig()
|
{
|
||||||
{
|
var sourceIp = Server.IPAddressLastMessageReceivedFrom;
|
||||||
BufferSize = 32768;
|
var sourcePort = Server.IPPortLastMessageReceivedFrom;
|
||||||
}
|
var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();
|
||||||
}
|
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
|
||||||
|
MessageQueue.TryToEnqueue(new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));
|
||||||
|
|
||||||
|
Debug.Console(2, this, "Bytes: {0}", bytes.ToString());
|
||||||
|
var bytesHandler = BytesReceived;
|
||||||
|
if (bytesHandler != null)
|
||||||
|
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
|
||||||
|
else
|
||||||
|
Debug.Console(2, this, "bytesHandler is null");
|
||||||
|
var textHandler = TextReceived;
|
||||||
|
if (textHandler != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
Debug.Console(2, this, "RX: {0}", str);
|
||||||
|
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Debug.Console(2, this, "textHandler is null");
|
||||||
|
}
|
||||||
|
server.ReceiveDataAsync(Receive);
|
||||||
|
|
||||||
|
// Attempt to enter the CCritical Secion and if we can, start the dequeue thread
|
||||||
|
var gotLock = DequeueLock.TryEnter();
|
||||||
|
if (gotLock)
|
||||||
|
CrestronInvoke.BeginInvoke((o) => DequeueEvent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
|
||||||
|
/// It will dequeue items as they are enqueued automatically.
|
||||||
|
/// </summary>
|
||||||
|
void DequeueEvent()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
|
||||||
|
var message = MessageQueue.Dequeue();
|
||||||
|
var dataRecivedExtra = DataRecievedExtra;
|
||||||
|
if (dataRecivedExtra != null)
|
||||||
|
{
|
||||||
|
dataRecivedExtra(this, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.Console(0, "GenericUdpServer DequeueEvent error: {0}\r", 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// General send method
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
public void SendText(string text)
|
||||||
|
{
|
||||||
|
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
|
||||||
|
|
||||||
|
if (IsConnected && Server != null)
|
||||||
|
{
|
||||||
|
Debug.Console(2, this, "TX: {0}", text);
|
||||||
|
Server.SendData(bytes, bytes.Length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendBytes(byte[] bytes)
|
||||||
|
{
|
||||||
|
//if (Debug.Level == 2)
|
||||||
|
// Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
|
||||||
|
if (IsConnected && Server != null)
|
||||||
|
Server.SendData(bytes, bytes.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenericUdpReceiveTextExtraArgs : EventArgs
|
||||||
|
{
|
||||||
|
public string Text { get; private set; }
|
||||||
|
public string IpAddress { get; private set; }
|
||||||
|
public int Port { get; private set; }
|
||||||
|
public byte[] Bytes { get; private set; }
|
||||||
|
|
||||||
|
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
|
||||||
|
{
|
||||||
|
Text = text;
|
||||||
|
IpAddress = ipAddress;
|
||||||
|
Port = port;
|
||||||
|
Bytes = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stupid S+ Constructor
|
||||||
|
/// </summary>
|
||||||
|
public GenericUdpReceiveTextExtraArgs() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UdpServerPropertiesConfig
|
||||||
|
{
|
||||||
|
[JsonProperty(Required = Required.Always)]
|
||||||
|
public string Address { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty(Required = Required.Always)]
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defaults to 32768
|
||||||
|
/// </summary>
|
||||||
|
public int BufferSize { get; set; }
|
||||||
|
|
||||||
|
public UdpServerPropertiesConfig()
|
||||||
|
{
|
||||||
|
BufferSize = 32768;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Crestron.SimplSharp;
|
||||||
|
using PepperDash.Core;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace PepperDash.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Client config object for TCP client with server that inherits from TcpSshPropertiesConfig and adds properties for shared key and heartbeat
|
||||||
|
/// </summary>
|
||||||
|
public class TcpClientConfigObject
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// TcpSsh Properties
|
||||||
|
/// </summary>
|
||||||
|
public ControlPropertiesConfig Control { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
|
||||||
|
/// </summary>
|
||||||
|
public bool Secure { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
|
||||||
|
/// </summary>
|
||||||
|
public bool SharedKeyRequired { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The shared key that must match on the server and client
|
||||||
|
/// </summary>
|
||||||
|
public string SharedKey { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
|
||||||
|
/// heartbeats do not raise received events.
|
||||||
|
/// </summary>
|
||||||
|
public bool HeartbeatRequired { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The interval in seconds for the heartbeat from the client. If not received client is disconnected
|
||||||
|
/// </summary>
|
||||||
|
public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
|
||||||
|
/// </summary>
|
||||||
|
public string HeartbeatStringToMatch { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Receive Queue size must be greater than 20 or defaults to 20
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiveQueueSize { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,17 +6,55 @@ using Crestron.SimplSharp;
|
|||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
|
||||||
|
/// </summary>
|
||||||
public class TcpServerConfigObject
|
public class TcpServerConfigObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Uique key
|
||||||
|
/// </summary>
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
public bool Secure { get; set; }
|
/// <summary>
|
||||||
|
/// Max Clients that the server will allow to connect.
|
||||||
|
/// </summary>
|
||||||
public ushort MaxClients { get; set; }
|
public ushort MaxClients { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
|
||||||
|
/// </summary>
|
||||||
|
public bool Secure { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Port for the server to listen on
|
||||||
|
/// </summary>
|
||||||
public int Port { get; set; }
|
public int Port { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
|
||||||
|
/// </summary>
|
||||||
public bool SharedKeyRequired { get; set; }
|
public bool SharedKeyRequired { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The shared key that must match on the server and client
|
||||||
|
/// </summary>
|
||||||
public string SharedKey { get; set; }
|
public string SharedKey { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Require a heartbeat on the client/server connection that will cause the server/client to disconnect if the heartbeat is not received.
|
||||||
|
/// heartbeats do not raise received events.
|
||||||
|
/// </summary>
|
||||||
public bool HeartbeatRequired { get; set; }
|
public bool HeartbeatRequired { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The interval in seconds for the heartbeat from the client. If not received client is disconnected
|
||||||
|
/// </summary>
|
||||||
public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
|
public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
|
||||||
|
/// </summary>
|
||||||
public string HeartbeatStringToMatch { get; set; }
|
public string HeartbeatStringToMatch { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
|
||||||
|
/// </summary>
|
||||||
public int BufferSize { get; set; }
|
public int BufferSize { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// Receive Queue size must be greater than 20 or defaults to 20
|
||||||
|
/// </summary>
|
||||||
|
public int ReceiveQueueSize { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,9 @@ using Crestron.SimplSharp;
|
|||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Crestron Control Methods for a comm object
|
||||||
|
/// </summary>
|
||||||
public enum eControlMethod
|
public enum eControlMethod
|
||||||
{
|
{
|
||||||
None = 0, Com, IpId, IpidTcp, IR, Ssh, Tcpip, Telnet, Cresnet, Cec, Udp
|
None = 0, Com, IpId, IpidTcp, IR, Ssh, Tcpip, Telnet, Cresnet, Cec, Udp
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ namespace PepperDash.Core.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="doubleConfig"></param>
|
/// <param name="doubleConfig"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
static JObject MergeConfigs(JObject doubleConfig)
|
public static JObject MergeConfigs(JObject doubleConfig)
|
||||||
{
|
{
|
||||||
var system = JObject.FromObject(doubleConfig["system"]);
|
var system = JObject.FromObject(doubleConfig["system"]);
|
||||||
var template = JObject.FromObject(doubleConfig["template"]);
|
var template = JObject.FromObject(doubleConfig["template"]);
|
||||||
@@ -114,28 +114,35 @@ namespace PepperDash.Core.Config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Merges the contents of a base and a delta array, matching the entries on a top-level property
|
/// Merges the contents of a base and a delta array, matching the entries on a top-level property
|
||||||
/// given by propertyName. Returns a merge of them. Items in the delta array that do not have
|
/// given by propertyName. Returns a merge of them. Items in the delta array that do not have
|
||||||
/// a matched item in base array will not be merged.
|
/// a matched item in base array will not be merged. Non keyed system items will replace the template items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName, string path)
|
static JArray MergeArraysOnTopLevelProperty(JArray a1, JArray a2, string propertyName, string path)
|
||||||
{
|
{
|
||||||
var result = new JArray();
|
var result = new JArray();
|
||||||
if (a2 == null)
|
if (a2 == null || a2.Count == 0) // If the system array is null or empty, return the template array
|
||||||
result = a1;
|
return a1;
|
||||||
else if (a1 != null)
|
else if (a1 != null)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < a1.Count(); i++)
|
if (a2[0]["key"] == null) // If the first item in the system array has no key, overwrite the template array
|
||||||
{
|
{ // with the system array
|
||||||
var a1Dev = a1[i];
|
return a2;
|
||||||
// Try to get a system device and if found, merge it onto template
|
}
|
||||||
var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value<int>("uid") == tmplDev.Value<int>("uid"));
|
else // The arrays are keyed, merge them by key
|
||||||
if (a2Match != null)
|
{
|
||||||
{
|
for (int i = 0; i < a1.Count(); i++)
|
||||||
var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));
|
{
|
||||||
result.Add(mergedItem);
|
var a1Dev = a1[i];
|
||||||
}
|
// Try to get a system device and if found, merge it onto template
|
||||||
else
|
var a2Match = a2.FirstOrDefault(t => t[propertyName].Equals(a1Dev[propertyName]));// t.Value<int>("uid") == tmplDev.Value<int>("uid"));
|
||||||
result.Add(a1Dev);
|
if (a2Match != null)
|
||||||
}
|
{
|
||||||
|
var mergedItem = Merge(a1Dev, a2Match, string.Format("{0}[{1}].", path, i));// Merge(JObject.FromObject(a1Dev), JObject.FromObject(a2Match));
|
||||||
|
result.Add(mergedItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result.Add(a1Dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,25 @@ using Crestron.SimplSharp;
|
|||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unique key interface to require a unique key for the class
|
||||||
|
/// </summary>
|
||||||
public interface IKeyed
|
public interface IKeyed
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unique Key
|
||||||
|
/// </summary>
|
||||||
string Key { get; }
|
string Key { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Named Keyed device interface. Forces the devie to have a Unique Key and a name.
|
||||||
|
/// </summary>
|
||||||
public interface IKeyName : IKeyed
|
public interface IKeyName : IKeyed
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Isn't it obvious :)
|
||||||
|
/// </summary>
|
||||||
string Name { get; }
|
string Name { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,8 +10,17 @@ namespace PepperDash.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Device : IKeyName
|
public class Device : IKeyName
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Unique Key
|
||||||
|
/// </summary>
|
||||||
public string Key { get; protected set; }
|
public string Key { get; protected set; }
|
||||||
public string Name { get; protected set; }
|
/// <summary>
|
||||||
|
/// Name of the devie
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; protected set; }
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
public bool Enabled { get; protected set; }
|
public bool Enabled { get; protected set; }
|
||||||
|
|
||||||
///// <summary>
|
///// <summary>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using Crestron.SimplSharp;
|
|||||||
|
|
||||||
namespace PepperDash.Core.JsonToSimpl
|
namespace PepperDash.Core.JsonToSimpl
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constants for Simpl modules
|
||||||
|
/// </summary>
|
||||||
public class JsonToSimplConstants
|
public class JsonToSimplConstants
|
||||||
{
|
{
|
||||||
public const ushort JsonIsValidBoolChange = 2;
|
public const ushort JsonIsValidBoolChange = 2;
|
||||||
@@ -13,6 +16,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
public const ushort BoolValueChange = 1;
|
public const ushort BoolValueChange = 1;
|
||||||
public const ushort UshortValueChange = 101;
|
public const ushort UshortValueChange = 101;
|
||||||
public const ushort StringValueChange = 201;
|
public const ushort StringValueChange = 201;
|
||||||
|
public const ushort FullPathToArrayChange = 202;
|
||||||
}
|
}
|
||||||
|
|
||||||
//**************************************************************************************************//
|
//**************************************************************************************************//
|
||||||
|
|||||||
@@ -11,10 +11,19 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
|
public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
|
||||||
{
|
{
|
||||||
public string SearchPropertyName { get; set; }
|
public string SearchPropertyName { get; set; }
|
||||||
public string SearchPropertyValue { get; set; }
|
public string SearchPropertyValue { get; set; }
|
||||||
|
|
||||||
int ArrayIndex;
|
int ArrayIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For <2.4.1 array lookups
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="pathPrefix"></param>
|
||||||
|
/// <param name="pathSuffix"></param>
|
||||||
|
/// <param name="searchPropertyName"></param>
|
||||||
|
/// <param name="searchPropertyValue"></param>
|
||||||
public void Initialize(string file, string key, string pathPrefix, string pathSuffix,
|
public void Initialize(string file, string key, string pathPrefix, string pathSuffix,
|
||||||
string searchPropertyName, string searchPropertyValue)
|
string searchPropertyName, string searchPropertyValue)
|
||||||
{
|
{
|
||||||
@@ -24,6 +33,28 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For newer >=2.4.1 array lookups.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="file"></param>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="pathPrefix"></param>
|
||||||
|
/// <param name="pathAppend"></param>
|
||||||
|
/// <param name="pathSuffix"></param>
|
||||||
|
/// <param name="searchPropertyName"></param>
|
||||||
|
/// <param name="searchPropertyValue"></param>
|
||||||
|
public void InitializeWithAppend(string file, string key, string pathPrefix, string pathAppend,
|
||||||
|
string pathSuffix, string searchPropertyName, string searchPropertyValue)
|
||||||
|
{
|
||||||
|
string pathPrefixWithAppend = (pathPrefix != null ? pathPrefix : "") + GetPathAppend(pathAppend);
|
||||||
|
base.Initialize(file, key, pathPrefixWithAppend, pathSuffix);
|
||||||
|
|
||||||
|
SearchPropertyName = searchPropertyName;
|
||||||
|
SearchPropertyValue = searchPropertyValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//PathPrefix+ArrayName+[x]+path+PathSuffix
|
//PathPrefix+ArrayName+[x]+path+PathSuffix
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
@@ -32,9 +63,10 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected override string GetFullPath(string path)
|
protected override string GetFullPath(string path)
|
||||||
{
|
{
|
||||||
return string.Format("{0}[{1}].{2}{3}",
|
return string.Format("{0}[{1}].{2}{3}",
|
||||||
PathPrefix == null ? "" : PathPrefix,
|
PathPrefix == null ? "" : PathPrefix,
|
||||||
ArrayIndex, path,
|
ArrayIndex,
|
||||||
|
path,
|
||||||
PathSuffix == null ? "" : PathSuffix);
|
PathSuffix == null ? "" : PathSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +76,30 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
base.ProcessAll();
|
base.ProcessAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides the path append for GetFullPath
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
string GetPathAppend(string a)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(a))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (a.StartsWith("."))
|
||||||
|
{
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return "." + a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
bool FindInArray()
|
bool FindInArray()
|
||||||
{
|
{
|
||||||
if (Master == null)
|
if (Master == null)
|
||||||
@@ -51,41 +107,45 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
if (Master.JsonObject == null)
|
if (Master.JsonObject == null)
|
||||||
throw new InvalidOperationException("Cannot do operations before master JSON has read");
|
throw new InvalidOperationException("Cannot do operations before master JSON has read");
|
||||||
if (PathPrefix == null)
|
if (PathPrefix == null)
|
||||||
throw new InvalidOperationException("Cannot do operations before PathPrefix is set");
|
throw new InvalidOperationException("Cannot do operations before PathPrefix is set");
|
||||||
|
|
||||||
var token = Master.JsonObject.SelectToken(PathPrefix);
|
|
||||||
if (token is JArray)
|
var token = Master.JsonObject.SelectToken(PathPrefix);
|
||||||
{
|
if (token is JArray)
|
||||||
var array = token as JArray;
|
{
|
||||||
try
|
var array = token as JArray;
|
||||||
{
|
try
|
||||||
var item = array.FirstOrDefault(o =>
|
{
|
||||||
{
|
var item = array.FirstOrDefault(o =>
|
||||||
var prop = o[SearchPropertyName];
|
{
|
||||||
return prop != null && prop.Value<string>()
|
var prop = o[SearchPropertyName];
|
||||||
.Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);
|
return prop != null && prop.Value<string>()
|
||||||
});
|
.Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);
|
||||||
if (item == null)
|
});
|
||||||
{
|
if (item == null)
|
||||||
Debug.Console(1, "JSON Child[{0}] Array '{1}' '{2}={3}' not found: ", Key,
|
{
|
||||||
PathPrefix, SearchPropertyName, SearchPropertyValue);
|
Debug.Console(1, "JSON Child[{0}] Array '{1}' '{2}={3}' not found: ", Key,
|
||||||
this.LinkedToObject = false;
|
PathPrefix, SearchPropertyName, SearchPropertyValue);
|
||||||
return false;
|
this.LinkedToObject = false;
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
this.LinkedToObject = true;
|
|
||||||
ArrayIndex = array.IndexOf(item);
|
this.LinkedToObject = true;
|
||||||
Debug.Console(1, "JSON Child[{0}] Found array match at index {1}", Key, ArrayIndex);
|
ArrayIndex = array.IndexOf(item);
|
||||||
return true;
|
OnStringChange(string.Format("{0}[{1}]", PathPrefix, ArrayIndex), 0, JsonToSimplConstants.FullPathToArrayChange);
|
||||||
}
|
Debug.Console(1, "JSON Child[{0}] Found array match at index {1}", Key, ArrayIndex);
|
||||||
catch (Exception e)
|
return true;
|
||||||
{
|
}
|
||||||
Debug.Console(1, "JSON Child[{0}] Array '{1}' lookup error: '{2}={3}'\r{4}", Key,
|
catch (Exception e)
|
||||||
PathPrefix, SearchPropertyName, SearchPropertyValue, e);
|
{
|
||||||
}
|
Debug.Console(1, "JSON Child[{0}] Array '{1}' lookup error: '{2}={3}'\r{4}", Key,
|
||||||
|
PathPrefix, SearchPropertyName, SearchPropertyValue, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Console(1, "JSON Child[{0}] Path '{1}' is not an array", Key, PathPrefix);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
Debug.Console(1, "JSON Child[{0}] Path '{1}' is not an array", Key, PathPrefix);
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
/// This will be prepended to all paths to allow path swapping or for more organized
|
/// This will be prepended to all paths to allow path swapping or for more organized
|
||||||
/// sub-paths
|
/// sub-paths
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string PathPrefix { get; protected set; }
|
public string PathPrefix { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is added to the end of all paths
|
/// This is added to the end of all paths
|
||||||
@@ -285,7 +285,7 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
protected virtual string GetFullPath(string path)
|
protected virtual string GetFullPath(string path)
|
||||||
{
|
{
|
||||||
return (PathPrefix != null ? PathPrefix : "") +
|
return (PathPrefix != null ? PathPrefix : "") +
|
||||||
path + (PathSuffix != null ? PathSuffix : "");
|
path + (PathSuffix != null ? PathSuffix : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helpers for events
|
// Helpers for events
|
||||||
|
|||||||
@@ -106,10 +106,10 @@ namespace PepperDash.Core.JsonToSimpl
|
|||||||
/// <param name="child"></param>
|
/// <param name="child"></param>
|
||||||
public void AddChild(JsonToSimplChildObjectBase child)
|
public void AddChild(JsonToSimplChildObjectBase child)
|
||||||
{
|
{
|
||||||
if (Children.Contains(child)) {
|
if (!Children.Contains(child))
|
||||||
Children.Remove(child);
|
{
|
||||||
}
|
Children.Add(child);
|
||||||
Children.Add(child);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,393 +1,421 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharp.CrestronLogger;
|
using Crestron.SimplSharp.Reflection;
|
||||||
using Crestron.SimplSharp.CrestronIO;
|
using Crestron.SimplSharp.CrestronLogger;
|
||||||
using Newtonsoft.Json;
|
using Crestron.SimplSharp.CrestronIO;
|
||||||
using PepperDash.Core.DebugThings;
|
using Newtonsoft.Json;
|
||||||
|
using PepperDash.Core.DebugThings;
|
||||||
|
|
||||||
namespace PepperDash.Core
|
|
||||||
{
|
namespace PepperDash.Core
|
||||||
public static class Debug
|
{
|
||||||
{
|
public static class Debug
|
||||||
/// <summary>
|
{
|
||||||
/// Describes the folder location where a given program stores it's debug level memory. By default, the
|
/// <summary>
|
||||||
/// file written will be named appNdebug where N is 1-10.
|
/// Describes the folder location where a given program stores it's debug level memory. By default, the
|
||||||
/// </summary>
|
/// file written will be named appNdebug where N is 1-10.
|
||||||
public static string FilePathPrefix = @"\nvram\debug\";
|
/// </summary>
|
||||||
|
public static string FilePathPrefix = @"\nvram\debug\";
|
||||||
/// <summary>
|
|
||||||
/// The name of the file containing the current debug settings.
|
/// <summary>
|
||||||
/// </summary>
|
/// The name of the file containing the current debug settings.
|
||||||
public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
/// </summary>
|
||||||
|
public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
|
||||||
public static int Level { get; private set; }
|
|
||||||
|
public static int Level { get; private set; }
|
||||||
static DebugContextCollection Contexts;
|
|
||||||
|
static DebugContextCollection Contexts;
|
||||||
static int SaveTimeoutMs = 30000;
|
|
||||||
|
static int SaveTimeoutMs = 30000;
|
||||||
static CTimer SaveTimer;
|
|
||||||
|
public static string PepperDashCoreVersion { get; private set; }
|
||||||
/// <summary>
|
|
||||||
/// When true, the IncludedExcludedKeys dict will contain keys to include.
|
static CTimer SaveTimer;
|
||||||
/// When false (default), IncludedExcludedKeys will contain keys to exclude.
|
|
||||||
/// </summary>
|
/// <summary>
|
||||||
static bool ExcludeAllMode;
|
/// When true, the IncludedExcludedKeys dict will contain keys to include.
|
||||||
|
/// When false (default), IncludedExcludedKeys will contain keys to exclude.
|
||||||
//static bool ExcludeNoKeyMessages;
|
/// </summary>
|
||||||
|
static bool ExcludeAllMode;
|
||||||
static Dictionary<string, object> IncludedExcludedKeys;
|
|
||||||
|
//static bool ExcludeNoKeyMessages;
|
||||||
static Debug()
|
|
||||||
{
|
static Dictionary<string, object> IncludedExcludedKeys;
|
||||||
IncludedExcludedKeys = new Dictionary<string, object>();
|
|
||||||
|
static Debug()
|
||||||
//CrestronDataStoreStatic.InitCrestronDataStore();
|
{
|
||||||
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
// Get the assembly version and print it to console and the log
|
||||||
{
|
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||||
// Add command to console
|
|
||||||
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
|
PepperDashCoreVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
|
||||||
"appdebug:P [0-2]: Sets the application's console debug message level",
|
|
||||||
ConsoleAccessLevelEnum.AccessOperator);
|
var msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, PepperDashCoreVersion);
|
||||||
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
|
||||||
"appdebuglog:P [all] Use \"all\" for full log.",
|
CrestronConsole.PrintLine(msg);
|
||||||
ConsoleAccessLevelEnum.AccessOperator);
|
|
||||||
CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
|
LogError(ErrorLogLevel.Notice, msg);
|
||||||
"appdebugclear:P Clears the current custom log",
|
|
||||||
ConsoleAccessLevelEnum.AccessOperator);
|
IncludedExcludedKeys = new Dictionary<string, object>();
|
||||||
CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
|
|
||||||
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
|
//CrestronDataStoreStatic.InitCrestronDataStore();
|
||||||
}
|
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
|
||||||
|
{
|
||||||
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
// Add command to console
|
||||||
|
CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
|
||||||
LoadMemory();
|
"appdebug:P [0-2]: Sets the application's console debug message level",
|
||||||
Level = Contexts.GetOrCreateItem("DEFAULT").Level;
|
ConsoleAccessLevelEnum.AccessOperator);
|
||||||
|
CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
|
||||||
CrestronLogger.Initialize(2, LoggerModeEnum.RM); // Use RM instead of DEFAULT as not to double-up console messages.
|
"appdebuglog:P [all] Use \"all\" for full log.",
|
||||||
}
|
ConsoleAccessLevelEnum.AccessOperator);
|
||||||
|
CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
|
||||||
/// <summary>
|
"appdebugclear:P Clears the current custom log",
|
||||||
/// Used to save memory when shutting down
|
ConsoleAccessLevelEnum.AccessOperator);
|
||||||
/// </summary>
|
CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
|
||||||
/// <param name="programEventType"></param>
|
"appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
|
||||||
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
}
|
||||||
{
|
|
||||||
if (programEventType == eProgramStatusEventType.Stopping)
|
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
|
||||||
{
|
|
||||||
if (SaveTimer != null)
|
LoadMemory();
|
||||||
{
|
Level = Contexts.GetOrCreateItem("DEFAULT").Level;
|
||||||
SaveTimer.Stop();
|
|
||||||
SaveTimer = null;
|
try
|
||||||
}
|
{
|
||||||
Console(0, "Saving debug settings");
|
if (InitialParametersClass.NumberOfRemovableDrives > 0)
|
||||||
SaveMemory();
|
{
|
||||||
}
|
CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives);
|
||||||
}
|
CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages.
|
||||||
|
}
|
||||||
/// <summary>
|
else
|
||||||
/// Callback for console command
|
CrestronConsole.PrintLine("No RM Drive(s) Present.");
|
||||||
/// </summary>
|
}
|
||||||
/// <param name="levelString"></param>
|
catch (Exception e)
|
||||||
public static void SetDebugFromConsole(string levelString)
|
{
|
||||||
{
|
|
||||||
try
|
CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
|
||||||
{
|
}
|
||||||
if (string.IsNullOrEmpty(levelString.Trim()))
|
}
|
||||||
{
|
|
||||||
CrestronConsole.PrintLine("AppDebug level = {0}", Level);
|
/// <summary>
|
||||||
return;
|
/// Used to save memory when shutting down
|
||||||
}
|
/// </summary>
|
||||||
|
/// <param name="programEventType"></param>
|
||||||
SetDebugLevel(Convert.ToInt32(levelString));
|
static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
|
||||||
}
|
{
|
||||||
catch
|
if (programEventType == eProgramStatusEventType.Stopping)
|
||||||
{
|
{
|
||||||
CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
|
if (SaveTimer != null)
|
||||||
}
|
{
|
||||||
}
|
SaveTimer.Stop();
|
||||||
|
SaveTimer = null;
|
||||||
public static void SetDebugFilterFromConsole(string items)
|
}
|
||||||
{
|
Console(0, "Saving debug settings");
|
||||||
var str = items.Trim();
|
SaveMemory();
|
||||||
if (str == "?")
|
}
|
||||||
{
|
}
|
||||||
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
|
||||||
"+all: at beginning puts filter into 'default include' mode\r" +
|
/// <summary>
|
||||||
" All keys that follow will be excluded from output.\r" +
|
/// Callback for console command
|
||||||
"-all: at beginning puts filter into 'default excluse all' mode.\r" +
|
/// </summary>
|
||||||
" All keys that follow will be the only keys that are shown\r" +
|
/// <param name="levelString"></param>
|
||||||
"+nokey: Enables messages with no key (default)\r" +
|
public static void SetDebugFromConsole(string levelString)
|
||||||
"-nokey: Disables messages with no key.\r" +
|
{
|
||||||
"(nokey settings are independent of all other settings)");
|
try
|
||||||
return;
|
{
|
||||||
}
|
if (string.IsNullOrEmpty(levelString.Trim()))
|
||||||
var keys = Regex.Split(str, @"\s*");
|
{
|
||||||
foreach (var keyToken in keys)
|
CrestronConsole.PrintLine("AppDebug level = {0}", Level);
|
||||||
{
|
return;
|
||||||
var lkey = keyToken.ToLower();
|
}
|
||||||
if (lkey == "+all")
|
|
||||||
{
|
SetDebugLevel(Convert.ToInt32(levelString));
|
||||||
IncludedExcludedKeys.Clear();
|
}
|
||||||
ExcludeAllMode = false;
|
catch
|
||||||
}
|
{
|
||||||
else if (lkey == "-all")
|
CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
|
||||||
{
|
}
|
||||||
IncludedExcludedKeys.Clear();
|
}
|
||||||
ExcludeAllMode = true;
|
|
||||||
}
|
public static void SetDebugFilterFromConsole(string items)
|
||||||
//else if (lkey == "+nokey")
|
{
|
||||||
//{
|
var str = items.Trim();
|
||||||
// ExcludeNoKeyMessages = false;
|
if (str == "?")
|
||||||
//}
|
{
|
||||||
//else if (lkey == "-nokey")
|
CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
|
||||||
//{
|
"+all: at beginning puts filter into 'default include' mode\r" +
|
||||||
// ExcludeNoKeyMessages = true;
|
" All keys that follow will be excluded from output.\r" +
|
||||||
//}
|
"-all: at beginning puts filter into 'default excluse all' mode.\r" +
|
||||||
else
|
" All keys that follow will be the only keys that are shown\r" +
|
||||||
{
|
"+nokey: Enables messages with no key (default)\r" +
|
||||||
string key = null; ;
|
"-nokey: Disables messages with no key.\r" +
|
||||||
if (lkey.StartsWith("-"))
|
"(nokey settings are independent of all other settings)");
|
||||||
{
|
return;
|
||||||
key = lkey.Substring(1);
|
}
|
||||||
// if in exclude all mode, we need to remove this from the inclusions
|
var keys = Regex.Split(str, @"\s*");
|
||||||
if (ExcludeAllMode)
|
foreach (var keyToken in keys)
|
||||||
{
|
{
|
||||||
if (IncludedExcludedKeys.ContainsKey(key))
|
var lkey = keyToken.ToLower();
|
||||||
IncludedExcludedKeys.Remove(key);
|
if (lkey == "+all")
|
||||||
}
|
{
|
||||||
// otherwise include all mode, add to the exclusions
|
IncludedExcludedKeys.Clear();
|
||||||
else
|
ExcludeAllMode = false;
|
||||||
{
|
}
|
||||||
IncludedExcludedKeys[key] = new object();
|
else if (lkey == "-all")
|
||||||
}
|
{
|
||||||
}
|
IncludedExcludedKeys.Clear();
|
||||||
else if (lkey.StartsWith("+"))
|
ExcludeAllMode = true;
|
||||||
{
|
}
|
||||||
key = lkey.Substring(1);
|
//else if (lkey == "+nokey")
|
||||||
// if in exclude all mode, we need to add this as inclusion
|
//{
|
||||||
if (ExcludeAllMode)
|
// ExcludeNoKeyMessages = false;
|
||||||
{
|
//}
|
||||||
|
//else if (lkey == "-nokey")
|
||||||
IncludedExcludedKeys[key] = new object();
|
//{
|
||||||
}
|
// ExcludeNoKeyMessages = true;
|
||||||
// otherwise include all mode, remove this from exclusions
|
//}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (IncludedExcludedKeys.ContainsKey(key))
|
string key = null; ;
|
||||||
IncludedExcludedKeys.Remove(key);
|
if (lkey.StartsWith("-"))
|
||||||
}
|
{
|
||||||
}
|
key = lkey.Substring(1);
|
||||||
}
|
// if in exclude all mode, we need to remove this from the inclusions
|
||||||
}
|
if (ExcludeAllMode)
|
||||||
}
|
{
|
||||||
|
if (IncludedExcludedKeys.ContainsKey(key))
|
||||||
|
IncludedExcludedKeys.Remove(key);
|
||||||
/// <summary>
|
}
|
||||||
/// Sets the debug level
|
// otherwise include all mode, add to the exclusions
|
||||||
/// </summary>
|
else
|
||||||
/// <param name="level"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>
|
{
|
||||||
public static void SetDebugLevel(int level)
|
IncludedExcludedKeys[key] = new object();
|
||||||
{
|
}
|
||||||
if (level <= 2)
|
}
|
||||||
{
|
else if (lkey.StartsWith("+"))
|
||||||
Level = level;
|
{
|
||||||
Contexts.GetOrCreateItem("DEFAULT").Level = level;
|
key = lkey.Substring(1);
|
||||||
SaveMemoryOnTimeout();
|
// if in exclude all mode, we need to add this as inclusion
|
||||||
|
if (ExcludeAllMode)
|
||||||
CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
|
{
|
||||||
InitialParametersClass.ApplicationNumber, Level);
|
|
||||||
|
IncludedExcludedKeys[key] = new object();
|
||||||
//var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level);
|
}
|
||||||
//if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
// otherwise include all mode, remove this from exclusions
|
||||||
// CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err);
|
else
|
||||||
}
|
{
|
||||||
}
|
if (IncludedExcludedKeys.ContainsKey(key))
|
||||||
|
IncludedExcludedKeys.Remove(key);
|
||||||
/// <summary>
|
}
|
||||||
///
|
}
|
||||||
/// </summary>
|
}
|
||||||
public static void ShowDebugLog(string s)
|
}
|
||||||
{
|
}
|
||||||
var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
|
|
||||||
foreach (var l in loglist)
|
|
||||||
CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
|
/// <summary>
|
||||||
}
|
/// Sets the debug level
|
||||||
|
/// </summary>
|
||||||
/// <summary>
|
/// <param name="level"> Valid values 0 (no debug), 1 (critical), 2 (all messages)</param>
|
||||||
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
public static void SetDebugLevel(int level)
|
||||||
/// Uses CrestronConsole.PrintLine.
|
{
|
||||||
/// </summary>
|
if (level <= 2)
|
||||||
/// <param name="level"></param>
|
{
|
||||||
/// <param name="format">Console format string</param>
|
Level = level;
|
||||||
/// <param name="items">Object parameters</param>
|
Contexts.GetOrCreateItem("DEFAULT").Level = level;
|
||||||
public static void Console(uint level, string format, params object[] items)
|
SaveMemoryOnTimeout();
|
||||||
{
|
|
||||||
if (Level >= level)
|
CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
|
||||||
CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
|
InitialParametersClass.ApplicationNumber, Level);
|
||||||
string.Format(format, items));
|
|
||||||
}
|
//var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level);
|
||||||
|
//if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
|
||||||
/// <summary>
|
// CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err);
|
||||||
/// Logs to Console when at-level, and all messages to error log, including device key
|
}
|
||||||
/// </summary>
|
}
|
||||||
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
|
||||||
{
|
/// <summary>
|
||||||
if (Level >= level)
|
///
|
||||||
Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
|
/// </summary>
|
||||||
}
|
public static void ShowDebugLog(string s)
|
||||||
|
{
|
||||||
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
|
||||||
string format, params object[] items)
|
foreach (var l in loglist)
|
||||||
{
|
CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
|
||||||
var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
|
}
|
||||||
LogError(errorLogLevel, str);
|
|
||||||
if (Level >= level)
|
/// <summary>
|
||||||
{
|
/// Prints message to console if current debug level is equal to or higher than the level of this message.
|
||||||
Console(level, str);
|
/// Uses CrestronConsole.PrintLine.
|
||||||
}
|
/// </summary>
|
||||||
}
|
/// <param name="level"></param>
|
||||||
|
/// <param name="format">Console format string</param>
|
||||||
/// <summary>
|
/// <param name="items">Object parameters</param>
|
||||||
/// Logs to Console when at-level, and all messages to error log
|
public static void Console(uint level, string format, params object[] items)
|
||||||
/// </summary>
|
{
|
||||||
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
if (Level >= level)
|
||||||
string format, params object[] items)
|
CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
|
||||||
{
|
string.Format(format, items));
|
||||||
var str = string.Format(format, items);
|
}
|
||||||
LogError(errorLogLevel, str);
|
|
||||||
if (Level >= level)
|
/// <summary>
|
||||||
{
|
/// Logs to Console when at-level, and all messages to error log, including device key
|
||||||
Console(level, str);
|
/// </summary>
|
||||||
}
|
public static void Console(uint level, IKeyed dev, string format, params object[] items)
|
||||||
}
|
{
|
||||||
|
if (Level >= level)
|
||||||
/// <summary>
|
Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
|
||||||
/// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
|
}
|
||||||
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
|
||||||
/// it will only be written to the log.
|
public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
|
||||||
/// </summary>
|
string format, params object[] items)
|
||||||
/// <param name="level"></param>
|
{
|
||||||
/// <param name="format"></param>
|
var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
|
||||||
/// <param name="items"></param>
|
LogError(errorLogLevel, str);
|
||||||
public static void ConsoleWithLog(uint level, string format, params object[] items)
|
if (Level >= level)
|
||||||
{
|
{
|
||||||
var str = string.Format(format, items);
|
Console(level, str);
|
||||||
if (Level >= level)
|
}
|
||||||
CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
}
|
||||||
CrestronLogger.WriteToLog(str, level);
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Logs to Console when at-level, and all messages to error log
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
|
public static void Console(uint level, ErrorLogLevel errorLogLevel,
|
||||||
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
string format, params object[] items)
|
||||||
/// it will only be written to the log.
|
{
|
||||||
/// </summary>
|
var str = string.Format(format, items);
|
||||||
/// <param name="level"></param>
|
LogError(errorLogLevel, str);
|
||||||
/// <param name="dev"></param>
|
if (Level >= level)
|
||||||
/// <param name="format">String.format string</param>
|
{
|
||||||
/// <param name="items">Parameters for substitution in the format string.</param>
|
Console(level, str);
|
||||||
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
}
|
||||||
{
|
}
|
||||||
var str = string.Format(format, items);
|
|
||||||
if (Level >= level)
|
/// <summary>
|
||||||
ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
|
/// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
|
||||||
}
|
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
||||||
|
/// it will only be written to the log.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Prints to log and error log
|
/// <param name="level"></param>
|
||||||
/// </summary>
|
/// <param name="format"></param>
|
||||||
/// <param name="errorLogLevel"></param>
|
/// <param name="items"></param>
|
||||||
/// <param name="str"></param>
|
public static void ConsoleWithLog(uint level, string format, params object[] items)
|
||||||
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
{
|
||||||
{
|
var str = string.Format(format, items);
|
||||||
string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
if (Level >= level)
|
||||||
switch (errorLogLevel)
|
CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||||
{
|
CrestronLogger.WriteToLog(str, level);
|
||||||
case ErrorLogLevel.Error:
|
}
|
||||||
ErrorLog.Error(msg);
|
|
||||||
break;
|
/// <summary>
|
||||||
case ErrorLogLevel.Warning:
|
/// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
|
||||||
ErrorLog.Warn(msg);
|
/// or above the level provided, then the output will be written to both console and the log. Otherwise
|
||||||
break;
|
/// it will only be written to the log.
|
||||||
case ErrorLogLevel.Notice:
|
/// </summary>
|
||||||
ErrorLog.Notice(msg);
|
/// <param name="level"></param>
|
||||||
break;
|
/// <param name="dev"></param>
|
||||||
}
|
/// <param name="format">String.format string</param>
|
||||||
}
|
/// <param name="items">Parameters for substitution in the format string.</param>
|
||||||
|
public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
|
||||||
/// <summary>
|
{
|
||||||
/// Writes the memory object after timeout
|
var str = string.Format(format, items);
|
||||||
/// </summary>
|
if (Level >= level)
|
||||||
static void SaveMemoryOnTimeout()
|
ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
|
||||||
{
|
}
|
||||||
if (SaveTimer == null)
|
|
||||||
SaveTimer = new CTimer(o =>
|
/// <summary>
|
||||||
{
|
/// Prints to log and error log
|
||||||
SaveTimer = null;
|
/// </summary>
|
||||||
SaveMemory();
|
/// <param name="errorLogLevel"></param>
|
||||||
}, SaveTimeoutMs);
|
/// <param name="str"></param>
|
||||||
else
|
public static void LogError(ErrorLogLevel errorLogLevel, string str)
|
||||||
SaveTimer.Reset(SaveTimeoutMs);
|
{
|
||||||
}
|
string msg = string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
|
||||||
|
switch (errorLogLevel)
|
||||||
/// <summary>
|
{
|
||||||
/// Writes the memory - use SaveMemoryOnTimeout
|
case ErrorLogLevel.Error:
|
||||||
/// </summary>
|
ErrorLog.Error(msg);
|
||||||
static void SaveMemory()
|
break;
|
||||||
{
|
case ErrorLogLevel.Warning:
|
||||||
//var dir = @"\NVRAM\debug";
|
ErrorLog.Warn(msg);
|
||||||
//if (!Directory.Exists(dir))
|
break;
|
||||||
// Directory.Create(dir);
|
case ErrorLogLevel.Notice:
|
||||||
|
ErrorLog.Notice(msg);
|
||||||
using (StreamWriter sw = new StreamWriter(GetMemoryFileName()))
|
break;
|
||||||
{
|
}
|
||||||
var json = JsonConvert.SerializeObject(Contexts);
|
}
|
||||||
sw.Write(json);
|
|
||||||
sw.Flush();
|
/// <summary>
|
||||||
}
|
/// Writes the memory object after timeout
|
||||||
}
|
/// </summary>
|
||||||
|
static void SaveMemoryOnTimeout()
|
||||||
/// <summary>
|
{
|
||||||
///
|
if (SaveTimer == null)
|
||||||
/// </summary>
|
SaveTimer = new CTimer(o =>
|
||||||
static void LoadMemory()
|
{
|
||||||
{
|
SaveTimer = null;
|
||||||
var file = GetMemoryFileName();
|
SaveMemory();
|
||||||
if (File.Exists(file))
|
}, SaveTimeoutMs);
|
||||||
{
|
else
|
||||||
using (StreamReader sr = new StreamReader(file))
|
SaveTimer.Reset(SaveTimeoutMs);
|
||||||
{
|
}
|
||||||
var json = sr.ReadToEnd();
|
|
||||||
Contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);
|
/// <summary>
|
||||||
|
/// Writes the memory - use SaveMemoryOnTimeout
|
||||||
if (Contexts != null)
|
/// </summary>
|
||||||
{
|
static void SaveMemory()
|
||||||
Debug.Console(1, "Debug memory restored from file");
|
{
|
||||||
return;
|
//var dir = @"\NVRAM\debug";
|
||||||
}
|
//if (!Directory.Exists(dir))
|
||||||
}
|
// Directory.Create(dir);
|
||||||
}
|
|
||||||
|
using (StreamWriter sw = new StreamWriter(GetMemoryFileName()))
|
||||||
Contexts = new DebugContextCollection();
|
{
|
||||||
}
|
var json = JsonConvert.SerializeObject(Contexts);
|
||||||
|
sw.Write(json);
|
||||||
/// <summary>
|
sw.Flush();
|
||||||
/// Helper to get the file path for this app's debug memory
|
}
|
||||||
/// </summary>
|
}
|
||||||
static string GetMemoryFileName()
|
|
||||||
{
|
/// <summary>
|
||||||
return string.Format(@"\NVRAM\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
|
///
|
||||||
}
|
/// </summary>
|
||||||
|
static void LoadMemory()
|
||||||
public enum ErrorLogLevel
|
{
|
||||||
{
|
var file = GetMemoryFileName();
|
||||||
Error, Warning, Notice, None
|
if (File.Exists(file))
|
||||||
}
|
{
|
||||||
}
|
using (StreamReader sr = new StreamReader(file))
|
||||||
|
{
|
||||||
|
var json = sr.ReadToEnd();
|
||||||
|
Contexts = JsonConvert.DeserializeObject<DebugContextCollection>(json);
|
||||||
|
|
||||||
|
if (Contexts != null)
|
||||||
|
{
|
||||||
|
Debug.Console(1, "Debug memory restored from file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Contexts = new DebugContextCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to get the file path for this app's debug memory
|
||||||
|
/// </summary>
|
||||||
|
static string GetMemoryFileName()
|
||||||
|
{
|
||||||
|
return string.Format(@"\NVRAM\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ErrorLogLevel
|
||||||
|
{
|
||||||
|
Error, Warning, Notice, None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,10 +6,14 @@ using Crestron.SimplSharp;
|
|||||||
|
|
||||||
namespace PepperDash.Core
|
namespace PepperDash.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not in use
|
||||||
|
/// </summary>
|
||||||
public static class NetworkComm
|
public static class NetworkComm
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Not in use
|
||||||
|
/// </summary>
|
||||||
static NetworkComm()
|
static NetworkComm()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
<NoConfig>true</NoConfig>
|
<NoConfig>true</NoConfig>
|
||||||
|
<DocumentationFile>bin\PepperDash_Core.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
|
<AllowedReferenceRelatedFileExtensions>.allowedReferenceRelatedFileExtensions</AllowedReferenceRelatedFileExtensions>
|
||||||
@@ -42,6 +43,7 @@
|
|||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<NoStdLib>true</NoStdLib>
|
<NoStdLib>true</NoStdLib>
|
||||||
<NoConfig>true</NoConfig>
|
<NoConfig>true</NoConfig>
|
||||||
|
<DocumentationFile>bin\PepperDash_Core.xml</DocumentationFile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="mscorlib" />
|
<Reference Include="mscorlib" />
|
||||||
@@ -57,6 +59,10 @@
|
|||||||
<SpecificVersion>False</SpecificVersion>
|
<SpecificVersion>False</SpecificVersion>
|
||||||
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll</HintPath>
|
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
|
||||||
|
<SpecificVersion>False</SpecificVersion>
|
||||||
|
<HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
<Reference Include="System.Data" />
|
<Reference Include="System.Data" />
|
||||||
@@ -76,6 +82,7 @@
|
|||||||
<Compile Include="Comm\EventArgs.cs" />
|
<Compile Include="Comm\EventArgs.cs" />
|
||||||
<Compile Include="Comm\GenericSshClient.cs" />
|
<Compile Include="Comm\GenericSshClient.cs" />
|
||||||
<Compile Include="Comm\GenericUdpServer.cs" />
|
<Compile Include="Comm\GenericUdpServer.cs" />
|
||||||
|
<Compile Include="Comm\TcpClientConfigObject.cs" />
|
||||||
<Compile Include="Comm\TcpServerConfigObject.cs" />
|
<Compile Include="Comm\TcpServerConfigObject.cs" />
|
||||||
<Compile Include="Config\PortalConfigReader.cs" />
|
<Compile Include="Config\PortalConfigReader.cs" />
|
||||||
<Compile Include="CoreInterfaces.cs" />
|
<Compile Include="CoreInterfaces.cs" />
|
||||||
|
|||||||
@@ -4,4 +4,4 @@
|
|||||||
[assembly: AssemblyCompany("")]
|
[assembly: AssemblyCompany("")]
|
||||||
[assembly: AssemblyProduct("Pepperdash_Core")]
|
[assembly: AssemblyProduct("Pepperdash_Core")]
|
||||||
[assembly: AssemblyCopyright("Copyright © PepperDash 2019")]
|
[assembly: AssemblyCopyright("Copyright © PepperDash 2019")]
|
||||||
[assembly: AssemblyVersion("1.0.14.*")]
|
[assembly: AssemblyVersion("1.0.23.*")]
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
function Update-SourceVersion
|
||||||
|
{
|
||||||
|
Param ([string]$Version)
|
||||||
|
$NewVersion = ‘AssemblyVersion("‘ + $Version + ‘.*")’;
|
||||||
|
foreach ($o in $input)
|
||||||
|
{
|
||||||
|
Write-output $o.FullName
|
||||||
|
$TmpFile = $o.FullName + “.tmp”
|
||||||
|
get-content $o.FullName |
|
||||||
|
%{$_ -replace ‘AssemblyVersion\("(\d+\.\d+\.\d+)\.\*"\)’, $NewVersion } > $TmpFile
|
||||||
|
move-item $TmpFile $o.FullName -force
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Update-AllAssemblyInfoFiles ( $version )
|
||||||
|
{
|
||||||
|
foreach ($file in “AssemblyInfo.cs”, “AssemblyInfo.vb” )
|
||||||
|
{
|
||||||
|
get-childitem -recurse |? {$_.Name -eq $file} | Update-SourceVersion $version ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# validate arguments
|
||||||
|
$r= [System.Text.RegularExpressions.Regex]::Match($args[0], "^\d+\.\d+\.\d+$");
|
||||||
|
if ($r.Success)
|
||||||
|
{
|
||||||
|
Update-AllAssemblyInfoFiles $args[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
echo ” “;
|
||||||
|
echo “Error: Input version does not match x.y.z format!”
|
||||||
|
echo ” “;
|
||||||
|
echo "Unable to apply version to AssemblyInfo.cs files";
|
||||||
|
}
|
||||||
17
Readme.md
Normal file
17
Readme.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Pepperdash Core
|
||||||
|
|
||||||
|
#### Workflow process
|
||||||
|
|
||||||
|
- Create a Jira issue for the feature/bugfix. If you know you're targeting this update to coincide with a new release of Core, specify that release (or create a new one) as the Fix Version for the Jira issue
|
||||||
|
- Branch from development using the syntax [feature/bugfix]/[pdc-x] (where x is the Jira issue number)
|
||||||
|
- Modify code to suit and test. Make commits to the branch as you work.
|
||||||
|
- Log a Pull Request on www.bitbucket.org and tag Heath and Neil as reviewers
|
||||||
|
|
||||||
|
#### Pull Request process
|
||||||
|
|
||||||
|
- Check out the branch for the PR and review.
|
||||||
|
- If necessary, merge the latest Development branch into the PR branch. Resolve any conflicts.
|
||||||
|
- Increment the appropriate Assembly version number to match the next release in Jira
|
||||||
|
- Build the project
|
||||||
|
- Copy PepperDash_Core.cpz and PepperDash_Core.dll from the bin folder to the CLZ Builds folder in the repo root. Commit.
|
||||||
|
- Merge the PR in Bitbucket
|
||||||
Reference in New Issue
Block a user