diff --git a/Pepperdash Core/Pepperdash Core.suo b/Pepperdash Core/Pepperdash Core.suo
index df08759..fb8dbb6 100644
Binary files a/Pepperdash Core/Pepperdash Core.suo and b/Pepperdash Core/Pepperdash Core.suo differ
diff --git a/Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs b/Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs
new file mode 100644
index 0000000..bad57f4
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs
@@ -0,0 +1,684 @@
+/*PepperDash Technology Corp.
+JAG
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+using PepperDash.Core;
+
+namespace DynamicTCP
+{
+ public class DynamicTCPServer : Device
+ {
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+ #endregion
+
+ #region Properties/Variables
+ ///
+ /// Secure or unsecure TCP server. Defaults to Unsecure or standard TCP server without SSL
+ ///
+ public bool Secure { get; set; }
+
+ ///
+ /// S+ Helper for Secure bool. Parameter in SIMPL+ so there is no get, one way set from simpl+ Param to property in func main of SIMPL+
+ ///
+ public ushort USecure
+ {
+ set
+ {
+ if (value == 1)
+ Secure = true;
+ else if (value == 0)
+ Secure = false;
+ }
+ }
+
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
+ {
+ get
+ {
+ if (Secure ? SecureServer != null : UnsecureServer != null)
+ return Secure ? SecureServer.State.ToString() : UnsecureServer.State.ToString();
+ else
+ return "";
+ }
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ (Secure ? SecureServer.State == ServerState.SERVER_CONNECTED : UnsecureServer.State == ServerState.SERVER_CONNECTED);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsListening
+ {
+ get { return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ (Secure ? SecureServer.State == ServerState.SERVER_LISTENING : UnsecureServer.State == ServerState.SERVER_LISTENING); }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+
+ public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
+ {
+ if (Secure ? SecureServer != null : UnsecureServer != null)
+ return Secure ? (ushort)SecureServer.NumberOfClientsConnected : (ushort)UnsecureServer.NumberOfClientsConnected;
+ return 0;
+ }
+ }
+
+ ///
+ /// Port Server should listen on
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper for Port
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
+ }
+ }
+
+ ///
+ /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
+ /// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
+ ///
+ public bool HeartbeatRequired { get; set; }
+
+ ///
+ /// S+ Helper for Heartbeat Required
+ ///
+ public ushort UHeartbeatRequired
+ {
+ set
+ {
+ if (value == 1)
+ HeartbeatRequired = true;
+ else
+ HeartbeatRequired = false;
+ }
+ }
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatRequiredIntervalMs { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
+
+ ///
+ /// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+
+ //private timers for Heartbeats per client
+ Dictionary HeartbeatTimerDictionary = new Dictionary();
+
+ //flags to show the secure server is waiting for client at index to send the shared key
+ List WaitingForSharedKey = new List();
+
+ //Store the connected client indexes
+ List ConnectedClientsIndexes = new List();
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Private flag to note that the server has stopped intentionally
+ ///
+ private bool ServerStopped { get; set; }
+
+ //Servers
+ SecureTCPServer SecureServer;
+ TCPServer UnsecureServer;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// constructor
+ ///
+ public DynamicTCPServer()
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ Secure = false;
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Start listening on the specified port
+ ///
+ public void Listen()
+ {
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericSecureTcpClient '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("GenericSecureTcpClient '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericSecureTcpClient '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("GenericSecureTcpClient '{0}': No Shared Key set", Key));
+ return;
+ }
+ if (IsListening)
+ return;
+ if (Secure)
+ {
+ SecureServer = new SecureTCPServer(Port, MaxClients);
+ SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
+ ServerStopped = false;
+ SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
+ onServerStateChange();
+ Debug.Console(2, "Secure Server Status: {0}, Socket Status: {1}\r\n", SecureServer.State.ToString(), SecureServer.ServerSocketStatus);
+ }
+ else
+ {
+ UnsecureServer = new TCPServer(Port, MaxClients);
+ UnsecureServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(UnsecureServer_SocketStatusChange);
+ ServerStopped = false;
+ UnsecureServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
+ onServerStateChange();
+ Debug.Console(2, "Unsecure Server Status: {0}, Socket Status: {1}\r\n", UnsecureServer.State.ToString(), UnsecureServer.ServerSocketStatus);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("Error with Dynamic Server: {0}", ex.ToString());
+ }
+ }
+
+ ///
+ /// Stop Listeneing
+ ///
+ public void StopListening()
+ {
+ Debug.Console(2, "Stopping Listener");
+ if (SecureServer != null)
+ SecureServer.Stop();
+ if (UnsecureServer != null)
+ UnsecureServer.Stop();
+ ServerStopped = true;
+ onServerStateChange();
+ }
+
+ ///
+ /// Disconnect All Clients
+ ///
+ public void DisconnectAllClients()
+ {
+ Debug.Console(2, "Disconnecting All Clients");
+ if (SecureServer != null)
+ SecureServer.DisconnectAll();
+ if (UnsecureServer != null)
+ UnsecureServer.DisconnectAll();
+ onConnectionChange();
+ onServerStateChange(); //State shows both listening and connected
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ public void BroadcastText(string text)
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Secure)
+ foreach (uint i in ConnectedClientsIndexes)
+ SecureServer.SendDataAsync(i, b, b.Length, SecureSendDataAsyncCallback);
+ else
+ foreach (uint i in ConnectedClientsIndexes)
+ UnsecureServer.SendDataAsync(i, b, b.Length, UnsecureSendDataAsyncCallback);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Secure)
+ SecureServer.SendDataAsync(clientIndex, b, b.Length, SecureSendDataAsyncCallback);
+ else
+ UnsecureServer.SendDataAsync(clientIndex, b, b.Length, UnsecureSendDataAsyncCallback);
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ void checkHeartbeat(uint clientIndex, string received)
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ if (received == HeartbeatStringToMatch)
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ else
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Methods - HeartbeatTimer Callback
+
+ void HeartbeatTimer_CallbackFunction(object o)
+ {
+ uint clientIndex = (uint)o;
+
+ string address = Secure ? SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex) :
+ UnsecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ ErrorLog.Error("Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+ Debug.Console(2, "Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+
+ if (Secure)
+ SecureServer.Disconnect(clientIndex);
+ else
+ UnsecureServer.Disconnect(clientIndex);
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+
+ #endregion
+
+ #region Methods - Socket Status Changed Callbacks
+ ///
+ /// Secure Server Socket Status Changed Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
+ if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Add(clientIndex);
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ else
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if(SecureServer.ServerSocketStatus.ToString() != Status)
+ onConnectionChange();
+ }
+
+ ///
+ /// TCP Server (Unsecure) Socket Status Change Callback
+ ///
+ ///
+ ///
+ ///
+ void UnsecureServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
+ if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Add(clientIndex);
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ else
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (UnsecureServer.ServerSocketStatus.ToString() != Status)
+ onConnectionChange();
+ }
+ #endregion
+
+ #region Methods Connected Callbacks
+ ///
+ /// Secure TCP Client Connected to Secure Server Callback
+ ///
+ ///
+ ///
+ void SecureConnectCallback(SecureTCPServer mySecureTCPServer, uint clientIndex)
+ {
+ if (mySecureTCPServer.ClientConnected(clientIndex))
+ {
+ if (SharedKeyRequired)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
+ mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, SecureSendDataAsyncCallback);
+ Debug.Console(2, "Sent Shared Key to client at {0}", mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ if (HeartbeatRequired)
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
+ if (mySecureTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ mySecureTCPServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
+ }
+ }
+
+ ///
+ /// Unsecure TCP Client Connected to Unsecure Server Callback
+ ///
+ ///
+ ///
+ void UnsecureConnectCallback(TCPServer myTCPServer, uint clientIndex)
+ {
+ if (myTCPServer.ClientConnected(clientIndex))
+ {
+ if (SharedKeyRequired)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
+ myTCPServer.SendDataAsync(clientIndex, b, b.Length, UnsecureSendDataAsyncCallback);
+ Debug.Console(2, "Sent Shared Key to client at {0}", myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ if (HeartbeatRequired)
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ myTCPServer.ReceiveDataAsync(clientIndex, UnsecureReceivedDataAsyncCallback);
+ if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ myTCPServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
+ }
+ if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ myTCPServer.WaitForConnectionAsync(IPAddress.Any, UnsecureConnectCallback);
+ }
+ #endregion
+
+ #region Methods - Send/Receive Callbacks
+ ///
+ /// Secure Send Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureSendDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesSent)
+ {
+ //Seems there is nothing to do here
+ }
+
+ ///
+ /// Unsecure Send Data Asyc Callback
+ ///
+ ///
+ ///
+ ///
+ void UnsecureSendDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesSent)
+ {
+ //Seems there is nothing to do here
+ }
+
+ ///
+ /// Secure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+ string received = "Nothing";
+ byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
+ received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ {
+ received = received.Replace("\r", "");
+ received = received.Replace("\n", "");
+ if (received != SharedKey)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
+ Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
+ mySecureTCPServer.Disconnect(clientIndex);
+ }
+ if (mySecureTCPServer.NumberOfClientsConnected > 0)
+ mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
+ mySecureTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
+ mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
+ }
+ else
+ {
+ mySecureTCPServer.ReceiveDataAsync(SecureReceivedDataAsyncCallback);
+ Debug.Console(2, "Secure Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
+ mySecureTCPServer.PortNumber, mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
+ onTextReceived(received);
+ }
+ checkHeartbeat(clientIndex, received);
+ }
+ if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
+ }
+
+ ///
+ /// Unsecure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void UnsecureReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+ string received = "Nothing";
+ byte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
+ received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ {
+ received = received.Replace("\r", "");
+ received = received.Replace("\n", "");
+ if (received != SharedKey)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
+ Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ myTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
+ myTCPServer.Disconnect(clientIndex);
+ }
+ if (myTCPServer.NumberOfClientsConnected > 0)
+ myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
+ myTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
+ myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
+ }
+ else
+ {
+ myTCPServer.ReceiveDataAsync(UnsecureReceivedDataAsyncCallback);
+ Debug.Console(2, "Secure Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
+ myTCPServer.PortNumber, myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
+ onTextReceived(received);
+ }
+ checkHeartbeat(clientIndex, received);
+ }
+ if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ myTCPServer.ReceiveDataAsync(clientIndex, UnsecureReceivedDataAsyncCallback);
+ }
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange()
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ if (Secure)
+ handler(this, new DynamicTCPSocketStatusChangeEventArgs(SecureServer, Secure));
+ else
+ handler(this, new DynamicTCPSocketStatusChangeEventArgs(UnsecureServer, Secure));
+ }
+ }
+
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text)
+ {
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new CopyCoreForSimplpGenericCommMethodReceiveTextArgs(text));
+ }
+
+ //Private Helper Method to call the Server State Change Event
+ void onServerStateChange()
+ {
+ var handler = ServerStateChange;
+ if(handler != null)
+ {
+ if(Secure)
+ handler(this, new DynamicTCPServerStateChangedEventArgs(SecureServer, Secure));
+ else
+ handler(this, new DynamicTCPServerStateChangedEventArgs(UnsecureServer, Secure));
+ }
+ }
+
+ //Private Event Handler method to handle the closing of connections when the program stops
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Debug.Console(1, this, "Program stopping. Closing server");
+ DisconnectAllClients();
+ StopListening();
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs b/Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs
index cc7afe4..3732e0d 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs
+++ b/Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs
@@ -1,4 +1,14 @@
-using System;
+/*PepperDash Technology Corp.
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -24,6 +34,43 @@ namespace PepperDash.Core
}
#endregion
+ #region DynamicTCPServerStateChangedEventArgs
+ public delegate void DynamicTCPServerStateChangedEventDelegate(object server);
+
+ public class DynamicTCPServerStateChangedEventArgs : EventArgs
+ {
+ public bool Secure { get; private set; }
+ public object Server { get; private set; }
+
+ public DynamicTCPServerStateChangedEventArgs() { }
+
+ public DynamicTCPServerStateChangedEventArgs(object server, bool secure)
+ {
+ Secure = secure;
+ Server = server;
+ }
+ }
+ #endregion
+
+ #region DynamicTCPSocketStatusChangeEventDelegate
+ public delegate void DynamicTCPSocketStatusChangeEventDelegate(object server);
+
+ public class DynamicTCPSocketStatusChangeEventArgs : EventArgs
+ {
+ public bool Secure { get; private set; }
+ public object Server { get; private set; }
+
+ public DynamicTCPSocketStatusChangeEventArgs() { }
+
+ public DynamicTCPSocketStatusChangeEventArgs(object server, bool secure)
+ {
+ Secure = secure;
+ Server = server;
+ }
+ }
+ #endregion
+
+
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs
new file mode 100644
index 0000000..82305ad
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs
@@ -0,0 +1,371 @@
+/*PepperDash Technology Corp.
+JAG
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+
+namespace PepperDash.Core
+{
+ public class GenericSecureTcpIpClient : Device, ISocketStatus, IAutoReconnect
+ {
+ #region Events
+
+ public event EventHandler BytesReceived;
+
+ public event EventHandler TextReceived;
+
+ public event EventHandler ConnectionChange;
+
+ #endregion
+
+ #region Properties & Variables
+ ///
+ /// Address of server
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Port on server
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. This is used in the DynamicTCPServer class
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
+ }
+ }
+
+ ///
+ /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// flag to show the client is waiting for the server to send the shared key
+ ///
+ private bool WaitingForSharedKeyResponse { get; set; }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ return (Client != null) && (Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ if (Client == null)
+ return SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ return Client.ClientStatus;
+ }
+ }
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected would be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)ClientStatus; }
+ }
+
+ ///
+ /// Status text shows the message associated with socket status
+ ///
+ public string ClientStatusText { get { return ClientStatus.ToString(); } }
+
+ ///
+ /// bool to track if auto reconnect should be set on the socket
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+ ///
+ /// Milliseconds to wait before attempting to reconnect. Defaults to 5000
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Flag Set only when the disconnect method is called.
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ /// Connected bool
+ ///
+ public bool Connected
+ {
+ get { return (Client != null) && (Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED); }
+ }
+
+ CTimer RetryTimer; //private Timer for auto reconnect
+
+ SecureTCPClient Client; //Secure Client Class
+
+ #endregion
+
+ #region Constructors
+
+ //Base class constructor
+ public GenericSecureTcpIpClient(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+ }
+
+ //base class constructor
+ public GenericSecureTcpIpClient()
+ : base("Uninitialized DynamicTcpClient")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Just to help S+ set the key
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ if (Client != null)
+ {
+ Debug.Console(1, this, "Program stopping. Closing connection");
+ Client.DisconnectFromServer();
+ Client.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
+ ///
+ public void Connect()
+ {
+ if (IsConnected)
+ return;
+
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "DynamicTcpClient '{0}': No address set", Key);
+ ErrorLog.Warn(string.Format("DynamicTcpClient '{0}': No address set", Key));
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "DynamicTcpClient '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("DynamicTcpClient '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "DynamicTcpClient '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("DynamicTcpClient '{0}': No Shared Key set", Key));
+ return;
+ }
+ Client = new SecureTCPClient(Hostname, Port, BufferSize);
+ Client.SocketStatusChange += SecureClient_SocketStatusChange;
+ DisconnectCalledByUser = false;
+ if (SharedKeyRequired)
+ WaitingForSharedKeyResponse = true;
+ Client.ConnectToServer();
+ }
+
+ ///
+ /// Disconnect client. Does not dispose.
+ ///
+ public void Disconnect()
+ {
+ DisconnectCalledByUser = true;
+ if(Client != null)
+ Client.DisconnectFromServer();
+ }
+
+ ///
+ /// callback after connection made
+ ///
+ ///
+ void ConnectToServerCallback(object o)
+ {
+ Client.ConnectToServer();
+ if (Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ WaitAndTryReconnect();
+ }
+
+ ///
+ /// Called from Socket Status change if auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void WaitAndTryReconnect()
+ {
+ Client.DisconnectFromServer();
+ Debug.Console(2, "Attempting reconnect, status={0}", Client.ClientStatus);
+
+ if (!DisconnectCalledByUser)
+ RetryTimer = new CTimer(ConnectToServerCallback, AutoReconnectIntervalMs);
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void SecureReceive(SecureTCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ if (WaitingForSharedKeyResponse && SharedKeyRequired)
+ {
+ if (str != (SharedKey + "\n"))
+ {
+ WaitingForSharedKeyResponse = false;
+ client.DisconnectFromServer();
+ CrestronConsole.PrintLine("Client {0} was disconnected from server because the server did not respond with a matching shared key after connection", Key);
+ ErrorLog.Error("Client {0} was disconnected from server because the server did not respond with a matching shared key after connection", Key);
+ return;
+ }
+ else
+ {
+ WaitingForSharedKeyResponse = false;
+ CrestronConsole.PrintLine("Client {0} successfully connected to the server and received the Shared Key. Ready for communication", Key);
+ }
+ }
+ else
+ {
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ client.ReceiveDataAsync(SecureReceive);
+ }
+
+ ///
+ /// General send method
+ ///
+ public void SendText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ Client.SendData(bytes, bytes.Length);
+ }
+
+ public void SendBytes(byte[] bytes)
+ {
+ Client.SendData(bytes, bytes.Length);
+ }
+
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void SecureClient_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
+ {
+ Debug.Console(2, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
+ if (client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED && !DisconnectCalledByUser && AutoReconnect)
+ WaitAndTryReconnect();
+
+ // Probably doesn't need to be a switch since all other cases were eliminated
+ switch (clientSocketStatus)
+ {
+ case SocketStatus.SOCKET_STATUS_CONNECTED:
+ client.ReceiveDataAsync(SecureReceive);
+ if(SharedKeyRequired)
+ SendText(SharedKey + "\n");
+ DisconnectCalledByUser = false;
+ break;
+ }
+
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs
new file mode 100644
index 0000000..365cbcb
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs
@@ -0,0 +1,505 @@
+/*PepperDash Technology Corp.
+JAG
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+
+namespace PepperDash.Core
+{
+ public class GenericSecureTcpIpServer : Device
+ {
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+ #endregion
+
+ #region Properties/Variables
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
+ {
+ get
+ {
+ if (Server != null)
+ return Server.State.ToString();
+ else
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+ }
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get { return (Server != null) && (Server.State == ServerState.SERVER_CONNECTED); }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsListening
+ {
+ get { return (Server != null) && (Server.State == ServerState.SERVER_LISTENING); }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+
+ public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
+ {
+ if (Server != null)
+ return (ushort)Server.NumberOfClientsConnected;
+ return 0;
+ }
+ }
+
+ ///
+ /// Port Server should listen on
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper for Port
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
+ }
+ }
+
+ ///
+ /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
+ /// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
+ ///
+ public bool HeartbeatRequired { get; set; }
+
+ ///
+ /// S+ Helper for Heartbeat Required
+ ///
+ public ushort UHeartbeatRequired
+ {
+ set
+ {
+ if (value == 1)
+ HeartbeatRequired = true;
+ else
+ HeartbeatRequired = false;
+ }
+ }
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatRequiredIntervalMs { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
+
+ ///
+ /// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+
+ //private timers for Heartbeats per client
+ Dictionary HeartbeatTimerDictionary = new Dictionary();
+
+ //flags to show the server is waiting for client at index to send the shared key
+ List WaitingForSharedKey = new List();
+
+ //Store the connected client indexes
+ List ConnectedClientsIndexes = new List();
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Private flag to note that the server has stopped intentionally
+ ///
+ private bool ServerStopped { get; set; }
+
+ //Servers
+ SecureTCPServer Server;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// constructor
+ ///
+ public GenericSecureTcpIpServer()
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Start listening on the specified port
+ ///
+ public void Listen()
+ {
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
+ if (IsListening)
+ return;
+ Server = new SecureTCPServer(Port, MaxClients);
+ Server.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SocketStatusChange);
+ ServerStopped = false;
+ Server.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ onServerStateChange();
+ Debug.Console(2, "Server Status: {0}, Socket Status: {1}\r\n", Server.State.ToString(), Server.ServerSocketStatus);
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("Error with Dynamic Server: {0}", ex.ToString());
+ }
+ }
+
+ ///
+ /// Stop Listeneing
+ ///
+ public void StopListening()
+ {
+ Debug.Console(2, "Stopping Listener");
+ if (Server != null)
+ Server.Stop();
+ ServerStopped = true;
+ onServerStateChange();
+ }
+
+ ///
+ /// Disconnect All Clients
+ ///
+ public void DisconnectAllClients()
+ {
+ Debug.Console(2, "Disconnecting All Clients");
+ if (Server != null)
+ Server.DisconnectAll();
+ onConnectionChange();
+ onServerStateChange(); //State shows both listening and connected
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ public void BroadcastText(string text)
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ Server.SendDataAsync(i, b, b.Length, SendDataAsyncCallback);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ Server.SendDataAsync(clientIndex, b, b.Length, SendDataAsyncCallback);
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ void checkHeartbeat(uint clientIndex, string received)
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ if (received == HeartbeatStringToMatch)
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ else
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Methods - Callbacks
+ ///
+ /// Callback to disconnect if heartbeat timer finishes without being reset
+ ///
+ ///
+ void HeartbeatTimer_CallbackFunction(object o)
+ {
+ uint clientIndex = (uint)o;
+
+ string address = Server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ ErrorLog.Error("Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+ Debug.Console(2, "Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+ Server.Disconnect(clientIndex);
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+
+ ///
+ /// TCP Server Socket Status Change Callback
+ ///
+ ///
+ ///
+ ///
+ void SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
+ if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Add(clientIndex);
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ else
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (Server.ServerSocketStatus.ToString() != Status)
+ onConnectionChange();
+ }
+
+ ///
+ /// TCP Client Connected to Server Callback
+ ///
+ ///
+ ///
+ void ConnectCallback(SecureTCPServer mySecureTCPServer, uint clientIndex)
+ {
+ if (mySecureTCPServer.ClientConnected(clientIndex))
+ {
+ if (SharedKeyRequired)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
+ mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, SendDataAsyncCallback);
+ Debug.Console(2, "Sent Shared Key to client at {0}", mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ if (HeartbeatRequired)
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ mySecureTCPServer.ReceiveDataAsync(clientIndex, ReceivedDataAsyncCallback);
+ if (mySecureTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ mySecureTCPServer.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ }
+ if (mySecureTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ mySecureTCPServer.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ }
+
+ ///
+ /// Send Data Asyc Callback
+ ///
+ ///
+ ///
+ ///
+ void SendDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesSent)
+ {
+ //Seems there is nothing to do here
+ }
+
+ ///
+ /// Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void ReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+ string received = "Nothing";
+ byte[] bytes = mySecureTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
+ received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ {
+ received = received.Replace("\r", "");
+ received = received.Replace("\n", "");
+ if (received != SharedKey)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
+ Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ mySecureTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
+ mySecureTCPServer.Disconnect(clientIndex);
+ }
+ if (mySecureTCPServer.NumberOfClientsConnected > 0)
+ mySecureTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
+ mySecureTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
+ mySecureTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ }
+ else
+ {
+ mySecureTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ Debug.Console(2, "Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
+ mySecureTCPServer.PortNumber, mySecureTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
+ onTextReceived(received);
+ }
+ checkHeartbeat(clientIndex, received);
+ }
+ if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ mySecureTCPServer.ReceiveDataAsync(clientIndex, ReceivedDataAsyncCallback);
+ }
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange()
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ handler(this, new DynamicTCPSocketStatusChangeEventArgs(Server, false));
+ }
+
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text)
+ {
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericCommMethodReceiveTextArgs(text));
+ }
+
+ //Private Helper Method to call the Server State Change Event
+ void onServerStateChange()
+ {
+ var handler = ServerStateChange;
+ if (handler != null)
+ handler(this, new DynamicTCPServerStateChangedEventArgs(Server, false));
+ }
+
+ //Private Event Handler method to handle the closing of connections when the program stops
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Debug.Console(1, this, "Program stopping. Closing server");
+ DisconnectAllClients();
+ StopListening();
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs
new file mode 100644
index 0000000..5b96684
--- /dev/null
+++ b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs
@@ -0,0 +1,505 @@
+/*PepperDash Technology Corp.
+JAG
+Copyright: 2017
+------------------------------------
+***Notice of Ownership and Copyright***
+The material in which this notice appears is the property of PepperDash Technology Corporation,
+which claims copyright under the laws of the United States of America in the entire body of material
+and in all parts thereof, regardless of the use to which it is being put. Any use, in whole or in part,
+of this material by another party without the express written permission of PepperDash Technology Corporation is prohibited.
+PepperDash Technology Corporation reserves all rights under applicable laws.
+------------------------------------ */
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+
+namespace PepperDash.Core
+{
+ public class GenericTcpIpServer : Device
+ {
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+ #endregion
+
+ #region Properties/Variables
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
+ {
+ get
+ {
+ if (Server != null)
+ return Server.State.ToString();
+ else
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+ }
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get { return (Server != null) && (Server.State == ServerState.SERVER_CONNECTED); }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsListening
+ {
+ get { return (Server != null) && (Server.State == ServerState.SERVER_LISTENING); }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+
+ public ushort MaxClients { get; set; } // should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
+ {
+ if (Server != null)
+ return (ushort)Server.NumberOfClientsConnected;
+ return 0;
+ }
+ }
+
+ ///
+ /// Port Server should listen on
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// S+ helper for Port
+ ///
+ public ushort UPort
+ {
+ get { return Convert.ToUInt16(Port); }
+ set { Port = Convert.ToInt32(value); }
+ }
+
+ ///
+ /// Bool to show whether the server requires a preshared key. Must be set the same in the client, and if true shared keys must be identical on server/client
+ ///
+ public bool SharedKeyRequired { get; set; }
+
+ ///
+ /// S+ helper for requires shared key bool
+ ///
+ public ushort USharedKeyRequired
+ {
+ set
+ {
+ if (value == 1)
+ SharedKeyRequired = true;
+ else
+ SharedKeyRequired = false;
+ }
+ }
+
+ ///
+ /// SharedKey is sent for varification to the server. Shared key can be any text (255 char limit in SIMPL+ Module), but must match the Shared Key on the Server module.
+ /// If SharedKey changes while server is listening or clients are connected, disconnect and stop listening will be called
+ ///
+ public string SharedKey { get; set; }
+
+ ///
+ /// Heartbeat Required bool sets whether server disconnects client if heartbeat is not received
+ ///
+ public bool HeartbeatRequired { get; set; }
+
+ ///
+ /// S+ Helper for Heartbeat Required
+ ///
+ public ushort UHeartbeatRequired
+ {
+ set
+ {
+ if (value == 1)
+ HeartbeatRequired = true;
+ else
+ HeartbeatRequired = false;
+ }
+ }
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatRequiredIntervalMs { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatRequiredIntervalMs = (value * 1000); } }
+
+ ///
+ /// String to Match for heartbeat. If null or empty any string will reset heartbeat timer
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+
+ //private timers for Heartbeats per client
+ Dictionary HeartbeatTimerDictionary = new Dictionary();
+
+ //flags to show the server is waiting for client at index to send the shared key
+ List WaitingForSharedKey = new List();
+
+ //Store the connected client indexes
+ List ConnectedClientsIndexes = new List();
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Private flag to note that the server has stopped intentionally
+ ///
+ private bool ServerStopped { get; set; }
+
+ //Servers
+ TCPServer Server;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// constructor
+ ///
+ public GenericTcpIpServer()
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Start listening on the specified port
+ ///
+ public void Listen()
+ {
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
+ if (IsListening)
+ return;
+ Server = new TCPServer(Port, MaxClients);
+ Server.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(SocketStatusChange);
+ ServerStopped = false;
+ Server.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ onServerStateChange();
+ Debug.Console(2, "Server Status: {0}, Socket Status: {1}\r\n", Server.State.ToString(), Server.ServerSocketStatus);
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("Error with Dynamic Server: {0}", ex.ToString());
+ }
+ }
+
+ ///
+ /// Stop Listeneing
+ ///
+ public void StopListening()
+ {
+ Debug.Console(2, "Stopping Listener");
+ if (Server != null)
+ Server.Stop();
+ ServerStopped = true;
+ onServerStateChange();
+ }
+
+ ///
+ /// Disconnect All Clients
+ ///
+ public void DisconnectAllClients()
+ {
+ Debug.Console(2, "Disconnecting All Clients");
+ if (Server != null)
+ Server.DisconnectAll();
+ onConnectionChange();
+ onServerStateChange(); //State shows both listening and connected
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ public void BroadcastText(string text)
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ Server.SendDataAsync(i, b, b.Length, SendDataAsyncCallback);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ Server.SendDataAsync(clientIndex, b, b.Length, SendDataAsyncCallback);
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ void checkHeartbeat(uint clientIndex, string received)
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ if (received == HeartbeatStringToMatch)
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ else
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Methods - Callbacks
+ ///
+ /// Callback to disconnect if heartbeat timer finishes without being reset
+ ///
+ ///
+ void HeartbeatTimer_CallbackFunction(object o)
+ {
+ uint clientIndex = (uint)o;
+
+ string address = Server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ ErrorLog.Error("Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+ Debug.Console(2, "Heartbeat not received for Client at IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE", address);
+
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+ Server.Disconnect(clientIndex);
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+
+ ///
+ /// TCP Server Socket Status Change Callback
+ ///
+ ///
+ ///
+ ///
+ void SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ Debug.Console(2, "Client at {0} ServerSocketStatus {1}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), serverSocketStatus.ToString());
+ if (server.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (SharedKeyRequired && !WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Add(clientIndex);
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ else
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (Server.ServerSocketStatus.ToString() != Status)
+ onConnectionChange();
+ }
+
+ ///
+ /// TCP Client Connected to Server Callback
+ ///
+ ///
+ ///
+ void ConnectCallback(TCPServer myTCPServer, uint clientIndex)
+ {
+ if (myTCPServer.ClientConnected(clientIndex))
+ {
+ if (SharedKeyRequired)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(SharedKey + "\n");
+ myTCPServer.SendDataAsync(clientIndex, b, b.Length, SendDataAsyncCallback);
+ Debug.Console(2, "Sent Shared Key to client at {0}", myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ if (HeartbeatRequired)
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ myTCPServer.ReceiveDataAsync(clientIndex, ReceivedDataAsyncCallback);
+ if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ myTCPServer.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ }
+ if (myTCPServer.State != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ myTCPServer.WaitForConnectionAsync(IPAddress.Any, ConnectCallback);
+ }
+
+ ///
+ /// Send Data Asyc Callback
+ ///
+ ///
+ ///
+ ///
+ void SendDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesSent)
+ {
+ //Seems there is nothing to do here
+ }
+
+ ///
+ /// Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void ReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+ string received = "Nothing";
+ byte[] bytes = myTCPServer.GetIncomingDataBufferForSpecificClient(clientIndex);
+ received = System.Text.Encoding.GetEncoding(28591).GetString(bytes, 0, numberOfBytesReceived);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ {
+ received = received.Replace("\r", "");
+ received = received.Replace("\n", "");
+ if (received != SharedKey)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("Shared key did not match server. Disconnecting");
+ Debug.Console(2, "Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ ErrorLog.Error("Client at index {0} Shared key did not match the server, disconnecting client", clientIndex);
+ myTCPServer.SendDataAsync(clientIndex, b, b.Length, null);
+ myTCPServer.Disconnect(clientIndex);
+ }
+ if (myTCPServer.NumberOfClientsConnected > 0)
+ myTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] skResponse = Encoding.GetEncoding(28591).GetBytes("Shared Key Match, Connected and ready for communication");
+ myTCPServer.SendDataAsync(clientIndex, skResponse, skResponse.Length, null);
+ myTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ }
+ else
+ {
+ myTCPServer.ReceiveDataAsync(ReceivedDataAsyncCallback);
+ Debug.Console(2, "Server Listening on Port: {0}, client IP: {1}, NumberOfBytesReceived: {2}, Received: {3}\r\n",
+ myTCPServer.PortNumber, myTCPServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex), numberOfBytesReceived, received);
+ onTextReceived(received);
+ }
+ checkHeartbeat(clientIndex, received);
+ }
+ if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ myTCPServer.ReceiveDataAsync(clientIndex, ReceivedDataAsyncCallback);
+ }
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange()
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ handler(this, new DynamicTCPSocketStatusChangeEventArgs(Server, false));
+ }
+
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text)
+ {
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericCommMethodReceiveTextArgs(text));
+ }
+
+ //Private Helper Method to call the Server State Change Event
+ void onServerStateChange()
+ {
+ var handler = ServerStateChange;
+ if (handler != null)
+ handler(this, new DynamicTCPServerStateChangedEventArgs(Server, false));
+ }
+
+ //Private Event Handler method to handle the closing of connections when the program stops
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ Debug.Console(1, this, "Program stopping. Closing server");
+ DisconnectAllClients();
+ StopListening();
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
index 8d0a646..4506190 100644
--- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
+++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
@@ -64,10 +64,13 @@
+
+
+ Code
+
-
@@ -76,6 +79,7 @@
+
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo b/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo
index 700ed77..2060a7f 100644
Binary files a/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo and b/Pepperdash Core/Pepperdash Core/PepperDash_Core.projectinfo differ
diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz
index a4084b6..85471a9 100644
Binary files a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.clz differ
diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config
index 2913c74..b28fb6d 100644
--- a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config
+++ b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.config
@@ -10,8 +10,8 @@
- 3/17/2017 10:28:42 AM
- 1.0.6285.17060
+ 3/18/2017 5:00:59 PM
+ 1.0.6286.28828
Crestron.SIMPLSharp, Version=2.0.52.0, Culture=neutral, PublicKeyToken=812d080f93e2de10
diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll
index 71a2449..acb55a8 100644
Binary files a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.dll differ
diff --git a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb
index e89f325..f639405 100644
Binary files a/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb and b/Pepperdash Core/Pepperdash Core/bin/PepperDash_Core.pdb differ
diff --git a/Pepperdash Core/Pepperdash Core/bin/manifest.info b/Pepperdash Core/Pepperdash Core/bin/manifest.info
index c4b0216..dde690b 100644
--- a/Pepperdash Core/Pepperdash Core/bin/manifest.info
+++ b/Pepperdash Core/Pepperdash Core/bin/manifest.info
@@ -1,4 +1,4 @@
-MainAssembly=PepperDash_Core.dll:1cb57e7d4dc5c0e9b66ba4c496fb831f
+MainAssembly=PepperDash_Core.dll:9f208dcbcc49e496f38e91289aee13c4
MainAssemblyMinFirmwareVersion=1.007.0017
MainAssemblyResource=SimplSharpData.dat:315526abf906cded47fb0c7510266a7e
ü
diff --git a/Pepperdash Core/Pepperdash Core/bin/manifest.ser b/Pepperdash Core/Pepperdash Core/bin/manifest.ser
index 3079acb..ecf2682 100644
Binary files a/Pepperdash Core/Pepperdash Core/bin/manifest.ser and b/Pepperdash Core/Pepperdash Core/bin/manifest.ser differ
diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll
index 3c782b2..bc85983 100644
Binary files a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll and b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.dll differ
diff --git a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb
index 8b58d9d..c0dc85d 100644
Binary files a/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb and b/Pepperdash Core/Pepperdash Core/obj/Debug/PepperDash_Core.pdb differ