diff --git a/Pepperdash Core/Pepperdash Core.slnold b/Pepperdash Core/Pepperdash Core.slnold
deleted file mode 100644
index f56017b..0000000
--- a/Pepperdash Core/Pepperdash Core.slnold
+++ /dev/null
@@ -1,19 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Core", "Pepperdash Core\PepperDash_Core.csproj", "{87E29B4C-569B-4368-A4ED-984AC1440C96}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/Pepperdash Core/Pepperdash Core.suo.orig b/Pepperdash Core/Pepperdash Core.suo.orig
deleted file mode 100644
index 6d73571..0000000
Binary files a/Pepperdash Core/Pepperdash Core.suo.orig and /dev/null differ
diff --git a/Pepperdash Core/Pepperdash Core.sln b/Pepperdash_Core.3Series.sln
similarity index 88%
rename from Pepperdash Core/Pepperdash Core.sln
rename to Pepperdash_Core.3Series.sln
index 527f806..df6c1bc 100644
--- a/Pepperdash Core/Pepperdash Core.sln
+++ b/Pepperdash_Core.3Series.sln
@@ -1,19 +1,19 @@
-Microsoft Visual Studio Solution File, Format Version 10.00
-# Visual Studio 2008
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Core", "Pepperdash Core\PepperDash_Core.csproj", "{87E29B4C-569B-4368-A4ED-984AC1440C96}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PepperDash_Core", "src\PepperDash_Core.csproj", "{87E29B4C-569B-4368-A4ED-984AC1440C96}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {87E29B4C-569B-4368-A4ED-984AC1440C96}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {87E29B4C-569B-4368-A4ED-984AC1440C96}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Pepperdash Core/Pepperdash Core/Comm/._GenericSshClient.cs b/src/Comm/._GenericSshClient.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/._GenericSshClient.cs
rename to src/Comm/._GenericSshClient.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/._GenericTcpIpClient.cs b/src/Comm/._GenericTcpIpClient.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/._GenericTcpIpClient.cs
rename to src/Comm/._GenericTcpIpClient.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/CommunicationGather.cs b/src/Comm/CommunicationGather.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Comm/CommunicationGather.cs
rename to src/Comm/CommunicationGather.cs
index f71a496..9ffe826 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/CommunicationGather.cs
+++ b/src/Comm/CommunicationGather.cs
@@ -1,179 +1,179 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-
-using PepperDash.Core;
-
-
-namespace PepperDash.Core
-{
- ///
- /// Defines the string event handler for line events on the gather
- ///
- ///
- public delegate void LineReceivedHandler(string text);
-
- ///
- /// Attaches to IBasicCommunication as a text gather
- ///
- public class CommunicationGather
- {
- ///
- /// Event that fires when a line is received from the IBasicCommunication source.
- /// The event merely contains the text, not an EventArgs type class.
- ///
- public event EventHandler LineReceived;
-
- ///
- /// The communication port that this gathers on
- ///
- public ICommunicationReceiver Port { get; private set; }
-
- ///
- /// Default false. If true, the delimiter will be included in the line output
- /// events
- ///
- public bool IncludeDelimiter { get; set; }
-
- ///
- /// For receive buffer
- ///
- StringBuilder ReceiveBuffer = new StringBuilder();
-
- ///
- /// Delimiter, like it says!
- ///
- char Delimiter;
-
- string[] StringDelimiters;
-
- ///
- /// Constructor for using a char delimiter
- ///
- ///
- ///
- public CommunicationGather(ICommunicationReceiver port, char delimiter)
- {
- Port = port;
- Delimiter = delimiter;
- port.TextReceived += new EventHandler(Port_TextReceived);
- }
-
- ///
- /// Constructor for using a single string delimiter
- ///
- ///
- ///
- public CommunicationGather(ICommunicationReceiver port, string delimiter)
- :this(port, new string[] { delimiter} )
- {
- }
-
- ///
- /// Constructor for using an array of string delimiters
- ///
- ///
- ///
- public CommunicationGather(ICommunicationReceiver port, string[] delimiters)
- {
- Port = port;
- StringDelimiters = delimiters;
- port.TextReceived += Port_TextReceivedStringDelimiter;
- }
-
- ///
- /// Disconnects this gather from the Port's TextReceived event. This will not fire LineReceived
- /// after the this call.
- ///
- public void Stop()
- {
- Port.TextReceived -= Port_TextReceived;
- Port.TextReceived -= Port_TextReceivedStringDelimiter;
- }
-
- ///
- /// Handler for raw data coming from port
- ///
- void Port_TextReceived(object sender, GenericCommMethodReceiveTextArgs args)
- {
- var handler = LineReceived;
- if (handler != null)
- {
- ReceiveBuffer.Append(args.Text);
- var str = ReceiveBuffer.ToString();
- var lines = str.Split(Delimiter);
- if (lines.Length > 0)
- {
- for (int i = 0; i < lines.Length - 1; i++)
- {
- string strToSend = null;
- if (IncludeDelimiter)
- strToSend = lines[i] + Delimiter;
- else
- strToSend = lines[i];
- handler(this, new GenericCommMethodReceiveTextArgs(strToSend));
- }
- ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
- }
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- void Port_TextReceivedStringDelimiter(object sender, GenericCommMethodReceiveTextArgs args)
- {
- var handler = LineReceived;
- if (handler != null)
- {
- // Receive buffer should either be empty or not contain the delimiter
- // If the line does not have a delimiter, append the
- ReceiveBuffer.Append(args.Text);
- var str = ReceiveBuffer.ToString();
-
- // Case: Receiving DEVICE get version\x0d\0x0a+OK "value":"1234"\x0d\x0a
-
- // RX: DEV
- // Split: (1) "DEV"
- // RX: I
- // Split: (1) "DEVI"
- // RX: CE get version
- // Split: (1) "DEVICE get version"
- // RX: \x0d\x0a+OK "value":"1234"\x0d\x0a
- // Split: (2) DEVICE get version, +OK "value":"1234"
-
- // Iterate the delimiters and fire an event for any matching delimiter
- foreach (var delimiter in StringDelimiters)
- {
- var lines = Regex.Split(str, delimiter);
- if (lines.Length == 1)
- continue;
-
- for (int i = 0; i < lines.Length - 1; i++)
- {
- string strToSend = null;
- if (IncludeDelimiter)
- strToSend = lines[i] + delimiter;
- else
- strToSend = lines[i];
- handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));
- }
- ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
- }
- }
- }
-
- ///
- /// Deconstructor. Disconnects from port TextReceived events.
- ///
- ~CommunicationGather()
- {
- Stop();
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+
+using PepperDash.Core;
+
+
+namespace PepperDash.Core
+{
+ ///
+ /// Defines the string event handler for line events on the gather
+ ///
+ ///
+ public delegate void LineReceivedHandler(string text);
+
+ ///
+ /// Attaches to IBasicCommunication as a text gather
+ ///
+ public class CommunicationGather
+ {
+ ///
+ /// Event that fires when a line is received from the IBasicCommunication source.
+ /// The event merely contains the text, not an EventArgs type class.
+ ///
+ public event EventHandler LineReceived;
+
+ ///
+ /// The communication port that this gathers on
+ ///
+ public ICommunicationReceiver Port { get; private set; }
+
+ ///
+ /// Default false. If true, the delimiter will be included in the line output
+ /// events
+ ///
+ public bool IncludeDelimiter { get; set; }
+
+ ///
+ /// For receive buffer
+ ///
+ StringBuilder ReceiveBuffer = new StringBuilder();
+
+ ///
+ /// Delimiter, like it says!
+ ///
+ char Delimiter;
+
+ string[] StringDelimiters;
+
+ ///
+ /// Constructor for using a char delimiter
+ ///
+ ///
+ ///
+ public CommunicationGather(ICommunicationReceiver port, char delimiter)
+ {
+ Port = port;
+ Delimiter = delimiter;
+ port.TextReceived += new EventHandler(Port_TextReceived);
+ }
+
+ ///
+ /// Constructor for using a single string delimiter
+ ///
+ ///
+ ///
+ public CommunicationGather(ICommunicationReceiver port, string delimiter)
+ :this(port, new string[] { delimiter} )
+ {
+ }
+
+ ///
+ /// Constructor for using an array of string delimiters
+ ///
+ ///
+ ///
+ public CommunicationGather(ICommunicationReceiver port, string[] delimiters)
+ {
+ Port = port;
+ StringDelimiters = delimiters;
+ port.TextReceived += Port_TextReceivedStringDelimiter;
+ }
+
+ ///
+ /// Disconnects this gather from the Port's TextReceived event. This will not fire LineReceived
+ /// after the this call.
+ ///
+ public void Stop()
+ {
+ Port.TextReceived -= Port_TextReceived;
+ Port.TextReceived -= Port_TextReceivedStringDelimiter;
+ }
+
+ ///
+ /// Handler for raw data coming from port
+ ///
+ void Port_TextReceived(object sender, GenericCommMethodReceiveTextArgs args)
+ {
+ var handler = LineReceived;
+ if (handler != null)
+ {
+ ReceiveBuffer.Append(args.Text);
+ var str = ReceiveBuffer.ToString();
+ var lines = str.Split(Delimiter);
+ if (lines.Length > 0)
+ {
+ for (int i = 0; i < lines.Length - 1; i++)
+ {
+ string strToSend = null;
+ if (IncludeDelimiter)
+ strToSend = lines[i] + Delimiter;
+ else
+ strToSend = lines[i];
+ handler(this, new GenericCommMethodReceiveTextArgs(strToSend));
+ }
+ ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ void Port_TextReceivedStringDelimiter(object sender, GenericCommMethodReceiveTextArgs args)
+ {
+ var handler = LineReceived;
+ if (handler != null)
+ {
+ // Receive buffer should either be empty or not contain the delimiter
+ // If the line does not have a delimiter, append the
+ ReceiveBuffer.Append(args.Text);
+ var str = ReceiveBuffer.ToString();
+
+ // Case: Receiving DEVICE get version\x0d\0x0a+OK "value":"1234"\x0d\x0a
+
+ // RX: DEV
+ // Split: (1) "DEV"
+ // RX: I
+ // Split: (1) "DEVI"
+ // RX: CE get version
+ // Split: (1) "DEVICE get version"
+ // RX: \x0d\x0a+OK "value":"1234"\x0d\x0a
+ // Split: (2) DEVICE get version, +OK "value":"1234"
+
+ // Iterate the delimiters and fire an event for any matching delimiter
+ foreach (var delimiter in StringDelimiters)
+ {
+ var lines = Regex.Split(str, delimiter);
+ if (lines.Length == 1)
+ continue;
+
+ for (int i = 0; i < lines.Length - 1; i++)
+ {
+ string strToSend = null;
+ if (IncludeDelimiter)
+ strToSend = lines[i] + delimiter;
+ else
+ strToSend = lines[i];
+ handler(this, new GenericCommMethodReceiveTextArgs(strToSend, delimiter));
+ }
+ ReceiveBuffer = new StringBuilder(lines[lines.Length - 1]);
+ }
+ }
+ }
+
+ ///
+ /// Deconstructor. Disconnects from port TextReceived events.
+ ///
+ ~CommunicationGather()
+ {
+ Stop();
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs b/src/Comm/CommunicationStreamDebugging.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/CommunicationStreamDebugging.cs
rename to src/Comm/CommunicationStreamDebugging.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/ControlPropertiesConfig.cs b/src/Comm/ControlPropertiesConfig.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/ControlPropertiesConfig.cs
rename to src/Comm/ControlPropertiesConfig.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs b/src/Comm/DynamicTCPServer.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs
rename to src/Comm/DynamicTCPServer.cs
index bad57f4..c74219d 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/DynamicTCPServer.cs
+++ b/src/Comm/DynamicTCPServer.cs
@@ -1,684 +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
- }
+/*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/src/Comm/EventArgs.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs
rename to src/Comm/EventArgs.cs
index 95dfa99..cf76d6b 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/EventArgs.cs
+++ b/src/Comm/EventArgs.cs
@@ -1,251 +1,251 @@
-/*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;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronSockets;
-
-
-namespace PepperDash.Core
-{
- ///
- /// Delegate for notifying of socket status changes
- ///
- ///
- public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
-
- ///
- /// EventArgs class for socket status changes
- ///
- public class GenericSocketStatusChageEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ISocketStatus Client { get; private set; }
-
- ///
- ///
- ///
- ///
- public GenericSocketStatusChageEventArgs(ISocketStatus client)
- {
- Client = client;
- }
- ///
- /// S+ Constructor
- ///
- public GenericSocketStatusChageEventArgs() { }
- }
-
- ///
- /// Delegate for notifying of TCP Server state changes
- ///
- ///
- public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
-
- ///
- /// EventArgs class for TCP Server state changes
- ///
- public class GenericTcpServerStateChangedEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ServerState State { get; private set; }
-
- ///
- ///
- ///
- ///
- public GenericTcpServerStateChangedEventArgs(ServerState state)
- {
- State = state;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerStateChangedEventArgs() { }
- }
-
- ///
- /// Delegate for TCP Server socket status changes
- ///
- ///
- ///
- ///
- public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
- ///
- /// EventArgs for TCP server socket status changes
- ///
- public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
- {
- ///
- ///
- ///
- public object Socket { get; private set; }
- ///
- ///
- ///
- public uint ReceivedFromClientIndex { get; private set; }
- ///
- ///
- ///
- public SocketStatus ClientStatus { get; set; }
-
- ///
- ///
- ///
- ///
- ///
- public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
- {
- Socket = socket;
- ClientStatus = clientStatus;
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)
- {
- Socket = socket;
- ReceivedFromClientIndex = clientIndex;
- ClientStatus = clientStatus;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerSocketStatusChangeEventArgs() { }
- }
-
- ///
- /// EventArgs for TCP server com method receive text
- ///
- public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
- {
- ///
- ///
- ///
- public uint ReceivedFromClientIndex { get; private set; }
-
- ///
- ///
- ///
- public ushort ReceivedFromClientIndexShort
- {
- get
- {
- return (ushort)ReceivedFromClientIndex;
- }
- }
-
- ///
- ///
- ///
- public string Text { get; private set; }
-
- ///
- ///
- ///
- ///
- public GenericTcpServerCommMethodReceiveTextArgs(string text)
- {
- Text = text;
- }
-
- ///
- ///
- ///
- ///
- ///
- public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)
- {
- Text = text;
- ReceivedFromClientIndex = clientIndex;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerCommMethodReceiveTextArgs() { }
- }
-
- ///
- /// EventArgs for TCP server client ready for communication
- ///
- public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
- {
- ///
- ///
- ///
- public bool IsReady;
-
- ///
- ///
- ///
- ///
- public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)
- {
- IsReady = isReady;
- }
- ///
- /// S+ Constructor
- ///
- public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
- }
-
- ///
- /// EventArgs for UDP connected
- ///
- public class GenericUdpConnectedEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ushort UConnected;
- ///
- ///
- ///
- public bool Connected;
-
- ///
- /// Constructor
- ///
- public GenericUdpConnectedEventArgs() { }
-
- ///
- ///
- ///
- ///
- public GenericUdpConnectedEventArgs(ushort uconnected)
- {
- UConnected = uconnected;
- }
-
- ///
- ///
- ///
- ///
- public GenericUdpConnectedEventArgs(bool connected)
- {
- Connected = connected;
- }
-
- }
-
-
-
+/*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;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+
+
+namespace PepperDash.Core
+{
+ ///
+ /// Delegate for notifying of socket status changes
+ ///
+ ///
+ public delegate void GenericSocketStatusChangeEventDelegate(ISocketStatus client);
+
+ ///
+ /// EventArgs class for socket status changes
+ ///
+ public class GenericSocketStatusChageEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ISocketStatus Client { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericSocketStatusChageEventArgs(ISocketStatus client)
+ {
+ Client = client;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericSocketStatusChageEventArgs() { }
+ }
+
+ ///
+ /// Delegate for notifying of TCP Server state changes
+ ///
+ ///
+ public delegate void GenericTcpServerStateChangedEventDelegate(ServerState state);
+
+ ///
+ /// EventArgs class for TCP Server state changes
+ ///
+ public class GenericTcpServerStateChangedEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ServerState State { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerStateChangedEventArgs(ServerState state)
+ {
+ State = state;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerStateChangedEventArgs() { }
+ }
+
+ ///
+ /// Delegate for TCP Server socket status changes
+ ///
+ ///
+ ///
+ ///
+ public delegate void GenericTcpServerSocketStatusChangeEventDelegate(object socket, uint clientIndex, SocketStatus clientStatus);
+ ///
+ /// EventArgs for TCP server socket status changes
+ ///
+ public class GenericTcpServerSocketStatusChangeEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public object Socket { get; private set; }
+ ///
+ ///
+ ///
+ public uint ReceivedFromClientIndex { get; private set; }
+ ///
+ ///
+ ///
+ public SocketStatus ClientStatus { get; set; }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs(object socket, SocketStatus clientStatus)
+ {
+ Socket = socket;
+ ClientStatus = clientStatus;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs(object socket, uint clientIndex, SocketStatus clientStatus)
+ {
+ Socket = socket;
+ ReceivedFromClientIndex = clientIndex;
+ ClientStatus = clientStatus;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerSocketStatusChangeEventArgs() { }
+ }
+
+ ///
+ /// EventArgs for TCP server com method receive text
+ ///
+ public class GenericTcpServerCommMethodReceiveTextArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public uint ReceivedFromClientIndex { get; private set; }
+
+ ///
+ ///
+ ///
+ public ushort ReceivedFromClientIndexShort
+ {
+ get
+ {
+ return (ushort)ReceivedFromClientIndex;
+ }
+ }
+
+ ///
+ ///
+ ///
+ public string Text { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs(string text)
+ {
+ Text = text;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs(string text, uint clientIndex)
+ {
+ Text = text;
+ ReceivedFromClientIndex = clientIndex;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerCommMethodReceiveTextArgs() { }
+ }
+
+ ///
+ /// EventArgs for TCP server client ready for communication
+ ///
+ public class GenericTcpServerClientReadyForcommunicationsEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public bool IsReady;
+
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpServerClientReadyForcommunicationsEventArgs(bool isReady)
+ {
+ IsReady = isReady;
+ }
+ ///
+ /// S+ Constructor
+ ///
+ public GenericTcpServerClientReadyForcommunicationsEventArgs() { }
+ }
+
+ ///
+ /// EventArgs for UDP connected
+ ///
+ public class GenericUdpConnectedEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ushort UConnected;
+ ///
+ ///
+ ///
+ public bool Connected;
+
+ ///
+ /// Constructor
+ ///
+ public GenericUdpConnectedEventArgs() { }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericUdpConnectedEventArgs(ushort uconnected)
+ {
+ UConnected = uconnected;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericUdpConnectedEventArgs(bool connected)
+ {
+ Connected = connected;
+ }
+
+ }
+
+
+
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/FINISH CommStatic.cs b/src/Comm/FINISH CommStatic.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/FINISH CommStatic.cs
rename to src/Comm/FINISH CommStatic.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericHttpSseClient.cs b/src/Comm/GenericHttpSseClient.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericHttpSseClient.cs
rename to src/Comm/GenericHttpSseClient.cs
index 3b941c9..6992ef7 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericHttpSseClient.cs
+++ b/src/Comm/GenericHttpSseClient.cs
@@ -1,314 +1,314 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Crestron.SimplSharp.Net.Http;
-
-namespace PepperDash.Core
-{
- ///
- /// Client for communicating with an HTTP Server Side Event pattern
- ///
- public class GenericHttpSseClient : ICommunicationReceiver
- {
- ///
- /// Notifies when bytes have been received
- ///
- public event EventHandler BytesReceived;
- ///
- /// Notifies when text has been received
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Indicates connection status
- ///
- public bool IsConnected
- {
- get;
- private set;
- }
-
- ///
- /// Unique identifier for the instance
- ///
- public string Key
- {
- get;
- private set;
- }
-
- ///
- /// Name for the instance
- ///
- public string Name
- {
- get;
- private set;
- }
-
- ///
- /// URL of the server
- ///
- public string Url { get; set; }
-
- HttpClient Client;
- HttpClientRequest Request;
-
- ///
- /// Constructor
- ///
- ///
- ///
- public GenericHttpSseClient(string key, string name)
- {
- Key = key;
- Name = name;
- }
-
- ///
- /// Connects to the server. Requires Url to be set first.
- ///
- public void Connect()
- {
- InitiateConnection(Url);
- }
-
- ///
- /// Disconnects from the server
- ///
- public void Disconnect()
- {
- CloseConnection(null);
- }
-
- ///
- /// Initiates connection to the server
- ///
- ///
- public void InitiateConnection(string url)
- {
- CrestronInvoke.BeginInvoke(o =>
- {
- try
- {
- if(string.IsNullOrEmpty(url))
- {
- Debug.Console(0, this, "Error connecting to Server. No URL specified");
- return;
- }
-
- Client = new HttpClient();
- Request = new HttpClientRequest();
- Client.Verbose = true;
- Client.KeepAlive = true;
- Request.Url.Parse(url);
- Request.RequestType = RequestType.Get;
- Request.Header.SetHeaderValue("Accept", "text/event-stream");
-
- // In order to get a handle on the response stream, we have to get
- // the request stream first. Boo
- Client.BeginGetRequestStream(GetRequestStreamCallback, Request, null);
- CrestronConsole.PrintLine("Request made!");
- }
- catch (Exception e)
- {
- ErrorLog.Notice("Exception occured in AsyncWebPostHttps(): " + e.ToString());
- }
- });
- }
-
- ///
- /// Closes the connection to the server
- ///
- ///
- public void CloseConnection(string s)
- {
- if (Client != null)
- {
- Client.Abort();
- IsConnected = false;
-
- Debug.Console(1, this, "Client Disconnected");
- }
- }
-
- private void GetRequestStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
- {
-
- try
- {
- // End the the async request operation and return the data stream
- Stream requestStream = request.ThisClient.EndGetRequestStream(request, null);
- // If this were something other than a GET we could write to the stream here
-
- // Closing makes the request happen
- requestStream.Close();
-
- // Get a handle on the response stream.
- request.ThisClient.BeginGetResponseStream(GetResponseStreamCallback, request, status);
- }
- catch (Exception e)
- {
- ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- ///
- private void GetResponseStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
- {
- try
- {
- // This closes up the GetResponseStream async
- var response = request.ThisClient.EndGetResponseStream(request);
-
- response.DataConnection.OnBytesReceived += new EventHandler(DataConnection_OnBytesReceived);
-
- IsConnected = true;
-
- Debug.Console(1, this, "Client Disconnected");
-
- Stream streamResponse = response.ContentStream;
- // Object containing various states to be passed back to async callback below
- RequestState asyncState = new RequestState();
- asyncState.Request = request;
- asyncState.Response = response;
- asyncState.StreamResponse = streamResponse;
- asyncState.HttpClient = request.ThisClient;
-
- // This processes the ongoing data stream
- Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult = null;
- do
- {
- asyncResult = streamResponse.BeginRead(asyncState.BufferRead, 0, RequestState.BUFFER_SIZE,
- new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), asyncState);
- }
- while (asyncResult.CompletedSynchronously && !asyncState.Done);
-
- //Console.WriteLine("\r\nExit Response Callback\r\n");
- }
- catch (Exception e)
- {
- ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
- }
- }
-
- void DataConnection_OnBytesReceived(object sender, EventArgs e)
- {
- Debug.Console(1, this, "DataConnection OnBytesReceived Fired");
- }
-
- private void ReadCallBack(Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult)
- {
- //we are getting back everything here, so cast the state from the call
- RequestState requestState = asyncResult.AsyncState as RequestState;
- Stream responseStream = requestState.StreamResponse;
-
- int read = responseStream.EndRead(asyncResult);
- // Read the HTML page and then print it to the console.
- if (read > 0)
- {
- var bytes = requestState.BufferRead;
-
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
-
- //requestState.RequestData.Append(Encoding.ASCII.GetString(requestState.BufferRead, 0, read));
- //CrestronConsole.PrintLine(requestState.RequestData.ToString());
-
- //clear the byte array buffer used.
- Array.Clear(requestState.BufferRead, 0, requestState.BufferRead.Length);
-
- if (asyncResult.CompletedSynchronously)
- {
- return;
- }
-
- Crestron.SimplSharp.CrestronIO.IAsyncResult asynchronousResult;
- do
- {
- asynchronousResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE,
- new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), requestState);
- }
- while (asynchronousResult.CompletedSynchronously && !requestState.Done);
- }
- else
- {
- requestState.Done = true;
- }
- }
- }
-
- ///
- /// Stores the state of the request
- ///
- public class RequestState
- {
- ///
- ///
- ///
- public const int BUFFER_SIZE = 10000;
- ///
- ///
- ///
- public byte[] BufferRead;
- ///
- ///
- ///
- public HttpClient HttpClient;
- ///
- ///
- ///
- public HttpClientRequest Request;
- ///
- ///
- ///
- public HttpClientResponse Response;
- ///
- ///
- ///
- public Stream StreamResponse;
- ///
- ///
- ///
- public bool Done;
-
- ///
- /// Constructor
- ///
- public RequestState()
- {
- BufferRead = new byte[BUFFER_SIZE];
- HttpClient = null;
- Request = null;
- Response = null;
- StreamResponse = null;
- Done = false;
- }
- }
-
- ///
- /// Waithandle for main thread.
- ///
- public class StreamAsyncTest
- {
- ///
- ///
- ///
- public CEvent wait_for_response = new CEvent(true, false);
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Crestron.SimplSharp.Net.Http;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Client for communicating with an HTTP Server Side Event pattern
+ ///
+ public class GenericHttpSseClient : ICommunicationReceiver
+ {
+ ///
+ /// Notifies when bytes have been received
+ ///
+ public event EventHandler BytesReceived;
+ ///
+ /// Notifies when text has been received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Indicates connection status
+ ///
+ public bool IsConnected
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Unique identifier for the instance
+ ///
+ public string Key
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// Name for the instance
+ ///
+ public string Name
+ {
+ get;
+ private set;
+ }
+
+ ///
+ /// URL of the server
+ ///
+ public string Url { get; set; }
+
+ HttpClient Client;
+ HttpClientRequest Request;
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public GenericHttpSseClient(string key, string name)
+ {
+ Key = key;
+ Name = name;
+ }
+
+ ///
+ /// Connects to the server. Requires Url to be set first.
+ ///
+ public void Connect()
+ {
+ InitiateConnection(Url);
+ }
+
+ ///
+ /// Disconnects from the server
+ ///
+ public void Disconnect()
+ {
+ CloseConnection(null);
+ }
+
+ ///
+ /// Initiates connection to the server
+ ///
+ ///
+ public void InitiateConnection(string url)
+ {
+ CrestronInvoke.BeginInvoke(o =>
+ {
+ try
+ {
+ if(string.IsNullOrEmpty(url))
+ {
+ Debug.Console(0, this, "Error connecting to Server. No URL specified");
+ return;
+ }
+
+ Client = new HttpClient();
+ Request = new HttpClientRequest();
+ Client.Verbose = true;
+ Client.KeepAlive = true;
+ Request.Url.Parse(url);
+ Request.RequestType = RequestType.Get;
+ Request.Header.SetHeaderValue("Accept", "text/event-stream");
+
+ // In order to get a handle on the response stream, we have to get
+ // the request stream first. Boo
+ Client.BeginGetRequestStream(GetRequestStreamCallback, Request, null);
+ CrestronConsole.PrintLine("Request made!");
+ }
+ catch (Exception e)
+ {
+ ErrorLog.Notice("Exception occured in AsyncWebPostHttps(): " + e.ToString());
+ }
+ });
+ }
+
+ ///
+ /// Closes the connection to the server
+ ///
+ ///
+ public void CloseConnection(string s)
+ {
+ if (Client != null)
+ {
+ Client.Abort();
+ IsConnected = false;
+
+ Debug.Console(1, this, "Client Disconnected");
+ }
+ }
+
+ private void GetRequestStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
+ {
+
+ try
+ {
+ // End the the async request operation and return the data stream
+ Stream requestStream = request.ThisClient.EndGetRequestStream(request, null);
+ // If this were something other than a GET we could write to the stream here
+
+ // Closing makes the request happen
+ requestStream.Close();
+
+ // Get a handle on the response stream.
+ request.ThisClient.BeginGetResponseStream(GetResponseStreamCallback, request, status);
+ }
+ catch (Exception e)
+ {
+ ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void GetResponseStreamCallback(HttpClientRequest request, HTTP_CALLBACK_ERROR error, object status)
+ {
+ try
+ {
+ // This closes up the GetResponseStream async
+ var response = request.ThisClient.EndGetResponseStream(request);
+
+ response.DataConnection.OnBytesReceived += new EventHandler(DataConnection_OnBytesReceived);
+
+ IsConnected = true;
+
+ Debug.Console(1, this, "Client Disconnected");
+
+ Stream streamResponse = response.ContentStream;
+ // Object containing various states to be passed back to async callback below
+ RequestState asyncState = new RequestState();
+ asyncState.Request = request;
+ asyncState.Response = response;
+ asyncState.StreamResponse = streamResponse;
+ asyncState.HttpClient = request.ThisClient;
+
+ // This processes the ongoing data stream
+ Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult = null;
+ do
+ {
+ asyncResult = streamResponse.BeginRead(asyncState.BufferRead, 0, RequestState.BUFFER_SIZE,
+ new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), asyncState);
+ }
+ while (asyncResult.CompletedSynchronously && !asyncState.Done);
+
+ //Console.WriteLine("\r\nExit Response Callback\r\n");
+ }
+ catch (Exception e)
+ {
+ ErrorLog.Notice("Exception occured in GetSecureRequestStreamCallback(): " + e.ToString());
+ }
+ }
+
+ void DataConnection_OnBytesReceived(object sender, EventArgs e)
+ {
+ Debug.Console(1, this, "DataConnection OnBytesReceived Fired");
+ }
+
+ private void ReadCallBack(Crestron.SimplSharp.CrestronIO.IAsyncResult asyncResult)
+ {
+ //we are getting back everything here, so cast the state from the call
+ RequestState requestState = asyncResult.AsyncState as RequestState;
+ Stream responseStream = requestState.StreamResponse;
+
+ int read = responseStream.EndRead(asyncResult);
+ // Read the HTML page and then print it to the console.
+ if (read > 0)
+ {
+ var bytes = requestState.BufferRead;
+
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+
+ //requestState.RequestData.Append(Encoding.ASCII.GetString(requestState.BufferRead, 0, read));
+ //CrestronConsole.PrintLine(requestState.RequestData.ToString());
+
+ //clear the byte array buffer used.
+ Array.Clear(requestState.BufferRead, 0, requestState.BufferRead.Length);
+
+ if (asyncResult.CompletedSynchronously)
+ {
+ return;
+ }
+
+ Crestron.SimplSharp.CrestronIO.IAsyncResult asynchronousResult;
+ do
+ {
+ asynchronousResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE,
+ new Crestron.SimplSharp.CrestronIO.AsyncCallback(ReadCallBack), requestState);
+ }
+ while (asynchronousResult.CompletedSynchronously && !requestState.Done);
+ }
+ else
+ {
+ requestState.Done = true;
+ }
+ }
+ }
+
+ ///
+ /// Stores the state of the request
+ ///
+ public class RequestState
+ {
+ ///
+ ///
+ ///
+ public const int BUFFER_SIZE = 10000;
+ ///
+ ///
+ ///
+ public byte[] BufferRead;
+ ///
+ ///
+ ///
+ public HttpClient HttpClient;
+ ///
+ ///
+ ///
+ public HttpClientRequest Request;
+ ///
+ ///
+ ///
+ public HttpClientResponse Response;
+ ///
+ ///
+ ///
+ public Stream StreamResponse;
+ ///
+ ///
+ ///
+ public bool Done;
+
+ ///
+ /// Constructor
+ ///
+ public RequestState()
+ {
+ BufferRead = new byte[BUFFER_SIZE];
+ HttpClient = null;
+ Request = null;
+ Response = null;
+ StreamResponse = null;
+ Done = false;
+ }
+ }
+
+ ///
+ /// Waithandle for main thread.
+ ///
+ public class StreamAsyncTest
+ {
+ ///
+ ///
+ ///
+ public CEvent wait_for_response = new CEvent(true, false);
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs b/src/Comm/GenericSecureTcpIpClient.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient.cs
rename to src/Comm/GenericSecureTcpIpClient.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient_ForServer.cs b/src/Comm/GenericSecureTcpIpClient_ForServer.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient_ForServer.cs
rename to src/Comm/GenericSecureTcpIpClient_ForServer.cs
index 143b3cc..f99c16d 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpClient_ForServer.cs
+++ b/src/Comm/GenericSecureTcpIpClient_ForServer.cs
@@ -1,908 +1,908 @@
-/*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
-{
- ///
- /// Generic secure TCP/IP client for server
- ///
- public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
- {
- ///
- /// Band aid delegate for choked server
- ///
- internal delegate void ConnectionHasHungCallbackDelegate();
-
- #region Events
-
- //public event EventHandler BytesReceived;
-
- ///
- /// Notifies of text received
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Notifies of auto reconnect sequence triggered
- ///
- public event EventHandler AutoReconnectTriggered;
-
- ///
- /// 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.
- ///
- public event EventHandler TextReceivedQueueInvoke;
-
- ///
- /// Notifies of socket status change
- ///
- public event EventHandler ConnectionChange;
-
-
- ///
- /// This is something of a band-aid callback. If the client times out during the connection process, because the server
- /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
- /// keep a watch on the server and reset it if necessary.
- ///
- internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
-
- ///
- /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
- /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
- ///
- public event EventHandler ClientReadyForCommunications;
-
- #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; }
-
- ///
- /// Semaphore on connect method
- ///
- bool IsTryingToConnect;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
- else
- return false;
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is ready for communication after shared key exchange
- ///
- public bool IsReadyForCommunication { get; set; }
-
- ///
- /// S+ helper for IsReadyForCommunication
- ///
- public ushort UIsReadyForCommunication
- {
- get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
- }
-
- ///
- /// Client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus;
- else
- return SocketStatus.SOCKET_STATUS_NO_CONNECT;
- }
- }
-
- ///
- /// 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;
-
- ///
- /// private Timer for auto reconnect
- ///
- CTimer RetryTimer;
-
-
- ///
- ///
- ///
- public bool HeartbeatEnabled { get; set; }
- ///
- ///
- ///
- public ushort UHeartbeatEnabled
- {
- get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
- set { HeartbeatEnabled = value == 1; }
- }
-
- ///
- ///
- ///
- public string HeartbeatString { get; set; }
- //public int HeartbeatInterval = 50000;
-
- ///
- /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
- ///
- public int HeartbeatInterval { get; set; }
-
- ///
- /// Simpl+ Heartbeat Analog value in seconds
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
-
- CTimer HeartbeatSendTimer;
- CTimer HeartbeatAckTimer;
- ///
- /// Used to force disconnection on a dead connect attempt
- ///
- CTimer ConnectFailTimer;
- CTimer WaitForSharedKey;
- private int ConnectionCount;
- ///
- /// Internal secure client
- ///
- SecureTCPClient Client;
-
- bool ProgramIsStopping;
-
- ///
- /// Queue lock
- ///
- CCriticalSection DequeueLock = new CCriticalSection();
-
- ///
- /// 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.
- ///
- public int ReceiveQueueSize { get; set; }
-
- ///
- /// 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.
- ///
- private CrestronQueue MessageQueue;
-
-
- #endregion
-
- #region Constructors
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- public GenericSecureTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
- AutoReconnectIntervalMs = 5000;
-
- }
-
- ///
- /// Constructor for S+
- ///
- public GenericSecureTcpIpClient_ForServer()
- : base("Uninitialized Secure Tcp Client For Server")
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object.
- ///
- ///
- ///
- public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Initialize(clientConfigObject);
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Just to help S+ set the key
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
- ///
- ///
- 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(ReceiveQueueSize);
- }
- else
- {
- ErrorLog.Error("Could not initialize client with key: {0}", Key);
- }
- }
- catch
- {
- ErrorLog.Error("Could not initialize client with key: {0}", Key);
- }
- }
-
- ///
- /// Handles closing this up when the program shuts down
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
- ProgramIsStopping = true;
- Disconnect();
- }
-
- }
-
- ///
- /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
- ///
- public void Connect()
- {
- ConnectionCount++;
- Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
-
-
- if (IsConnected)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
- return;
- }
- if (IsTryingToConnect)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
- return;
- }
- try
- {
- IsTryingToConnect = true;
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (string.IsNullOrEmpty(Hostname))
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
- return;
- }
-
- // clean up previous client
- if (Client != null)
- {
- Cleanup();
- }
- DisconnectCalledByUser = false;
-
- Client = new SecureTCPClient(Hostname, Port, BufferSize);
- Client.SocketStatusChange += Client_SocketStatusChange;
- if (HeartbeatEnabled)
- Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
- Client.AddressClientConnectedTo = Hostname;
- Client.PortNumber = Port;
- // SecureClient = c;
-
- //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
-
- ConnectFailTimer = new CTimer(o =>
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
- if (IsTryingToConnect)
- {
- IsTryingToConnect = false;
-
- //if (ConnectionHasHungCallback != null)
- //{
- // ConnectionHasHungCallback();
- //}
- //SecureClient.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- }
- }, 30000);
-
- Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
- Client.ConnectToServerAsync(o =>
- {
- Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
-
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- }
- IsTryingToConnect = false;
-
- if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
- o.ReceiveDataAsync(Receive);
-
- if (SharedKeyRequired)
- {
- WaitingForSharedKeyResponse = true;
- WaitForSharedKey = new CTimer(timer =>
- {
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
- // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
- // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
- o.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- //OnClientReadyForcommunications(false); // Should send false event
- }, 15000);
- }
- else
- {
- //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
- //required this is called by the shared key being negotiated
- if (IsReadyForCommunication == false)
- {
- OnClientReadyForcommunications(true); // Key not required
- }
- }
- }
- else
- {
- Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
- CheckClosedAndTryReconnect();
- }
- });
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
- IsTryingToConnect = false;
- CheckClosedAndTryReconnect();
- }
- }
-
- ///
- ///
- ///
- public void Disconnect()
- {
- Debug.Console(2, "Disconnect Called");
-
- DisconnectCalledByUser = true;
- if (IsConnected)
- {
- Client.DisconnectFromServer();
-
- }
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- Cleanup();
- }
-
- ///
- /// Internal call to close up client. ALWAYS use this when disconnecting.
- ///
- void Cleanup()
- {
- IsTryingToConnect = false;
-
- if (Client != null)
- {
- //SecureClient.DisconnectFromServer();
- Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
- Client.SocketStatusChange -= Client_SocketStatusChange;
- Client.Dispose();
- Client = null;
- }
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- ConnectFailTimer.Dispose();
- ConnectFailTimer = null;
- }
- }
-
-
- /// ff
- /// Called from Connect failure or Socket Status change if
- /// auto reconnect and socket disconnected (Not disconnected by user)
- ///
- void CheckClosedAndTryReconnect()
- {
- if (Client != null)
- {
- Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
- Cleanup();
- }
- if (!DisconnectCalledByUser && AutoReconnect)
- {
- var halfInterval = AutoReconnectIntervalMs / 2;
- var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
- Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if(AutoReconnectTriggered != null)
- AutoReconnectTriggered(this, new EventArgs());
- RetryTimer = new CTimer(o => Connect(), rndTime);
- }
- }
-
- ///
- /// Receive callback
- ///
- ///
- ///
- void Receive(SecureTCPClient client, int numBytes)
- {
- if (numBytes > 0)
- {
- string str = string.Empty;
- var handler = TextReceivedQueueInvoke;
- try
- {
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
- if (!string.IsNullOrEmpty(checkHeartbeat(str)))
- {
-
- if (SharedKeyRequired && str == "SharedKey:")
- {
- Debug.Console(2, this, "Server asking for shared key, sending");
- SendText(SharedKey + "\n");
- }
- else if (SharedKeyRequired && str == "Shared Key Match")
- {
- StopWaitForSharedKeyTimer();
-
-
- Debug.Console(2, this, "Shared key confirmed. Ready for communication");
- OnClientReadyForcommunications(true); // Successful key exchange
- }
- else
- {
- //var bytesHandler = BytesReceived;
- //if (bytesHandler != null)
- // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
- if (handler != null)
- {
- MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
- }
- }
- }
- }
- catch (Exception ex)
- {
- 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();
- }
- }
-
- ///
- /// 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 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();
- }
- }
-
- void HeartbeatStart()
- {
- if (HeartbeatEnabled)
- {
- Debug.Console(2, this, "Starting Heartbeat");
- if (HeartbeatSendTimer == null)
- {
-
- HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
- }
- if (HeartbeatAckTimer == null)
- {
- HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
- }
- }
-
- }
- void HeartbeatStop()
- {
-
- if (HeartbeatSendTimer != null)
- {
- Debug.Console(2, this, "Stoping Heartbeat Send");
- HeartbeatSendTimer.Stop();
- HeartbeatSendTimer = null;
- }
- if (HeartbeatAckTimer != null)
- {
- Debug.Console(2, this, "Stoping Heartbeat Ack");
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer = null;
- }
-
- }
- void SendHeartbeat(object notused)
- {
- this.SendText(HeartbeatString);
- Debug.Console(2, this, "Sending Heartbeat");
-
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(string received)
- {
- try
- {
- if (HeartbeatEnabled)
- {
- if (!string.IsNullOrEmpty(HeartbeatString))
- {
- var remainingText = received.Replace(HeartbeatString, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatString))
- {
- if (HeartbeatAckTimer != null)
- {
- HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
- }
- else
- {
- HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
- }
- Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
- return remainingText;
- }
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
-
-
- void HeartbeatAckTimerFail(object o)
- {
- try
- {
-
- if (IsConnected)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
- SendText("Heartbeat not received by server, closing connection");
- CheckClosedAndTryReconnect();
- }
-
- }
- catch (Exception ex)
- {
- ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
- }
- }
-
- ///
- ///
- ///
- void StopWaitForSharedKeyTimer()
- {
- if (WaitForSharedKey != null)
- {
- WaitForSharedKey.Stop();
- WaitForSharedKey = null;
- }
- }
-
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- if (!string.IsNullOrEmpty(text))
- {
- try
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
- {
- // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
- if (n <= 0)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
- }
- });
- }
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
- }
- }
- }
-
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes.Length > 0)
- {
- try
- {
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- Client.SendData(bytes, bytes.Length);
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
- }
- }
- }
-
- ///
- /// SocketStatusChange Callback
- ///
- ///
- ///
- void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
- {
- if (ProgramIsStopping)
- {
- ProgramIsStopping = false;
- return;
- }
- try
- {
- Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
-
- OnConnectionChange();
- // The client could be null or disposed by this time...
- if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- HeartbeatStop();
- OnClientReadyForcommunications(false); // socket has gone low
- CheckClosedAndTryReconnect();
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
- }
- }
-
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
- }
-
- ///
- /// Helper to fire ClientReadyForCommunications event
- ///
- void OnClientReadyForcommunications(bool isReady)
- {
- IsReadyForCommunication = isReady;
- if (this.IsReadyForCommunication) { HeartbeatStart(); }
- var handler = ClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
- }
- #endregion
- }
-
+/*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
+{
+ ///
+ /// Generic secure TCP/IP client for server
+ ///
+ public class GenericSecureTcpIpClient_ForServer : Device, IAutoReconnect
+ {
+ ///
+ /// Band aid delegate for choked server
+ ///
+ internal delegate void ConnectionHasHungCallbackDelegate();
+
+ #region Events
+
+ //public event EventHandler BytesReceived;
+
+ ///
+ /// Notifies of text received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Notifies of auto reconnect sequence triggered
+ ///
+ public event EventHandler AutoReconnectTriggered;
+
+ ///
+ /// 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.
+ ///
+ public event EventHandler TextReceivedQueueInvoke;
+
+ ///
+ /// Notifies of socket status change
+ ///
+ public event EventHandler ConnectionChange;
+
+
+ ///
+ /// This is something of a band-aid callback. If the client times out during the connection process, because the server
+ /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
+ /// keep a watch on the server and reset it if necessary.
+ ///
+ internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
+
+ ///
+ /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
+ /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
+ ///
+ public event EventHandler ClientReadyForCommunications;
+
+ #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; }
+
+ ///
+ /// Semaphore on connect method
+ ///
+ bool IsTryingToConnect;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
+ else
+ return false;
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is ready for communication after shared key exchange
+ ///
+ public bool IsReadyForCommunication { get; set; }
+
+ ///
+ /// S+ helper for IsReadyForCommunication
+ ///
+ public ushort UIsReadyForCommunication
+ {
+ get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
+ }
+
+ ///
+ /// Client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus;
+ else
+ return SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ }
+ }
+
+ ///
+ /// 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;
+
+ ///
+ /// private Timer for auto reconnect
+ ///
+ CTimer RetryTimer;
+
+
+ ///
+ ///
+ ///
+ public bool HeartbeatEnabled { get; set; }
+ ///
+ ///
+ ///
+ public ushort UHeartbeatEnabled
+ {
+ get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
+ set { HeartbeatEnabled = value == 1; }
+ }
+
+ ///
+ ///
+ ///
+ public string HeartbeatString { get; set; }
+ //public int HeartbeatInterval = 50000;
+
+ ///
+ /// Milliseconds before server expects another heartbeat. Set by property HeartbeatRequiredIntervalInSeconds which is driven from S+
+ ///
+ public int HeartbeatInterval { get; set; }
+
+ ///
+ /// Simpl+ Heartbeat Analog value in seconds
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { set { HeartbeatInterval = (value * 1000); } }
+
+ CTimer HeartbeatSendTimer;
+ CTimer HeartbeatAckTimer;
+ ///
+ /// Used to force disconnection on a dead connect attempt
+ ///
+ CTimer ConnectFailTimer;
+ CTimer WaitForSharedKey;
+ private int ConnectionCount;
+ ///
+ /// Internal secure client
+ ///
+ SecureTCPClient Client;
+
+ bool ProgramIsStopping;
+
+ ///
+ /// Queue lock
+ ///
+ CCriticalSection DequeueLock = new CCriticalSection();
+
+ ///
+ /// 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.
+ ///
+ public int ReceiveQueueSize { get; set; }
+
+ ///
+ /// 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.
+ ///
+ private CrestronQueue MessageQueue;
+
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+
+ }
+
+ ///
+ /// Constructor for S+
+ ///
+ public GenericSecureTcpIpClient_ForServer()
+ : base("Uninitialized Secure Tcp Client For Server")
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object.
+ ///
+ ///
+ ///
+ public GenericSecureTcpIpClient_ForServer(string key, TcpClientConfigObject clientConfigObject)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Initialize(clientConfigObject);
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Just to help S+ set the key
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialize called by the constructor that accepts a client config object. Can be called later to reset properties of client.
+ ///
+ ///
+ 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(ReceiveQueueSize);
+ }
+ else
+ {
+ ErrorLog.Error("Could not initialize client with key: {0}", Key);
+ }
+ }
+ catch
+ {
+ ErrorLog.Error("Could not initialize client with key: {0}", Key);
+ }
+ }
+
+ ///
+ /// Handles closing this up when the program shuts down
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping || programEventType == eProgramStatusEventType.Paused)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
+ ProgramIsStopping = true;
+ Disconnect();
+ }
+
+ }
+
+ ///
+ /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
+ ///
+ public void Connect()
+ {
+ ConnectionCount++;
+ Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
+
+
+ if (IsConnected)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
+ return;
+ }
+ if (IsTryingToConnect)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
+ return;
+ }
+ try
+ {
+ IsTryingToConnect = true;
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
+ return;
+ }
+
+ // clean up previous client
+ if (Client != null)
+ {
+ Cleanup();
+ }
+ DisconnectCalledByUser = false;
+
+ Client = new SecureTCPClient(Hostname, Port, BufferSize);
+ Client.SocketStatusChange += Client_SocketStatusChange;
+ if (HeartbeatEnabled)
+ Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
+ Client.AddressClientConnectedTo = Hostname;
+ Client.PortNumber = Port;
+ // SecureClient = c;
+
+ //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
+
+ ConnectFailTimer = new CTimer(o =>
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
+ if (IsTryingToConnect)
+ {
+ IsTryingToConnect = false;
+
+ //if (ConnectionHasHungCallback != null)
+ //{
+ // ConnectionHasHungCallback();
+ //}
+ //SecureClient.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ }
+ }, 30000);
+
+ Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
+ Client.ConnectToServerAsync(o =>
+ {
+ Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ }
+ IsTryingToConnect = false;
+
+ if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
+ o.ReceiveDataAsync(Receive);
+
+ if (SharedKeyRequired)
+ {
+ WaitingForSharedKeyResponse = true;
+ WaitForSharedKey = new CTimer(timer =>
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
+ // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
+ // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
+ o.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ //OnClientReadyForcommunications(false); // Should send false event
+ }, 15000);
+ }
+ else
+ {
+ //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
+ //required this is called by the shared key being negotiated
+ if (IsReadyForCommunication == false)
+ {
+ OnClientReadyForcommunications(true); // Key not required
+ }
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
+ CheckClosedAndTryReconnect();
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
+ IsTryingToConnect = false;
+ CheckClosedAndTryReconnect();
+ }
+ }
+
+ ///
+ ///
+ ///
+ public void Disconnect()
+ {
+ Debug.Console(2, "Disconnect Called");
+
+ DisconnectCalledByUser = true;
+ if (IsConnected)
+ {
+ Client.DisconnectFromServer();
+
+ }
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ Cleanup();
+ }
+
+ ///
+ /// Internal call to close up client. ALWAYS use this when disconnecting.
+ ///
+ void Cleanup()
+ {
+ IsTryingToConnect = false;
+
+ if (Client != null)
+ {
+ //SecureClient.DisconnectFromServer();
+ Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
+ Client.SocketStatusChange -= Client_SocketStatusChange;
+ Client.Dispose();
+ Client = null;
+ }
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ ConnectFailTimer.Dispose();
+ ConnectFailTimer = null;
+ }
+ }
+
+
+ /// ff
+ /// Called from Connect failure or Socket Status change if
+ /// auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void CheckClosedAndTryReconnect()
+ {
+ if (Client != null)
+ {
+ Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
+ Cleanup();
+ }
+ if (!DisconnectCalledByUser && AutoReconnect)
+ {
+ var halfInterval = AutoReconnectIntervalMs / 2;
+ var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
+ Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if(AutoReconnectTriggered != null)
+ AutoReconnectTriggered(this, new EventArgs());
+ RetryTimer = new CTimer(o => Connect(), rndTime);
+ }
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void Receive(SecureTCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ string str = string.Empty;
+ var handler = TextReceivedQueueInvoke;
+ try
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
+ if (!string.IsNullOrEmpty(checkHeartbeat(str)))
+ {
+
+ if (SharedKeyRequired && str == "SharedKey:")
+ {
+ Debug.Console(2, this, "Server asking for shared key, sending");
+ SendText(SharedKey + "\n");
+ }
+ else if (SharedKeyRequired && str == "Shared Key Match")
+ {
+ StopWaitForSharedKeyTimer();
+
+
+ Debug.Console(2, this, "Shared key confirmed. Ready for communication");
+ OnClientReadyForcommunications(true); // Successful key exchange
+ }
+ else
+ {
+ //var bytesHandler = BytesReceived;
+ //if (bytesHandler != null)
+ // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
+ if (handler != null)
+ {
+ MessageQueue.TryToEnqueue(new GenericTcpServerCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ 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();
+ }
+ }
+
+ ///
+ /// 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 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();
+ }
+ }
+
+ void HeartbeatStart()
+ {
+ if (HeartbeatEnabled)
+ {
+ Debug.Console(2, this, "Starting Heartbeat");
+ if (HeartbeatSendTimer == null)
+ {
+
+ HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
+ }
+ if (HeartbeatAckTimer == null)
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ }
+
+ }
+ void HeartbeatStop()
+ {
+
+ if (HeartbeatSendTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Send");
+ HeartbeatSendTimer.Stop();
+ HeartbeatSendTimer = null;
+ }
+ if (HeartbeatAckTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Ack");
+ HeartbeatAckTimer.Stop();
+ HeartbeatAckTimer = null;
+ }
+
+ }
+ void SendHeartbeat(object notused)
+ {
+ this.SendText(HeartbeatString);
+ Debug.Console(2, this, "Sending Heartbeat");
+
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(string received)
+ {
+ try
+ {
+ if (HeartbeatEnabled)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatString))
+ {
+ var remainingText = received.Replace(HeartbeatString, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatString))
+ {
+ if (HeartbeatAckTimer != null)
+ {
+ HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
+ }
+ else
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
+ return remainingText;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+
+
+ void HeartbeatAckTimerFail(object o)
+ {
+ try
+ {
+
+ if (IsConnected)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
+ SendText("Heartbeat not received by server, closing connection");
+ CheckClosedAndTryReconnect();
+ }
+
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
+ }
+ }
+
+ ///
+ ///
+ ///
+ void StopWaitForSharedKeyTimer()
+ {
+ if (WaitForSharedKey != null)
+ {
+ WaitForSharedKey.Stop();
+ WaitForSharedKey = null;
+ }
+ }
+
+ ///
+ /// General send method
+ ///
+ public void SendText(string text)
+ {
+ if (!string.IsNullOrEmpty(text))
+ {
+ try
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ {
+ // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
+ if (n <= 0)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
+ }
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ try
+ {
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ Client.SendData(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(SecureTCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (ProgramIsStopping)
+ {
+ ProgramIsStopping = false;
+ return;
+ }
+ try
+ {
+ Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
+
+ OnConnectionChange();
+ // The client could be null or disposed by this time...
+ if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ HeartbeatStop();
+ OnClientReadyForcommunications(false); // socket has gone low
+ CheckClosedAndTryReconnect();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ }
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
+ }
+
+ ///
+ /// Helper to fire ClientReadyForCommunications event
+ ///
+ void OnClientReadyForcommunications(bool isReady)
+ {
+ IsReadyForCommunication = isReady;
+ if (this.IsReadyForCommunication) { HeartbeatStart(); }
+ var handler = ClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ }
+ #endregion
+ }
+
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs b/src/Comm/GenericSecureTcpIpServer.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs
rename to src/Comm/GenericSecureTcpIpServer.cs
index d53a801..1fb9d05 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericSecureTcpIpServer.cs
+++ b/src/Comm/GenericSecureTcpIpServer.cs
@@ -1,1085 +1,1085 @@
-/*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 Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core
-{
- ///
- /// Generic secure TCP/IP server
- ///
- public class GenericSecureTcpIpServer : Device
- {
- #region Events
- ///
- /// Event for Receiving text
- ///
- public event EventHandler TextReceived;
-
- ///
- /// 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.
- ///
- public event EventHandler TextReceivedQueueInvoke;
-
- ///
- /// Event for client connection socket status change
- ///
- public event EventHandler ClientConnectionChange;
-
- ///
- /// Event for Server State Change
- ///
- public event EventHandler ServerStateChange;
-
- ///
- /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
- /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
- ///
- public event EventHandler ServerClientReadyForCommunications;
-
- ///
- /// A band aid event to notify user that the server has choked.
- ///
- public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
-
- ///
- ///
- ///
- public delegate void ServerHasChokedCallbackDelegate();
-
- #endregion
-
- #region Properties/Variables
-
- ///
- /// Server listen lock
- ///
- CCriticalSection ServerCCSection = new CCriticalSection();
-
- ///
- /// Queue lock
- ///
- CCriticalSection DequeueLock = new CCriticalSection();
-
- ///
- /// 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.
- ///
- public int ReceiveQueueSize { get; set; }
-
- ///
- /// 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.
- ///
- private CrestronQueue MessageQueue;
-
- ///
- /// A bandaid client that monitors whether the server is reachable
- ///
- GenericSecureTcpIpClient_ForServer MonitorClient;
-
- ///
- /// Timer to operate the bandaid monitor client in a loop.
- ///
- CTimer MonitorClientTimer;
-
- ///
- ///
- ///
- int MonitorClientFailureCount;
-
- ///
- /// 3 by default
- ///
- public int MonitorClientMaxFailureCount { get; set; }
-
- ///
- /// Text representation of the Socket Status enum values for the server
- ///
- public string Status
- {
- get
- {
- if (SecureServer != null)
- return SecureServer.State.ToString();
- return ServerState.SERVER_NOT_LISTENING.ToString();
-
- }
-
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (SecureServer != null)
- return (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
- return false;
-
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
- // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == 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
- {
- if (SecureServer != null)
- return (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
- else
- return false;
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
- // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsListening
- {
- get { return (ushort)(IsListening ? 1 : 0); }
- }
- ///
- /// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65
- ///
- 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 (SecureServer != null)
- return (ushort)SecureServer.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();
-
- List ClientReadyAfterKeyExchange = new List();
-
- ///
- /// The connected client indexes
- ///
- public 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;
-
- ///
- ///
- ///
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
- ///
- /// 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.
- ///
- public GenericSecureTcpIpServer()
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- }
-
- ///
- /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
- ///
- ///
- public GenericSecureTcpIpServer(string key)
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Key = key;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size.
- ///
- ///
- public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
- : base("Uninitialized Secure TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Initialize(serverConfigObject);
- }
- #endregion
-
- #region Methods - Server Actions
- ///
- /// Disconnects all clients and stops the server
- ///
- public void KillServer()
- {
- ServerStopped = true;
- if (MonitorClient != null)
- {
- MonitorClient.Disconnect();
- }
- DisconnectAllClientsForShutdown();
- StopListening();
- }
-
- ///
- /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
- ///
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialze the server
- ///
- ///
- public void Initialize(TcpServerConfigObject serverConfigObject)
- {
- try
- {
- if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
- {
- Key = serverConfigObject.Key;
- MaxClients = serverConfigObject.MaxClients;
- Port = serverConfigObject.Port;
- SharedKeyRequired = serverConfigObject.SharedKeyRequired;
- SharedKey = serverConfigObject.SharedKey;
- HeartbeatRequired = serverConfigObject.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
- HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
- BufferSize = serverConfigObject.BufferSize;
- ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;
- MessageQueue = new CrestronQueue(ReceiveQueueSize);
- }
- else
- {
- ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
- catch
- {
- ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
-
- ///
- /// Start listening on the specified port
- ///
- public void Listen()
- {
- ServerCCSection.Enter();
- try
- {
- if (Port < 1 || Port > 65535)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
- ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
- ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
- return;
- }
-
-
- if (SecureServer == null)
- {
- SecureServer = new SecureTCPServer(Port, MaxClients);
- if (HeartbeatRequired)
- SecureServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
- SecureServer.HandshakeTimeout = 30;
- SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
- }
- else
- {
- SecureServer.PortNumber = Port;
- }
- ServerStopped = false;
-
- // Start the listner
- SocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
- if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error starting WaitForConnectionAsync {0}", status);
- }
- else
- {
- ServerStopped = false;
- }
- OnServerStateChange(SecureServer.State);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
- ServerCCSection.Leave();
-
- }
- catch (Exception ex)
- {
- ServerCCSection.Leave();
- ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
- }
- }
-
- ///
- /// Stop Listeneing
- ///
- public void StopListening()
- {
- try
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
- if (SecureServer != null)
- {
- SecureServer.Stop();
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
- OnServerStateChange(SecureServer.State);
- }
- ServerStopped = true;
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
- }
- }
-
- ///
- /// Disconnects Client
- ///
- ///
- public void DisconnectClient(uint client)
- {
- try
- {
- SecureServer.Disconnect(client);
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
- }
- }
- ///
- /// Disconnect All Clients
- ///
- public void DisconnectAllClientsForShutdown()
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
- if (SecureServer != null)
- {
- SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;
- foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
- {
- var i = index;
- if (!SecureServer.ClientConnected(index))
- continue;
- try
- {
- SecureServer.Disconnect(i);
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
- }
- }
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
- }
-
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
- ConnectedClientsIndexes.Clear();
-
- if (!ProgramIsStopping)
- {
- OnConnectionChange();
- OnServerStateChange(SecureServer.State); //State shows both listening and connected
- }
-
- // var o = new { };
- }
-
- ///
- /// Broadcast text from server to all connected clients
- ///
- ///
- public void BroadcastText(string text)
- {
- CCriticalSection CCBroadcast = new CCriticalSection();
- CCBroadcast.Enter();
- try
- {
- if (ConnectedClientsIndexes.Count > 0)
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- foreach (uint i in ConnectedClientsIndexes)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
- {
- SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
- if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- Debug.Console(2, error.ToString());
- }
- }
- }
- CCBroadcast.Leave();
- }
- catch (Exception ex)
- {
- CCBroadcast.Leave();
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
- }
- }
-
- ///
- /// Not sure this is useful in library, maybe Pro??
- ///
- ///
- ///
- public void SendTextToClient(string text, uint clientIndex)
- {
- try
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- if (SecureServer != null && SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- SecureServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
- }
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(uint clientIndex, string received)
- {
- try
- {
- if (HeartbeatRequired)
- {
- if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
- {
- var remainingText = received.Replace(HeartbeatStringToMatch, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatStringToMatch))
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
- else
- {
- CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
- HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
- }
- Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
- // Return Heartbeat
- SendTextToClient(HeartbeatStringToMatch, clientIndex);
- return remainingText;
- }
- }
- else
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
- else
- {
- CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
- HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
- }
- Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
- ///
- /// Get the IP Address for the client at the specifed index
- ///
- ///
- ///
- public string GetClientIPAddress(uint clientIndex)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- {
- var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
- return ipa;
-
- }
- else
- {
- return "";
- }
- }
-
- #endregion
-
- #region Methods - HeartbeatTimer Callback
-
- void HeartbeatTimer_CallbackFunction(object o)
- {
- uint clientIndex = 99999;
- string address = string.Empty;
- try
- {
- clientIndex = (uint)o;
- address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
- address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
-
- if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
-
- var discoResult = SecureServer.Disconnect(clientIndex);
- //Debug.Console(1, this, "{0}", discoResult);
-
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
- }
- }
- catch (Exception ex)
- {
- ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
- }
- }
-
- #endregion
-
- #region Methods - Socket Status Changed Callbacks
- ///
- /// Secure Server Socket Status Changed Callback
- ///
- ///
- ///
- ///
- void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
- {
- try
- {
-
-
- // Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
-
- if (ConnectedClientsIndexes.Contains(clientIndex))
- ConnectedClientsIndexes.Remove(clientIndex);
- if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
- }
- if (ClientReadyAfterKeyExchange.Contains(clientIndex))
- ClientReadyAfterKeyExchange.Remove(clientIndex);
- if (WaitingForSharedKey.Contains(clientIndex))
- WaitingForSharedKey.Remove(clientIndex);
- if (SecureServer.MaxNumberOfClientSupported > SecureServer.NumberOfClientsConnected)
- {
- Listen();
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
- }
- //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
-
- #region Methods Connected Callbacks
- ///
- /// Secure TCP Client Connected to Secure Server Callback
- ///
- ///
- ///
- void SecureConnectCallback(SecureTCPServer server, uint clientIndex)
- {
- try
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
- server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
- clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- if (clientIndex != 0)
- {
- if (server.ClientConnected(clientIndex))
- {
-
- if (!ConnectedClientsIndexes.Contains(clientIndex))
- {
- ConnectedClientsIndexes.Add(clientIndex);
- }
- if (SharedKeyRequired)
- {
- if (!WaitingForSharedKey.Contains(clientIndex))
- {
- WaitingForSharedKey.Add(clientIndex);
- }
- byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
- server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- }
- else
- {
- OnServerClientReadyForCommunications(clientIndex);
- }
- if (HeartbeatRequired)
- {
- if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
- }
- }
-
- server.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
- }
- }
- else
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
- }
-
- // Rearm the listner
- SocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
- if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Socket status connect callback status {0}", status);
- if (status == SocketErrorCodes.SOCKET_CONNECTION_IN_PROGRESS)
- {
- // There is an issue where on a failed negotiation we need to stop and start the server. This should still leave connected clients intact.
- server.Stop();
- Listen();
- }
- }
- }
-
- #endregion
-
- #region Methods - Send/Receive Callbacks
- ///
- /// Secure Received Data Async Callback
- ///
- ///
- ///
- ///
- void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
- {
- if (numberOfBytesReceived > 0)
- {
-
- string received = "Nothing";
- var handler = TextReceivedQueueInvoke;
- try
- {
- 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(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
- mySecureTCPServer.SendData(clientIndex, b, b.Length);
- mySecureTCPServer.Disconnect(clientIndex);
-
- return;
- }
-
- WaitingForSharedKey.Remove(clientIndex);
- byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
- mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
- OnServerClientReadyForCommunications(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));
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
- }
- if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
-
- //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
- if (handler != null)
- {
- var gotLock = DequeueLock.TryEnter();
- if (gotLock)
- CrestronInvoke.BeginInvoke((o) => DequeueEvent());
- }
- }
- else
- {
- mySecureTCPServer.Disconnect(clientIndex);
- }
- }
-
- ///
- /// 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 handler = TextReceivedQueueInvoke;
- if (handler != null)
- {
- handler(this, message);
- }
- }
- }
- catch (Exception e)
- {
- Debug.Console(2, "DequeueEvent error: {0}\r", e);
- }
- // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
- if (DequeueLock != null)
- {
- DequeueLock.Leave();
- }
- }
-
- #endregion
-
- #region Methods - EventHelpers/Callbacks
-
- //Private Helper method to call the Connection Change Event
- void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
- {
- if (clientIndex != 0) //0 is error not valid client change
- {
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(SecureServer, clientIndex, clientStatus));
- }
- }
- }
-
- //Private Helper method to call the Connection Change Event
- void OnConnectionChange()
- {
- if (ProgramIsStopping)
- {
- return;
- }
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
- }
- }
-
- //Private Helper Method to call the Text Received Event
- void onTextReceived(string text, uint clientIndex)
- {
- var handler = TextReceived;
- if (handler != null)
- handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
- }
-
- //Private Helper Method to call the Server State Change Event
- void OnServerStateChange(ServerState state)
- {
- if (ProgramIsStopping)
- {
- return;
- }
- var handler = ServerStateChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerStateChangedEventArgs(state));
- }
- }
-
- ///
- /// Private Event Handler method to handle the closing of connections when the program stops
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- ProgramIsStopping = true;
- // kill bandaid things
- if (MonitorClientTimer != null)
- MonitorClientTimer.Stop();
- if (MonitorClient != null)
- MonitorClient.Disconnect();
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
- KillServer();
- }
- }
-
- //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
- void OnServerClientReadyForCommunications(uint clientIndex)
- {
- ClientReadyAfterKeyExchange.Add(clientIndex);
- var handler = ServerClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
- this, clientIndex, SecureServer.GetServerSocketStatusForSpecificClient(clientIndex)));
- }
- #endregion
-
- #region Monitor Client
- ///
- /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
- ///
- void StartMonitorClient()
- {
- if (MonitorClientTimer != null)
- {
- return;
- }
- MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
- }
-
- ///
- ///
- ///
- void RunMonitorClient()
- {
- MonitorClient = new GenericSecureTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
- MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
- MonitorClient.SharedKey = this.SharedKey;
- MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
- //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
- MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
-
- MonitorClient.Connect();
- // From here MonitorCLient either connects or hangs, MonitorClient will call back
-
- }
-
- ///
- ///
- ///
- void StopMonitorClient()
- {
- if (MonitorClient == null)
- return;
-
- MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
- MonitorClient.Disconnect();
- MonitorClient = null;
- }
-
- ///
- /// On monitor connect, restart the operation
- ///
- void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
- {
- if (args.IsReady)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- MonitorClientFailureCount = 0;
- CrestronEnvironment.Sleep(2000);
- StopMonitorClient();
- StartMonitorClient();
- }
- }
-
- ///
- /// If the client hangs, add to counter and maybe fire the choke event
- ///
- void MonitorClientHasHungCallback()
- {
- MonitorClientFailureCount++;
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- StopMonitorClient();
- if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
- MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
- StartMonitorClient();
- }
- else
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error,
- "\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
- MonitorClientMaxFailureCount);
-
- var handler = ServerHasChoked;
- if (handler != null)
- handler();
- // Some external thing is in charge here. Expected reset of program
- }
- }
- #endregion
- }
+/*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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Generic secure TCP/IP server
+ ///
+ public class GenericSecureTcpIpServer : Device
+ {
+ #region Events
+ ///
+ /// Event for Receiving text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// 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.
+ ///
+ public event EventHandler TextReceivedQueueInvoke;
+
+ ///
+ /// Event for client connection socket status change
+ ///
+ public event EventHandler ClientConnectionChange;
+
+ ///
+ /// Event for Server State Change
+ ///
+ public event EventHandler ServerStateChange;
+
+ ///
+ /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
+ /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
+ ///
+ public event EventHandler ServerClientReadyForCommunications;
+
+ ///
+ /// A band aid event to notify user that the server has choked.
+ ///
+ public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
+
+ ///
+ ///
+ ///
+ public delegate void ServerHasChokedCallbackDelegate();
+
+ #endregion
+
+ #region Properties/Variables
+
+ ///
+ /// Server listen lock
+ ///
+ CCriticalSection ServerCCSection = new CCriticalSection();
+
+ ///
+ /// Queue lock
+ ///
+ CCriticalSection DequeueLock = new CCriticalSection();
+
+ ///
+ /// 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.
+ ///
+ public int ReceiveQueueSize { get; set; }
+
+ ///
+ /// 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.
+ ///
+ private CrestronQueue MessageQueue;
+
+ ///
+ /// A bandaid client that monitors whether the server is reachable
+ ///
+ GenericSecureTcpIpClient_ForServer MonitorClient;
+
+ ///
+ /// Timer to operate the bandaid monitor client in a loop.
+ ///
+ CTimer MonitorClientTimer;
+
+ ///
+ ///
+ ///
+ int MonitorClientFailureCount;
+
+ ///
+ /// 3 by default
+ ///
+ public int MonitorClientMaxFailureCount { get; set; }
+
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
+ {
+ get
+ {
+ if (SecureServer != null)
+ return SecureServer.State.ToString();
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+
+ }
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (SecureServer != null)
+ return (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
+ return false;
+
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
+ // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == 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
+ {
+ if (SecureServer != null)
+ return (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
+ else
+ return false;
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
+ // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+ ///
+ /// Max number of clients this server will allow for connection. Crestron max is 64. This number should be less than 65
+ ///
+ 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 (SecureServer != null)
+ return (ushort)SecureServer.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();
+
+ List ClientReadyAfterKeyExchange = new List();
+
+ ///
+ /// The connected client indexes
+ ///
+ public 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;
+
+ ///
+ ///
+ ///
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// 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.
+ ///
+ public GenericSecureTcpIpServer()
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ }
+
+ ///
+ /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
+ ///
+ ///
+ public GenericSecureTcpIpServer(string key)
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Key = key;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object. This does set Queue size.
+ ///
+ ///
+ public GenericSecureTcpIpServer(TcpServerConfigObject serverConfigObject)
+ : base("Uninitialized Secure TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Initialize(serverConfigObject);
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// Disconnects all clients and stops the server
+ ///
+ public void KillServer()
+ {
+ ServerStopped = true;
+ if (MonitorClient != null)
+ {
+ MonitorClient.Disconnect();
+ }
+ DisconnectAllClientsForShutdown();
+ StopListening();
+ }
+
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialze the server
+ ///
+ ///
+ public void Initialize(TcpServerConfigObject serverConfigObject)
+ {
+ try
+ {
+ if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
+ {
+ Key = serverConfigObject.Key;
+ MaxClients = serverConfigObject.MaxClients;
+ Port = serverConfigObject.Port;
+ SharedKeyRequired = serverConfigObject.SharedKeyRequired;
+ SharedKey = serverConfigObject.SharedKey;
+ HeartbeatRequired = serverConfigObject.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
+ HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
+ BufferSize = serverConfigObject.BufferSize;
+ ReceiveQueueSize = serverConfigObject.ReceiveQueueSize > 20 ? serverConfigObject.ReceiveQueueSize : 20;
+ MessageQueue = new CrestronQueue(ReceiveQueueSize);
+ }
+ else
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+ catch
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+
+ ///
+ /// Start listening on the specified port
+ ///
+ public void Listen()
+ {
+ ServerCCSection.Enter();
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
+
+
+ if (SecureServer == null)
+ {
+ SecureServer = new SecureTCPServer(Port, MaxClients);
+ if (HeartbeatRequired)
+ SecureServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
+ SecureServer.HandshakeTimeout = 30;
+ SecureServer.SocketStatusChange += new SecureTCPServerSocketStatusChangeEventHandler(SecureServer_SocketStatusChange);
+ }
+ else
+ {
+ SecureServer.PortNumber = Port;
+ }
+ ServerStopped = false;
+
+ // Start the listner
+ SocketErrorCodes status = SecureServer.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
+ if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Error starting WaitForConnectionAsync {0}", status);
+ }
+ else
+ {
+ ServerStopped = false;
+ }
+ OnServerStateChange(SecureServer.State);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Secure Server Status: {0}, Socket Status: {1}", SecureServer.State, SecureServer.ServerSocketStatus);
+ ServerCCSection.Leave();
+
+ }
+ catch (Exception ex)
+ {
+ ServerCCSection.Leave();
+ ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
+ }
+ }
+
+ ///
+ /// Stop Listeneing
+ ///
+ public void StopListening()
+ {
+ try
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
+ if (SecureServer != null)
+ {
+ SecureServer.Stop();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", SecureServer.State);
+ OnServerStateChange(SecureServer.State);
+ }
+ ServerStopped = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
+ }
+ }
+
+ ///
+ /// Disconnects Client
+ ///
+ ///
+ public void DisconnectClient(uint client)
+ {
+ try
+ {
+ SecureServer.Disconnect(client);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
+ }
+ }
+ ///
+ /// Disconnect All Clients
+ ///
+ public void DisconnectAllClientsForShutdown()
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
+ if (SecureServer != null)
+ {
+ SecureServer.SocketStatusChange -= SecureServer_SocketStatusChange;
+ foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ {
+ var i = index;
+ if (!SecureServer.ClientConnected(index))
+ continue;
+ try
+ {
+ SecureServer.Disconnect(i);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
+ }
+ }
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", SecureServer.ServerSocketStatus);
+ }
+
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
+ ConnectedClientsIndexes.Clear();
+
+ if (!ProgramIsStopping)
+ {
+ OnConnectionChange();
+ OnServerStateChange(SecureServer.State); //State shows both listening and connected
+ }
+
+ // var o = new { };
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ public void BroadcastText(string text)
+ {
+ CCriticalSection CCBroadcast = new CCriticalSection();
+ CCBroadcast.Enter();
+ try
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
+ {
+ SocketErrorCodes error = SecureServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
+ if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ Debug.Console(2, error.ToString());
+ }
+ }
+ }
+ CCBroadcast.Leave();
+ }
+ catch (Exception ex)
+ {
+ CCBroadcast.Leave();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ try
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ if (SecureServer != null && SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ SecureServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(uint clientIndex, string received)
+ {
+ try
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ var remainingText = received.Replace(HeartbeatStringToMatch, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatStringToMatch))
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
+ // Return Heartbeat
+ SendTextToClient(HeartbeatStringToMatch, clientIndex);
+ return remainingText;
+ }
+ }
+ else
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+ ///
+ /// Get the IP Address for the client at the specifed index
+ ///
+ ///
+ ///
+ public string GetClientIPAddress(uint clientIndex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ {
+ var ipa = this.SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
+ return ipa;
+
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ #endregion
+
+ #region Methods - HeartbeatTimer Callback
+
+ void HeartbeatTimer_CallbackFunction(object o)
+ {
+ uint clientIndex = 99999;
+ string address = string.Empty;
+ try
+ {
+ clientIndex = (uint)o;
+ address = SecureServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
+ address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
+
+ if (SecureServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+
+ var discoResult = SecureServer.Disconnect(clientIndex);
+ //Debug.Console(1, this, "{0}", discoResult);
+
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
+ }
+ }
+
+ #endregion
+
+ #region Methods - Socket Status Changed Callbacks
+ ///
+ /// Secure Server Socket Status Changed Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureServer_SocketStatusChange(SecureTCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ try
+ {
+
+
+ // Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.SecureServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.SecureServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange ConnectedCLients: {0} ServerState: {1} Port: {2}", SecureServer.NumberOfClientsConnected, SecureServer.State, SecureServer.PortNumber);
+
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (ClientReadyAfterKeyExchange.Contains(clientIndex))
+ ClientReadyAfterKeyExchange.Remove(clientIndex);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Remove(clientIndex);
+ if (SecureServer.MaxNumberOfClientSupported > SecureServer.NumberOfClientsConnected)
+ {
+ Listen();
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
+ }
+ //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
+
+ #region Methods Connected Callbacks
+ ///
+ /// Secure TCP Client Connected to Secure Server Callback
+ ///
+ ///
+ ///
+ void SecureConnectCallback(SecureTCPServer server, uint clientIndex)
+ {
+ try
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
+ clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ if (clientIndex != 0)
+ {
+ if (server.ClientConnected(clientIndex))
+ {
+
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ {
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ if (SharedKeyRequired)
+ {
+ if (!WaitingForSharedKey.Contains(clientIndex))
+ {
+ WaitingForSharedKey.Add(clientIndex);
+ }
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
+ server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ else
+ {
+ OnServerClientReadyForCommunications(clientIndex);
+ }
+ if (HeartbeatRequired)
+ {
+ if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
+ }
+ }
+
+ server.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
+ }
+
+ // Rearm the listner
+ SocketErrorCodes status = server.WaitForConnectionAsync(IPAddress.Any, SecureConnectCallback);
+ if (status != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Socket status connect callback status {0}", status);
+ if (status == SocketErrorCodes.SOCKET_CONNECTION_IN_PROGRESS)
+ {
+ // There is an issue where on a failed negotiation we need to stop and start the server. This should still leave connected clients intact.
+ server.Stop();
+ Listen();
+ }
+ }
+ }
+
+ #endregion
+
+ #region Methods - Send/Receive Callbacks
+ ///
+ /// Secure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void SecureReceivedDataAsyncCallback(SecureTCPServer mySecureTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+
+ string received = "Nothing";
+ var handler = TextReceivedQueueInvoke;
+ try
+ {
+ 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(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
+ mySecureTCPServer.SendData(clientIndex, b, b.Length);
+ mySecureTCPServer.Disconnect(clientIndex);
+
+ return;
+ }
+
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
+ mySecureTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
+ OnServerClientReadyForCommunications(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));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
+ }
+ if (mySecureTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ mySecureTCPServer.ReceiveDataAsync(clientIndex, SecureReceivedDataAsyncCallback);
+
+ //Check to see if there is a subscription to the TextReceivedQueueInvoke event. If there is start the dequeue thread.
+ if (handler != null)
+ {
+ var gotLock = DequeueLock.TryEnter();
+ if (gotLock)
+ CrestronInvoke.BeginInvoke((o) => DequeueEvent());
+ }
+ }
+ else
+ {
+ mySecureTCPServer.Disconnect(clientIndex);
+ }
+ }
+
+ ///
+ /// 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 handler = TextReceivedQueueInvoke;
+ if (handler != null)
+ {
+ handler(this, message);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.Console(2, "DequeueEvent error: {0}\r", e);
+ }
+ // Make sure to leave the CCritical section in case an exception above stops this thread, or we won't be able to restart it.
+ if (DequeueLock != null)
+ {
+ DequeueLock.Leave();
+ }
+ }
+
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
+ {
+ if (clientIndex != 0) //0 is error not valid client change
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(SecureServer, clientIndex, clientStatus));
+ }
+ }
+ }
+
+ //Private Helper method to call the Connection Change Event
+ void OnConnectionChange()
+ {
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
+ }
+ }
+
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text, uint clientIndex)
+ {
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
+ }
+
+ //Private Helper Method to call the Server State Change Event
+ void OnServerStateChange(ServerState state)
+ {
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ServerStateChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerStateChangedEventArgs(state));
+ }
+ }
+
+ ///
+ /// Private Event Handler method to handle the closing of connections when the program stops
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ ProgramIsStopping = true;
+ // kill bandaid things
+ if (MonitorClientTimer != null)
+ MonitorClientTimer.Stop();
+ if (MonitorClient != null)
+ MonitorClient.Disconnect();
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
+ KillServer();
+ }
+ }
+
+ //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
+ void OnServerClientReadyForCommunications(uint clientIndex)
+ {
+ ClientReadyAfterKeyExchange.Add(clientIndex);
+ var handler = ServerClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
+ this, clientIndex, SecureServer.GetServerSocketStatusForSpecificClient(clientIndex)));
+ }
+ #endregion
+
+ #region Monitor Client
+ ///
+ /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
+ ///
+ void StartMonitorClient()
+ {
+ if (MonitorClientTimer != null)
+ {
+ return;
+ }
+ MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
+ }
+
+ ///
+ ///
+ ///
+ void RunMonitorClient()
+ {
+ MonitorClient = new GenericSecureTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
+ MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
+ MonitorClient.SharedKey = this.SharedKey;
+ MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
+ //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
+ MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
+
+ MonitorClient.Connect();
+ // From here MonitorCLient either connects or hangs, MonitorClient will call back
+
+ }
+
+ ///
+ ///
+ ///
+ void StopMonitorClient()
+ {
+ if (MonitorClient == null)
+ return;
+
+ MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
+ MonitorClient.Disconnect();
+ MonitorClient = null;
+ }
+
+ ///
+ /// On monitor connect, restart the operation
+ ///
+ void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
+ {
+ if (args.IsReady)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
+ MonitorClientTimer.Stop();
+ MonitorClientTimer = null;
+ MonitorClientFailureCount = 0;
+ CrestronEnvironment.Sleep(2000);
+ StopMonitorClient();
+ StartMonitorClient();
+ }
+ }
+
+ ///
+ /// If the client hangs, add to counter and maybe fire the choke event
+ ///
+ void MonitorClientHasHungCallback()
+ {
+ MonitorClientFailureCount++;
+ MonitorClientTimer.Stop();
+ MonitorClientTimer = null;
+ StopMonitorClient();
+ if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
+ MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
+ StartMonitorClient();
+ }
+ else
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error,
+ "\r***************************\rMonitor client connection has hung a maximum of {0} times. \r***************************",
+ MonitorClientMaxFailureCount);
+
+ var handler = ServerHasChoked;
+ if (handler != null)
+ handler();
+ // Some external thing is in charge here. Expected reset of program
+ }
+ }
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs b/src/Comm/GenericSshClient.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs
rename to src/Comm/GenericSshClient.cs
index e13c284..0d5113e 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericSshClient.cs
+++ b/src/Comm/GenericSshClient.cs
@@ -1,610 +1,610 @@
-using System;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronSockets;
-using Crestron.SimplSharp.Ssh;
-using Crestron.SimplSharp.Ssh.Common;
-
-namespace PepperDash.Core
-{
- ///
- ///
- ///
- public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
- {
- private const string SPlusKey = "Uninitialized SshClient";
- ///
- /// Object to enable stream debugging
- ///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Event that fires when data is received. Delivers args with byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Event that fires when data is received. Delivered as text.
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Event when the connection status changes.
- ///
- public event EventHandler ConnectionChange;
-
- /////
- /////
- /////
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
-
- ///
- /// Address of server
- ///
- public string Hostname { get; set; }
-
- ///
- /// Port on server
- ///
- public int Port { get; set; }
-
- ///
- /// Username for server
- ///
- public string Username { get; set; }
-
- ///
- /// And... Password for server. That was worth documenting!
- ///
- public string Password { get; set; }
-
- ///
- /// True when the server is connected - when status == 2.
- ///
- public bool IsConnected
- {
- // returns false if no client or not connected
- get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- ///
- ///
- public SocketStatus ClientStatus
- {
- get { return _ClientStatus; }
- private set
- {
- if (_ClientStatus == value)
- return;
- _ClientStatus = value;
- OnConnectionChange();
- }
- }
- SocketStatus _ClientStatus;
-
- ///
- /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
- /// and IsConnected with be true when this == 2.
- ///
- public ushort UStatus
- {
- get { return (ushort)_ClientStatus; }
- }
-
- ///
- /// Determines whether client will attempt reconnection on failure. Default is true
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// Will be set and unset by connect and disconnect only
- ///
- public bool ConnectEnabled { get; private set; }
-
- ///
- /// S+ helper for AutoReconnect
- ///
- public ushort UAutoReconnect
- {
- get { return (ushort)(AutoReconnect ? 1 : 0); }
- set { AutoReconnect = value == 1; }
- }
-
- ///
- /// Millisecond value, determines the timeout period in between reconnect attempts.
- /// Set to 5000 by default
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- SshClient Client;
-
- ShellStream TheStream;
-
- CTimer ReconnectTimer;
-
- //Lock object to prevent simulatneous connect/disconnect operations
- private CCriticalSection connectLock = new CCriticalSection();
-
- private bool DisconnectLogged = false;
-
- ///
- /// Typical constructor.
- ///
- public GenericSshClient(string key, string hostname, int port, string username, string password) :
- base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Key = key;
- Hostname = hostname;
- Port = port;
- Username = username;
- Password = password;
- AutoReconnectIntervalMs = 5000;
-
- ReconnectTimer = new CTimer(o =>
- {
- if (ConnectEnabled)
- {
- Connect();
- }
- }, Timeout.Infinite);
- }
-
- ///
- /// S+ Constructor - Must set all properties before calling Connect
- ///
- public GenericSshClient()
- : base(SPlusKey)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
-
- ReconnectTimer = new CTimer(o =>
- {
- if (ConnectEnabled)
- {
- Connect();
- }
- }, Timeout.Infinite);
- }
-
- ///
- /// 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");
- Disconnect();
- }
- }
- }
-
- ///
- /// Connect to the server, using the provided properties.
- ///
- public void Connect()
- {
- // Don't go unless everything is here
- if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
- || Username == null || Password == null)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Connect failed. Check hostname, port, username and password are set or not null");
- return;
- }
-
- ConnectEnabled = true;
-
- try
- {
- connectLock.Enter();
- if (IsConnected)
- {
- Debug.Console(1, this, "Connection already connected. Exiting Connect()");
- }
- else
- {
- Debug.Console(1, this, "Attempting connect");
-
- // Cancel reconnect if running.
- if (ReconnectTimer != null)
- {
- ReconnectTimer.Stop();
- }
-
- // Cleanup the old client if it already exists
- if (Client != null)
- {
- Debug.Console(1, this, "Cleaning up disconnected client");
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
- }
-
- // This handles both password and keyboard-interactive (like on OS-X, 'nixes)
- KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Username);
- kauth.AuthenticationPrompt += new EventHandler(kauth_AuthenticationPrompt);
- PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
-
- Debug.Console(1, this, "Creating new SshClient");
- ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
- Client = new SshClient(connectionInfo);
- Client.ErrorOccurred += Client_ErrorOccurred;
-
- //Attempt to connect
- ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
- try
- {
- Client.Connect();
- TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534);
- TheStream.DataReceived += Stream_DataReceived;
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Connected");
- ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
- DisconnectLogged = false;
- }
- catch (SshConnectionException e)
- {
- var ie = e.InnerException; // The details are inside!!
- var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
-
- if (ie is SocketException)
- Debug.Console(1, this, errorLogLevel, "'{0}' CONNECTION failure: Cannot reach host, ({1})", Key, ie.Message);
- else if (ie is System.Net.Sockets.SocketException)
- Debug.Console(1, this, errorLogLevel, "'{0}' Connection failure: Cannot reach host '{1}' on port {2}, ({3})",
- Key, Hostname, Port, ie.GetType());
- else if (ie is SshAuthenticationException)
- {
- Debug.Console(1, this, errorLogLevel, "Authentication failure for username '{0}', ({1})",
- Username, ie.Message);
- }
- else
- Debug.Console(1, this, errorLogLevel, "Error on connect:\r({0})", ie.Message);
-
- DisconnectLogged = true;
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect)
- {
- Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
- }
- }
- catch (Exception e)
- {
- var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
- Debug.Console(1, this, errorLogLevel, "Unhandled exception on connect:\r({0})", e.Message);
- DisconnectLogged = true;
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- if (AutoReconnect)
- {
- Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
- }
- }
- }
- }
- finally
- {
- connectLock.Leave();
- }
- }
-
- ///
- /// Disconnect the clients and put away it's resources.
- ///
- public void Disconnect()
- {
- ConnectEnabled = false;
- // Stop trying reconnects, if we are
- if (ReconnectTimer != null)
- {
- ReconnectTimer.Stop();
- // ReconnectTimer = null;
- }
-
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
- }
-
- ///
- /// Kills the stream, cleans up the client and sets it to null
- ///
- private void KillClient(SocketStatus status)
- {
- KillStream();
-
- try
- {
- if (Client != null)
- {
- Client.ErrorOccurred -= Client_ErrorOccurred;
- Client.Disconnect();
- Client.Dispose();
- Client = null;
- ClientStatus = status;
- Debug.Console(1, this, "Disconnected");
- }
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Client:{0}", ex);
- }
- }
-
- ///
- /// Anything to do with reestablishing connection on failures
- ///
- void HandleConnectionFailure()
- {
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
-
- Debug.Console(1, this, "Client nulled due to connection failure. AutoReconnect: {0}, ConnectEnabled: {1}", AutoReconnect, ConnectEnabled);
- if (AutoReconnect && ConnectEnabled)
- {
- Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- if (ReconnectTimer == null)
- {
- ReconnectTimer = new CTimer(o =>
- {
- Connect();
- }, AutoReconnectIntervalMs);
- Debug.Console(1, this, "Attempting connection in {0} seconds",
- (float) (AutoReconnectIntervalMs/1000));
- }
- else
- {
- Debug.Console(1, this, "{0} second reconnect cycle running",
- (float) (AutoReconnectIntervalMs/1000));
- }
- }
- }
-
- ///
- /// Kills the stream
- ///
- void KillStream()
- {
- try
- {
- if (TheStream != null)
- {
- TheStream.DataReceived -= Stream_DataReceived;
- TheStream.Close();
- TheStream.Dispose();
- TheStream = null;
- Debug.Console(1, this, "Disconnected stream");
- }
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Stream:{0}", ex);
- }
- }
-
- ///
- /// Handles the keyboard interactive authentication, should it be required.
- ///
- void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
- {
- foreach (AuthenticationPrompt prompt in e.Prompts)
- if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
- prompt.Response = Password;
- }
-
- ///
- /// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
- ///
- void Stream_DataReceived(object sender, Crestron.SimplSharp.Ssh.Common.ShellDataEventArgs e)
- {
- var bytes = e.Data;
- if (bytes.Length > 0)
- {
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
-
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Received: '{0}'", ComTextHelper.GetDebugText(str));
-
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
- }
- }
-
-
- ///
- /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
- /// event
- ///
- void Client_ErrorOccurred(object sender, Crestron.SimplSharp.Ssh.Common.ExceptionEventArgs e)
- {
- CrestronInvoke.BeginInvoke(o =>
- {
- if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Disconnected by remote");
- else
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Unhandled SSH client error: {0}", e.Exception);
-
- try
- {
- connectLock.Enter();
- KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
- }
- finally
- {
- connectLock.Leave();
- }
- if (AutoReconnect && ConnectEnabled)
- {
- Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
- ReconnectTimer.Reset(AutoReconnectIntervalMs);
- }
- });
- }
-
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- if (ConnectionChange != null)
- ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
- }
-
- #region IBasicCommunication Members
-
- ///
- /// Sends text to the server
- ///
- ///
- public void SendText(string text)
- {
- try
- {
- if (Client != null && TheStream != null && IsConnected)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0,
- this,
- "Sending {0} characters of text: '{1}'",
- text.Length,
- ComTextHelper.GetDebugText(text));
-
- TheStream.Write(text);
- TheStream.Flush();
- }
- else
- {
- Debug.Console(1, this, "Client is null or disconnected. Cannot Send Text");
- }
- }
- catch (ObjectDisposedException ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
-
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Reset();
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
- }
- }
-
- ///
- /// Sends Bytes to the server
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- try
- {
- if (Client != null && TheStream != null && IsConnected)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
-
- TheStream.Write(bytes, 0, bytes.Length);
- TheStream.Flush();
- }
- else
- {
- Debug.Console(1, this, "Client is null or disconnected. Cannot Send Bytes");
- }
- }
- catch (ObjectDisposedException ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
-
- KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
- ReconnectTimer.Reset();
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
- }
- }
-
- #endregion
- }
-
- //*****************************************************************************************************
- //*****************************************************************************************************
- ///
- /// Fired when connection changes
- ///
- public class SshConnectionChangeEventArgs : EventArgs
- {
- ///
- /// Connection State
- ///
- public bool IsConnected { get; private set; }
-
- ///
- /// Connection Status represented as a ushort
- ///
- public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } }
-
- ///
- /// The client
- ///
- public GenericSshClient Client { get; private set; }
-
- ///
- /// Socket Status as represented by
- ///
- public ushort Status { get { return Client.UStatus; } }
-
- ///
- /// S+ Constructor
- ///
- public SshConnectionChangeEventArgs() { }
-
- ///
- /// EventArgs class
- ///
- /// Connection State
- /// The Client
- public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
- {
- IsConnected = isConnected;
- Client = client;
- }
- }
-}
+using System;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+using Crestron.SimplSharp.Ssh;
+using Crestron.SimplSharp.Ssh.Common;
+
+namespace PepperDash.Core
+{
+ ///
+ ///
+ ///
+ public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+ {
+ private const string SPlusKey = "Uninitialized SshClient";
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Event that fires when data is received. Delivers args with byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Event that fires when data is received. Delivered as text.
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Event when the connection status changes.
+ ///
+ public event EventHandler ConnectionChange;
+
+ /////
+ /////
+ /////
+ //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname { get; set; }
+
+ ///
+ /// Port on server
+ ///
+ public int Port { get; set; }
+
+ ///
+ /// Username for server
+ ///
+ public string Username { get; set; }
+
+ ///
+ /// And... Password for server. That was worth documenting!
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// True when the server is connected - when status == 2.
+ ///
+ public bool IsConnected
+ {
+ // returns false if no client or not connected
+ get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ ///
+ ///
+ public SocketStatus ClientStatus
+ {
+ get { return _ClientStatus; }
+ private set
+ {
+ if (_ClientStatus == value)
+ return;
+ _ClientStatus = value;
+ OnConnectionChange();
+ }
+ }
+ SocketStatus _ClientStatus;
+
+ ///
+ /// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
+ /// and IsConnected with be true when this == 2.
+ ///
+ public ushort UStatus
+ {
+ get { return (ushort)_ClientStatus; }
+ }
+
+ ///
+ /// Determines whether client will attempt reconnection on failure. Default is true
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// Will be set and unset by connect and disconnect only
+ ///
+ public bool ConnectEnabled { get; private set; }
+
+ ///
+ /// S+ helper for AutoReconnect
+ ///
+ public ushort UAutoReconnect
+ {
+ get { return (ushort)(AutoReconnect ? 1 : 0); }
+ set { AutoReconnect = value == 1; }
+ }
+
+ ///
+ /// Millisecond value, determines the timeout period in between reconnect attempts.
+ /// Set to 5000 by default
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ SshClient Client;
+
+ ShellStream TheStream;
+
+ CTimer ReconnectTimer;
+
+ //Lock object to prevent simulatneous connect/disconnect operations
+ private CCriticalSection connectLock = new CCriticalSection();
+
+ private bool DisconnectLogged = false;
+
+ ///
+ /// Typical constructor.
+ ///
+ public GenericSshClient(string key, string hostname, int port, string username, string password) :
+ base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Key = key;
+ Hostname = hostname;
+ Port = port;
+ Username = username;
+ Password = password;
+ AutoReconnectIntervalMs = 5000;
+
+ ReconnectTimer = new CTimer(o =>
+ {
+ if (ConnectEnabled)
+ {
+ Connect();
+ }
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// S+ Constructor - Must set all properties before calling Connect
+ ///
+ public GenericSshClient()
+ : base(SPlusKey)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+
+ ReconnectTimer = new CTimer(o =>
+ {
+ if (ConnectEnabled)
+ {
+ Connect();
+ }
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// 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");
+ Disconnect();
+ }
+ }
+ }
+
+ ///
+ /// Connect to the server, using the provided properties.
+ ///
+ public void Connect()
+ {
+ // Don't go unless everything is here
+ if (string.IsNullOrEmpty(Hostname) || Port < 1 || Port > 65535
+ || Username == null || Password == null)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Connect failed. Check hostname, port, username and password are set or not null");
+ return;
+ }
+
+ ConnectEnabled = true;
+
+ try
+ {
+ connectLock.Enter();
+ if (IsConnected)
+ {
+ Debug.Console(1, this, "Connection already connected. Exiting Connect()");
+ }
+ else
+ {
+ Debug.Console(1, this, "Attempting connect");
+
+ // Cancel reconnect if running.
+ if (ReconnectTimer != null)
+ {
+ ReconnectTimer.Stop();
+ }
+
+ // Cleanup the old client if it already exists
+ if (Client != null)
+ {
+ Debug.Console(1, this, "Cleaning up disconnected client");
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+ }
+
+ // This handles both password and keyboard-interactive (like on OS-X, 'nixes)
+ KeyboardInteractiveAuthenticationMethod kauth = new KeyboardInteractiveAuthenticationMethod(Username);
+ kauth.AuthenticationPrompt += new EventHandler(kauth_AuthenticationPrompt);
+ PasswordAuthenticationMethod pauth = new PasswordAuthenticationMethod(Username, Password);
+
+ Debug.Console(1, this, "Creating new SshClient");
+ ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
+ Client = new SshClient(connectionInfo);
+ Client.ErrorOccurred += Client_ErrorOccurred;
+
+ //Attempt to connect
+ ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
+ try
+ {
+ Client.Connect();
+ TheStream = Client.CreateShellStream("PDTShell", 100, 80, 100, 200, 65534);
+ TheStream.DataReceived += Stream_DataReceived;
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Connected");
+ ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
+ DisconnectLogged = false;
+ }
+ catch (SshConnectionException e)
+ {
+ var ie = e.InnerException; // The details are inside!!
+ var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
+
+ if (ie is SocketException)
+ Debug.Console(1, this, errorLogLevel, "'{0}' CONNECTION failure: Cannot reach host, ({1})", Key, ie.Message);
+ else if (ie is System.Net.Sockets.SocketException)
+ Debug.Console(1, this, errorLogLevel, "'{0}' Connection failure: Cannot reach host '{1}' on port {2}, ({3})",
+ Key, Hostname, Port, ie.GetType());
+ else if (ie is SshAuthenticationException)
+ {
+ Debug.Console(1, this, errorLogLevel, "Authentication failure for username '{0}', ({1})",
+ Username, ie.Message);
+ }
+ else
+ Debug.Console(1, this, errorLogLevel, "Error on connect:\r({0})", ie.Message);
+
+ DisconnectLogged = true;
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect)
+ {
+ Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ }
+ }
+ catch (Exception e)
+ {
+ var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
+ Debug.Console(1, this, errorLogLevel, "Unhandled exception on connect:\r({0})", e.Message);
+ DisconnectLogged = true;
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ if (AutoReconnect)
+ {
+ Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ }
+ }
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ ///
+ /// Disconnect the clients and put away it's resources.
+ ///
+ public void Disconnect()
+ {
+ ConnectEnabled = false;
+ // Stop trying reconnects, if we are
+ if (ReconnectTimer != null)
+ {
+ ReconnectTimer.Stop();
+ // ReconnectTimer = null;
+ }
+
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
+ }
+
+ ///
+ /// Kills the stream, cleans up the client and sets it to null
+ ///
+ private void KillClient(SocketStatus status)
+ {
+ KillStream();
+
+ try
+ {
+ if (Client != null)
+ {
+ Client.ErrorOccurred -= Client_ErrorOccurred;
+ Client.Disconnect();
+ Client.Dispose();
+ Client = null;
+ ClientStatus = status;
+ Debug.Console(1, this, "Disconnected");
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Client:{0}", ex);
+ }
+ }
+
+ ///
+ /// Anything to do with reestablishing connection on failures
+ ///
+ void HandleConnectionFailure()
+ {
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+
+ Debug.Console(1, this, "Client nulled due to connection failure. AutoReconnect: {0}, ConnectEnabled: {1}", AutoReconnect, ConnectEnabled);
+ if (AutoReconnect && ConnectEnabled)
+ {
+ Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ if (ReconnectTimer == null)
+ {
+ ReconnectTimer = new CTimer(o =>
+ {
+ Connect();
+ }, AutoReconnectIntervalMs);
+ Debug.Console(1, this, "Attempting connection in {0} seconds",
+ (float) (AutoReconnectIntervalMs/1000));
+ }
+ else
+ {
+ Debug.Console(1, this, "{0} second reconnect cycle running",
+ (float) (AutoReconnectIntervalMs/1000));
+ }
+ }
+ }
+
+ ///
+ /// Kills the stream
+ ///
+ void KillStream()
+ {
+ try
+ {
+ if (TheStream != null)
+ {
+ TheStream.DataReceived -= Stream_DataReceived;
+ TheStream.Close();
+ TheStream.Dispose();
+ TheStream = null;
+ Debug.Console(1, this, "Disconnected stream");
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception in Kill Stream:{0}", ex);
+ }
+ }
+
+ ///
+ /// Handles the keyboard interactive authentication, should it be required.
+ ///
+ void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
+ {
+ foreach (AuthenticationPrompt prompt in e.Prompts)
+ if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
+ prompt.Response = Password;
+ }
+
+ ///
+ /// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
+ ///
+ void Stream_DataReceived(object sender, Crestron.SimplSharp.Ssh.Common.ShellDataEventArgs e)
+ {
+ var bytes = e.Data;
+ if (bytes.Length > 0)
+ {
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ if (StreamDebugging.RxStreamDebuggingIsEnabled)
+ {
+ Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
+ }
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ if (StreamDebugging.RxStreamDebuggingIsEnabled)
+ Debug.Console(0, this, "Received: '{0}'", ComTextHelper.GetDebugText(str));
+
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+
+
+ ///
+ /// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
+ /// event
+ ///
+ void Client_ErrorOccurred(object sender, Crestron.SimplSharp.Ssh.Common.ExceptionEventArgs e)
+ {
+ CrestronInvoke.BeginInvoke(o =>
+ {
+ if (e.Exception is SshConnectionException || e.Exception is System.Net.Sockets.SocketException)
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Disconnected by remote");
+ else
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Unhandled SSH client error: {0}", e.Exception);
+
+ try
+ {
+ connectLock.Enter();
+ KillClient(SocketStatus.SOCKET_STATUS_BROKEN_REMOTELY);
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ if (AutoReconnect && ConnectEnabled)
+ {
+ Debug.Console(1, this, "Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
+ ReconnectTimer.Reset(AutoReconnectIntervalMs);
+ }
+ });
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ if (ConnectionChange != null)
+ ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ }
+
+ #region IBasicCommunication Members
+
+ ///
+ /// Sends text to the server
+ ///
+ ///
+ public void SendText(string text)
+ {
+ try
+ {
+ if (Client != null && TheStream != null && IsConnected)
+ {
+ if (StreamDebugging.TxStreamDebuggingIsEnabled)
+ Debug.Console(0,
+ this,
+ "Sending {0} characters of text: '{1}'",
+ text.Length,
+ ComTextHelper.GetDebugText(text));
+
+ TheStream.Write(text);
+ TheStream.Flush();
+ }
+ else
+ {
+ Debug.Console(1, this, "Client is null or disconnected. Cannot Send Text");
+ }
+ }
+ catch (ObjectDisposedException ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
+
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ ReconnectTimer.Reset();
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
+ }
+ }
+
+ ///
+ /// Sends Bytes to the server
+ ///
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ try
+ {
+ if (Client != null && TheStream != null && IsConnected)
+ {
+ if (StreamDebugging.TxStreamDebuggingIsEnabled)
+ Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+
+ TheStream.Write(bytes, 0, bytes.Length);
+ TheStream.Flush();
+ }
+ else
+ {
+ Debug.Console(1, this, "Client is null or disconnected. Cannot Send Bytes");
+ }
+ }
+ catch (ObjectDisposedException ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
+
+ KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
+ ReconnectTimer.Reset();
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Exception: {0}", ex.Message);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Stack Trace: {0}", ex.StackTrace);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Stream write failed");
+ }
+ }
+
+ #endregion
+ }
+
+ //*****************************************************************************************************
+ //*****************************************************************************************************
+ ///
+ /// Fired when connection changes
+ ///
+ public class SshConnectionChangeEventArgs : EventArgs
+ {
+ ///
+ /// Connection State
+ ///
+ public bool IsConnected { get; private set; }
+
+ ///
+ /// Connection Status represented as a ushort
+ ///
+ public ushort UIsConnected { get { return (ushort)(Client.IsConnected ? 1 : 0); } }
+
+ ///
+ /// The client
+ ///
+ public GenericSshClient Client { get; private set; }
+
+ ///
+ /// Socket Status as represented by
+ ///
+ public ushort Status { get { return Client.UStatus; } }
+
+ ///
+ /// S+ Constructor
+ ///
+ public SshConnectionChangeEventArgs() { }
+
+ ///
+ /// EventArgs class
+ ///
+ /// Connection State
+ /// The Client
+ public SshConnectionChangeEventArgs(bool isConnected, GenericSshClient client)
+ {
+ IsConnected = isConnected;
+ Client = client;
+ }
+ }
+}
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs b/src/Comm/GenericTcpIpClient.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs
rename to src/Comm/GenericTcpIpClient.cs
index 024221f..9fa17c1 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient.cs
+++ b/src/Comm/GenericTcpIpClient.cs
@@ -1,567 +1,567 @@
-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;
-
-namespace PepperDash.Core
-{
- ///
- /// A class to handle basic TCP/IP communications with a server
- ///
- public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
- {
- private const string SplusKey = "Uninitialized TcpIpClient";
- ///
- /// Object to enable stream debugging
- ///
- public CommunicationStreamDebugging StreamDebugging { get; private set; }
-
- ///
- /// Fires when data is received from the server and returns it as a Byte array
- ///
- public event EventHandler BytesReceived;
-
- ///
- /// Fires when data is received from the server and returns it as text
- ///
- public event EventHandler TextReceived;
-
- ///
- ///
- ///
- //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
- public event EventHandler ConnectionChange;
-
-
- private string _hostname;
-
- ///
- /// Address of server
- ///
- public string Hostname
- {
- get
- {
- return _hostname;
- }
-
- set
- {
- _hostname = value;
- if (_client != null)
- {
- _client.AddressClientConnectedTo = _hostname;
- }
- }
- }
-
- ///
- /// 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); }
- }
-
- ///
- /// Defaults to 2000
- ///
- public int BufferSize { get; set; }
-
- ///
- /// The actual client class
- ///
- private TCPClient _client;
-
- ///
- /// 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
- {
- return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _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(); } }
-
- ///
- /// Ushort representation of client status
- ///
- [Obsolete]
- public ushort UClientStatus { get { return (ushort)ClientStatus; } }
-
- ///
- /// Connection failure reason
- ///
- public string ConnectionFailure { 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; }
-
- ///
- /// Set only when the disconnect method is called
- ///
- bool DisconnectCalledByUser;
-
- ///
- ///
- ///
- public bool Connected
- {
- get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
- }
-
- //Lock object to prevent simulatneous connect/disconnect operations
- private CCriticalSection connectLock = new CCriticalSection();
-
- // private Timer for auto reconnect
- private CTimer RetryTimer;
-
- ///
- /// Constructor
- ///
- /// unique string to differentiate between instances
- ///
- ///
- ///
- public GenericTcpIpClient(string key, string address, int port, int bufferSize)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
-
- RetryTimer = new CTimer(o =>
- {
- Reconnect();
- }, Timeout.Infinite);
- }
-
- ///
- /// Constructor
- ///
- ///
- public GenericTcpIpClient(string key)
- : base(key)
- {
- StreamDebugging = new CommunicationStreamDebugging(key);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- RetryTimer = new CTimer(o =>
- {
- Reconnect();
- }, Timeout.Infinite);
- }
-
- ///
- /// Default constructor for S+
- ///
- public GenericTcpIpClient()
- : base(SplusKey)
- {
- StreamDebugging = new CommunicationStreamDebugging(SplusKey);
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- AutoReconnectIntervalMs = 5000;
- BufferSize = 2000;
-
- RetryTimer = new CTimer(o =>
- {
- Reconnect();
- }, Timeout.Infinite);
- }
-
- ///
- /// 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)
- {
- Debug.Console(1, this, "Program stopping. Closing connection");
- Deactivate();
- }
- }
-
- ///
- ///
- ///
- ///
- public override bool Deactivate()
- {
- RetryTimer.Stop();
- RetryTimer.Dispose();
- if (_client != null)
- {
- _client.SocketStatusChange -= this.Client_SocketStatusChange;
- DisconnectClient();
- }
- return true;
- }
-
- ///
- /// Attempts to connect to the server
- ///
- public void Connect()
- {
- if (string.IsNullOrEmpty(Hostname))
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", Key);
- return;
- }
- }
-
- try
- {
- connectLock.Enter();
- if (IsConnected)
- {
- Debug.Console(1, this, "Connection already connected. Exiting Connect()");
- }
- else
- {
- //Stop retry timer if running
- RetryTimer.Stop();
- _client = new TCPClient(Hostname, Port, BufferSize);
- _client.SocketStatusChange -= Client_SocketStatusChange;
- _client.SocketStatusChange += Client_SocketStatusChange;
- DisconnectCalledByUser = false;
- _client.ConnectToServerAsync(ConnectToServerCallback);
- }
- }
- finally
- {
- connectLock.Leave();
- }
- }
-
- private void Reconnect()
- {
- if (_client == null)
- {
- return;
- }
- try
- {
- connectLock.Enter();
- if (IsConnected || DisconnectCalledByUser == true)
- {
- Debug.Console(1, this, "Reconnect no longer needed. Exiting Reconnect()");
- }
- else
- {
- Debug.Console(1, this, "Attempting reconnect now");
- _client.ConnectToServerAsync(ConnectToServerCallback);
- }
- }
- finally
- {
- connectLock.Leave();
- }
- }
-
- ///
- /// Attempts to disconnect the client
- ///
- public void Disconnect()
- {
- try
- {
- connectLock.Enter();
- DisconnectCalledByUser = true;
-
- // Stop trying reconnects, if we are
- RetryTimer.Stop();
- DisconnectClient();
- }
- finally
- {
- connectLock.Leave();
- }
- }
-
- ///
- /// Does the actual disconnect business
- ///
- public void DisconnectClient()
- {
- if (_client != null)
- {
- Debug.Console(1, this, "Disconnecting client");
- if (IsConnected)
- _client.DisconnectFromServer();
- }
- }
-
- ///
- /// Callback method for connection attempt
- ///
- ///
- void ConnectToServerCallback(TCPClient c)
- {
- if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
- WaitAndTryReconnect();
- }
- else
- {
- Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
- }
- }
-
- ///
- /// Disconnects, waits and attemtps to connect again
- ///
- void WaitAndTryReconnect()
- {
- CrestronInvoke.BeginInvoke(o =>
- {
- try
- {
- connectLock.Enter();
- if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)
- {
- DisconnectClient();
- Debug.Console(1, this, "Attempting reconnect, status={0}", _client.ClientStatus);
- RetryTimer.Reset(AutoReconnectIntervalMs);
- }
- }
- finally
- {
- connectLock.Leave();
- }
- });
- }
-
- ///
- /// Recieves incoming data
- ///
- ///
- ///
- void Receive(TCPClient client, int numBytes)
- {
- if (client != null)
- {
- if (numBytes > 0)
- {
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- var bytesHandler = BytesReceived;
- if (bytesHandler != null)
- {
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
- }
- bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- }
- var textHandler = TextReceived;
- if (textHandler != null)
- {
- var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
-
- if (StreamDebugging.RxStreamDebuggingIsEnabled)
- {
- Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
- }
-
- textHandler(this, new GenericCommMethodReceiveTextArgs(str));
- }
- }
- client.ReceiveDataAsync(Receive);
- }
- }
-
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- // Check debug level before processing byte array
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
- if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
-
- ///
- /// This is useful from console and...?
- ///
- public void SendEscapedText(string text)
- {
- var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
- {
- var hex = s.Groups[1].Value;
- return ((char)Convert.ToByte(hex, 16)).ToString();
- });
- SendText(unescapedText);
- }
-
- ///
- /// Sends Bytes to the server
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (StreamDebugging.TxStreamDebuggingIsEnabled)
- Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
- if (_client != null)
- _client.SendData(bytes, bytes.Length);
- }
-
- ///
- /// Socket Status Change Handler
- ///
- ///
- ///
- void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
- {
- if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
- WaitAndTryReconnect();
- }
- else
- {
- Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
- _client.ReceiveDataAsync(Receive);
- }
-
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
- }
- }
-
- ///
- /// Configuration properties for TCP/SSH Connections
- ///
- public class TcpSshPropertiesConfig
- {
- ///
- /// Address to connect to
- ///
- [JsonProperty(Required = Required.Always)]
- public string Address { get; set; }
-
- ///
- /// Port to connect to
- ///
- [JsonProperty(Required = Required.Always)]
- public int Port { get; set; }
-
- ///
- /// Username credential
- ///
- public string Username { get; set; }
- ///
- /// Passord credential
- ///
- public string Password { get; set; }
-
- ///
- /// Defaults to 32768
- ///
- public int BufferSize { get; set; }
-
- ///
- /// Defaults to true
- ///
- public bool AutoReconnect { get; set; }
-
- ///
- /// Defaults to 5000ms
- ///
- public int AutoReconnectIntervalMs { get; set; }
-
- ///
- /// Default constructor
- ///
- public TcpSshPropertiesConfig()
- {
- BufferSize = 32768;
- AutoReconnect = true;
- AutoReconnectIntervalMs = 5000;
- Username = "";
- Password = "";
- }
-
- }
-
+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;
+
+namespace PepperDash.Core
+{
+ ///
+ /// A class to handle basic TCP/IP communications with a server
+ ///
+ public class GenericTcpIpClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
+ {
+ private const string SplusKey = "Uninitialized TcpIpClient";
+ ///
+ /// Object to enable stream debugging
+ ///
+ public CommunicationStreamDebugging StreamDebugging { get; private set; }
+
+ ///
+ /// Fires when data is received from the server and returns it as a Byte array
+ ///
+ public event EventHandler BytesReceived;
+
+ ///
+ /// Fires when data is received from the server and returns it as text
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ ///
+ ///
+ //public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
+ public event EventHandler ConnectionChange;
+
+
+ private string _hostname;
+
+ ///
+ /// Address of server
+ ///
+ public string Hostname
+ {
+ get
+ {
+ return _hostname;
+ }
+
+ set
+ {
+ _hostname = value;
+ if (_client != null)
+ {
+ _client.AddressClientConnectedTo = _hostname;
+ }
+ }
+ }
+
+ ///
+ /// 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); }
+ }
+
+ ///
+ /// Defaults to 2000
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// The actual client class
+ ///
+ private TCPClient _client;
+
+ ///
+ /// 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
+ {
+ return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _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(); } }
+
+ ///
+ /// Ushort representation of client status
+ ///
+ [Obsolete]
+ public ushort UClientStatus { get { return (ushort)ClientStatus; } }
+
+ ///
+ /// Connection failure reason
+ ///
+ public string ConnectionFailure { 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; }
+
+ ///
+ /// Set only when the disconnect method is called
+ ///
+ bool DisconnectCalledByUser;
+
+ ///
+ ///
+ ///
+ public bool Connected
+ {
+ get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
+ }
+
+ //Lock object to prevent simulatneous connect/disconnect operations
+ private CCriticalSection connectLock = new CCriticalSection();
+
+ // private Timer for auto reconnect
+ private CTimer RetryTimer;
+
+ ///
+ /// Constructor
+ ///
+ /// unique string to differentiate between instances
+ ///
+ ///
+ ///
+ public GenericTcpIpClient(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// Constructor
+ ///
+ ///
+ public GenericTcpIpClient(string key)
+ : base(key)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(key);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// Default constructor for S+
+ ///
+ public GenericTcpIpClient()
+ : base(SplusKey)
+ {
+ StreamDebugging = new CommunicationStreamDebugging(SplusKey);
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ AutoReconnectIntervalMs = 5000;
+ BufferSize = 2000;
+
+ RetryTimer = new CTimer(o =>
+ {
+ Reconnect();
+ }, Timeout.Infinite);
+ }
+
+ ///
+ /// 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)
+ {
+ Debug.Console(1, this, "Program stopping. Closing connection");
+ Deactivate();
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public override bool Deactivate()
+ {
+ RetryTimer.Stop();
+ RetryTimer.Dispose();
+ if (_client != null)
+ {
+ _client.SocketStatusChange -= this.Client_SocketStatusChange;
+ DisconnectClient();
+ }
+ return true;
+ }
+
+ ///
+ /// Attempts to connect to the server
+ ///
+ public void Connect()
+ {
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': Invalid port", Key);
+ return;
+ }
+ }
+
+ try
+ {
+ connectLock.Enter();
+ if (IsConnected)
+ {
+ Debug.Console(1, this, "Connection already connected. Exiting Connect()");
+ }
+ else
+ {
+ //Stop retry timer if running
+ RetryTimer.Stop();
+ _client = new TCPClient(Hostname, Port, BufferSize);
+ _client.SocketStatusChange -= Client_SocketStatusChange;
+ _client.SocketStatusChange += Client_SocketStatusChange;
+ DisconnectCalledByUser = false;
+ _client.ConnectToServerAsync(ConnectToServerCallback);
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ private void Reconnect()
+ {
+ if (_client == null)
+ {
+ return;
+ }
+ try
+ {
+ connectLock.Enter();
+ if (IsConnected || DisconnectCalledByUser == true)
+ {
+ Debug.Console(1, this, "Reconnect no longer needed. Exiting Reconnect()");
+ }
+ else
+ {
+ Debug.Console(1, this, "Attempting reconnect now");
+ _client.ConnectToServerAsync(ConnectToServerCallback);
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ ///
+ /// Attempts to disconnect the client
+ ///
+ public void Disconnect()
+ {
+ try
+ {
+ connectLock.Enter();
+ DisconnectCalledByUser = true;
+
+ // Stop trying reconnects, if we are
+ RetryTimer.Stop();
+ DisconnectClient();
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ }
+
+ ///
+ /// Does the actual disconnect business
+ ///
+ public void DisconnectClient()
+ {
+ if (_client != null)
+ {
+ Debug.Console(1, this, "Disconnecting client");
+ if (IsConnected)
+ _client.DisconnectFromServer();
+ }
+ }
+
+ ///
+ /// Callback method for connection attempt
+ ///
+ ///
+ void ConnectToServerCallback(TCPClient c)
+ {
+ if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
+ WaitAndTryReconnect();
+ }
+ else
+ {
+ Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
+ }
+ }
+
+ ///
+ /// Disconnects, waits and attemtps to connect again
+ ///
+ void WaitAndTryReconnect()
+ {
+ CrestronInvoke.BeginInvoke(o =>
+ {
+ try
+ {
+ connectLock.Enter();
+ if (!IsConnected && AutoReconnect && !DisconnectCalledByUser && _client != null)
+ {
+ DisconnectClient();
+ Debug.Console(1, this, "Attempting reconnect, status={0}", _client.ClientStatus);
+ RetryTimer.Reset(AutoReconnectIntervalMs);
+ }
+ }
+ finally
+ {
+ connectLock.Leave();
+ }
+ });
+ }
+
+ ///
+ /// Recieves incoming data
+ ///
+ ///
+ ///
+ void Receive(TCPClient client, int numBytes)
+ {
+ if (client != null)
+ {
+ if (numBytes > 0)
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ var bytesHandler = BytesReceived;
+ if (bytesHandler != null)
+ {
+ if (StreamDebugging.RxStreamDebuggingIsEnabled)
+ {
+ Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
+ }
+ bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ }
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ {
+ var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+
+ if (StreamDebugging.RxStreamDebuggingIsEnabled)
+ {
+ Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
+ }
+
+ textHandler(this, new GenericCommMethodReceiveTextArgs(str));
+ }
+ }
+ client.ReceiveDataAsync(Receive);
+ }
+ }
+
+ ///
+ /// General send method
+ ///
+ public void SendText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ // Check debug level before processing byte array
+ if (StreamDebugging.TxStreamDebuggingIsEnabled)
+ Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
+ if (_client != null)
+ _client.SendData(bytes, bytes.Length);
+ }
+
+ ///
+ /// This is useful from console and...?
+ ///
+ public void SendEscapedText(string text)
+ {
+ var unescapedText = Regex.Replace(text, @"\\x([0-9a-fA-F][0-9a-fA-F])", s =>
+ {
+ var hex = s.Groups[1].Value;
+ return ((char)Convert.ToByte(hex, 16)).ToString();
+ });
+ SendText(unescapedText);
+ }
+
+ ///
+ /// Sends Bytes to the server
+ ///
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (StreamDebugging.TxStreamDebuggingIsEnabled)
+ Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
+ if (_client != null)
+ _client.SendData(bytes, bytes.Length);
+ }
+
+ ///
+ /// Socket Status Change Handler
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
+ WaitAndTryReconnect();
+ }
+ else
+ {
+ Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
+ _client.ReceiveDataAsync(Receive);
+ }
+
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
+ }
+ }
+
+ ///
+ /// Configuration properties for TCP/SSH Connections
+ ///
+ public class TcpSshPropertiesConfig
+ {
+ ///
+ /// Address to connect to
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public string Address { get; set; }
+
+ ///
+ /// Port to connect to
+ ///
+ [JsonProperty(Required = Required.Always)]
+ public int Port { get; set; }
+
+ ///
+ /// Username credential
+ ///
+ public string Username { get; set; }
+ ///
+ /// Passord credential
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Defaults to 32768
+ ///
+ public int BufferSize { get; set; }
+
+ ///
+ /// Defaults to true
+ ///
+ public bool AutoReconnect { get; set; }
+
+ ///
+ /// Defaults to 5000ms
+ ///
+ public int AutoReconnectIntervalMs { get; set; }
+
+ ///
+ /// Default constructor
+ ///
+ public TcpSshPropertiesConfig()
+ {
+ BufferSize = 32768;
+ AutoReconnect = true;
+ AutoReconnectIntervalMs = 5000;
+ Username = "";
+ Password = "";
+ }
+
+ }
+
}
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient_ForServer.cs b/src/Comm/GenericTcpIpClient_ForServer.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient_ForServer.cs
rename to src/Comm/GenericTcpIpClient_ForServer.cs
index 1e31c6f..df277f0 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpClient_ForServer.cs
+++ b/src/Comm/GenericTcpIpClient_ForServer.cs
@@ -1,774 +1,774 @@
-/*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
-{
- ///
- /// Generic TCP/IP client for server
- ///
- public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
- {
- ///
- /// Band aid delegate for choked server
- ///
- internal delegate void ConnectionHasHungCallbackDelegate();
-
- #region Events
-
- //public event EventHandler BytesReceived;
-
- ///
- /// Notifies of text received
- ///
- public event EventHandler TextReceived;
-
- ///
- /// Notifies of socket status change
- ///
- public event EventHandler ConnectionChange;
-
-
- ///
- /// This is something of a band-aid callback. If the client times out during the connection process, because the server
- /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
- /// keep a watch on the server and reset it if necessary.
- ///
- internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
-
- ///
- /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
- /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
- ///
- public event EventHandler ClientReadyForCommunications;
-
- #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; }
-
- ///
- /// Semaphore on connect method
- ///
- bool IsTryingToConnect;
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
- else
- return false;
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsConnected
- {
- get { return (ushort)(IsConnected ? 1 : 0); }
- }
-
- ///
- /// Bool showing if socket is ready for communication after shared key exchange
- ///
- public bool IsReadyForCommunication { get; set; }
-
- ///
- /// S+ helper for IsReadyForCommunication
- ///
- public ushort UIsReadyForCommunication
- {
- get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
- }
-
- ///
- /// Client socket status Read only
- ///
- public SocketStatus ClientStatus
- {
- get
- {
- if (Client != null)
- return Client.ClientStatus;
- else
- return SocketStatus.SOCKET_STATUS_NO_CONNECT;
- }
- }
-
- ///
- /// 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;
-
- ///
- /// private Timer for auto reconnect
- ///
- CTimer RetryTimer;
-
-
- ///
- ///
- ///
- public bool HeartbeatEnabled { get; set; }
-
- ///
- ///
- ///
- public ushort UHeartbeatEnabled
- {
- get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
- set { HeartbeatEnabled = value == 1; }
- }
-
- ///
- ///
- ///
- public string HeartbeatString = "heartbeat";
-
- ///
- ///
- ///
- public int HeartbeatInterval = 50000;
-
- CTimer HeartbeatSendTimer;
- CTimer HeartbeatAckTimer;
- ///
- /// Used to force disconnection on a dead connect attempt
- ///
- CTimer ConnectFailTimer;
- CTimer WaitForSharedKey;
- private int ConnectionCount;
- ///
- /// Internal secure client
- ///
- TCPClient Client;
-
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
-
- ///
- /// Constructor
- ///
- ///
- ///
- ///
- ///
- public GenericTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
- : base(key)
- {
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- Hostname = address;
- Port = port;
- BufferSize = bufferSize;
- AutoReconnectIntervalMs = 5000;
-
- }
-
- ///
- /// Constructor for S+
- ///
- public GenericTcpIpClient_ForServer()
- : 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 || programEventType == eProgramStatusEventType.Paused)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
- ProgramIsStopping = true;
- Disconnect();
- }
-
- }
-
- ///
- /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
- ///
- public void Connect()
- {
- ConnectionCount++;
- Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
-
-
- if (IsConnected)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
- return;
- }
- if (IsTryingToConnect)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
- return;
- }
- try
- {
- IsTryingToConnect = true;
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- if (string.IsNullOrEmpty(Hostname))
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
- return;
- }
- if (Port < 1 || Port > 65535)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
- return;
- }
-
- // clean up previous client
- if (Client != null)
- {
- Cleanup();
- }
- DisconnectCalledByUser = false;
-
- Client = new TCPClient(Hostname, Port, BufferSize);
- Client.SocketStatusChange += Client_SocketStatusChange;
- if(HeartbeatEnabled)
- Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
- Client.AddressClientConnectedTo = Hostname;
- Client.PortNumber = Port;
- // SecureClient = c;
-
- //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
-
- ConnectFailTimer = new CTimer(o =>
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
- if (IsTryingToConnect)
- {
- IsTryingToConnect = false;
-
- //if (ConnectionHasHungCallback != null)
- //{
- // ConnectionHasHungCallback();
- //}
- //SecureClient.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- }
- }, 30000);
-
- Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
- Client.ConnectToServerAsync(o =>
- {
- Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
-
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- }
- IsTryingToConnect = false;
-
- if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
- o.ReceiveDataAsync(Receive);
-
- if (SharedKeyRequired)
- {
- WaitingForSharedKeyResponse = true;
- WaitForSharedKey = new CTimer(timer =>
- {
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
- // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
- // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
- o.DisconnectFromServer();
- //CheckClosedAndTryReconnect();
- //OnClientReadyForcommunications(false); // Should send false event
- }, 15000);
- }
- else
- {
- //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
- //required this is called by the shared key being negotiated
- if (IsReadyForCommunication == false)
- {
- OnClientReadyForcommunications(true); // Key not required
- }
- }
- }
- else
- {
- Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
- CheckClosedAndTryReconnect();
- }
- });
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
- IsTryingToConnect = false;
- CheckClosedAndTryReconnect();
- }
- }
-
- ///
- ///
- ///
- public void Disconnect()
- {
- Debug.Console(2, "Disconnect Called");
-
- DisconnectCalledByUser = true;
- if (IsConnected)
- {
- Client.DisconnectFromServer();
-
- }
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- Cleanup();
- }
-
- ///
- /// Internal call to close up client. ALWAYS use this when disconnecting.
- ///
- void Cleanup()
- {
- IsTryingToConnect = false;
-
- if (Client != null)
- {
- //SecureClient.DisconnectFromServer();
- Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
- Client.SocketStatusChange -= Client_SocketStatusChange;
- Client.Dispose();
- Client = null;
- }
- if (ConnectFailTimer != null)
- {
- ConnectFailTimer.Stop();
- ConnectFailTimer.Dispose();
- ConnectFailTimer = null;
- }
- }
-
-
- /// ff
- /// Called from Connect failure or Socket Status change if
- /// auto reconnect and socket disconnected (Not disconnected by user)
- ///
- void CheckClosedAndTryReconnect()
- {
- if (Client != null)
- {
- Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
- Cleanup();
- }
- if (!DisconnectCalledByUser && AutoReconnect)
- {
- var halfInterval = AutoReconnectIntervalMs / 2;
- var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
- Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
- if (RetryTimer != null)
- {
- RetryTimer.Stop();
- RetryTimer = null;
- }
- RetryTimer = new CTimer(o => Connect(), rndTime);
- }
- }
-
- ///
- /// Receive callback
- ///
- ///
- ///
- void Receive(TCPClient client, int numBytes)
- {
- if (numBytes > 0)
- {
- string str = string.Empty;
-
- try
- {
- var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
- str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
- Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
- if (!string.IsNullOrEmpty(checkHeartbeat(str)))
- {
- if (SharedKeyRequired && str == "SharedKey:")
- {
- Debug.Console(2, this, "Server asking for shared key, sending");
- SendText(SharedKey + "\n");
- }
- else if (SharedKeyRequired && str == "Shared Key Match")
- {
- StopWaitForSharedKeyTimer();
- Debug.Console(2, this, "Shared key confirmed. Ready for communication");
- OnClientReadyForcommunications(true); // Successful key exchange
- }
- else
- {
- //var bytesHandler = BytesReceived;
- //if (bytesHandler != null)
- // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
- var textHandler = TextReceived;
- if (textHandler != null)
- textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
- }
- }
- if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- client.ReceiveDataAsync(Receive);
- }
-
- void HeartbeatStart()
- {
- if (HeartbeatEnabled)
- {
- Debug.Console(2, this, "Starting Heartbeat");
- if (HeartbeatSendTimer == null)
- {
-
- HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
- }
- if (HeartbeatAckTimer == null)
- {
- HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
- }
- }
-
- }
- void HeartbeatStop()
- {
-
- if (HeartbeatSendTimer != null)
- {
- Debug.Console(2, this, "Stoping Heartbeat Send");
- HeartbeatSendTimer.Stop();
- HeartbeatSendTimer = null;
- }
- if (HeartbeatAckTimer != null)
- {
- Debug.Console(2, this, "Stoping Heartbeat Ack");
- HeartbeatAckTimer.Stop();
- HeartbeatAckTimer = null;
- }
-
- }
- void SendHeartbeat(object notused)
- {
- this.SendText(HeartbeatString);
- Debug.Console(2, this, "Sending Heartbeat");
-
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(string received)
- {
- try
- {
- if (HeartbeatEnabled)
- {
- if (!string.IsNullOrEmpty(HeartbeatString))
- {
- var remainingText = received.Replace(HeartbeatString, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatString))
- {
- if (HeartbeatAckTimer != null)
- {
- HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
- }
- else
- {
- HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
- }
- Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
- return remainingText;
- }
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
-
-
- void HeartbeatAckTimerFail(object o)
- {
- try
- {
-
- if (IsConnected)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
- SendText("Heartbeat not received by server, closing connection");
- CheckClosedAndTryReconnect();
- }
-
- }
- catch (Exception ex)
- {
- ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
- }
- }
-
- ///
- ///
- ///
- void StopWaitForSharedKeyTimer()
- {
- if (WaitForSharedKey != null)
- {
- WaitForSharedKey.Stop();
- WaitForSharedKey = null;
- }
- }
-
- ///
- /// General send method
- ///
- public void SendText(string text)
- {
- if (!string.IsNullOrEmpty(text))
- {
- try
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
- {
- // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
- if (n <= 0)
- {
- Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
- }
- });
- }
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
- }
- }
- }
-
- ///
- ///
- ///
- public void SendBytes(byte[] bytes)
- {
- if (bytes.Length > 0)
- {
- try
- {
- if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
- Client.SendData(bytes, bytes.Length);
- }
- catch (Exception ex)
- {
- Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
- }
- }
- }
-
- ///
- /// SocketStatusChange Callback
- ///
- ///
- ///
- void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
- {
- if (ProgramIsStopping)
- {
- ProgramIsStopping = false;
- return;
- }
- try
- {
- Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
-
- OnConnectionChange();
-
- // The client could be null or disposed by this time...
- if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- HeartbeatStop();
- OnClientReadyForcommunications(false); // socket has gone low
- CheckClosedAndTryReconnect();
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
- }
- }
-
- ///
- /// Helper for ConnectionChange event
- ///
- void OnConnectionChange()
- {
- var handler = ConnectionChange;
- if (handler != null)
- ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
- }
-
- ///
- /// Helper to fire ClientReadyForCommunications event
- ///
- void OnClientReadyForcommunications(bool isReady)
- {
- IsReadyForCommunication = isReady;
- if (this.IsReadyForCommunication) { HeartbeatStart(); }
- var handler = ClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
- }
- #endregion
- }
-
+/*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
+{
+ ///
+ /// Generic TCP/IP client for server
+ ///
+ public class GenericTcpIpClient_ForServer : Device, IAutoReconnect
+ {
+ ///
+ /// Band aid delegate for choked server
+ ///
+ internal delegate void ConnectionHasHungCallbackDelegate();
+
+ #region Events
+
+ //public event EventHandler BytesReceived;
+
+ ///
+ /// Notifies of text received
+ ///
+ public event EventHandler TextReceived;
+
+ ///
+ /// Notifies of socket status change
+ ///
+ public event EventHandler ConnectionChange;
+
+
+ ///
+ /// This is something of a band-aid callback. If the client times out during the connection process, because the server
+ /// is stuck, this will fire. It is intended to be used by the Server class monitor client, to help
+ /// keep a watch on the server and reset it if necessary.
+ ///
+ internal ConnectionHasHungCallbackDelegate ConnectionHasHungCallback;
+
+ ///
+ /// For a client with a pre shared key, this will fire after the communication is established and the key exchange is complete. If you require
+ /// a key and subscribe to the socket change event and try to send data on a connection the data sent will interfere with the key exchange and disconnect.
+ ///
+ public event EventHandler ClientReadyForCommunications;
+
+ #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; }
+
+ ///
+ /// Semaphore on connect method
+ ///
+ bool IsTryingToConnect;
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED;
+ else
+ return false;
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsConnected
+ {
+ get { return (ushort)(IsConnected ? 1 : 0); }
+ }
+
+ ///
+ /// Bool showing if socket is ready for communication after shared key exchange
+ ///
+ public bool IsReadyForCommunication { get; set; }
+
+ ///
+ /// S+ helper for IsReadyForCommunication
+ ///
+ public ushort UIsReadyForCommunication
+ {
+ get { return (ushort)(IsReadyForCommunication ? 1 : 0); }
+ }
+
+ ///
+ /// Client socket status Read only
+ ///
+ public SocketStatus ClientStatus
+ {
+ get
+ {
+ if (Client != null)
+ return Client.ClientStatus;
+ else
+ return SocketStatus.SOCKET_STATUS_NO_CONNECT;
+ }
+ }
+
+ ///
+ /// 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;
+
+ ///
+ /// private Timer for auto reconnect
+ ///
+ CTimer RetryTimer;
+
+
+ ///
+ ///
+ ///
+ public bool HeartbeatEnabled { get; set; }
+
+ ///
+ ///
+ ///
+ public ushort UHeartbeatEnabled
+ {
+ get { return (ushort)(HeartbeatEnabled ? 1 : 0); }
+ set { HeartbeatEnabled = value == 1; }
+ }
+
+ ///
+ ///
+ ///
+ public string HeartbeatString = "heartbeat";
+
+ ///
+ ///
+ ///
+ public int HeartbeatInterval = 50000;
+
+ CTimer HeartbeatSendTimer;
+ CTimer HeartbeatAckTimer;
+ ///
+ /// Used to force disconnection on a dead connect attempt
+ ///
+ CTimer ConnectFailTimer;
+ CTimer WaitForSharedKey;
+ private int ConnectionCount;
+ ///
+ /// Internal secure client
+ ///
+ TCPClient Client;
+
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericTcpIpClient_ForServer(string key, string address, int port, int bufferSize)
+ : base(key)
+ {
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ Hostname = address;
+ Port = port;
+ BufferSize = bufferSize;
+ AutoReconnectIntervalMs = 5000;
+
+ }
+
+ ///
+ /// Constructor for S+
+ ///
+ public GenericTcpIpClient_ForServer()
+ : 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 || programEventType == eProgramStatusEventType.Paused)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing Client connection");
+ ProgramIsStopping = true;
+ Disconnect();
+ }
+
+ }
+
+ ///
+ /// Connect Method. Will return if already connected. Will write errors if missing address, port, or unique key/name.
+ ///
+ public void Connect()
+ {
+ ConnectionCount++;
+ Debug.Console(2, this, "Attempting connect Count:{0}", ConnectionCount);
+
+
+ if (IsConnected)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already connected. Ignoring.");
+ return;
+ }
+ if (IsTryingToConnect)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Notice, "Already trying to connect. Ignoring.");
+ return;
+ }
+ try
+ {
+ IsTryingToConnect = true;
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ if (string.IsNullOrEmpty(Hostname))
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No address set");
+ return;
+ }
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: Invalid port");
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Warning, "DynamicTcpClient: No Shared Key set");
+ return;
+ }
+
+ // clean up previous client
+ if (Client != null)
+ {
+ Cleanup();
+ }
+ DisconnectCalledByUser = false;
+
+ Client = new TCPClient(Hostname, Port, BufferSize);
+ Client.SocketStatusChange += Client_SocketStatusChange;
+ if(HeartbeatEnabled)
+ Client.SocketSendOrReceiveTimeOutInMs = (HeartbeatInterval * 5);
+ Client.AddressClientConnectedTo = Hostname;
+ Client.PortNumber = Port;
+ // SecureClient = c;
+
+ //var timeOfConnect = DateTime.Now.ToString("HH:mm:ss.fff");
+
+ ConnectFailTimer = new CTimer(o =>
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Connect attempt has not finished after 30sec Count:{0}", ConnectionCount);
+ if (IsTryingToConnect)
+ {
+ IsTryingToConnect = false;
+
+ //if (ConnectionHasHungCallback != null)
+ //{
+ // ConnectionHasHungCallback();
+ //}
+ //SecureClient.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ }
+ }, 30000);
+
+ Debug.Console(2, this, "Making Connection Count:{0}", ConnectionCount);
+ Client.ConnectToServerAsync(o =>
+ {
+ Debug.Console(2, this, "ConnectToServerAsync Count:{0} Ran!", ConnectionCount);
+
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ }
+ IsTryingToConnect = false;
+
+ if (o.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Debug.Console(2, this, "Client connected to {0} on port {1}", o.AddressClientConnectedTo, o.LocalPortNumberOfClient);
+ o.ReceiveDataAsync(Receive);
+
+ if (SharedKeyRequired)
+ {
+ WaitingForSharedKeyResponse = true;
+ WaitForSharedKey = new CTimer(timer =>
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Shared key exchange timer expired. IsReadyForCommunication={0}", IsReadyForCommunication);
+ // Debug.Console(1, this, "Connect attempt failed {0}", c.ClientStatus);
+ // This is the only case where we should call DisconectFromServer...Event handeler will trigger the cleanup
+ o.DisconnectFromServer();
+ //CheckClosedAndTryReconnect();
+ //OnClientReadyForcommunications(false); // Should send false event
+ }, 15000);
+ }
+ else
+ {
+ //CLient connected and shared key is not required so just raise the ready for communication event. if Shared key
+ //required this is called by the shared key being negotiated
+ if (IsReadyForCommunication == false)
+ {
+ OnClientReadyForcommunications(true); // Key not required
+ }
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, "Connect attempt failed {0}", o.ClientStatus);
+ CheckClosedAndTryReconnect();
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, Debug.ErrorLogLevel.Error, "Client connection exception: {0}", ex.Message);
+ IsTryingToConnect = false;
+ CheckClosedAndTryReconnect();
+ }
+ }
+
+ ///
+ ///
+ ///
+ public void Disconnect()
+ {
+ Debug.Console(2, "Disconnect Called");
+
+ DisconnectCalledByUser = true;
+ if (IsConnected)
+ {
+ Client.DisconnectFromServer();
+
+ }
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ Cleanup();
+ }
+
+ ///
+ /// Internal call to close up client. ALWAYS use this when disconnecting.
+ ///
+ void Cleanup()
+ {
+ IsTryingToConnect = false;
+
+ if (Client != null)
+ {
+ //SecureClient.DisconnectFromServer();
+ Debug.Console(2, this, "Disconnecting Client {0}", DisconnectCalledByUser ? ", Called by user" : "");
+ Client.SocketStatusChange -= Client_SocketStatusChange;
+ Client.Dispose();
+ Client = null;
+ }
+ if (ConnectFailTimer != null)
+ {
+ ConnectFailTimer.Stop();
+ ConnectFailTimer.Dispose();
+ ConnectFailTimer = null;
+ }
+ }
+
+
+ /// ff
+ /// Called from Connect failure or Socket Status change if
+ /// auto reconnect and socket disconnected (Not disconnected by user)
+ ///
+ void CheckClosedAndTryReconnect()
+ {
+ if (Client != null)
+ {
+ Debug.Console(2, this, "Cleaning up remotely closed/failed connection.");
+ Cleanup();
+ }
+ if (!DisconnectCalledByUser && AutoReconnect)
+ {
+ var halfInterval = AutoReconnectIntervalMs / 2;
+ var rndTime = new Random().Next(-halfInterval, halfInterval) + AutoReconnectIntervalMs;
+ Debug.Console(2, this, "Attempting reconnect in {0} ms, randomized", rndTime);
+ if (RetryTimer != null)
+ {
+ RetryTimer.Stop();
+ RetryTimer = null;
+ }
+ RetryTimer = new CTimer(o => Connect(), rndTime);
+ }
+ }
+
+ ///
+ /// Receive callback
+ ///
+ ///
+ ///
+ void Receive(TCPClient client, int numBytes)
+ {
+ if (numBytes > 0)
+ {
+ string str = string.Empty;
+
+ try
+ {
+ var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
+ str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
+ Debug.Console(2, this, "Client Received:\r--------\r{0}\r--------", str);
+ if (!string.IsNullOrEmpty(checkHeartbeat(str)))
+ {
+ if (SharedKeyRequired && str == "SharedKey:")
+ {
+ Debug.Console(2, this, "Server asking for shared key, sending");
+ SendText(SharedKey + "\n");
+ }
+ else if (SharedKeyRequired && str == "Shared Key Match")
+ {
+ StopWaitForSharedKeyTimer();
+ Debug.Console(2, this, "Shared key confirmed. Ready for communication");
+ OnClientReadyForcommunications(true); // Successful key exchange
+ }
+ else
+ {
+ //var bytesHandler = BytesReceived;
+ //if (bytesHandler != null)
+ // bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
+ var textHandler = TextReceived;
+ if (textHandler != null)
+ textHandler(this, new GenericTcpServerCommMethodReceiveTextArgs(str));
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error receiving data: {1}. Error: {0}", ex.Message, str);
+ }
+ }
+ if (client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ client.ReceiveDataAsync(Receive);
+ }
+
+ void HeartbeatStart()
+ {
+ if (HeartbeatEnabled)
+ {
+ Debug.Console(2, this, "Starting Heartbeat");
+ if (HeartbeatSendTimer == null)
+ {
+
+ HeartbeatSendTimer = new CTimer(this.SendHeartbeat, null, HeartbeatInterval, HeartbeatInterval);
+ }
+ if (HeartbeatAckTimer == null)
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ }
+
+ }
+ void HeartbeatStop()
+ {
+
+ if (HeartbeatSendTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Send");
+ HeartbeatSendTimer.Stop();
+ HeartbeatSendTimer = null;
+ }
+ if (HeartbeatAckTimer != null)
+ {
+ Debug.Console(2, this, "Stoping Heartbeat Ack");
+ HeartbeatAckTimer.Stop();
+ HeartbeatAckTimer = null;
+ }
+
+ }
+ void SendHeartbeat(object notused)
+ {
+ this.SendText(HeartbeatString);
+ Debug.Console(2, this, "Sending Heartbeat");
+
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(string received)
+ {
+ try
+ {
+ if (HeartbeatEnabled)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatString))
+ {
+ var remainingText = received.Replace(HeartbeatString, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatString))
+ {
+ if (HeartbeatAckTimer != null)
+ {
+ HeartbeatAckTimer.Reset(HeartbeatInterval * 2);
+ }
+ else
+ {
+ HeartbeatAckTimer = new CTimer(HeartbeatAckTimerFail, null, (HeartbeatInterval * 2), (HeartbeatInterval * 2));
+ }
+ Debug.Console(2, this, "Heartbeat Received: {0}, from Server", HeartbeatString);
+ return remainingText;
+ }
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+
+
+ void HeartbeatAckTimerFail(object o)
+ {
+ try
+ {
+
+ if (IsConnected)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "Heartbeat not received from Server...DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE");
+ SendText("Heartbeat not received by server, closing connection");
+ CheckClosedAndTryReconnect();
+ }
+
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("Heartbeat timeout Error on Client: {0}, {1}", Key, ex);
+ }
+ }
+
+ ///
+ ///
+ ///
+ void StopWaitForSharedKeyTimer()
+ {
+ if (WaitForSharedKey != null)
+ {
+ WaitForSharedKey.Stop();
+ WaitForSharedKey = null;
+ }
+ }
+
+ ///
+ /// General send method
+ ///
+ public void SendText(string text)
+ {
+ if (!string.IsNullOrEmpty(text))
+ {
+ try
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ Client.SendDataAsync(bytes, bytes.Length, (c, n) =>
+ {
+ // HOW IN THE HELL DO WE CATCH AN EXCEPTION IN SENDING?????
+ if (n <= 0)
+ {
+ Debug.Console(1, Debug.ErrorLogLevel.Warning, "[{0}] Sent zero bytes. Was there an error?", this.Key);
+ }
+ });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
+ }
+
+ ///
+ ///
+ ///
+ public void SendBytes(byte[] bytes)
+ {
+ if (bytes.Length > 0)
+ {
+ try
+ {
+ if (Client != null && Client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED)
+ Client.SendData(bytes, bytes.Length);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(0, this, "Error sending bytes. Error: {0}", ex.Message);
+ }
+ }
+ }
+
+ ///
+ /// SocketStatusChange Callback
+ ///
+ ///
+ ///
+ void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
+ {
+ if (ProgramIsStopping)
+ {
+ ProgramIsStopping = false;
+ return;
+ }
+ try
+ {
+ Debug.Console(2, this, "Socket status change: {0} ({1})", client.ClientStatus, (ushort)(client.ClientStatus));
+
+ OnConnectionChange();
+
+ // The client could be null or disposed by this time...
+ if (Client == null || Client.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ HeartbeatStop();
+ OnClientReadyForcommunications(false); // socket has gone low
+ CheckClosedAndTryReconnect();
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Error in socket status change callback. Error: {0}\r\r{1}", ex, ex.InnerException);
+ }
+ }
+
+ ///
+ /// Helper for ConnectionChange event
+ ///
+ void OnConnectionChange()
+ {
+ var handler = ConnectionChange;
+ if (handler != null)
+ ConnectionChange(this, new GenericTcpServerSocketStatusChangeEventArgs(this, Client.ClientStatus));
+ }
+
+ ///
+ /// Helper to fire ClientReadyForCommunications event
+ ///
+ void OnClientReadyForcommunications(bool isReady)
+ {
+ IsReadyForCommunication = isReady;
+ if (this.IsReadyForCommunication) { HeartbeatStart(); }
+ var handler = ClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerClientReadyForcommunicationsEventArgs(IsReadyForCommunication));
+ }
+ #endregion
+ }
+
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs b/src/Comm/GenericTcpIpServer.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs
rename to src/Comm/GenericTcpIpServer.cs
index e3a00f2..7351657 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/GenericTcpIpServer.cs
+++ b/src/Comm/GenericTcpIpServer.cs
@@ -1,1012 +1,1012 @@
-/*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 Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core
-{
- ///
- /// Generic TCP/IP server device
- ///
- 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;
-
- ///
- /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
- /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
- ///
- public event EventHandler ServerClientReadyForCommunications;
-
- ///
- /// A band aid event to notify user that the server has choked.
- ///
- public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
-
- ///
- ///
- ///
- public delegate void ServerHasChokedCallbackDelegate();
-
- #endregion
-
- #region Properties/Variables
-
- ///
- ///
- ///
- CCriticalSection ServerCCSection = new CCriticalSection();
-
-
- ///
- /// A bandaid client that monitors whether the server is reachable
- ///
- GenericTcpIpClient_ForServer MonitorClient;
-
- ///
- /// Timer to operate the bandaid monitor client in a loop.
- ///
- CTimer MonitorClientTimer;
-
- ///
- ///
- ///
- int MonitorClientFailureCount;
-
- ///
- /// 3 by default
- ///
- public int MonitorClientMaxFailureCount { get; set; }
-
- ///
- /// Text representation of the Socket Status enum values for the server
- ///
- public string Status
- {
- get
- {
- if (myTcpServer != null)
- return myTcpServer.State.ToString();
- return ServerState.SERVER_NOT_LISTENING.ToString();
-
- }
-
- }
-
- ///
- /// Bool showing if socket is connected
- ///
- public bool IsConnected
- {
- get
- {
- if (myTcpServer != null)
- return (myTcpServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
- return false;
-
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
- // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == 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
- {
- if (myTcpServer != null)
- return (myTcpServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
- else
- return false;
- //return (Secure ? SecureServer != null : UnsecureServer != null) &&
- //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
- // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
- }
- }
-
- ///
- /// S+ helper for IsConnected
- ///
- public ushort UIsListening
- {
- get { return (ushort)(IsListening ? 1 : 0); }
- }
-
- ///
- /// The maximum number of clients.
- /// Should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
- ///
- public ushort MaxClients { get; set; }
-
- ///
- /// Number of clients currently connected.
- ///
- public ushort NumberOfClientsConnected
- {
- get
- {
- if (myTcpServer != null)
- return (ushort)myTcpServer.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();
-
- List ClientReadyAfterKeyExchange = new List();
-
- ///
- /// The connected client indexes
- ///
- public 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 myTcpServer;
-
- ///
- ///
- ///
- bool ProgramIsStopping;
-
- #endregion
-
- #region Constructors
- ///
- /// 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.
- ///
- public GenericTcpIpServer()
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- }
-
- ///
- /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
- ///
- ///
- public GenericTcpIpServer(string key)
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Key = key;
- }
-
- ///
- /// Contstructor that sets all properties by calling the initialize method with a config object.
- ///
- ///
- public GenericTcpIpServer(TcpServerConfigObject serverConfigObject)
- : base("Uninitialized Dynamic TCP Server")
- {
- HeartbeatRequiredIntervalInSeconds = 15;
- CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
- BufferSize = 2000;
- MonitorClientMaxFailureCount = 3;
- Initialize(serverConfigObject);
- }
- #endregion
-
- #region Methods - Server Actions
- ///
- /// Disconnects all clients and stops the server
- ///
- public void KillServer()
- {
- ServerStopped = true;
- if (MonitorClient != null)
- {
- MonitorClient.Disconnect();
- }
- DisconnectAllClientsForShutdown();
- StopListening();
- }
-
- ///
- /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
- ///
- ///
- public void Initialize(string key)
- {
- Key = key;
- }
-
- ///
- /// Initialze with server configuration object
- ///
- ///
- public void Initialize(TcpServerConfigObject serverConfigObject)
- {
- try
- {
- if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
- {
- Key = serverConfigObject.Key;
- MaxClients = serverConfigObject.MaxClients;
- Port = serverConfigObject.Port;
- SharedKeyRequired = serverConfigObject.SharedKeyRequired;
- SharedKey = serverConfigObject.SharedKey;
- HeartbeatRequired = serverConfigObject.HeartbeatRequired;
- HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
- HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
- BufferSize = serverConfigObject.BufferSize;
-
- }
- else
- {
- ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
- catch
- {
- ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
- }
- }
-
- ///
- /// Start listening on the specified port
- ///
- public void Listen()
- {
- ServerCCSection.Enter();
- try
- {
- if (Port < 1 || Port > 65535)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
- ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
- return;
- }
- if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
- ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
- return;
- }
- if (IsListening)
- return;
-
- if (myTcpServer == null)
- {
- myTcpServer = new TCPServer(Port, MaxClients);
- if(HeartbeatRequired)
- myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
-
- // myTcpServer.HandshakeTimeout = 30;
- }
- else
- {
- KillServer();
- myTcpServer.PortNumber = Port;
- }
-
- myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
- myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
-
- ServerStopped = false;
- myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
- OnServerStateChange(myTcpServer.State);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
-
- // StartMonitorClient();
-
-
- ServerCCSection.Leave();
- }
- catch (Exception ex)
- {
- ServerCCSection.Leave();
- ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
- }
- }
-
- ///
- /// Stop Listening
- ///
- public void StopListening()
- {
- try
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
- if (myTcpServer != null)
- {
- myTcpServer.Stop();
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
- OnServerStateChange(myTcpServer.State);
- }
- ServerStopped = true;
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
- }
- }
-
- ///
- /// Disconnects Client
- ///
- ///
- public void DisconnectClient(uint client)
- {
- try
- {
- myTcpServer.Disconnect(client);
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
- }
- }
- ///
- /// Disconnect All Clients
- ///
- public void DisconnectAllClientsForShutdown()
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
- if (myTcpServer != null)
- {
- myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
- foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
- {
- var i = index;
- if (!myTcpServer.ClientConnected(index))
- continue;
- try
- {
- myTcpServer.Disconnect(i);
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
- }
- }
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
- }
-
- Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
- ConnectedClientsIndexes.Clear();
-
- if (!ProgramIsStopping)
- {
- OnConnectionChange();
- OnServerStateChange(myTcpServer.State); //State shows both listening and connected
- }
-
- // var o = new { };
- }
-
- ///
- /// Broadcast text from server to all connected clients
- ///
- ///
- public void BroadcastText(string text)
- {
- CCriticalSection CCBroadcast = new CCriticalSection();
- CCBroadcast.Enter();
- try
- {
- if (ConnectedClientsIndexes.Count > 0)
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- foreach (uint i in ConnectedClientsIndexes)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
- {
- SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
- if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
- Debug.Console(2, error.ToString());
- }
- }
- }
- CCBroadcast.Leave();
- }
- catch (Exception ex)
- {
- CCBroadcast.Leave();
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
- }
- }
-
- ///
- /// Not sure this is useful in library, maybe Pro??
- ///
- ///
- ///
- public void SendTextToClient(string text, uint clientIndex)
- {
- try
- {
- byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
- if (myTcpServer != null && myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- myTcpServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
- }
- }
-
- //private method to check heartbeat requirements and start or reset timer
- string checkHeartbeat(uint clientIndex, string received)
- {
- try
- {
- if (HeartbeatRequired)
- {
- if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
- {
- var remainingText = received.Replace(HeartbeatStringToMatch, "");
- var noDelimiter = received.Trim(new char[] { '\r', '\n' });
- if (noDelimiter.Contains(HeartbeatStringToMatch))
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
- else
- {
- CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
- HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
- }
- Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
- // Return Heartbeat
- SendTextToClient(HeartbeatStringToMatch, clientIndex);
- return remainingText;
- }
- }
- else
- {
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
- else
- {
- CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
- HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
- }
- Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
- }
- return received;
- }
-
- ///
- /// Gets the IP address based on the client index
- ///
- ///
- /// IP address of the client
- public string GetClientIPAddress(uint clientIndex)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
- if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
- {
- var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
- return ipa;
-
- }
- else
- {
- return "";
- }
- }
-
- #endregion
-
- #region Methods - HeartbeatTimer Callback
-
- void HeartbeatTimer_CallbackFunction(object o)
- {
- uint clientIndex = 99999;
- string address = string.Empty;
- try
- {
- clientIndex = (uint)o;
- address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
- address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
-
- if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
-
- var discoResult = myTcpServer.Disconnect(clientIndex);
- //Debug.Console(1, this, "{0}", discoResult);
-
- if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
- }
- }
- catch (Exception ex)
- {
- ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
- }
- }
-
- #endregion
-
- #region Methods - Socket Status Changed Callbacks
- ///
- /// Secure Server Socket Status Changed Callback
- ///
- ///
- ///
- ///
- void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
- {
- try
- {
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
- {
- if (ConnectedClientsIndexes.Contains(clientIndex))
- ConnectedClientsIndexes.Remove(clientIndex);
- if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary[clientIndex].Stop();
- HeartbeatTimerDictionary[clientIndex].Dispose();
- HeartbeatTimerDictionary.Remove(clientIndex);
- }
- if (ClientReadyAfterKeyExchange.Contains(clientIndex))
- ClientReadyAfterKeyExchange.Remove(clientIndex);
- if (WaitingForSharedKey.Contains(clientIndex))
- WaitingForSharedKey.Remove(clientIndex);
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
- }
- onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- }
-
- #endregion
-
- #region Methods Connected Callbacks
- ///
- /// Secure TCP Client Connected to Secure Server Callback
- ///
- ///
- ///
- void TcpConnectCallback(TCPServer server, uint clientIndex)
- {
- try
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
- server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
- clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
- if (clientIndex != 0)
- {
- if (server.ClientConnected(clientIndex))
- {
-
- if (!ConnectedClientsIndexes.Contains(clientIndex))
- {
- ConnectedClientsIndexes.Add(clientIndex);
- }
- if (SharedKeyRequired)
- {
- if (!WaitingForSharedKey.Contains(clientIndex))
- {
- WaitingForSharedKey.Add(clientIndex);
- }
- byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
- server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
- }
- else
- {
- OnServerClientReadyForCommunications(clientIndex);
- }
- if (HeartbeatRequired)
- {
- if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
- {
- HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
- }
- }
-
- server.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
- }
- }
- else
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
- if (!ServerStopped)
- {
- server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
- return;
- }
- }
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
- }
- //Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
- // server.State,
- // MaxClients,
- // ServerStopped);
- if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Waiting for next connection");
- server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
-
- }
- }
-
- #endregion
-
- #region Methods - Send/Receive Callbacks
- ///
- /// Secure Received Data Async Callback
- ///
- ///
- ///
- ///
- void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
- {
- if (numberOfBytesReceived > 0)
- {
- string received = "Nothing";
- try
- {
- 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(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
- myTCPServer.SendData(clientIndex, b, b.Length);
- myTCPServer.Disconnect(clientIndex);
- return;
- }
-
- WaitingForSharedKey.Remove(clientIndex);
- byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
- myTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
- OnServerClientReadyForCommunications(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);
- }
- catch (Exception ex)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
- }
- if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
- myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
- }
- else
- {
- // If numberOfBytesReceived <= 0
- myTCPServer.Disconnect();
- }
-
- }
-
- #endregion
-
- #region Methods - EventHelpers/Callbacks
-
- //Private Helper method to call the Connection Change Event
- void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
- {
- if (clientIndex != 0) //0 is error not valid client change
- {
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(myTcpServer, clientIndex, clientStatus));
- }
- }
- }
-
- //Private Helper method to call the Connection Change Event
- void OnConnectionChange()
- {
- if (ProgramIsStopping)
- {
- return;
- }
- var handler = ClientConnectionChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
- }
- }
-
- //Private Helper Method to call the Text Received Event
- void onTextReceived(string text, uint clientIndex)
- {
- var handler = TextReceived;
- if (handler != null)
- handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
- }
-
- //Private Helper Method to call the Server State Change Event
- void OnServerStateChange(ServerState state)
- {
- if (ProgramIsStopping)
- {
- return;
- }
- var handler = ServerStateChange;
- if (handler != null)
- {
- handler(this, new GenericTcpServerStateChangedEventArgs(state));
- }
- }
-
- ///
- /// Private Event Handler method to handle the closing of connections when the program stops
- ///
- ///
- void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- ProgramIsStopping = true;
- // kill bandaid things
- if (MonitorClientTimer != null)
- MonitorClientTimer.Stop();
- if (MonitorClient != null)
- MonitorClient.Disconnect();
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
- KillServer();
- }
- }
-
- //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
- void OnServerClientReadyForCommunications(uint clientIndex)
- {
- ClientReadyAfterKeyExchange.Add(clientIndex);
- var handler = ServerClientReadyForCommunications;
- if (handler != null)
- handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
- this, clientIndex, myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex)));
- }
- #endregion
-
- #region Monitor Client
- ///
- /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
- ///
- void StartMonitorClient()
- {
- if (MonitorClientTimer != null)
- {
- return;
- }
- MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
- }
-
- ///
- ///
- ///
- void RunMonitorClient()
- {
- MonitorClient = new GenericTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
- MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
- MonitorClient.SharedKey = this.SharedKey;
- MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
- //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
- MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
-
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
-
- MonitorClient.Connect();
- // From here MonitorCLient either connects or hangs, MonitorClient will call back
-
- }
-
- ///
- ///
- ///
- void StopMonitorClient()
- {
- if (MonitorClient == null)
- return;
-
- MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
- MonitorClient.Disconnect();
- MonitorClient = null;
- }
-
- ///
- /// On monitor connect, restart the operation
- ///
- void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
- {
- if (args.IsReady)
- {
- Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- MonitorClientFailureCount = 0;
- CrestronEnvironment.Sleep(2000);
- StopMonitorClient();
- StartMonitorClient();
- }
- }
-
- ///
- /// If the client hangs, add to counter and maybe fire the choke event
- ///
- void MonitorClientHasHungCallback()
- {
- MonitorClientFailureCount++;
- MonitorClientTimer.Stop();
- MonitorClientTimer = null;
- StopMonitorClient();
- if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
- MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
- StartMonitorClient();
- }
- else
- {
- Debug.Console(2, this, Debug.ErrorLogLevel.Error,
- "\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
- MonitorClientMaxFailureCount);
-
- var handler = ServerHasChoked;
- if (handler != null)
- handler();
- // Some external thing is in charge here. Expected reset of program
- }
- }
- #endregion
- }
+/*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 Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Generic TCP/IP server device
+ ///
+ 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;
+
+ ///
+ /// For a server with a pre shared key, this will fire after the communication is established and the key exchange is complete. If no shared key, this will fire
+ /// after connection is successful. Use this event to know when the client is ready for communication to avoid stepping on shared key.
+ ///
+ public event EventHandler ServerClientReadyForCommunications;
+
+ ///
+ /// A band aid event to notify user that the server has choked.
+ ///
+ public ServerHasChokedCallbackDelegate ServerHasChoked { get; set; }
+
+ ///
+ ///
+ ///
+ public delegate void ServerHasChokedCallbackDelegate();
+
+ #endregion
+
+ #region Properties/Variables
+
+ ///
+ ///
+ ///
+ CCriticalSection ServerCCSection = new CCriticalSection();
+
+
+ ///
+ /// A bandaid client that monitors whether the server is reachable
+ ///
+ GenericTcpIpClient_ForServer MonitorClient;
+
+ ///
+ /// Timer to operate the bandaid monitor client in a loop.
+ ///
+ CTimer MonitorClientTimer;
+
+ ///
+ ///
+ ///
+ int MonitorClientFailureCount;
+
+ ///
+ /// 3 by default
+ ///
+ public int MonitorClientMaxFailureCount { get; set; }
+
+ ///
+ /// Text representation of the Socket Status enum values for the server
+ ///
+ public string Status
+ {
+ get
+ {
+ if (myTcpServer != null)
+ return myTcpServer.State.ToString();
+ return ServerState.SERVER_NOT_LISTENING.ToString();
+
+ }
+
+ }
+
+ ///
+ /// Bool showing if socket is connected
+ ///
+ public bool IsConnected
+ {
+ get
+ {
+ if (myTcpServer != null)
+ return (myTcpServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED;
+ return false;
+
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_CONNECTED) == ServerState.SERVER_CONNECTED :
+ // (UnsecureServer.State & ServerState.SERVER_CONNECTED) == 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
+ {
+ if (myTcpServer != null)
+ return (myTcpServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING;
+ else
+ return false;
+ //return (Secure ? SecureServer != null : UnsecureServer != null) &&
+ //(Secure ? (SecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING :
+ // (UnsecureServer.State & ServerState.SERVER_LISTENING) == ServerState.SERVER_LISTENING);
+ }
+ }
+
+ ///
+ /// S+ helper for IsConnected
+ ///
+ public ushort UIsListening
+ {
+ get { return (ushort)(IsListening ? 1 : 0); }
+ }
+
+ ///
+ /// The maximum number of clients.
+ /// Should be set by parameter in SIMPL+ in the MAIN method, Should not ever need to be configurable
+ ///
+ public ushort MaxClients { get; set; }
+
+ ///
+ /// Number of clients currently connected.
+ ///
+ public ushort NumberOfClientsConnected
+ {
+ get
+ {
+ if (myTcpServer != null)
+ return (ushort)myTcpServer.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();
+
+ List ClientReadyAfterKeyExchange = new List();
+
+ ///
+ /// The connected client indexes
+ ///
+ public 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 myTcpServer;
+
+ ///
+ ///
+ ///
+ bool ProgramIsStopping;
+
+ #endregion
+
+ #region Constructors
+ ///
+ /// 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.
+ ///
+ public GenericTcpIpServer()
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ }
+
+ ///
+ /// constructor with debug key set at instantiation. Make sure to set all properties before listening.
+ ///
+ ///
+ public GenericTcpIpServer(string key)
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Key = key;
+ }
+
+ ///
+ /// Contstructor that sets all properties by calling the initialize method with a config object.
+ ///
+ ///
+ public GenericTcpIpServer(TcpServerConfigObject serverConfigObject)
+ : base("Uninitialized Dynamic TCP Server")
+ {
+ HeartbeatRequiredIntervalInSeconds = 15;
+ CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
+ BufferSize = 2000;
+ MonitorClientMaxFailureCount = 3;
+ Initialize(serverConfigObject);
+ }
+ #endregion
+
+ #region Methods - Server Actions
+ ///
+ /// Disconnects all clients and stops the server
+ ///
+ public void KillServer()
+ {
+ ServerStopped = true;
+ if (MonitorClient != null)
+ {
+ MonitorClient.Disconnect();
+ }
+ DisconnectAllClientsForShutdown();
+ StopListening();
+ }
+
+ ///
+ /// Initialize Key for device using client name from SIMPL+. Called on Listen from SIMPL+
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ Key = key;
+ }
+
+ ///
+ /// Initialze with server configuration object
+ ///
+ ///
+ public void Initialize(TcpServerConfigObject serverConfigObject)
+ {
+ try
+ {
+ if (serverConfigObject != null || string.IsNullOrEmpty(serverConfigObject.Key))
+ {
+ Key = serverConfigObject.Key;
+ MaxClients = serverConfigObject.MaxClients;
+ Port = serverConfigObject.Port;
+ SharedKeyRequired = serverConfigObject.SharedKeyRequired;
+ SharedKey = serverConfigObject.SharedKey;
+ HeartbeatRequired = serverConfigObject.HeartbeatRequired;
+ HeartbeatRequiredIntervalInSeconds = serverConfigObject.HeartbeatRequiredIntervalInSeconds;
+ HeartbeatStringToMatch = serverConfigObject.HeartbeatStringToMatch;
+ BufferSize = serverConfigObject.BufferSize;
+
+ }
+ else
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+ catch
+ {
+ ErrorLog.Error("Could not initialize server with key: {0}", serverConfigObject.Key);
+ }
+ }
+
+ ///
+ /// Start listening on the specified port
+ ///
+ public void Listen()
+ {
+ ServerCCSection.Enter();
+ try
+ {
+ if (Port < 1 || Port > 65535)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': Invalid port", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': Invalid port", Key));
+ return;
+ }
+ if (string.IsNullOrEmpty(SharedKey) && SharedKeyRequired)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Server '{0}': No Shared Key set", Key);
+ ErrorLog.Warn(string.Format("Server '{0}': No Shared Key set", Key));
+ return;
+ }
+ if (IsListening)
+ return;
+
+ if (myTcpServer == null)
+ {
+ myTcpServer = new TCPServer(Port, MaxClients);
+ if(HeartbeatRequired)
+ myTcpServer.SocketSendOrReceiveTimeOutInMs = (this.HeartbeatRequiredIntervalMs * 5);
+
+ // myTcpServer.HandshakeTimeout = 30;
+ }
+ else
+ {
+ KillServer();
+ myTcpServer.PortNumber = Port;
+ }
+
+ myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
+ myTcpServer.SocketStatusChange += TcpServer_SocketStatusChange;
+
+ ServerStopped = false;
+ myTcpServer.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+ OnServerStateChange(myTcpServer.State);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "TCP Server Status: {0}, Socket Status: {1}", myTcpServer.State, myTcpServer.ServerSocketStatus);
+
+ // StartMonitorClient();
+
+
+ ServerCCSection.Leave();
+ }
+ catch (Exception ex)
+ {
+ ServerCCSection.Leave();
+ ErrorLog.Error("{1} Error with Dynamic Server: {0}", ex.ToString(), Key);
+ }
+ }
+
+ ///
+ /// Stop Listening
+ ///
+ public void StopListening()
+ {
+ try
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Stopping Listener");
+ if (myTcpServer != null)
+ {
+ myTcpServer.Stop();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server State: {0}", myTcpServer.State);
+ OnServerStateChange(myTcpServer.State);
+ }
+ ServerStopped = true;
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error stopping server. Error: {0}", ex);
+ }
+ }
+
+ ///
+ /// Disconnects Client
+ ///
+ ///
+ public void DisconnectClient(uint client)
+ {
+ try
+ {
+ myTcpServer.Disconnect(client);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", client);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", client, ex);
+ }
+ }
+ ///
+ /// Disconnect All Clients
+ ///
+ public void DisconnectAllClientsForShutdown()
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Disconnecting All Clients");
+ if (myTcpServer != null)
+ {
+ myTcpServer.SocketStatusChange -= TcpServer_SocketStatusChange;
+ foreach (var index in ConnectedClientsIndexes.ToList()) // copy it here so that it iterates properly
+ {
+ var i = index;
+ if (!myTcpServer.ClientConnected(index))
+ continue;
+ try
+ {
+ myTcpServer.Disconnect(i);
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected client index: {0}", i);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Disconnecting client index: {0}. Error: {1}", i, ex);
+ }
+ }
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Server Status: {0}", myTcpServer.ServerSocketStatus);
+ }
+
+ Debug.Console(2, this, Debug.ErrorLogLevel.Notice, "Disconnected All Clients");
+ ConnectedClientsIndexes.Clear();
+
+ if (!ProgramIsStopping)
+ {
+ OnConnectionChange();
+ OnServerStateChange(myTcpServer.State); //State shows both listening and connected
+ }
+
+ // var o = new { };
+ }
+
+ ///
+ /// Broadcast text from server to all connected clients
+ ///
+ ///
+ public void BroadcastText(string text)
+ {
+ CCriticalSection CCBroadcast = new CCriticalSection();
+ CCBroadcast.Enter();
+ try
+ {
+ if (ConnectedClientsIndexes.Count > 0)
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ foreach (uint i in ConnectedClientsIndexes)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(i)))
+ {
+ SocketErrorCodes error = myTcpServer.SendDataAsync(i, b, b.Length, (x, y, z) => { });
+ if (error != SocketErrorCodes.SOCKET_OK && error != SocketErrorCodes.SOCKET_OPERATION_PENDING)
+ Debug.Console(2, error.ToString());
+ }
+ }
+ }
+ CCBroadcast.Leave();
+ }
+ catch (Exception ex)
+ {
+ CCBroadcast.Leave();
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Broadcasting messages from server. Error: {0}", ex.Message);
+ }
+ }
+
+ ///
+ /// Not sure this is useful in library, maybe Pro??
+ ///
+ ///
+ ///
+ public void SendTextToClient(string text, uint clientIndex)
+ {
+ try
+ {
+ byte[] b = Encoding.GetEncoding(28591).GetBytes(text);
+ if (myTcpServer != null && myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ myTcpServer.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, "Error sending text to client. Text: {1}. Error: {0}", ex.Message, text);
+ }
+ }
+
+ //private method to check heartbeat requirements and start or reset timer
+ string checkHeartbeat(uint clientIndex, string received)
+ {
+ try
+ {
+ if (HeartbeatRequired)
+ {
+ if (!string.IsNullOrEmpty(HeartbeatStringToMatch))
+ {
+ var remainingText = received.Replace(HeartbeatStringToMatch, "");
+ var noDelimiter = received.Trim(new char[] { '\r', '\n' });
+ if (noDelimiter.Contains(HeartbeatStringToMatch))
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", HeartbeatStringToMatch, clientIndex);
+ // Return Heartbeat
+ SendTextToClient(HeartbeatStringToMatch, clientIndex);
+ return remainingText;
+ }
+ }
+ else
+ {
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ HeartbeatTimerDictionary[clientIndex].Reset(HeartbeatRequiredIntervalMs);
+ else
+ {
+ CTimer HeartbeatTimer = new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs);
+ HeartbeatTimerDictionary.Add(clientIndex, HeartbeatTimer);
+ }
+ Debug.Console(1, this, "Heartbeat Received: {0}, from client index: {1}", received, clientIndex);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(1, this, "Error checking heartbeat: {0}", ex.Message);
+ }
+ return received;
+ }
+
+ ///
+ /// Gets the IP address based on the client index
+ ///
+ ///
+ /// IP address of the client
+ public string GetClientIPAddress(uint clientIndex)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress Index: {0}", clientIndex);
+ if (!SharedKeyRequired || (SharedKeyRequired && ClientReadyAfterKeyExchange.Contains(clientIndex)))
+ {
+ var ipa = this.myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "GetClientIPAddress IPAddreess: {0}", ipa);
+ return ipa;
+
+ }
+ else
+ {
+ return "";
+ }
+ }
+
+ #endregion
+
+ #region Methods - HeartbeatTimer Callback
+
+ void HeartbeatTimer_CallbackFunction(object o)
+ {
+ uint clientIndex = 99999;
+ string address = string.Empty;
+ try
+ {
+ clientIndex = (uint)o;
+ address = myTcpServer.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex);
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Warning, "Heartbeat not received for Client index {2} IP: {0}, DISCONNECTING BECAUSE HEARTBEAT REQUIRED IS TRUE {1}",
+ address, string.IsNullOrEmpty(HeartbeatStringToMatch) ? "" : ("HeartbeatStringToMatch: " + HeartbeatStringToMatch), clientIndex);
+
+ if (myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ SendTextToClient("Heartbeat not received by server, closing connection", clientIndex);
+
+ var discoResult = myTcpServer.Disconnect(clientIndex);
+ //Debug.Console(1, this, "{0}", discoResult);
+
+ if (HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ }
+ catch (Exception ex)
+ {
+ ErrorLog.Error("{3}: Heartbeat timeout Error on Client Index: {0}, at address: {1}, error: {2}", clientIndex, address, ex.Message, Key);
+ }
+ }
+
+ #endregion
+
+ #region Methods - Socket Status Changed Callbacks
+ ///
+ /// Secure Server Socket Status Changed Callback
+ ///
+ ///
+ ///
+ ///
+ void TcpServer_SocketStatusChange(TCPServer server, uint clientIndex, SocketStatus serverSocketStatus)
+ {
+ try
+ {
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "SecureServerSocketStatusChange Index:{0} status:{1} Port:{2} IP:{3}", clientIndex, serverSocketStatus, this.myTcpServer.GetPortNumberServerAcceptedConnectionFromForSpecificClient(clientIndex), this.myTcpServer.GetLocalAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ if (serverSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
+ {
+ if (ConnectedClientsIndexes.Contains(clientIndex))
+ ConnectedClientsIndexes.Remove(clientIndex);
+ if (HeartbeatRequired && HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary[clientIndex].Stop();
+ HeartbeatTimerDictionary[clientIndex].Dispose();
+ HeartbeatTimerDictionary.Remove(clientIndex);
+ }
+ if (ClientReadyAfterKeyExchange.Contains(clientIndex))
+ ClientReadyAfterKeyExchange.Remove(clientIndex);
+ if (WaitingForSharedKey.Contains(clientIndex))
+ WaitingForSharedKey.Remove(clientIndex);
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Change Callback. Error: {0}", ex);
+ }
+ onConnectionChange(clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ }
+
+ #endregion
+
+ #region Methods Connected Callbacks
+ ///
+ /// Secure TCP Client Connected to Secure Server Callback
+ ///
+ ///
+ ///
+ void TcpConnectCallback(TCPServer server, uint clientIndex)
+ {
+ try
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "ConnectCallback: IPAddress: {0}. Index: {1}. Status: {2}",
+ server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex),
+ clientIndex, server.GetServerSocketStatusForSpecificClient(clientIndex));
+ if (clientIndex != 0)
+ {
+ if (server.ClientConnected(clientIndex))
+ {
+
+ if (!ConnectedClientsIndexes.Contains(clientIndex))
+ {
+ ConnectedClientsIndexes.Add(clientIndex);
+ }
+ if (SharedKeyRequired)
+ {
+ if (!WaitingForSharedKey.Contains(clientIndex))
+ {
+ WaitingForSharedKey.Add(clientIndex);
+ }
+ byte[] b = Encoding.GetEncoding(28591).GetBytes("SharedKey:");
+ server.SendDataAsync(clientIndex, b, b.Length, (x, y, z) => { });
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Sent Shared Key Request to client at {0}", server.GetAddressServerAcceptedConnectionFromForSpecificClient(clientIndex));
+ }
+ else
+ {
+ OnServerClientReadyForCommunications(clientIndex);
+ }
+ if (HeartbeatRequired)
+ {
+ if (!HeartbeatTimerDictionary.ContainsKey(clientIndex))
+ {
+ HeartbeatTimerDictionary.Add(clientIndex, new CTimer(HeartbeatTimer_CallbackFunction, clientIndex, HeartbeatRequiredIntervalMs));
+ }
+ }
+
+ server.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
+ }
+ }
+ else
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Error, "Client attempt faulty.");
+ if (!ServerStopped)
+ {
+ server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+ return;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error in Socket Status Connect Callback. Error: {0}", ex);
+ }
+ //Debug.Console(1, this, Debug.ErrorLogLevel, "((((((Server State bitfield={0}; maxclient={1}; ServerStopped={2}))))))",
+ // server.State,
+ // MaxClients,
+ // ServerStopped);
+ if ((server.State & ServerState.SERVER_LISTENING) != ServerState.SERVER_LISTENING && MaxClients > 1 && !ServerStopped)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Waiting for next connection");
+ server.WaitForConnectionAsync(IPAddress.Any, TcpConnectCallback);
+
+ }
+ }
+
+ #endregion
+
+ #region Methods - Send/Receive Callbacks
+ ///
+ /// Secure Received Data Async Callback
+ ///
+ ///
+ ///
+ ///
+ void TcpServerReceivedDataAsyncCallback(TCPServer myTCPServer, uint clientIndex, int numberOfBytesReceived)
+ {
+ if (numberOfBytesReceived > 0)
+ {
+ string received = "Nothing";
+ try
+ {
+ 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(1, this, Debug.ErrorLogLevel.Warning, "Client at index {0} Shared key did not match the server, disconnecting client. Key: {1}", clientIndex, received);
+ myTCPServer.SendData(clientIndex, b, b.Length);
+ myTCPServer.Disconnect(clientIndex);
+ return;
+ }
+
+ WaitingForSharedKey.Remove(clientIndex);
+ byte[] success = Encoding.GetEncoding(28591).GetBytes("Shared Key Match");
+ myTCPServer.SendDataAsync(clientIndex, success, success.Length, null);
+ OnServerClientReadyForCommunications(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);
+ }
+ catch (Exception ex)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error, "Error Receiving data: {0}. Error: {1}", received, ex);
+ }
+ if (myTCPServer.GetServerSocketStatusForSpecificClient(clientIndex) == SocketStatus.SOCKET_STATUS_CONNECTED)
+ myTCPServer.ReceiveDataAsync(clientIndex, TcpServerReceivedDataAsyncCallback);
+ }
+ else
+ {
+ // If numberOfBytesReceived <= 0
+ myTCPServer.Disconnect();
+ }
+
+ }
+
+ #endregion
+
+ #region Methods - EventHelpers/Callbacks
+
+ //Private Helper method to call the Connection Change Event
+ void onConnectionChange(uint clientIndex, SocketStatus clientStatus)
+ {
+ if (clientIndex != 0) //0 is error not valid client change
+ {
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(myTcpServer, clientIndex, clientStatus));
+ }
+ }
+ }
+
+ //Private Helper method to call the Connection Change Event
+ void OnConnectionChange()
+ {
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ClientConnectionChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs());
+ }
+ }
+
+ //Private Helper Method to call the Text Received Event
+ void onTextReceived(string text, uint clientIndex)
+ {
+ var handler = TextReceived;
+ if (handler != null)
+ handler(this, new GenericTcpServerCommMethodReceiveTextArgs(text, clientIndex));
+ }
+
+ //Private Helper Method to call the Server State Change Event
+ void OnServerStateChange(ServerState state)
+ {
+ if (ProgramIsStopping)
+ {
+ return;
+ }
+ var handler = ServerStateChange;
+ if (handler != null)
+ {
+ handler(this, new GenericTcpServerStateChangedEventArgs(state));
+ }
+ }
+
+ ///
+ /// Private Event Handler method to handle the closing of connections when the program stops
+ ///
+ ///
+ void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ ProgramIsStopping = true;
+ // kill bandaid things
+ if (MonitorClientTimer != null)
+ MonitorClientTimer.Stop();
+ if (MonitorClient != null)
+ MonitorClient.Disconnect();
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Program stopping. Closing server");
+ KillServer();
+ }
+ }
+
+ //Private event handler method to raise the event that the server is ready to send data after a successful client shared key negotiation
+ void OnServerClientReadyForCommunications(uint clientIndex)
+ {
+ ClientReadyAfterKeyExchange.Add(clientIndex);
+ var handler = ServerClientReadyForCommunications;
+ if (handler != null)
+ handler(this, new GenericTcpServerSocketStatusChangeEventArgs(
+ this, clientIndex, myTcpServer.GetServerSocketStatusForSpecificClient(clientIndex)));
+ }
+ #endregion
+
+ #region Monitor Client
+ ///
+ /// Starts the monitor client cycle. Timed wait, then call RunMonitorClient
+ ///
+ void StartMonitorClient()
+ {
+ if (MonitorClientTimer != null)
+ {
+ return;
+ }
+ MonitorClientTimer = new CTimer(o => RunMonitorClient(), 60000);
+ }
+
+ ///
+ ///
+ ///
+ void RunMonitorClient()
+ {
+ MonitorClient = new GenericTcpIpClient_ForServer(Key + "-MONITOR", "127.0.0.1", Port, 2000);
+ MonitorClient.SharedKeyRequired = this.SharedKeyRequired;
+ MonitorClient.SharedKey = this.SharedKey;
+ MonitorClient.ConnectionHasHungCallback = MonitorClientHasHungCallback;
+ //MonitorClient.ConnectionChange += MonitorClient_ConnectionChange;
+ MonitorClient.ClientReadyForCommunications += MonitorClient_IsReadyForComm;
+
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Starting monitor check");
+
+ MonitorClient.Connect();
+ // From here MonitorCLient either connects or hangs, MonitorClient will call back
+
+ }
+
+ ///
+ ///
+ ///
+ void StopMonitorClient()
+ {
+ if (MonitorClient == null)
+ return;
+
+ MonitorClient.ClientReadyForCommunications -= MonitorClient_IsReadyForComm;
+ MonitorClient.Disconnect();
+ MonitorClient = null;
+ }
+
+ ///
+ /// On monitor connect, restart the operation
+ ///
+ void MonitorClient_IsReadyForComm(object sender, GenericTcpServerClientReadyForcommunicationsEventArgs args)
+ {
+ if (args.IsReady)
+ {
+ Debug.Console(1, this, Debug.ErrorLogLevel.Notice, "Monitor client connection success. Disconnecting in 2s");
+ MonitorClientTimer.Stop();
+ MonitorClientTimer = null;
+ MonitorClientFailureCount = 0;
+ CrestronEnvironment.Sleep(2000);
+ StopMonitorClient();
+ StartMonitorClient();
+ }
+ }
+
+ ///
+ /// If the client hangs, add to counter and maybe fire the choke event
+ ///
+ void MonitorClientHasHungCallback()
+ {
+ MonitorClientFailureCount++;
+ MonitorClientTimer.Stop();
+ MonitorClientTimer = null;
+ StopMonitorClient();
+ if (MonitorClientFailureCount < MonitorClientMaxFailureCount)
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Warning, "Monitor client connection has hung {0} time{1}, maximum {2}",
+ MonitorClientFailureCount, MonitorClientFailureCount > 1 ? "s" : "", MonitorClientMaxFailureCount);
+ StartMonitorClient();
+ }
+ else
+ {
+ Debug.Console(2, this, Debug.ErrorLogLevel.Error,
+ "\r***************************\rMonitor client connection has hung a maximum of {0} times.\r***************************",
+ MonitorClientMaxFailureCount);
+
+ var handler = ServerHasChoked;
+ if (handler != null)
+ handler();
+ // Some external thing is in charge here. Expected reset of program
+ }
+ }
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs b/src/Comm/GenericUdpServer.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/GenericUdpServer.cs
rename to src/Comm/GenericUdpServer.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs b/src/Comm/QscCoreDoubleTcpIpClient.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/QscCoreDoubleTcpIpClient.cs
rename to src/Comm/QscCoreDoubleTcpIpClient.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/TcpClientConfigObject.cs b/src/Comm/TcpClientConfigObject.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/TcpClientConfigObject.cs
rename to src/Comm/TcpClientConfigObject.cs
diff --git a/Pepperdash Core/Pepperdash Core/Comm/TcpServerConfigObject.cs b/src/Comm/TcpServerConfigObject.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Comm/TcpServerConfigObject.cs
rename to src/Comm/TcpServerConfigObject.cs
index 785851b..043cf58 100644
--- a/Pepperdash Core/Pepperdash Core/Comm/TcpServerConfigObject.cs
+++ b/src/Comm/TcpServerConfigObject.cs
@@ -1,60 +1,60 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core
-{
- ///
- /// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
- ///
- public class TcpServerConfigObject
- {
- ///
- /// Uique key
- ///
- public string Key { get; set; }
- ///
- /// Max Clients that the server will allow to connect.
- ///
- public ushort MaxClients { get; set; }
- ///
- /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
- ///
- public bool Secure { get; set; }
- ///
- /// Port for the server to listen on
- ///
- public int Port { get; set; }
- ///
- /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
- ///
- public bool SharedKeyRequired { get; set; }
- ///
- /// The shared key that must match on the server and client
- ///
- public string SharedKey { get; set; }
- ///
- /// 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.
- ///
- public bool HeartbeatRequired { get; set; }
- ///
- /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
- ///
- public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
- ///
- /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
- ///
- public string HeartbeatStringToMatch { get; set; }
- ///
- /// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
- ///
- public int BufferSize { get; set; }
- ///
- /// Receive Queue size must be greater than 20 or defaults to 20
- ///
- public int ReceiveQueueSize { get; set; }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Tcp Server Config object with properties for a tcp server with shared key and heartbeat capabilities
+ ///
+ public class TcpServerConfigObject
+ {
+ ///
+ /// Uique key
+ ///
+ public string Key { get; set; }
+ ///
+ /// Max Clients that the server will allow to connect.
+ ///
+ public ushort MaxClients { get; set; }
+ ///
+ /// Bool value for secure. Currently not implemented in TCP sockets as they are not dynamic
+ ///
+ public bool Secure { get; set; }
+ ///
+ /// Port for the server to listen on
+ ///
+ public int Port { get; set; }
+ ///
+ /// Require a shared key that both server and client negotiate. If negotiation fails server disconnects the client
+ ///
+ public bool SharedKeyRequired { get; set; }
+ ///
+ /// The shared key that must match on the server and client
+ ///
+ public string SharedKey { get; set; }
+ ///
+ /// 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.
+ ///
+ public bool HeartbeatRequired { get; set; }
+ ///
+ /// The interval in seconds for the heartbeat from the client. If not received client is disconnected
+ ///
+ public ushort HeartbeatRequiredIntervalInSeconds { get; set; }
+ ///
+ /// HeartbeatString that will be checked against the message received. defaults to heartbeat if no string is provided.
+ ///
+ public string HeartbeatStringToMatch { get; set; }
+ ///
+ /// Client buffer size. See Crestron help. defaults to 2000 if not greater than 2000
+ ///
+ public int BufferSize { get; set; }
+ ///
+ /// Receive Queue size must be greater than 20 or defaults to 20
+ ///
+ public int ReceiveQueueSize { get; set; }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs b/src/Comm/eControlMethods.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Comm/eControlMethods.cs
rename to src/Comm/eControlMethods.cs
diff --git a/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs b/src/CommunicationExtras.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/CommunicationExtras.cs
rename to src/CommunicationExtras.cs
index 353b14f..d588e47 100644
--- a/Pepperdash Core/Pepperdash Core/CommunicationExtras.cs
+++ b/src/CommunicationExtras.cs
@@ -1,242 +1,242 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronSockets;
-using System.Text.RegularExpressions;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core
-{
- ///
- /// An incoming communication stream
- ///
- public interface ICommunicationReceiver : IKeyed
- {
- ///
- /// Notifies of bytes received
- ///
- event EventHandler BytesReceived;
- ///
- /// Notifies of text received
- ///
- event EventHandler TextReceived;
-
- ///
- /// Indicates connection status
- ///
- bool IsConnected { get; }
- ///
- /// Connect to the device
- ///
- void Connect();
- ///
- /// Disconnect from the device
- ///
- void Disconnect();
- }
-
- ///
- /// Represents a device that uses basic connection
- ///
- public interface IBasicCommunication : ICommunicationReceiver
- {
- ///
- /// Send text to the device
- ///
- ///
- void SendText(string text);
-
- ///
- /// Send bytes to the device
- ///
- ///
- void SendBytes(byte[] bytes);
- }
-
- ///
- /// Represents a device that implements IBasicCommunication and IStreamDebugging
- ///
- public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
- {
-
- }
-
- ///
- /// Represents a device with stream debugging capablities
- ///
- public interface IStreamDebugging
- {
- ///
- /// Object to enable stream debugging
- ///
- CommunicationStreamDebugging StreamDebugging { get; }
- }
-
- ///
- /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
- /// GenericTcpIpClient
- ///
- public interface ISocketStatus : IBasicCommunication
- {
- ///
- /// Notifies of socket status changes
- ///
- event EventHandler ConnectionChange;
-
- ///
- /// The current socket status of the client
- ///
- SocketStatus ClientStatus { get; }
- }
-
- ///
- /// Describes a device that implements ISocketStatus and IStreamDebugging
- ///
- public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
- {
-
- }
-
- ///
- /// Describes a device that can automatically attempt to reconnect
- ///
- public interface IAutoReconnect
- {
- ///
- /// Enable automatic recconnect
- ///
- bool AutoReconnect { get; set; }
- ///
- /// Interval in ms to attempt automatic recconnections
- ///
- int AutoReconnectIntervalMs { get; set; }
- }
-
- ///
- ///
- ///
- public enum eGenericCommMethodStatusChangeType
- {
- ///
- /// Connected
- ///
- Connected,
- ///
- /// Disconnected
- ///
- Disconnected
- }
-
- ///
- /// This delegate defines handler for IBasicCommunication status changes
- ///
- /// Device firing the status change
- ///
- public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
-
- ///
- ///
- ///
- public class GenericCommMethodReceiveBytesArgs : EventArgs
- {
- ///
- ///
- ///
- public byte[] Bytes { get; private set; }
-
- ///
- ///
- ///
- ///
- public GenericCommMethodReceiveBytesArgs(byte[] bytes)
- {
- Bytes = bytes;
- }
-
- ///
- /// S+ Constructor
- ///
- public GenericCommMethodReceiveBytesArgs() { }
- }
-
- ///
- ///
- ///
- public class GenericCommMethodReceiveTextArgs : EventArgs
- {
- ///
- ///
- ///
- public string Text { get; private set; }
- ///
- ///
- ///
- public string Delimiter { get; private set; }
- ///
- ///
- ///
- ///
- public GenericCommMethodReceiveTextArgs(string text)
- {
- Text = text;
- }
-
- ///
- ///
- ///
- ///
- ///
- public GenericCommMethodReceiveTextArgs(string text, string delimiter)
- :this(text)
- {
- Delimiter = delimiter;
- }
-
- ///
- /// S+ Constructor
- ///
- public GenericCommMethodReceiveTextArgs() { }
- }
-
-
-
- ///
- ///
- ///
- public class ComTextHelper
- {
- ///
- /// Gets escaped text for a byte array
- ///
- ///
- ///
- public static string GetEscapedText(byte[] bytes)
- {
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets escaped text for a string
- ///
- ///
- ///
- public static string GetEscapedText(string text)
- {
- var bytes = Encoding.GetEncoding(28591).GetBytes(text);
- return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
- }
-
- ///
- /// Gets debug text for a string
- ///
- ///
- ///
- public static string GetDebugText(string text)
- {
- return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronSockets;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core
+{
+ ///
+ /// An incoming communication stream
+ ///
+ public interface ICommunicationReceiver : IKeyed
+ {
+ ///
+ /// Notifies of bytes received
+ ///
+ event EventHandler BytesReceived;
+ ///
+ /// Notifies of text received
+ ///
+ event EventHandler TextReceived;
+
+ ///
+ /// Indicates connection status
+ ///
+ bool IsConnected { get; }
+ ///
+ /// Connect to the device
+ ///
+ void Connect();
+ ///
+ /// Disconnect from the device
+ ///
+ void Disconnect();
+ }
+
+ ///
+ /// Represents a device that uses basic connection
+ ///
+ public interface IBasicCommunication : ICommunicationReceiver
+ {
+ ///
+ /// Send text to the device
+ ///
+ ///
+ void SendText(string text);
+
+ ///
+ /// Send bytes to the device
+ ///
+ ///
+ void SendBytes(byte[] bytes);
+ }
+
+ ///
+ /// Represents a device that implements IBasicCommunication and IStreamDebugging
+ ///
+ public interface IBasicCommunicationWithStreamDebugging : IBasicCommunication, IStreamDebugging
+ {
+
+ }
+
+ ///
+ /// Represents a device with stream debugging capablities
+ ///
+ public interface IStreamDebugging
+ {
+ ///
+ /// Object to enable stream debugging
+ ///
+ CommunicationStreamDebugging StreamDebugging { get; }
+ }
+
+ ///
+ /// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
+ /// GenericTcpIpClient
+ ///
+ public interface ISocketStatus : IBasicCommunication
+ {
+ ///
+ /// Notifies of socket status changes
+ ///
+ event EventHandler ConnectionChange;
+
+ ///
+ /// The current socket status of the client
+ ///
+ SocketStatus ClientStatus { get; }
+ }
+
+ ///
+ /// Describes a device that implements ISocketStatus and IStreamDebugging
+ ///
+ public interface ISocketStatusWithStreamDebugging : ISocketStatus, IStreamDebugging
+ {
+
+ }
+
+ ///
+ /// Describes a device that can automatically attempt to reconnect
+ ///
+ public interface IAutoReconnect
+ {
+ ///
+ /// Enable automatic recconnect
+ ///
+ bool AutoReconnect { get; set; }
+ ///
+ /// Interval in ms to attempt automatic recconnections
+ ///
+ int AutoReconnectIntervalMs { get; set; }
+ }
+
+ ///
+ ///
+ ///
+ public enum eGenericCommMethodStatusChangeType
+ {
+ ///
+ /// Connected
+ ///
+ Connected,
+ ///
+ /// Disconnected
+ ///
+ Disconnected
+ }
+
+ ///
+ /// This delegate defines handler for IBasicCommunication status changes
+ ///
+ /// Device firing the status change
+ ///
+ public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
+
+ ///
+ ///
+ ///
+ public class GenericCommMethodReceiveBytesArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public byte[] Bytes { get; private set; }
+
+ ///
+ ///
+ ///
+ ///
+ public GenericCommMethodReceiveBytesArgs(byte[] bytes)
+ {
+ Bytes = bytes;
+ }
+
+ ///
+ /// S+ Constructor
+ ///
+ public GenericCommMethodReceiveBytesArgs() { }
+ }
+
+ ///
+ ///
+ ///
+ public class GenericCommMethodReceiveTextArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public string Text { get; private set; }
+ ///
+ ///
+ ///
+ public string Delimiter { get; private set; }
+ ///
+ ///
+ ///
+ ///
+ public GenericCommMethodReceiveTextArgs(string text)
+ {
+ Text = text;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public GenericCommMethodReceiveTextArgs(string text, string delimiter)
+ :this(text)
+ {
+ Delimiter = delimiter;
+ }
+
+ ///
+ /// S+ Constructor
+ ///
+ public GenericCommMethodReceiveTextArgs() { }
+ }
+
+
+
+ ///
+ ///
+ ///
+ public class ComTextHelper
+ {
+ ///
+ /// Gets escaped text for a byte array
+ ///
+ ///
+ ///
+ public static string GetEscapedText(byte[] bytes)
+ {
+ return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets escaped text for a string
+ ///
+ ///
+ ///
+ public static string GetEscapedText(string text)
+ {
+ var bytes = Encoding.GetEncoding(28591).GetBytes(text);
+ return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
+ }
+
+ ///
+ /// Gets debug text for a string
+ ///
+ ///
+ ///
+ public static string GetDebugText(string text)
+ {
+ return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Config/PortalConfigReader.cs b/src/Config/PortalConfigReader.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Config/PortalConfigReader.cs
rename to src/Config/PortalConfigReader.cs
diff --git a/Pepperdash Core/Pepperdash Core/Conversion/Convert.cs b/src/Conversion/Convert.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Conversion/Convert.cs
rename to src/Conversion/Convert.cs
diff --git a/Pepperdash Core/Pepperdash Core/CoreInterfaces.cs b/src/CoreInterfaces.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/CoreInterfaces.cs
rename to src/CoreInterfaces.cs
diff --git a/Pepperdash Core/Pepperdash Core/Device.cs b/src/Device.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Device.cs
rename to src/Device.cs
index c889dd3..55d4c15 100644
--- a/Pepperdash Core/Pepperdash Core/Device.cs
+++ b/src/Device.cs
@@ -1,162 +1,162 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-
-namespace PepperDash.Core
-{
- //*********************************************************************************************************
- ///
- /// The core event and status-bearing class that most if not all device and connectors can derive from.
- ///
- public class Device : IKeyName
- {
- ///
- /// Unique Key
- ///
- public string Key { get; protected set; }
- ///
- /// Name of the devie
- ///
- public string Name { get; protected set; }
- ///
- ///
- ///
- public bool Enabled { get; protected set; }
-
- /////
- ///// A place to store reference to the original config object, if any. These values should
- ///// NOT be used as properties on the device as they are all publicly-settable values.
- /////
- //public DeviceConfig Config { get; private set; }
- /////
- ///// Helper method to check if Config exists
- /////
- //public bool HasConfig { get { return Config != null; } }
-
- List _PreActivationActions;
- List _PostActivationActions;
-
- ///
- ///
- ///
- public static Device DefaultDevice { get { return _DefaultDevice; } }
- static Device _DefaultDevice = new Device("Default", "Default");
-
- ///
- /// Base constructor for all Devices.
- ///
- ///
- public Device(string key)
- {
- Key = key;
- if (key.Contains('.')) Debug.Console(0, this, "WARNING: Device name's should not include '.'");
- Name = "";
-
- }
-
- ///
- /// Constructor with key and name
- ///
- ///
- ///
- public Device(string key, string name) : this(key)
- {
- Name = name;
-
- }
-
- //public Device(DeviceConfig config)
- // : this(config.Key, config.Name)
- //{
- // Config = config;
- //}
-
- ///
- /// Adds a pre activation action
- ///
- ///
- public void AddPreActivationAction(Action act)
- {
- if (_PreActivationActions == null)
- _PreActivationActions = new List();
- _PreActivationActions.Add(act);
- }
-
- ///
- /// Adds a post activation action
- ///
- ///
- public void AddPostActivationAction(Action act)
- {
- if (_PostActivationActions == null)
- _PostActivationActions = new List();
- _PostActivationActions.Add(act);
- }
-
- ///
- /// Executes the preactivation actions
- ///
- public void PreActivate()
- {
- if (_PreActivationActions != null)
- _PreActivationActions.ForEach(a => a.Invoke());
- }
-
- ///
- /// Gets this device ready to be used in the system. Runs any added pre-activation items, and
- /// all post-activation at end. Classes needing additional logic to
- /// run should override CustomActivate()
- ///
- public bool Activate()
- {
- //if (_PreActivationActions != null)
- // _PreActivationActions.ForEach(a => a.Invoke());
- var result = CustomActivate();
- //if(result && _PostActivationActions != null)
- // _PostActivationActions.ForEach(a => a.Invoke());
- return result;
- }
-
- ///
- /// Executes the postactivation actions
- ///
- public void PostActivate()
- {
- if (_PostActivationActions != null)
- _PostActivationActions.ForEach(a => a.Invoke());
- }
-
- ///
- /// Called in between Pre and PostActivationActions when Activate() is called.
- /// Override to provide addtitional setup when calling activation. Overriding classes
- /// do not need to call base.CustomActivate()
- ///
- /// true if device activated successfully.
- public virtual bool CustomActivate() { return true; }
-
- ///
- /// Call to deactivate device - unlink events, etc. Overriding classes do not
- /// need to call base.Deactivate()
- ///
- ///
- public virtual bool Deactivate() { return true; }
-
- ///
- /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
- ///
- public virtual void Initialize()
- {
- }
-
- ///
- /// Helper method to check object for bool value false and fire an Action method
- ///
- /// Should be of type bool, others will be ignored
- /// Action to be run when o is false
- public void OnFalse(object o, Action a)
- {
- if (o is bool && !(bool)o) a();
- }
-
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PepperDash.Core
+{
+ //*********************************************************************************************************
+ ///
+ /// The core event and status-bearing class that most if not all device and connectors can derive from.
+ ///
+ public class Device : IKeyName
+ {
+ ///
+ /// Unique Key
+ ///
+ public string Key { get; protected set; }
+ ///
+ /// Name of the devie
+ ///
+ public string Name { get; protected set; }
+ ///
+ ///
+ ///
+ public bool Enabled { get; protected set; }
+
+ /////
+ ///// A place to store reference to the original config object, if any. These values should
+ ///// NOT be used as properties on the device as they are all publicly-settable values.
+ /////
+ //public DeviceConfig Config { get; private set; }
+ /////
+ ///// Helper method to check if Config exists
+ /////
+ //public bool HasConfig { get { return Config != null; } }
+
+ List _PreActivationActions;
+ List _PostActivationActions;
+
+ ///
+ ///
+ ///
+ public static Device DefaultDevice { get { return _DefaultDevice; } }
+ static Device _DefaultDevice = new Device("Default", "Default");
+
+ ///
+ /// Base constructor for all Devices.
+ ///
+ ///
+ public Device(string key)
+ {
+ Key = key;
+ if (key.Contains('.')) Debug.Console(0, this, "WARNING: Device name's should not include '.'");
+ Name = "";
+
+ }
+
+ ///
+ /// Constructor with key and name
+ ///
+ ///
+ ///
+ public Device(string key, string name) : this(key)
+ {
+ Name = name;
+
+ }
+
+ //public Device(DeviceConfig config)
+ // : this(config.Key, config.Name)
+ //{
+ // Config = config;
+ //}
+
+ ///
+ /// Adds a pre activation action
+ ///
+ ///
+ public void AddPreActivationAction(Action act)
+ {
+ if (_PreActivationActions == null)
+ _PreActivationActions = new List();
+ _PreActivationActions.Add(act);
+ }
+
+ ///
+ /// Adds a post activation action
+ ///
+ ///
+ public void AddPostActivationAction(Action act)
+ {
+ if (_PostActivationActions == null)
+ _PostActivationActions = new List();
+ _PostActivationActions.Add(act);
+ }
+
+ ///
+ /// Executes the preactivation actions
+ ///
+ public void PreActivate()
+ {
+ if (_PreActivationActions != null)
+ _PreActivationActions.ForEach(a => a.Invoke());
+ }
+
+ ///
+ /// Gets this device ready to be used in the system. Runs any added pre-activation items, and
+ /// all post-activation at end. Classes needing additional logic to
+ /// run should override CustomActivate()
+ ///
+ public bool Activate()
+ {
+ //if (_PreActivationActions != null)
+ // _PreActivationActions.ForEach(a => a.Invoke());
+ var result = CustomActivate();
+ //if(result && _PostActivationActions != null)
+ // _PostActivationActions.ForEach(a => a.Invoke());
+ return result;
+ }
+
+ ///
+ /// Executes the postactivation actions
+ ///
+ public void PostActivate()
+ {
+ if (_PostActivationActions != null)
+ _PostActivationActions.ForEach(a => a.Invoke());
+ }
+
+ ///
+ /// Called in between Pre and PostActivationActions when Activate() is called.
+ /// Override to provide addtitional setup when calling activation. Overriding classes
+ /// do not need to call base.CustomActivate()
+ ///
+ /// true if device activated successfully.
+ public virtual bool CustomActivate() { return true; }
+
+ ///
+ /// Call to deactivate device - unlink events, etc. Overriding classes do not
+ /// need to call base.Deactivate()
+ ///
+ ///
+ public virtual bool Deactivate() { return true; }
+
+ ///
+ /// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
+ ///
+ public virtual void Initialize()
+ {
+ }
+
+ ///
+ /// Helper method to check object for bool value false and fire an Action method
+ ///
+ /// Should be of type bool, others will be ignored
+ /// Action to be run when o is false
+ public void OnFalse(object o, Action a)
+ {
+ if (o is bool && !(bool)o) a();
+ }
+
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/EthernetHelper.cs b/src/EthernetHelper.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/EthernetHelper.cs
rename to src/EthernetHelper.cs
diff --git a/Pepperdash Core/Pepperdash Core/EventArgs.cs b/src/EventArgs.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/EventArgs.cs
rename to src/EventArgs.cs
index 6def673..29ef13a 100644
--- a/Pepperdash Core/Pepperdash Core/EventArgs.cs
+++ b/src/EventArgs.cs
@@ -1,172 +1,172 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core
-{
- ///
- /// Bool change event args
- ///
- public class BoolChangeEventArgs : EventArgs
- {
- ///
- /// Boolean state property
- ///
- public bool State { get; set; }
-
- ///
- /// Boolean ushort value property
- ///
- public ushort IntValue { get { return (ushort)(State ? 1 : 0); } }
-
- ///
- /// Boolean change event args type
- ///
- public ushort Type { get; set; }
-
- ///
- /// Boolean change event args index
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public BoolChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public BoolChangeEventArgs(bool state, ushort type)
- {
- State = state;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public BoolChangeEventArgs(bool state, ushort type, ushort index)
- {
- State = state;
- Type = type;
- Index = index;
- }
- }
-
- ///
- /// Ushort change event args
- ///
- public class UshrtChangeEventArgs : EventArgs
- {
- ///
- /// Ushort change event args integer value
- ///
- public ushort IntValue { get; set; }
-
- ///
- /// Ushort change event args type
- ///
- public ushort Type { get; set; }
-
- ///
- /// Ushort change event args index
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public UshrtChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public UshrtChangeEventArgs(ushort intValue, ushort type)
- {
- IntValue = intValue;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public UshrtChangeEventArgs(ushort intValue, ushort type, ushort index)
- {
- IntValue = intValue;
- Type = type;
- Index = index;
- }
- }
-
- ///
- /// String change event args
- ///
- public class StringChangeEventArgs : EventArgs
- {
- ///
- /// String change event args value
- ///
- public string StringValue { get; set; }
-
- ///
- /// String change event args type
- ///
- public ushort Type { get; set; }
-
- ///
- /// string change event args index
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public StringChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public StringChangeEventArgs(string stringValue, ushort type)
- {
- StringValue = stringValue;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public StringChangeEventArgs(string stringValue, ushort type, ushort index)
- {
- StringValue = stringValue;
- Type = type;
- Index = index;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core
+{
+ ///
+ /// Bool change event args
+ ///
+ public class BoolChangeEventArgs : EventArgs
+ {
+ ///
+ /// Boolean state property
+ ///
+ public bool State { get; set; }
+
+ ///
+ /// Boolean ushort value property
+ ///
+ public ushort IntValue { get { return (ushort)(State ? 1 : 0); } }
+
+ ///
+ /// Boolean change event args type
+ ///
+ public ushort Type { get; set; }
+
+ ///
+ /// Boolean change event args index
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public BoolChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public BoolChangeEventArgs(bool state, ushort type)
+ {
+ State = state;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public BoolChangeEventArgs(bool state, ushort type, ushort index)
+ {
+ State = state;
+ Type = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// Ushort change event args
+ ///
+ public class UshrtChangeEventArgs : EventArgs
+ {
+ ///
+ /// Ushort change event args integer value
+ ///
+ public ushort IntValue { get; set; }
+
+ ///
+ /// Ushort change event args type
+ ///
+ public ushort Type { get; set; }
+
+ ///
+ /// Ushort change event args index
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public UshrtChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public UshrtChangeEventArgs(ushort intValue, ushort type)
+ {
+ IntValue = intValue;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public UshrtChangeEventArgs(ushort intValue, ushort type, ushort index)
+ {
+ IntValue = intValue;
+ Type = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// String change event args
+ ///
+ public class StringChangeEventArgs : EventArgs
+ {
+ ///
+ /// String change event args value
+ ///
+ public string StringValue { get; set; }
+
+ ///
+ /// String change event args type
+ ///
+ public ushort Type { get; set; }
+
+ ///
+ /// string change event args index
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public StringChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public StringChangeEventArgs(string stringValue, ushort type)
+ {
+ StringValue = stringValue;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public StringChangeEventArgs(string stringValue, ushort type, ushort index)
+ {
+ StringValue = stringValue;
+ Type = type;
+ Index = index;
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/Constants.cs b/src/GenericRESTfulCommunications/Constants.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/Constants.cs
rename to src/GenericRESTfulCommunications/Constants.cs
index 9df43f0..1b78c33 100644
--- a/Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/Constants.cs
+++ b/src/GenericRESTfulCommunications/Constants.cs
@@ -1,39 +1,39 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.GenericRESTfulCommunications
-{
- ///
- /// Constants
- ///
- public class GenericRESTfulConstants
- {
- ///
- /// Generic boolean change
- ///
- public const ushort BoolValueChange = 1;
- ///
- /// Generic Ushort change
- ///
- public const ushort UshrtValueChange = 101;
- ///
- /// Response Code Ushort change
- ///
- public const ushort ResponseCodeChange = 102;
- ///
- /// Generic String chagne
- ///
- public const ushort StringValueChange = 201;
- ///
- /// Response string change
- ///
- public const ushort ResponseStringChange = 202;
- ///
- /// Error string change
- ///
- public const ushort ErrorStringChange = 203;
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.GenericRESTfulCommunications
+{
+ ///
+ /// Constants
+ ///
+ public class GenericRESTfulConstants
+ {
+ ///
+ /// Generic boolean change
+ ///
+ public const ushort BoolValueChange = 1;
+ ///
+ /// Generic Ushort change
+ ///
+ public const ushort UshrtValueChange = 101;
+ ///
+ /// Response Code Ushort change
+ ///
+ public const ushort ResponseCodeChange = 102;
+ ///
+ /// Generic String chagne
+ ///
+ public const ushort StringValueChange = 201;
+ ///
+ /// Response string change
+ ///
+ public const ushort ResponseStringChange = 202;
+ ///
+ /// Error string change
+ ///
+ public const ushort ErrorStringChange = 203;
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/GenericRESTfulClient.cs b/src/GenericRESTfulCommunications/GenericRESTfulClient.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/GenericRESTfulClient.cs
rename to src/GenericRESTfulCommunications/GenericRESTfulClient.cs
index c1db1aa..bd33e13 100644
--- a/Pepperdash Core/Pepperdash Core/GenericRESTfulCommunications/GenericRESTfulClient.cs
+++ b/src/GenericRESTfulCommunications/GenericRESTfulClient.cs
@@ -1,256 +1,256 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.Net.Http;
-using Crestron.SimplSharp.Net.Https;
-
-namespace PepperDash.Core.GenericRESTfulCommunications
-{
- ///
- /// Generic RESTful communication class
- ///
- public class GenericRESTfulClient
- {
- ///
- /// Boolean event handler
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event handler
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event handler
- ///
- public event EventHandler StringChange;
-
- ///
- /// Constructor
- ///
- public GenericRESTfulClient()
- {
-
- }
-
- ///
- /// Generic RESTful submit request
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public void SubmitRequest(string url, ushort port, ushort requestType, string contentType, string username, string password)
- {
- if (url.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
- {
- SubmitRequestHttps(url, port, requestType, contentType, username, password);
- }
- else if (url.StartsWith("http:", StringComparison.OrdinalIgnoreCase))
- {
- SubmitRequestHttp(url, port, requestType, contentType, username, password);
- }
- else
- {
- OnStringChange(string.Format("Invalid URL {0}", url), 0, GenericRESTfulConstants.ErrorStringChange);
- }
- }
-
- ///
- /// Private HTTP submit request
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private void SubmitRequestHttp(string url, ushort port, ushort requestType, string contentType, string username, string password)
- {
- try
- {
- HttpClient client = new HttpClient();
- HttpClientRequest request = new HttpClientRequest();
- HttpClientResponse response;
-
- client.KeepAlive = false;
-
- if(port >= 1 || port <= 65535)
- client.Port = port;
- else
- client.Port = 80;
-
- var authorization = "";
- if (!string.IsNullOrEmpty(username))
- authorization = EncodeBase64(username, password);
-
- if (!string.IsNullOrEmpty(authorization))
- request.Header.SetHeaderValue("Authorization", authorization);
-
- if (!string.IsNullOrEmpty(contentType))
- request.Header.ContentType = contentType;
-
- request.Url.Parse(url);
- request.RequestType = (Crestron.SimplSharp.Net.Http.RequestType)requestType;
-
- response = client.Dispatch(request);
-
- CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
-
- if (!string.IsNullOrEmpty(response.ContentString.ToString()))
- OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
-
- if (response.Code > 0)
- OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
- }
- catch (Exception e)
- {
- //var msg = string.Format("SubmitRequestHttp({0}, {1}, {2}) failed:{3}", url, port, requestType, e.Message);
- //CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
-
- CrestronConsole.PrintLine(e.Message);
- OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
- }
- }
-
- ///
- /// Private HTTPS submit request
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- private void SubmitRequestHttps(string url, ushort port, ushort requestType, string contentType, string username, string password)
- {
- try
- {
- HttpsClient client = new HttpsClient();
- HttpsClientRequest request = new HttpsClientRequest();
- HttpsClientResponse response;
-
- client.KeepAlive = false;
- client.HostVerification = false;
- client.PeerVerification = false;
-
- var authorization = "";
- if (!string.IsNullOrEmpty(username))
- authorization = EncodeBase64(username, password);
-
- if (!string.IsNullOrEmpty(authorization))
- request.Header.SetHeaderValue("Authorization", authorization);
-
- if (!string.IsNullOrEmpty(contentType))
- request.Header.ContentType = contentType;
-
- request.Url.Parse(url);
- request.RequestType = (Crestron.SimplSharp.Net.Https.RequestType)requestType;
-
- response = client.Dispatch(request);
-
- CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
-
- if(!string.IsNullOrEmpty(response.ContentString.ToString()))
- OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
-
- if(response.Code > 0)
- OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
-
- }
- catch (Exception e)
- {
- //var msg = string.Format("SubmitRequestHttps({0}, {1}, {2}, {3}, {4}) failed:{5}", url, port, requestType, username, password, e.Message);
- //CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
-
- CrestronConsole.PrintLine(e.Message);
- OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
- }
- }
-
- ///
- /// Private method to encode username and password to Base64 string
- ///
- ///
- ///
- /// authorization
- private string EncodeBase64(string username, string password)
- {
- var authorization = "";
-
- try
- {
- if (!string.IsNullOrEmpty(username))
- {
- string base64String = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(string.Format("{0}:{1}", username, password)));
- authorization = string.Format("Basic {0}", base64String);
- }
- }
- catch (Exception e)
- {
- var msg = string.Format("EncodeBase64({0}, {1}) failed:\r{2}", username, password, e);
- CrestronConsole.PrintLine(msg);
- ErrorLog.Error(msg);
- return "" ;
- }
-
- return authorization;
- }
-
- ///
- /// Protected method to handle boolean change events
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected mehtod to handle ushort change events
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// Protected method to handle string change events
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Net.Http;
+using Crestron.SimplSharp.Net.Https;
+
+namespace PepperDash.Core.GenericRESTfulCommunications
+{
+ ///
+ /// Generic RESTful communication class
+ ///
+ public class GenericRESTfulClient
+ {
+ ///
+ /// Boolean event handler
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event handler
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event handler
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Constructor
+ ///
+ public GenericRESTfulClient()
+ {
+
+ }
+
+ ///
+ /// Generic RESTful submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SubmitRequest(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ if (url.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
+ {
+ SubmitRequestHttps(url, port, requestType, contentType, username, password);
+ }
+ else if (url.StartsWith("http:", StringComparison.OrdinalIgnoreCase))
+ {
+ SubmitRequestHttp(url, port, requestType, contentType, username, password);
+ }
+ else
+ {
+ OnStringChange(string.Format("Invalid URL {0}", url), 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private HTTP submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void SubmitRequestHttp(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ try
+ {
+ HttpClient client = new HttpClient();
+ HttpClientRequest request = new HttpClientRequest();
+ HttpClientResponse response;
+
+ client.KeepAlive = false;
+
+ if(port >= 1 || port <= 65535)
+ client.Port = port;
+ else
+ client.Port = 80;
+
+ var authorization = "";
+ if (!string.IsNullOrEmpty(username))
+ authorization = EncodeBase64(username, password);
+
+ if (!string.IsNullOrEmpty(authorization))
+ request.Header.SetHeaderValue("Authorization", authorization);
+
+ if (!string.IsNullOrEmpty(contentType))
+ request.Header.ContentType = contentType;
+
+ request.Url.Parse(url);
+ request.RequestType = (Crestron.SimplSharp.Net.Http.RequestType)requestType;
+
+ response = client.Dispatch(request);
+
+ CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
+
+ if (!string.IsNullOrEmpty(response.ContentString.ToString()))
+ OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
+
+ if (response.Code > 0)
+ OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
+ }
+ catch (Exception e)
+ {
+ //var msg = string.Format("SubmitRequestHttp({0}, {1}, {2}) failed:{3}", url, port, requestType, e.Message);
+ //CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+
+ CrestronConsole.PrintLine(e.Message);
+ OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private HTTPS submit request
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private void SubmitRequestHttps(string url, ushort port, ushort requestType, string contentType, string username, string password)
+ {
+ try
+ {
+ HttpsClient client = new HttpsClient();
+ HttpsClientRequest request = new HttpsClientRequest();
+ HttpsClientResponse response;
+
+ client.KeepAlive = false;
+ client.HostVerification = false;
+ client.PeerVerification = false;
+
+ var authorization = "";
+ if (!string.IsNullOrEmpty(username))
+ authorization = EncodeBase64(username, password);
+
+ if (!string.IsNullOrEmpty(authorization))
+ request.Header.SetHeaderValue("Authorization", authorization);
+
+ if (!string.IsNullOrEmpty(contentType))
+ request.Header.ContentType = contentType;
+
+ request.Url.Parse(url);
+ request.RequestType = (Crestron.SimplSharp.Net.Https.RequestType)requestType;
+
+ response = client.Dispatch(request);
+
+ CrestronConsole.PrintLine(string.Format("SubmitRequestHttp Response[{0}]: {1}", response.Code, response.ContentString.ToString()));
+
+ if(!string.IsNullOrEmpty(response.ContentString.ToString()))
+ OnStringChange(response.ContentString.ToString(), 0, GenericRESTfulConstants.ResponseStringChange);
+
+ if(response.Code > 0)
+ OnUshrtChange((ushort)response.Code, 0, GenericRESTfulConstants.ResponseCodeChange);
+
+ }
+ catch (Exception e)
+ {
+ //var msg = string.Format("SubmitRequestHttps({0}, {1}, {2}, {3}, {4}) failed:{5}", url, port, requestType, username, password, e.Message);
+ //CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+
+ CrestronConsole.PrintLine(e.Message);
+ OnStringChange(e.Message, 0, GenericRESTfulConstants.ErrorStringChange);
+ }
+ }
+
+ ///
+ /// Private method to encode username and password to Base64 string
+ ///
+ ///
+ ///
+ /// authorization
+ private string EncodeBase64(string username, string password)
+ {
+ var authorization = "";
+
+ try
+ {
+ if (!string.IsNullOrEmpty(username))
+ {
+ string base64String = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(string.Format("{0}:{1}", username, password)));
+ authorization = string.Format("Basic {0}", base64String);
+ }
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("EncodeBase64({0}, {1}) failed:\r{2}", username, password, e);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ return "" ;
+ }
+
+ return authorization;
+ }
+
+ ///
+ /// Protected method to handle boolean change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected mehtod to handle ushort change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected method to handle string change events
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/EventArgs and Constants.cs b/src/JsonStandardObjects/EventArgs and Constants.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/JsonStandardObjects/EventArgs and Constants.cs
rename to src/JsonStandardObjects/EventArgs and Constants.cs
index 2bac226..ed02ccb 100644
--- a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/EventArgs and Constants.cs
+++ b/src/JsonStandardObjects/EventArgs and Constants.cs
@@ -1,77 +1,77 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.JsonStandardObjects
-{
- ///
- /// Constants for simpl modules
- ///
- public class JsonStandardDeviceConstants
- {
- ///
- /// Json object evaluated constant
- ///
- public const ushort JsonObjectEvaluated = 2;
-
- ///
- /// Json object changed constant
- ///
- public const ushort JsonObjectChanged = 104;
- }
-
- ///
- ///
- ///
- public class DeviceChangeEventArgs : EventArgs
- {
- ///
- /// Device change event args object
- ///
- public DeviceConfig Device { get; set; }
-
- ///
- /// Device change event args type
- ///
- public ushort Type { get; set; }
-
- ///
- /// Device change event args index
- ///
- public ushort Index { get; set; }
-
- ///
- /// Default constructor
- ///
- public DeviceChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public DeviceChangeEventArgs(DeviceConfig device, ushort type)
- {
- Device = device;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public DeviceChangeEventArgs(DeviceConfig device, ushort type, ushort index)
- {
- Device = device;
- Type = type;
- Index = index;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.JsonStandardObjects
+{
+ ///
+ /// Constants for simpl modules
+ ///
+ public class JsonStandardDeviceConstants
+ {
+ ///
+ /// Json object evaluated constant
+ ///
+ public const ushort JsonObjectEvaluated = 2;
+
+ ///
+ /// Json object changed constant
+ ///
+ public const ushort JsonObjectChanged = 104;
+ }
+
+ ///
+ ///
+ ///
+ public class DeviceChangeEventArgs : EventArgs
+ {
+ ///
+ /// Device change event args object
+ ///
+ public DeviceConfig Device { get; set; }
+
+ ///
+ /// Device change event args type
+ ///
+ public ushort Type { get; set; }
+
+ ///
+ /// Device change event args index
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Default constructor
+ ///
+ public DeviceChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public DeviceChangeEventArgs(DeviceConfig device, ushort type)
+ {
+ Device = device;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public DeviceChangeEventArgs(DeviceConfig device, ushort type, ushort index)
+ {
+ Device = device;
+ Type = type;
+ Index = index;
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDevice.cs b/src/JsonStandardObjects/JsonToSimplDevice.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDevice.cs
rename to src/JsonStandardObjects/JsonToSimplDevice.cs
index eef68f2..e4b1567 100644
--- a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDevice.cs
+++ b/src/JsonStandardObjects/JsonToSimplDevice.cs
@@ -1,186 +1,186 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using PepperDash.Core.JsonToSimpl;
-
-namespace PepperDash.Core.JsonStandardObjects
-{
- ///
- /// Device class
- ///
- public class DeviceConfig
- {
- ///
- /// JSON config key property
- ///
- public string key { get; set; }
- ///
- /// JSON config name property
- ///
- public string name { get; set; }
- ///
- /// JSON config type property
- ///
- public string type { get; set; }
- ///
- /// JSON config properties
- ///
- public PropertiesConfig properties { get; set; }
-
- ///
- /// Bool change event handler
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort change event handler
- ///
- public event EventHandler UshrtChange;
- ///
- /// String change event handler
- ///
- public event EventHandler StringChange;
- ///
- /// Object change event handler
- ///
- public event EventHandler DeviceChange;
-
- ///
- /// Constructor
- ///
- public DeviceConfig()
- {
- properties = new PropertiesConfig();
- }
-
- ///
- /// Initialize method
- ///
- ///
- ///
- public void Initialize(string uniqueID, string deviceKey)
- {
- // S+ set EvaluateFb low
- OnBoolChange(false, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
- // validate parameters
- if (string.IsNullOrEmpty(uniqueID) || string.IsNullOrEmpty(deviceKey))
- {
- Debug.Console(1, "UniqueID ({0} or key ({1} is null or empty", uniqueID, deviceKey);
- // S+ set EvaluteFb high
- OnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
- return;
- }
-
- key = deviceKey;
-
- try
- {
- // get the file using the unique ID
- JsonToSimplMaster jsonMaster = J2SGlobal.GetMasterByFile(uniqueID);
- if (jsonMaster == null)
- {
- Debug.Console(1, "Could not find JSON file with uniqueID {0}", uniqueID);
- return;
- }
-
- // get the device configuration using the key
- var devices = jsonMaster.JsonObject.ToObject().devices;
- var device = devices.FirstOrDefault(d => d.key.Equals(key));
- if (device == null)
- {
- Debug.Console(1, "Could not find device with key {0}", key);
- return;
- }
- OnObjectChange(device, 0, JsonStandardDeviceConstants.JsonObjectChanged);
-
- var index = devices.IndexOf(device);
- OnStringChange(string.Format("devices[{0}]", index), 0, JsonToSimplConstants.FullPathToArrayChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("Device {0} lookup failed:\r{1}", key, e);
- CrestronConsole.PrintLine(msg);
- ErrorLog.Error(msg);
- }
- finally
- {
- // S+ set EvaluteFb high
- OnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
- }
- }
-
- #region EventHandler Helpers
-
- ///
- /// BoolChange event handler helper
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// UshrtChange event handler helper
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort state, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(state, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// StringChange event handler helper
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
-
- ///
- /// ObjectChange event handler helper
- ///
- ///
- ///
- ///
- protected void OnObjectChange(DeviceConfig device, ushort index, ushort type)
- {
- if (DeviceChange != null)
- {
- var args = new DeviceChangeEventArgs(device, type);
- args.Index = index;
- DeviceChange(this, args);
- }
- }
-
- #endregion EventHandler Helpers
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using PepperDash.Core.JsonToSimpl;
+
+namespace PepperDash.Core.JsonStandardObjects
+{
+ ///
+ /// Device class
+ ///
+ public class DeviceConfig
+ {
+ ///
+ /// JSON config key property
+ ///
+ public string key { get; set; }
+ ///
+ /// JSON config name property
+ ///
+ public string name { get; set; }
+ ///
+ /// JSON config type property
+ ///
+ public string type { get; set; }
+ ///
+ /// JSON config properties
+ ///
+ public PropertiesConfig properties { get; set; }
+
+ ///
+ /// Bool change event handler
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort change event handler
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String change event handler
+ ///
+ public event EventHandler StringChange;
+ ///
+ /// Object change event handler
+ ///
+ public event EventHandler DeviceChange;
+
+ ///
+ /// Constructor
+ ///
+ public DeviceConfig()
+ {
+ properties = new PropertiesConfig();
+ }
+
+ ///
+ /// Initialize method
+ ///
+ ///
+ ///
+ public void Initialize(string uniqueID, string deviceKey)
+ {
+ // S+ set EvaluateFb low
+ OnBoolChange(false, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
+ // validate parameters
+ if (string.IsNullOrEmpty(uniqueID) || string.IsNullOrEmpty(deviceKey))
+ {
+ Debug.Console(1, "UniqueID ({0} or key ({1} is null or empty", uniqueID, deviceKey);
+ // S+ set EvaluteFb high
+ OnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
+ return;
+ }
+
+ key = deviceKey;
+
+ try
+ {
+ // get the file using the unique ID
+ JsonToSimplMaster jsonMaster = J2SGlobal.GetMasterByFile(uniqueID);
+ if (jsonMaster == null)
+ {
+ Debug.Console(1, "Could not find JSON file with uniqueID {0}", uniqueID);
+ return;
+ }
+
+ // get the device configuration using the key
+ var devices = jsonMaster.JsonObject.ToObject().devices;
+ var device = devices.FirstOrDefault(d => d.key.Equals(key));
+ if (device == null)
+ {
+ Debug.Console(1, "Could not find device with key {0}", key);
+ return;
+ }
+ OnObjectChange(device, 0, JsonStandardDeviceConstants.JsonObjectChanged);
+
+ var index = devices.IndexOf(device);
+ OnStringChange(string.Format("devices[{0}]", index), 0, JsonToSimplConstants.FullPathToArrayChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("Device {0} lookup failed:\r{1}", key, e);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ }
+ finally
+ {
+ // S+ set EvaluteFb high
+ OnBoolChange(true, 0, JsonStandardDeviceConstants.JsonObjectEvaluated);
+ }
+ }
+
+ #region EventHandler Helpers
+
+ ///
+ /// BoolChange event handler helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// UshrtChange event handler helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort state, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(state, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// StringChange event handler helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+
+ ///
+ /// ObjectChange event handler helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnObjectChange(DeviceConfig device, ushort index, ushort type)
+ {
+ if (DeviceChange != null)
+ {
+ var args = new DeviceChangeEventArgs(device, type);
+ args.Index = index;
+ DeviceChange(this, args);
+ }
+ }
+
+ #endregion EventHandler Helpers
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs b/src/JsonStandardObjects/JsonToSimplDeviceConfig.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs
rename to src/JsonStandardObjects/JsonToSimplDeviceConfig.cs
index 114609e..fa23d87 100644
--- a/Pepperdash Core/Pepperdash Core/JsonStandardObjects/JsonToSimplDeviceConfig.cs
+++ b/src/JsonStandardObjects/JsonToSimplDeviceConfig.cs
@@ -1,257 +1,257 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.JsonStandardObjects
-{
- /*
- Convert JSON snippt to C#: http://json2csharp.com/#
-
- JSON Snippet:
- {
- "devices": [
- {
- "key": "deviceKey",
- "name": "deviceName",
- "type": "deviceType",
- "properties": {
- "deviceId": 1,
- "enabled": true,
- "control": {
- "method": "methodName",
- "controlPortDevKey": "deviceControlPortDevKey",
- "controlPortNumber": 1,
- "comParams": {
- "baudRate": 9600,
- "dataBits": 8,
- "stopBits": 1,
- "parity": "None",
- "protocol": "RS232",
- "hardwareHandshake": "None",
- "softwareHandshake": "None",
- "pacing": 0
- },
- "tcpSshProperties": {
- "address": "172.22.1.101",
- "port": 23,
- "username": "user01",
- "password": "password01",
- "autoReconnect": false,
- "autoReconnectIntervalMs": 10000
- }
- }
- }
- }
- ]
- }
- */
- ///
- /// Device communication parameter class
- ///
- public class ComParamsConfig
- {
- ///
- ///
- ///
- public int baudRate { get; set; }
- ///
- ///
- ///
- public int dataBits { get; set; }
- ///
- ///
- ///
- public int stopBits { get; set; }
- ///
- ///
- ///
- public string parity { get; set; }
- ///
- ///
- ///
- public string protocol { get; set; }
- ///
- ///
- ///
- public string hardwareHandshake { get; set; }
- ///
- ///
- ///
- public string softwareHandshake { get; set; }
- ///
- ///
- ///
- public int pacing { get; set; }
-
- // convert properties for simpl
- ///
- ///
- ///
- public ushort simplBaudRate { get { return Convert.ToUInt16(baudRate); } }
- ///
- ///
- ///
- public ushort simplDataBits { get { return Convert.ToUInt16(dataBits); } }
- ///
- ///
- ///
- public ushort simplStopBits { get { return Convert.ToUInt16(stopBits); } }
- ///
- ///
- ///
- public ushort simplPacing { get { return Convert.ToUInt16(pacing); } }
-
- ///
- /// Constructor
- ///
- public ComParamsConfig()
- {
-
- }
- }
-
- ///
- /// Device TCP/SSH properties class
- ///
- public class TcpSshPropertiesConfig
- {
- ///
- ///
- ///
- public string address { get; set; }
- ///
- ///
- ///
- public int port { get; set; }
- ///
- ///
- ///
- public string username { get; set; }
- ///
- ///
- ///
- public string password { get; set; }
- ///
- ///
- ///
- public bool autoReconnect { get; set; }
- ///
- ///
- ///
- public int autoReconnectIntervalMs { get; set; }
-
- // convert properties for simpl
- ///
- ///
- ///
- public ushort simplPort { get { return Convert.ToUInt16(port); } }
- ///
- ///
- ///
- public ushort simplAutoReconnect { get { return (ushort)(autoReconnect ? 1 : 0); } }
- ///
- ///
- ///
- public ushort simplAutoReconnectIntervalMs { get { return Convert.ToUInt16(autoReconnectIntervalMs); } }
-
- ///
- /// Constructor
- ///
- public TcpSshPropertiesConfig()
- {
-
- }
- }
-
- ///
- /// Device control class
- ///
- public class ControlConfig
- {
- ///
- ///
- ///
- public string method { get; set; }
- ///
- ///
- ///
- public string controlPortDevKey { get; set; }
- ///
- ///
- ///
- public int controlPortNumber { get; set; }
- ///
- ///
- ///
- public ComParamsConfig comParams { get; set; }
- ///
- ///
- ///
- public TcpSshPropertiesConfig tcpSshProperties { get; set; }
-
- // convert properties for simpl
- ///
- ///
- ///
- public ushort simplControlPortNumber { get { return Convert.ToUInt16(controlPortNumber); } }
-
- ///
- /// Constructor
- ///
- public ControlConfig()
- {
- comParams = new ComParamsConfig();
- tcpSshProperties = new TcpSshPropertiesConfig();
- }
- }
-
- ///
- /// Device properties class
- ///
- public class PropertiesConfig
- {
- ///
- ///
- ///
- public int deviceId { get; set; }
- ///
- ///
- ///
- public bool enabled { get; set; }
- ///
- ///
- ///
- public ControlConfig control { get; set; }
-
- // convert properties for simpl
- ///
- ///
- ///
- public ushort simplDeviceId { get { return Convert.ToUInt16(deviceId); } }
- ///
- ///
- ///
- public ushort simplEnabled { get { return (ushort)(enabled ? 1 : 0); } }
-
- ///
- /// Constructor
- ///
- public PropertiesConfig()
- {
- control = new ControlConfig();
- }
- }
-
- ///
- /// Root device class
- ///
- public class RootObject
- {
- ///
- /// The collection of devices
- ///
- public List devices { get; set; }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.JsonStandardObjects
+{
+ /*
+ Convert JSON snippt to C#: http://json2csharp.com/#
+
+ JSON Snippet:
+ {
+ "devices": [
+ {
+ "key": "deviceKey",
+ "name": "deviceName",
+ "type": "deviceType",
+ "properties": {
+ "deviceId": 1,
+ "enabled": true,
+ "control": {
+ "method": "methodName",
+ "controlPortDevKey": "deviceControlPortDevKey",
+ "controlPortNumber": 1,
+ "comParams": {
+ "baudRate": 9600,
+ "dataBits": 8,
+ "stopBits": 1,
+ "parity": "None",
+ "protocol": "RS232",
+ "hardwareHandshake": "None",
+ "softwareHandshake": "None",
+ "pacing": 0
+ },
+ "tcpSshProperties": {
+ "address": "172.22.1.101",
+ "port": 23,
+ "username": "user01",
+ "password": "password01",
+ "autoReconnect": false,
+ "autoReconnectIntervalMs": 10000
+ }
+ }
+ }
+ }
+ ]
+ }
+ */
+ ///
+ /// Device communication parameter class
+ ///
+ public class ComParamsConfig
+ {
+ ///
+ ///
+ ///
+ public int baudRate { get; set; }
+ ///
+ ///
+ ///
+ public int dataBits { get; set; }
+ ///
+ ///
+ ///
+ public int stopBits { get; set; }
+ ///
+ ///
+ ///
+ public string parity { get; set; }
+ ///
+ ///
+ ///
+ public string protocol { get; set; }
+ ///
+ ///
+ ///
+ public string hardwareHandshake { get; set; }
+ ///
+ ///
+ ///
+ public string softwareHandshake { get; set; }
+ ///
+ ///
+ ///
+ public int pacing { get; set; }
+
+ // convert properties for simpl
+ ///
+ ///
+ ///
+ public ushort simplBaudRate { get { return Convert.ToUInt16(baudRate); } }
+ ///
+ ///
+ ///
+ public ushort simplDataBits { get { return Convert.ToUInt16(dataBits); } }
+ ///
+ ///
+ ///
+ public ushort simplStopBits { get { return Convert.ToUInt16(stopBits); } }
+ ///
+ ///
+ ///
+ public ushort simplPacing { get { return Convert.ToUInt16(pacing); } }
+
+ ///
+ /// Constructor
+ ///
+ public ComParamsConfig()
+ {
+
+ }
+ }
+
+ ///
+ /// Device TCP/SSH properties class
+ ///
+ public class TcpSshPropertiesConfig
+ {
+ ///
+ ///
+ ///
+ public string address { get; set; }
+ ///
+ ///
+ ///
+ public int port { get; set; }
+ ///
+ ///
+ ///
+ public string username { get; set; }
+ ///
+ ///
+ ///
+ public string password { get; set; }
+ ///
+ ///
+ ///
+ public bool autoReconnect { get; set; }
+ ///
+ ///
+ ///
+ public int autoReconnectIntervalMs { get; set; }
+
+ // convert properties for simpl
+ ///
+ ///
+ ///
+ public ushort simplPort { get { return Convert.ToUInt16(port); } }
+ ///
+ ///
+ ///
+ public ushort simplAutoReconnect { get { return (ushort)(autoReconnect ? 1 : 0); } }
+ ///
+ ///
+ ///
+ public ushort simplAutoReconnectIntervalMs { get { return Convert.ToUInt16(autoReconnectIntervalMs); } }
+
+ ///
+ /// Constructor
+ ///
+ public TcpSshPropertiesConfig()
+ {
+
+ }
+ }
+
+ ///
+ /// Device control class
+ ///
+ public class ControlConfig
+ {
+ ///
+ ///
+ ///
+ public string method { get; set; }
+ ///
+ ///
+ ///
+ public string controlPortDevKey { get; set; }
+ ///
+ ///
+ ///
+ public int controlPortNumber { get; set; }
+ ///
+ ///
+ ///
+ public ComParamsConfig comParams { get; set; }
+ ///
+ ///
+ ///
+ public TcpSshPropertiesConfig tcpSshProperties { get; set; }
+
+ // convert properties for simpl
+ ///
+ ///
+ ///
+ public ushort simplControlPortNumber { get { return Convert.ToUInt16(controlPortNumber); } }
+
+ ///
+ /// Constructor
+ ///
+ public ControlConfig()
+ {
+ comParams = new ComParamsConfig();
+ tcpSshProperties = new TcpSshPropertiesConfig();
+ }
+ }
+
+ ///
+ /// Device properties class
+ ///
+ public class PropertiesConfig
+ {
+ ///
+ ///
+ ///
+ public int deviceId { get; set; }
+ ///
+ ///
+ ///
+ public bool enabled { get; set; }
+ ///
+ ///
+ ///
+ public ControlConfig control { get; set; }
+
+ // convert properties for simpl
+ ///
+ ///
+ ///
+ public ushort simplDeviceId { get { return Convert.ToUInt16(deviceId); } }
+ ///
+ ///
+ ///
+ public ushort simplEnabled { get { return (ushort)(enabled ? 1 : 0); } }
+
+ ///
+ /// Constructor
+ ///
+ public PropertiesConfig()
+ {
+ control = new ControlConfig();
+ }
+ }
+
+ ///
+ /// Root device class
+ ///
+ public class RootObject
+ {
+ ///
+ /// The collection of devices
+ ///
+ public List devices { get; set; }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/Constants.cs b/src/JsonToSimpl/Constants.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/Constants.cs
rename to src/JsonToSimpl/Constants.cs
index 0bf9872..d87b50c 100644
--- a/Pepperdash Core/Pepperdash Core/JsonToSimpl/Constants.cs
+++ b/src/JsonToSimpl/Constants.cs
@@ -1,143 +1,143 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.JsonToSimpl
-{
- ///
- /// Constants for Simpl modules
- ///
- public class JsonToSimplConstants
- {
- ///
- ///
- ///
- public const ushort BoolValueChange = 1;
- ///
- ///
- ///
- public const ushort JsonIsValidBoolChange = 2;
-
- ///
- /// Reports the if the device is 3-series compatible
- ///
- public const ushort ProgramCompatibility3SeriesChange = 3;
-
- ///
- /// Reports the if the device is 4-series compatible
- ///
- public const ushort ProgramCompatibility4SeriesChange = 4;
-
- ///
- /// Reports the device platform enum value
- ///
- public const ushort DevicePlatformValueChange = 5;
-
- ///
- ///
- ///
- public const ushort UshortValueChange = 101;
-
- ///
- ///
- ///
- public const ushort StringValueChange = 201;
- ///
- ///
- ///
- public const ushort FullPathToArrayChange = 202;
- ///
- ///
- ///
- public const ushort ActualFilePathChange = 203;
-
- ///
- ///
- ///
- public const ushort FilenameResolvedChange = 204;
- ///
- ///
- ///
- public const ushort FilePathResolvedChange = 205;
-
- ///
- /// Reports the root directory change
- ///
- public const ushort RootDirectoryChange = 206;
-
- ///
- /// Reports the room ID change
- ///
- public const ushort RoomIdChange = 207;
-
- ///
- /// Reports the room name change
- ///
- public const ushort RoomNameChange = 208;
- }
-
- ///
- /// S+ values delegate
- ///
- public delegate void SPlusValuesDelegate();
-
- ///
- /// S+ values wrapper
- ///
- public class SPlusValueWrapper
- {
- ///
- ///
- ///
- public SPlusType ValueType { get; private set; }
- ///
- ///
- ///
- public ushort Index { get; private set; }
- ///
- ///
- ///
- public ushort BoolUShortValue { get; set; }
- ///
- ///
- ///
- public string StringValue { get; set; }
-
- ///
- ///
- ///
- public SPlusValueWrapper() {}
-
- ///
- ///
- ///
- ///
- ///
- public SPlusValueWrapper(SPlusType type, ushort index)
- {
- ValueType = type;
- Index = index;
- }
- }
-
- ///
- /// S+ types enum
- ///
- public enum SPlusType
- {
- ///
- /// Digital
- ///
- Digital,
- ///
- /// Analog
- ///
- Analog,
- ///
- /// String
- ///
- String
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Constants for Simpl modules
+ ///
+ public class JsonToSimplConstants
+ {
+ ///
+ ///
+ ///
+ public const ushort BoolValueChange = 1;
+ ///
+ ///
+ ///
+ public const ushort JsonIsValidBoolChange = 2;
+
+ ///
+ /// Reports the if the device is 3-series compatible
+ ///
+ public const ushort ProgramCompatibility3SeriesChange = 3;
+
+ ///
+ /// Reports the if the device is 4-series compatible
+ ///
+ public const ushort ProgramCompatibility4SeriesChange = 4;
+
+ ///
+ /// Reports the device platform enum value
+ ///
+ public const ushort DevicePlatformValueChange = 5;
+
+ ///
+ ///
+ ///
+ public const ushort UshortValueChange = 101;
+
+ ///
+ ///
+ ///
+ public const ushort StringValueChange = 201;
+ ///
+ ///
+ ///
+ public const ushort FullPathToArrayChange = 202;
+ ///
+ ///
+ ///
+ public const ushort ActualFilePathChange = 203;
+
+ ///
+ ///
+ ///
+ public const ushort FilenameResolvedChange = 204;
+ ///
+ ///
+ ///
+ public const ushort FilePathResolvedChange = 205;
+
+ ///
+ /// Reports the root directory change
+ ///
+ public const ushort RootDirectoryChange = 206;
+
+ ///
+ /// Reports the room ID change
+ ///
+ public const ushort RoomIdChange = 207;
+
+ ///
+ /// Reports the room name change
+ ///
+ public const ushort RoomNameChange = 208;
+ }
+
+ ///
+ /// S+ values delegate
+ ///
+ public delegate void SPlusValuesDelegate();
+
+ ///
+ /// S+ values wrapper
+ ///
+ public class SPlusValueWrapper
+ {
+ ///
+ ///
+ ///
+ public SPlusType ValueType { get; private set; }
+ ///
+ ///
+ ///
+ public ushort Index { get; private set; }
+ ///
+ ///
+ ///
+ public ushort BoolUShortValue { get; set; }
+ ///
+ ///
+ ///
+ public string StringValue { get; set; }
+
+ ///
+ ///
+ ///
+ public SPlusValueWrapper() {}
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SPlusValueWrapper(SPlusType type, ushort index)
+ {
+ ValueType = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// S+ types enum
+ ///
+ public enum SPlusType
+ {
+ ///
+ /// Digital
+ ///
+ Digital,
+ ///
+ /// Analog
+ ///
+ Analog,
+ ///
+ /// String
+ ///
+ String
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/Global.cs b/src/JsonToSimpl/Global.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/Global.cs
rename to src/JsonToSimpl/Global.cs
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs b/src/JsonToSimpl/JsonToSimplArrayLookupChild.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs
rename to src/JsonToSimpl/JsonToSimplArrayLookupChild.cs
index 6535308..eb15448 100644
--- a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplArrayLookupChild.cs
+++ b/src/JsonToSimpl/JsonToSimplArrayLookupChild.cs
@@ -1,165 +1,165 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core.JsonToSimpl
-{
- ///
- /// Used to interact with an array of values with the S+ modules
- ///
- public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
- {
- ///
- ///
- ///
- public string SearchPropertyName { get; set; }
- ///
- ///
- ///
- public string SearchPropertyValue { get; set; }
-
- int ArrayIndex;
-
- ///
- /// For gt2.4.1 array lookups
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public void Initialize(string file, string key, string pathPrefix, string pathSuffix,
- string searchPropertyName, string searchPropertyValue)
- {
- base.Initialize(file, key, pathPrefix, pathSuffix);
- SearchPropertyName = searchPropertyName;
- SearchPropertyValue = searchPropertyValue;
- }
-
-
- ///
- /// For newer >=2.4.1 array lookups.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- public void InitializeWithAppend(string file, string key, string pathPrefix, string pathAppend,
- string pathSuffix, string searchPropertyName, string searchPropertyValue)
- {
- string pathPrefixWithAppend = (pathPrefix != null ? pathPrefix : "") + GetPathAppend(pathAppend);
- base.Initialize(file, key, pathPrefixWithAppend, pathSuffix);
-
- SearchPropertyName = searchPropertyName;
- SearchPropertyValue = searchPropertyValue;
- }
-
-
-
- //PathPrefix+ArrayName+[x]+path+PathSuffix
- ///
- ///
- ///
- ///
- ///
- protected override string GetFullPath(string path)
- {
- return string.Format("{0}[{1}].{2}{3}",
- PathPrefix == null ? "" : PathPrefix,
- ArrayIndex,
- path,
- PathSuffix == null ? "" : PathSuffix);
- }
-
- ///
- /// Process all values
- ///
- public override void ProcessAll()
- {
- if (FindInArray())
- base.ProcessAll();
- }
-
- ///
- /// Provides the path append for GetFullPath
- ///
- ///
- string GetPathAppend(string a)
- {
- if (string.IsNullOrEmpty(a))
- {
- return "";
- }
- if (a.StartsWith("."))
- {
- return a;
- }
- else
- {
- return "." + a;
- }
- }
-
- ///
- ///
- ///
- ///
- bool FindInArray()
- {
- if (Master == null)
- throw new InvalidOperationException("Cannot do operations before master is linked");
- if (Master.JsonObject == null)
- throw new InvalidOperationException("Cannot do operations before master JSON has read");
- if (PathPrefix == null)
- throw new InvalidOperationException("Cannot do operations before PathPrefix is set");
-
-
- var token = Master.JsonObject.SelectToken(PathPrefix);
- if (token is JArray)
- {
- var array = token as JArray;
- try
- {
- var item = array.FirstOrDefault(o =>
- {
- var prop = o[SearchPropertyName];
- return prop != null && prop.Value()
- .Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);
- });
- if (item == null)
- {
- Debug.Console(1, "JSON Child[{0}] Array '{1}' '{2}={3}' not found: ", Key,
- PathPrefix, SearchPropertyName, SearchPropertyValue);
- this.LinkedToObject = false;
- return false;
- }
-
- this.LinkedToObject = true;
- ArrayIndex = array.IndexOf(item);
- OnStringChange(string.Format("{0}[{1}]", PathPrefix, ArrayIndex), 0, JsonToSimplConstants.FullPathToArrayChange);
- Debug.Console(1, "JSON Child[{0}] Found array match at index {1}", Key, ArrayIndex);
- return true;
- }
- catch (Exception e)
- {
- Debug.Console(1, "JSON Child[{0}] Array '{1}' lookup error: '{2}={3}'\r{4}", Key,
- PathPrefix, SearchPropertyName, SearchPropertyValue, e);
- }
- }
- else
- {
- Debug.Console(1, "JSON Child[{0}] Path '{1}' is not an array", Key, PathPrefix);
- }
-
- return false;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Used to interact with an array of values with the S+ modules
+ ///
+ public class JsonToSimplArrayLookupChild : JsonToSimplChildObjectBase
+ {
+ ///
+ ///
+ ///
+ public string SearchPropertyName { get; set; }
+ ///
+ ///
+ ///
+ public string SearchPropertyValue { get; set; }
+
+ int ArrayIndex;
+
+ ///
+ /// For gt2.4.1 array lookups
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Initialize(string file, string key, string pathPrefix, string pathSuffix,
+ string searchPropertyName, string searchPropertyValue)
+ {
+ base.Initialize(file, key, pathPrefix, pathSuffix);
+ SearchPropertyName = searchPropertyName;
+ SearchPropertyValue = searchPropertyValue;
+ }
+
+
+ ///
+ /// For newer >=2.4.1 array lookups.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void InitializeWithAppend(string file, string key, string pathPrefix, string pathAppend,
+ string pathSuffix, string searchPropertyName, string searchPropertyValue)
+ {
+ string pathPrefixWithAppend = (pathPrefix != null ? pathPrefix : "") + GetPathAppend(pathAppend);
+ base.Initialize(file, key, pathPrefixWithAppend, pathSuffix);
+
+ SearchPropertyName = searchPropertyName;
+ SearchPropertyValue = searchPropertyValue;
+ }
+
+
+
+ //PathPrefix+ArrayName+[x]+path+PathSuffix
+ ///
+ ///
+ ///
+ ///
+ ///
+ protected override string GetFullPath(string path)
+ {
+ return string.Format("{0}[{1}].{2}{3}",
+ PathPrefix == null ? "" : PathPrefix,
+ ArrayIndex,
+ path,
+ PathSuffix == null ? "" : PathSuffix);
+ }
+
+ ///
+ /// Process all values
+ ///
+ public override void ProcessAll()
+ {
+ if (FindInArray())
+ base.ProcessAll();
+ }
+
+ ///
+ /// Provides the path append for GetFullPath
+ ///
+ ///
+ string GetPathAppend(string a)
+ {
+ if (string.IsNullOrEmpty(a))
+ {
+ return "";
+ }
+ if (a.StartsWith("."))
+ {
+ return a;
+ }
+ else
+ {
+ return "." + a;
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ bool FindInArray()
+ {
+ if (Master == null)
+ throw new InvalidOperationException("Cannot do operations before master is linked");
+ if (Master.JsonObject == null)
+ throw new InvalidOperationException("Cannot do operations before master JSON has read");
+ if (PathPrefix == null)
+ throw new InvalidOperationException("Cannot do operations before PathPrefix is set");
+
+
+ var token = Master.JsonObject.SelectToken(PathPrefix);
+ if (token is JArray)
+ {
+ var array = token as JArray;
+ try
+ {
+ var item = array.FirstOrDefault(o =>
+ {
+ var prop = o[SearchPropertyName];
+ return prop != null && prop.Value()
+ .Equals(SearchPropertyValue, StringComparison.OrdinalIgnoreCase);
+ });
+ if (item == null)
+ {
+ Debug.Console(1, "JSON Child[{0}] Array '{1}' '{2}={3}' not found: ", Key,
+ PathPrefix, SearchPropertyName, SearchPropertyValue);
+ this.LinkedToObject = false;
+ return false;
+ }
+
+ this.LinkedToObject = true;
+ ArrayIndex = array.IndexOf(item);
+ OnStringChange(string.Format("{0}[{1}]", PathPrefix, ArrayIndex), 0, JsonToSimplConstants.FullPathToArrayChange);
+ Debug.Console(1, "JSON Child[{0}] Found array match at index {1}", Key, ArrayIndex);
+ return true;
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, "JSON Child[{0}] Array '{1}' lookup error: '{2}={3}'\r{4}", Key,
+ PathPrefix, SearchPropertyName, SearchPropertyValue, e);
+ }
+ }
+ else
+ {
+ Debug.Console(1, "JSON Child[{0}] Path '{1}' is not an array", Key, PathPrefix);
+ }
+
+ return false;
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs b/src/JsonToSimpl/JsonToSimplChildObjectBase.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs
rename to src/JsonToSimpl/JsonToSimplChildObjectBase.cs
index e507105..e294fc8 100644
--- a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplChildObjectBase.cs
+++ b/src/JsonToSimpl/JsonToSimplChildObjectBase.cs
@@ -1,407 +1,407 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core.JsonToSimpl
-{
- ///
- /// Base class for JSON objects
- ///
- public abstract class JsonToSimplChildObjectBase : IKeyed
- {
- ///
- /// Notifies of bool change
- ///
- public event EventHandler BoolChange;
- ///
- /// Notifies of ushort change
- ///
- public event EventHandler UShortChange;
- ///
- /// Notifies of string change
- ///
- public event EventHandler StringChange;
-
- ///
- /// Delegate to get all values
- ///
- public SPlusValuesDelegate GetAllValuesDelegate { get; set; }
-
- ///
- /// Use a callback to reduce task switch/threading
- ///
- public SPlusValuesDelegate SetAllPathsDelegate { get; set; }
-
- ///
- /// Unique identifier for instance
- ///
- public string Key { get; protected set; }
-
- ///
- /// This will be prepended to all paths to allow path swapping or for more organized
- /// sub-paths
- ///
- public string PathPrefix { get; protected set; }
-
- ///
- /// This is added to the end of all paths
- ///
- public string PathSuffix { get; protected set; }
-
- ///
- /// Indicates if the instance is linked to an object
- ///
- public bool LinkedToObject { get; protected set; }
-
- ///
- /// Reference to Master instance
- ///
- protected JsonToSimplMaster Master;
-
- ///
- /// Paths to boolean values in JSON structure
- ///
- protected Dictionary BoolPaths = new Dictionary();
- ///
- /// Paths to numeric values in JSON structure
- ///
- protected Dictionary UshortPaths = new Dictionary();
- ///
- /// Paths to string values in JSON structure
- ///
- protected Dictionary StringPaths = new Dictionary();
-
- ///
- /// Call this before doing anything else
- ///
- ///
- ///
- ///
- ///
- public void Initialize(string masterUniqueId, string key, string pathPrefix, string pathSuffix)
- {
- Key = key;
- PathPrefix = pathPrefix;
- PathSuffix = pathSuffix;
-
- Master = J2SGlobal.GetMasterByFile(masterUniqueId);
- if (Master != null)
- Master.AddChild(this);
- else
- Debug.Console(1, "JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
- }
-
- ///
- /// Sets the path prefix for the object
- ///
- ///
- public void SetPathPrefix(string pathPrefix)
- {
- PathPrefix = pathPrefix;
- }
- ///
- /// Set the JPath to evaluate for a given bool out index.
- ///
- public void SetBoolPath(ushort index, string path)
- {
- Debug.Console(1, "JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
- if (path == null || path.Trim() == string.Empty) return;
- BoolPaths[index] = path;
- }
-
- ///
- /// Set the JPath for a ushort out index.
- ///
- public void SetUshortPath(ushort index, string path)
- {
- Debug.Console(1, "JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
- if (path == null || path.Trim() == string.Empty) return;
- UshortPaths[index] = path;
- }
-
- ///
- /// Set the JPath for a string output index.
- ///
- public void SetStringPath(ushort index, string path)
- {
- Debug.Console(1, "JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
- if (path == null || path.Trim() == string.Empty) return;
- StringPaths[index] = path;
- }
-
- ///
- /// Evalutates all outputs with defined paths. called by S+ when paths are ready to process
- /// and by Master when file is read.
- ///
- public virtual void ProcessAll()
- {
- if (!LinkedToObject)
- {
- Debug.Console(1, this, "Not linked to object in file. Skipping");
- return;
- }
-
- if (SetAllPathsDelegate == null)
- {
- Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring ProcessAll");
- return;
- }
- SetAllPathsDelegate();
- foreach (var kvp in BoolPaths)
- ProcessBoolPath(kvp.Key);
- foreach (var kvp in UshortPaths)
- ProcessUshortPath(kvp.Key);
- foreach (var kvp in StringPaths)
- ProcessStringPath(kvp.Key);
- }
-
- ///
- /// Processes a bool property, converting to bool, firing off a BoolChange event
- ///
- void ProcessBoolPath(ushort index)
- {
- string response;
- if (Process(BoolPaths[index], out response))
- OnBoolChange(response.Equals("true", StringComparison.OrdinalIgnoreCase),
- index, JsonToSimplConstants.BoolValueChange);
- else { }
- // OnBoolChange(false, index, JsonToSimplConstants.BoolValueChange);
- }
-
- // Processes the path to a ushort, converting to ushort if able, twos complement if necessary, firing off UshrtChange event
- void ProcessUshortPath(ushort index) {
- string response;
- if (Process(UshortPaths[index], out response)) {
- ushort val;
- try { val = Convert.ToInt32(response) < 0 ? (ushort)(Convert.ToInt16(response) + 65536) : Convert.ToUInt16(response); }
- catch { val = 0; }
-
- OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange);
- }
- else { }
- // OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange);
- }
-
- // Processes the path to a string property and fires of a StringChange event.
- void ProcessStringPath(ushort index)
- {
- string response;
- if (Process(StringPaths[index], out response))
- OnStringChange(response, index, JsonToSimplConstants.StringValueChange);
- else { }
- // OnStringChange("", index, JsonToSimplConstants.StringValueChange);
- }
-
- ///
- /// Processes the given path.
- ///
- /// JPath formatted path to the desired property
- /// The string value of the property, or a default value if it
- /// doesn't exist
- /// This will return false in the case that EvaulateAllOnJsonChange
- /// is false and the path does not evaluate to a property in the incoming JSON.
- bool Process(string path, out string response)
- {
- path = GetFullPath(path);
- Debug.Console(1, "JSON Child[{0}] Processing {1}", Key, path);
- response = "";
- if (Master == null)
- {
- Debug.Console(1, "JSONChild[{0}] cannot process without Master attached", Key);
- return false;
- }
-
- if (Master.JsonObject != null && path != string.Empty)
- {
- bool isCount = false;
- path = path.Trim();
- if (path.EndsWith(".Count"))
- {
- path = path.Remove(path.Length - 6, 6);
- isCount = true;
- }
- try // Catch a strange cast error on a bad path
- {
- var t = Master.JsonObject.SelectToken(path);
- if (t != null)
- {
- // return the count of children objects - if any
- if (isCount)
- response = (t.HasValues ? t.Children().Count() : 0).ToString();
- else
- response = t.Value();
- Debug.Console(1, " ='{0}'", response);
- return true;
- }
- }
- catch
- {
- response = "";
- }
- }
- // If the path isn't found, return this to determine whether to pass out the non-value or not.
- return false;
- }
-
-
- //************************************************************************************************
- // Save-related functions
-
-
- ///
- /// Called from Master to read inputs and update their values in master JObject
- /// Callback should hit one of the following four methods
- ///
- public void UpdateInputsForMaster()
- {
- if (!LinkedToObject)
- {
- Debug.Console(1, this, "Not linked to object in file. Skipping");
- return;
- }
-
- if (SetAllPathsDelegate == null)
- {
- Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
- return;
- }
- SetAllPathsDelegate();
- var del = GetAllValuesDelegate;
- if (del != null)
- GetAllValuesDelegate();
- }
-
- ///
- ///
- ///
- ///
- ///
- public void USetBoolValue(ushort key, ushort theValue)
- {
- SetBoolValue(key, theValue == 1);
- }
-
- ///
- ///
- ///
- ///
- ///
- public void SetBoolValue(ushort key, bool theValue)
- {
- if (BoolPaths.ContainsKey(key))
- SetValueOnMaster(BoolPaths[key], new JValue(theValue));
- }
-
- ///
- ///
- ///
- ///
- ///
- public void SetUShortValue(ushort key, ushort theValue)
- {
- if (UshortPaths.ContainsKey(key))
- SetValueOnMaster(UshortPaths[key], new JValue(theValue));
- }
-
- ///
- ///
- ///
- ///
- ///
- public void SetStringValue(ushort key, string theValue)
- {
- if (StringPaths.ContainsKey(key))
- SetValueOnMaster(StringPaths[key], new JValue(theValue));
- }
-
- ///
- ///
- ///
- ///
- ///
- public void SetValueOnMaster(string keyPath, JValue valueToSave)
- {
- var path = GetFullPath(keyPath);
- try
- {
- Debug.Console(1, "JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
-
- //var token = Master.JsonObject.SelectToken(path);
- //if (token != null) // The path exists in the file
- Master.AddUnsavedValue(path, valueToSave);
- }
- catch (Exception e)
- {
- Debug.Console(1, "JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
- }
- }
-
- ///
- /// Called during Process(...) to get the path to a given property. By default,
- /// returns PathPrefix+path+PathSuffix. Override to change the way path is built.
- ///
- protected virtual string GetFullPath(string path)
- {
- return (PathPrefix != null ? PathPrefix : "") +
- path + (PathSuffix != null ? PathSuffix : "");
- }
-
- // Helpers for events
- //******************************************************************************************
- ///
- /// Event helper
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- //******************************************************************************************
- ///
- /// Event helper
- ///
- ///
- ///
- ///
- protected void OnUShortChange(ushort state, ushort index, ushort type)
- {
- var handler = UShortChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(state, type);
- args.Index = index;
- UShortChange(this, args);
- }
- }
-
- ///
- /// Event helper
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Base class for JSON objects
+ ///
+ public abstract class JsonToSimplChildObjectBase : IKeyed
+ {
+ ///
+ /// Notifies of bool change
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Notifies of ushort change
+ ///
+ public event EventHandler UShortChange;
+ ///
+ /// Notifies of string change
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Delegate to get all values
+ ///
+ public SPlusValuesDelegate GetAllValuesDelegate { get; set; }
+
+ ///
+ /// Use a callback to reduce task switch/threading
+ ///
+ public SPlusValuesDelegate SetAllPathsDelegate { get; set; }
+
+ ///
+ /// Unique identifier for instance
+ ///
+ public string Key { get; protected set; }
+
+ ///
+ /// This will be prepended to all paths to allow path swapping or for more organized
+ /// sub-paths
+ ///
+ public string PathPrefix { get; protected set; }
+
+ ///
+ /// This is added to the end of all paths
+ ///
+ public string PathSuffix { get; protected set; }
+
+ ///
+ /// Indicates if the instance is linked to an object
+ ///
+ public bool LinkedToObject { get; protected set; }
+
+ ///
+ /// Reference to Master instance
+ ///
+ protected JsonToSimplMaster Master;
+
+ ///
+ /// Paths to boolean values in JSON structure
+ ///
+ protected Dictionary BoolPaths = new Dictionary();
+ ///
+ /// Paths to numeric values in JSON structure
+ ///
+ protected Dictionary UshortPaths = new Dictionary();
+ ///
+ /// Paths to string values in JSON structure
+ ///
+ protected Dictionary StringPaths = new Dictionary();
+
+ ///
+ /// Call this before doing anything else
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void Initialize(string masterUniqueId, string key, string pathPrefix, string pathSuffix)
+ {
+ Key = key;
+ PathPrefix = pathPrefix;
+ PathSuffix = pathSuffix;
+
+ Master = J2SGlobal.GetMasterByFile(masterUniqueId);
+ if (Master != null)
+ Master.AddChild(this);
+ else
+ Debug.Console(1, "JSON Child [{0}] cannot link to master {1}", key, masterUniqueId);
+ }
+
+ ///
+ /// Sets the path prefix for the object
+ ///
+ ///
+ public void SetPathPrefix(string pathPrefix)
+ {
+ PathPrefix = pathPrefix;
+ }
+ ///
+ /// Set the JPath to evaluate for a given bool out index.
+ ///
+ public void SetBoolPath(ushort index, string path)
+ {
+ Debug.Console(1, "JSON Child[{0}] SetBoolPath {1}={2}", Key, index, path);
+ if (path == null || path.Trim() == string.Empty) return;
+ BoolPaths[index] = path;
+ }
+
+ ///
+ /// Set the JPath for a ushort out index.
+ ///
+ public void SetUshortPath(ushort index, string path)
+ {
+ Debug.Console(1, "JSON Child[{0}] SetUshortPath {1}={2}", Key, index, path);
+ if (path == null || path.Trim() == string.Empty) return;
+ UshortPaths[index] = path;
+ }
+
+ ///
+ /// Set the JPath for a string output index.
+ ///
+ public void SetStringPath(ushort index, string path)
+ {
+ Debug.Console(1, "JSON Child[{0}] SetStringPath {1}={2}", Key, index, path);
+ if (path == null || path.Trim() == string.Empty) return;
+ StringPaths[index] = path;
+ }
+
+ ///
+ /// Evalutates all outputs with defined paths. called by S+ when paths are ready to process
+ /// and by Master when file is read.
+ ///
+ public virtual void ProcessAll()
+ {
+ if (!LinkedToObject)
+ {
+ Debug.Console(1, this, "Not linked to object in file. Skipping");
+ return;
+ }
+
+ if (SetAllPathsDelegate == null)
+ {
+ Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring ProcessAll");
+ return;
+ }
+ SetAllPathsDelegate();
+ foreach (var kvp in BoolPaths)
+ ProcessBoolPath(kvp.Key);
+ foreach (var kvp in UshortPaths)
+ ProcessUshortPath(kvp.Key);
+ foreach (var kvp in StringPaths)
+ ProcessStringPath(kvp.Key);
+ }
+
+ ///
+ /// Processes a bool property, converting to bool, firing off a BoolChange event
+ ///
+ void ProcessBoolPath(ushort index)
+ {
+ string response;
+ if (Process(BoolPaths[index], out response))
+ OnBoolChange(response.Equals("true", StringComparison.OrdinalIgnoreCase),
+ index, JsonToSimplConstants.BoolValueChange);
+ else { }
+ // OnBoolChange(false, index, JsonToSimplConstants.BoolValueChange);
+ }
+
+ // Processes the path to a ushort, converting to ushort if able, twos complement if necessary, firing off UshrtChange event
+ void ProcessUshortPath(ushort index) {
+ string response;
+ if (Process(UshortPaths[index], out response)) {
+ ushort val;
+ try { val = Convert.ToInt32(response) < 0 ? (ushort)(Convert.ToInt16(response) + 65536) : Convert.ToUInt16(response); }
+ catch { val = 0; }
+
+ OnUShortChange(val, index, JsonToSimplConstants.UshortValueChange);
+ }
+ else { }
+ // OnUShortChange(0, index, JsonToSimplConstants.UshortValueChange);
+ }
+
+ // Processes the path to a string property and fires of a StringChange event.
+ void ProcessStringPath(ushort index)
+ {
+ string response;
+ if (Process(StringPaths[index], out response))
+ OnStringChange(response, index, JsonToSimplConstants.StringValueChange);
+ else { }
+ // OnStringChange("", index, JsonToSimplConstants.StringValueChange);
+ }
+
+ ///
+ /// Processes the given path.
+ ///
+ /// JPath formatted path to the desired property
+ /// The string value of the property, or a default value if it
+ /// doesn't exist
+ /// This will return false in the case that EvaulateAllOnJsonChange
+ /// is false and the path does not evaluate to a property in the incoming JSON.
+ bool Process(string path, out string response)
+ {
+ path = GetFullPath(path);
+ Debug.Console(1, "JSON Child[{0}] Processing {1}", Key, path);
+ response = "";
+ if (Master == null)
+ {
+ Debug.Console(1, "JSONChild[{0}] cannot process without Master attached", Key);
+ return false;
+ }
+
+ if (Master.JsonObject != null && path != string.Empty)
+ {
+ bool isCount = false;
+ path = path.Trim();
+ if (path.EndsWith(".Count"))
+ {
+ path = path.Remove(path.Length - 6, 6);
+ isCount = true;
+ }
+ try // Catch a strange cast error on a bad path
+ {
+ var t = Master.JsonObject.SelectToken(path);
+ if (t != null)
+ {
+ // return the count of children objects - if any
+ if (isCount)
+ response = (t.HasValues ? t.Children().Count() : 0).ToString();
+ else
+ response = t.Value();
+ Debug.Console(1, " ='{0}'", response);
+ return true;
+ }
+ }
+ catch
+ {
+ response = "";
+ }
+ }
+ // If the path isn't found, return this to determine whether to pass out the non-value or not.
+ return false;
+ }
+
+
+ //************************************************************************************************
+ // Save-related functions
+
+
+ ///
+ /// Called from Master to read inputs and update their values in master JObject
+ /// Callback should hit one of the following four methods
+ ///
+ public void UpdateInputsForMaster()
+ {
+ if (!LinkedToObject)
+ {
+ Debug.Console(1, this, "Not linked to object in file. Skipping");
+ return;
+ }
+
+ if (SetAllPathsDelegate == null)
+ {
+ Debug.Console(1, this, "No SetAllPathsDelegate set. Ignoring UpdateInputsForMaster");
+ return;
+ }
+ SetAllPathsDelegate();
+ var del = GetAllValuesDelegate;
+ if (del != null)
+ GetAllValuesDelegate();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void USetBoolValue(ushort key, ushort theValue)
+ {
+ SetBoolValue(key, theValue == 1);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SetBoolValue(ushort key, bool theValue)
+ {
+ if (BoolPaths.ContainsKey(key))
+ SetValueOnMaster(BoolPaths[key], new JValue(theValue));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SetUShortValue(ushort key, ushort theValue)
+ {
+ if (UshortPaths.ContainsKey(key))
+ SetValueOnMaster(UshortPaths[key], new JValue(theValue));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SetStringValue(ushort key, string theValue)
+ {
+ if (StringPaths.ContainsKey(key))
+ SetValueOnMaster(StringPaths[key], new JValue(theValue));
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public void SetValueOnMaster(string keyPath, JValue valueToSave)
+ {
+ var path = GetFullPath(keyPath);
+ try
+ {
+ Debug.Console(1, "JSON Child[{0}] Queueing value on master {1}='{2}'", Key, path, valueToSave);
+
+ //var token = Master.JsonObject.SelectToken(path);
+ //if (token != null) // The path exists in the file
+ Master.AddUnsavedValue(path, valueToSave);
+ }
+ catch (Exception e)
+ {
+ Debug.Console(1, "JSON Child[{0}] Failed setting value for path '{1}'\r{2}", Key, path, e);
+ }
+ }
+
+ ///
+ /// Called during Process(...) to get the path to a given property. By default,
+ /// returns PathPrefix+path+PathSuffix. Override to change the way path is built.
+ ///
+ protected virtual string GetFullPath(string path)
+ {
+ return (PathPrefix != null ? PathPrefix : "") +
+ path + (PathSuffix != null ? PathSuffix : "");
+ }
+
+ // Helpers for events
+ //******************************************************************************************
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ //******************************************************************************************
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnUShortChange(ushort state, ushort index, ushort type)
+ {
+ var handler = UShortChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(state, type);
+ args.Index = index;
+ UShortChange(this, args);
+ }
+ }
+
+ ///
+ /// Event helper
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs b/src/JsonToSimpl/JsonToSimplFileMaster.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs
rename to src/JsonToSimpl/JsonToSimplFileMaster.cs
index c5f50d5..6ed8cd3 100644
--- a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFileMaster.cs
+++ b/src/JsonToSimpl/JsonToSimplFileMaster.cs
@@ -1,291 +1,291 @@
-using System;
-//using System.IO;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace PepperDash.Core.JsonToSimpl
-{
- ///
- /// Represents a JSON file that can be read and written to
- ///
- public class JsonToSimplFileMaster : JsonToSimplMaster
- {
- ///
- /// Sets the filepath as well as registers this with the Global.Masters list
- ///
- public string Filepath { get; private set; }
-
- ///
- /// Filepath to the actual file that will be read (Portal or local)
- ///
- public string ActualFilePath { get; private set; }
-
- ///
- ///
- ///
- public string Filename { get; private set; }
- ///
- ///
- ///
- public string FilePathName { get; private set; }
-
- /*****************************************************************************************/
- /** Privates **/
-
-
- // The JSON file in JObject form
- // For gathering the incoming data
- object StringBuilderLock = new object();
- // To prevent multiple same-file access
- static object FileLock = new object();
-
- /*****************************************************************************************/
-
- ///
- /// SIMPL+ default constructor.
- ///
- public JsonToSimplFileMaster()
- {
- }
-
- ///
- /// Read, evaluate and udpate status
- ///
- public void EvaluateFile(string filepath)
- {
- try
- {
- OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
-
- var dirSeparator = Path.DirectorySeparatorChar;
- var dirSeparatorAlt = Path.AltDirectorySeparatorChar;
-
- var series = CrestronEnvironment.ProgramCompatibility;
-
- var is3Series = (eCrestronSeries.Series3 == (series & eCrestronSeries.Series3));
- OnBoolChange(is3Series, 0,
- JsonToSimplConstants.ProgramCompatibility3SeriesChange);
-
- var is4Series = (eCrestronSeries.Series4 == (series & eCrestronSeries.Series4));
- OnBoolChange(is4Series, 0,
- JsonToSimplConstants.ProgramCompatibility4SeriesChange);
-
- var isServer = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server;
- OnBoolChange(isServer, 0,
- JsonToSimplConstants.DevicePlatformValueChange);
-
- // get the roomID
- var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId;
- if (!string.IsNullOrEmpty(roomId))
- {
- OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange);
- }
-
- // get the roomName
- var roomName = Crestron.SimplSharp.InitialParametersClass.RoomName;
- if (!string.IsNullOrEmpty(roomName))
- {
- OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);
- }
-
- var rootDirectory = Directory.GetApplicationRootDirectory();
- OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);
-
- var splusPath = string.Empty;
- if (Regex.IsMatch(filepath, @"user", RegexOptions.IgnoreCase))
- {
- if (is4Series)
- splusPath = Regex.Replace(filepath, "user", "user", RegexOptions.IgnoreCase);
- else if (isServer)
- splusPath = Regex.Replace(filepath, "user", "User", RegexOptions.IgnoreCase);
- else
- splusPath = filepath;
- }
-
- filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator);
-
- Filepath = string.Format("{1}{0}{2}", dirSeparator, rootDirectory,
- filepath.TrimStart(dirSeparator, dirSeparatorAlt));
-
- OnStringChange(string.Format("Attempting to evaluate {0}", Filepath), 0, JsonToSimplConstants.StringValueChange);
-
- if (string.IsNullOrEmpty(Filepath))
- {
- OnStringChange(string.Format("Cannot evaluate file. JSON file path not set"), 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
- return;
- }
-
- // get file directory and name to search
- var fileDirectory = Path.GetDirectoryName(Filepath);
- var fileName = Path.GetFileName(Filepath);
-
- OnStringChange(string.Format("Checking '{0}' for '{1}'", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);
- Debug.Console(1, "Checking '{0}' for '{1}'", fileDirectory, fileName);
-
- if (Directory.Exists(fileDirectory))
- {
- // get the directory info
- var directoryInfo = new DirectoryInfo(fileDirectory);
-
- // get the file to be read
- var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault();
- if (actualFile == null)
- {
- var msg = string.Format("JSON file not found: {0}", Filepath);
- OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(msg);
- ErrorLog.Error(msg);
- return;
- }
-
- // \xSE\xR\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
- // \USER\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
- ActualFilePath = actualFile.FullName;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
- OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);
- Debug.Console(1, "Actual JSON file is {0}", ActualFilePath);
-
- Filename = actualFile.Name;
- OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);
- OnStringChange(string.Format("JSON Filename is {0}", Filename), 0, JsonToSimplConstants.StringValueChange);
- Debug.Console(1, "JSON Filename is {0}", Filename);
-
-
- FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator);
- OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);
- OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
- Debug.Console(1, "JSON File Path is {0}", FilePathName);
-
- var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
-
- JsonObject = JObject.Parse(json);
- foreach (var child in Children)
- child.ProcessAll();
-
- OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
- }
- else
- {
- OnStringChange(string.Format("'{0}' not found", fileDirectory), 0, JsonToSimplConstants.StringValueChange);
- Debug.Console(1, "'{0}' not found", fileDirectory);
- }
- }
- catch (Exception e)
- {
- var msg = string.Format("EvaluateFile Exception: Message\r{0}", e.Message);
- OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(msg);
- ErrorLog.Error(msg);
-
- var stackTrace = string.Format("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
- OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);
- CrestronConsole.PrintLine(stackTrace);
- ErrorLog.Error(stackTrace);
- }
- }
-
-
- ///
- /// Sets the debug level
- ///
- ///
- public void setDebugLevel(int level)
- {
- Debug.SetDebugLevel(level);
- }
-
- ///
- /// Saves the values to the file
- ///
- public override void Save()
- {
- // this code is duplicated in the other masters!!!!!!!!!!!!!
- UnsavedValues = new Dictionary();
- // Make each child update their values into master object
- foreach (var child in Children)
- {
- Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
- child.UpdateInputsForMaster();
- }
-
- if (UnsavedValues == null || UnsavedValues.Count == 0)
- {
- Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
- return;
- }
- lock (FileLock)
- {
- Debug.Console(1, "Saving");
- foreach (var path in UnsavedValues.Keys)
- {
- var tokenToReplace = JsonObject.SelectToken(path);
- if (tokenToReplace != null)
- {// It's found
- tokenToReplace.Replace(UnsavedValues[path]);
- Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
- }
- else // No token. Let's make one
- {
- //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
- Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
-
- // JContainer jpart = JsonObject;
- // // walk down the path and find where it goes
- //#warning Does not handle arrays.
- // foreach (var part in path.Split('.'))
- // {
-
- // var openPos = part.IndexOf('[');
- // if (openPos > -1)
- // {
- // openPos++; // move to number
- // var closePos = part.IndexOf(']');
- // var arrayName = part.Substring(0, openPos - 1); // get the name
- // var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos));
-
- // // Check if the array itself exists and add the item if so
- // if (jpart[arrayName] != null)
- // {
- // var arrayObj = jpart[arrayName] as JArray;
- // var item = arrayObj[index];
- // if (item == null)
- // arrayObj.Add(new JObject());
- // }
-
- // Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW");
- // continue;
- // }
- // // Build the
- // if (jpart[part] == null)
- // jpart.Add(new JProperty(part, new JObject()));
- // jpart = jpart[part] as JContainer;
- // }
- // jpart.Replace(UnsavedValues[path]);
- }
- }
- using (StreamWriter sw = new StreamWriter(ActualFilePath))
- {
- try
- {
- sw.Write(JsonObject.ToString());
- sw.Flush();
- }
- catch (Exception e)
- {
- string err = string.Format("Error writing JSON file:\r{0}", e);
- Debug.Console(0, err);
- ErrorLog.Warn(err);
- return;
- }
- }
- }
- }
- }
-}
+using System;
+//using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Represents a JSON file that can be read and written to
+ ///
+ public class JsonToSimplFileMaster : JsonToSimplMaster
+ {
+ ///
+ /// Sets the filepath as well as registers this with the Global.Masters list
+ ///
+ public string Filepath { get; private set; }
+
+ ///
+ /// Filepath to the actual file that will be read (Portal or local)
+ ///
+ public string ActualFilePath { get; private set; }
+
+ ///
+ ///
+ ///
+ public string Filename { get; private set; }
+ ///
+ ///
+ ///
+ public string FilePathName { get; private set; }
+
+ /*****************************************************************************************/
+ /** Privates **/
+
+
+ // The JSON file in JObject form
+ // For gathering the incoming data
+ object StringBuilderLock = new object();
+ // To prevent multiple same-file access
+ static object FileLock = new object();
+
+ /*****************************************************************************************/
+
+ ///
+ /// SIMPL+ default constructor.
+ ///
+ public JsonToSimplFileMaster()
+ {
+ }
+
+ ///
+ /// Read, evaluate and udpate status
+ ///
+ public void EvaluateFile(string filepath)
+ {
+ try
+ {
+ OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+
+ var dirSeparator = Path.DirectorySeparatorChar;
+ var dirSeparatorAlt = Path.AltDirectorySeparatorChar;
+
+ var series = CrestronEnvironment.ProgramCompatibility;
+
+ var is3Series = (eCrestronSeries.Series3 == (series & eCrestronSeries.Series3));
+ OnBoolChange(is3Series, 0,
+ JsonToSimplConstants.ProgramCompatibility3SeriesChange);
+
+ var is4Series = (eCrestronSeries.Series4 == (series & eCrestronSeries.Series4));
+ OnBoolChange(is4Series, 0,
+ JsonToSimplConstants.ProgramCompatibility4SeriesChange);
+
+ var isServer = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server;
+ OnBoolChange(isServer, 0,
+ JsonToSimplConstants.DevicePlatformValueChange);
+
+ // get the roomID
+ var roomId = Crestron.SimplSharp.InitialParametersClass.RoomId;
+ if (!string.IsNullOrEmpty(roomId))
+ {
+ OnStringChange(roomId, 0, JsonToSimplConstants.RoomIdChange);
+ }
+
+ // get the roomName
+ var roomName = Crestron.SimplSharp.InitialParametersClass.RoomName;
+ if (!string.IsNullOrEmpty(roomName))
+ {
+ OnStringChange(roomName, 0, JsonToSimplConstants.RoomNameChange);
+ }
+
+ var rootDirectory = Directory.GetApplicationRootDirectory();
+ OnStringChange(rootDirectory, 0, JsonToSimplConstants.RootDirectoryChange);
+
+ var splusPath = string.Empty;
+ if (Regex.IsMatch(filepath, @"user", RegexOptions.IgnoreCase))
+ {
+ if (is4Series)
+ splusPath = Regex.Replace(filepath, "user", "user", RegexOptions.IgnoreCase);
+ else if (isServer)
+ splusPath = Regex.Replace(filepath, "user", "User", RegexOptions.IgnoreCase);
+ else
+ splusPath = filepath;
+ }
+
+ filepath = splusPath.Replace(dirSeparatorAlt, dirSeparator);
+
+ Filepath = string.Format("{1}{0}{2}", dirSeparator, rootDirectory,
+ filepath.TrimStart(dirSeparator, dirSeparatorAlt));
+
+ OnStringChange(string.Format("Attempting to evaluate {0}", Filepath), 0, JsonToSimplConstants.StringValueChange);
+
+ if (string.IsNullOrEmpty(Filepath))
+ {
+ OnStringChange(string.Format("Cannot evaluate file. JSON file path not set"), 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
+ return;
+ }
+
+ // get file directory and name to search
+ var fileDirectory = Path.GetDirectoryName(Filepath);
+ var fileName = Path.GetFileName(Filepath);
+
+ OnStringChange(string.Format("Checking '{0}' for '{1}'", fileDirectory, fileName), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "Checking '{0}' for '{1}'", fileDirectory, fileName);
+
+ if (Directory.Exists(fileDirectory))
+ {
+ // get the directory info
+ var directoryInfo = new DirectoryInfo(fileDirectory);
+
+ // get the file to be read
+ var actualFile = directoryInfo.GetFiles(fileName).FirstOrDefault();
+ if (actualFile == null)
+ {
+ var msg = string.Format("JSON file not found: {0}", Filepath);
+ OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ return;
+ }
+
+ // \xSE\xR\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
+ // \USER\PDT000-Template_Main_Config-Combined_DSP_v00.02.json
+ ActualFilePath = actualFile.FullName;
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ OnStringChange(string.Format("Actual JSON file is {0}", ActualFilePath), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "Actual JSON file is {0}", ActualFilePath);
+
+ Filename = actualFile.Name;
+ OnStringChange(Filename, 0, JsonToSimplConstants.FilenameResolvedChange);
+ OnStringChange(string.Format("JSON Filename is {0}", Filename), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "JSON Filename is {0}", Filename);
+
+
+ FilePathName = string.Format(@"{0}{1}", actualFile.DirectoryName, dirSeparator);
+ OnStringChange(string.Format(@"{0}", actualFile.DirectoryName), 0, JsonToSimplConstants.FilePathResolvedChange);
+ OnStringChange(string.Format(@"JSON File Path is {0}", actualFile.DirectoryName), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "JSON File Path is {0}", FilePathName);
+
+ var json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
+
+ JsonObject = JObject.Parse(json);
+ foreach (var child in Children)
+ child.ProcessAll();
+
+ OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+ }
+ else
+ {
+ OnStringChange(string.Format("'{0}' not found", fileDirectory), 0, JsonToSimplConstants.StringValueChange);
+ Debug.Console(1, "'{0}' not found", fileDirectory);
+ }
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("EvaluateFile Exception: Message\r{0}", e.Message);
+ OnStringChange(msg, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+
+ var stackTrace = string.Format("EvaluateFile: Stack Trace\r{0}", e.StackTrace);
+ OnStringChange(stackTrace, 0, JsonToSimplConstants.StringValueChange);
+ CrestronConsole.PrintLine(stackTrace);
+ ErrorLog.Error(stackTrace);
+ }
+ }
+
+
+ ///
+ /// Sets the debug level
+ ///
+ ///
+ public void setDebugLevel(int level)
+ {
+ Debug.SetDebugLevel(level);
+ }
+
+ ///
+ /// Saves the values to the file
+ ///
+ public override void Save()
+ {
+ // this code is duplicated in the other masters!!!!!!!!!!!!!
+ UnsavedValues = new Dictionary();
+ // Make each child update their values into master object
+ foreach (var child in Children)
+ {
+ Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
+ child.UpdateInputsForMaster();
+ }
+
+ if (UnsavedValues == null || UnsavedValues.Count == 0)
+ {
+ Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
+ return;
+ }
+ lock (FileLock)
+ {
+ Debug.Console(1, "Saving");
+ foreach (var path in UnsavedValues.Keys)
+ {
+ var tokenToReplace = JsonObject.SelectToken(path);
+ if (tokenToReplace != null)
+ {// It's found
+ tokenToReplace.Replace(UnsavedValues[path]);
+ Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
+ }
+ else // No token. Let's make one
+ {
+ //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
+ Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
+
+ // JContainer jpart = JsonObject;
+ // // walk down the path and find where it goes
+ //#warning Does not handle arrays.
+ // foreach (var part in path.Split('.'))
+ // {
+
+ // var openPos = part.IndexOf('[');
+ // if (openPos > -1)
+ // {
+ // openPos++; // move to number
+ // var closePos = part.IndexOf(']');
+ // var arrayName = part.Substring(0, openPos - 1); // get the name
+ // var index = Convert.ToInt32(part.Substring(openPos, closePos - openPos));
+
+ // // Check if the array itself exists and add the item if so
+ // if (jpart[arrayName] != null)
+ // {
+ // var arrayObj = jpart[arrayName] as JArray;
+ // var item = arrayObj[index];
+ // if (item == null)
+ // arrayObj.Add(new JObject());
+ // }
+
+ // Debug.Console(0, "IGNORING MISSING ARRAY VALUE FOR NOW");
+ // continue;
+ // }
+ // // Build the
+ // if (jpart[part] == null)
+ // jpart.Add(new JProperty(part, new JObject()));
+ // jpart = jpart[part] as JContainer;
+ // }
+ // jpart.Replace(UnsavedValues[path]);
+ }
+ }
+ using (StreamWriter sw = new StreamWriter(ActualFilePath))
+ {
+ try
+ {
+ sw.Write(JsonObject.ToString());
+ sw.Flush();
+ }
+ catch (Exception e)
+ {
+ string err = string.Format("Error writing JSON file:\r{0}", e);
+ Debug.Console(0, err);
+ ErrorLog.Warn(err);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs b/src/JsonToSimpl/JsonToSimplFixedPathObject.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplFixedPathObject.cs
rename to src/JsonToSimpl/JsonToSimplFixedPathObject.cs
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs b/src/JsonToSimpl/JsonToSimplGenericMaster.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplGenericMaster.cs
rename to src/JsonToSimpl/JsonToSimplGenericMaster.cs
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs b/src/JsonToSimpl/JsonToSimplMaster.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplMaster.cs
rename to src/JsonToSimpl/JsonToSimplMaster.cs
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs b/src/JsonToSimpl/JsonToSimplPortalFileMaster.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs
rename to src/JsonToSimpl/JsonToSimplPortalFileMaster.cs
index ca307e5..2a2b535 100644
--- a/Pepperdash Core/Pepperdash Core/JsonToSimpl/JsonToSimplPortalFileMaster.cs
+++ b/src/JsonToSimpl/JsonToSimplPortalFileMaster.cs
@@ -1,195 +1,195 @@
-using System;
-//using System.IO;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.CrestronIO;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-using PepperDash.Core.Config;
-
-namespace PepperDash.Core.JsonToSimpl
-{
- ///
- /// Portal File Master
- ///
- public class JsonToSimplPortalFileMaster : JsonToSimplMaster
- {
- ///
- /// Sets the filepath as well as registers this with the Global.Masters list
- ///
- public string PortalFilepath { get; private set; }
-
- ///
- /// File path of the actual file being read (Portal or local)
- ///
- public string ActualFilePath { get; private set; }
-
- /*****************************************************************************************/
- /** Privates **/
-
- // To prevent multiple same-file access
- object StringBuilderLock = new object();
- static object FileLock = new object();
-
- /*****************************************************************************************/
-
- ///
- /// SIMPL+ default constructor.
- ///
- public JsonToSimplPortalFileMaster()
- {
- }
-
- ///
- /// Read, evaluate and udpate status
- ///
- public void EvaluateFile(string portalFilepath)
- {
- PortalFilepath = portalFilepath;
-
- OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
- if (string.IsNullOrEmpty(PortalFilepath))
- {
- CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
- return;
- }
-
- // Resolve possible wildcarded filename
-
- // If the portal file is xyz.json, then
- // the file we want to check for first will be called xyz.local.json
- var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json");
- Debug.Console(0, this, "Checking for local file {0}", localFilepath);
- var actualLocalFile = GetActualFileInfoFromPath(localFilepath);
-
- if (actualLocalFile != null)
- {
- ActualFilePath = actualLocalFile.FullName;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
- }
- // If the local file does not exist, then read the portal file xyz.json
- // and create the local.
- else
- {
- Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
- var actualPortalFile = GetActualFileInfoFromPath(portalFilepath);
- if (actualPortalFile != null)
- {
- var newLocalPath = Path.ChangeExtension(actualPortalFile.FullName, "local.json");
- // got the portal file, hand off to the merge / save method
- PortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath);
- ActualFilePath = newLocalPath;
- OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
- }
- else
- {
- var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath);
- Debug.Console(1, this, msg);
- ErrorLog.Error(msg);
- return;
- }
- }
-
- // At this point we should have a local file. Do it.
- Debug.Console(1, "Reading local JSON file {0}", ActualFilePath);
-
- string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
-
- try
- {
- JsonObject = JObject.Parse(json);
- foreach (var child in Children)
- child.ProcessAll();
- OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("JSON parsing failed:\r{0}", e);
- CrestronConsole.PrintLine(msg);
- ErrorLog.Error(msg);
- return;
- }
- }
-
- ///
- /// Returns the FileInfo object for a given path, with possible wildcards
- ///
- ///
- ///
- FileInfo GetActualFileInfoFromPath(string path)
- {
- var dir = Path.GetDirectoryName(path);
- var localFilename = Path.GetFileName(path);
- var directory = new DirectoryInfo(dir);
- // search the directory for the file w/ wildcards
- return directory.GetFiles(localFilename).FirstOrDefault();
- }
-
- ///
- ///
- ///
- ///
- public void setDebugLevel(int level)
- {
- Debug.SetDebugLevel(level);
- }
-
- ///
- ///
- ///
- public override void Save()
- {
- // this code is duplicated in the other masters!!!!!!!!!!!!!
- UnsavedValues = new Dictionary();
- // Make each child update their values into master object
- foreach (var child in Children)
- {
- Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
- child.UpdateInputsForMaster();
- }
-
- if (UnsavedValues == null || UnsavedValues.Count == 0)
- {
- Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
- return;
- }
- lock (FileLock)
- {
- Debug.Console(1, "Saving");
- foreach (var path in UnsavedValues.Keys)
- {
- var tokenToReplace = JsonObject.SelectToken(path);
- if (tokenToReplace != null)
- {// It's found
- tokenToReplace.Replace(UnsavedValues[path]);
- Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
- }
- else // No token. Let's make one
- {
- //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
- Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
-
- }
- }
- using (StreamWriter sw = new StreamWriter(ActualFilePath))
- {
- try
- {
- sw.Write(JsonObject.ToString());
- sw.Flush();
- }
- catch (Exception e)
- {
- string err = string.Format("Error writing JSON file:\r{0}", e);
- Debug.Console(0, err);
- ErrorLog.Warn(err);
- return;
- }
- }
- }
- }
- }
-}
+using System;
+//using System.IO;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.CrestronIO;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+using PepperDash.Core.Config;
+
+namespace PepperDash.Core.JsonToSimpl
+{
+ ///
+ /// Portal File Master
+ ///
+ public class JsonToSimplPortalFileMaster : JsonToSimplMaster
+ {
+ ///
+ /// Sets the filepath as well as registers this with the Global.Masters list
+ ///
+ public string PortalFilepath { get; private set; }
+
+ ///
+ /// File path of the actual file being read (Portal or local)
+ ///
+ public string ActualFilePath { get; private set; }
+
+ /*****************************************************************************************/
+ /** Privates **/
+
+ // To prevent multiple same-file access
+ object StringBuilderLock = new object();
+ static object FileLock = new object();
+
+ /*****************************************************************************************/
+
+ ///
+ /// SIMPL+ default constructor.
+ ///
+ public JsonToSimplPortalFileMaster()
+ {
+ }
+
+ ///
+ /// Read, evaluate and udpate status
+ ///
+ public void EvaluateFile(string portalFilepath)
+ {
+ PortalFilepath = portalFilepath;
+
+ OnBoolChange(false, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+ if (string.IsNullOrEmpty(PortalFilepath))
+ {
+ CrestronConsole.PrintLine("Cannot evaluate file. JSON file path not set");
+ return;
+ }
+
+ // Resolve possible wildcarded filename
+
+ // If the portal file is xyz.json, then
+ // the file we want to check for first will be called xyz.local.json
+ var localFilepath = Path.ChangeExtension(PortalFilepath, "local.json");
+ Debug.Console(0, this, "Checking for local file {0}", localFilepath);
+ var actualLocalFile = GetActualFileInfoFromPath(localFilepath);
+
+ if (actualLocalFile != null)
+ {
+ ActualFilePath = actualLocalFile.FullName;
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ }
+ // If the local file does not exist, then read the portal file xyz.json
+ // and create the local.
+ else
+ {
+ Debug.Console(1, this, "Local JSON file not found {0}\rLoading portal JSON file", localFilepath);
+ var actualPortalFile = GetActualFileInfoFromPath(portalFilepath);
+ if (actualPortalFile != null)
+ {
+ var newLocalPath = Path.ChangeExtension(actualPortalFile.FullName, "local.json");
+ // got the portal file, hand off to the merge / save method
+ PortalConfigReader.ReadAndMergeFileIfNecessary(actualPortalFile.FullName, newLocalPath);
+ ActualFilePath = newLocalPath;
+ OnStringChange(ActualFilePath, 0, JsonToSimplConstants.ActualFilePathChange);
+ }
+ else
+ {
+ var msg = string.Format("Portal JSON file not found: {0}", PortalFilepath);
+ Debug.Console(1, this, msg);
+ ErrorLog.Error(msg);
+ return;
+ }
+ }
+
+ // At this point we should have a local file. Do it.
+ Debug.Console(1, "Reading local JSON file {0}", ActualFilePath);
+
+ string json = File.ReadToEnd(ActualFilePath, System.Text.Encoding.ASCII);
+
+ try
+ {
+ JsonObject = JObject.Parse(json);
+ foreach (var child in Children)
+ child.ProcessAll();
+ OnBoolChange(true, 0, JsonToSimplConstants.JsonIsValidBoolChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("JSON parsing failed:\r{0}", e);
+ CrestronConsole.PrintLine(msg);
+ ErrorLog.Error(msg);
+ return;
+ }
+ }
+
+ ///
+ /// Returns the FileInfo object for a given path, with possible wildcards
+ ///
+ ///
+ ///
+ FileInfo GetActualFileInfoFromPath(string path)
+ {
+ var dir = Path.GetDirectoryName(path);
+ var localFilename = Path.GetFileName(path);
+ var directory = new DirectoryInfo(dir);
+ // search the directory for the file w/ wildcards
+ return directory.GetFiles(localFilename).FirstOrDefault();
+ }
+
+ ///
+ ///
+ ///
+ ///
+ public void setDebugLevel(int level)
+ {
+ Debug.SetDebugLevel(level);
+ }
+
+ ///
+ ///
+ ///
+ public override void Save()
+ {
+ // this code is duplicated in the other masters!!!!!!!!!!!!!
+ UnsavedValues = new Dictionary();
+ // Make each child update their values into master object
+ foreach (var child in Children)
+ {
+ Debug.Console(1, "Master [{0}] checking child [{1}] for updates to save", UniqueID, child.Key);
+ child.UpdateInputsForMaster();
+ }
+
+ if (UnsavedValues == null || UnsavedValues.Count == 0)
+ {
+ Debug.Console(1, "Master [{0}] No updated values to save. Skipping", UniqueID);
+ return;
+ }
+ lock (FileLock)
+ {
+ Debug.Console(1, "Saving");
+ foreach (var path in UnsavedValues.Keys)
+ {
+ var tokenToReplace = JsonObject.SelectToken(path);
+ if (tokenToReplace != null)
+ {// It's found
+ tokenToReplace.Replace(UnsavedValues[path]);
+ Debug.Console(1, "JSON Master[{0}] Updating '{1}'", UniqueID, path);
+ }
+ else // No token. Let's make one
+ {
+ //http://stackoverflow.com/questions/17455052/how-to-set-the-value-of-a-json-path-using-json-net
+ Debug.Console(1, "JSON Master[{0}] Cannot write value onto missing property: '{1}'", UniqueID, path);
+
+ }
+ }
+ using (StreamWriter sw = new StreamWriter(ActualFilePath))
+ {
+ try
+ {
+ sw.Write(JsonObject.ToString());
+ sw.Flush();
+ }
+ catch (Exception e)
+ {
+ string err = string.Format("Error writing JSON file:\r{0}", e);
+ Debug.Console(0, err);
+ ErrorLog.Warn(err);
+ return;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/Pepperdash Core/Pepperdash Core/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs b/src/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs
rename to src/JsonToSimpl/REMOVE JsonToSimplFixedPathObject.cs
diff --git a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs b/src/Logging/Debug.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Logging/Debug.cs
rename to src/Logging/Debug.cs
index 1a84968..62bca73 100644
--- a/Pepperdash Core/Pepperdash Core/Logging/Debug.cs
+++ b/src/Logging/Debug.cs
@@ -1,625 +1,625 @@
-using System;
-using System.Collections.Generic;
-using System.Text.RegularExpressions;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.Reflection;
-using Crestron.SimplSharp.CrestronLogger;
-using Crestron.SimplSharp.CrestronIO;
-using Newtonsoft.Json;
-using PepperDash.Core.DebugThings;
-
-
-namespace PepperDash.Core
-{
- ///
- /// Contains debug commands for use in various situations
- ///
- public static class Debug
- {
- ///
- /// Describes the folder location where a given program stores it's debug level memory. By default, the
- /// file written will be named appNdebug where N is 1-10.
- ///
- public static string OldFilePathPrefix = @"\nvram\debug\";
-
- ///
- /// Describes the new folder location where a given program stores it's debug level memory. By default, the
- /// file written will be named appNdebug where N is 1-10.
- ///
- public static string NewFilePathPrefix = @"\user\debug\";
-
- ///
- /// The name of the file containing the current debug settings.
- ///
- public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
-
- ///
- /// Debug level to set for a given program.
- ///
- public static int Level { get; private set; }
-
- ///
- /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
- ///
- public static bool DoNotLoadOnNextBoot { get; private set; }
-
- private static DebugContextCollection _contexts;
-
- private const int SaveTimeoutMs = 30000;
-
- ///
- /// Version for the currently loaded PepperDashCore dll
- ///
- public static string PepperDashCoreVersion { get; private set; }
-
- private static CTimer _saveTimer;
-
- ///
- /// When true, the IncludedExcludedKeys dict will contain keys to include.
- /// When false (default), IncludedExcludedKeys will contain keys to exclude.
- ///
- private static bool _excludeAllMode;
-
- //static bool ExcludeNoKeyMessages;
-
- private static readonly Dictionary IncludedExcludedKeys;
-
- static Debug()
- {
- // Get the assembly version and print it to console and the log
- GetVersion();
-
- string msg = "";
-
- if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
- {
- msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, PepperDashCoreVersion);
- }
- else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
- {
- msg = string.Format("[Room {0}] Using PepperDash_Core v{1}", InitialParametersClass.RoomId, PepperDashCoreVersion);
- }
-
- CrestronConsole.PrintLine(msg);
-
- LogError(ErrorLogLevel.Notice, msg);
-
- IncludedExcludedKeys = new Dictionary();
-
- //CrestronDataStoreStatic.InitCrestronDataStore();
- if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
- {
- // Add command to console
- CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
- "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
-
- CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
- "appdebug:P [0-2]: Sets the application's console debug message level",
- ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
- "appdebuglog:P [all] Use \"all\" for full log.",
- ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
- "appdebugclear:P Clears the current custom log",
- ConsoleAccessLevelEnum.AccessOperator);
- CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
- "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
- }
-
- CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
-
- LoadMemory();
-
- var context = _contexts.GetOrCreateItem("DEFAULT");
- Level = context.Level;
- DoNotLoadOnNextBoot = context.DoNotLoadOnNextBoot;
-
- if(DoNotLoadOnNextBoot)
- CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
-
- try
- {
- if (InitialParametersClass.NumberOfRemovableDrives > 0)
- {
- CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives);
- CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages.
- }
- else
- CrestronConsole.PrintLine("No RM Drive(s) Present.");
- }
- catch (Exception e)
- {
-
- CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
- }
- }
-
- private static void GetVersion()
- {
- var assembly = Assembly.GetExecutingAssembly();
- var ver =
- assembly
- .GetCustomAttributes(typeof (AssemblyInformationalVersionAttribute), false);
-
- if (ver != null && ver.Length > 0)
- {
- var verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
-
- if (verAttribute != null)
- {
- PepperDashCoreVersion = verAttribute.InformationalVersion;
- }
- }
- else
- {
- var version = assembly.GetName().Version;
- PepperDashCoreVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build,
- version.Revision);
- }
- }
-
- ///
- /// Used to save memory when shutting down
- ///
- ///
- static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
- {
- if (programEventType == eProgramStatusEventType.Stopping)
- {
- if (_saveTimer != null)
- {
- _saveTimer.Stop();
- _saveTimer = null;
- }
- Console(0, "Saving debug settings");
- SaveMemory();
- }
- }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDebugFromConsole(string levelString)
- {
- try
- {
- if (string.IsNullOrEmpty(levelString.Trim()))
- {
- CrestronConsole.PrintLine("AppDebug level = {0}", Level);
- return;
- }
-
- SetDebugLevel(Convert.ToInt32(levelString));
- }
- catch
- {
- CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
- }
- }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
- {
- try
- {
- if (string.IsNullOrEmpty(stateString.Trim()))
- {
- CrestronConsole.PrintLine("DoNotLoadOnNextBoot = {0}", DoNotLoadOnNextBoot);
- return;
- }
-
- SetDoNotLoadOnNextBoot(Boolean.Parse(stateString));
- }
- catch
- {
- CrestronConsole.PrintLine("Usage: donotloadonnextboot:P [true/false]");
- }
- }
-
- ///
- /// Callback for console command
- ///
- ///
- public static void SetDebugFilterFromConsole(string items)
- {
- var str = items.Trim();
- if (str == "?")
- {
- CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
- "+all: at beginning puts filter into 'default include' mode\r" +
- " All keys that follow will be excluded from output.\r" +
- "-all: at beginning puts filter into 'default excluse all' mode.\r" +
- " All keys that follow will be the only keys that are shown\r" +
- "+nokey: Enables messages with no key (default)\r" +
- "-nokey: Disables messages with no key.\r" +
- "(nokey settings are independent of all other settings)");
- return;
- }
- var keys = Regex.Split(str, @"\s*");
- foreach (var keyToken in keys)
- {
- var lkey = keyToken.ToLower();
- if (lkey == "+all")
- {
- IncludedExcludedKeys.Clear();
- _excludeAllMode = false;
- }
- else if (lkey == "-all")
- {
- IncludedExcludedKeys.Clear();
- _excludeAllMode = true;
- }
- //else if (lkey == "+nokey")
- //{
- // ExcludeNoKeyMessages = false;
- //}
- //else if (lkey == "-nokey")
- //{
- // ExcludeNoKeyMessages = true;
- //}
- else
- {
- string key;
- if (lkey.StartsWith("-"))
- {
- key = lkey.Substring(1);
- // if in exclude all mode, we need to remove this from the inclusions
- if (_excludeAllMode)
- {
- if (IncludedExcludedKeys.ContainsKey(key))
- IncludedExcludedKeys.Remove(key);
- }
- // otherwise include all mode, add to the exclusions
- else
- {
- IncludedExcludedKeys[key] = new object();
- }
- }
- else if (lkey.StartsWith("+"))
- {
- key = lkey.Substring(1);
- // if in exclude all mode, we need to add this as inclusion
- if (_excludeAllMode)
- {
-
- IncludedExcludedKeys[key] = new object();
- }
- // otherwise include all mode, remove this from exclusions
- else
- {
- if (IncludedExcludedKeys.ContainsKey(key))
- IncludedExcludedKeys.Remove(key);
- }
- }
- }
- }
- }
-
-
- ///
- /// Sets the debug level
- ///
- /// Valid values 0 (no debug), 1 (critical), 2 (all messages)
- public static void SetDebugLevel(int level)
- {
- if (level <= 2)
- {
- Level = level;
- _contexts.GetOrCreateItem("DEFAULT").Level = level;
- SaveMemoryOnTimeout();
-
- CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
- InitialParametersClass.ApplicationNumber, Level);
-
- //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level);
- //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
- // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err);
- }
- }
-
- ///
- /// sets the settings for a device or creates a new entry
- ///
- ///
- ///
- ///
- public static void SetDeviceDebugSettings(string deviceKey, object settings)
- {
- _contexts.SetDebugSettingsForKey(deviceKey, settings);
- SaveMemoryOnTimeout();
- }
-
- ///
- /// Gets the device settings for a device by key or returns null
- ///
- ///
- ///
- public static object GetDeviceDebugSettingsForKey(string deviceKey)
- {
- return _contexts.GetDebugSettingsForKey(deviceKey);
- }
-
- ///
- /// Sets the flag to prevent application starting on next boot
- ///
- ///
- public static void SetDoNotLoadOnNextBoot(bool state)
- {
- DoNotLoadOnNextBoot = state;
- _contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
- SaveMemoryOnTimeout();
-
- CrestronConsole.PrintLine("[Application {0}], Do Not Start on Next Boot set to {1}",
- InitialParametersClass.ApplicationNumber, DoNotLoadOnNextBoot);
- }
-
- ///
- ///
- ///
- public static void ShowDebugLog(string s)
- {
- var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
- foreach (var l in loglist)
- CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
- }
-
- ///
- /// Prints message to console if current debug level is equal to or higher than the level of this message.
- /// Uses CrestronConsole.PrintLine.
- ///
- ///
- /// Console format string
- /// Object parameters
- public static void Console(uint level, string format, params object[] items)
- {
- if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
- {
- var logString = string.Format("[level {0}] {1}", level, string.Format(format, items));
-
- LogError(ErrorLogLevel.Notice, logString);
- return;
- }
-
- if(Level < level)
- {
- return;
- }
-
- CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
- string.Format(format, items));
- }
-
- ///
- /// Logs to Console when at-level, and all messages to error log, including device key
- ///
- public static void Console(uint level, IKeyed dev, string format, params object[] items)
- {
- if (Level >= level)
- Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
- }
-
- ///
- /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
- /// Uses CrestronConsole.PrintLine.
- ///
- public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
- string format, params object[] items)
- {
- var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
- if (errorLogLevel != ErrorLogLevel.None)
- {
- LogError(errorLogLevel, str);
- }
- if (Level >= level)
- {
- Console(level, str);
- }
- }
-
- ///
- /// Logs to Console when at-level, and all messages to error log
- ///
- public static void Console(uint level, ErrorLogLevel errorLogLevel,
- string format, params object[] items)
- {
- var str = string.Format(format, items);
- if (errorLogLevel != ErrorLogLevel.None)
- {
- LogError(errorLogLevel, str);
- }
- if (Level >= level)
- {
- Console(level, str);
- }
- }
-
- ///
- /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
- /// or above the level provided, then the output will be written to both console and the log. Otherwise
- /// it will only be written to the log.
- ///
- public static void ConsoleWithLog(uint level, string format, params object[] items)
- {
- var str = string.Format(format, items);
- if (Level >= level)
- CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
- CrestronLogger.WriteToLog(str, level);
- }
-
- ///
- /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
- /// or above the level provided, then the output will be written to both console and the log. Otherwise
- /// it will only be written to the log.
- ///
- public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
- {
- var str = string.Format(format, items);
- if (Level >= level)
- ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
- }
-
- ///
- /// Prints to log and error log
- ///
- ///
- ///
- public static void LogError(ErrorLogLevel errorLogLevel, string str)
- {
-
- var msg = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str);
- switch (errorLogLevel)
- {
- case ErrorLogLevel.Error:
- ErrorLog.Error(msg);
- break;
- case ErrorLogLevel.Warning:
- ErrorLog.Warn(msg);
- break;
- case ErrorLogLevel.Notice:
- ErrorLog.Notice(msg);
- break;
- }
- }
-
- ///
- /// Writes the memory object after timeout
- ///
- static void SaveMemoryOnTimeout()
- {
- Console(0, "Saving debug settings");
- if (_saveTimer == null)
- _saveTimer = new CTimer(o =>
- {
- _saveTimer = null;
- SaveMemory();
- }, SaveTimeoutMs);
- else
- _saveTimer.Reset(SaveTimeoutMs);
- }
-
- ///
- /// Writes the memory - use SaveMemoryOnTimeout
- ///
- static void SaveMemory()
- {
- //var dir = @"\NVRAM\debug";
- //if (!Directory.Exists(dir))
- // Directory.Create(dir);
-
- var fileName = GetMemoryFileName();
-
- Console(0, ErrorLogLevel.Notice, "Loading debug settings file from {0}", fileName);
-
- using (var sw = new StreamWriter(fileName))
- {
- var json = JsonConvert.SerializeObject(_contexts);
- sw.Write(json);
- sw.Flush();
- }
- }
-
- ///
- ///
- ///
- static void LoadMemory()
- {
- var file = GetMemoryFileName();
- if (File.Exists(file))
- {
- using (var sr = new StreamReader(file))
- {
- var json = sr.ReadToEnd();
- _contexts = JsonConvert.DeserializeObject(json);
-
- if (_contexts != null)
- {
- Console(1, "Debug memory restored from file");
- return;
- }
- }
- }
-
- _contexts = new DebugContextCollection();
- }
-
- ///
- /// Helper to get the file path for this app's debug memory
- ///
- static string GetMemoryFileName()
- {
- if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
- {
- CheckForMigration();
- return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
- }
-
- return string.Format("{0}{1}user{1}debugSettings{1}{2}.json",Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
- }
-
- private static void CheckForMigration()
- {
- var oldFilePath = String.Format(@"\nvram\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
- var newFilePath = String.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
-
- //check for file at old path
- if (!File.Exists(oldFilePath))
- {
- Console(0, ErrorLogLevel.Notice,
- String.Format(
- @"Debug settings file migration not necessary. Using file at \user\debugSettings\program{0}",
- InitialParametersClass.ApplicationNumber));
-
- return;
- }
-
- //create the new directory if it doesn't already exist
- if (!Directory.Exists(@"\user\debugSettings"))
- {
- Directory.CreateDirectory(@"\user\debugSettings");
- }
-
- Console(0, ErrorLogLevel.Notice,
- String.Format(
- @"File found at \nvram\debugSettings\program{0}. Migrating to \user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber));
-
- //Copy file from old path to new path, then delete it. This will overwrite the existing file
- File.Copy(oldFilePath, newFilePath, true);
- File.Delete(oldFilePath);
-
- //Check if the old directory is empty, then delete it if it is
- if (Directory.GetFiles(@"\nvram\debugSettings").Length > 0)
- {
- return;
- }
-
- Directory.Delete(@"\nvram\debugSettings");
- }
-
- ///
- /// Error level to for message to be logged at
- ///
- public enum ErrorLogLevel
- {
- ///
- /// Error
- ///
- Error,
- ///
- /// Warning
- ///
- Warning,
- ///
- /// Notice
- ///
- Notice,
- ///
- /// None
- ///
- None,
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+using Crestron.SimplSharp;
+using Crestron.SimplSharp.Reflection;
+using Crestron.SimplSharp.CrestronLogger;
+using Crestron.SimplSharp.CrestronIO;
+using Newtonsoft.Json;
+using PepperDash.Core.DebugThings;
+
+
+namespace PepperDash.Core
+{
+ ///
+ /// Contains debug commands for use in various situations
+ ///
+ public static class Debug
+ {
+ ///
+ /// Describes the folder location where a given program stores it's debug level memory. By default, the
+ /// file written will be named appNdebug where N is 1-10.
+ ///
+ public static string OldFilePathPrefix = @"\nvram\debug\";
+
+ ///
+ /// Describes the new folder location where a given program stores it's debug level memory. By default, the
+ /// file written will be named appNdebug where N is 1-10.
+ ///
+ public static string NewFilePathPrefix = @"\user\debug\";
+
+ ///
+ /// The name of the file containing the current debug settings.
+ ///
+ public static string FileName = string.Format(@"app{0}Debug.json", InitialParametersClass.ApplicationNumber);
+
+ ///
+ /// Debug level to set for a given program.
+ ///
+ public static int Level { get; private set; }
+
+ ///
+ /// When this is true, the configuration file will NOT be loaded until triggered by either a console command or a signal
+ ///
+ public static bool DoNotLoadOnNextBoot { get; private set; }
+
+ private static DebugContextCollection _contexts;
+
+ private const int SaveTimeoutMs = 30000;
+
+ ///
+ /// Version for the currently loaded PepperDashCore dll
+ ///
+ public static string PepperDashCoreVersion { get; private set; }
+
+ private static CTimer _saveTimer;
+
+ ///
+ /// When true, the IncludedExcludedKeys dict will contain keys to include.
+ /// When false (default), IncludedExcludedKeys will contain keys to exclude.
+ ///
+ private static bool _excludeAllMode;
+
+ //static bool ExcludeNoKeyMessages;
+
+ private static readonly Dictionary IncludedExcludedKeys;
+
+ static Debug()
+ {
+ // Get the assembly version and print it to console and the log
+ GetVersion();
+
+ string msg = "";
+
+ if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
+ {
+ msg = string.Format("[App {0}] Using PepperDash_Core v{1}", InitialParametersClass.ApplicationNumber, PepperDashCoreVersion);
+ }
+ else if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
+ {
+ msg = string.Format("[Room {0}] Using PepperDash_Core v{1}", InitialParametersClass.RoomId, PepperDashCoreVersion);
+ }
+
+ CrestronConsole.PrintLine(msg);
+
+ LogError(ErrorLogLevel.Notice, msg);
+
+ IncludedExcludedKeys = new Dictionary();
+
+ //CrestronDataStoreStatic.InitCrestronDataStore();
+ if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
+ {
+ // Add command to console
+ CrestronConsole.AddNewConsoleCommand(SetDoNotLoadOnNextBootFromConsole, "donotloadonnextboot",
+ "donotloadonnextboot:P [true/false]: Should the application load on next boot", ConsoleAccessLevelEnum.AccessOperator);
+
+ CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
+ "appdebug:P [0-2]: Sets the application's console debug message level",
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(ShowDebugLog, "appdebuglog",
+ "appdebuglog:P [all] Use \"all\" for full log.",
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(s => CrestronLogger.Clear(false), "appdebugclear",
+ "appdebugclear:P Clears the current custom log",
+ ConsoleAccessLevelEnum.AccessOperator);
+ CrestronConsole.AddNewConsoleCommand(SetDebugFilterFromConsole, "appdebugfilter",
+ "appdebugfilter [params]", ConsoleAccessLevelEnum.AccessOperator);
+ }
+
+ CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
+
+ LoadMemory();
+
+ var context = _contexts.GetOrCreateItem("DEFAULT");
+ Level = context.Level;
+ DoNotLoadOnNextBoot = context.DoNotLoadOnNextBoot;
+
+ if(DoNotLoadOnNextBoot)
+ CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
+
+ try
+ {
+ if (InitialParametersClass.NumberOfRemovableDrives > 0)
+ {
+ CrestronConsole.PrintLine("{0} RM Drive(s) Present.", InitialParametersClass.NumberOfRemovableDrives);
+ CrestronLogger.Initialize(2, LoggerModeEnum.DEFAULT); // Use RM instead of DEFAULT as not to double-up console messages.
+ }
+ else
+ CrestronConsole.PrintLine("No RM Drive(s) Present.");
+ }
+ catch (Exception e)
+ {
+
+ CrestronConsole.PrintLine("Initializing of CrestronLogger failed: {0}", e);
+ }
+ }
+
+ private static void GetVersion()
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var ver =
+ assembly
+ .GetCustomAttributes(typeof (AssemblyInformationalVersionAttribute), false);
+
+ if (ver != null && ver.Length > 0)
+ {
+ var verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
+
+ if (verAttribute != null)
+ {
+ PepperDashCoreVersion = verAttribute.InformationalVersion;
+ }
+ }
+ else
+ {
+ var version = assembly.GetName().Version;
+ PepperDashCoreVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build,
+ version.Revision);
+ }
+ }
+
+ ///
+ /// Used to save memory when shutting down
+ ///
+ ///
+ static void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
+ {
+ if (programEventType == eProgramStatusEventType.Stopping)
+ {
+ if (_saveTimer != null)
+ {
+ _saveTimer.Stop();
+ _saveTimer = null;
+ }
+ Console(0, "Saving debug settings");
+ SaveMemory();
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ public static void SetDebugFromConsole(string levelString)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(levelString.Trim()))
+ {
+ CrestronConsole.PrintLine("AppDebug level = {0}", Level);
+ return;
+ }
+
+ SetDebugLevel(Convert.ToInt32(levelString));
+ }
+ catch
+ {
+ CrestronConsole.PrintLine("Usage: appdebug:P [0-2]");
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ public static void SetDoNotLoadOnNextBootFromConsole(string stateString)
+ {
+ try
+ {
+ if (string.IsNullOrEmpty(stateString.Trim()))
+ {
+ CrestronConsole.PrintLine("DoNotLoadOnNextBoot = {0}", DoNotLoadOnNextBoot);
+ return;
+ }
+
+ SetDoNotLoadOnNextBoot(Boolean.Parse(stateString));
+ }
+ catch
+ {
+ CrestronConsole.PrintLine("Usage: donotloadonnextboot:P [true/false]");
+ }
+ }
+
+ ///
+ /// Callback for console command
+ ///
+ ///
+ public static void SetDebugFilterFromConsole(string items)
+ {
+ var str = items.Trim();
+ if (str == "?")
+ {
+ CrestronConsole.ConsoleCommandResponse("Usage:\r APPDEBUGFILTER key1 key2 key3....\r " +
+ "+all: at beginning puts filter into 'default include' mode\r" +
+ " All keys that follow will be excluded from output.\r" +
+ "-all: at beginning puts filter into 'default excluse all' mode.\r" +
+ " All keys that follow will be the only keys that are shown\r" +
+ "+nokey: Enables messages with no key (default)\r" +
+ "-nokey: Disables messages with no key.\r" +
+ "(nokey settings are independent of all other settings)");
+ return;
+ }
+ var keys = Regex.Split(str, @"\s*");
+ foreach (var keyToken in keys)
+ {
+ var lkey = keyToken.ToLower();
+ if (lkey == "+all")
+ {
+ IncludedExcludedKeys.Clear();
+ _excludeAllMode = false;
+ }
+ else if (lkey == "-all")
+ {
+ IncludedExcludedKeys.Clear();
+ _excludeAllMode = true;
+ }
+ //else if (lkey == "+nokey")
+ //{
+ // ExcludeNoKeyMessages = false;
+ //}
+ //else if (lkey == "-nokey")
+ //{
+ // ExcludeNoKeyMessages = true;
+ //}
+ else
+ {
+ string key;
+ if (lkey.StartsWith("-"))
+ {
+ key = lkey.Substring(1);
+ // if in exclude all mode, we need to remove this from the inclusions
+ if (_excludeAllMode)
+ {
+ if (IncludedExcludedKeys.ContainsKey(key))
+ IncludedExcludedKeys.Remove(key);
+ }
+ // otherwise include all mode, add to the exclusions
+ else
+ {
+ IncludedExcludedKeys[key] = new object();
+ }
+ }
+ else if (lkey.StartsWith("+"))
+ {
+ key = lkey.Substring(1);
+ // if in exclude all mode, we need to add this as inclusion
+ if (_excludeAllMode)
+ {
+
+ IncludedExcludedKeys[key] = new object();
+ }
+ // otherwise include all mode, remove this from exclusions
+ else
+ {
+ if (IncludedExcludedKeys.ContainsKey(key))
+ IncludedExcludedKeys.Remove(key);
+ }
+ }
+ }
+ }
+ }
+
+
+ ///
+ /// Sets the debug level
+ ///
+ /// Valid values 0 (no debug), 1 (critical), 2 (all messages)
+ public static void SetDebugLevel(int level)
+ {
+ if (level <= 2)
+ {
+ Level = level;
+ _contexts.GetOrCreateItem("DEFAULT").Level = level;
+ SaveMemoryOnTimeout();
+
+ CrestronConsole.PrintLine("[Application {0}], Debug level set to {1}",
+ InitialParametersClass.ApplicationNumber, Level);
+
+ //var err = CrestronDataStoreStatic.SetLocalUintValue("DebugLevel", level);
+ //if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
+ // CrestronConsole.PrintLine("Error saving console debug level setting: {0}", err);
+ }
+ }
+
+ ///
+ /// sets the settings for a device or creates a new entry
+ ///
+ ///
+ ///
+ ///
+ public static void SetDeviceDebugSettings(string deviceKey, object settings)
+ {
+ _contexts.SetDebugSettingsForKey(deviceKey, settings);
+ SaveMemoryOnTimeout();
+ }
+
+ ///
+ /// Gets the device settings for a device by key or returns null
+ ///
+ ///
+ ///
+ public static object GetDeviceDebugSettingsForKey(string deviceKey)
+ {
+ return _contexts.GetDebugSettingsForKey(deviceKey);
+ }
+
+ ///
+ /// Sets the flag to prevent application starting on next boot
+ ///
+ ///
+ public static void SetDoNotLoadOnNextBoot(bool state)
+ {
+ DoNotLoadOnNextBoot = state;
+ _contexts.GetOrCreateItem("DEFAULT").DoNotLoadOnNextBoot = state;
+ SaveMemoryOnTimeout();
+
+ CrestronConsole.PrintLine("[Application {0}], Do Not Start on Next Boot set to {1}",
+ InitialParametersClass.ApplicationNumber, DoNotLoadOnNextBoot);
+ }
+
+ ///
+ ///
+ ///
+ public static void ShowDebugLog(string s)
+ {
+ var loglist = CrestronLogger.PrintTheLog(s.ToLower() == "all");
+ foreach (var l in loglist)
+ CrestronConsole.ConsoleCommandResponse(l + CrestronEnvironment.NewLine);
+ }
+
+ ///
+ /// Prints message to console if current debug level is equal to or higher than the level of this message.
+ /// Uses CrestronConsole.PrintLine.
+ ///
+ ///
+ /// Console format string
+ /// Object parameters
+ public static void Console(uint level, string format, params object[] items)
+ {
+ if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server)
+ {
+ var logString = string.Format("[level {0}] {1}", level, string.Format(format, items));
+
+ LogError(ErrorLogLevel.Notice, logString);
+ return;
+ }
+
+ if(Level < level)
+ {
+ return;
+ }
+
+ CrestronConsole.PrintLine("[{0}]App {1}:{2}", DateTime.Now.ToString("HH:mm:ss.fff"), InitialParametersClass.ApplicationNumber,
+ string.Format(format, items));
+ }
+
+ ///
+ /// Logs to Console when at-level, and all messages to error log, including device key
+ ///
+ public static void Console(uint level, IKeyed dev, string format, params object[] items)
+ {
+ if (Level >= level)
+ Console(level, "[{0}] {1}", dev.Key, string.Format(format, items));
+ }
+
+ ///
+ /// Prints message to console if current debug level is equal to or higher than the level of this message. Always sends message to Error Log.
+ /// Uses CrestronConsole.PrintLine.
+ ///
+ public static void Console(uint level, IKeyed dev, ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ var str = string.Format("[{0}] {1}", dev.Key, string.Format(format, items));
+ if (errorLogLevel != ErrorLogLevel.None)
+ {
+ LogError(errorLogLevel, str);
+ }
+ if (Level >= level)
+ {
+ Console(level, str);
+ }
+ }
+
+ ///
+ /// Logs to Console when at-level, and all messages to error log
+ ///
+ public static void Console(uint level, ErrorLogLevel errorLogLevel,
+ string format, params object[] items)
+ {
+ var str = string.Format(format, items);
+ if (errorLogLevel != ErrorLogLevel.None)
+ {
+ LogError(errorLogLevel, str);
+ }
+ if (Level >= level)
+ {
+ Console(level, str);
+ }
+ }
+
+ ///
+ /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
+ /// or above the level provided, then the output will be written to both console and the log. Otherwise
+ /// it will only be written to the log.
+ ///
+ public static void ConsoleWithLog(uint level, string format, params object[] items)
+ {
+ var str = string.Format(format, items);
+ if (Level >= level)
+ CrestronConsole.PrintLine("App {0}:{1}", InitialParametersClass.ApplicationNumber, str);
+ CrestronLogger.WriteToLog(str, level);
+ }
+
+ ///
+ /// Logs to both console and the custom user log (not the built-in error log). If appdebug level is set at
+ /// or above the level provided, then the output will be written to both console and the log. Otherwise
+ /// it will only be written to the log.
+ ///
+ public static void ConsoleWithLog(uint level, IKeyed dev, string format, params object[] items)
+ {
+ var str = string.Format(format, items);
+ if (Level >= level)
+ ConsoleWithLog(level, "[{0}] {1}", dev.Key, str);
+ }
+
+ ///
+ /// Prints to log and error log
+ ///
+ ///
+ ///
+ public static void LogError(ErrorLogLevel errorLogLevel, string str)
+ {
+
+ var msg = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? string.Format("App {0}:{1}", InitialParametersClass.ApplicationNumber, str) : string.Format("Room {0}:{1}", InitialParametersClass.RoomId, str);
+ switch (errorLogLevel)
+ {
+ case ErrorLogLevel.Error:
+ ErrorLog.Error(msg);
+ break;
+ case ErrorLogLevel.Warning:
+ ErrorLog.Warn(msg);
+ break;
+ case ErrorLogLevel.Notice:
+ ErrorLog.Notice(msg);
+ break;
+ }
+ }
+
+ ///
+ /// Writes the memory object after timeout
+ ///
+ static void SaveMemoryOnTimeout()
+ {
+ Console(0, "Saving debug settings");
+ if (_saveTimer == null)
+ _saveTimer = new CTimer(o =>
+ {
+ _saveTimer = null;
+ SaveMemory();
+ }, SaveTimeoutMs);
+ else
+ _saveTimer.Reset(SaveTimeoutMs);
+ }
+
+ ///
+ /// Writes the memory - use SaveMemoryOnTimeout
+ ///
+ static void SaveMemory()
+ {
+ //var dir = @"\NVRAM\debug";
+ //if (!Directory.Exists(dir))
+ // Directory.Create(dir);
+
+ var fileName = GetMemoryFileName();
+
+ Console(0, ErrorLogLevel.Notice, "Loading debug settings file from {0}", fileName);
+
+ using (var sw = new StreamWriter(fileName))
+ {
+ var json = JsonConvert.SerializeObject(_contexts);
+ sw.Write(json);
+ sw.Flush();
+ }
+ }
+
+ ///
+ ///
+ ///
+ static void LoadMemory()
+ {
+ var file = GetMemoryFileName();
+ if (File.Exists(file))
+ {
+ using (var sr = new StreamReader(file))
+ {
+ var json = sr.ReadToEnd();
+ _contexts = JsonConvert.DeserializeObject(json);
+
+ if (_contexts != null)
+ {
+ Console(1, "Debug memory restored from file");
+ return;
+ }
+ }
+ }
+
+ _contexts = new DebugContextCollection();
+ }
+
+ ///
+ /// Helper to get the file path for this app's debug memory
+ ///
+ static string GetMemoryFileName()
+ {
+ if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance)
+ {
+ CheckForMigration();
+ return string.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
+ }
+
+ return string.Format("{0}{1}user{1}debugSettings{1}{2}.json",Directory.GetApplicationRootDirectory(), Path.DirectorySeparatorChar, InitialParametersClass.RoomId);
+ }
+
+ private static void CheckForMigration()
+ {
+ var oldFilePath = String.Format(@"\nvram\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
+ var newFilePath = String.Format(@"\user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber);
+
+ //check for file at old path
+ if (!File.Exists(oldFilePath))
+ {
+ Console(0, ErrorLogLevel.Notice,
+ String.Format(
+ @"Debug settings file migration not necessary. Using file at \user\debugSettings\program{0}",
+ InitialParametersClass.ApplicationNumber));
+
+ return;
+ }
+
+ //create the new directory if it doesn't already exist
+ if (!Directory.Exists(@"\user\debugSettings"))
+ {
+ Directory.CreateDirectory(@"\user\debugSettings");
+ }
+
+ Console(0, ErrorLogLevel.Notice,
+ String.Format(
+ @"File found at \nvram\debugSettings\program{0}. Migrating to \user\debugSettings\program{0}", InitialParametersClass.ApplicationNumber));
+
+ //Copy file from old path to new path, then delete it. This will overwrite the existing file
+ File.Copy(oldFilePath, newFilePath, true);
+ File.Delete(oldFilePath);
+
+ //Check if the old directory is empty, then delete it if it is
+ if (Directory.GetFiles(@"\nvram\debugSettings").Length > 0)
+ {
+ return;
+ }
+
+ Directory.Delete(@"\nvram\debugSettings");
+ }
+
+ ///
+ /// Error level to for message to be logged at
+ ///
+ public enum ErrorLogLevel
+ {
+ ///
+ /// Error
+ ///
+ Error,
+ ///
+ /// Warning
+ ///
+ Warning,
+ ///
+ /// Notice
+ ///
+ Notice,
+ ///
+ /// None
+ ///
+ None,
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Logging/DebugContext.cs b/src/Logging/DebugContext.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Logging/DebugContext.cs
rename to src/Logging/DebugContext.cs
diff --git a/Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs b/src/Logging/DebugMemory.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Logging/DebugMemory.cs
rename to src/Logging/DebugMemory.cs
diff --git a/Pepperdash Core/Pepperdash Core/Network/DiscoveryThings.cs b/src/Network/DiscoveryThings.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Network/DiscoveryThings.cs
rename to src/Network/DiscoveryThings.cs
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/Config.cs b/src/PasswordManagement/Config.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/Config.cs
rename to src/PasswordManagement/Config.cs
index 4d6b035..22aa488 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/Config.cs
+++ b/src/PasswordManagement/Config.cs
@@ -1,26 +1,26 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.PasswordManagement
-{
- ///
- /// JSON password configuration
- ///
- public class PasswordConfig
- {
- ///
- /// Password object configured password
- ///
- public string password { get; set; }
- ///
- /// Constructor
- ///
- public PasswordConfig()
- {
-
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ ///
+ /// JSON password configuration
+ ///
+ public class PasswordConfig
+ {
+ ///
+ /// Password object configured password
+ ///
+ public string password { get; set; }
+ ///
+ /// Constructor
+ ///
+ public PasswordConfig()
+ {
+
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/Constants.cs b/src/PasswordManagement/Constants.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/Constants.cs
rename to src/PasswordManagement/Constants.cs
index 8ba89e0..65a1bf4 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/Constants.cs
+++ b/src/PasswordManagement/Constants.cs
@@ -1,57 +1,57 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.PasswordManagement
-{
- ///
- /// Constants
- ///
- public class PasswordManagementConstants
- {
- ///
- /// Generic boolean value change constant
- ///
- public const ushort BoolValueChange = 1;
- ///
- /// Evaluated boolean change constant
- ///
- public const ushort PasswordInitializedChange = 2;
- ///
- /// Update busy change const
- ///
- public const ushort PasswordUpdateBusyChange = 3;
- ///
- /// Password is valid change constant
- ///
- public const ushort PasswordValidationChange = 4;
- ///
- /// Password LED change constant
- ///
- public const ushort PasswordLedFeedbackChange = 5;
-
- ///
- /// Generic ushort value change constant
- ///
- public const ushort UshrtValueChange = 101;
- ///
- /// Password count
- ///
- public const ushort PasswordManagerCountChange = 102;
- ///
- /// Password selecte index change constant
- ///
- public const ushort PasswordSelectIndexChange = 103;
- ///
- /// Password length
- ///
- public const ushort PasswordLengthChange = 104;
-
- ///
- /// Generic string value change constant
- ///
- public const ushort StringValueChange = 201;
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ ///
+ /// Constants
+ ///
+ public class PasswordManagementConstants
+ {
+ ///
+ /// Generic boolean value change constant
+ ///
+ public const ushort BoolValueChange = 1;
+ ///
+ /// Evaluated boolean change constant
+ ///
+ public const ushort PasswordInitializedChange = 2;
+ ///
+ /// Update busy change const
+ ///
+ public const ushort PasswordUpdateBusyChange = 3;
+ ///
+ /// Password is valid change constant
+ ///
+ public const ushort PasswordValidationChange = 4;
+ ///
+ /// Password LED change constant
+ ///
+ public const ushort PasswordLedFeedbackChange = 5;
+
+ ///
+ /// Generic ushort value change constant
+ ///
+ public const ushort UshrtValueChange = 101;
+ ///
+ /// Password count
+ ///
+ public const ushort PasswordManagerCountChange = 102;
+ ///
+ /// Password selecte index change constant
+ ///
+ public const ushort PasswordSelectIndexChange = 103;
+ ///
+ /// Password length
+ ///
+ public const ushort PasswordLengthChange = 104;
+
+ ///
+ /// Generic string value change constant
+ ///
+ public const ushort StringValueChange = 201;
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-Config.cs b/src/PasswordManagement/OLD-ARRAY-Config.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-Config.cs
rename to src/PasswordManagement/OLD-ARRAY-Config.cs
index c49df19..8167c4f 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-Config.cs
+++ b/src/PasswordManagement/OLD-ARRAY-Config.cs
@@ -1,149 +1,149 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.PasswordManagement
-{
- // Example JSON password array configuration object
- //{
- // "global":{
- // "passwords":[
- // {
- // "key": "Password01",
- // "name": "Technician Password",
- // "enabled": true,
- // "password": "1988"
- // }
- // ]
- // }
- //}
-
- ///
- /// JSON password array configuration object
- ///
- //public class PasswordConfig
- //{
- // ///
- // /// Key used to search for object in JSON array
- // ///
- // public string key { get; set; }
- // ///
- // /// Friendly name of password object
- // ///
- // public string name { get; set; }
- // ///
- // /// Password object enabled
- // ///
- // public bool enabled { get; set; }
- // ///
- // ///
- // ///
- // public ushort simplEnabled
- // {
- // get { return (ushort)(enabled ? 1 : 0); }
- // set { enabled = Convert.ToBoolean(value); }
- // }
- // ///
- // /// Password object configured password
- // ///
- // public string password { get; set; }
- // ///
- // /// Password type
- // ///
- // private int type { get; set; }
- // ///
- // /// Password Type for S+
- // ///
- // public ushort simplType
- // {
- // get { return Convert.ToUInt16(type); }
- // set { type = value; }
- // }
- // ///
- // /// Password path
- // /// **FUTURE** implementation of saving passwords recieved from Fusion or other external sources back to config
- // ///
- // public string path { get; set; }
- // ///
- // /// Constructor
- // ///
- // public PasswordConfig()
- // {
- // simplEnabled = 0;
- // simplType = 0;
- // }
- //}
-
- // Example JSON password collections configuration object
- //{
- // "global": {
- // "passwords": {
- // "1": {
- // "name": "Technician Password",
- // "password": "2468"
- // },
- // "2": {
- // "name": "System Password",
- // "password": "123456"
- // },
- // "3": {
- // "name": "Master Password",
- // "password": "abc123"
- // },
- // "5": {
- // "name": "Backdoor Password",
- // "password": "1988"
- // },
- // "10": {
- // "name": "Backdoor Password",
- // "password": "1988"
- // }
- // }
- // }
- //}
-
- ///
- /// JSON password array configuration object
- ///
- public class PasswordConfig
- {
- ///
- /// Password object configured password
- ///
- public string password { get; set; }
- ///
- /// Constructor
- ///
- public PasswordConfig()
- {
-
- }
- }
-
- ///
- /// Global JSON object
- ///
- //public class GlobalConfig
- //{
- // //public List passwords { get; set; }
- // public Dictionary passwords { get; set; }
-
- // ///
- // /// Constructor
- // ///
- // public GlobalConfig()
- // {
-
- // }
- //}
-
- ///
- /// Root JSON object
- ///
- //public class RootObject
- //{
- // public GlobalConfig global { get; set; }
- //}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ // Example JSON password array configuration object
+ //{
+ // "global":{
+ // "passwords":[
+ // {
+ // "key": "Password01",
+ // "name": "Technician Password",
+ // "enabled": true,
+ // "password": "1988"
+ // }
+ // ]
+ // }
+ //}
+
+ ///
+ /// JSON password array configuration object
+ ///
+ //public class PasswordConfig
+ //{
+ // ///
+ // /// Key used to search for object in JSON array
+ // ///
+ // public string key { get; set; }
+ // ///
+ // /// Friendly name of password object
+ // ///
+ // public string name { get; set; }
+ // ///
+ // /// Password object enabled
+ // ///
+ // public bool enabled { get; set; }
+ // ///
+ // ///
+ // ///
+ // public ushort simplEnabled
+ // {
+ // get { return (ushort)(enabled ? 1 : 0); }
+ // set { enabled = Convert.ToBoolean(value); }
+ // }
+ // ///
+ // /// Password object configured password
+ // ///
+ // public string password { get; set; }
+ // ///
+ // /// Password type
+ // ///
+ // private int type { get; set; }
+ // ///
+ // /// Password Type for S+
+ // ///
+ // public ushort simplType
+ // {
+ // get { return Convert.ToUInt16(type); }
+ // set { type = value; }
+ // }
+ // ///
+ // /// Password path
+ // /// **FUTURE** implementation of saving passwords recieved from Fusion or other external sources back to config
+ // ///
+ // public string path { get; set; }
+ // ///
+ // /// Constructor
+ // ///
+ // public PasswordConfig()
+ // {
+ // simplEnabled = 0;
+ // simplType = 0;
+ // }
+ //}
+
+ // Example JSON password collections configuration object
+ //{
+ // "global": {
+ // "passwords": {
+ // "1": {
+ // "name": "Technician Password",
+ // "password": "2468"
+ // },
+ // "2": {
+ // "name": "System Password",
+ // "password": "123456"
+ // },
+ // "3": {
+ // "name": "Master Password",
+ // "password": "abc123"
+ // },
+ // "5": {
+ // "name": "Backdoor Password",
+ // "password": "1988"
+ // },
+ // "10": {
+ // "name": "Backdoor Password",
+ // "password": "1988"
+ // }
+ // }
+ // }
+ //}
+
+ ///
+ /// JSON password array configuration object
+ ///
+ public class PasswordConfig
+ {
+ ///
+ /// Password object configured password
+ ///
+ public string password { get; set; }
+ ///
+ /// Constructor
+ ///
+ public PasswordConfig()
+ {
+
+ }
+ }
+
+ ///
+ /// Global JSON object
+ ///
+ //public class GlobalConfig
+ //{
+ // //public List passwords { get; set; }
+ // public Dictionary passwords { get; set; }
+
+ // ///
+ // /// Constructor
+ // ///
+ // public GlobalConfig()
+ // {
+
+ // }
+ //}
+
+ ///
+ /// Root JSON object
+ ///
+ //public class RootObject
+ //{
+ // public GlobalConfig global { get; set; }
+ //}
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordClient.cs b/src/PasswordManagement/OLD-ARRAY-PasswordClient.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordClient.cs
rename to src/PasswordManagement/OLD-ARRAY-PasswordClient.cs
index 634badf..540d935 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordClient.cs
+++ b/src/PasswordManagement/OLD-ARRAY-PasswordClient.cs
@@ -1,207 +1,207 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.PasswordManagement
-{
- public class PasswordClient
- {
- ///
- /// Password Client
- ///
- public PasswordConfig Client { get; set; }
- ///
- /// Used to build the password entered by the user
- ///
- public string PasswordToValidate { get; set; }
-
- ///
- /// Boolean event
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event
- ///
- public event EventHandler StringChange;
-
- ///
- /// Constructor
- ///
- public PasswordClient()
- {
-
- }
-
- ///
- /// Initialize method
- ///
- ///
- public void Initialize(string key)
- {
- OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
-
- Client = new PasswordConfig();
- PasswordToValidate = "";
-
- // there has to be a better way to get the index of the current index of password
- ushort i = 0;
- foreach (var password in PasswordManager.Passwords)
- {
- i++;
- OnUshrtChange((ushort)password.Key, (ushort)password.Key, PasswordManagementConstants.PasswordKey);
- }
-
- OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
- }
-
- ///
- /// Retrieves password by key
- ///
- ///
- //public void GetPasswordByKey(string key)
- //{
- // if (string.IsNullOrEmpty(key))
- // {
- // Debug.Console(1, "PassowrdClient.GetPasswordByKey failed:\rKey {0} is null or empty", key);
- // return;
- // }
-
- // PasswordConfig password = PasswordManager.Passwords.FirstOrDefault(p => p.key.Equals(key));
- // if (password == null)
- // {
- // OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
- // return;
- // }
-
- // Client = password;
- // OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
- // OnStringChange(Client.key, 0, PasswordManagementConstants.PasswordKeySelected);
- //}
-
- ///
- /// Retrieve password by index
- ///
- ///
- public void GetPasswordByIndex(ushort key)
- {
- PasswordConfig pw = PasswordManager.Passwords[key];
- if (pw == null)
- {
- OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
- return;
- }
-
- Client = pw;
- OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
- OnUshrtChange(key, 0, PasswordManagementConstants.PasswordKeySelected);
- }
-
- ///
- /// Password validation method
- ///
- ///
- public void ValidatePassword(string password)
- {
- if (string.IsNullOrEmpty(password))
- return;
-
- if (string.Equals(Client.password, password))
- {
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsValid);
- }
- else
- {
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsInvalid);
- }
-
-
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsValid);
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsInvalid);
-
- ClearPassword();
- }
-
- ///
- /// Builds the user entered passwrod string, will attempt to validate the user entered
- /// password against the selected password when the length of the 2 are equal
- ///
- ///
- public void BuildPassword(string data)
- {
- PasswordToValidate = String.Concat(PasswordToValidate, data);
- OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
-
- if (PasswordToValidate.Length == Client.password.Length)
- ValidatePassword(PasswordToValidate);
- }
-
- ///
- /// Clears the user entered password and resets the LEDs
- ///
- public void ClearPassword()
- {
- PasswordToValidate = "";
- OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
-
- for(var i = 1; i <= Client.password.Length; i++)
- OnBoolChange(false, (ushort)i, PasswordManagementConstants.PasswordLedChange);
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected ushort change event handler
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ public class PasswordClient
+ {
+ ///
+ /// Password Client
+ ///
+ public PasswordConfig Client { get; set; }
+ ///
+ /// Used to build the password entered by the user
+ ///
+ public string PasswordToValidate { get; set; }
+
+ ///
+ /// Boolean event
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Constructor
+ ///
+ public PasswordClient()
+ {
+
+ }
+
+ ///
+ /// Initialize method
+ ///
+ ///
+ public void Initialize(string key)
+ {
+ OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
+
+ Client = new PasswordConfig();
+ PasswordToValidate = "";
+
+ // there has to be a better way to get the index of the current index of password
+ ushort i = 0;
+ foreach (var password in PasswordManager.Passwords)
+ {
+ i++;
+ OnUshrtChange((ushort)password.Key, (ushort)password.Key, PasswordManagementConstants.PasswordKey);
+ }
+
+ OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
+ }
+
+ ///
+ /// Retrieves password by key
+ ///
+ ///
+ //public void GetPasswordByKey(string key)
+ //{
+ // if (string.IsNullOrEmpty(key))
+ // {
+ // Debug.Console(1, "PassowrdClient.GetPasswordByKey failed:\rKey {0} is null or empty", key);
+ // return;
+ // }
+
+ // PasswordConfig password = PasswordManager.Passwords.FirstOrDefault(p => p.key.Equals(key));
+ // if (password == null)
+ // {
+ // OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
+ // return;
+ // }
+
+ // Client = password;
+ // OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
+ // OnStringChange(Client.key, 0, PasswordManagementConstants.PasswordKeySelected);
+ //}
+
+ ///
+ /// Retrieve password by index
+ ///
+ ///
+ public void GetPasswordByIndex(ushort key)
+ {
+ PasswordConfig pw = PasswordManager.Passwords[key];
+ if (pw == null)
+ {
+ OnUshrtChange(0, 0, PasswordManagementConstants.SelectedPasswordLength);
+ return;
+ }
+
+ Client = pw;
+ OnUshrtChange((ushort)Client.password.Length, 0, PasswordManagementConstants.SelectedPasswordLength);
+ OnUshrtChange(key, 0, PasswordManagementConstants.PasswordKeySelected);
+ }
+
+ ///
+ /// Password validation method
+ ///
+ ///
+ public void ValidatePassword(string password)
+ {
+ if (string.IsNullOrEmpty(password))
+ return;
+
+ if (string.Equals(Client.password, password))
+ {
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsValid);
+ }
+ else
+ {
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordIsInvalid);
+ }
+
+
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsValid);
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordIsInvalid);
+
+ ClearPassword();
+ }
+
+ ///
+ /// Builds the user entered passwrod string, will attempt to validate the user entered
+ /// password against the selected password when the length of the 2 are equal
+ ///
+ ///
+ public void BuildPassword(string data)
+ {
+ PasswordToValidate = String.Concat(PasswordToValidate, data);
+ OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
+
+ if (PasswordToValidate.Length == Client.password.Length)
+ ValidatePassword(PasswordToValidate);
+ }
+
+ ///
+ /// Clears the user entered password and resets the LEDs
+ ///
+ public void ClearPassword()
+ {
+ PasswordToValidate = "";
+ OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedChange);
+
+ for(var i = 1; i <= Client.password.Length; i++)
+ OnBoolChange(false, (ushort)i, PasswordManagementConstants.PasswordLedChange);
+ }
+
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected ushort change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordManager.cs b/src/PasswordManagement/OLD-ARRAY-PasswordManager.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordManager.cs
rename to src/PasswordManagement/OLD-ARRAY-PasswordManager.cs
index b34db61..45b65e9 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/OLD-ARRAY-PasswordManager.cs
+++ b/src/PasswordManagement/OLD-ARRAY-PasswordManager.cs
@@ -1,233 +1,233 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Crestron.SimplSharp;
-using PepperDash.Core.JsonToSimpl;
-using PepperDash.Core.JsonStandardObjects;
-
-namespace PepperDash.Core.PasswordManagement
-{
- public class PasswordManager
- {
- ///
- /// List of passwords configured
- ///
- public static Dictionary Passwords = new Dictionary();
- private Dictionary TempPasswords = new Dictionary();
-
- CTimer UpdateTimer;
- public long UpdateTimerElapsedMs = 5000;
-
- ///
- /// Boolean event
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event
- ///
- public event EventHandler StringChange;
-
-
- ///
- /// Constructor
- ///
- public PasswordManager()
- {
- Passwords.Clear();
- }
-
- ///
- /// Initialize method
- ///
- ///
- ///
- //public void Initialize(string uniqueId, string key)
- //{
- // OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
-
- // try
- // {
- // if(string.IsNullOrEmpty(uniqueId) || string.IsNullOrEmpty(key))
- // {
- // Debug.Console(1, "PasswordManager.Initialize({0}, {1}) null or empty parameters", uniqueId, key);
- // return;
- // }
-
- // JsonToSimplMaster master = J2SGlobal.GetMasterByFile(uniqueId);
- // if(master == null)
- // {
- // Debug.Console(1, "PassowrdManager.Initialize failed:\rCould not find JSON file with uniqueID {0}", uniqueId);
- // return;
- // }
-
- // var global = master.JsonObject.ToObject().global;
- // var passwords = global.passwords;
- // if(passwords == null)
- // {
- // Debug.Console(1, "PasswordManager.Initialize failed:\rCould not find password object");
- // return;
- // }
-
- // foreach(var password in passwords)
- // {
- // if (password != null)
- // {
- // var index = passwords.IndexOf(password);
-
- // password.path = string.Format("global.passwords[{0}]", index);
- // Debug.Console(1, "PasswordManager.Initialize: {0}, {1}, {2}, {3}, {4}, {5}", password.key, password.name, password.simplEnabled, password.simplType, password.password, password.path);
- // //AddPassword(password);
-
- // OnStringChange(password.path, (ushort)index, PasswordManagementConstants.FullPathToPassword);
- // OnStringChange(password.key, (ushort)index, PasswordManagementConstants.PasswordKey);
- // }
- // }
-
- // OnUshrtChange(Convert.ToUInt16(Passwords.Count), 0, PasswordManagementConstants.PasswordListCount);
- // }
- // catch(Exception e)
- // {
- // var msg = string.Format("PasswordManager.Initialize({0}, {1}) failed:\r{2}", uniqueId, key, e.Message);
- // CrestronConsole.PrintLine(msg);
- // ErrorLog.Error(msg);
- // }
- // finally
- // {
- // OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
- // }
- //}
-
- ///
- /// Adds password to the list
- ///
- ///
- //private void AddPassword(PasswordConfig password)
- //{
- // if (password == null)
- // return;
-
- // var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
- // if (item != null)
- // Passwords.Remove(item);
- // Passwords.Add(password);
-
- // Passwords.Sort((x, y) => string.Compare(x.key, y.key));
- //}
-
- ///
- /// Removes password from the list
- ///
- ///
- //private void RemovePassword(PasswordConfig password)
- //{
- // if (password == null)
- // return;
-
- // var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
- // if (item != null)
- // Passwords.Remove(item);
- //}
-
- ///
- /// Updates password stored in the dictonary
- ///
- ///
- ///
- ///
- public void UpdatePassword(ushort key, string password)
- {
- if (string.IsNullOrEmpty(password))
- return;
-
- var pw = TempPasswords[key];
- if (pw == null)
- {
- pw = new PasswordConfig();
- }
- pw.password = password;
-
- if (UpdateTimer == null)
- {
- // (o) => SavePasswords removes the need to create a callback method that takes in an object
- UpdateTimer = new CTimer((o) => StorePassword(), UpdateTimerElapsedMs);
- }
- else
- {
- UpdateTimer.Reset();
- }
- }
-
- ///
- /// Stores the updated passwords in TempPassword in the Passwords dictionary
- ///
- private void StorePassword()
- {
- UpdateTimer.Stop();
-
- foreach (var tempPw in TempPasswords)
- {
- Passwords[tempPw.Key] = tempPw.Value;
- }
-
- TempPasswords.Clear();
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected ushort change event handler
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Crestron.SimplSharp;
+using PepperDash.Core.JsonToSimpl;
+using PepperDash.Core.JsonStandardObjects;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ public class PasswordManager
+ {
+ ///
+ /// List of passwords configured
+ ///
+ public static Dictionary Passwords = new Dictionary();
+ private Dictionary TempPasswords = new Dictionary();
+
+ CTimer UpdateTimer;
+ public long UpdateTimerElapsedMs = 5000;
+
+ ///
+ /// Boolean event
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event
+ ///
+ public event EventHandler StringChange;
+
+
+ ///
+ /// Constructor
+ ///
+ public PasswordManager()
+ {
+ Passwords.Clear();
+ }
+
+ ///
+ /// Initialize method
+ ///
+ ///
+ ///
+ //public void Initialize(string uniqueId, string key)
+ //{
+ // OnBoolChange(false, 0, PasswordManagementConstants.BoolEvaluatedChange);
+
+ // try
+ // {
+ // if(string.IsNullOrEmpty(uniqueId) || string.IsNullOrEmpty(key))
+ // {
+ // Debug.Console(1, "PasswordManager.Initialize({0}, {1}) null or empty parameters", uniqueId, key);
+ // return;
+ // }
+
+ // JsonToSimplMaster master = J2SGlobal.GetMasterByFile(uniqueId);
+ // if(master == null)
+ // {
+ // Debug.Console(1, "PassowrdManager.Initialize failed:\rCould not find JSON file with uniqueID {0}", uniqueId);
+ // return;
+ // }
+
+ // var global = master.JsonObject.ToObject().global;
+ // var passwords = global.passwords;
+ // if(passwords == null)
+ // {
+ // Debug.Console(1, "PasswordManager.Initialize failed:\rCould not find password object");
+ // return;
+ // }
+
+ // foreach(var password in passwords)
+ // {
+ // if (password != null)
+ // {
+ // var index = passwords.IndexOf(password);
+
+ // password.path = string.Format("global.passwords[{0}]", index);
+ // Debug.Console(1, "PasswordManager.Initialize: {0}, {1}, {2}, {3}, {4}, {5}", password.key, password.name, password.simplEnabled, password.simplType, password.password, password.path);
+ // //AddPassword(password);
+
+ // OnStringChange(password.path, (ushort)index, PasswordManagementConstants.FullPathToPassword);
+ // OnStringChange(password.key, (ushort)index, PasswordManagementConstants.PasswordKey);
+ // }
+ // }
+
+ // OnUshrtChange(Convert.ToUInt16(Passwords.Count), 0, PasswordManagementConstants.PasswordListCount);
+ // }
+ // catch(Exception e)
+ // {
+ // var msg = string.Format("PasswordManager.Initialize({0}, {1}) failed:\r{2}", uniqueId, key, e.Message);
+ // CrestronConsole.PrintLine(msg);
+ // ErrorLog.Error(msg);
+ // }
+ // finally
+ // {
+ // OnBoolChange(true, 0, PasswordManagementConstants.BoolEvaluatedChange);
+ // }
+ //}
+
+ ///
+ /// Adds password to the list
+ ///
+ ///
+ //private void AddPassword(PasswordConfig password)
+ //{
+ // if (password == null)
+ // return;
+
+ // var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
+ // if (item != null)
+ // Passwords.Remove(item);
+ // Passwords.Add(password);
+
+ // Passwords.Sort((x, y) => string.Compare(x.key, y.key));
+ //}
+
+ ///
+ /// Removes password from the list
+ ///
+ ///
+ //private void RemovePassword(PasswordConfig password)
+ //{
+ // if (password == null)
+ // return;
+
+ // var item = Passwords.FirstOrDefault(i => i.key.Equals(password.key));
+ // if (item != null)
+ // Passwords.Remove(item);
+ //}
+
+ ///
+ /// Updates password stored in the dictonary
+ ///
+ ///
+ ///
+ ///
+ public void UpdatePassword(ushort key, string password)
+ {
+ if (string.IsNullOrEmpty(password))
+ return;
+
+ var pw = TempPasswords[key];
+ if (pw == null)
+ {
+ pw = new PasswordConfig();
+ }
+ pw.password = password;
+
+ if (UpdateTimer == null)
+ {
+ // (o) => SavePasswords removes the need to create a callback method that takes in an object
+ UpdateTimer = new CTimer((o) => StorePassword(), UpdateTimerElapsedMs);
+ }
+ else
+ {
+ UpdateTimer.Reset();
+ }
+ }
+
+ ///
+ /// Stores the updated passwords in TempPassword in the Passwords dictionary
+ ///
+ private void StorePassword()
+ {
+ UpdateTimer.Stop();
+
+ foreach (var tempPw in TempPasswords)
+ {
+ Passwords[tempPw.Key] = tempPw.Value;
+ }
+
+ TempPasswords.Clear();
+ }
+
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected ushort change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordClient.cs b/src/PasswordManagement/PasswordClient.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordClient.cs
rename to src/PasswordManagement/PasswordClient.cs
index f965dfa..2d66c52 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordClient.cs
+++ b/src/PasswordManagement/PasswordClient.cs
@@ -1,202 +1,202 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.PasswordManagement
-{
- ///
- /// A class to allow user interaction with the PasswordManager
- ///
- public class PasswordClient
- {
- ///
- /// Password selected
- ///
- public string Password { get; set; }
- ///
- /// Password selected key
- ///
- public ushort Key { get; set; }
- ///
- /// Used to build the password entered by the user
- ///
- public string PasswordToValidate { get; set; }
-
- ///
- /// Boolean event
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event
- ///
- public event EventHandler StringChange;
-
- ///
- /// Constructor
- ///
- public PasswordClient()
- {
- PasswordManager.PasswordChange += new EventHandler(PasswordManager_PasswordChange);
- }
-
- ///
- /// Initialize method
- ///
- public void Initialize()
- {
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordInitializedChange);
-
- Password = "";
- PasswordToValidate = "";
-
- OnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
- }
-
- ///
- /// Retrieve password by index
- ///
- ///
- public void GetPasswordByIndex(ushort key)
- {
- OnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
-
- Key = key;
-
- var pw = PasswordManager.Passwords[Key];
- if (pw == null)
- {
- OnUshrtChange(0, 0, PasswordManagementConstants.PasswordLengthChange);
- return;
- }
-
- Password = pw;
- OnUshrtChange((ushort)Password.Length, 0, PasswordManagementConstants.PasswordLengthChange);
- OnUshrtChange(key, 0, PasswordManagementConstants.PasswordSelectIndexChange);
- }
-
- ///
- /// Password validation method
- ///
- ///
- public void ValidatePassword(string password)
- {
- if (string.IsNullOrEmpty(password))
- return;
-
- if (string.Equals(Password, password))
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordValidationChange);
- else
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordValidationChange);
-
- ClearPassword();
- }
-
- ///
- /// Builds the user entered passwrod string, will attempt to validate the user entered
- /// password against the selected password when the length of the 2 are equal
- ///
- ///
- public void BuildPassword(string data)
- {
- PasswordToValidate = String.Concat(PasswordToValidate, data);
- OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);
-
- if (PasswordToValidate.Length == Password.Length)
- ValidatePassword(PasswordToValidate);
- }
-
- ///
- /// Clears the user entered password and resets the LEDs
- ///
- public void ClearPassword()
- {
- PasswordToValidate = "";
- OnBoolChange(false, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);
- }
-
- ///
- /// Deletes the last character in the currently entered password field
- ///
- public void DeletePasswordCharacter()
- {
- ushort PasswordLengthBeforeDelete = (ushort)PasswordToValidate.Length;
- PasswordToValidate = PasswordToValidate.Substring(0, PasswordToValidate.Length - 1);
- OnBoolChange(false, (ushort)PasswordLengthBeforeDelete, PasswordManagementConstants.PasswordLedFeedbackChange);
- // Verify if OnStringChange is needed to update the S+ wrapper with the entered PasswordToValidate
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected ushort change event handler
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
-
- ///
- /// If password changes while selected change event will be notifed and update the client
- ///
- ///
- ///
- protected void PasswordManager_PasswordChange(object sender, StringChangeEventArgs args)
- {
- //throw new NotImplementedException();
- if (Key == args.Index)
- {
- //PasswordSelectedKey = args.Index;
- //PasswordSelected = args.StringValue;
- GetPasswordByIndex(args.Index);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ ///
+ /// A class to allow user interaction with the PasswordManager
+ ///
+ public class PasswordClient
+ {
+ ///
+ /// Password selected
+ ///
+ public string Password { get; set; }
+ ///
+ /// Password selected key
+ ///
+ public ushort Key { get; set; }
+ ///
+ /// Used to build the password entered by the user
+ ///
+ public string PasswordToValidate { get; set; }
+
+ ///
+ /// Boolean event
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Constructor
+ ///
+ public PasswordClient()
+ {
+ PasswordManager.PasswordChange += new EventHandler(PasswordManager_PasswordChange);
+ }
+
+ ///
+ /// Initialize method
+ ///
+ public void Initialize()
+ {
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordInitializedChange);
+
+ Password = "";
+ PasswordToValidate = "";
+
+ OnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
+ }
+
+ ///
+ /// Retrieve password by index
+ ///
+ ///
+ public void GetPasswordByIndex(ushort key)
+ {
+ OnUshrtChange((ushort)PasswordManager.Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
+
+ Key = key;
+
+ var pw = PasswordManager.Passwords[Key];
+ if (pw == null)
+ {
+ OnUshrtChange(0, 0, PasswordManagementConstants.PasswordLengthChange);
+ return;
+ }
+
+ Password = pw;
+ OnUshrtChange((ushort)Password.Length, 0, PasswordManagementConstants.PasswordLengthChange);
+ OnUshrtChange(key, 0, PasswordManagementConstants.PasswordSelectIndexChange);
+ }
+
+ ///
+ /// Password validation method
+ ///
+ ///
+ public void ValidatePassword(string password)
+ {
+ if (string.IsNullOrEmpty(password))
+ return;
+
+ if (string.Equals(Password, password))
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordValidationChange);
+ else
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordValidationChange);
+
+ ClearPassword();
+ }
+
+ ///
+ /// Builds the user entered passwrod string, will attempt to validate the user entered
+ /// password against the selected password when the length of the 2 are equal
+ ///
+ ///
+ public void BuildPassword(string data)
+ {
+ PasswordToValidate = String.Concat(PasswordToValidate, data);
+ OnBoolChange(true, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);
+
+ if (PasswordToValidate.Length == Password.Length)
+ ValidatePassword(PasswordToValidate);
+ }
+
+ ///
+ /// Clears the user entered password and resets the LEDs
+ ///
+ public void ClearPassword()
+ {
+ PasswordToValidate = "";
+ OnBoolChange(false, (ushort)PasswordToValidate.Length, PasswordManagementConstants.PasswordLedFeedbackChange);
+ }
+
+ ///
+ /// Deletes the last character in the currently entered password field
+ ///
+ public void DeletePasswordCharacter()
+ {
+ ushort PasswordLengthBeforeDelete = (ushort)PasswordToValidate.Length;
+ PasswordToValidate = PasswordToValidate.Substring(0, PasswordToValidate.Length - 1);
+ OnBoolChange(false, (ushort)PasswordLengthBeforeDelete, PasswordManagementConstants.PasswordLedFeedbackChange);
+ // Verify if OnStringChange is needed to update the S+ wrapper with the entered PasswordToValidate
+ }
+
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected ushort change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+
+ ///
+ /// If password changes while selected change event will be notifed and update the client
+ ///
+ ///
+ ///
+ protected void PasswordManager_PasswordChange(object sender, StringChangeEventArgs args)
+ {
+ //throw new NotImplementedException();
+ if (Key == args.Index)
+ {
+ //PasswordSelectedKey = args.Index;
+ //PasswordSelected = args.StringValue;
+ GetPasswordByIndex(args.Index);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordManager.cs b/src/PasswordManagement/PasswordManager.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordManager.cs
rename to src/PasswordManagement/PasswordManager.cs
index c9bc06b..8c8f1b7 100644
--- a/Pepperdash Core/Pepperdash Core/PasswordManagement/PasswordManager.cs
+++ b/src/PasswordManagement/PasswordManager.cs
@@ -1,247 +1,247 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using Crestron.SimplSharp;
-using PepperDash.Core.JsonToSimpl;
-using PepperDash.Core.JsonStandardObjects;
-
-namespace PepperDash.Core.PasswordManagement
-{
- ///
- /// Allows passwords to be stored and managed
- ///
- public class PasswordManager
- {
- ///
- /// Public dictionary of known passwords
- ///
- public static Dictionary Passwords = new Dictionary();
- ///
- /// Private dictionary, used when passwords are updated
- ///
- private Dictionary _passwords = new Dictionary();
-
- ///
- /// Timer used to wait until password changes have stopped before updating the dictionary
- ///
- CTimer PasswordTimer;
- ///
- /// Timer length
- ///
- public long PasswordTimerElapsedMs = 5000;
-
- ///
- /// Boolean event
- ///
- public event EventHandler BoolChange;
- ///
- /// Ushort event
- ///
- public event EventHandler UshrtChange;
- ///
- /// String event
- ///
- public event EventHandler StringChange;
- ///
- /// Event to notify clients of an updated password at the specified index (uint)
- ///
- public static event EventHandler PasswordChange;
-
- ///
- /// Constructor
- ///
- public PasswordManager()
- {
-
- }
-
- ///
- /// Initialize password manager
- ///
- public void Initialize()
- {
- if (Passwords == null)
- Passwords = new Dictionary();
-
- if (_passwords == null)
- _passwords = new Dictionary();
-
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
- }
-
- ///
- /// Updates password stored in the dictonary
- ///
- ///
- ///
- public void UpdatePassword(ushort key, string password)
- {
- // validate the parameters
- if (key > 0 && string.IsNullOrEmpty(password))
- {
- Debug.Console(1, string.Format("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password));
- return;
- }
-
- try
- {
- // if key exists, update the value
- if(_passwords.ContainsKey(key))
- _passwords[key] = password;
- // else add the key & value
- else
- _passwords.Add(key, password);
-
- Debug.Console(1, string.Format("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]));
-
- if (PasswordTimer == null)
- {
- PasswordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);
- Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
- OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
- }
- else
- {
- PasswordTimer.Reset(PasswordTimerElapsedMs);
- Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
- }
- }
- catch (Exception e)
- {
- var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
- Debug.Console(1, msg);
- }
- }
-
- ///
- /// CTimer callback function
- ///
- private void PasswordTimerElapsed()
- {
- try
- {
- PasswordTimer.Stop();
- Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: CTimer Stopped"));
- OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
- foreach (var pw in _passwords)
- {
- // if key exists, continue
- if (Passwords.ContainsKey(pw.Key))
- {
- Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value));
- if (Passwords[pw.Key] != _passwords[pw.Key])
- {
- Passwords[pw.Key] = _passwords[pw.Key];
- Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]));
- OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
- }
- }
- // else add the key & value
- else
- {
- Passwords.Add(pw.Key, pw.Value);
- }
- }
- OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", e);
- Debug.Console(1, msg);
- }
- }
-
- ///
- /// Method to change the default timer value, (default 5000ms/5s)
- ///
- ///
- public void PasswordTimerMs(ushort time)
- {
- PasswordTimerElapsedMs = Convert.ToInt64(time);
- }
-
- ///
- /// Helper method for debugging to see what passwords are in the lists
- ///
- public void ListPasswords()
- {
- Debug.Console(0, "PasswordManager.ListPasswords:\r");
- foreach (var pw in Passwords)
- Debug.Console(0, "Passwords[{0}]: {1}\r", pw.Key, pw.Value);
- Debug.Console(0, "\n");
- foreach (var pw in _passwords)
- Debug.Console(0, "_passwords[{0}]: {1}\r", pw.Key, pw.Value);
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected ushort change event handler
- ///
- ///
- ///
- ///
- protected void OnUshrtChange(ushort value, ushort index, ushort type)
- {
- var handler = UshrtChange;
- if (handler != null)
- {
- var args = new UshrtChangeEventArgs(value, type);
- args.Index = index;
- UshrtChange(this, args);
- }
- }
-
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
-
- ///
- /// Protected password change event handler
- ///
- ///
- ///
- ///
- protected void OnPasswordChange(string value, ushort index, ushort type)
- {
- var handler = PasswordChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- PasswordChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Crestron.SimplSharp;
+using PepperDash.Core.JsonToSimpl;
+using PepperDash.Core.JsonStandardObjects;
+
+namespace PepperDash.Core.PasswordManagement
+{
+ ///
+ /// Allows passwords to be stored and managed
+ ///
+ public class PasswordManager
+ {
+ ///
+ /// Public dictionary of known passwords
+ ///
+ public static Dictionary Passwords = new Dictionary();
+ ///
+ /// Private dictionary, used when passwords are updated
+ ///
+ private Dictionary _passwords = new Dictionary();
+
+ ///
+ /// Timer used to wait until password changes have stopped before updating the dictionary
+ ///
+ CTimer PasswordTimer;
+ ///
+ /// Timer length
+ ///
+ public long PasswordTimerElapsedMs = 5000;
+
+ ///
+ /// Boolean event
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Ushort event
+ ///
+ public event EventHandler UshrtChange;
+ ///
+ /// String event
+ ///
+ public event EventHandler StringChange;
+ ///
+ /// Event to notify clients of an updated password at the specified index (uint)
+ ///
+ public static event EventHandler PasswordChange;
+
+ ///
+ /// Constructor
+ ///
+ public PasswordManager()
+ {
+
+ }
+
+ ///
+ /// Initialize password manager
+ ///
+ public void Initialize()
+ {
+ if (Passwords == null)
+ Passwords = new Dictionary();
+
+ if (_passwords == null)
+ _passwords = new Dictionary();
+
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordInitializedChange);
+ }
+
+ ///
+ /// Updates password stored in the dictonary
+ ///
+ ///
+ ///
+ public void UpdatePassword(ushort key, string password)
+ {
+ // validate the parameters
+ if (key > 0 && string.IsNullOrEmpty(password))
+ {
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: key [{0}] or password are not valid", key, password));
+ return;
+ }
+
+ try
+ {
+ // if key exists, update the value
+ if(_passwords.ContainsKey(key))
+ _passwords[key] = password;
+ // else add the key & value
+ else
+ _passwords.Add(key, password);
+
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: _password[{0}] = {1}", key, _passwords[key]));
+
+ if (PasswordTimer == null)
+ {
+ PasswordTimer = new CTimer((o) => PasswordTimerElapsed(), PasswordTimerElapsedMs);
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Started"));
+ OnBoolChange(true, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
+ }
+ else
+ {
+ PasswordTimer.Reset(PasswordTimerElapsedMs);
+ Debug.Console(1, string.Format("PasswordManager.UpdatePassword: CTimer Reset"));
+ }
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("PasswordManager.UpdatePassword key-value[{0}, {1}] failed:\r{2}", key, password, e);
+ Debug.Console(1, msg);
+ }
+ }
+
+ ///
+ /// CTimer callback function
+ ///
+ private void PasswordTimerElapsed()
+ {
+ try
+ {
+ PasswordTimer.Stop();
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: CTimer Stopped"));
+ OnBoolChange(false, 0, PasswordManagementConstants.PasswordUpdateBusyChange);
+ foreach (var pw in _passwords)
+ {
+ // if key exists, continue
+ if (Passwords.ContainsKey(pw.Key))
+ {
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: pw.key[{0}] = {1}", pw.Key, pw.Value));
+ if (Passwords[pw.Key] != _passwords[pw.Key])
+ {
+ Passwords[pw.Key] = _passwords[pw.Key];
+ Debug.Console(1, string.Format("PasswordManager.PasswordTimerElapsed: Updated Password[{0} = {1}", pw.Key, Passwords[pw.Key]));
+ OnPasswordChange(Passwords[pw.Key], (ushort)pw.Key, PasswordManagementConstants.StringValueChange);
+ }
+ }
+ // else add the key & value
+ else
+ {
+ Passwords.Add(pw.Key, pw.Value);
+ }
+ }
+ OnUshrtChange((ushort)Passwords.Count, 0, PasswordManagementConstants.PasswordManagerCountChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("PasswordManager.PasswordTimerElapsed failed:\r{0}", e);
+ Debug.Console(1, msg);
+ }
+ }
+
+ ///
+ /// Method to change the default timer value, (default 5000ms/5s)
+ ///
+ ///
+ public void PasswordTimerMs(ushort time)
+ {
+ PasswordTimerElapsedMs = Convert.ToInt64(time);
+ }
+
+ ///
+ /// Helper method for debugging to see what passwords are in the lists
+ ///
+ public void ListPasswords()
+ {
+ Debug.Console(0, "PasswordManager.ListPasswords:\r");
+ foreach (var pw in Passwords)
+ Debug.Console(0, "Passwords[{0}]: {1}\r", pw.Key, pw.Value);
+ Debug.Console(0, "\n");
+ foreach (var pw in _passwords)
+ Debug.Console(0, "_passwords[{0}]: {1}\r", pw.Key, pw.Value);
+ }
+
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected ushort change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnUshrtChange(ushort value, ushort index, ushort type)
+ {
+ var handler = UshrtChange;
+ if (handler != null)
+ {
+ var args = new UshrtChangeEventArgs(value, type);
+ args.Index = index;
+ UshrtChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected password change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnPasswordChange(string value, ushort index, ushort type)
+ {
+ var handler = PasswordChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ PasswordChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj b/src/PepperDash_Core.csproj
similarity index 98%
rename from Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
rename to src/PepperDash_Core.csproj
index ec00542..372ad04 100644
--- a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj
+++ b/src/PepperDash_Core.csproj
@@ -1,156 +1,156 @@
-
-
- Release
- AnyCPU
- 9.0.30729
- 2.0
- {87E29B4C-569B-4368-A4ED-984AC1440C96}
- Library
- Properties
- PepperDash.Core
- PepperDash_Core
- {0B4745B0-194B-4BB6-8E21-E9057CA92500};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- WindowsCE
- E2BECB1F-8C8C-41ba-B736-9BE7D946A398
- 5.0
- SmartDeviceProject1
- v3.5
- Windows CE
-
-
-
-
- .allowedReferenceRelatedFileExtensions
- true
- full
- false
- bin\
- DEBUG;TRACE;
- prompt
- 4
- 512
- true
- true
- bin\PepperDash_Core.xml
-
-
- .allowedReferenceRelatedFileExtensions
- none
- true
- bin\
- prompt
- 4
- 512
- true
- true
- bin\PepperDash_Core.xml
-
-
-
-
- False
- ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll
-
-
- False
- ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll
-
-
- False
- ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll
-
-
- False
- ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll
-
-
- False
- ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Code
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- rem S# preparation will execute after these operations
- del "$(TargetDir)PepperDash_Core.*" /q
-
-
+
+
+ Release
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {87E29B4C-569B-4368-A4ED-984AC1440C96}
+ Library
+ Properties
+ PepperDash.Core
+ PepperDash_Core
+ {0B4745B0-194B-4BB6-8E21-E9057CA92500};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ WindowsCE
+ E2BECB1F-8C8C-41ba-B736-9BE7D946A398
+ 5.0
+ SmartDeviceProject1
+ v3.5
+ Windows CE
+
+
+
+
+ .allowedReferenceRelatedFileExtensions
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE;
+ prompt
+ 4
+ 512
+ true
+ true
+ bin\PepperDash_Core.xml
+
+
+ .allowedReferenceRelatedFileExtensions
+ none
+ true
+ bin\
+ prompt
+ 4
+ 512
+ true
+ true
+ bin\PepperDash_Core.xml
+
+
+
+
+ False
+ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCustomAttributesInterface.dll
+
+
+ False
+ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpCWSHelperInterface.dll
+
+
+ False
+ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpHelperInterface.dll
+
+
+ False
+ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpNewtonsoft.dll
+
+
+ False
+ ..\..\..\..\..\..\..\..\ProgramData\Crestron\SDK\SimplSharpReflectionInterface.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rem S# preparation will execute after these operations
+ del "$(TargetDir)PepperDash_Core.*" /q
+
+
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj.DotSettings b/src/PepperDash_Core.csproj.DotSettings
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/PepperDash_Core.csproj.DotSettings
rename to src/PepperDash_Core.csproj.DotSettings
diff --git a/Pepperdash Core/Pepperdash Core/PepperDash_Core.nuspec b/src/PepperDash_Core.nuspec
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/PepperDash_Core.nuspec
rename to src/PepperDash_Core.nuspec
diff --git a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs
rename to src/Properties/AssemblyInfo.cs
diff --git a/Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs.orig b/src/Properties/AssemblyInfo.cs.orig
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Properties/AssemblyInfo.cs.orig
rename to src/Properties/AssemblyInfo.cs.orig
diff --git a/Pepperdash Core/Pepperdash Core/Properties/ControlSystem.cfg b/src/Properties/ControlSystem.cfg
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/Properties/ControlSystem.cfg
rename to src/Properties/ControlSystem.cfg
index 276e200..f99176f 100644
--- a/Pepperdash Core/Pepperdash Core/Properties/ControlSystem.cfg
+++ b/src/Properties/ControlSystem.cfg
@@ -1,7 +1,7 @@
-
-
- MC3 SSH
- ssh 10.0.0.15
- Program01
- Internal Flash
+
+
+ MC3 SSH
+ ssh 10.0.0.15
+ Program01
+ Internal Flash
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Properties/UpdateAssemblyVersion.ps1 b/src/Properties/UpdateAssemblyVersion.ps1
similarity index 100%
rename from Pepperdash Core/Pepperdash Core/Properties/UpdateAssemblyVersion.ps1
rename to src/Properties/UpdateAssemblyVersion.ps1
diff --git a/Pepperdash Core/Pepperdash Core/SystemInfo/EventArgs and Constants.cs b/src/SystemInfo/EventArgs and Constants.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/SystemInfo/EventArgs and Constants.cs
rename to src/SystemInfo/EventArgs and Constants.cs
index 25f8099..cc71e30 100644
--- a/Pepperdash Core/Pepperdash Core/SystemInfo/EventArgs and Constants.cs
+++ b/src/SystemInfo/EventArgs and Constants.cs
@@ -1,264 +1,264 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.SystemInfo
-{
- ///
- /// Constants
- ///
- public class SystemInfoConstants
- {
- ///
- ///
- ///
- public const ushort BoolValueChange = 1;
- ///
- ///
- ///
- public const ushort CompleteBoolChange = 2;
- ///
- ///
- ///
- public const ushort BusyBoolChange = 3;
-
- ///
- ///
- ///
- public const ushort UshortValueChange = 101;
-
- ///
- ///
- ///
- public const ushort StringValueChange = 201;
- ///
- ///
- ///
- public const ushort ConsoleResponseChange = 202;
- ///
- ///
- ///
- public const ushort ProcessorUptimeChange = 203;
- ///
- ///
- ///
- public const ushort ProgramUptimeChange = 204;
-
- ///
- ///
- ///
- public const ushort ObjectChange = 301;
- ///
- ///
- ///
- public const ushort ProcessorConfigChange = 302;
- ///
- ///
- ///
- public const ushort EthernetConfigChange = 303;
- ///
- ///
- ///
- public const ushort ControlSubnetConfigChange = 304;
- ///
- ///
- ///
- public const ushort ProgramConfigChange = 305;
- }
-
- ///
- /// Processor Change Event Args Class
- ///
- public class ProcessorChangeEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ProcessorInfo Processor { get; set; }
- ///
- ///
- ///
- public ushort Type { get; set; }
- ///
- ///
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public ProcessorChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- public ProcessorChangeEventArgs(ProcessorInfo processor, ushort type)
- {
- Processor = processor;
- Type = type;
- }
-
- ///
- /// Constructor
- ///
- public ProcessorChangeEventArgs(ProcessorInfo processor, ushort type, ushort index)
- {
- Processor = processor;
- Type = type;
- Index = index;
- }
- }
-
- ///
- /// Ethernet Change Event Args Class
- ///
- public class EthernetChangeEventArgs : EventArgs
- {
- ///
- ///
- ///
- public EthernetInfo Adapter { get; set; }
- ///
- ///
- ///
- public ushort Type { get; set; }
- ///
- ///
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public EthernetChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type)
- {
- Adapter = ethernet;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type, ushort index)
- {
- Adapter = ethernet;
- Type = type;
- Index = index;
- }
- }
-
- ///
- /// Control Subnet Chage Event Args Class
- ///
- public class ControlSubnetChangeEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ControlSubnetInfo Adapter { get; set; }
- ///
- ///
- ///
- public ushort Type { get; set; }
- ///
- ///
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public ControlSubnetChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- public ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type)
- {
- Adapter = controlSubnet;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- public ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type, ushort index)
- {
- Adapter = controlSubnet;
- Type = type;
- Index = index;
- }
- }
-
- ///
- /// Program Change Event Args Class
- ///
- public class ProgramChangeEventArgs : EventArgs
- {
- ///
- ///
- ///
- public ProgramInfo Program { get; set; }
- ///
- ///
- ///
- public ushort Type { get; set; }
- ///
- ///
- ///
- public ushort Index { get; set; }
-
- ///
- /// Constructor
- ///
- public ProgramChangeEventArgs()
- {
-
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- public ProgramChangeEventArgs(ProgramInfo program, ushort type)
- {
- Program = program;
- Type = type;
- }
-
- ///
- /// Constructor overload
- ///
- ///
- ///
- ///
- public ProgramChangeEventArgs(ProgramInfo program, ushort type, ushort index)
- {
- Program = program;
- Type = type;
- Index = index;
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.SystemInfo
+{
+ ///
+ /// Constants
+ ///
+ public class SystemInfoConstants
+ {
+ ///
+ ///
+ ///
+ public const ushort BoolValueChange = 1;
+ ///
+ ///
+ ///
+ public const ushort CompleteBoolChange = 2;
+ ///
+ ///
+ ///
+ public const ushort BusyBoolChange = 3;
+
+ ///
+ ///
+ ///
+ public const ushort UshortValueChange = 101;
+
+ ///
+ ///
+ ///
+ public const ushort StringValueChange = 201;
+ ///
+ ///
+ ///
+ public const ushort ConsoleResponseChange = 202;
+ ///
+ ///
+ ///
+ public const ushort ProcessorUptimeChange = 203;
+ ///
+ ///
+ ///
+ public const ushort ProgramUptimeChange = 204;
+
+ ///
+ ///
+ ///
+ public const ushort ObjectChange = 301;
+ ///
+ ///
+ ///
+ public const ushort ProcessorConfigChange = 302;
+ ///
+ ///
+ ///
+ public const ushort EthernetConfigChange = 303;
+ ///
+ ///
+ ///
+ public const ushort ControlSubnetConfigChange = 304;
+ ///
+ ///
+ ///
+ public const ushort ProgramConfigChange = 305;
+ }
+
+ ///
+ /// Processor Change Event Args Class
+ ///
+ public class ProcessorChangeEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ProcessorInfo Processor { get; set; }
+ ///
+ ///
+ ///
+ public ushort Type { get; set; }
+ ///
+ ///
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ProcessorChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ public ProcessorChangeEventArgs(ProcessorInfo processor, ushort type)
+ {
+ Processor = processor;
+ Type = type;
+ }
+
+ ///
+ /// Constructor
+ ///
+ public ProcessorChangeEventArgs(ProcessorInfo processor, ushort type, ushort index)
+ {
+ Processor = processor;
+ Type = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// Ethernet Change Event Args Class
+ ///
+ public class EthernetChangeEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public EthernetInfo Adapter { get; set; }
+ ///
+ ///
+ ///
+ public ushort Type { get; set; }
+ ///
+ ///
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public EthernetChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type)
+ {
+ Adapter = ethernet;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public EthernetChangeEventArgs(EthernetInfo ethernet, ushort type, ushort index)
+ {
+ Adapter = ethernet;
+ Type = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// Control Subnet Chage Event Args Class
+ ///
+ public class ControlSubnetChangeEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ControlSubnetInfo Adapter { get; set; }
+ ///
+ ///
+ ///
+ public ushort Type { get; set; }
+ ///
+ ///
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ControlSubnetChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ public ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type)
+ {
+ Adapter = controlSubnet;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ public ControlSubnetChangeEventArgs(ControlSubnetInfo controlSubnet, ushort type, ushort index)
+ {
+ Adapter = controlSubnet;
+ Type = type;
+ Index = index;
+ }
+ }
+
+ ///
+ /// Program Change Event Args Class
+ ///
+ public class ProgramChangeEventArgs : EventArgs
+ {
+ ///
+ ///
+ ///
+ public ProgramInfo Program { get; set; }
+ ///
+ ///
+ ///
+ public ushort Type { get; set; }
+ ///
+ ///
+ ///
+ public ushort Index { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ProgramChangeEventArgs()
+ {
+
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ public ProgramChangeEventArgs(ProgramInfo program, ushort type)
+ {
+ Program = program;
+ Type = type;
+ }
+
+ ///
+ /// Constructor overload
+ ///
+ ///
+ ///
+ ///
+ public ProgramChangeEventArgs(ProgramInfo program, ushort type, ushort index)
+ {
+ Program = program;
+ Type = type;
+ Index = index;
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoConfig.cs b/src/SystemInfo/SystemInfoConfig.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoConfig.cs
rename to src/SystemInfo/SystemInfoConfig.cs
index 7b33b47..8dc3aca 100644
--- a/Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoConfig.cs
+++ b/src/SystemInfo/SystemInfoConfig.cs
@@ -1,204 +1,204 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.SystemInfo
-{
- ///
- /// Processor info class
- ///
- public class ProcessorInfo
- {
- ///
- ///
- ///
- public string Model { get; set; }
- ///
- ///
- ///
- public string SerialNumber { get; set; }
- ///
- ///
- ///
- public string Firmware { get; set; }
- ///
- ///
- ///
- public string FirmwareDate { get; set; }
- ///
- ///
- ///
- public string OsVersion { get; set; }
- ///
- ///
- ///
- public string RuntimeEnvironment { get; set; }
- ///
- ///
- ///
- public string DevicePlatform { get; set; }
- ///
- ///
- ///
- public string ModuleDirectory { get; set; }
- ///
- ///
- ///
- public string LocalTimeZone { get; set; }
- ///
- ///
- ///
- public string ProgramIdTag { get; set; }
-
- ///
- /// Constructor
- ///
- public ProcessorInfo()
- {
-
- }
- }
-
- ///
- /// Ethernet info class
- ///
- public class EthernetInfo
- {
- ///
- ///
- ///
- public ushort DhcpIsOn { get; set; }
- ///
- ///
- ///
- public string Hostname { get; set; }
- ///
- ///
- ///
- public string MacAddress { get; set; }
- ///
- ///
- ///
- public string IpAddress { get; set; }
- ///
- ///
- ///
- public string Subnet { get; set; }
- ///
- ///
- ///
- public string Gateway { get; set; }
- ///
- ///
- ///
- public string Dns1 { get; set; }
- ///
- ///
- ///
- public string Dns2 { get; set; }
- ///
- ///
- ///
- public string Dns3 { get; set; }
- ///
- ///
- ///
- public string Domain { get; set; }
-
- ///
- /// Constructor
- ///
- public EthernetInfo()
- {
-
- }
- }
-
- ///
- /// Control subnet info class
- ///
- public class ControlSubnetInfo
- {
- ///
- ///
- ///
- public ushort Enabled { get; set; }
- ///
- ///
- ///
- public ushort IsInAutomaticMode { get; set; }
- ///
- ///
- ///
- public string MacAddress { get; set; }
- ///
- ///
- ///
- public string IpAddress { get; set; }
- ///
- ///
- ///
- public string Subnet { get; set; }
- ///
- ///
- ///
- public string RouterPrefix { get; set; }
-
- ///
- /// Constructor
- ///
- public ControlSubnetInfo()
- {
-
- }
- }
-
- ///
- /// Program info class
- ///
- public class ProgramInfo
- {
- ///
- ///
- ///
- public string Name { get; set; }
- ///
- ///
- ///
- public string Header { get; set; }
- ///
- ///
- ///
- public string System { get; set; }
- ///
- ///
- ///
- public string ProgramIdTag { get; set; }
- ///
- ///
- ///
- public string CompileTime { get; set; }
- ///
- ///
- ///
- public string Database { get; set; }
- ///
- ///
- ///
- public string Environment { get; set; }
- ///
- ///
- ///
- public string Programmer { get; set; }
-
- ///
- /// Constructor
- ///
- public ProgramInfo()
- {
-
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.SystemInfo
+{
+ ///
+ /// Processor info class
+ ///
+ public class ProcessorInfo
+ {
+ ///
+ ///
+ ///
+ public string Model { get; set; }
+ ///
+ ///
+ ///
+ public string SerialNumber { get; set; }
+ ///
+ ///
+ ///
+ public string Firmware { get; set; }
+ ///
+ ///
+ ///
+ public string FirmwareDate { get; set; }
+ ///
+ ///
+ ///
+ public string OsVersion { get; set; }
+ ///
+ ///
+ ///
+ public string RuntimeEnvironment { get; set; }
+ ///
+ ///
+ ///
+ public string DevicePlatform { get; set; }
+ ///
+ ///
+ ///
+ public string ModuleDirectory { get; set; }
+ ///
+ ///
+ ///
+ public string LocalTimeZone { get; set; }
+ ///
+ ///
+ ///
+ public string ProgramIdTag { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ProcessorInfo()
+ {
+
+ }
+ }
+
+ ///
+ /// Ethernet info class
+ ///
+ public class EthernetInfo
+ {
+ ///
+ ///
+ ///
+ public ushort DhcpIsOn { get; set; }
+ ///
+ ///
+ ///
+ public string Hostname { get; set; }
+ ///
+ ///
+ ///
+ public string MacAddress { get; set; }
+ ///
+ ///
+ ///
+ public string IpAddress { get; set; }
+ ///
+ ///
+ ///
+ public string Subnet { get; set; }
+ ///
+ ///
+ ///
+ public string Gateway { get; set; }
+ ///
+ ///
+ ///
+ public string Dns1 { get; set; }
+ ///
+ ///
+ ///
+ public string Dns2 { get; set; }
+ ///
+ ///
+ ///
+ public string Dns3 { get; set; }
+ ///
+ ///
+ ///
+ public string Domain { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public EthernetInfo()
+ {
+
+ }
+ }
+
+ ///
+ /// Control subnet info class
+ ///
+ public class ControlSubnetInfo
+ {
+ ///
+ ///
+ ///
+ public ushort Enabled { get; set; }
+ ///
+ ///
+ ///
+ public ushort IsInAutomaticMode { get; set; }
+ ///
+ ///
+ ///
+ public string MacAddress { get; set; }
+ ///
+ ///
+ ///
+ public string IpAddress { get; set; }
+ ///
+ ///
+ ///
+ public string Subnet { get; set; }
+ ///
+ ///
+ ///
+ public string RouterPrefix { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ControlSubnetInfo()
+ {
+
+ }
+ }
+
+ ///
+ /// Program info class
+ ///
+ public class ProgramInfo
+ {
+ ///
+ ///
+ ///
+ public string Name { get; set; }
+ ///
+ ///
+ ///
+ public string Header { get; set; }
+ ///
+ ///
+ ///
+ public string System { get; set; }
+ ///
+ ///
+ ///
+ public string ProgramIdTag { get; set; }
+ ///
+ ///
+ ///
+ public string CompileTime { get; set; }
+ ///
+ ///
+ ///
+ public string Database { get; set; }
+ ///
+ ///
+ ///
+ public string Environment { get; set; }
+ ///
+ ///
+ ///
+ public string Programmer { get; set; }
+
+ ///
+ /// Constructor
+ ///
+ public ProgramInfo()
+ {
+
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoToSimpl.cs b/src/SystemInfo/SystemInfoToSimpl.cs
similarity index 97%
rename from Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoToSimpl.cs
rename to src/SystemInfo/SystemInfoToSimpl.cs
index d4fe40a..6677b9e 100644
--- a/Pepperdash Core/Pepperdash Core/SystemInfo/SystemInfoToSimpl.cs
+++ b/src/SystemInfo/SystemInfoToSimpl.cs
@@ -1,462 +1,462 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Crestron.SimplSharp;
-
-namespace PepperDash.Core.SystemInfo
-{
- ///
- /// System Info class
- ///
- public class SystemInfoToSimpl
- {
- ///
- /// Notifies of bool change
- ///
- public event EventHandler BoolChange;
- ///
- /// Notifies of string change
- ///
- public event EventHandler StringChange;
-
- ///
- /// Notifies of processor change
- ///
- public event EventHandler ProcessorChange;
- ///
- /// Notifies of ethernet change
- ///
- public event EventHandler EthernetChange;
- ///
- /// Notifies of control subnet change
- ///
- public event EventHandler ControlSubnetChange;
- ///
- /// Notifies of program change
- ///
- public event EventHandler ProgramChange;
-
- ///
- /// Constructor
- ///
- public SystemInfoToSimpl()
- {
-
- }
-
- ///
- /// Gets the current processor info
- ///
- public void GetProcessorInfo()
- {
- OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
-
- try
- {
- var processor = new ProcessorInfo();
- processor.Model = InitialParametersClass.ControllerPromptName;
- processor.SerialNumber = CrestronEnvironment.SystemInfo.SerialNumber;
- processor.ModuleDirectory = InitialParametersClass.ProgramDirectory.ToString();
- processor.ProgramIdTag = InitialParametersClass.ProgramIDTag;
- processor.DevicePlatform = CrestronEnvironment.DevicePlatform.ToString();
- processor.OsVersion = CrestronEnvironment.OSVersion.Version.ToString();
- processor.RuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment.ToString();
- processor.LocalTimeZone = CrestronEnvironment.GetTimeZone().Offset;
-
- // Does not return firmware version matching a "ver" command
- // returns the "ver -v" 'CAB' version
- // example return ver -v:
- // RMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0
- // Build: 14:05:46 Oct 09 2018 (3568.25373)
- // Cab: 1.503.0070
- // Applications: 1.0.6855.21351
- // Updater: 1.4.24
- // Bootloader: 1.22.00
- // RMC3-SetupProgram: 1.003.0011
- // IOPVersion: FPGA [v09] slot:7
- // PUF: Unknown
- //Firmware = CrestronEnvironment.OSVersion.Firmware;
- //Firmware = InitialParametersClass.FirmwareVersion;
-
- // Use below logic to get actual firmware ver, not the 'CAB' returned by the above
- // matches console return of a "ver" and on SystemInfo page
- // example return ver:
- // RMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0
- var response = "";
- CrestronConsole.SendControlSystemCommand("ver", ref response);
- processor.Firmware = ParseConsoleResponse(response, "Cntrl Eng", "[", "(");
- processor.FirmwareDate = ParseConsoleResponse(response, "Cntrl Eng", "(", ")");
-
- OnProcessorChange(processor, 0, SystemInfoConstants.ProcessorConfigChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("GetProcessorInfo failed: {0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
-
- OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
- }
-
- ///
- /// Gets the current ethernet info
- ///
- public void GetEthernetInfo()
- {
- OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
-
- var adapter = new EthernetInfo();
-
- try
- {
- // get lan adapter id
- var adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
-
- // get lan adapter info
- var dhcpState = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterId);
- if (!string.IsNullOrEmpty(dhcpState))
- adapter.DhcpIsOn = (ushort)(dhcpState.ToLower().Contains("on") ? 1 : 0);
-
- adapter.Hostname = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterId);
- adapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);
- adapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);
- adapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);
- adapter.Gateway = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterId);
- adapter.Domain = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterId);
-
- // returns comma seperate list of dns servers with trailing comma
- // example return: "8.8.8.8 (DHCP),8.8.4.4 (DHCP),"
- string dns = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterId);
- if (dns.Contains(","))
- {
- string[] dnsList = dns.Split(',');
- for (var i = 0; i < dnsList.Length; i++)
- {
- if(i == 0)
- adapter.Dns1 = !string.IsNullOrEmpty(dnsList[0]) ? dnsList[0] : "0.0.0.0";
- if(i == 1)
- adapter.Dns2 = !string.IsNullOrEmpty(dnsList[1]) ? dnsList[1] : "0.0.0.0";
- if(i == 2)
- adapter.Dns3 = !string.IsNullOrEmpty(dnsList[2]) ? dnsList[2] : "0.0.0.0";
- }
- }
- else
- {
- adapter.Dns1 = !string.IsNullOrEmpty(dns) ? dns : "0.0.0.0";
- adapter.Dns2 = "0.0.0.0";
- adapter.Dns3 = "0.0.0.0";
- }
-
- OnEthernetInfoChange(adapter, 0, SystemInfoConstants.EthernetConfigChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("GetEthernetInfo failed: {0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
-
- OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
- }
-
- ///
- /// Gets the current control subnet info
- ///
- public void GetControlSubnetInfo()
- {
- OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
-
- var adapter = new ControlSubnetInfo();
-
- try
- {
- // get cs adapter id
- var adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
- if (!adapterId.Equals(EthernetAdapterType.EthernetUnknownAdapter))
- {
- adapter.Enabled = 1;
- adapter.IsInAutomaticMode = (ushort)(CrestronEthernetHelper.IsControlSubnetInAutomaticMode ? 1 : 0);
- adapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);
- adapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);
- adapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);
- adapter.RouterPrefix = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CONTROL_SUBNET_ROUTER_PREFIX, adapterId);
- }
- }
- catch (Exception e)
- {
- adapter.Enabled = 0;
- adapter.IsInAutomaticMode = 0;
- adapter.MacAddress = "NA";
- adapter.IpAddress = "NA";
- adapter.Subnet = "NA";
- adapter.RouterPrefix = "NA";
-
- var msg = string.Format("GetControlSubnetInfo failed: {0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
-
- OnControlSubnetInfoChange(adapter, 0, SystemInfoConstants.ControlSubnetConfigChange);
- OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
- }
-
- ///
- /// Gets the program info by index
- ///
- ///
- public void GetProgramInfoByIndex(ushort index)
- {
- if (index < 1 || index > 10)
- return;
-
- OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
-
- var program = new ProgramInfo();
-
- try
- {
- var response = "";
- CrestronConsole.SendControlSystemCommand(string.Format("progcomments:{0}", index), ref response);
-
- // no program loaded or running
- if (response.Contains("Bad or Incomplete Command"))
- {
- program.Name = "";
- program.System = "";
- program.Programmer = "";
- program.CompileTime = "";
- program.Database = "";
- program.Environment = "";
- }
- else
- {
- // SIMPL returns
- program.Name = ParseConsoleResponse(response, "Program File", ":", "\x0D");
- program.System = ParseConsoleResponse(response, "System Name", ":", "\x0D");
- program.ProgramIdTag = ParseConsoleResponse(response, "Friendly Name", ":", "\x0D");
- program.Programmer = ParseConsoleResponse(response, "Programmer", ":", "\x0D");
- program.CompileTime = ParseConsoleResponse(response, "Compiled On", ":", "\x0D");
- program.Database = ParseConsoleResponse(response, "CrestronDB", ":", "\x0D");
- program.Environment = ParseConsoleResponse(response, "Source Env", ":", "\x0D");
-
- // S# returns
- if (program.System.Length == 0)
- program.System = ParseConsoleResponse(response, "Application Name", ":", "\x0D");
- if (program.Database.Length == 0)
- program.Database = ParseConsoleResponse(response, "PlugInVersion", ":", "\x0D");
- if (program.Environment.Length == 0)
- program.Environment = ParseConsoleResponse(response, "Program Tool", ":", "\x0D");
-
- }
-
- OnProgramChange(program, index, SystemInfoConstants.ProgramConfigChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("GetProgramInfoByIndex failed: {0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
-
- OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
- }
-
- ///
- /// Gets the processor uptime and passes it to S+
- ///
- public void RefreshProcessorUptime()
- {
- try
- {
- string response = "";
- CrestronConsole.SendControlSystemCommand("uptime", ref response);
- var uptime = ParseConsoleResponse(response, "running for", "running for", "\x0D");
- OnStringChange(uptime, 0, SystemInfoConstants.ProcessorUptimeChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("RefreshProcessorUptime failed:\r{0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
- }
-
- ///
- /// Gets the program uptime, by index, and passes it to S+
- ///
- ///
- public void RefreshProgramUptimeByIndex(int index)
- {
- try
- {
- string response = "";
- CrestronConsole.SendControlSystemCommand(string.Format("proguptime:{0}", index), ref response);
- string uptime = ParseConsoleResponse(response, "running for", "running for", "\x0D");
- OnStringChange(uptime, (ushort)index, SystemInfoConstants.ProgramUptimeChange);
- }
- catch (Exception e)
- {
- var msg = string.Format("RefreshProgramUptimebyIndex({0}) failed:\r{1}", index, e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
- }
-
- ///
- /// Sends command to console, passes response back using string change event
- ///
- ///
- public void SendConsoleCommand(string cmd)
- {
- if (string.IsNullOrEmpty(cmd))
- return;
-
- string response = "";
- CrestronConsole.SendControlSystemCommand(cmd, ref response);
- if (!string.IsNullOrEmpty(response))
- {
- if (response.EndsWith("\x0D\\x0A"))
- response.Trim('\n');
-
- OnStringChange(response, 0, SystemInfoConstants.ConsoleResponseChange);
- }
- }
-
- ///
- /// private method to parse console messages
- ///
- ///
- ///
- ///
- ///
- ///
- private string ParseConsoleResponse(string data, string line, string dataStart, string dataEnd)
- {
- var response = "";
-
- if (string.IsNullOrEmpty(data) || string.IsNullOrEmpty(line) || string.IsNullOrEmpty(dataStart) || string.IsNullOrEmpty(dataEnd))
- return response;
-
- try
- {
- var linePos = data.IndexOf(line);
- var startPos = data.IndexOf(dataStart, linePos) + dataStart.Length;
- var endPos = data.IndexOf(dataEnd, startPos);
- response = data.Substring(startPos, endPos - startPos).Trim();
- }
- catch (Exception e)
- {
- var msg = string.Format("ParseConsoleResponse failed: {0}", e.Message);
- CrestronConsole.PrintLine(msg);
- //ErrorLog.Error(msg);
- }
-
- return response;
- }
-
- ///
- /// Protected boolean change event handler
- ///
- ///
- ///
- ///
- protected void OnBoolChange(bool state, ushort index, ushort type)
- {
- var handler = BoolChange;
- if (handler != null)
- {
- var args = new BoolChangeEventArgs(state, type);
- args.Index = index;
- BoolChange(this, args);
- }
- }
-
- ///
- /// Protected string change event handler
- ///
- ///
- ///
- ///
- protected void OnStringChange(string value, ushort index, ushort type)
- {
- var handler = StringChange;
- if (handler != null)
- {
- var args = new StringChangeEventArgs(value, type);
- args.Index = index;
- StringChange(this, args);
- }
- }
-
- ///
- /// Protected processor config change event handler
- ///
- ///
- ///
- ///
- protected void OnProcessorChange(ProcessorInfo processor, ushort index, ushort type)
- {
- var handler = ProcessorChange;
- if (handler != null)
- {
- var args = new ProcessorChangeEventArgs(processor, type);
- args.Index = index;
- ProcessorChange(this, args);
- }
- }
-
- ///
- /// Ethernet change event handler
- ///
- ///
- ///
- ///
- protected void OnEthernetInfoChange(EthernetInfo ethernet, ushort index, ushort type)
- {
- var handler = EthernetChange;
- if (handler != null)
- {
- var args = new EthernetChangeEventArgs(ethernet, type);
- args.Index = index;
- EthernetChange(this, args);
- }
- }
-
- ///
- /// Control Subnet change event handler
- ///
- ///
- ///
- ///
- protected void OnControlSubnetInfoChange(ControlSubnetInfo ethernet, ushort index, ushort type)
- {
- var handler = ControlSubnetChange;
- if (handler != null)
- {
- var args = new ControlSubnetChangeEventArgs(ethernet, type);
- args.Index = index;
- ControlSubnetChange(this, args);
- }
- }
-
- ///
- /// Program change event handler
- ///
- ///
- ///
- ///
- protected void OnProgramChange(ProgramInfo program, ushort index, ushort type)
- {
- var handler = ProgramChange;
-
- if (handler != null)
- {
- var args = new ProgramChangeEventArgs(program, type);
- args.Index = index;
- ProgramChange(this, args);
- }
- }
- }
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Crestron.SimplSharp;
+
+namespace PepperDash.Core.SystemInfo
+{
+ ///
+ /// System Info class
+ ///
+ public class SystemInfoToSimpl
+ {
+ ///
+ /// Notifies of bool change
+ ///
+ public event EventHandler BoolChange;
+ ///
+ /// Notifies of string change
+ ///
+ public event EventHandler StringChange;
+
+ ///
+ /// Notifies of processor change
+ ///
+ public event EventHandler ProcessorChange;
+ ///
+ /// Notifies of ethernet change
+ ///
+ public event EventHandler EthernetChange;
+ ///
+ /// Notifies of control subnet change
+ ///
+ public event EventHandler ControlSubnetChange;
+ ///
+ /// Notifies of program change
+ ///
+ public event EventHandler ProgramChange;
+
+ ///
+ /// Constructor
+ ///
+ public SystemInfoToSimpl()
+ {
+
+ }
+
+ ///
+ /// Gets the current processor info
+ ///
+ public void GetProcessorInfo()
+ {
+ OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
+
+ try
+ {
+ var processor = new ProcessorInfo();
+ processor.Model = InitialParametersClass.ControllerPromptName;
+ processor.SerialNumber = CrestronEnvironment.SystemInfo.SerialNumber;
+ processor.ModuleDirectory = InitialParametersClass.ProgramDirectory.ToString();
+ processor.ProgramIdTag = InitialParametersClass.ProgramIDTag;
+ processor.DevicePlatform = CrestronEnvironment.DevicePlatform.ToString();
+ processor.OsVersion = CrestronEnvironment.OSVersion.Version.ToString();
+ processor.RuntimeEnvironment = CrestronEnvironment.RuntimeEnvironment.ToString();
+ processor.LocalTimeZone = CrestronEnvironment.GetTimeZone().Offset;
+
+ // Does not return firmware version matching a "ver" command
+ // returns the "ver -v" 'CAB' version
+ // example return ver -v:
+ // RMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0
+ // Build: 14:05:46 Oct 09 2018 (3568.25373)
+ // Cab: 1.503.0070
+ // Applications: 1.0.6855.21351
+ // Updater: 1.4.24
+ // Bootloader: 1.22.00
+ // RMC3-SetupProgram: 1.003.0011
+ // IOPVersion: FPGA [v09] slot:7
+ // PUF: Unknown
+ //Firmware = CrestronEnvironment.OSVersion.Firmware;
+ //Firmware = InitialParametersClass.FirmwareVersion;
+
+ // Use below logic to get actual firmware ver, not the 'CAB' returned by the above
+ // matches console return of a "ver" and on SystemInfo page
+ // example return ver:
+ // RMC3 Cntrl Eng [v1.503.3568.25373 (Oct 09 2018), #4001E302] @E-00107f4420f0
+ var response = "";
+ CrestronConsole.SendControlSystemCommand("ver", ref response);
+ processor.Firmware = ParseConsoleResponse(response, "Cntrl Eng", "[", "(");
+ processor.FirmwareDate = ParseConsoleResponse(response, "Cntrl Eng", "(", ")");
+
+ OnProcessorChange(processor, 0, SystemInfoConstants.ProcessorConfigChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("GetProcessorInfo failed: {0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+
+ OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
+ }
+
+ ///
+ /// Gets the current ethernet info
+ ///
+ public void GetEthernetInfo()
+ {
+ OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
+
+ var adapter = new EthernetInfo();
+
+ try
+ {
+ // get lan adapter id
+ var adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetLANAdapter);
+
+ // get lan adapter info
+ var dhcpState = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_DHCP_STATE, adapterId);
+ if (!string.IsNullOrEmpty(dhcpState))
+ adapter.DhcpIsOn = (ushort)(dhcpState.ToLower().Contains("on") ? 1 : 0);
+
+ adapter.Hostname = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, adapterId);
+ adapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);
+ adapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);
+ adapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);
+ adapter.Gateway = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_ROUTER, adapterId);
+ adapter.Domain = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DOMAIN_NAME, adapterId);
+
+ // returns comma seperate list of dns servers with trailing comma
+ // example return: "8.8.8.8 (DHCP),8.8.4.4 (DHCP),"
+ string dns = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_DNS_SERVER, adapterId);
+ if (dns.Contains(","))
+ {
+ string[] dnsList = dns.Split(',');
+ for (var i = 0; i < dnsList.Length; i++)
+ {
+ if(i == 0)
+ adapter.Dns1 = !string.IsNullOrEmpty(dnsList[0]) ? dnsList[0] : "0.0.0.0";
+ if(i == 1)
+ adapter.Dns2 = !string.IsNullOrEmpty(dnsList[1]) ? dnsList[1] : "0.0.0.0";
+ if(i == 2)
+ adapter.Dns3 = !string.IsNullOrEmpty(dnsList[2]) ? dnsList[2] : "0.0.0.0";
+ }
+ }
+ else
+ {
+ adapter.Dns1 = !string.IsNullOrEmpty(dns) ? dns : "0.0.0.0";
+ adapter.Dns2 = "0.0.0.0";
+ adapter.Dns3 = "0.0.0.0";
+ }
+
+ OnEthernetInfoChange(adapter, 0, SystemInfoConstants.EthernetConfigChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("GetEthernetInfo failed: {0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+
+ OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
+ }
+
+ ///
+ /// Gets the current control subnet info
+ ///
+ public void GetControlSubnetInfo()
+ {
+ OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
+
+ var adapter = new ControlSubnetInfo();
+
+ try
+ {
+ // get cs adapter id
+ var adapterId = CrestronEthernetHelper.GetAdapterdIdForSpecifiedAdapterType(EthernetAdapterType.EthernetCSAdapter);
+ if (!adapterId.Equals(EthernetAdapterType.EthernetUnknownAdapter))
+ {
+ adapter.Enabled = 1;
+ adapter.IsInAutomaticMode = (ushort)(CrestronEthernetHelper.IsControlSubnetInAutomaticMode ? 1 : 0);
+ adapter.MacAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, adapterId);
+ adapter.IpAddress = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, adapterId);
+ adapter.Subnet = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_MASK, adapterId);
+ adapter.RouterPrefix = CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CONTROL_SUBNET_ROUTER_PREFIX, adapterId);
+ }
+ }
+ catch (Exception e)
+ {
+ adapter.Enabled = 0;
+ adapter.IsInAutomaticMode = 0;
+ adapter.MacAddress = "NA";
+ adapter.IpAddress = "NA";
+ adapter.Subnet = "NA";
+ adapter.RouterPrefix = "NA";
+
+ var msg = string.Format("GetControlSubnetInfo failed: {0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+
+ OnControlSubnetInfoChange(adapter, 0, SystemInfoConstants.ControlSubnetConfigChange);
+ OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
+ }
+
+ ///
+ /// Gets the program info by index
+ ///
+ ///
+ public void GetProgramInfoByIndex(ushort index)
+ {
+ if (index < 1 || index > 10)
+ return;
+
+ OnBoolChange(true, 0, SystemInfoConstants.BusyBoolChange);
+
+ var program = new ProgramInfo();
+
+ try
+ {
+ var response = "";
+ CrestronConsole.SendControlSystemCommand(string.Format("progcomments:{0}", index), ref response);
+
+ // no program loaded or running
+ if (response.Contains("Bad or Incomplete Command"))
+ {
+ program.Name = "";
+ program.System = "";
+ program.Programmer = "";
+ program.CompileTime = "";
+ program.Database = "";
+ program.Environment = "";
+ }
+ else
+ {
+ // SIMPL returns
+ program.Name = ParseConsoleResponse(response, "Program File", ":", "\x0D");
+ program.System = ParseConsoleResponse(response, "System Name", ":", "\x0D");
+ program.ProgramIdTag = ParseConsoleResponse(response, "Friendly Name", ":", "\x0D");
+ program.Programmer = ParseConsoleResponse(response, "Programmer", ":", "\x0D");
+ program.CompileTime = ParseConsoleResponse(response, "Compiled On", ":", "\x0D");
+ program.Database = ParseConsoleResponse(response, "CrestronDB", ":", "\x0D");
+ program.Environment = ParseConsoleResponse(response, "Source Env", ":", "\x0D");
+
+ // S# returns
+ if (program.System.Length == 0)
+ program.System = ParseConsoleResponse(response, "Application Name", ":", "\x0D");
+ if (program.Database.Length == 0)
+ program.Database = ParseConsoleResponse(response, "PlugInVersion", ":", "\x0D");
+ if (program.Environment.Length == 0)
+ program.Environment = ParseConsoleResponse(response, "Program Tool", ":", "\x0D");
+
+ }
+
+ OnProgramChange(program, index, SystemInfoConstants.ProgramConfigChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("GetProgramInfoByIndex failed: {0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+
+ OnBoolChange(false, 0, SystemInfoConstants.BusyBoolChange);
+ }
+
+ ///
+ /// Gets the processor uptime and passes it to S+
+ ///
+ public void RefreshProcessorUptime()
+ {
+ try
+ {
+ string response = "";
+ CrestronConsole.SendControlSystemCommand("uptime", ref response);
+ var uptime = ParseConsoleResponse(response, "running for", "running for", "\x0D");
+ OnStringChange(uptime, 0, SystemInfoConstants.ProcessorUptimeChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("RefreshProcessorUptime failed:\r{0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+ }
+
+ ///
+ /// Gets the program uptime, by index, and passes it to S+
+ ///
+ ///
+ public void RefreshProgramUptimeByIndex(int index)
+ {
+ try
+ {
+ string response = "";
+ CrestronConsole.SendControlSystemCommand(string.Format("proguptime:{0}", index), ref response);
+ string uptime = ParseConsoleResponse(response, "running for", "running for", "\x0D");
+ OnStringChange(uptime, (ushort)index, SystemInfoConstants.ProgramUptimeChange);
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("RefreshProgramUptimebyIndex({0}) failed:\r{1}", index, e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+ }
+
+ ///
+ /// Sends command to console, passes response back using string change event
+ ///
+ ///
+ public void SendConsoleCommand(string cmd)
+ {
+ if (string.IsNullOrEmpty(cmd))
+ return;
+
+ string response = "";
+ CrestronConsole.SendControlSystemCommand(cmd, ref response);
+ if (!string.IsNullOrEmpty(response))
+ {
+ if (response.EndsWith("\x0D\\x0A"))
+ response.Trim('\n');
+
+ OnStringChange(response, 0, SystemInfoConstants.ConsoleResponseChange);
+ }
+ }
+
+ ///
+ /// private method to parse console messages
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private string ParseConsoleResponse(string data, string line, string dataStart, string dataEnd)
+ {
+ var response = "";
+
+ if (string.IsNullOrEmpty(data) || string.IsNullOrEmpty(line) || string.IsNullOrEmpty(dataStart) || string.IsNullOrEmpty(dataEnd))
+ return response;
+
+ try
+ {
+ var linePos = data.IndexOf(line);
+ var startPos = data.IndexOf(dataStart, linePos) + dataStart.Length;
+ var endPos = data.IndexOf(dataEnd, startPos);
+ response = data.Substring(startPos, endPos - startPos).Trim();
+ }
+ catch (Exception e)
+ {
+ var msg = string.Format("ParseConsoleResponse failed: {0}", e.Message);
+ CrestronConsole.PrintLine(msg);
+ //ErrorLog.Error(msg);
+ }
+
+ return response;
+ }
+
+ ///
+ /// Protected boolean change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnBoolChange(bool state, ushort index, ushort type)
+ {
+ var handler = BoolChange;
+ if (handler != null)
+ {
+ var args = new BoolChangeEventArgs(state, type);
+ args.Index = index;
+ BoolChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected string change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnStringChange(string value, ushort index, ushort type)
+ {
+ var handler = StringChange;
+ if (handler != null)
+ {
+ var args = new StringChangeEventArgs(value, type);
+ args.Index = index;
+ StringChange(this, args);
+ }
+ }
+
+ ///
+ /// Protected processor config change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnProcessorChange(ProcessorInfo processor, ushort index, ushort type)
+ {
+ var handler = ProcessorChange;
+ if (handler != null)
+ {
+ var args = new ProcessorChangeEventArgs(processor, type);
+ args.Index = index;
+ ProcessorChange(this, args);
+ }
+ }
+
+ ///
+ /// Ethernet change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnEthernetInfoChange(EthernetInfo ethernet, ushort index, ushort type)
+ {
+ var handler = EthernetChange;
+ if (handler != null)
+ {
+ var args = new EthernetChangeEventArgs(ethernet, type);
+ args.Index = index;
+ EthernetChange(this, args);
+ }
+ }
+
+ ///
+ /// Control Subnet change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnControlSubnetInfoChange(ControlSubnetInfo ethernet, ushort index, ushort type)
+ {
+ var handler = ControlSubnetChange;
+ if (handler != null)
+ {
+ var args = new ControlSubnetChangeEventArgs(ethernet, type);
+ args.Index = index;
+ ControlSubnetChange(this, args);
+ }
+ }
+
+ ///
+ /// Program change event handler
+ ///
+ ///
+ ///
+ ///
+ protected void OnProgramChange(ProgramInfo program, ushort index, ushort type)
+ {
+ var handler = ProgramChange;
+
+ if (handler != null)
+ {
+ var args = new ProgramChangeEventArgs(program, type);
+ args.Index = index;
+ ProgramChange(this, args);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs b/src/Web/RequestHandlers/DefaultRequestHandler.cs
similarity index 95%
rename from Pepperdash Core/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs
rename to src/Web/RequestHandlers/DefaultRequestHandler.cs
index 93cfae0..ca19cf2 100644
--- a/Pepperdash Core/Pepperdash Core/Web/RequestHandlers/DefaultRequestHandler.cs
+++ b/src/Web/RequestHandlers/DefaultRequestHandler.cs
@@ -1,17 +1,17 @@
-using Crestron.SimplSharp.WebScripting;
-
-namespace PepperDash.Core.Web.RequestHandlers
-{
- ///
- /// Web API default request handler
- ///
- public class DefaultRequestHandler : WebApiBaseRequestHandler
- {
- ///
- /// Constructor
- ///
- public DefaultRequestHandler()
- : base(true)
- { }
- }
+using Crestron.SimplSharp.WebScripting;
+
+namespace PepperDash.Core.Web.RequestHandlers
+{
+ ///
+ /// Web API default request handler
+ ///
+ public class DefaultRequestHandler : WebApiBaseRequestHandler
+ {
+ ///
+ /// Constructor
+ ///
+ public DefaultRequestHandler()
+ : base(true)
+ { }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs b/src/Web/RequestHandlers/WebApiBaseRequestHandler.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs
rename to src/Web/RequestHandlers/WebApiBaseRequestHandler.cs
index a73abd1..99e4aa9 100644
--- a/Pepperdash Core/Pepperdash Core/Web/RequestHandlers/WebApiBaseRequestHandler.cs
+++ b/src/Web/RequestHandlers/WebApiBaseRequestHandler.cs
@@ -1,165 +1,165 @@
-using System;
-using System.Collections.Generic;
-using Crestron.SimplSharp.WebScripting;
-
-namespace PepperDash.Core.Web.RequestHandlers
-{
- ///
- /// CWS Base Handler, implements IHttpCwsHandler
- ///
- public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
- {
- private readonly Dictionary> _handlers;
- protected readonly bool EnableCors;
-
- ///
- /// Constructor
- ///
- protected WebApiBaseRequestHandler(bool enableCors)
- {
- EnableCors = enableCors;
-
- _handlers = new Dictionary>
- {
- {"CONNECT", HandleConnect},
- {"DELETE", HandleDelete},
- {"GET", HandleGet},
- {"HEAD", HandleHead},
- {"OPTIONS", HandleOptions},
- {"PATCH", HandlePatch},
- {"POST", HandlePost},
- {"PUT", HandlePut},
- {"TRACE", HandleTrace}
- };
- }
-
- ///
- /// Constructor
- ///
- protected WebApiBaseRequestHandler()
- : this(false)
- {
- }
-
- ///
- /// Handles CONNECT method requests
- ///
- ///
- protected virtual void HandleConnect(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles DELETE method requests
- ///
- ///
- protected virtual void HandleDelete(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles GET method requests
- ///
- ///
- protected virtual void HandleGet(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles HEAD method requests
- ///
- ///
- protected virtual void HandleHead(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles OPTIONS method requests
- ///
- ///
- protected virtual void HandleOptions(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles PATCH method requests
- ///
- ///
- protected virtual void HandlePatch(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles POST method requests
- ///
- ///
- protected virtual void HandlePost(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles PUT method requests
- ///
- ///
- protected virtual void HandlePut(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Handles TRACE method requests
- ///
- ///
- protected virtual void HandleTrace(HttpCwsContext context)
- {
- context.Response.StatusCode = 501;
- context.Response.StatusDescription = "Not Implemented";
- context.Response.End();
- }
-
- ///
- /// Process request
- ///
- ///
- public void ProcessRequest(HttpCwsContext context)
- {
- Action handler;
-
- if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler))
- {
- return;
- }
-
- if (EnableCors)
- {
- context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
- context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
- }
-
- handler(context);
- }
- }
+using System;
+using System.Collections.Generic;
+using Crestron.SimplSharp.WebScripting;
+
+namespace PepperDash.Core.Web.RequestHandlers
+{
+ ///
+ /// CWS Base Handler, implements IHttpCwsHandler
+ ///
+ public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
+ {
+ private readonly Dictionary> _handlers;
+ protected readonly bool EnableCors;
+
+ ///
+ /// Constructor
+ ///
+ protected WebApiBaseRequestHandler(bool enableCors)
+ {
+ EnableCors = enableCors;
+
+ _handlers = new Dictionary>
+ {
+ {"CONNECT", HandleConnect},
+ {"DELETE", HandleDelete},
+ {"GET", HandleGet},
+ {"HEAD", HandleHead},
+ {"OPTIONS", HandleOptions},
+ {"PATCH", HandlePatch},
+ {"POST", HandlePost},
+ {"PUT", HandlePut},
+ {"TRACE", HandleTrace}
+ };
+ }
+
+ ///
+ /// Constructor
+ ///
+ protected WebApiBaseRequestHandler()
+ : this(false)
+ {
+ }
+
+ ///
+ /// Handles CONNECT method requests
+ ///
+ ///
+ protected virtual void HandleConnect(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles DELETE method requests
+ ///
+ ///
+ protected virtual void HandleDelete(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles GET method requests
+ ///
+ ///
+ protected virtual void HandleGet(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles HEAD method requests
+ ///
+ ///
+ protected virtual void HandleHead(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles OPTIONS method requests
+ ///
+ ///
+ protected virtual void HandleOptions(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles PATCH method requests
+ ///
+ ///
+ protected virtual void HandlePatch(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles POST method requests
+ ///
+ ///
+ protected virtual void HandlePost(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles PUT method requests
+ ///
+ ///
+ protected virtual void HandlePut(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Handles TRACE method requests
+ ///
+ ///
+ protected virtual void HandleTrace(HttpCwsContext context)
+ {
+ context.Response.StatusCode = 501;
+ context.Response.StatusDescription = "Not Implemented";
+ context.Response.End();
+ }
+
+ ///
+ /// Process request
+ ///
+ ///
+ public void ProcessRequest(HttpCwsContext context)
+ {
+ Action handler;
+
+ if (!_handlers.TryGetValue(context.Request.HttpMethod, out handler))
+ {
+ return;
+ }
+
+ if (EnableCors)
+ {
+ context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
+ context.Response.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
+ }
+
+ handler(context);
+ }
+ }
}
\ No newline at end of file
diff --git a/Pepperdash Core/Pepperdash Core/Web/WebApiServer.cs b/src/Web/WebApiServer.cs
similarity index 96%
rename from Pepperdash Core/Pepperdash Core/Web/WebApiServer.cs
rename to src/Web/WebApiServer.cs
index f2f8464..cf45b36 100644
--- a/Pepperdash Core/Pepperdash Core/Web/WebApiServer.cs
+++ b/src/Web/WebApiServer.cs
@@ -1,284 +1,284 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Crestron.SimplSharp;
-using Crestron.SimplSharp.WebScripting;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using PepperDash.Core.Web.RequestHandlers;
-
-namespace PepperDash.Core.Web
-{
- ///