diff --git a/CLZ Builds/PepperDash_Core.clz b/CLZ Builds/PepperDash_Core.clz index f3a2ec2..6899b0e 100644 Binary files a/CLZ Builds/PepperDash_Core.clz and b/CLZ Builds/PepperDash_Core.clz differ diff --git a/CLZ Builds/PepperDash_Core.dll b/CLZ Builds/PepperDash_Core.dll index 43d7667..9bdbeb2 100644 Binary files a/CLZ Builds/PepperDash_Core.dll and b/CLZ Builds/PepperDash_Core.dll differ diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs index 3d1101a..6af5d7b 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs @@ -174,8 +174,8 @@ namespace PepperDash.Core if (Client != null) { Debug.Console(1, this, "Program stopping. Closing connection"); - Client.Disconnect(); - Client.Dispose(); + Disconnect(); + //Client.Dispose(); } } } diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs index 2ef2845..da5c6fe 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs @@ -387,13 +387,16 @@ namespace PepperDash.Core myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5); // myTcpServer.HandshakeTimeout = 30; - myTcpServer.SocketStatusChange += new TCPServerSocketStatusChangeEventHandler(TcpServer_SocketStatusChange); } else { KillServer(); myTcpServer.PortNumber = Port; } + + myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange; + myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange; + ServerStopped = false; myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback); OnServerStateChange(myTcpServer.State); diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs b/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs index a67a05f..5b1bf3f 100644 --- a/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs +++ b/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs @@ -1,231 +1,311 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -using Crestron.SimplSharp; -using Crestron.SimplSharp.CrestronSockets; - -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - - -namespace PepperDash.Core -{ - public class GenericUdpServer : Device, IBasicCommunication - { - /// - /// - /// - public event EventHandler BytesReceived; - - /// - /// - /// - public event EventHandler TextReceived; - - /// - /// - /// - //public event GenericSocketStatusChangeEventDelegate SocketStatusChange; - public event EventHandler ConnectionChange; - - public SocketStatus ClientStatus - { - get - { - return Server.ServerStatus; - } - } - - /// - /// Address of server - /// - public string Hostname { get; set; } - - /// - /// Port on server - /// - public int Port { get; set; } - - /// - /// Another damn S+ helper because S+ seems to treat large port nums as signed ints - /// which screws up things - /// - public ushort UPort - { - get { return Convert.ToUInt16(Port); } - set { Port = Convert.ToInt32(value); } - } - - /// - /// Indicates that the UDP Server is enabled - /// - public bool IsConnected - { - get; - private set; - } - - /// - /// Defaults to 2000 - /// - public int BufferSize { get; set; } - - public UDPServer Server { get; private set; } - - public GenericUdpServer(string key, string address, int port, int buffefSize) - : base(key) - { - Hostname = address; - Port = port; - BufferSize = buffefSize; - - CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); - CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); - } - - void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) - { - // Re-enable the server if the link comes back up and the status should be connected - if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp - && IsConnected) - { - Connect(); - } - } - - void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) - { - if (programEventType == eProgramStatusEventType.Stopping) - { - Debug.Console(1, this, "Program stopping. Disabling Server"); - Disconnect(); - } - } - - /// - /// Enables the UDP Server - /// - public void Connect() - { - if (Server == null) - { - Server = new UDPServer(); - - } - - if (string.IsNullOrEmpty(Hostname)) - { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key); - return; - } - if (Port < 1 || Port > 65535) - { - { - Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key); - return; - } - } - - var status = Server.EnableUDPServer(Hostname, Port); - - Debug.Console(2, this, "SocketErrorCode: {0}", status); - if (status == SocketErrorCodes.SOCKET_OK) - IsConnected = true; - - // Start receiving data - Server.ReceiveDataAsync(Receive); - } - - /// - /// Disabled the UDP Server - /// - public void Disconnect() - { - if(Server != null) - Server.DisableUDPServer(); - - IsConnected = false; - } - - - /// - /// Recursive method to receive data - /// - /// - /// - void Receive(UDPServer server, int numBytes) - { - Debug.Console(2, this, "Received {0} bytes", numBytes); - - if (numBytes > 0) - { - var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray(); - - Debug.Console(2, this, "Bytes: {0}", bytes.ToString()); - var bytesHandler = BytesReceived; - if (bytesHandler != null) - bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); - else - Debug.Console(2, this, "bytesHandler is null"); - var textHandler = TextReceived; - if (textHandler != null) - { - var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); - Debug.Console(2, this, "RX: {0}", str); - textHandler(this, new GenericCommMethodReceiveTextArgs(str)); - } - else - Debug.Console(2, this, "textHandler is null"); - } - server.ReceiveDataAsync(Receive); - } - - /// - /// General send method - /// - /// - 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 UdpServerPropertiesConfig - { - [JsonProperty(Required = Required.Always)] - public string Address { get; set; } - - [JsonProperty(Required = Required.Always)] - public int Port { get; set; } - - /// - /// Defaults to 32768 - /// - public int BufferSize { get; set; } - - public UdpServerPropertiesConfig() - { - BufferSize = 32768; - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +using Crestron.SimplSharp; +using Crestron.SimplSharp.CrestronSockets; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + + + + +namespace PepperDash.Core +{ + public class GenericUdpServer : Device, IBasicCommunication + { + /// + /// + /// + public event EventHandler BytesReceived; + + /// + /// + /// + public event EventHandler TextReceived; + + /// + /// 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 DataRecievedExtra; + + /// + /// Queue to temporarily store received messages with the source IP and Port info + /// + private CrestronQueue MessageQueue; + + /// + /// + /// + //public event GenericSocketStatusChangeEventDelegate SocketStatusChange; + public event EventHandler ConnectionChange; + + public SocketStatus ClientStatus + { + get + { + return Server.ServerStatus; + } + } + + + CCriticalSection DequeueLock; + /// + /// Address of server + /// + public string Hostname { get; set; } + + /// + /// IP Address of the sender of the last recieved message + /// + + + /// + /// Port on server + /// + public int Port { get; set; } + + /// + /// Another damn S+ helper because S+ seems to treat large port nums as signed ints + /// which screws up things + /// + public ushort UPort + { + get { return Convert.ToUInt16(Port); } + set { Port = Convert.ToInt32(value); } + } + + /// + /// Indicates that the UDP Server is enabled + /// + public bool IsConnected + { + get; + private set; + } + + /// + /// Defaults to 2000 + /// + public int BufferSize { get; set; } + + public UDPServer Server { get; private set; } + + public GenericUdpServer(string key, string address, int port, int buffefSize) + : base(key) + { + Hostname = address; + Port = port; + BufferSize = buffefSize; + + DequeueLock = new CCriticalSection(); + MessageQueue = new CrestronQueue(); + + CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); + CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); + } + + void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) + { + // Re-enable the server if the link comes back up and the status should be connected + if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp + && IsConnected) + { + Connect(); + } + } + + void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType) + { + if (programEventType == eProgramStatusEventType.Stopping) + { + Debug.Console(1, this, "Program stopping. Disabling Server"); + Disconnect(); + } + } + + /// + /// Enables the UDP Server + /// + public void Connect() + { + if (Server == null) + { + Server = new UDPServer(); + + } + + if (string.IsNullOrEmpty(Hostname)) + { + Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key); + return; + } + if (Port < 1 || Port > 65535) + { + { + Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': Invalid port", Key); + return; + } + } + + var status = Server.EnableUDPServer(Hostname, Port); + + Debug.Console(2, this, "SocketErrorCode: {0}", status); + if (status == SocketErrorCodes.SOCKET_OK) + IsConnected = true; + + // Start receiving data + Server.ReceiveDataAsync(Receive); + } + + /// + /// Disabled the UDP Server + /// + public void Disconnect() + { + if(Server != null) + Server.DisableUDPServer(); + + IsConnected = false; + } + + + /// + /// Recursive method to receive data + /// + /// + /// + void Receive(UDPServer server, int numBytes) + { + Debug.Console(2, this, "Received {0} bytes", numBytes); + + if (numBytes > 0) + { + var sourceIp = Server.IPAddressLastMessageReceivedFrom; + var sourcePort = Server.IPPortLastMessageReceivedFrom; + var bytes = server.IncomingDataBuffer.Take(numBytes).ToArray(); + var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); + MessageQueue.TryToEnqueue(new GenericUdpReceiveTextExtraArgs(str, sourceIp, sourcePort, bytes)); + + Debug.Console(2, this, "Bytes: {0}", bytes.ToString()); + var bytesHandler = BytesReceived; + if (bytesHandler != null) + bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); + else + Debug.Console(2, this, "bytesHandler is null"); + var textHandler = TextReceived; + if (textHandler != null) + { + + Debug.Console(2, this, "RX: {0}", str); + textHandler(this, new GenericCommMethodReceiveTextArgs(str)); + } + else + Debug.Console(2, this, "textHandler is null"); + } + server.ReceiveDataAsync(Receive); + + // Attempt to enter the CCritical Secion and if we can, start the dequeue thread + var gotLock = DequeueLock.TryEnter(); + if (gotLock) + CrestronInvoke.BeginInvoke((o) => DequeueEvent()); + } + + /// + /// 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. + /// + void DequeueEvent() + { + try + { + while (true) + { + // Pull from Queue and fire an event. Block indefinitely until an item can be removed, similar to a Gather. + var message = MessageQueue.Dequeue(); + var dataRecivedExtra = DataRecievedExtra; + if (dataRecivedExtra != null) + { + dataRecivedExtra(this, message); + } + } + } + catch (Exception e) + { + Debug.Console(0, "GenericUdpServer DequeueEvent error: {0}\r", e); + } + 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(); + } + } + + /// + /// General send method + /// + /// + 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; + } + + /// + /// Stupid S+ Constructor + /// + public GenericUdpReceiveTextExtraArgs() { } + } + + public class UdpServerPropertiesConfig + { + [JsonProperty(Required = Required.Always)] + public string Address { get; set; } + + [JsonProperty(Required = Required.Always)] + public int Port { get; set; } + + /// + /// Defaults to 32768 + /// + public int BufferSize { get; set; } + + public UdpServerPropertiesConfig() + { + BufferSize = 32768; + } + } } \ 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 f9584be..8a033a2 100644 --- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj +++ b/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj @@ -59,7 +59,7 @@ False - ..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll + ..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll diff --git a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs b/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs index 658a74f..973cc52 100644 --- a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs +++ b/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs @@ -4,4 +4,4 @@ [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Pepperdash_Core")] [assembly: AssemblyCopyright("Copyright © PepperDash 2019")] -[assembly: AssemblyVersion("1.0.16.*")] +[assembly: AssemblyVersion("1.0.17.*")]