Merged in bugfix/pdc-7 (pull request #10)

Bugfix/pdc 7

Approved-by: Neil Dorin <ndorin@pepperdash.com>
This commit is contained in:
Neil Dorin
2019-05-16 18:31:22 +00:00
committed by Heath Volmer
7 changed files with 318 additions and 235 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -174,8 +174,8 @@ namespace PepperDash.Core
if (Client != null) if (Client != null)
{ {
Debug.Console(1, this, "Program stopping. Closing connection"); Debug.Console(1, this, "Program stopping. Closing connection");
Client.Disconnect(); Disconnect();
Client.Dispose(); //Client.Dispose();
} }
} }
} }

View File

@@ -387,13 +387,16 @@ namespace PepperDash.Core
myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5); myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
// myTcpServer.HandshakeTimeout = 30; // myTcpServer.HandshakeTimeout = 30;
myTcpServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(TcpServer_SocketStatusChange);
} }
else else
{ {
KillServer(); KillServer();
myTcpServer.PortNumber = Port; myTcpServer.PortNumber = Port;
} }
myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
ServerStopped = false; ServerStopped = false;
myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback); myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
OnServerStateChange(myTcpServer.State); OnServerStateChange(myTcpServer.State);

View File

@@ -1,231 +1,311 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets; using Crestron.SimplSharp.CrestronSockets;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace PepperDash.Core
{
public class GenericUdpServer : Device, IBasicCommunication namespace PepperDash.Core
{ {
/// <summary> public class GenericUdpServer : Device, IBasicCommunication
/// {
/// </summary> /// <summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; ///
/// </summary>
/// <summary> public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
///
/// </summary> /// <summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; ///
/// </summary>
/// <summary> public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
///
/// </summary> /// <summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange; /// This event will fire when a message is dequeued that includes the source IP and Port info if needed to determine the source of the received data.
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange; /// </summary>
public event EventHandler<GenericUdpReceiveTextExtraArgs> DataRecievedExtra;
public SocketStatus ClientStatus
{ /// <summary>
get /// Queue to temporarily store received messages with the source IP and Port info
{ /// </summary>
return Server.ServerStatus; private CrestronQueue<GenericUdpReceiveTextExtraArgs> MessageQueue;
}
} /// <summary>
///
/// <summary> /// </summary>
/// Address of server //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
/// </summary> public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
public string Hostname { get; set; }
public SocketStatus ClientStatus
/// <summary> {
/// Port on server get
/// </summary> {
public int Port { get; set; } return Server.ServerStatus;
}
/// <summary> }
/// Another damn S+ helper because S+ seems to treat large port nums as signed ints
/// which screws up things
/// </summary> CCriticalSection DequeueLock;
public ushort UPort /// <summary>
{ /// Address of server
get { return Convert.ToUInt16(Port); } /// </summary>
set { Port = Convert.ToInt32(value); } public string Hostname { get; set; }
}
/// <summary>
/// <summary> /// IP Address of the sender of the last recieved message
/// Indicates that the UDP Server is enabled /// </summary>
/// </summary>
public bool IsConnected
{ /// <summary>
get; /// Port on server
private set; /// </summary>
} public int Port { get; set; }
/// <summary> /// <summary>
/// Defaults to 2000 /// Another damn S+ helper because S+ seems to treat large port nums as signed ints
/// </summary> /// which screws up things
public int BufferSize { get; set; } /// </summary>
public ushort UPort
public UDPServer Server { get; private set; } {
get { return Convert.ToUInt16(Port); }
public GenericUdpServer(string key, string address, int port, int buffefSize) set { Port = Convert.ToInt32(value); }
: base(key) }
{
Hostname = address; /// <summary>
Port = port; /// Indicates that the UDP Server is enabled
BufferSize = buffefSize; /// </summary>
public bool IsConnected
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); {
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); get;
} private set;
}
void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{ /// <summary>
// Re-enable the server if the link comes back up and the status should be connected /// Defaults to 2000
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp /// </summary>
&& IsConnected) public int BufferSize { get; set; }
{
Connect(); public UDPServer Server { get; private set; }
}
} public GenericUdpServer(string key, string address, int port, int buffefSize)
: base(key)
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) {
{ Hostname = address;
if (programEventType == eProgramStatusEventType.Stopping) Port = port;
{ BufferSize = buffefSize;
Debug.Console(1, this, "Program stopping. Disabling Server");
Disconnect(); DequeueLock = new CCriticalSection();
} MessageQueue = new CrestronQueue<GenericUdpReceiveTextExtraArgs>();
}
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
/// <summary> CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
/// Enables the UDP Server }
/// </summary>
public void Connect() void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{ {
if (Server == null) // Re-enable the server if the link comes back up and the status should be connected
{ if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp
Server = new UDPServer(); && IsConnected)
{
} Connect();
}
if (string.IsNullOrEmpty(Hostname)) }
{
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key); void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
return; {
} if (programEventType == eProgramStatusEventType.Stopping)
if (Port < 1 || Port > 65535) {
{ Debug.Console(1, this, "Program stopping. Disabling Server");
{ Disconnect();
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key); }
return; }
}
} /// <summary>
/// Enables the UDP Server
var status = Server.EnableUDPServer(Hostname, Port); /// </summary>
public void Connect()
Debug.Console(2, this, "SocketErrorCode: {0}", status); {
if (status == SocketErrorCodes.SOCKET_OK) if (Server == null)
IsConnected = true; {
Server = new UDPServer();
// Start receiving data
Server.ReceiveDataAsync(Receive); }
}
if (string.IsNullOrEmpty(Hostname))
/// <summary> {
/// Disabled the UDP Server Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
/// </summary> return;
public void Disconnect() }
{ if (Port < 1 || Port > 65535)
if(Server != null) {
Server.DisableUDPServer(); {
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key);
IsConnected = false; return;
} }
}
/// <summary> var status = Server.EnableUDPServer(Hostname, Port);
/// Recursive method to receive data
/// </summary> Debug.Console(2, this, "SocketErrorCode: {0}", status);
/// <param name="server"></param> if (status == SocketErrorCodes.SOCKET_OK)
/// <param name="numBytes"></param> IsConnected = true;
void Receive(UDPServer server, int numBytes)
{ // Start receiving data
Debug.Console(2, this, "Received {0} bytes", numBytes); Server.ReceiveDataAsync(Receive);
}
if (numBytes > 0)
{ /// <summary>
var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray(); /// Disabled the UDP Server
/// </summary>
Debug.Console(2, this, "Bytes: {0}", bytes.ToString()); public void Disconnect()
var bytesHandler = BytesReceived; {
if (bytesHandler != null) if(Server != null)
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); Server.DisableUDPServer();
else
Debug.Console(2, this, "bytesHandler is null"); IsConnected = false;
var textHandler = TextReceived; }
if (textHandler != null)
{
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); /// <summary>
Debug.Console(2, this, "RX: {0}", str); /// Recursive method to receive data
textHandler(this, new GenericCommMethodReceiveTextArgs(str)); /// </summary>
} /// <param name="server"></param>
else /// <param name="numBytes"></param>
Debug.Console(2, this, "textHandler is null"); void Receive(UDPServer server, int numBytes)
} {
server.ReceiveDataAsync(Receive); Debug.Console(2, this, "Received {0} bytes", numBytes);
}
if (numBytes > 0)
/// <summary> {
/// General send method var sourceIp = Server.IPAddressLastMessageReceivedFrom;
/// </summary> var sourcePort = Server.IPPortLastMessageReceivedFrom;
/// <param name="text"></param> var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray();
public void SendText(string text) var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
{ MessageQueue.TryToEnqueue(new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes));
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
Debug.Console(2, this, "Bytes: {0}", bytes.ToString());
if (IsConnected && Server != null) var bytesHandler = BytesReceived;
{ if (bytesHandler != null)
Debug.Console(2, this, "TX: {0}", text); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
Server.SendData(bytes, bytes.Length); else
} Debug.Console(2, this, "bytesHandler is null");
} var textHandler = TextReceived;
if (textHandler != null)
public void SendBytes(byte[] bytes) {
{
//if (Debug.Level == 2) Debug.Console(2, this, "RX: {0}", str);
// Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes)); textHandler(this, new GenericCommMethodReceiveTextArgs(str));
if (IsConnected && Server != null) }
Server.SendData(bytes, bytes.Length); else
} Debug.Console(2, this, "textHandler is null");
}
server.ReceiveDataAsync(Receive);
} // Attempt to enter the CCritical Secion and if we can, start the dequeue thread
var gotLock = DequeueLock.TryEnter();
public class UdpServerPropertiesConfig if (gotLock)
{ CrestronInvoke.BeginInvoke((o) => DequeueEvent());
[JsonProperty(Required = Required.Always)] }
public string Address { get; set; }
/// <summary>
[JsonProperty(Required = Required.Always)] /// This method gets spooled up in its own thread an protected by a CCriticalSection to prevent multiple threads from running concurrently.
public int Port { get; set; } /// It will dequeue items as they are enqueued automatically.
/// </summary>
/// <summary> void DequeueEvent()
/// Defaults to 32768 {
/// </summary> try
public int BufferSize { get; set; } {
while (true)
public UdpServerPropertiesConfig() {
{ // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather.
BufferSize = 32768; var message = MessageQueue.Dequeue();
} var dataRecivedExtra = DataRecievedExtra;
} if (dataRecivedExtra != null)
{
dataRecivedExtra(this, message);
}
}
}
catch (Exception e)
{
Debug.Console(0, "GenericUdpServer DequeueEvent error: {0}\r", e);
}
finally
{
// Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
DequeueLock.Leave();
}
}
/// <summary>
/// General send method
/// </summary>
/// <param name="text"></param>
public void SendText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
if (IsConnected && Server != null)
{
Debug.Console(2, this, "TX: {0}", text);
Server.SendData(bytes, bytes.Length);
}
}
public void SendBytes(byte[] bytes)
{
//if (Debug.Level == 2)
// Debug.Console(2, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length);
}
}
public class GenericUdpReceiveTextExtraArgs : EventArgs
{
public string Text { get; private set; }
public string IpAddress { get; private set; }
public int Port { get; private set; }
public byte[] Bytes { get; private set; }
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
{
Text = text;
IpAddress = ipAddress;
Port = port;
Bytes = bytes;
}
/// <summary>
/// Stupid S+ Constructor
/// </summary>
public GenericUdpReceiveTextExtraArgs() { }
}
public class UdpServerPropertiesConfig
{
[JsonProperty(Required = Required.Always)]
public string Address { get; set; }
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
public UdpServerPropertiesConfig()
{
BufferSize = 32768;
}
}
} }

View File

@@ -59,7 +59,7 @@
</Reference> </Reference>
<Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL"> <Reference Include="SimplSharpReflectionInterface, Version=1.0.5583.25238, Culture=neutral, PublicKeyToken=1099c178b3b54c3b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath> <HintPath>..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />

View File

@@ -4,4 +4,4 @@
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Pepperdash_Core")] [assembly: AssemblyProduct("Pepperdash_Core")]
[assembly: AssemblyCopyright("Copyright © PepperDash 2019")] [assembly: AssemblyCopyright("Copyright © PepperDash 2019")]
[assembly: AssemblyVersion("1.0.16.*")] [assembly: AssemblyVersion("1.0.17.*")]