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
{
/// <summary>
/// A class to handle basic TCP/IP communications with a server
/// </summary>
public class GenericTcpIpClient : Device, ISocketStatus, IAutoReconnect
{
/// <summary>
///
/// Fires when data is received from the server and returns it as a Byte array
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
///
/// Fires when data is received from the server and returns it as text
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
@@ -72,12 +75,12 @@ namespace PepperDash.Core
public int BufferSize { get; set; }
/// <summary>
///
/// The actual client class
/// </summary>
public TCPClient Client { get; private set; }
/// <summary>
///
/// True if connected to the server
/// </summary>
public bool IsConnected
{
@@ -93,7 +96,7 @@ namespace PepperDash.Core
}
/// <summary>
///
/// Status of the socket
/// </summary>
public SocketStatus ClientStatus
{
@@ -115,23 +118,23 @@ namespace PepperDash.Core
}
/// <summary>
///
/// Status of the socket
/// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } }
[Obsolete]
/// <summary>
///
/// Ushort representation of client status
/// </summary>
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
/// <summary>
///
/// Connection failure reason
/// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary>
///
/// If true, enables AutoConnect
/// </summary>
public bool AutoReconnect { get; set; }
@@ -164,7 +167,7 @@ namespace PepperDash.Core
CTimer RetryTimer;
/// <summary>
///
/// Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="address"></param>
@@ -180,26 +183,10 @@ namespace PepperDash.Core
AutoReconnectIntervalMs = 5000;
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>
///
/// Constructor
/// </summary>
/// <param name="key"></param>
public GenericTcpIpClient(string key)
@@ -241,18 +228,23 @@ namespace PepperDash.Core
}
}
//public override bool CustomActivate()
//{
// return true;
//}
/// <summary>
///
/// </summary>
/// <returns></returns>
public override bool Deactivate()
{
if(Client != null)
Client.SocketStatusChange -= this.Client_SocketStatusChange;
if (Client != null)
{
Client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient();
}
return true;
}
/// <summary>
/// Attempts to connect to the server
/// </summary>
public void Connect()
{
if (IsConnected)
@@ -274,6 +266,7 @@ namespace PepperDash.Core
if (Client == null)
{
Client = new TCPClient(Hostname, Port, BufferSize);
Client.SocketStatusChange -= Client_SocketStatusChange;
Client.SocketStatusChange += Client_SocketStatusChange;
}
DisconnectCalledByUser = false;
@@ -281,23 +274,37 @@ namespace PepperDash.Core
Client.ConnectToServerAsync(ConnectToServerCallback); // (null);
}
/// <summary>
/// Attempts to disconnect the client
/// </summary>
public void Disconnect()
{
DisconnectCalledByUser = true;
DisconnectClient();
if (Client != null)
{
DisconnectCalledByUser = true;
DisconnectClient();
Client = null;
Debug.Console(1, this, "Disconnected");
}
}
/// <summary>
/// Does the actual disconnect business
/// </summary>
public void DisconnectClient()
{
if (Client != null)
{
Debug.Console(1, this, "Disconnecting client");
//Client.SocketStatusChange -= Client_SocketStatusChange;
if(IsConnected)
Client.DisconnectFromServer();
}
}
/// <summary>
/// Callback method for connection attempt
/// </summary>
/// <param name="c"></param>
void ConnectToServerCallback(TCPClient c)
{
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
@@ -305,31 +312,48 @@ namespace PepperDash.Core
WaitAndTryReconnect();
}
/// <summary>
/// Disconnects, waits and attemtps to connect again
/// </summary>
void WaitAndTryReconnect()
{
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)
{
if (numBytes > 0)
{
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
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));
}
}
Client.ReceiveDataAsync(Receive);
if (client != null)
{
if (numBytes > 0)
{
var bytes = client.IncomingDataBuffer.Take(numBytes).ToArray();
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));
}
}
client.ReceiveDataAsync(Receive);
}
}
/// <summary>
@@ -358,6 +382,10 @@ namespace PepperDash.Core
SendText(unescapedText);
}
/// <summary>
/// Sends Bytes to the server
/// </summary>
/// <param name="bytes"></param>
public void SendBytes(byte[] bytes)
{
//if (Debug.Level == 2)
@@ -366,7 +394,11 @@ namespace PepperDash.Core
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)
{
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
{
/// <summary>
/// Address to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Address { get; set; }
/// <summary>
/// Port to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
/// <summary>
/// Username credential
/// </summary>
public string Username { get; set; }
/// <summary>
/// Passord credential
/// </summary>
public string Password { get; set; }
/// <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 bool DoNotLoadOnNextBoot { get; private set; }
static DebugContextCollection Contexts;
static int SaveTimeoutMs = 30000;
@@ -65,6 +67,9 @@ namespace PepperDash.Core
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);
@@ -81,7 +86,12 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += CrestronEnvironment_ProgramStatusEventHandler;
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
{
@@ -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)
{
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>

View File

@@ -47,9 +47,19 @@ namespace PepperDash.Core.DebugThings
public class DebugContextItem
{
/// <summary>
/// The level of debug messages to print
/// </summary>
[JsonProperty("level")]
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)
{