Merged in bugfix/pdc-36 (pull request #39)

Bugfix/pdc 36

Approved-by: Neil Dorin <ndorin@pepperdash.com>
This commit is contained in:
Neil Dorin
2019-12-12 22:50:59 +00:00
3 changed files with 163 additions and 87 deletions

View File

@@ -11,15 +11,18 @@ using Newtonsoft.Json.Linq;
namespace PepperDash.Core namespace PepperDash.Core
{ {
/// <summary>
/// A class to handle basic TCP/IP communications with a server
/// </summary>
public class GenericTcpIpClient : Device, ISocketStatus, IAutoReconnect public class GenericTcpIpClient : Device, ISocketStatus, IAutoReconnect
{ {
/// <summary> /// <summary>
/// /// Fires when data is received from the server and returns it as a Byte array
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary> /// <summary>
/// /// Fires when data is received from the server and returns it as text
/// </summary> /// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
@@ -72,12 +75,12 @@ namespace PepperDash.Core
public int BufferSize { get; set; } public int BufferSize { get; set; }
/// <summary> /// <summary>
/// /// The actual client class
/// </summary> /// </summary>
public TCPClient Client { get; private set; } public TCPClient Client { get; private set; }
/// <summary> /// <summary>
/// /// True if connected to the server
/// </summary> /// </summary>
public bool IsConnected public bool IsConnected
{ {
@@ -93,7 +96,7 @@ namespace PepperDash.Core
} }
/// <summary> /// <summary>
/// /// Status of the socket
/// </summary> /// </summary>
public SocketStatus ClientStatus public SocketStatus ClientStatus
{ {
@@ -115,23 +118,23 @@ namespace PepperDash.Core
} }
/// <summary> /// <summary>
/// /// Status of the socket
/// </summary> /// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } } public string ClientStatusText { get { return ClientStatus.ToString(); } }
[Obsolete] [Obsolete]
/// <summary> /// <summary>
/// /// Ushort representation of client status
/// </summary> /// </summary>
public ushort UClientStatus { get { return (ushort)ClientStatus; } } public ushort UClientStatus { get { return (ushort)ClientStatus; } }
/// <summary> /// <summary>
/// /// Connection failure reason
/// </summary> /// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } } public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary> /// <summary>
/// /// If true, enables AutoConnect
/// </summary> /// </summary>
public bool AutoReconnect { get; set; } public bool AutoReconnect { get; set; }
@@ -164,7 +167,7 @@ namespace PepperDash.Core
CTimer RetryTimer; CTimer RetryTimer;
/// <summary> /// <summary>
/// /// Constructor
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="address"></param> /// <param name="address"></param>
@@ -180,26 +183,10 @@ namespace PepperDash.Core
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
//if (string.IsNullOrEmpty(address))
//{
// 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;
// }
//}
//Client = new TCPClient(address, port, bufferSize);
//Client.SocketStatusChange += Client_SocketStatusChange;
} }
/// <summary> /// <summary>
/// /// Constructor
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
public GenericTcpIpClient(string key) public GenericTcpIpClient(string key)
@@ -241,18 +228,23 @@ namespace PepperDash.Core
} }
} }
//public override bool CustomActivate() /// <summary>
//{ ///
// return true; /// </summary>
//} /// <returns></returns>
public override bool Deactivate() public override bool Deactivate()
{ {
if(Client != null) if (Client != null)
Client.SocketStatusChange -= this.Client_SocketStatusChange; {
Client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient();
}
return true; return true;
} }
/// <summary>
/// Attempts to connect to the server
/// </summary>
public void Connect() public void Connect()
{ {
if (IsConnected) if (IsConnected)
@@ -274,6 +266,7 @@ namespace PepperDash.Core
if (Client == null) if (Client == null)
{ {
Client = new TCPClient(Hostname, Port, BufferSize); Client = new TCPClient(Hostname, Port, BufferSize);
Client.SocketStatusChange -= Client_SocketStatusChange;
Client.SocketStatusChange += Client_SocketStatusChange; Client.SocketStatusChange += Client_SocketStatusChange;
} }
DisconnectCalledByUser = false; DisconnectCalledByUser = false;
@@ -281,23 +274,37 @@ namespace PepperDash.Core
Client.ConnectToServerAsync(ConnectToServerCallback); // (null); Client.ConnectToServerAsync(ConnectToServerCallback); // (null);
} }
/// <summary>
/// Attempts to disconnect the client
/// </summary>
public void Disconnect() public void Disconnect()
{ {
DisconnectCalledByUser = true; if (Client != null)
DisconnectClient(); {
DisconnectCalledByUser = true;
DisconnectClient();
Client = null;
Debug.Console(1, this, "Disconnected");
}
} }
/// <summary>
/// Does the actual disconnect business
/// </summary>
public void DisconnectClient() public void DisconnectClient()
{ {
if (Client != null) if (Client != null)
{ {
Debug.Console(1, this, "Disconnecting client"); Debug.Console(1, this, "Disconnecting client");
//Client.SocketStatusChange -= Client_SocketStatusChange;
if(IsConnected) if(IsConnected)
Client.DisconnectFromServer(); Client.DisconnectFromServer();
} }
} }
/// <summary>
/// Callback method for connection attempt
/// </summary>
/// <param name="c"></param>
void ConnectToServerCallback(TCPClient c) void ConnectToServerCallback(TCPClient c)
{ {
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus); Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
@@ -305,31 +312,48 @@ namespace PepperDash.Core
WaitAndTryReconnect(); WaitAndTryReconnect();
} }
/// <summary>
/// Disconnects, waits and attemtps to connect again
/// </summary>
void WaitAndTryReconnect() void WaitAndTryReconnect()
{ {
DisconnectClient(); DisconnectClient();
if (Client != null)
{
Debug.Console(1, "Attempting reconnect, status={0}", Client.ClientStatus);
if (!DisconnectCalledByUser)
RetryTimer = new CTimer(o => { Client.ConnectToServerAsync(ConnectToServerCallback); }, AutoReconnectIntervalMs);
}
Debug.Console(1, "Attempting reconnect, status={0}", Client.ClientStatus);
if(!DisconnectCalledByUser)
RetryTimer = new CTimer(o => { Client.ConnectToServerAsync(ConnectToServerCallback); }, AutoReconnectIntervalMs);
} }
/// <summary>
/// Recieves incoming data
/// </summary>
/// <param name="client"></param>
/// <param name="numBytes"></param>
void Receive(TCPClient client, int numBytes) void Receive(TCPClient client, int numBytes)
{ {
if (numBytes > 0) if (client != null)
{ {
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray(); if (numBytes > 0)
var bytesHandler = BytesReceived; {
if (bytesHandler != null) var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes)); var bytesHandler = BytesReceived;
var textHandler = TextReceived; if (bytesHandler != null)
if (textHandler != null) bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
{ var textHandler = TextReceived;
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); if (textHandler != null)
textHandler(this, new GenericCommMethodReceiveTextArgs(str)); {
} var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
} textHandler(this, new GenericCommMethodReceiveTextArgs(str));
Client.ReceiveDataAsync(Receive); }
}
client.ReceiveDataAsync(Receive);
}
} }
/// <summary> /// <summary>
@@ -358,6 +382,10 @@ namespace PepperDash.Core
SendText(unescapedText); SendText(unescapedText);
} }
/// <summary>
/// Sends Bytes to the server
/// </summary>
/// <param name="bytes"></param>
public void SendBytes(byte[] bytes) public void SendBytes(byte[] bytes)
{ {
//if (Debug.Level == 2) //if (Debug.Level == 2)
@@ -366,7 +394,11 @@ namespace PepperDash.Core
Client.SendData(bytes, bytes.Length); Client.SendData(bytes, bytes.Length);
} }
/// <summary>
/// Socket Status Change Handler
/// </summary>
/// <param name="client"></param>
/// <param name="clientSocketStatus"></param>
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus) void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
{ {
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText); Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
@@ -393,15 +425,30 @@ namespace PepperDash.Core
} }
} }
/// <summary>
/// Configuration properties for TCP/SSH Connections
/// </summary>
public class TcpSshPropertiesConfig public class TcpSshPropertiesConfig
{ {
/// <summary>
/// Address to connect to
/// </summary>
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public string Address { get; set; } public string Address { get; set; }
/// <summary>
/// Port to connect to
/// </summary>
[JsonProperty(Required = Required.Always)] [JsonProperty(Required = Required.Always)]
public int Port { get; set; } public int Port { get; set; }
/// <summary>
/// Username credential
/// </summary>
public string Username { get; set; } public string Username { get; set; }
/// <summary>
/// Passord credential
/// </summary>
public string Password { get; set; } public string Password { get; set; }
/// <summary> /// <summary>
@@ -430,35 +477,4 @@ namespace PepperDash.Core
} }
//public class TcpIpConfig
//{
// [JsonProperty(Required = Required.Always)]
// public string Address { get; set; }
// [JsonProperty(Required = Required.Always)]
// public int Port { get; set; }
// /// <summary>
// /// Defaults to 32768
// /// </summary>
// public int BufferSize { get; set; }
// /// <summary>
// /// Defaults to true
// /// </summary>
// public bool AutoReconnect { get; set; }
// /// <summary>
// /// Defaults to 5000ms
// /// </summary>
// public int AutoReconnectIntervalMs { get; set; }
// public TcpIpConfig()
// {
// BufferSize = 32768;
// AutoReconnect = true;
// AutoReconnectIntervalMs = 5000;
// }
//}
} }

View File

@@ -28,6 +28,8 @@ namespace PepperDash.Core
public static int Level { get; private set; } public static int Level { get; private set; }
public static bool DoNotLoadOnNextBoot { get; private set; }
static DebugContextCollection Contexts; static DebugContextCollection Contexts;
static int SaveTimeoutMs = 30000; static int SaveTimeoutMs = 30000;
@@ -65,6 +67,9 @@ namespace PepperDash.Core
if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro) if (CrestronEnvironment.RuntimeEnvironment == eRuntimeEnvironment.SimplSharpPro)
{ {
// Add command to console // 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", CrestronConsole.AddNewConsoleCommand(SetDebugFromConsole, "appdebug",
"appdebug:P [0-2]: Sets the application's console debug message level", "appdebug:P [0-2]: Sets the application's console debug message level",
ConsoleAccessLevelEnum.AccessOperator); ConsoleAccessLevelEnum.AccessOperator);
@@ -81,7 +86,12 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler; CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
LoadMemory(); LoadMemory();
Level = Contexts.GetOrCreateItem("DEFAULT").Level;
var context = Contexts.GetOrCreateItem("DEFAULT");
Level = context.Level;
DoNotLoadOnNextBoot = context.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 try
{ {
@@ -140,6 +150,32 @@ namespace PepperDash.Core
} }
} }
/// <summary>
/// Callback for console command
/// </summary>
/// <param name="stateString"></param>
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]");
}
}
/// <summary>
/// Callback for console command
/// </summary>
/// <param name="items"></param>
public static void SetDebugFilterFromConsole(string items) public static void SetDebugFilterFromConsole(string items)
{ {
var str = items.Trim(); var str = items.Trim();
@@ -237,6 +273,20 @@ namespace PepperDash.Core
} }
} }
/// <summary>
/// Sets the flag to prevent application starting on next boot
/// </summary>
/// <param name="state"></param>
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);
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>

View File

@@ -47,9 +47,19 @@ namespace PepperDash.Core.DebugThings
public class DebugContextItem public class DebugContextItem
{ {
/// <summary>
/// The level of debug messages to print
/// </summary>
[JsonProperty("level")] [JsonProperty("level")]
public int Level { get; set; } public int Level { get; set; }
/// <summary>
/// Property to tell the program not to intitialize when it boots, if desired
/// </summary>
[JsonProperty("doNotLoadOnNextBoot")]
public bool DoNotLoadOnNextBoot { get; set; }
public DebugContextItem(DebugContextCollection parent) public DebugContextItem(DebugContextCollection parent)
{ {