Merged in maintenance/PR-3 (pull request #21)

Maintenance/PR-3

Approved-by: Neil Dorin <ndorin@pepperdash.com>
This commit is contained in:
Neil Dorin 2019-07-17 21:05:37 +00:00
commit c1b5cfe193
17 changed files with 469 additions and 2166 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -93,6 +93,24 @@ namespace PepperDash.Core
} }
} }
public class GenericUdpConnectedEventArgs : EventArgs
{
public ushort UConnected;
public bool Connected;
public GenericUdpConnectedEventArgs() { }
public GenericUdpConnectedEventArgs(ushort uconnected)
{
UConnected = uconnected;
}
public GenericUdpConnectedEventArgs(bool connected)
{
Connected = connected;
}
}
} }

View file

@ -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,7 @@ namespace PepperDash.Core
Client = new SecureTCPClient(Hostname, Port, BufferSize); Client = new SecureTCPClient(Hostname, Port, BufferSize);
Client.SocketStatusChange += Client_SocketStatusChange; Client.SocketStatusChange += Client_SocketStatusChange;
if(HeartbeatEnabled) if (HeartbeatEnabled)
Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5); Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
Client.AddressClientConnectedTo = Hostname; Client.AddressClientConnectedTo = Hostname;
Client.PortNumber = Port; Client.PortNumber = Port;
@ -473,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);
} }
} }
@ -487,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();
@ -517,6 +607,10 @@ namespace PepperDash.Core
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));
}
} }
} }
} }
@ -524,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()

View file

@ -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
{ {
@ -391,7 +416,7 @@ namespace PepperDash.Core
} }
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;
@ -678,7 +703,10 @@ namespace PepperDash.Core
{ {
Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex); Debug.Console(0, 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
@ -770,6 +798,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);
@ -792,11 +821,16 @@ namespace PepperDash.Core
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);
}
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
{
onTextReceived(received, clientIndex);
if (handler != null)
{
MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(received, clientIndex));
}
} }
else if (!string.IsNullOrEmpty(checkHeartbeat(clientIndex, received)))
onTextReceived(received, clientIndex);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -804,13 +838,49 @@ namespace PepperDash.Core
} }
if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED) if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback); 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());
}
} }
else else
{ {
// If numberOfBytesReceived <= 0
mySecureTCPServer.Disconnect(clientIndex); 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(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();
}
} }
#endregion #endregion

View file

@ -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)]

View file

@ -43,6 +43,8 @@ namespace PepperDash.Core
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange; //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange; public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
public event EventHandler<GenericUdpConnectedEventArgs> UpdateConnectionStatus;
public SocketStatus ClientStatus public SocketStatus ClientStatus
{ {
get get
@ -51,6 +53,11 @@ namespace PepperDash.Core
} }
} }
public ushort UStatus
{
get { return (ushort)Server.ServerStatus; }
}
CCriticalSection DequeueLock; CCriticalSection DequeueLock;
/// <summary> /// <summary>
@ -87,6 +94,11 @@ namespace PepperDash.Core
private set; private set;
} }
public ushort UIsConnected
{
get { return IsConnected ? (ushort)1 : (ushort)0; }
}
/// <summary> /// <summary>
/// Defaults to 2000 /// Defaults to 2000
/// </summary> /// </summary>
@ -94,6 +106,20 @@ namespace PepperDash.Core
public UDPServer Server { get; private set; } public UDPServer Server { get; private set; }
/// <summary>
/// Constructor for S+. Make sure to set key, address, port, and buffersize using init method
/// </summary>
public GenericUdpServer()
: base("Uninitialized Udp Server")
{
BufferSize = 5000;
DequeueLock = new CCriticalSection();
MessageQueue = new CrestronQueue<GenericUdpReceiveTextExtraArgs>();
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
}
public GenericUdpServer(string key, string address, int port, int buffefSize) public GenericUdpServer(string key, string address, int port, int buffefSize)
: base(key) : base(key)
{ {
@ -108,6 +134,13 @@ namespace PepperDash.Core
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
} }
public void Initialize(string key, string address, ushort port)
{
Key = key;
Hostname = address;
UPort = port;
}
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{ {
// Re-enable the server if the link comes back up and the status should be connected // Re-enable the server if the link comes back up and the status should be connected
@ -135,7 +168,6 @@ namespace PepperDash.Core
if (Server == null) if (Server == null)
{ {
Server = new UDPServer(); Server = new UDPServer();
} }
if (string.IsNullOrEmpty(Hostname)) if (string.IsNullOrEmpty(Hostname))
@ -157,6 +189,10 @@ namespace PepperDash.Core
if (status == SocketErrorCodes.SOCKET_OK) if (status == SocketErrorCodes.SOCKET_OK)
IsConnected = true; IsConnected = true;
var handler = UpdateConnectionStatus;
if (handler != null)
handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
// Start receiving data // Start receiving data
Server.ReceiveDataAsync(Receive); Server.ReceiveDataAsync(Receive);
} }
@ -170,6 +206,10 @@ namespace PepperDash.Core
Server.DisableUDPServer(); Server.DisableUDPServer();
IsConnected = false; IsConnected = false;
var handler = UpdateConnectionStatus;
if (handler != null)
handler(this, new GenericUdpConnectedEventArgs(UIsConnected));
} }

View file

@ -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; }
}
}

View file

@ -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; }
} }
} }

View file

@ -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

View file

@ -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; }
} }
} }

View file

@ -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>

View file

@ -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;

View file

@ -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()
{ {
} }

View file

@ -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>
@ -81,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" />

View file

@ -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";
}