Compare commits

..

1 Commits

47 changed files with 1111 additions and 2320 deletions

1
.gitignore vendored
View File

@@ -396,4 +396,3 @@ _site/
api/
*.DS_Store
/._PepperDash.Essentials.4Series.sln
dotnet

View File

@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>2.19.4-local</Version>
<Version>2.15.1-local</Version>
<InformationalVersion>$(Version)</InformationalVersion>
<Authors>PepperDash Technology</Authors>
<Company>PepperDash Technology</Company>

View File

@@ -1,43 +0,0 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace PepperDash.Core
{
/// <summary>
/// Helper class for formatting communication text and byte data for debugging purposes.
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <param name="bytes"></param>
/// <returns>string with all bytes escaped</returns>
public static string GetEscapedText(byte[] bytes)
{
return string.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all bytes escaped</returns>
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());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns>string with all non-printable characters escaped</returns>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
}

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
@@ -36,14 +37,14 @@ namespace PepperDash.Core
{
get
{
return _DebugTimeoutInMs / 60000;
return _DebugTimeoutInMs/60000;
}
}
/// <summary>
/// Gets or sets the RxStreamDebuggingIsEnabled
/// </summary>
public bool RxStreamDebuggingIsEnabled { get; private set; }
public bool RxStreamDebuggingIsEnabled{ get; private set; }
/// <summary>
/// Indicates that transmit stream debugging is enabled
@@ -107,7 +108,7 @@ namespace PepperDash.Core
TxStreamDebuggingIsEnabled = true;
Debug.SetDeviceDebugSettings(ParentDeviceKey, setting);
}
/// <summary>
@@ -135,4 +136,51 @@ namespace PepperDash.Core
DebugExpiryPeriod = null;
}
}
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
/// <summary>
/// Enumeration of eStreamDebuggingSetting values
/// </summary>
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
/// <summary>
/// The available settings for stream debugging response types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text,
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Crestron.SimplSharp;
@@ -12,12 +11,11 @@ using Renci.SshNet.Common;
namespace PepperDash.Core
{
/// <summary>
/// SSH Client
///
/// </summary>
public class GenericSshClient : Device, ISocketStatusWithStreamDebugging, IAutoReconnect
{
private const string SPlusKey = "Uninitialized SshClient";
/// <summary>
/// Object to enable stream debugging
/// </summary>
@@ -38,6 +36,11 @@ namespace PepperDash.Core
/// </summary>
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
/// <summary>
/////
///// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
/// <summary>
/// Gets or sets the Hostname
/// </summary>
@@ -64,7 +67,7 @@ namespace PepperDash.Core
public bool IsConnected
{
// returns false if no client or not connected
get { return client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
get { return Client != null && ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
/// <summary>
@@ -80,26 +83,16 @@ namespace PepperDash.Core
/// </summary>
public SocketStatus ClientStatus
{
get { lock (_stateLock) { return _ClientStatus; } }
get { return _ClientStatus; }
private set
{
bool shouldFireEvent = false;
lock (_stateLock)
{
if (_ClientStatus != value)
{
_ClientStatus = value;
shouldFireEvent = true;
}
}
// Fire event outside lock to avoid deadlock
if (shouldFireEvent)
OnConnectionChange();
if (_ClientStatus == value)
return;
_ClientStatus = value;
OnConnectionChange();
}
}
private SocketStatus _ClientStatus;
private bool _ConnectEnabled;
SocketStatus _ClientStatus;
/// <summary>
/// Contains the familiar Simpl analog status values. This drives the ConnectionChange event
@@ -107,7 +100,7 @@ namespace PepperDash.Core
/// </summary>
public ushort UStatus
{
get { lock (_stateLock) { return (ushort)_ClientStatus; } }
get { return (ushort)_ClientStatus; }
}
/// <summary>
@@ -118,11 +111,7 @@ namespace PepperDash.Core
/// <summary>
/// Will be set and unset by connect and disconnect only
/// </summary>
public bool ConnectEnabled
{
get { lock (_stateLock) { return _ConnectEnabled; } }
private set { lock (_stateLock) { _ConnectEnabled = value; } }
}
public bool ConnectEnabled { get; private set; }
/// <summary>
/// S+ helper for AutoReconnect
@@ -138,25 +127,17 @@ namespace PepperDash.Core
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
private SshClient client;
SshClient Client;
private ShellStream shellStream;
ShellStream TheStream;
private readonly Timer reconnectTimer;
CTimer ReconnectTimer;
//Lock object to prevent simulatneous connect/disconnect operations
//private CCriticalSection connectLock = new CCriticalSection();
private readonly SemaphoreSlim connectLock = new SemaphoreSlim(1);
private SemaphoreSlim connectLock = new SemaphoreSlim(1);
// Thread-safety lock for state changes
private readonly object _stateLock = new object();
private bool disconnectLogged = false;
/// <summary>
/// When true, turns off echo for the SSH session
/// </summary>
public bool DisableEcho { get; set; }
private bool DisconnectLogged = false;
/// <summary>
/// Typical constructor.
@@ -173,13 +154,13 @@ namespace PepperDash.Core
Password = password;
AutoReconnectIntervalMs = 5000;
reconnectTimer = new Timer(o =>
ReconnectTimer = new CTimer(o =>
{
if (ConnectEnabled) // Now thread-safe property access
if (ConnectEnabled)
{
Connect();
}
}, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}, System.Threading.Timeout.Infinite);
}
/// <summary>
@@ -191,23 +172,23 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
reconnectTimer = new Timer(o =>
ReconnectTimer = new CTimer(o =>
{
if (ConnectEnabled) // Now thread-safe property access
if (ConnectEnabled)
{
Connect();
}
}, null, System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}, System.Threading.Timeout.Infinite);
}
/// <summary>
/// Handles closing this up when the program shuts down
/// </summary>
private void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
if (programEventType == eProgramStatusEventType.Stopping)
{
if (client != null)
if (Client != null)
{
this.LogDebug("Program stopping. Closing connection");
Disconnect();
@@ -242,10 +223,10 @@ namespace PepperDash.Core
this.LogDebug("Attempting connect");
// Cancel reconnect if running.
StopReconnectTimer();
ReconnectTimer?.Stop();
// Cleanup the old client if it already exists
if (client != null)
if (Client != null)
{
this.LogDebug("Cleaning up disconnected client");
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
@@ -258,36 +239,29 @@ namespace PepperDash.Core
this.LogDebug("Creating new SshClient");
ConnectionInfo connectionInfo = new ConnectionInfo(Hostname, Port, Username, pauth, kauth);
client = new SshClient(connectionInfo);
client.ErrorOccurred += Client_ErrorOccurred;
Client = new SshClient(connectionInfo);
Client.ErrorOccurred += Client_ErrorOccurred;
//Attempt to connect
ClientStatus = SocketStatus.SOCKET_STATUS_WAITING;
try
{
client.Connect();
var modes = new Dictionary<TerminalModes, uint>();
if (DisableEcho)
{
modes.Add(TerminalModes.ECHO, 0);
}
shellStream = client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534, modes);
if (shellStream.DataAvailable)
Client.Connect();
TheStream = Client.CreateShellStream("PDTShell", 0, 0, 0, 0, 65534);
if (TheStream.DataAvailable)
{
// empty the buffer if there is data
shellStream.Read();
string str = TheStream.Read();
}
shellStream.DataReceived += Stream_DataReceived;
TheStream.DataReceived += Stream_DataReceived;
this.LogInformation("Connected");
ClientStatus = SocketStatus.SOCKET_STATUS_CONNECTED;
disconnectLogged = false;
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)
{
@@ -312,36 +286,37 @@ namespace PepperDash.Core
this.LogVerbose(ie, "Exception details: ");
}
disconnectLogged = true;
DisconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {autoReconnect}, {autoReconnectInterval}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
ReconnectTimer.Reset(AutoReconnectIntervalMs);
}
}
catch (SshOperationTimeoutException ex)
{
this.LogWarning("Connection attempt timed out: {message}", ex.Message);
disconnectLogged = true;
DisconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
ReconnectTimer.Reset(AutoReconnectIntervalMs);
}
}
catch (Exception e)
{
var errorLogLevel = DisconnectLogged == true ? Debug.ErrorLogLevel.None : Debug.ErrorLogLevel.Error;
this.LogError("Unhandled exception on connect: {error}", e.Message);
this.LogVerbose(e, "Exception details: ");
disconnectLogged = true;
DisconnectLogged = true;
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
if (AutoReconnect)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
ReconnectTimer.Reset(AutoReconnectIntervalMs);
}
}
}
@@ -359,7 +334,11 @@ namespace PepperDash.Core
{
ConnectEnabled = false;
// Stop trying reconnects, if we are
StopReconnectTimer();
if (ReconnectTimer != null)
{
ReconnectTimer.Stop();
// ReconnectTimer = null;
}
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
}
@@ -373,12 +352,12 @@ namespace PepperDash.Core
try
{
if (client != null)
if (Client != null)
{
client.ErrorOccurred -= Client_ErrorOccurred;
client.Disconnect();
client.Dispose();
client = null;
Client.ErrorOccurred -= Client_ErrorOccurred;
Client.Disconnect();
Client.Dispose();
Client = null;
ClientStatus = status;
this.LogDebug("Disconnected");
}
@@ -392,16 +371,16 @@ namespace PepperDash.Core
/// <summary>
/// Kills the stream
/// </summary>
private void KillStream()
void KillStream()
{
try
{
if (shellStream != null)
if (TheStream != null)
{
shellStream.DataReceived -= Stream_DataReceived;
shellStream.Close();
shellStream.Dispose();
shellStream = null;
TheStream.DataReceived -= Stream_DataReceived;
TheStream.Close();
TheStream.Dispose();
TheStream = null;
this.LogDebug("Disconnected stream");
}
}
@@ -414,7 +393,7 @@ namespace PepperDash.Core
/// <summary>
/// Handles the keyboard interactive authentication, should it be required.
/// </summary>
private void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
void kauth_AuthenticationPrompt(object sender, AuthenticationPromptEventArgs e)
{
foreach (AuthenticationPrompt prompt in e.Prompts)
if (prompt.Request.IndexOf("Password:", StringComparison.InvariantCultureIgnoreCase) != -1)
@@ -424,7 +403,7 @@ namespace PepperDash.Core
/// <summary>
/// Handler for data receive on ShellStream. Passes data across to queue for line parsing.
/// </summary>
private void Stream_DataReceived(object sender, ShellDataEventArgs e)
void Stream_DataReceived(object sender, ShellDataEventArgs e)
{
if (((ShellStream)sender).Length <= 0L)
{
@@ -437,14 +416,18 @@ namespace PepperDash.Core
if (bytesHandler != null)
{
var bytes = Encoding.UTF8.GetBytes(response);
this.PrintReceivedBytes(bytes);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
this.LogInformation("Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
this.PrintReceivedText(response);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
this.LogInformation("Received: '{0}'", ComTextHelper.GetDebugText(response));
textHandler(this, new GenericCommMethodReceiveTextArgs(response));
}
@@ -456,7 +439,7 @@ namespace PepperDash.Core
/// Error event handler for client events - disconnect, etc. Will forward those events via ConnectionChange
/// event
/// </summary>
private void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
void Client_ErrorOccurred(object sender, ExceptionEventArgs e)
{
CrestronInvoke.BeginInvoke(o =>
{
@@ -476,7 +459,7 @@ namespace PepperDash.Core
if (AutoReconnect && ConnectEnabled)
{
this.LogDebug("Checking autoreconnect: {0}, {1}ms", AutoReconnect, AutoReconnectIntervalMs);
StartReconnectTimer();
ReconnectTimer.Reset(AutoReconnectIntervalMs);
}
});
}
@@ -484,7 +467,7 @@ namespace PepperDash.Core
/// <summary>
/// Helper for ConnectionChange event
/// </summary>
private void OnConnectionChange()
void OnConnectionChange()
{
ConnectionChange?.Invoke(this, new GenericSocketStatusChageEventArgs(this));
}
@@ -499,12 +482,16 @@ namespace PepperDash.Core
{
try
{
if (client != null && shellStream != null && IsConnected)
if (Client != null && TheStream != null && IsConnected)
{
this.PrintSentText(text);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
this.LogInformation(
"Sending {length} characters of text: '{text}'",
text.Length,
ComTextHelper.GetDebugText(text));
shellStream.Write(text);
shellStream.Flush();
TheStream.Write(text);
TheStream.Flush();
}
else
{
@@ -516,7 +503,7 @@ namespace PepperDash.Core
this.LogError("ObjectDisposedException sending '{message}'. Restarting connection...", text.Trim());
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
StartReconnectTimer();
ReconnectTimer.Reset();
}
catch (Exception ex)
{
@@ -532,12 +519,13 @@ namespace PepperDash.Core
{
try
{
if (client != null && shellStream != null && IsConnected)
if (Client != null && TheStream != null && IsConnected)
{
this.PrintSentBytes(bytes);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
this.LogInformation("Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
shellStream.Write(bytes, 0, bytes.Length);
shellStream.Flush();
TheStream.Write(bytes, 0, bytes.Length);
TheStream.Flush();
}
else
{
@@ -549,7 +537,7 @@ namespace PepperDash.Core
this.LogException(ex, "ObjectDisposedException sending {message}", ComTextHelper.GetEscapedText(bytes));
KillClient(SocketStatus.SOCKET_STATUS_CONNECT_FAILED);
StartReconnectTimer();
ReconnectTimer.Reset();
}
catch (Exception ex)
{
@@ -558,83 +546,6 @@ namespace PepperDash.Core
}
#endregion
/// <summary>
/// Safely starts the reconnect timer with exception handling
/// </summary>
private void StartReconnectTimer()
{
try
{
reconnectTimer?.Change(AutoReconnectIntervalMs, System.Threading.Timeout.Infinite);
}
catch (ObjectDisposedException)
{
// Timer was disposed, ignore
this.LogDebug("Attempted to start timer but it was already disposed");
}
}
/// <summary>
/// Safely stops the reconnect timer with exception handling
/// </summary>
private void StopReconnectTimer()
{
try
{
reconnectTimer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}
catch (ObjectDisposedException)
{
// Timer was disposed, ignore
this.LogDebug("Attempted to stop timer but it was already disposed");
}
}
/// <summary>
/// Deactivate method - properly dispose of resources
/// </summary>
public override bool Deactivate()
{
try
{
this.LogDebug("Deactivating SSH client - disposing resources");
// Stop trying reconnects
ConnectEnabled = false;
StopReconnectTimer();
// Disconnect and cleanup client
KillClient(SocketStatus.SOCKET_STATUS_BROKEN_LOCALLY);
// Dispose timer
try
{
reconnectTimer?.Dispose();
}
catch (ObjectDisposedException)
{
// Already disposed, ignore
}
// Dispose semaphore
try
{
connectLock?.Dispose();
}
catch (ObjectDisposedException)
{
// Already disposed, ignore
}
return base.Deactivate();
}
catch (Exception ex)
{
this.LogException(ex, "Error during SSH client deactivation");
return false;
}
}
}
//*****************************************************************************************************

View File

@@ -19,44 +19,44 @@ namespace PepperDash.Core
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <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 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;
/// <summary>
/// Fires when data is received from the server and returns it as text
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
///
/// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
/// <summary>
///
/// </summary>
//public event GenericSocketStatusChangeEventDelegate SocketStatusChange;
public event EventHandler<GenericSocketStatusChageEventArgs> ConnectionChange;
private string _hostname;
private string _hostname;
/// <summary>
/// Address of server
/// </summary>
public string Hostname
{
get
{
return _hostname;
}
get
{
return _hostname;
}
set
{
_hostname = value;
if (_client != null)
{
_client.AddressClientConnectedTo = _hostname;
}
}
}
set
{
_hostname = value;
if (_client != null)
{
_client.AddressClientConnectedTo = _hostname;
}
}
}
/// <summary>
/// Gets or sets the Port
@@ -78,19 +78,19 @@ namespace PepperDash.Core
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// The actual client class
/// </summary>
private TCPClient _client;
/// <summary>
/// The actual client class
/// </summary>
private TCPClient _client;
/// <summary>
/// Bool showing if socket is connected
/// </summary>
public bool IsConnected
{
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
/// <summary>
/// Bool showing if socket is connected
/// </summary>
public bool IsConnected
{
get { return _client != null && _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
/// <summary>
/// S+ helper for IsConnected
/// </summary>
@@ -99,15 +99,15 @@ namespace PepperDash.Core
get { return (ushort)(IsConnected ? 1 : 0); }
}
/// <summary>
/// _client socket status Read only
/// </summary>
public SocketStatus ClientStatus
{
get
/// <summary>
/// _client socket status Read only
/// </summary>
public SocketStatus ClientStatus
{
get
{
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
}
return _client == null ? SocketStatus.SOCKET_STATUS_NO_CONNECT : _client.ClientStatus;
}
}
/// <summary>
@@ -119,26 +119,26 @@ namespace PepperDash.Core
get { return (ushort)ClientStatus; }
}
/// <summary>
/// <summary>
/// Status text shows the message associated with socket status
/// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } }
/// </summary>
public string ClientStatusText { get { return ClientStatus.ToString(); } }
/// <summary>
/// Ushort representation of client status
/// </summary>
/// <summary>
/// Ushort representation of client status
/// </summary>
[Obsolete]
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
public ushort UClientStatus { get { return (ushort)ClientStatus; } }
/// <summary>
/// Connection failure reason
/// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary>
/// Connection failure reason
/// </summary>
public string ConnectionFailure { get { return ClientStatus.ToString(); } }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// S+ helper for AutoReconnect
@@ -149,29 +149,29 @@ namespace PepperDash.Core
set { AutoReconnect = value == 1; }
}
/// <summary>
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Milliseconds to wait before attempting to reconnect. Defaults to 5000
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Set only when the disconnect method is called
/// </summary>
bool DisconnectCalledByUser;
/// <summary>
/// Set only when the disconnect method is called
/// </summary>
bool DisconnectCalledByUser;
/// <summary>
///
/// </summary>
public bool Connected
{
get { return _client.ClientStatus == SocketStatus.SOCKET_STATUS_CONNECTED; }
}
/// <summary>
///
/// </summary>
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;
private CTimer RetryTimer;
/// <summary>
/// Constructor
@@ -181,8 +181,8 @@ namespace PepperDash.Core
/// <param name="port"></param>
/// <param name="bufferSize"></param>
public GenericTcpIpClient(string key, string address, int port, int bufferSize)
: base(key)
{
: base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
@@ -218,18 +218,18 @@ namespace PepperDash.Core
/// Default constructor for S+
/// </summary>
public GenericTcpIpClient()
: base(SplusKey)
: base(SplusKey)
{
StreamDebugging = new CommunicationStreamDebugging(SplusKey);
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
AutoReconnectIntervalMs = 5000;
BufferSize = 2000;
RetryTimer = new CTimer(o =>
{
Reconnect();
}, Timeout.Infinite);
}
}
/// <summary>
/// Initialize method
@@ -255,26 +255,26 @@ namespace PepperDash.Core
///
/// </summary>
/// <returns></returns>
/// <summary>
/// Deactivate method
/// </summary>
public override bool Deactivate()
{
/// <summary>
/// Deactivate method
/// </summary>
public override bool Deactivate()
{
RetryTimer.Stop();
RetryTimer.Dispose();
if (_client != null)
{
_client.SocketStatusChange -= this.Client_SocketStatusChange;
_client.SocketStatusChange -= this.Client_SocketStatusChange;
DisconnectClient();
}
return true;
}
return true;
}
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
if (string.IsNullOrEmpty(Hostname))
{
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericTcpIpClient '{0}': No address set", Key);
@@ -310,7 +310,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
}
}
private void Reconnect()
{
@@ -337,11 +337,11 @@ namespace PepperDash.Core
}
}
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
try
{
connectLock.Enter();
@@ -355,7 +355,7 @@ namespace PepperDash.Core
{
connectLock.Leave();
}
}
}
/// <summary>
/// DisconnectClient method
@@ -375,7 +375,7 @@ namespace PepperDash.Core
/// </summary>
/// <param name="c"></param>
void ConnectToServerCallback(TCPClient c)
{
{
if (c.ClientStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Server connection result: {0}", c.ClientStatus);
@@ -385,13 +385,13 @@ namespace PepperDash.Core
{
Debug.Console(1, this, "Server connection result: {0}", c.ClientStatus);
}
}
}
/// <summary>
/// Disconnects, waits and attemtps to connect again
/// </summary>
void WaitAndTryReconnect()
{
{
CrestronInvoke.BeginInvoke(o =>
{
try
@@ -409,7 +409,7 @@ namespace PepperDash.Core
connectLock.Leave();
}
});
}
}
/// <summary>
/// Recieves incoming data
@@ -417,7 +417,7 @@ namespace PepperDash.Core
/// <param name="client"></param>
/// <param name="numBytes"></param>
void Receive(TCPClient client, int numBytes)
{
{
if (client != null)
{
if (numBytes > 0)
@@ -426,7 +426,10 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
this.PrintReceivedBytes(bytes);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
{
Debug.Console(0, this, "Received {1} bytes: '{0}'", ComTextHelper.GetEscapedText(bytes), bytes.Length);
}
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
@@ -434,53 +437,58 @@ namespace PepperDash.Core
{
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
this.PrintReceivedText(str);
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);
}
}
}
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(text);
// Check debug level before processing byte array
this.PrintSentText(text);
/// <summary>
/// SendText method
/// </summary>
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);
}
_client.SendData(bytes, bytes.Length);
}
/// <summary>
/// SendEscapedText method
/// </summary>
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);
}
/// <summary>
/// SendEscapedText method
/// </summary>
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);
}
/// <summary>
/// Sends Bytes to the server
/// </summary>
/// <param name="bytes"></param>
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
this.PrintSentBytes(bytes);
/// <summary>
/// SendBytes method
/// </summary>
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);
}
_client.SendData(bytes, bytes.Length);
}
/// <summary>
/// Socket Status Change Handler
@@ -488,7 +496,7 @@ namespace PepperDash.Core
/// <param name="client"></param>
/// <param name="clientSocketStatus"></param>
void Client_SocketStatusChange(TCPClient client, SocketStatus clientSocketStatus)
{
{
if (clientSocketStatus != SocketStatus.SOCKET_STATUS_CONNECTED)
{
Debug.Console(0, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
@@ -497,73 +505,68 @@ namespace PepperDash.Core
else
{
Debug.Console(1, this, "Socket status change {0} ({1})", clientSocketStatus, ClientStatusText);
_client.ReceiveDataAsync(Receive);
_client.ReceiveDataAsync(Receive);
}
var handler = ConnectionChange;
if (handler != null)
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
}
}
var handler = ConnectionChange;
if (handler != null)
ConnectionChange(this, new GenericSocketStatusChageEventArgs(this));
}
}
/// <summary>
/// Represents a TcpSshPropertiesConfig
/// </summary>
public class TcpSshPropertiesConfig
{
/// <summary>
/// Represents a TcpSshPropertiesConfig
/// </summary>
public class TcpSshPropertiesConfig
{
/// <summary>
/// Address to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public string Address { get; set; }
public string Address { get; set; }
/// <summary>
/// Port to connect to
/// </summary>
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
[JsonProperty(Required = Required.Always)]
public int Port { get; set; }
/// <summary>
/// Username credential
/// </summary>
public string Username { get; set; }
/// <summary>
/// Gets or sets the Password
/// </summary>
public string Password { get; set; }
public string Username { get; set; }
/// <summary>
/// Gets or sets the Password
/// </summary>
public string Password { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Defaults to 32768
/// </summary>
public int BufferSize { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnect
/// </summary>
public bool AutoReconnect { get; set; }
/// <summary>
/// Gets or sets the AutoReconnectIntervalMs
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// When true, turns off echo for the SSH session
/// </summary>
[JsonProperty("disableSshEcho")]
public bool DisableSshEcho { get; set; }
/// <summary>
/// Gets or sets the AutoReconnectIntervalMs
/// </summary>
public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// Default constructor
/// </summary>
public TcpSshPropertiesConfig()
{
BufferSize = 32768;
AutoReconnect = true;
AutoReconnectIntervalMs = 5000;
{
BufferSize = 32768;
AutoReconnect = true;
AutoReconnectIntervalMs = 5000;
Username = "";
Password = "";
DisableSshEcho = false;
}
}
}
}
}

View File

@@ -124,7 +124,7 @@ namespace PepperDash.Core
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
}
/// <summary>
///
/// </summary>
@@ -135,7 +135,7 @@ namespace PepperDash.Core
public GenericUdpServer(string key, string address, int port, int bufferSize)
: base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
StreamDebugging = new CommunicationStreamDebugging(key);
Hostname = address;
Port = port;
BufferSize = bufferSize;
@@ -180,7 +180,7 @@ namespace PepperDash.Core
/// <param name="programEventType"></param>
void CrestronEnvironment_ProgramStatusEventHandler(eProgramStatusEventType programEventType)
{
if (programEventType != eProgramStatusEventType.Stopping)
if (programEventType != eProgramStatusEventType.Stopping)
return;
Debug.Console(1, this, "Program stopping. Disabling Server");
@@ -243,7 +243,7 @@ namespace PepperDash.Core
/// </summary>
public void Disconnect()
{
if (Server != null)
if(Server != null)
Server.DisableUDPServer();
IsConnected = false;
@@ -265,7 +265,7 @@ namespace PepperDash.Core
try
{
if (numBytes <= 0)
if (numBytes <= 0)
return;
var sourceIp = Server.IPAddressLastMessageReceivedFrom;
@@ -281,13 +281,17 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
this.PrintReceivedBytes(bytes);
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)
{
this.PrintReceivedText(str);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Received {1} characters of text: '{0}'", ComTextHelper.GetDebugText(str), str.Length);
textHandler(this, new GenericCommMethodReceiveTextArgs(str));
}
}
@@ -314,7 +318,8 @@ namespace PepperDash.Core
if (IsConnected && Server != null)
{
this.PrintSentText(text);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
Server.SendData(bytes, bytes.Length);
}
@@ -329,7 +334,8 @@ namespace PepperDash.Core
/// </summary>
public void SendBytes(byte[] bytes)
{
this.PrintSentBytes(bytes);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length);
@@ -337,11 +343,11 @@ namespace PepperDash.Core
}
/// <summary>
/// Represents a GenericUdpReceiveTextExtraArgs
/// </summary>
public class GenericUdpReceiveTextExtraArgs : EventArgs
{
/// <summary>
/// Represents a GenericUdpReceiveTextExtraArgs
/// </summary>
public class GenericUdpReceiveTextExtraArgs : EventArgs
{
/// <summary>
///
/// </summary>
@@ -353,7 +359,7 @@ namespace PepperDash.Core
/// <summary>
///
/// </summary>
public int Port { get; private set; }
public int Port { get; private set; }
/// <summary>
///
/// </summary>
@@ -367,18 +373,18 @@ namespace PepperDash.Core
/// <param name="port"></param>
/// <param name="bytes"></param>
public GenericUdpReceiveTextExtraArgs(string text, string ipAddress, int port, byte[] bytes)
{
Text = text;
IpAddress = ipAddress;
Port = port;
Bytes = bytes;
}
{
Text = text;
IpAddress = ipAddress;
Port = port;
Bytes = bytes;
}
/// <summary>
/// Stupid S+ Constructor
/// </summary>
public GenericUdpReceiveTextExtraArgs() { }
}
/// <summary>
/// Stupid S+ Constructor
/// </summary>
public GenericUdpReceiveTextExtraArgs() { }
}
/// <summary>
///

View File

@@ -1,69 +0,0 @@
using System;
using Crestron.SimplSharp;
namespace PepperDash.Core
{
/// <summary>
/// Extension methods for stream debugging
/// </summary>
public static class StreamDebuggingExtensions
{
private static readonly string app = CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? $"App {InitialParametersClass.ApplicationNumber}" : $"{InitialParametersClass.RoomId}";
/// <summary>
/// Print the sent bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
public static void PrintSentBytes(this IStreamDebugging comms, byte[] bytes)
{
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
}
/// <summary>
/// Print the received bytes to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="bytes">bytes to print</param>
public static void PrintReceivedBytes(this IStreamDebugging comms, byte[] bytes)
{
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received {bytes.Length} bytes: '{ComTextHelper.GetEscapedText(bytes)}'");
}
/// <summary>
/// Print the sent text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
public static void PrintSentText(this IStreamDebugging comms, string text)
{
if (!comms.StreamDebugging.TxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Sending Text: '{ComTextHelper.GetDebugText(text)}'");
}
/// <summary>
/// Print the received text to the console
/// </summary>
/// <param name="comms">comms device</param>
/// <param name="text">text to print</param>
public static void PrintReceivedText(this IStreamDebugging comms, string text)
{
if (!comms.StreamDebugging.RxStreamDebuggingIsEnabled) return;
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
CrestronConsole.PrintLine($"[{timestamp}][{app}][{comms.Key}] Received Text: '{ComTextHelper.GetDebugText(text)}'");
}
}
}

View File

@@ -78,10 +78,6 @@ namespace PepperDash.Core
/// <summary>
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
/// </summary>
ComBridge,
/// <summary>
/// InfinetEX control
/// </summary>
InfinetEx
ComBridge
}
}

View File

@@ -1,24 +0,0 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging data format types
/// </summary>
[Flags]
public enum eStreamDebuggingDataTypeSettings
{
/// <summary>
/// Debug data in byte format
/// </summary>
Bytes = 0,
/// <summary>
/// Debug data in text format
/// </summary>
Text = 1,
/// <summary>
/// Debug data in both byte and text formats
/// </summary>
Both = Bytes | Text
}
}

View File

@@ -1,28 +0,0 @@
using System;
namespace PepperDash.Core
{
/// <summary>
/// The available settings for stream debugging
/// </summary>
[Flags]
public enum eStreamDebuggingSetting
{
/// <summary>
/// Debug off
/// </summary>
Off = 0,
/// <summary>
/// Debug received data
/// </summary>
Rx = 1,
/// <summary>
/// Debug transmitted data
/// </summary>
Tx = 2,
/// <summary>
/// Debug both received and transmitted data
/// </summary>
Both = Rx | Tx
}
}

View File

@@ -1,7 +1,10 @@
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;
namespace PepperDash.Core
@@ -39,7 +42,7 @@ namespace PepperDash.Core
/// Defines the contract for IBasicCommunication
/// </summary>
public interface IBasicCommunication : ICommunicationReceiver
{
{
/// <summary>
/// Send text to the device
/// </summary>
@@ -51,7 +54,7 @@ namespace PepperDash.Core
/// </summary>
/// <param name="bytes"></param>
void SendBytes(byte[] bytes);
}
}
/// <summary>
/// Represents a device that implements IBasicCommunication and IStreamDebugging
@@ -64,7 +67,7 @@ namespace PepperDash.Core
/// <summary>
/// Represents a device with stream debugging capablities
/// </summary>
public interface IStreamDebugging : IKeyed
public interface IStreamDebugging
{
/// <summary>
/// Object to enable stream debugging
@@ -73,12 +76,12 @@ namespace PepperDash.Core
CommunicationStreamDebugging StreamDebugging { get; }
}
/// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient
/// </summary>
public interface ISocketStatus : IBasicCommunication
{
/// <summary>
/// For IBasicCommunication classes that have SocketStatus. GenericSshClient,
/// GenericTcpIpClient
/// </summary>
public interface ISocketStatus : IBasicCommunication
{
/// <summary>
/// Notifies of socket status changes
/// </summary>
@@ -90,7 +93,7 @@ namespace PepperDash.Core
[JsonProperty("clientStatus")]
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
SocketStatus ClientStatus { get; }
}
}
/// <summary>
/// Describes a device that implements ISocketStatus and IStreamDebugging
@@ -104,24 +107,24 @@ namespace PepperDash.Core
/// Describes a device that can automatically attempt to reconnect
/// </summary>
public interface IAutoReconnect
{
{
/// <summary>
/// Enable automatic recconnect
/// </summary>
[JsonProperty("autoReconnect")]
bool AutoReconnect { get; set; }
bool AutoReconnect { get; set; }
/// <summary>
/// Interval in ms to attempt automatic recconnections
/// </summary>
[JsonProperty("autoReconnectIntervalMs")]
int AutoReconnectIntervalMs { get; set; }
}
int AutoReconnectIntervalMs { get; set; }
}
/// <summary>
///
/// </summary>
public enum eGenericCommMethodStatusChangeType
{
/// <summary>
///
/// </summary>
public enum eGenericCommMethodStatusChangeType
{
/// <summary>
/// Connected
/// </summary>
@@ -130,45 +133,45 @@ namespace PepperDash.Core
/// Disconnected
/// </summary>
Disconnected
}
}
/// <summary>
/// This delegate defines handler for IBasicCommunication status changes
/// </summary>
/// <param name="comm">Device firing the status change</param>
/// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary>
/// This delegate defines handler for IBasicCommunication status changes
/// </summary>
/// <param name="comm">Device firing the status change</param>
/// <param name="status"></param>
public delegate void GenericCommMethodStatusHandler(IBasicCommunication comm, eGenericCommMethodStatusChangeType status);
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs
{
/// <summary>
/// Gets or sets the Bytes
/// </summary>
public byte[] Bytes { get; private set; }
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveBytesArgs : EventArgs
{
/// <summary>
/// Gets or sets the Bytes
/// </summary>
public byte[] Bytes { get; private set; }
/// <summary>
///
/// </summary>
/// <param name="bytes"></param>
public GenericCommMethodReceiveBytesArgs(byte[] bytes)
{
Bytes = bytes;
}
{
Bytes = bytes;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveBytesArgs() { }
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveBytesArgs() { }
}
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
public class GenericCommMethodReceiveTextArgs : EventArgs
{
/// <summary>
///
/// </summary>
@@ -182,9 +185,9 @@ namespace PepperDash.Core
/// </summary>
/// <param name="text"></param>
public GenericCommMethodReceiveTextArgs(string text)
{
Text = text;
}
{
Text = text;
}
/// <summary>
///
@@ -192,14 +195,59 @@ namespace PepperDash.Core
/// <param name="text"></param>
/// <param name="delimiter"></param>
public GenericCommMethodReceiveTextArgs(string text, string delimiter)
: this(text)
:this(text)
{
Delimiter = delimiter;
}
/// <summary>
/// S+ Constructor
/// </summary>
public GenericCommMethodReceiveTextArgs() { }
}
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// S+ Constructor
/// Gets escaped text for a byte array
/// </summary>
public GenericCommMethodReceiveTextArgs() { }
}
/// <param name="bytes"></param>
/// <returns></returns>
public static string GetEscapedText(byte[] bytes)
{
return String.Concat(bytes.Select(b => string.Format(@"[{0:X2}]", (int)b)).ToArray());
}
/// <summary>
/// Gets escaped text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetEscapedText method
/// </summary>
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());
}
/// <summary>
/// Gets debug text for a string
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
/// <summary>
/// GetDebugText method
/// </summary>
public static string GetDebugText(string text)
{
return Regex.Replace(text, @"[^\u0020-\u007E]", a => GetEscapedText(a.Value));
}
}
}

View File

@@ -9,59 +9,40 @@ using Serilog.Events;
namespace PepperDash.Core.Config
{
/// <summary>
/// Reads a Portal formatted config file
/// </summary>
public class PortalConfigReader
{
const string template = "template";
const string system = "system";
const string systemUrl = "system_url";
const string templateUrl = "template_url";
const string info = "info";
const string devices = "devices";
const string rooms = "rooms";
const string sourceLists = "sourceLists";
const string destinationLists = "destinationLists";
const string cameraLists = "cameraLists";
const string audioControlPointLists = "audioControlPointLists";
const string tieLines = "tieLines";
const string joinMaps = "joinMaps";
const string global = "global";
/// <summary>
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
/// </summary>
/// <returns>JObject of config file</returns>
public static void ReadAndMergeFileIfNecessary(string filePath, string savePath)
/// <summary>
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
/// </summary>
/// <returns>JObject of config file</returns>
public static void ReadAndMergeFileIfNecessary(string filePath, string savePath)
{
try
{
if (!File.Exists(filePath))
{
Debug.LogError(
Debug.Console(1, Debug.ErrorLogLevel.Error,
"ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
}
using (StreamReader fs = new StreamReader(filePath))
{
var jsonObj = JObject.Parse(fs.ReadToEnd());
if(jsonObj[template] != null && jsonObj[system] != null)
if(jsonObj["template"] != null && jsonObj["system"] != null)
{
// it's a double-config, merge it.
var merged = MergeConfigs(jsonObj);
if (jsonObj[systemUrl] != null)
if (jsonObj["system_url"] != null)
{
merged[systemUrl] = jsonObj[systemUrl].Value<string>();
merged["systemUrl"] = jsonObj["system_url"].Value<string>();
}
if (jsonObj[templateUrl] != null)
if (jsonObj["template_url"] != null)
{
merged[templateUrl] = jsonObj[templateUrl].Value<string>();
merged["templateUrl"] = jsonObj["template_url"].Value<string>();
}
jsonObj = merged;
@@ -96,62 +77,62 @@ namespace PepperDash.Core.Config
var merged = new JObject();
// Put together top-level objects
if (system[info] != null)
merged.Add(info, Merge(template[info], system[info], info));
if (system["info"] != null)
merged.Add("info", Merge(template["info"], system["info"], "infO"));
else
merged.Add(info, template[info]);
merged.Add("info", template["info"]);
merged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray,
system[devices] as JArray, "key", devices));
merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
system["devices"] as JArray, "key", "devices"));
if (system[rooms] == null)
merged.Add(rooms, template[rooms]);
if (system["rooms"] == null)
merged.Add("rooms", template["rooms"]);
else
merged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray,
system[rooms] as JArray, "key", rooms));
merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
system["rooms"] as JArray, "key", "rooms"));
if (system[sourceLists] == null)
merged.Add(sourceLists, template[sourceLists]);
if (system["sourceLists"] == null)
merged.Add("sourceLists", template["sourceLists"]);
else
merged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists));
merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists"));
if (system[destinationLists] == null)
merged.Add(destinationLists, template[destinationLists]);
if (system["destinationLists"] == null)
merged.Add("destinationLists", template["destinationLists"]);
else
merged.Add(destinationLists,
Merge(template[destinationLists], system[destinationLists], destinationLists));
merged.Add("destinationLists",
Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
if (system[cameraLists] == null)
merged.Add(cameraLists, template[cameraLists]);
if (system["cameraLists"] == null)
merged.Add("cameraLists", template["cameraLists"]);
else
merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists));
merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
if (system[audioControlPointLists] == null)
merged.Add(audioControlPointLists, template[audioControlPointLists]);
if (system["audioControlPointLists"] == null)
merged.Add("audioControlPointLists", template["audioControlPointLists"]);
else
merged.Add(audioControlPointLists,
Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists));
merged.Add("audioControlPointLists",
Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
// Template tie lines take precedence. Config tool doesn't do them at system
// level anyway...
if (template[tieLines] != null)
merged.Add(tieLines, template[tieLines]);
else if (system[tieLines] != null)
merged.Add(tieLines, system[tieLines]);
if (template["tieLines"] != null)
merged.Add("tieLines", template["tieLines"]);
else if (system["tieLines"] != null)
merged.Add("tieLines", system["tieLines"]);
else
merged.Add(tieLines, new JArray());
merged.Add("tieLines", new JArray());
if (template[joinMaps] != null)
merged.Add(joinMaps, template[joinMaps]);
if (template["joinMaps"] != null)
merged.Add("joinMaps", template["joinMaps"]);
else
merged.Add(joinMaps, new JObject());
merged.Add("joinMaps", new JObject());
if (system[global] != null)
merged.Add(global, Merge(template[global], system[global], global));
if (system["global"] != null)
merged.Add("global", Merge(template["global"], system["global"], "global"));
else
merged.Add(global, template[global]);
merged.Add("global", template["global"]);
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
return merged;
@@ -247,7 +228,7 @@ namespace PepperDash.Core.Config
}
catch (Exception e)
{
Debug.LogError($"Cannot merge items at path {propPath}: \r{e}");
Debug.Console(1, Debug.ErrorLogLevel.Warning, "Cannot merge items at path {0}: \r{1}", propPath, e);
}
}
}

View File

@@ -168,7 +168,7 @@ namespace PepperDash.Core
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
levelSwitch: _fileLogLevelSwitch
);
@@ -1081,6 +1081,9 @@ namespace PepperDash.Core
/// Logs to Console when at-level, and all messages to error log
/// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
/// <summary>
/// Console method
/// </summary>
public static void Console(uint level, ErrorLogLevel errorLogLevel,
string format, params object[] items)
{
@@ -1093,6 +1096,9 @@ namespace PepperDash.Core
/// it will only be written to the log.
/// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")]
/// <summary>
/// ConsoleWithLog method
/// </summary>
public static void ConsoleWithLog(uint level, string format, params object[] items)
{
LogMessage(level, format, items);

View File

@@ -2,29 +2,25 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport;
using Crestron.SimplSharpPro.EthernetCommunication;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Config;
using Serilog.Events;
//using PepperDash.Essentials.Devices.Common.Cameras;
namespace PepperDash.Essentials.Core.Bridges
{
/// <summary>
/// Base class for bridge API variants
/// </summary>
[Obsolete("Will be removed in v3.0.0")]
public abstract class BridgeApi : EssentialsDevice
{
/// <summary>
/// Constructor
/// </summary>
/// <param name="key">Device key</param>
protected BridgeApi(string key) :
base(key)
{
@@ -33,36 +29,23 @@ namespace PepperDash.Essentials.Core.Bridges
}
/// <summary>
/// Class to link devices and rooms to an EISC Instance
/// Represents a EiscApiAdvanced
/// </summary>
public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor
{
/// <summary>
/// Gets the PropertiesConfig
/// </summary>
public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
/// <summary>
/// Gets the JoinMaps dictionary
/// </summary>
public Dictionary<string, JoinMapBaseAdvanced> JoinMaps { get; private set; }
/// <summary>
/// Gets the EISC instance
/// </summary>
public BasicTriList Eisc { get; private set; }
/// <summary>
/// Constructor
/// </summary>
/// <param name="dc">Device configuration</param>
/// <param name="eisc">EISC instance</param>
public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :
base(dc.Key)
{
JoinMaps = new Dictionary<string, JoinMapBaseAdvanced>();
PropertiesConfig = dc.Properties.ToObject<EiscApiPropertiesConfig>();
//PropertiesConfig = JsonConvert.DeserializeObject<EiscApiPropertiesConfig>(dc.Properties.ToString());
Eisc = eisc;
@@ -77,7 +60,8 @@ namespace PepperDash.Essentials.Core.Bridges
/// <summary>
/// CustomActivate method
/// </summary>
/// </summary>
/// <inheritdoc />
public override bool CustomActivate()
{
CommunicationMonitor.Start();
@@ -99,7 +83,7 @@ namespace PepperDash.Essentials.Core.Bridges
if (PropertiesConfig.Devices == null)
{
this.LogDebug("No devices linked to this bridge");
Debug.LogMessage(LogEventLevel.Debug, this, "No devices linked to this bridge");
return;
}
@@ -120,7 +104,9 @@ namespace PepperDash.Essentials.Core.Bridges
continue;
}
this.LogWarning("{deviceKey} is not compatible with this bridge type. Please update the device.", device.Key);
Debug.LogMessage(LogEventLevel.Information, this,
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
device.Key);
}
}
@@ -135,31 +121,34 @@ namespace PepperDash.Essentials.Core.Bridges
if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)
{
this.LogVerbose("Registration result: {registerResult}", registerResult);
Debug.LogMessage(LogEventLevel.Verbose, this, "Registration result: {0}", registerResult);
return;
}
this.LogDebug("EISC registration successful");
Debug.LogMessage(LogEventLevel.Debug, this, "EISC registration successful");
}
/// <summary>
/// Link rooms to this EISC. Rooms MUST implement IBridgeAdvanced
/// LinkRooms method
/// </summary>
public void LinkRooms()
{
this.LogDebug("Linking Rooms...");
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Rooms...");
if (PropertiesConfig.Rooms == null)
{
this.LogDebug("No rooms linked to this bridge.");
Debug.LogMessage(LogEventLevel.Debug, this, "No rooms linked to this bridge.");
return;
}
foreach (var room in PropertiesConfig.Rooms)
{
if (!(DeviceManager.GetDeviceForKey(room.RoomKey) is IBridgeAdvanced rm))
var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced;
if (rm == null)
{
this.LogDebug("Room {roomKey} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
Debug.LogMessage(LogEventLevel.Debug, this,
"Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
continue;
}
@@ -170,8 +159,11 @@ namespace PepperDash.Essentials.Core.Bridges
/// <summary>
/// Adds a join map
/// </summary>
/// <param name="deviceKey">The key of the device to add the join map for</param>
/// <param name="joinMap">The join map to add</param>
/// <param name="deviceKey"></param>
/// <param name="joinMap"></param>
/// <summary>
/// AddJoinMap method
/// </summary>
public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)
{
if (!JoinMaps.ContainsKey(deviceKey))
@@ -180,13 +172,14 @@ namespace PepperDash.Essentials.Core.Bridges
}
else
{
this.LogWarning("Unable to add join map with key '{deviceKey}'. Key already exists in JoinMaps dictionary", deviceKey);
Debug.LogMessage(LogEventLevel.Verbose, this, "Unable to add join map with key '{0}'. Key already exists in JoinMaps dictionary", deviceKey);
}
}
/// <summary>
/// PrintJoinMaps method
/// </summary>
/// </summary>
/// <inheritdoc />
public virtual void PrintJoinMaps()
{
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
@@ -197,17 +190,17 @@ namespace PepperDash.Essentials.Core.Bridges
joinMap.Value.PrintJoinMapInfo();
}
}
/// <summary>
/// MarkdownForBridge method
/// </summary>
/// </summary>
/// <inheritdoc />
public virtual void MarkdownForBridge(string bridgeKey)
{
this.LogInformation("Writing Joinmaps to files for EISC IPID: {eiscId}", Eisc.ID.ToString("X"));
Debug.LogMessage(LogEventLevel.Information, this, "Writing Joinmaps to files for EISC IPID: {0}", Eisc.ID.ToString("X"));
foreach (var joinMap in JoinMaps)
{
this.LogInformation("Generating markdown for device '{deviceKey}':", joinMap.Key);
Debug.LogMessage(LogEventLevel.Information, "Generating markdown for device '{0}':", joinMap.Key);
joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);
}
}
@@ -215,45 +208,53 @@ namespace PepperDash.Essentials.Core.Bridges
/// <summary>
/// Prints the join map for a device by key
/// </summary>
/// <param name="deviceKey">The key of the device to print the join map for</param>
/// <param name="deviceKey"></param>
/// <summary>
/// PrintJoinMapForDevice method
/// </summary>
public void PrintJoinMapForDevice(string deviceKey)
{
var joinMap = JoinMaps[deviceKey];
if (joinMap == null)
{
this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
return;
}
this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
joinMap.PrintJoinMapInfo();
}
/// <summary>
/// Prints the join map for a device by key in Markdown format
/// Prints the join map for a device by key
/// </summary>
/// <param name="deviceKey"></param>
/// <summary>
/// MarkdownJoinMapForDevice method
/// </summary>
/// <param name="deviceKey">The key of the device to print the join map for</param>
/// <param name="bridgeKey">The key of the bridge to use for the Markdown output</param>
public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)
{
var joinMap = JoinMaps[deviceKey];
if (joinMap == null)
{
this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
return;
}
this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);
}
/// <summary>
/// Used for debugging to trigger an action based on a join number and type
/// </summary>
/// <param name="join">The join number to execute the action for</param>
/// <param name="type">The type of join (digital, analog, serial)</param>
/// <param name="state">The state to pass to the action</param>
/// <param name="join"></param>
/// <param name="type"></param>
/// <param name="state"></param>
/// <summary>
/// ExecuteJoinAction method
/// </summary>
public void ExecuteJoinAction(uint join, string type, object state)
{
try
@@ -262,87 +263,78 @@ namespace PepperDash.Essentials.Core.Bridges
{
case "digital":
{
if (Eisc.BooleanOutput[join].UserObject is Action<bool> userObject)
var uo = Eisc.BooleanOutput[join].UserObject as Action<bool>;
if (uo != null)
{
this.LogVerbose("Executing Boolean Action");
userObject(Convert.ToBoolean(state));
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
uo(Convert.ToBoolean(state));
}
else
this.LogVerbose("User Object is null. Nothing to Execute");
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
break;
}
case "analog":
{
if (Eisc.UShortOutput[join].UserObject is Action<ushort> userObject)
var uo = Eisc.BooleanOutput[join].UserObject as Action<ushort>;
if (uo != null)
{
this.LogVerbose("Executing Analog Action");
userObject(Convert.ToUInt16(state));
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
uo(Convert.ToUInt16(state));
}
else
this.LogVerbose("User Object is null. Nothing to Execute");
break;
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); break;
}
case "serial":
{
if (Eisc.StringOutput[join].UserObject is Action<string> userObject)
var uo = Eisc.BooleanOutput[join].UserObject as Action<string>;
if (uo != null)
{
this.LogVerbose("Executing Serial Action");
userObject(Convert.ToString(state));
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
uo(Convert.ToString(state));
}
else
this.LogVerbose("User Object is null. Nothing to Execute");
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
break;
}
default:
{
this.LogVerbose("Unknown join type. Use digital/serial/analog");
Debug.LogMessage(LogEventLevel.Verbose, "Unknown join type. Use digital/serial/analog");
break;
}
}
}
catch (Exception e)
{
this.LogError("ExecuteJoinAction error: {message}", e.Message);
this.LogDebug(e, "Stack Trace: ");
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
}
}
/// <summary>
/// Handle incoming sig changes
/// Handles incoming sig changes
/// </summary>
/// <param name="currentDevice">BasicTriList device that triggered the event</param>
/// <param name="args">Event arguments containing the signal information</param>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
protected void Eisc_SigChange(object currentDevice, SigEventArgs args)
{
try
{
this.LogVerbose("EiscApiAdvanced change: {type} {number}={value}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var userObject = args.Sig.UserObject;
Debug.LogMessage(LogEventLevel.Verbose, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject;
if (userObject == null) return;
if (uo == null) return;
if (userObject is Action<bool>)
{
this.LogDebug("Executing Boolean Action");
(userObject as Action<bool>)(args.Sig.BoolValue);
}
else if (userObject is Action<ushort>)
{
this.LogDebug("Executing Analog Action");
(userObject as Action<ushort>)(args.Sig.UShortValue);
}
else if (userObject is Action<string>)
{
this.LogDebug("Executing Serial Action");
(userObject as Action<string>)(args.Sig.StringValue);
}
Debug.LogMessage(LogEventLevel.Debug, this, "Executing Action: {0}", uo.ToString());
if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
catch (Exception e)
{
this.LogError("Eisc_SigChange handler error: {message}", e.Message);
this.LogDebug(e, "Stack Trace: ");
Debug.LogMessage(LogEventLevel.Verbose, this, "Error in Eisc_SigChange handler: {0}", e);
}
}
@@ -431,33 +423,22 @@ namespace PepperDash.Essentials.Core.Bridges
}
/// <summary>
/// Factory class for EiscApiAdvanced devices
/// Represents a EiscApiAdvancedFactory
/// </summary>
/// <remarks>
/// Supported types:
/// eiscapiadv - Create a standard EISC client over TCP/IP
/// eiscapiadvanced - Create a standard EISC client over TCP/IP
/// eiscapiadvancedserver - Create an EISC server
/// eiscapiadvancedclient - Create an EISC client
/// vceiscapiadv - Create a VC-4 EISC client
/// vceiscapiadvanced - Create a VC-4 EISC client
/// eiscapiadvudp - Create a standard EISC client over UDP
/// eiscapiadvancedudp - Create a standard EISC client over UDP
/// </remarks>
public class EiscApiAdvancedFactory : EssentialsDeviceFactory<EiscApiAdvanced>
{
/// <summary>
/// Constructor
/// </summary>
public EiscApiAdvancedFactory()
{
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced", "eiscapiadvudp", "eiscapiadvancedudp" };
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced" };
}
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc)
{
Debug.LogDebug("Attempting to create new EiscApiAdvanced Device");
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EiscApiAdvanced Device");
var controlProperties = CommFactory.GetControlPropertiesConfig(dc);
@@ -465,13 +446,6 @@ namespace PepperDash.Essentials.Core.Bridges
switch (dc.Type.ToLower())
{
case "eiscapiadvudp":
case "eiscapiadvancedudp":
{
eisc = new EthernetIntersystemCommunications(controlProperties.IpIdInt,
controlProperties.TcpSshProperties.Address, Global.ControlSystem);
break;
}
case "eiscapiadv":
case "eiscapiadvanced":
{
@@ -494,7 +468,7 @@ namespace PepperDash.Essentials.Core.Bridges
{
if (string.IsNullOrEmpty(controlProperties.RoomId))
{
Debug.LogInformation("Unable to build VC-4 EISC Client for device {deviceKey}. Room ID is missing or empty", dc.Key);
Debug.LogMessage(LogEventLevel.Information, "Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key);
eisc = null;
break;
}

View File

@@ -12,15 +12,15 @@ using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a CecPortController
/// </summary>
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
/// <summary>
/// Represents a CecPortController
/// </summary>
public class CecPortController : Device, IBasicCommunicationWithStreamDebugging
{
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
@@ -33,16 +33,16 @@ namespace PepperDash.Essentials.Core
ICec Port;
public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc,
EssentialsControlPropertiesConfig config) : base(key)
EssentialsControlPropertiesConfig config):base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
StreamDebugging = new CommunicationStreamDebugging(key);
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
Port.StreamCec.CecChange += StreamCec_CecChange;
});
});
}
public CecPortController(string key, ICec port)
@@ -58,25 +58,27 @@ namespace PepperDash.Essentials.Core
if (args.EventId == CecEventIds.CecMessageReceivedEventId)
OnDataReceived(cecDevice.Received.StringValue);
else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
if (cecDevice.ErrorFeedback.BoolValue)
if(cecDevice.ErrorFeedback.BoolValue)
Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
}
void OnDataReceived(string s)
{
var bytesHandler = BytesReceived;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
this.PrintReceivedBytes(bytes);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
}
var textHandler = TextReceived;
if (textHandler != null)
{
this.PrintReceivedText(s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
}
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
}
}
#region IBasicCommunication Members
@@ -88,7 +90,8 @@ namespace PepperDash.Essentials.Core
{
if (Port == null)
return;
this.PrintSentText(text);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.StreamCec.Send.StringValue = text;
}
@@ -100,8 +103,8 @@ namespace PepperDash.Essentials.Core
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
this.PrintSentBytes(bytes);
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.StreamCec.Send.StringValue = text;
}

View File

@@ -1,78 +1,59 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events;
namespace PepperDash.Essentials.Core
{
/// <summary>
/// Represents a ComPortController
/// </summary>
/// <summary>
/// Represents a ComPortController
/// </summary>
public class ComPortController : Device, IBasicCommunicationWithStreamDebugging
{
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Gets or sets the StreamDebugging
/// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Event fired when bytes are received
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Event fired when text is received
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary>
/// Gets or sets the IsConnected
/// </summary>
/// <summary>
/// Gets or sets the IsConnected
/// </summary>
public bool IsConnected { get { return true; } }
ComPort Port;
ComPort.ComPortSpec Spec;
/// <summary>
/// Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="postActivationFunc"></param>
/// <param name="spec"></param>
/// <param name="config"></param>
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{
StreamDebugging = new CommunicationStreamDebugging(key);
Spec = spec;
Spec = spec;
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
AddPostActivationAction(() =>
{
Port = postActivationFunc(config);
RegisterAndConfigureComPort();
});
}
RegisterAndConfigureComPort();
});
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="key">Device key</param>
/// <param name="port">COM port instance</param>
/// <param name="spec">COM port specification</param>
public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key)
{
if (port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function");
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Invalid com port, continuing but comms will not function");
return;
}
@@ -83,77 +64,71 @@ namespace PepperDash.Essentials.Core
RegisterAndConfigureComPort();
}
private void RegisterAndConfigureComPort()
{
if (Port == null)
{
this.LogInformation($"Configured {Port.Parent.GetType().Name}-comport-{Port.ID} for {Key} does not exist.");
return;
}
private void RegisterAndConfigureComPort()
{
if (Port == null)
{
Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
return;
}
if (Port.Parent is CrestronControlSystem)
{
var result = Port.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Com port: {0}", result);
return; // false
}
}
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Cannot set comspec");
return;
}
Port.SerialDataReceived += Port_SerialDataReceived;
}
if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
{
var result = Port.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{
this.LogError($"Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
return;
}
this.LogInformation($"Successfully registered {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
}
var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0)
{
this.LogError($"Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
return;
}
this.LogInformation($"Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
Port.SerialDataReceived += Port_SerialDataReceived;
}
/// <summary>
/// Destructor
/// </summary>
~ComPortController()
~ComPortController()
{
Port.SerialDataReceived -= Port_SerialDataReceived;
}
void Port_SerialDataReceived(ComPort ReceivingComPort, ComPortSerialDataEventArgs args)
{
OnDataReceived(args.SerialData);
OnDataReceived(args.SerialData);
}
void OnDataReceived(string s)
{
void OnDataReceived(string s)
{
var eventSubscribed = false;
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
this.PrintReceivedBytes(bytes);
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
var bytesHandler = BytesReceived;
if (bytesHandler != null)
{
var bytes = Encoding.GetEncoding(28591).GetBytes(s);
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", ComTextHelper.GetEscapedText(bytes));
bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true;
}
var textHandler = TextReceived;
if (textHandler != null)
{
this.PrintReceivedText(s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
}
var textHandler = TextReceived;
if (textHandler != null)
{
if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true;
}
}
if (!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
}
if(!eventSubscribed) Debug.LogMessage(LogEventLevel.Warning, this, "Received data but no handler is registered");
}
/// <summary>
/// Deactivate method
/// </summary>
/// <inheritdoc />
/// <summary>
/// Deactivate method
/// </summary>
/// <inheritdoc />
public override bool Deactivate()
{
return Port.UnRegister() == eDeviceRegistrationUnRegistrationResponse.Success;
@@ -161,68 +136,70 @@ namespace PepperDash.Essentials.Core
#region IBasicCommunication Members
/// <summary>
/// SendText method
/// </summary>
/// <summary>
/// SendText method
/// </summary>
public void SendText(string text)
{
if (Port == null)
return;
this.PrintSentText(text);
Port.Send(text);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.Send(text);
}
/// <summary>
/// SendBytes method
/// </summary>
/// <summary>
/// SendBytes method
/// </summary>
public void SendBytes(byte[] bytes)
{
if (Port == null)
return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length);
this.PrintSentBytes(bytes);
if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.Send(text);
}
/// <summary>
/// Connect method
/// </summary>
/// <summary>
/// Connect method
/// </summary>
public void Connect()
{
{
}
/// <summary>
/// Disconnect method
/// </summary>
/// <summary>
/// Disconnect method
/// </summary>
public void Disconnect()
{
}
#endregion
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <summary>
/// SimulateReceive method
/// </summary>
public void SimulateReceive(string s)
{
// split out hex chars and build string
var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
StringBuilder b = new StringBuilder();
foreach (var t in split)
{
if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
else
b.Append(t);
}
/// <summary>
///
/// </summary>
/// <param name="s"></param>
/// <summary>
/// SimulateReceive method
/// </summary>
public void SimulateReceive(string s)
{
// split out hex chars and build string
var split = Regex.Split(s, @"(\\[Xx][0-9a-fA-F][0-9a-fA-F])");
StringBuilder b = new StringBuilder();
foreach (var t in split)
{
if (t.StartsWith(@"\") && t.Length == 4)
b.Append((char)(Convert.ToByte(t.Substring(2, 2), 16)));
else
b.Append(t);
}
OnDataReceived(b.ToString());
}
OnDataReceived(b.ToString());
}
}
}

View File

@@ -64,11 +64,8 @@ namespace PepperDash.Essentials.Core
break;
case eControlMethod.Ssh:
{
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password)
{
AutoReconnect = c.AutoReconnect,
DisableEcho = c.DisableSshEcho
};
var ssh = new GenericSshClient(deviceConfig.Key + "-ssh", c.Address, c.Port, c.Username, c.Password);
ssh.AutoReconnect = c.AutoReconnect;
if (ssh.AutoReconnect)
ssh.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = ssh;
@@ -76,10 +73,8 @@ namespace PepperDash.Essentials.Core
}
case eControlMethod.Tcpip:
{
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize)
{
AutoReconnect = c.AutoReconnect
};
var tcp = new GenericTcpIpClient(deviceConfig.Key + "-tcp", c.Address, c.Port, c.BufferSize);
tcp.AutoReconnect = c.AutoReconnect;
if (tcp.AutoReconnect)
tcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = tcp;
@@ -95,10 +90,8 @@ namespace PepperDash.Essentials.Core
break;
case eControlMethod.SecureTcpIp:
{
var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize)
{
AutoReconnect = c.AutoReconnect
};
var secureTcp = new GenericSecureTcpIpClient(deviceConfig.Key + "-secureTcp", c.Address, c.Port, c.BufferSize);
secureTcp.AutoReconnect = c.AutoReconnect;
if (secureTcp.AutoReconnect)
secureTcp.AutoReconnectIntervalMs = c.AutoReconnectIntervalMs;
comm = secureTcp;

View File

@@ -124,35 +124,22 @@ namespace PepperDash.Essentials.Core.Config
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded Local Config");
return true;
}
}
else
{
var parsedConfig = JObject.Parse(fs.ReadToEnd());
// Check if it's a v2 config (check for "version" node)
// this means it's already merged by the Portal API
// from the v2 config tool
var isV2Config = parsedConfig["versions"] != null;
if (isV2Config)
{
Debug.LogMessage(LogEventLevel.Information, "Config file is a v2 format, no merge necessary.");
ConfigObject = parsedConfig.ToObject<EssentialsConfig>();
Debug.LogMessage(LogEventLevel.Information, "Successfully Loaded v2 Config");
return true;
}
var doubleObj = JObject.Parse(fs.ReadToEnd());
ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject<EssentialsConfig>();
// Extract SystemUrl and TemplateUrl into final config output
ConfigObject = PortalConfigReader.MergeConfigs(parsedConfig).ToObject<EssentialsConfig>();
if (parsedConfig["system_url"] != null)
if (doubleObj["system_url"] != null)
{
ConfigObject.SystemUrl = parsedConfig["system_url"].Value<string>();
ConfigObject.SystemUrl = doubleObj["system_url"].Value<string>();
}
if (parsedConfig["template_url"] != null)
if (doubleObj["template_url"] != null)
{
ConfigObject.TemplateUrl = parsedConfig["template_url"].Value<string>();
ConfigObject.TemplateUrl = doubleObj["template_url"].Value<string>();
}
}

View File

@@ -11,169 +11,86 @@ using PepperDash.Core;
namespace PepperDash.Essentials.Core.Config
{
/// <summary>
/// Loads the ConfigObject from the file
/// </summary>
public class EssentialsConfig : BasicConfig
{
/// <summary>
/// Gets or sets the SystemUrl
/// </summary>
[JsonProperty("system_url")]
/// <summary>
/// Loads the ConfigObject from the file
/// </summary>
public class EssentialsConfig : BasicConfig
{
[JsonProperty("system_url")]
public string SystemUrl { get; set; }
/// <summary>
/// Gets or sets the TemplateUrl
/// </summary>
[JsonProperty("template_url")]
[JsonProperty("template_url")]
public string TemplateUrl { get; set; }
/// <summary>
/// Gets the SystemUuid extracted from the SystemUrl
/// </summary>
[JsonProperty("systemUuid")]
public string SystemUuid
public string SystemUuid
{
get
{
string uuid;
if (string.IsNullOrEmpty(SystemUrl))
return "missing url";
if (string.IsNullOrEmpty(SystemUrl))
{
uuid = "missing url";
}
else if (SystemUrl.Contains("#"))
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
uuid = result.Groups[1].Value;
}
else if (SystemUrl.Contains("detail"))
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/detail\/(.*)\/.*");
uuid = result.Groups[1].Value;
}
else
{
if (SystemUrl.Contains("#"))
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
uuid = result.Groups[1].Value;
string uuid = result.Groups[1].Value;
return uuid;
}
return uuid;
}
}
/// <summary>
/// Gets the TemplateUuid extracted from the TemplateUrl
/// </summary>
[JsonProperty("templateUuid")]
public string TemplateUuid
public string TemplateUuid
{
get
{
string uuid;
if (string.IsNullOrEmpty(TemplateUrl))
return "missing template url";
if (string.IsNullOrEmpty(TemplateUrl))
{
uuid = "missing template url";
}
else if (TemplateUrl.Contains("#"))
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
uuid = result.Groups[1].Value;
}
else if (TemplateUrl.Contains("detail"))
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/detail\/(.*)\/system-template-versions\/detail\/(.*)\/.*");
uuid = result.Groups[2].Value;
}
else
{
if (TemplateUrl.Contains("#"))
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
string uuid = result.Groups[1].Value;
return uuid;
} else
{
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
uuid = result.Groups[2].Value;
string uuid = result.Groups[2].Value;
return uuid;
}
return uuid;
}
}
[JsonProperty("rooms")]
/// <summary>
/// Gets or sets the Rooms
/// </summary>
[JsonProperty("rooms")]
public List<DeviceConfig> Rooms { get; set; }
/// <summary>
/// Gets or sets the Versions
/// </summary>
public VersionData Versions { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="EssentialsConfig"/> class.
/// </summary>
public EssentialsConfig()
: base()
{
Rooms = new List<DeviceConfig>();
}
}
/// <summary>
/// Represents version data for Essentials and its packages
/// </summary>
public class VersionData
{
/// <summary>
/// Gets or sets the Essentials version
/// </summary>
[JsonProperty("essentials")]
public NugetVersion Essentials { get; set; }
/// <summary>
/// Gets or sets the list of Packages
/// </summary>
[JsonProperty("packages")]
public List<NugetVersion> Packages { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="VersionData"/> class.
/// </summary>
public VersionData()
{
Packages = new List<NugetVersion>();
}
}
/// <summary>
/// Represents a NugetVersion
/// </summary>
public class NugetVersion
{
/// <summary>
/// Gets or sets the Version
/// </summary>
[JsonProperty("version")]
public string Version { get; set; }
/// <summary>
/// Gets or sets the PackageId
/// </summary>
[JsonProperty("packageId")]
public string PackageId { get; set; }
}
/// <summary>
/// Represents a SystemTemplateConfigs
/// </summary>
public class SystemTemplateConfigs
{
/// <summary>
/// Gets or sets the System
/// </summary>
}
/// <summary>
/// Represents a SystemTemplateConfigs
/// </summary>
public class SystemTemplateConfigs
{
/// <summary>
/// Gets or sets the System
/// </summary>
public EssentialsConfig System { get; set; }
/// <summary>
/// Gets or sets the Template
/// </summary>
public EssentialsConfig Template { get; set; }
}
}
}

View File

@@ -60,9 +60,9 @@ namespace PepperDash.Essentials.Core
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(DeviceJsonApi.DoDeviceActionWithJson, "devjson", "",
ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s).Replace(Environment.NewLine, "\r\n")), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s).Replace(Environment.NewLine, "\r\n")), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s).Replace(Environment.NewLine, "\r\n")), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetProperties(s)), "devprops", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetMethods(s)), "devmethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s => CrestronConsole.ConsoleCommandResponse(DeviceJsonApi.GetApiMethods(s)), "apimethods", "", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(SimulateComReceiveOnDevice, "devsimreceive",
"Simulates incoming data on a com device", ConsoleAccessLevelEnum.AccessOperator);

View File

@@ -77,6 +77,9 @@ namespace PepperDash.Essentials.Core
/// A name that will override the source's name on the UI
/// </summary>
[JsonProperty("name")]
/// <summary>
/// Gets or sets the Name
/// </summary>
public string Name { get; set; }
/// <summary>

View File

@@ -1,4 +1,6 @@
using Crestron.SimplSharp;

using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronXml;
using Crestron.SimplSharp.CrestronXml.Serialization;
@@ -6,7 +8,6 @@ using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.Fusion;
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using Serilog.Events;
@@ -14,49 +15,32 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
namespace PepperDash.Essentials.Core.Fusion
{
/// <summary>
/// Represents a EssentialsHuddleSpaceFusionSystemControllerBase
/// </summary>
public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest, IHasFeedback
public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider
{
private IEssentialsRoomFusionControllerPropertiesConfig _config;
private EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;
private readonly EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;
private const string RemoteOccupancyXml = "<Occupancy><Type>Local</Type><State>{0}</State></Occupancy>";
private bool _guidFileExists;
private readonly bool _guidFileExists;
private readonly Dictionary<Device, BoolInputSig> _sourceToFeedbackSigs =
new Dictionary<Device, BoolInputSig>();
/// <summary>
/// Gets or sets the CurrentRoomSourceNameSig
/// </summary>
protected StringSigData CurrentRoomSourceNameSig;
private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge();
/// <summary>
/// Gets or sets the FusionOccSensor
/// </summary>
protected FusionOccupancySensorAsset FusionOccSensor;
private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor;
/// <summary>
/// Gets or sets the FusionRoom
/// </summary>
protected FusionRoom FusionRoom;
/// <summary>
/// Gets or sets the FusionStaticAssets
/// </summary>
protected Dictionary<int, FusionAsset> FusionStaticAssets;
private readonly long PushNotificationTimeout = 5000;
private IEssentialsRoom Room;
private readonly IEssentialsRoom Room;
private readonly long SchedulePollInterval = 300000;
private Event _currentMeeting;
@@ -64,7 +48,8 @@ namespace PepperDash.Essentials.Core.Fusion
private CTimer _dailyTimeRequestTimer;
private StatusMonitorCollection _errorMessageRollUp;
private FusionRoomGuids _guids;
private FusionRoomGuids _guiDs;
private uint _ipId;
private bool _isRegisteredForSchedulePushNotifications;
private Event _nextMeeting;
@@ -75,30 +60,14 @@ namespace PepperDash.Essentials.Core.Fusion
private string _roomOccupancyRemoteString;
private bool _helpRequestSent;
#region System Info Sigs
private eFusionHelpResponse _helpRequestStatus;
//StringSigData SystemName;
//StringSigData Model;
//StringSigData SerialNumber;
//StringSigData Uptime;
/// <inheritdoc />
public StringFeedback HelpRequestResponseFeedback { get; private set; }
/// <inheritdoc />
public BoolFeedback HelpRequestSentFeedback { get; private set; }
/// <inheritdoc />
public StringFeedback HelpRequestStatusFeedback { get; private set; }
private Timer _helpRequestTimeoutTimer;
/// <summary>
/// Gets the DefaultHelpRequestTimeoutMs
/// </summary>
public int HelpRequestTimeoutMs => _config.HelpRequestTimeoutMs;
/// <summary>
/// Gets whether to use a timer for help requests
/// </summary>
public bool UseHelpRequestTimer => _config.UseTimeoutForHelpRequests;
#endregion
#region Processor Info Sigs
@@ -124,110 +93,71 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion
/// <summary>
/// Constructor
/// </summary>
public IEssentialsRoomFusionController(string key, string name, IEssentialsRoomFusionControllerPropertiesConfig config)
: base(key, name)
{
_config = config;
AddPostActivationAction(() =>
{
var room = DeviceManager.GetDeviceForKey<IEssentialsRoom>(_config.RoomKey);
if (room == null)
{
this.LogError("Error Creating Fusion Room Controller. No room found with key '{0}'", _config.RoomKey);
return;
}
this.LogInformation("Creating Fusion Room Controller for room '{0}' at IPID: {1:X2}", room.Key, _config.IpIdInt);
ConstructorHelper(room, _config.IpIdInt, _config.JoinMapKey);
});
}
/// <summary>
///
/// </summary>
/// <param name="room"></param>
/// <param name="ipId"></param>
/// <param name="joinMapKey"></param>
public IEssentialsRoomFusionController(IEssentialsRoom room, string ipId, string joinMapKey)
public EssentialsHuddleSpaceFusionSystemControllerBase(IEssentialsRoom room, uint ipId, string joinMapKey)
: base(room.Key + "-fusion")
{
_config = new IEssentialsRoomFusionControllerPropertiesConfig()
{
IpId = ipId,
RoomKey = room.Key,
JoinMapKey = joinMapKey
};
ConstructorHelper(room, _config.IpIdInt, joinMapKey);
}
private void ConstructorHelper(IEssentialsRoom room, uint ipId, string joinMapKey)
{
try
{
this.LogDebug("ConstructorHelper called for Fusion Room Controller for room '{0}' with IPID {1:X2}", room.Key, ipId);
this.LogDebug("JoinMap Key: {0}", joinMapKey);
JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1);
this.LogDebug("JoinMap created");
CrestronConsole.AddNewConsoleCommand((o) =>
{
if (o is string deviceKey)
{
if (string.IsNullOrEmpty(deviceKey) || deviceKey == "?")
{
CrestronConsole.ConsoleCommandResponse("Please provide a device key for a Fusion Room instance");
return;
}
else if (deviceKey != this.Key)
{
return;
}
}
else
{
CrestronConsole.ConsoleCommandResponse("Invalid parameter. Please provide a device key for a Fusion Room instance");
return;
}
JoinMap.PrintJoinMapInfo();
}, "printfusionjoinmap", "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand((o) => JoinMap.PrintJoinMapInfo(), string.Format("ptjnmp-{0}", Key), "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator);
if (!string.IsNullOrEmpty(joinMapKey))
{
// this.LogDebug("Attempting to get custom join map for key: {0}", joinMapKey);
var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);
if (customJoins != null)
{
JoinMap.SetCustomJoinData(customJoins);
}
}
Room = room;
this.LogDebug("Room found: {0}", Room.Key);
_ipId = ipId;
FusionStaticAssets = new Dictionary<int, FusionAsset>();
this.LogDebug("FusionStaticAssets dictionary created");
_guiDs = new FusionRoomGuids();
_guids = new FusionRoomGuids();
var mac =
CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId);
var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
if (File.Exists(oldGuidFilePath))
{
Debug.LogMessage(LogEventLevel.Information, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
File.Copy(oldGuidFilePath, guidFilePath);
File.Delete(oldGuidFilePath);
}
_guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
{
// Does not exist. Create GUIDs
_guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac),
FusionStaticAssets);
}
else
{
// Exists. Read GUIDs
ReadGuidFile(guidFilePath);
}
this.LogDebug("FusionRoomGuids created");
if (Room is IRoomOccupancy occupancyRoom)
{
Debug.LogDebug(this, "Room '{0}' supports IRoomOccupancy", Room.Key);
if (occupancyRoom.RoomOccupancy != null)
{
if (occupancyRoom.OccupancyStatusProviderIsRemote)
@@ -241,21 +171,8 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
this.LogDebug("Occupancy setup complete");
HelpRequestResponseFeedback = new StringFeedback("HelpRequestResponse", () => FusionRoom.Help.OutputSig.StringValue);
HelpRequestSentFeedback = new BoolFeedback("HelpRequestSent", () => _helpRequestSent);
HelpRequestStatusFeedback = new StringFeedback("HelpRequestStatus", () => _helpRequestStatus.ToString());
Feedbacks.Add(HelpRequestResponseFeedback);
Feedbacks.Add(HelpRequestSentFeedback);
Feedbacks.Add(HelpRequestStatusFeedback);
if (RoomOccupancyRemoteStringFeedback != null)
Feedbacks.Add(RoomOccupancyRemoteStringFeedback);
if (RoomIsOccupiedFeedback != null)
Feedbacks.Add(RoomIsOccupiedFeedback);
AddPostActivationAction(() => PostActivate(guidFilePath));
}
catch (Exception e)
{
@@ -263,54 +180,9 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
private string GetGuidFilePath(uint ipId)
private void PostActivate(string guidFilePath)
{
var mac =
CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _config.IpIdInt);
var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
if (File.Exists(oldGuidFilePath))
{
Debug.LogMessage(LogEventLevel.Information, this, "Migrating from old Fusion GUID file to new Fusion GUID File");
File.Copy(oldGuidFilePath, guidFilePath);
File.Delete(oldGuidFilePath);
}
_guidFileExists = File.Exists(guidFilePath);
// Check if file exists
if (!_guidFileExists)
{
// Does not exist. Create GUIDs
_guids = new FusionRoomGuids(Room.Name, ipId, _guids.GenerateNewRoomGuid(slot, mac),
FusionStaticAssets);
}
else
{
// Exists. Read GUIDs
ReadGuidFile(guidFilePath);
}
return guidFilePath;
}
/// <inheritdoc />
public override void Initialize()
{
GenerateGuidFile(GetGuidFilePath(_config.IpIdInt));
CreateSymbolAndBasicSigs(_config.IpIdInt);
CreateSymbolAndBasicSigs(_ipId);
SetUpSources();
SetUpCommunitcationMonitors();
SetUpDisplay();
@@ -319,14 +191,12 @@ namespace PepperDash.Essentials.Core.Fusion
FusionRVI.GenerateFileForAllFusionDevices();
GenerateGuidFile(guidFilePath);
}
/// <summary>
/// Gets the RoomGuid
/// </summary>
protected string RoomGuid
{
get { return _guids.RoomGuid; }
get { return _guiDs.RoomGuid; }
}
/// <summary>
@@ -334,9 +204,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; }
/// <summary>
/// Gets the RoomIsOccupiedFeedbackFunc
/// </summary>
protected Func<bool> RoomIsOccupiedFeedbackFunc
{
get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; }
@@ -351,21 +218,10 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion
/// <inheritdoc />
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
/// <summary>
/// ScheduleChange event
/// </summary>
public event EventHandler<ScheduleChangeEventArgs> ScheduleChange;
//public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning;
//public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning;
/// <summary>
/// RoomInfoChange event
/// </summary>
public event EventHandler<EventArgs> RoomInfoChange;
//ScheduleResponseEvent NextMeeting;
@@ -402,11 +258,11 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file");
_guids = FusionOccSensor == null
? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets)
: new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor);
_guiDs = FusionOccSensor == null
? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets)
: new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets, FusionOccSensor);
var json = JsonConvert.SerializeObject(_guids, Newtonsoft.Json.Formatting.Indented);
var json = JsonConvert.SerializeObject(_guiDs, Newtonsoft.Json.Formatting.Indented);
using (var sw = new StreamWriter(filePath))
{
@@ -456,17 +312,17 @@ namespace PepperDash.Essentials.Core.Fusion
{
var json = File.ReadToEnd(filePath, Encoding.ASCII);
_guids = JsonConvert.DeserializeObject<FusionRoomGuids>(json);
_guiDs = JsonConvert.DeserializeObject<FusionRoomGuids>(json);
// _config.IpId = _guids.IpId;
_ipId = _guiDs.IpId;
FusionStaticAssets = _guids.StaticAssets;
FusionStaticAssets = _guiDs.StaticAssets;
}
Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}",
filePath);
Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _config.IpIdInt, RoomGuid);
Debug.LogMessage(LogEventLevel.Debug, this, "\r\n********************\r\n\tRoom Name: {0}\r\n\tIPID: {1:X}\r\n\tRoomGuid: {2}\r\n*******************", Room.Name, _ipId, RoomGuid);
foreach (var item in FusionStaticAssets)
{
@@ -487,10 +343,6 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// CreateSymbolAndBasicSigs method
/// </summary>
/// <param name="ipId"></param>
protected virtual void CreateSymbolAndBasicSigs(uint ipId)
{
Debug.LogMessage(LogEventLevel.Information, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
@@ -553,10 +405,6 @@ namespace PepperDash.Essentials.Core.Fusion
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
}
/// <summary>
/// CrestronEnvironment_EthernetEventHandler method
/// </summary>
/// <param name="ethernetEventArgs"></param>
protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)
@@ -565,9 +413,6 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// GetSystemInfo method
/// </summary>
protected void GetSystemInfo()
{
//SystemName.InputSig.StringValue = Room.Name;
@@ -581,9 +426,6 @@ namespace PepperDash.Essentials.Core.Fusion
() => CrestronConsole.SendControlSystemCommand("reboot", ref response));
}
/// <summary>
/// SetUpEthernetValues method
/// </summary>
protected void SetUpEthernetValues()
{
_ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly);
@@ -599,9 +441,6 @@ namespace PepperDash.Essentials.Core.Fusion
_netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly);
}
/// <summary>
/// GetProcessorEthernetValues method
/// </summary>
protected void GetProcessorEthernetValues()
{
_ip1.InputSig.StringValue =
@@ -636,7 +475,7 @@ namespace PepperDash.Essentials.Core.Fusion
// Interface 1
if (InitialParametersClass.NumberOfEthernetInterfaces > 1)
// Only get these values if the processor has more than 1 NIC
// Only get these values if the processor has more than 1 NIC
{
_ip2.InputSig.StringValue =
CrestronEthernetHelper.GetEthernetParameter(
@@ -650,9 +489,6 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// GetProcessorInfo method
/// </summary>
protected void GetProcessorInfo()
{
_firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly);
@@ -663,7 +499,7 @@ namespace PepperDash.Essentials.Core.Fusion
{
var join = JoinMap.ProgramNameStart.JoinNumber + i;
var progNum = i + 1;
_program[i] = FusionRoom.CreateOffsetStringSig((uint)join,
_program[i] = FusionRoom.CreateOffsetStringSig((uint) join,
string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly);
}
}
@@ -671,9 +507,6 @@ namespace PepperDash.Essentials.Core.Fusion
_firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
}
/// <summary>
/// GetCustomProperties method
/// </summary>
protected void GetCustomProperties()
{
if (FusionRoom.IsOnline)
@@ -691,16 +524,11 @@ namespace PepperDash.Essentials.Core.Fusion
// TODO: Get IP and Project Name from TP
}
/// <summary>
/// FusionRoom_OnlineStatusChange method
/// </summary>
/// <param name="currentDevice"></param>
/// <param name="args"></param>
protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
if (args.DeviceOnLine)
{
CrestronInvoke.BeginInvoke((o) =>
CrestronInvoke.BeginInvoke( (o) =>
{
CrestronEnvironment.Sleep(200);
@@ -848,7 +676,7 @@ namespace PepperDash.Essentials.Core.Fusion
var extendTime = _currentMeeting.dtEnd - DateTime.Now;
var extendMinutesRaw = extendTime.TotalMinutes;
extendMinutes += (int)Math.Round(extendMinutesRaw);
extendMinutes += (int) Math.Round(extendMinutesRaw);
}
@@ -956,11 +784,11 @@ namespace PepperDash.Essentials.Core.Fusion
var parameters = actionResponse["Parameters"];
foreach (var isRegistered in from XmlElement parameter in parameters
where parameter.HasAttributes
select parameter.Attributes
where parameter.HasAttributes
select parameter.Attributes
into attributes
where attributes["ID"].Value == "Registered"
select Int32.Parse(attributes["Value"].Value))
where attributes["ID"].Value == "Registered"
select Int32.Parse(attributes["Value"].Value))
{
switch (isRegistered)
{
@@ -1017,9 +845,9 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime);
// Parse time and date from response and insert values
CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute,
(ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day,
(ushort)currentTime.Year);
CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute,
(ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day,
(ushort) currentTime.Year);
Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime());
}
@@ -1237,9 +1065,6 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// SetUpSources method
/// </summary>
protected virtual void SetUpSources()
{
// Sources
@@ -1249,10 +1074,10 @@ namespace PepperDash.Essentials.Core.Fusion
// NEW PROCESS:
// Make these lists and insert the fusion attributes by iterating these
var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
uint i = 0;
uint i = 1;
foreach (var kvp in setTopBoxes)
{
TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
TryAddRouteActionSigs(JoinMap.Display1SetTopBoxSourceStart.AttributeName + " " + i, JoinMap.Display1SetTopBoxSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++;
if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots
{
@@ -1261,10 +1086,10 @@ namespace PepperDash.Essentials.Core.Fusion
}
var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
i = 0;
i = 1;
foreach (var kvp in discPlayers)
{
TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
TryAddRouteActionSigs(JoinMap.Display1DiscPlayerSourceStart.AttributeName + " " + i, JoinMap.Display1DiscPlayerSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++;
if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots
{
@@ -1273,10 +1098,10 @@ namespace PepperDash.Essentials.Core.Fusion
}
var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource);
i = 0;
i = 1;
foreach (var kvp in laptops)
{
TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + (i + 1), JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
TryAddRouteActionSigs(JoinMap.Display1LaptopSourceStart.AttributeName + " " + i, JoinMap.Display1LaptopSourceStart.JoinNumber + i, kvp.Key, kvp.Value.SourceDevice);
i++;
if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots???
{
@@ -1286,7 +1111,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType<IUsageTracking>())
{
usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) { UsageIsTracked = true };
usageDevice.UsageTracker = new UsageTracking(usageDevice as Device) {UsageIsTracked = true};
usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
}
}
@@ -1332,31 +1157,17 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Device usage string: {0}", deviceUsage);
}
/// <summary>
/// Tries to add route action sigs for a source
/// </summary>
/// <param name="attrName"></param>
/// <param name="attrNum"></param>
/// <param name="routeKey"></param>
/// <param name="pSrc"></param>
protected void TryAddRouteActionSigs(string attrName, uint attrNum, string routeKey, Device pSrc)
{
this.LogVerbose("Creating attribute '{0}' with join {1} for source {2}",
Debug.LogMessage(LogEventLevel.Verbose, this, "Creating attribute '{0}' with join {1} for source {2}",
attrName, attrNum, pSrc.Key);
try
{
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
// Need feedback when this source is selected
// Event handler, added below, will compare source changes with this sig dict
if (!_sourceToFeedbackSigs.ContainsKey(pSrc))
{
_sourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
}
else
{
this.LogWarning("Source '{0}' already has a feedback sig mapped. Overwriting.", pSrc.Key);
_sourceToFeedbackSigs[pSrc] = sigD.InputSig;
}
_sourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
// And respond to selection in Fusion
sigD.OutputSig.SetSigFalseAction(() =>
@@ -1369,12 +1180,14 @@ namespace PepperDash.Essentials.Core.Fusion
}
catch (Exception)
{
this.LogVerbose("Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING",
Debug.LogMessage(LogEventLevel.Verbose, this, "Error creating Fusion signal {0} {1} for device '{2}'. THIS NEEDS REWORKING",
attrNum, attrName, pSrc.Key);
}
}
/// <summary>
///
/// </summary>
private void SetUpCommunitcationMonitors()
{
uint displayNum = 0;
@@ -1461,8 +1274,6 @@ namespace PepperDash.Essentials.Core.Fusion
if (attrName != null)
{
this.LogDebug("Linking communication monitor for device '{0}' to Fusion attribute '{1}' at join {2}",
dev.Key, attrName, attrNum);
// Link comm status to sig and update
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
var smd = dev as ICommunicationMonitor;
@@ -1474,9 +1285,6 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// SetUpDisplay method
/// </summary>
protected virtual void SetUpDisplay()
{
try
@@ -1489,7 +1297,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var display in displays.Cast<IDisplay>())
{
display.UsageTracker = new UsageTracking(display as Device) { UsageIsTracked = true };
display.UsageTracker = new UsageTracking(display as Device) {UsageIsTracked = true};
display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
}
@@ -1602,7 +1410,7 @@ namespace PepperDash.Essentials.Core.Fusion
// Power on
var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On",
var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On",
eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{
@@ -1613,7 +1421,7 @@ namespace PepperDash.Essentials.Core.Fusion
});
// Power Off
var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off",
var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off",
eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{
@@ -1631,7 +1439,7 @@ namespace PepperDash.Essentials.Core.Fusion
}
// Current Source
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8,
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8,
displayName + "Source None", eSigIoMask.InputOutputSig);
defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b =>
{
@@ -1699,7 +1507,7 @@ namespace PepperDash.Essentials.Core.Fusion
//if (Room.OccupancyObj != null)
//{
var tempOccAsset = _guids.OccupancyAsset;
var tempOccAsset = _guiDs.OccupancyAsset;
if (tempOccAsset == null)
{
@@ -1724,7 +1532,7 @@ namespace PepperDash.Essentials.Core.Fusion
occRoom.RoomOccupancy.RoomIsOccupiedFeedback.OutputChange += RoomIsOccupiedFeedback_OutputChange;
}
RoomOccupancyRemoteStringFeedback = new StringFeedback(() => _roomOccupancyRemoteString);
RoomOccupancyRemoteStringFeedback.LinkInputSig(occSensorAsset.RoomOccupancyInfo.InputSig);
//}
@@ -1780,82 +1588,12 @@ namespace PepperDash.Essentials.Core.Fusion
}
}
/// <summary>
/// Event handler for Fusion state changes
/// </summary>
/// <param name="device"></param>
/// <param name="args"></param>
protected void FusionRoom_FusionStateChange(FusionBase device, FusionStateEventArgs args)
{
if (args.EventId == FusionEventIds.HelpMessageReceivedEventId)
{
this.LogInformation("Help message received from Fusion for room '{0}'",
Room.Name);
this.LogDebug("Help message content: {0}", FusionRoom.Help.OutputSig.StringValue);
// Fire help request event
HelpRequestResponseFeedback.FireUpdate();
if (!string.IsNullOrEmpty(FusionRoom.Help.OutputSig.StringValue))
{
switch (FusionRoom.Help.OutputSig.StringValue)
{
case "Please wait, a technician is on his / her way.":
// this.LogInformation("Please wait, a technician is on his / her way.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.HelpOnTheWay;
break;
case "Please call the helpdesk.":
// this.LogInformation("Please call the helpdesk.");
_helpRequestStatus = eFusionHelpResponse.CallHelpDesk;
break;
case "Please wait, I will reschedule your meeting to a different room.":
// this.LogInformation("Please wait, I will reschedule your meeting to a different room.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.ReschedulingMeeting;
break;
case "I will be taking control of your system. Please be patient while I adjust the settings.":
// this.LogInformation("I will be taking control of your system. Please be patient while I adjust the settings.",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.TakingControl;
break;
default:
// this.LogInformation("Unknown help request code received from Fusion for room '{0}'",
// Room.Name);
_helpRequestStatus = eFusionHelpResponse.None;
break;
}
}
else
{
_helpRequestStatus = eFusionHelpResponse.None;
}
if (_helpRequestStatus == eFusionHelpResponse.None)
{
_helpRequestSent = false;
HelpRequestSentFeedback.FireUpdate();
}
HelpRequestStatusFeedback.FireUpdate();
if (_helpRequestTimeoutTimer != null)
{
_helpRequestTimeoutTimer.Stop();
_helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;
_helpRequestTimeoutTimer.Dispose();
_helpRequestTimeoutTimer = null;
}
}
// The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
// even though they all contain sigs.
BoolOutputSig outSig;
if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData)
{
@@ -1894,102 +1632,9 @@ namespace PepperDash.Essentials.Core.Fusion
(outSig.UserObject as Action<string>).Invoke(outSig.StringValue);
}
}
/// <inheritdoc />
public void SendHelpRequest()
{
var now = DateTime.Now;
var breakString = _config.UseHtmlFormatForHelpRequests ? "<BR>" : "\r\n";
var date = now.ToString("MMMM dd, yyyy");
var time = now.ToString("hh:mm tt");
if (_config.Use24HourTimeFormat)
{
time = now.ToString("HH:mm");
}
var requestString = $"HR00: {breakString} Assistance has been requested from room {Room.Name}{breakString}on {date} at {time}";
FusionRoom.Help.InputSig.StringValue = requestString;
this.LogInformation("Help request sent to Fusion from room '{0}'", Room.Name);
this.LogDebug("Help request content: {0}", FusionRoom.Help.InputSig.StringValue);
_helpRequestSent = true;
HelpRequestSentFeedback.FireUpdate();
if (UseHelpRequestTimer)
{
if (_helpRequestTimeoutTimer == null)
{
_helpRequestTimeoutTimer = new Timer(HelpRequestTimeoutMs);
_helpRequestTimeoutTimer.AutoReset = false;
_helpRequestTimeoutTimer.Enabled = true;
_helpRequestTimeoutTimer.Elapsed += OnTimedEvent;
}
_helpRequestTimeoutTimer.Interval = HelpRequestTimeoutMs;
_helpRequestTimeoutTimer.Start();
this.LogDebug("Help request timeout timer started for room '{0}' with timeout of {1} ms.",
Room.Name, HelpRequestTimeoutMs);
}
_helpRequestStatus = eFusionHelpResponse.HelpRequested;
HelpRequestStatusFeedback.FireUpdate();
}
private void OnTimedEvent(object source, ElapsedEventArgs e)
{
this.LogInformation("Help request timeout reached for room '{0}'. Cancelling help request.", Room.Name);
CancelHelpRequest();
}
/// <inheritdoc />
public void CancelHelpRequest()
{
if (_helpRequestSent)
{
FusionRoom.Help.InputSig.StringValue = "";
_helpRequestSent = false;
HelpRequestSentFeedback.FireUpdate();
_helpRequestStatus = eFusionHelpResponse.None;
HelpRequestStatusFeedback.FireUpdate();
Debug.LogMessage(LogEventLevel.Information, this, "Help request cancelled for room '{0}'", Room.Name);
}
if (_helpRequestTimeoutTimer != null)
{
_helpRequestTimeoutTimer.Stop();
_helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;
_helpRequestTimeoutTimer.Dispose();
_helpRequestTimeoutTimer = null;
this.LogDebug("Help request timeout timer stopped for room '{0}'.", Room.Name);
}
}
/// <inheritdoc />
public void ToggleHelpRequest()
{
if (_helpRequestSent)
{
CancelHelpRequest();
}
else
{
SendHelpRequest();
}
}
}
/// <summary>
/// Extensions to enhance Fusion room, asset and signal creation.
/// </summary>
public static class FusionRoomExtensions
{
/// <summary>
@@ -2003,8 +1648,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{
Debug.LogDebug("Creating Offset Bool Sig: {0} at Join {1}", name, number);
if (number < 50)
{
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -2025,8 +1668,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{
Debug.LogDebug("Creating Offset UShort Sig: {0} at Join {1}", name, number);
if (number < 50)
{
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -2047,8 +1688,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask)
{
Debug.LogDebug("Creating Offset String Sig: {0} at Join {1}", name, number);
if (number < 50)
{
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -2164,9 +1803,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public class RoomInformation
{
/// <summary>
/// Constructor
/// </summary>
public RoomInformation()
{
FusionCustomProperties = new List<FusionCustomProperty>();
@@ -2219,17 +1855,10 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary>
public class FusionCustomProperty
{
/// <summary>
/// Constructor
/// </summary>
public FusionCustomProperty()
{
}
/// <summary>
/// Constructor with id
/// </summary>
/// <param name="id"></param>
public FusionCustomProperty(string id)
{
ID = id;

View File

@@ -16,201 +16,121 @@ namespace PepperDash.Essentials.Core.Fusion
{
// Processor Attributes
/// <summary>
/// Processor IP 1
/// </summary>
[JoinName("ProcessorIp1")]
public JoinDataComplete ProcessorIp1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Info - Processor - IP 1" },
new JoinMetadata { Description = "Info - Processor - IP 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor IP 2
/// </summary>
[JoinName("ProcessorIp2")]
public JoinDataComplete ProcessorIp2 = new JoinDataComplete(new JoinData { JoinNumber = 51, JoinSpan = 1, AttributeName = "Info - Processor - IP 2" },
new JoinMetadata { Description = "Info - Processor - IP 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Gateway
/// </summary>
[JoinName("ProcessorGateway")]
public JoinDataComplete ProcessorGateway = new JoinDataComplete(new JoinData { JoinNumber = 52, JoinSpan = 1, AttributeName = "Info - Processor - Gateway" },
new JoinMetadata { Description = "Info - Processor - Gateway", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Hostname
/// </summary>
[JoinName("ProcessorHostname")]
public JoinDataComplete ProcessorHostname = new JoinDataComplete(new JoinData { JoinNumber = 53, JoinSpan = 1, AttributeName = "Info - Processor - Hostname" },
new JoinMetadata { Description = "Info - Processor - Hostname", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Domain
/// </summary>
[JoinName("ProcessorDomain")]
public JoinDataComplete ProcessorDomain = new JoinDataComplete(new JoinData { JoinNumber = 54, JoinSpan = 1, AttributeName = "Info - Processor - Domain" },
new JoinMetadata { Description = "Info - Processor - Domain", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor DNS 1
/// </summary>
[JoinName("ProcessorDns1")]
public JoinDataComplete ProcessorDns1 = new JoinDataComplete(new JoinData { JoinNumber = 55, JoinSpan = 1, AttributeName = "Info - Processor - DNS 1" },
new JoinMetadata { Description = "Info - Processor - DNS 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor DNS 2
/// </summary>
[JoinName("ProcessorDns2")]
public JoinDataComplete ProcessorDns2 = new JoinDataComplete(new JoinData { JoinNumber = 56, JoinSpan = 1, AttributeName = "Info - Processor - DNS 2" },
new JoinMetadata { Description = "Info - Processor - DNS 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor MAC 1
/// </summary>
[JoinName("ProcessorMac1")]
public JoinDataComplete ProcessorMac1 = new JoinDataComplete(new JoinData { JoinNumber = 57, JoinSpan = 1, AttributeName = "Info - Processor - MAC 1" },
new JoinMetadata { Description = "Info - Processor - MAC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor MAC 2
/// </summary>
[JoinName("ProcessorMac2")]
public JoinDataComplete ProcessorMac2 = new JoinDataComplete(new JoinData { JoinNumber = 58, JoinSpan = 1, AttributeName = "Info - Processor - MAC 2" },
new JoinMetadata { Description = "Info - Processor - MAC 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Net Mask 1
/// </summary>
[JoinName("ProcessorNetMask1")]
public JoinDataComplete ProcessorNetMask1 = new JoinDataComplete(new JoinData { JoinNumber = 59, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 1" },
new JoinMetadata { Description = "Info - Processor - Net Mask 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Net Mask 2
/// </summary>
[JoinName("ProcessorNetMask2")]
public JoinDataComplete ProcessorNetMask2 = new JoinDataComplete(new JoinData { JoinNumber = 60, JoinSpan = 1, AttributeName = "Info - Processor - Net Mask 2" },
new JoinMetadata { Description = "Info - Processor - Net Mask 2", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Firmware
/// </summary>
[JoinName("ProcessorFirmware")]
public JoinDataComplete ProcessorFirmware = new JoinDataComplete(new JoinData { JoinNumber = 61, JoinSpan = 1, AttributeName = "Info - Processor - Firmware" },
new JoinMetadata { Description = "Info - Processor - Firmware", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Program Name Start
/// </summary>
[JoinName("ProgramNameStart")]
public JoinDataComplete ProgramNameStart = new JoinDataComplete(new JoinData { JoinNumber = 62, JoinSpan = 10, AttributeName = "Info - Processor - Program" },
new JoinMetadata { Description = "Info - Processor - Program", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// Processor Reboot
/// </summary>
[JoinName("ProcessorReboot")]
public JoinDataComplete ProcessorReboot = new JoinDataComplete(new JoinData { JoinNumber = 74, JoinSpan = 1, AttributeName = "Processor - Reboot" },
new JoinMetadata { Description = "Processor - Reboot", JoinCapabilities = eJoinCapabilities.FromFusion, JoinType = eJoinType.Digital });
// Volume Controls
/// <summary>
/// Volume Fader 1
/// </summary>
[JoinName("VolumeFader1")]
public JoinDataComplete VolumeFader1 = new JoinDataComplete(new JoinData { JoinNumber = 50, JoinSpan = 1, AttributeName = "Volume - Fader01" },
new JoinMetadata { Description = "Volume - Fader01", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Analog });
// Codec Info
/// <summary>
/// VC Codec In Call
/// </summary>
[JoinName("VcCodecInCall")]
public JoinDataComplete VcCodecInCall = new JoinDataComplete(new JoinData { JoinNumber = 69, JoinSpan = 1, AttributeName = "Conf - VC 1 In Call" },
new JoinMetadata { Description = "Conf - VC 1 In Call", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// VC Codec Online
/// </summary>
[JoinName("VcCodecOnline")]
public JoinDataComplete VcCodecOnline = new JoinDataComplete(new JoinData { JoinNumber = 122, JoinSpan = 1, AttributeName = "Online - VC 1" },
new JoinMetadata { Description = "Online - VC 1", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// VC Codec IP Address
/// </summary>
[JoinName("VcCodecIpAddress")]
public JoinDataComplete VcCodecIpAddress = new JoinDataComplete(new JoinData { JoinNumber = 121, JoinSpan = 1, AttributeName = "IP Address - VC" },
new JoinMetadata { Description = "IP Address - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
/// <summary>
/// VC Codec IP Port
/// </summary>
[JoinName("VcCodecIpPort")]
public JoinDataComplete VcCodecIpPort = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 1, AttributeName = "IP Port - VC" },
new JoinMetadata { Description = "IP Port - VC", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
// Source Attributes
/// <summary>
/// Display 1 Current Source Name
/// </summary>
[JoinName("Display1CurrentSourceName")]
public JoinDataComplete Display1CurrentSourceName = new JoinDataComplete(new JoinData { JoinNumber = 84, JoinSpan = 1, AttributeName = "Display 1 - Current Source" },
new JoinMetadata { Description = "Display 1 - Current Source", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Serial });
// Device Online Status
/// <summary>
/// Touchpanel Online Start
/// </summary>
[JoinName("TouchpanelOnlineStart")]
public JoinDataComplete TouchpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 150, JoinSpan = 10, AttributeName = "Online - Touch Panel" },
new JoinMetadata { Description = "Online - Touch Panel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Xpanel Online Start
/// </summary>
[JoinName("XpanelOnlineStart")]
public JoinDataComplete XpanelOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 160, JoinSpan = 5, AttributeName = "Online - XPanel" },
new JoinMetadata { Description = "Online - XPanel", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display Online Start
/// </summary>
[JoinName("DisplayOnlineStart")]
public JoinDataComplete DisplayOnlineStart = new JoinDataComplete(new JoinData { JoinNumber = 170, JoinSpan = 10, AttributeName = "Online - Display" },
new JoinMetadata { Description = "Online - Display", JoinCapabilities = eJoinCapabilities.ToFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Laptop Source Start
/// </summary>
[JoinName("Display1LaptopSourceStart")]
public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 165, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" },
public JoinDataComplete Display1LaptopSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 166, JoinSpan = 5, AttributeName = "Display 1 - Source Laptop" },
new JoinMetadata { Description = "Display 1 - Source Laptop", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Disc Player Source Start
/// </summary>
[JoinName("Display1DiscPlayerSourceStart")]
public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 180, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" },
public JoinDataComplete Display1DiscPlayerSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 181, JoinSpan = 5, AttributeName = "Display 1 - Source Disc Player" },
new JoinMetadata { Description = "Display 1 - Source Disc Player", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Display 1 Set Top Box Source Start
/// </summary>
[JoinName("Display1SetTopBoxSourceStart")]
public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 185, JoinSpan = 5, AttributeName = "Display 1 - Source TV" },
public JoinDataComplete Display1SetTopBoxSourceStart = new JoinDataComplete(new JoinData { JoinNumber = 188, JoinSpan = 5, AttributeName = "Display 1 - Source TV" },
new JoinMetadata { Description = "Display 1 - Source TV", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
// Display 1
/// <summary>
/// Display 1 Start
/// </summary>
[JoinName("Display1Start")]
public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 190, JoinSpan = 1 },
public JoinDataComplete Display1Start = new JoinDataComplete(new JoinData { JoinNumber = 158, JoinSpan = 1 },
new JoinMetadata { Description = "Display 1 Start", JoinCapabilities = eJoinCapabilities.ToFromFusion, JoinType = eJoinType.Digital });
/// <summary>
/// Constructor to use when instantiating this Join Map without inheriting from it
/// </summary>

View File

@@ -1,33 +0,0 @@
using System.Collections.Generic;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Fusion;
/// <summary>
/// Factory for creating IEssentialsRoomFusionController devices
/// </summary>
public class IEssentialsRoomFusionControllerFactory : EssentialsDeviceFactory<IEssentialsRoomFusionController>
{
/// <summary>
/// Constructor
/// </summary>
public IEssentialsRoomFusionControllerFactory()
{
TypeNames = new List<string>() { "fusionRoom" };
}
/// <summary>
/// Builds the device
/// </summary>
/// <param name="dc"></param>
/// <returns></returns>
public override EssentialsDevice BuildDevice(PepperDash.Essentials.Core.Config.DeviceConfig dc)
{
Debug.LogDebug("Factory Attempting to create new IEssentialsRoomFusionController Device");
var properties = dc.Properties.ToObject<IEssentialsRoomFusionControllerPropertiesConfig>();
return new IEssentialsRoomFusionController(dc.Key, dc.Name, properties);
}
}

View File

@@ -1,71 +0,0 @@
using Newtonsoft.Json;
using PepperDash.Core;
/// <summary>
/// Config properties for an IEssentialsRoomFusionController device
/// </summary>
public class IEssentialsRoomFusionControllerPropertiesConfig
{
/// <summary>
/// Gets or sets the IP ID of the Fusion Room Controller
/// </summary>
[JsonProperty("ipId")]
public string IpId { get; set; }
/// <summary>
/// Gets the IP ID as a UInt16
/// </summary>
[JsonIgnore]
public uint IpIdInt
{
get
{
// Try to parse the IpId string to UInt16 as hex
if (ushort.TryParse(IpId, System.Globalization.NumberStyles.HexNumber, null, out ushort result))
{
return result;
}
else
{
Debug.LogWarning( "Failed to parse IpId '{0}' as UInt16", IpId);
return 0;
}
}
}
/// <summary>
/// Gets or sets the join map key
/// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; }
/// <summary>
/// Gets or sets the room key associated with this Fusion Room Controller
/// </summary>
[JsonProperty("roomKey")]
public string RoomKey { get; set; }
/// <summary>
/// Gets or sets whether to use HTML format for help requests
/// </summary>
[JsonProperty("useHtmlFormatForHelpRequests")]
public bool UseHtmlFormatForHelpRequests { get; set; } = false;
/// <summary>
/// Gets or sets whether to use 24-hour time format
/// </summary>
[JsonProperty("use24HourTimeFormat")]
public bool Use24HourTimeFormat { get; set; } = false;
/// <summary>
/// Gets or sets whether to use a timeout for help requests
/// </summary>
[JsonProperty("useTimeoutForHelpRequests")]
public bool UseTimeoutForHelpRequests { get; set; } = false;
/// <summary>
/// Gets or sets the timeout duration for help requests in milliseconds
/// </summary>
[JsonProperty("helpRequestTimeoutMs")]
public int HelpRequestTimeoutMs { get; set; } = 30000;
}

View File

@@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Core.Fusion
{
/// <summary>
/// Represents Fusion Help Request functionality
/// </summary>
public interface IFusionHelpRequest
{
/// <summary>
/// Feedback containing the response to a help request
/// </summary>
StringFeedback HelpRequestResponseFeedback { get; }
/// <summary>
/// Indicates whether a help request has been sent
/// </summary>
BoolFeedback HelpRequestSentFeedback { get; }
/// <summary>
/// Feedback containing the current status of the help request
/// </summary>
StringFeedback HelpRequestStatusFeedback { get; }
/// <summary>
/// Sends a help request
/// </summary>
void SendHelpRequest();
/// <summary>
/// Clears the current help request status
/// </summary>
void CancelHelpRequest();
/// <summary>
/// Toggles between sending and cancelling a help request
/// </summary>
void ToggleHelpRequest();
}
}

View File

@@ -1,37 +0,0 @@
namespace PepperDash.Essentials.Core.Fusion
{
/// <summary>
/// Enumeration of possible Fusion Help Responses based on the standard responses from Fusion
/// </summary>
public enum eFusionHelpResponse
{
/// <summary>
/// No help response
/// </summary>
None,
/// <summary>
/// Help has been requested
/// </summary>
HelpRequested,
/// <summary>
/// Help is on the way
/// </summary>
HelpOnTheWay,
/// <summary>
/// Please call the helpdesk.
/// </summary>
CallHelpDesk,
/// <summary>
/// Rescheduling meeting.
/// </summary>
ReschedulingMeeting,
/// <summary>
/// Technician taking control.
/// </summary>
TakingControl,
}
}

View File

@@ -49,7 +49,7 @@ namespace PepperDash.Essentials.Core
/// </summary>
public IRoomOccupancy Room { get; private set; }
private Fusion.IEssentialsRoomFusionController FusionRoom;
private Fusion.EssentialsHuddleSpaceFusionSystemControllerBase FusionRoom;
public RoomOnToDefaultSourceWhenOccupied(DeviceConfig config) :
base (config)
@@ -74,7 +74,7 @@ namespace PepperDash.Essentials.Core
var fusionRoomKey = PropertiesConfig.RoomKey + "-fusion";
FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.IEssentialsRoomFusionController;
FusionRoom = DeviceManager.GetDeviceForKey(fusionRoomKey) as Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase;
if (FusionRoom == null)
Debug.LogMessage(LogEventLevel.Debug, this, "Unable to get Fusion Room from Device Manager with key: {0}", fusionRoomKey);

View File

@@ -105,21 +105,12 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomPropertiesConfig
{
/// <summary>
/// Gets or sets the Addresses
/// </summary>
[JsonProperty("addresses")]
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
/// <summary>
/// Gets or sets the Description
/// </summary>
[JsonProperty("description")]
public string Description { get; set; }
/// <summary>
/// Gets or sets the Emergency
/// </summary>
[JsonProperty("emergency")]
public EssentialsRoomEmergencyConfig Emergency { get; set; }
@@ -235,11 +226,11 @@ namespace PepperDash.Essentials.Room.Config
/// Indicates if this room represents a combination of other rooms
/// </summary>
[JsonProperty("isRoomCombinationScenario")]
/// <summary>
/// Gets or sets the IsRoomCombinationScenario
/// </summary>
public bool IsRoomCombinationScenario { get; set; }
/// <summary>
/// Constructor
/// </summary>
public EssentialsRoomPropertiesConfig()
{
LogoLight = new EssentialsLogoPropertiesConfig();
@@ -252,10 +243,10 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomUiBehaviorConfig
{
[JsonProperty("disableActivityButtonsWhileWarmingCooling")]
/// <summary>
/// Gets or sets the DisableActivityButtonsWhileWarmingCooling
/// </summary>
[JsonProperty("disableActivityButtonsWhileWarmingCooling")]
public bool DisableActivityButtonsWhileWarmingCooling { get; set; }
}
@@ -264,86 +255,74 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig
{
[JsonProperty("defaultAudioKey")]
/// <summary>
/// Gets or sets the DefaultAudioKey
/// </summary>
[JsonProperty("defaultAudioKey")]
public string DefaultAudioKey { get; set; }
/// <summary>
/// Gets or sets the DefaultOnDspPresetKey
/// </summary>
[JsonProperty("defaultOnDspPresetKey")]
public string DefaultOnDspPresetKey { get; set; }
/// <summary>
/// Gets or sets the DefaultOffDspPresetKey
/// </summary>
[JsonProperty("defaultOffDspPresetKey")]
public string DefaultOffDspPresetKey { get; set; }
[JsonProperty("sourceListKey")]
/// <summary>
/// Gets or sets the SourceListKey
/// </summary>
/// </summary>
[JsonProperty("sourceListKey")]
public string SourceListKey { get; set; }
[JsonProperty("destinationListKey")]
/// <summary>
/// Gets or sets the DestinationListKey
/// </summary>
[JsonProperty("destinationListKey")]
public string DestinationListKey { get; set; }
[JsonProperty("audioControlPointListKey")]
/// <summary>
/// Gets or sets the AudioControlPointListKey
/// </summary>
[JsonProperty("audioControlPointListKey")]
public string AudioControlPointListKey { get; set; }
[JsonProperty("cameraListKey")]
/// <summary>
/// Gets or sets the CameraListKey
/// </summary>
[JsonProperty("cameraListKey")]
public string CameraListKey { get; set; }
[JsonProperty("defaultSourceItem")]
/// <summary>
/// Gets or sets the DefaultSourceItem
/// </summary>
[JsonProperty("defaultSourceItem")]
public string DefaultSourceItem { get; set; }
/// <summary>
/// Indicates if the room supports advanced sharing
/// </summary>
[JsonProperty("supportsAdvancedSharing")]
/// <summary>
/// Gets or sets the SupportsAdvancedSharing
/// </summary>
public bool SupportsAdvancedSharing { get; set; }
/// <summary>
/// Indicates if non-tech users can change the share mode
/// </summary>
[JsonProperty("userCanChangeShareMode")]
/// <summary>
/// Gets or sets the UserCanChangeShareMode
/// </summary>
public bool UserCanChangeShareMode { get; set; }
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
/// <summary>
/// Gets or sets the MatrixRoutingKey
/// </summary>
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
public string MatrixRoutingKey { get; set; }
}
/// <summary>
/// Represents a EssentialsConferenceRoomPropertiesConfig
/// </summary>
public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig
{
[JsonProperty("videoCodecKey")]
/// <summary>
/// Gets or sets the VideoCodecKey
/// </summary>
[JsonProperty("videoCodecKey")]
public string VideoCodecKey { get; set; }
[JsonProperty("audioCodecKey")]
/// <summary>
/// Gets or sets the AudioCodecKey
/// </summary>
[JsonProperty("audioCodecKey")]
public string AudioCodecKey { get; set; }
}
@@ -358,15 +337,12 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public bool Enabled { get; set; }
[JsonProperty("deviceKeys")]
/// <summary>
/// Gets or sets the DeviceKeys
/// </summary>
[JsonProperty("deviceKeys")]
public List<string> DeviceKeys { get; set; }
/// <summary>
/// Constructor
/// </summary>
public EssentialsEnvironmentPropertiesConfig()
{
DeviceKeys = new List<string>();
@@ -379,9 +355,6 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomFusionConfig
{
/// <summary>
/// Gets the the IpId as a UInt16
/// </summary>
public uint IpIdInt
{
get
@@ -398,16 +371,16 @@ namespace PepperDash.Essentials.Room.Config
}
}
[JsonProperty("ipId")]
/// <summary>
/// Gets or sets the IpId
/// </summary>
[JsonProperty("ipId")]
public string IpId { get; set; }
[JsonProperty("joinMapKey")]
/// <summary>
/// Gets or sets the JoinMapKey
/// </summary>
[JsonProperty("joinMapKey")]
public string JoinMapKey { get; set; }
}
@@ -417,16 +390,16 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomMicrophonePrivacyConfig
{
[JsonProperty("deviceKey")]
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
[JsonProperty("behaviour")]
/// <summary>
/// Gets or sets the Behaviour
/// </summary>
[JsonProperty("behaviour")]
public string Behaviour { get; set; }
}
@@ -435,15 +408,12 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsHelpPropertiesConfig
{
[JsonProperty("message")]
/// <summary>
/// Gets or sets the Message
/// </summary>
[JsonProperty("message")]
public string Message { get; set; }
/// <summary>
/// Gets or sets the ShowCallButton
/// </summary>
[JsonProperty("showCallButton")]
public bool ShowCallButton { get; set; }
@@ -451,11 +421,11 @@ namespace PepperDash.Essentials.Room.Config
/// Defaults to "Call Help Desk"
/// </summary>
[JsonProperty("callButtonText")]
/// <summary>
/// Gets or sets the CallButtonText
/// </summary>
public string CallButtonText { get; set; }
/// <summary>
/// Constructor
/// </summary>
public EssentialsHelpPropertiesConfig()
{
CallButtonText = "Call Help Desk";
@@ -467,28 +437,22 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsOneButtonMeetingPropertiesConfig
{
[JsonProperty("enable")]
/// <summary>
/// Gets or sets the Enable
/// </summary>
[JsonProperty("enable")]
public bool Enable { get; set; }
}
/// <summary>
/// Represents a EssentialsRoomAddressPropertiesConfig
/// </summary>
public class EssentialsRoomAddressPropertiesConfig
{
/// <summary>
/// Gets or sets the PhoneNumber
/// </summary>
[JsonProperty("phoneNumber")]
public string PhoneNumber { get; set; }
[JsonProperty("sipAddress")]
/// <summary>
/// Gets or sets the SipAddress
/// </summary>
[JsonProperty("sipAddress")]
public string SipAddress { get; set; }
}
@@ -498,18 +462,14 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsLogoPropertiesConfig
{
[JsonProperty("type")]
/// <summary>
/// Gets or sets the Type
/// </summary>
[JsonProperty("type")]
public string Type { get; set; }
/// <summary>
/// Gets or sets the Url
/// </summary>
[JsonProperty("url")]
public string Url { get; set; }
/// <summary>
/// GetLogoUrlLight method
/// </summary>
@@ -542,28 +502,22 @@ namespace PepperDash.Essentials.Room.Config
/// </summary>
public class EssentialsRoomOccSensorConfig
{
[JsonProperty("deviceKey")]
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
[JsonProperty("deviceKey")]
public string DeviceKey { get; set; }
/// <summary>
/// Gets or sets the TimeoutMinutes
/// </summary>
[JsonProperty("timeoutMinutes")]
public int TimeoutMinutes { get; set; }
}
/// <summary>
/// Represents a EssentialsRoomTechConfig
/// </summary>
public class EssentialsRoomTechConfig
{
[JsonProperty("password")]
/// <summary>
/// Gets or sets the Password
/// </summary>
[JsonProperty("password")]
public string Password { get; set; }
}
}

View File

@@ -408,7 +408,7 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Information, this, "Timeout Minutes from Config is: {0}", timeoutMinutes);
// If status provider is fusion, set flag to remote
if (statusProvider is Core.Fusion.IEssentialsRoomFusionController)
if (statusProvider is Core.Fusion.EssentialsHuddleSpaceFusionSystemControllerBase)
OccupancyStatusProviderIsRemote = true;
if(timeoutMinutes > 0)

View File

@@ -28,7 +28,7 @@ namespace PepperDash.Essentials.Core
[Obsolete("Will be removed in a future version")]
public interface IHasMultipleDisplays
{
Dictionary<eSourceListItemDestinationTypes, IRoutingSink> Displays { get; }
Dictionary<string, IRoutingSink> Displays { get; }
}
/// <summary>

View File

@@ -1,11 +1,11 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Crestron.SimplSharpPro.Keypads;
using Crestron.SimplSharpPro.Keypads;
using PepperDash.Essentials.Core.Queues;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Debug = PepperDash.Core.Debug;
@@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core
public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
{
// if it's a single signal type, find the route
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) &&
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) &&
!(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio)))
{
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType);
@@ -134,15 +134,14 @@ namespace PepperDash.Essentials.Core
}
// otherwise, audioVideo needs to be handled as two steps.
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", destination, source.Key, signalType);
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
RouteDescriptor audioRouteDescriptor;
if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio))
{
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.SecondaryAudio);
}
else
} else
{
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio);
}
@@ -200,13 +199,13 @@ namespace PepperDash.Essentials.Core
Source = source,
SourcePort = sourcePort,
SignalType = signalType
};
};
var coolingDevice = destination as IWarmingCooling;
//We already have a route request for this device, and it's a cooling device and is cooling
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
{
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
@@ -220,7 +219,7 @@ namespace PepperDash.Essentials.Core
//New Request
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
{
{
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
RouteRequests.Add(destination.Key, routeRequest);
@@ -240,9 +239,9 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
}
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, destinationPort?.Key ?? string.Empty, false));
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false));
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
}
/// <summary>
@@ -273,8 +272,7 @@ namespace PepperDash.Essentials.Core
audioOrSingleRoute.ExecuteRoutes();
videoRoute?.ExecuteRoutes();
}
catch (Exception ex)
} catch(Exception ex)
{
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
}
@@ -307,10 +305,9 @@ namespace PepperDash.Essentials.Core
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
current.ReleaseRoutes(clearRoute);
}
}
catch (Exception ex)
} catch (Exception ex)
{
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'", null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
}
}

View File

@@ -98,7 +98,7 @@ namespace PepperDash.Essentials.Core.Routing
/// <param name="inputPort">The currently selected input port on the destination device.</param>
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
{
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this, destination?.Key, inputPort?.Key);
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
if(inputPort == null)
{

View File

@@ -1,22 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core;
using PepperDash.Core.Logging;
using Crestron.SimplSharpPro.UI;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Serilog.Events;
namespace PepperDash.Essentials.Core.UI
{
/// <summary>
/// Base class for Touchpanel devices
/// </summary>
public abstract class TouchpanelBase : EssentialsDevice, IHasBasicTriListWithSmartObject
public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmartObject
{
/// <summary>
/// Gets or sets the configuration for the Crestron touchpanel.
/// </summary>
protected CrestronTouchpanelPropertiesConfig _config;
/// <summary>
/// Gets or sets the Panel
@@ -29,11 +27,12 @@ namespace PepperDash.Essentials.Core.UI
/// is provided.
/// </summary>
/// <param name="key">Essentials Device Key</param>
/// <param name="name">Essentials Device Name</param>
/// <param name="panel">Crestron Touchpanel Device</param>
/// <param name="config">Touchpanel Configuration</param>
/// <param name="name">Essentials Device Name</param>
/// <param name="type">Touchpanel Type to build</param>
/// <param name="config">Touchpanel Configuration</param>
/// <param name="id">IP-ID to use for touch panel</param>
protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config)
: base(key, name)
:base(key, name)
{
if (panel == null)
@@ -56,21 +55,23 @@ namespace PepperDash.Essentials.Core.UI
tsw.ButtonStateChange += Tsw_ButtonStateChange;
}
_config = config;
_config = config;
AddPreActivationAction(() => {
if (Panel.Register() != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
AddPreActivationAction(() =>
{
// Give up cleanly if SGD is not present.
var sgdName = Global.FilePathPrefix + "sgd" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName))
{
this.LogInformation("Smart object file '{0}' not present in User folder. Looking for embedded file", sgdName);
Debug.LogMessage(LogEventLevel.Information, this, "Smart object file '{0}' not present in User folder. Looking for embedded file", sgdName);
sgdName = Global.ApplicationDirectoryPathPrefix + Global.DirectorySeparator + "SGD" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName))
{
this.LogWarning("Unable to find SGD file '{0}' in User sgd or application SGD folder. Exiting touchpanel load.", sgdName);
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find SGD file '{0}' in User sgd or application SGD folder. Exiting touchpanel load.", sgdName);
return;
}
}
@@ -81,11 +82,12 @@ namespace PepperDash.Essentials.Core.UI
AddPostActivationAction(() =>
{
// Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event
var roomCombiner = DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) as IEssentialsRoomCombiner;
if (DeviceManager.AllDevices.FirstOrDefault((d) => d is IEssentialsRoomCombiner) is IEssentialsRoomCombiner roomCombiner)
if (roomCombiner != null)
{
// Subscribe to the even
roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(RoomCombiner_RoomCombinationScenarioChanged);
roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(roomCombiner_RoomCombinationScenarioChanged);
// Connect to the initial roomKey
if (roomCombiner.CurrentScenario != null)
@@ -104,11 +106,6 @@ namespace PepperDash.Essentials.Core.UI
// No room combiner, use the default key
SetupPanelDrivers(_config.DefaultRoomKey);
}
var panelRegistrationResponse = Panel.Register();
if (panelRegistrationResponse != eDeviceRegistrationUnRegistrationResponse.Success)
this.LogInformation("WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
});
}
@@ -118,6 +115,7 @@ namespace PepperDash.Essentials.Core.UI
/// <param name="roomKey">Room Key for this panel</param>
protected abstract void SetupPanelDrivers(string roomKey);
/// <summary>
/// Event handler for System Extender Events
/// </summary>
@@ -131,7 +129,7 @@ namespace PepperDash.Essentials.Core.UI
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void RoomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e)
protected virtual void roomCombiner_RoomCombinationScenarioChanged(object sender, EventArgs e)
{
var roomCombiner = sender as IEssentialsRoomCombiner;
@@ -158,23 +156,23 @@ namespace PepperDash.Essentials.Core.UI
SetupPanelDrivers(newRoomKey);
}
private void Panel_SigChange(object currentDevice, SigEventArgs args)
{
this.LogVerbose("Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject;
if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args)
{
var uo = args.Button.UserObject;
if (uo is Action<bool>)
(uo as Action<bool>)(args.Button.State == eButtonState.Pressed);
}
private void Panel_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
{
Debug.LogMessage(LogEventLevel.Verbose, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject;
if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue);
else if (uo is Action<ushort>)
(uo as Action<ushort>)(args.Sig.UShortValue);
else if (uo is Action<string>)
(uo as Action<string>)(args.Sig.StringValue);
}
private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args)
{
var uo = args.Button.UserObject;
if(uo is Action<bool>)
(uo as Action<bool>)(args.Button.State == eButtonState.Pressed);
}
}
}

View File

@@ -6,9 +6,9 @@ using PepperDash.Core.Web.RequestHandlers;
namespace PepperDash.Essentials.Core.Web.RequestHandlers
{
/// <summary>
/// Represents a SetDeviceStreamDebugRequestHandler
/// </summary>
/// <summary>
/// Represents a SetDeviceStreamDebugRequestHandler
/// </summary>
public class SetDeviceStreamDebugRequestHandler : WebApiBaseRequestHandler
{
/// <summary>
@@ -122,23 +122,23 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
return;
}
if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))
{
context.Response.StatusCode = 404;
context.Response.StatusDescription = "Not Found";
context.Response.End();
if (!(DeviceManager.GetDeviceForKey(body.DeviceKey) is IStreamDebugging device))
{
context.Response.StatusCode = 404;
context.Response.StatusDescription = "Not Found";
context.Response.End();
return;
}
return;
}
eStreamDebuggingSetting debugSetting;
eStreamDebuggingSetting debugSetting;
try
{
debugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), body.Setting, true);
debugSetting = (eStreamDebuggingSetting) Enum.Parse(typeof (eStreamDebuggingSetting), body.Setting, true);
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Exception handling set debug request");
Debug.LogMessage(ex, "Exception handling set debug request");
context.Response.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error";
context.Response.End();
@@ -164,7 +164,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
}
catch (Exception ex)
{
Debug.LogMessage(ex, "Exception handling set debug request");
Debug.LogMessage(ex, "Exception handling set debug request");
context.Response.StatusCode = 500;
context.Response.StatusDescription = "Internal Server Error";
context.Response.End();
@@ -198,21 +198,21 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
public class SetDeviceStreamDebugConfig
{
[JsonProperty("deviceKey", NullValueHandling = NullValueHandling.Include)]
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
/// <summary>
/// Gets or sets the DeviceKey
/// </summary>
public string DeviceKey { get; set; }
[JsonProperty("setting", NullValueHandling = NullValueHandling.Include)]
/// <summary>
/// Gets or sets the Setting
/// </summary>
/// <summary>
/// Gets or sets the Setting
/// </summary>
public string Setting { get; set; }
[JsonProperty("timeout")]
/// <summary>
/// Gets or sets the Timeout
/// </summary>
/// <summary>
/// Gets or sets the Timeout
/// </summary>
public int Timeout { get; set; }
public SetDeviceStreamDebugConfig()

View File

@@ -1,10 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Routing;
using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Generic
@@ -12,17 +9,8 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// <summary>
/// Represents a GenericSink
/// </summary>
public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort, ICurrentSources
public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort
{
/// <inheritdoc/>
public Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; private set; }
/// <inheritdoc/>
public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; private set; }
/// <inheritdoc />
public event EventHandler CurrentSourcesChanged;
/// <summary>
/// Initializes a new instance of the GenericSink class
/// </summary>
@@ -32,52 +20,9 @@ namespace PepperDash.Essentials.Devices.Common.Generic
{
InputPorts = new RoutingPortCollection<RoutingInputPort>();
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this);
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo, eRoutingPortConnectionType.Hdmi, null, this);
InputPorts.Add(inputPort);
CurrentSources = new Dictionary<eRoutingSignalType, SourceListItem>
{
{ eRoutingSignalType.Audio, null },
{ eRoutingSignalType.Video, null },
};
CurrentSourceKeys = new Dictionary<eRoutingSignalType, string>
{
{ eRoutingSignalType.Audio, string.Empty },
{ eRoutingSignalType.Video, string.Empty },
};
}
/// <inheritdoc />
public void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
{
foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType)))
{
var flagValue = Convert.ToInt32(type);
// Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag).
// (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set.
if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0)
{
this.LogDebug("Skipping {type}", type);
continue;
}
this.LogDebug("setting {type}", type);
if (signalType.HasFlag(type))
{
UpdateCurrentSources(type, sourceListKey, sourceListItem);
}
}
// Raise the CurrentSourcesChanged event
CurrentSourcesChanged?.Invoke(this, EventArgs.Empty);
}
private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
{
CurrentSources[signalType] = sourceListItem;
CurrentSourceKeys[signalType] = sourceListKey;
}
/// <summary>
@@ -121,15 +66,6 @@ namespace PepperDash.Essentials.Devices.Common.Generic
/// Event fired when the current source changes
/// </summary>
public event SourceInfoChangeHandler CurrentSourceChange;
/// <inheritdoc />
public event InputChangedEventHandler InputChanged;
/// <inheritdoc />
public void ExecuteSwitch(object inputSelector)
{
this.LogDebug("GenericSink Executing Switch to: {inputSelector}", inputSelector);
}
}
/// <summary>

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
@@ -40,14 +39,10 @@ namespace PepperDash.Essentials.AppServer.Messengers
sourceDevice.CurrentSourcesChanged += (sender, e) =>
{
// need to copy the dictionaries to avoid enumeration issues
var currentSourceKeys = sourceDevice.CurrentSourceKeys.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
var currentSources = sourceDevice.CurrentSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
PostStatusMessage(JToken.FromObject(new
{
currentSourceKeys,
currentSources,
currentSourceKeys = sourceDevice.CurrentSourceKeys,
currentSources = sourceDevice.CurrentSources
}));
};
}

View File

@@ -130,33 +130,34 @@ namespace PepperDash.Essentials.AppServer.Messengers
feedback.MuteFeedback.OutputChange += (sender, args) =>
{
var message = new VolumeStateMessage
{
Volume = new Volume
{
Muted = args.BoolValue
}
};
PostStatusMessage(JToken.FromObject(message));
PostStatusMessage(JToken.FromObject(
new
{
volume = new
{
muted = args.BoolValue
}
})
);
};
feedback.VolumeLevelFeedback.OutputChange += (sender, args) =>
{
var message = new VolumeStateMessage
var rawValue = "";
if (feedback is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
{
Volume = new Volume
rawValue = volumeAdvanced.RawVolumeLevel.ToString();
}
var message = new
{
volume = new
{
Level = args.IntValue,
level = args.IntValue,
rawValue
}
};
if (device is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
{
message.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString();
message.Volume.Units = volumeAdvanced.Units;
}
PostStatusMessage(JToken.FromObject(message));
};
}

View File

@@ -31,12 +31,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
/// <remarks>
/// Unsoliciited feedback from a device in a messenger will ONLY be sent to devices in this subscription list. When a client disconnects, it's ID will be removed from the collection.
/// </remarks>
private readonly HashSet<string> subscriberIds = new HashSet<string>();
/// <summary>
/// Lock object for thread-safe access to SubscriberIds
/// </summary>
private readonly object _subscriberLock = new object();
protected HashSet<string> SubscriberIds = new HashSet<string>();
private readonly List<string> _deviceInterfaces;
@@ -194,18 +189,18 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
if (!enableMessengerSubscriptions)
{
this.LogWarning("Messenger subscriptions not enabled");
return;
}
lock (_subscriberLock)
if (SubscriberIds.Any(id => id == clientId))
{
if (!subscriberIds.Add(clientId))
{
this.LogVerbose("Client {clientId} already subscribed", clientId);
return;
}
this.LogVerbose("Client {clientId} already subscribed", clientId);
return;
}
SubscriberIds.Add(clientId);
this.LogDebug("Client {clientId} subscribed", clientId);
}
@@ -217,26 +212,19 @@ namespace PepperDash.Essentials.AppServer.Messengers
{
if (!enableMessengerSubscriptions)
{
this.LogWarning("Messenger subscriptions not enabled");
return;
}
bool wasSubscribed;
lock (_subscriberLock)
{
wasSubscribed = subscriberIds.Contains(clientId);
if (wasSubscribed)
{
subscriberIds.Remove(clientId);
}
}
if (!wasSubscribed)
if (!SubscriberIds.Any(i => i == clientId))
{
this.LogVerbose("Client with ID {clientId} is not subscribed", clientId);
return;
}
this.LogDebug("Client with ID {clientId} unsubscribed", clientId);
SubscriberIds.RemoveWhere((i) => i == clientId);
this.LogInformation("Client with ID {clientId} unsubscribed", clientId);
}
/// <summary>
@@ -270,8 +258,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
catch (Exception ex)
{
this.LogError("Exception posting status message for {messagePath} to {clientId}: {message}", MessagePath, clientId ?? "all clients", ex.Message);
this.LogDebug(ex, "Stack trace: ");
this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients");
}
}
@@ -300,8 +287,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
}
catch (Exception ex)
{
this.LogError("Exception posting status message for {type} to {clientId}: {message}", type, clientId ?? "all clients", ex.Message);
this.LogDebug(ex, "Stack trace: ");
this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients");
}
}
@@ -326,14 +312,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
// If client is null or empty, this message is unsolicited feedback. Iterate through the subscriber list and send to all interested parties
if (string.IsNullOrEmpty(clientId))
{
// Create a snapshot of subscribers to avoid collection modification during iteration
List<string> subscriberSnapshot;
lock (_subscriberLock)
{
subscriberSnapshot = new List<string>(subscriberIds);
}
foreach (var client in subscriberSnapshot)
foreach (var client in SubscriberIds)
{
AppServerController?.SendMessageObject(new MobileControlMessage { Type = !string.IsNullOrEmpty(type) ? type : MessagePath, ClientId = client, Content = content });
}

View File

@@ -1312,11 +1312,6 @@ namespace PepperDash.Essentials
/// <inheritdoc />
public override void Initialize()
{
if (!Config.EnableMessengerSubscriptions)
{
this.LogWarning("Messenger subscriptions disabled. add \"enableMessengerSubscriptions\": true to config for {key} to enable.", Key);
}
foreach (var messenger in _messengers)
{
try
@@ -2179,7 +2174,6 @@ namespace PepperDash.Essentials
{
var clientId = content["clientId"].Value<string>();
var roomKey = content["roomKey"].Value<string>();
var touchpanelKey = content.SelectToken("touchpanelKey");
if (_roomCombiner == null)
{
@@ -2191,10 +2185,6 @@ namespace PepperDash.Essentials
};
SendMessageObject(message);
SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return;
}
@@ -2206,12 +2196,7 @@ namespace PepperDash.Essentials
ClientId = clientId,
Content = roomKey
};
SendMessageObject(message);
SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return;
}
@@ -2229,10 +2214,6 @@ namespace PepperDash.Essentials
};
SendMessageObject(message);
SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
return;
}
@@ -2246,54 +2227,6 @@ namespace PepperDash.Essentials
};
SendMessageObject(newMessage);
SendDeviceInterfaces(clientId);
SendTouchpanelKey(clientId, touchpanelKey);
}
private void SendTouchpanelKey(string clientId, JToken touchpanelKeyToken)
{
if (touchpanelKeyToken == null)
{
this.LogWarning("Touchpanel key not found for client {clientId}", clientId);
return;
}
SendMessageObject(new MobileControlMessage
{
Type = "/system/touchpanelKey",
ClientId = clientId,
Content = touchpanelKeyToken.Value<string>()
});
}
private void SendDeviceInterfaces(string clientId)
{
this.LogDebug("Sending Device interfaces");
var devices = DeviceManager.GetDevices();
Dictionary<string, DeviceInterfaceInfo> deviceInterfaces = new Dictionary<string, DeviceInterfaceInfo>();
foreach (var device in devices)
{
var interfaces = device?.GetType().GetInterfaces().Select((i) => i.Name).ToList() ?? new List<string>();
deviceInterfaces.Add(device.Key, new DeviceInterfaceInfo
{
Key = device.Key,
Name = (device as IKeyName)?.Name ?? "",
Interfaces = interfaces
});
}
var message = new MobileControlMessage
{
Type = "/system/deviceInterfaces",
ClientId = clientId,
Content = JToken.FromObject(new { deviceInterfaces })
};
SendMessageObject(message);
}
private void HandleUserCode(JToken content, Action<string, string> action = null)
@@ -2404,7 +2337,7 @@ namespace PepperDash.Essentials
foreach (var handler in handlers)
{
Task.Run(() =>
Task.Run(async () =>
{
try
{

View File

@@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Touchpanel
return;
}
AddAction($"/fullStatus", (id, context) => SendFullStatus(id));
AddAction($"/fullStatus", (id, context) => SendFullStatus());
AddAction($"/openApp", (id, context) => _appControl.OpenApp());

View File

@@ -252,7 +252,6 @@ namespace PepperDash.Essentials.Touchpanel
if (!x70Panel.ExtenderApplicationControlReservedSigs.HideOpenedApplicationFeedback.BoolValue)
{
x70Panel.ExtenderButtonToolbarReservedSigs.ShowButtonToolbar();
x70Panel.ExtenderButtonToolbarReservedSigs.Button2On();
}
else
@@ -295,16 +294,17 @@ namespace PepperDash.Essentials.Touchpanel
handler(this, new DeviceInfoEventArgs(DeviceInfo));
};
x70Panel.ExtenderButtonToolbarReservedSigs.DeviceExtenderSigChange += (o, a) =>
{
this.LogVerbose("X70 Button Toolbar Device Extender args: {event}:{sig}:{name}:{type}:{boolValue}:{ushortValue}:{stringValue}", a.Event, a.Sig, a.Sig.Name, a.Sig.Type, a.Sig.BoolValue, a.Sig.UShortValue, a.Sig.StringValue);
};
x70Panel.ExtenderApplicationControlReservedSigs.Use();
x70Panel.ExtenderZoomRoomAppReservedSigs.Use();
x70Panel.ExtenderEthernetReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Use();
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
return;
}
@@ -414,79 +414,34 @@ namespace PepperDash.Essentials.Touchpanel
McServerUrlFeedback.LinkInputSig(Panel.StringInput[3]);
UserCodeFeedback.LinkInputSig(Panel.StringInput[4]);
Panel.IpInformationChange -= Panel_IpInformationChange;
Panel.IpInformationChange += Panel_IpInformationChange;
Panel.OnlineStatusChange -= Panel_OnlineChange;
Panel.OnlineStatusChange += Panel_OnlineChange;
}
private void Panel_OnlineChange(GenericBase sender, OnlineOfflineEventArgs args)
{
try
Panel.IpInformationChange += (sender, args) =>
{
if (!args.DeviceOnLine)
if (args.Connected)
{
this.LogInformation("panel is offline");
return;
}
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
this.LogDebug("panel is online");
var appUrl = GetUrlWithCorrectIp(_appUrl);
Panel.StringInput[1].StringValue = appUrl;
SetAppUrl(appUrl);
}
else
{
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
}
};
Panel.OnlineStatusChange += (sender, args) =>
{
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
UpdateFeedbacks();
Panel.StringInput[1].StringValue = _appUrl;
Panel.StringInput[2].StringValue = QrCodeUrlFeedback.StringValue;
Panel.StringInput[3].StringValue = McServerUrlFeedback.StringValue;
Panel.StringInput[4].StringValue = UserCodeFeedback.StringValue;
if (Panel is TswXX70Base x70Panel)
{
this.LogDebug("setting buttons off");
x70Panel.ExtenderButtonToolbarReservedSigs.Button1Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button3Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button4Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button5Off();
x70Panel.ExtenderButtonToolbarReservedSigs.Button6Off();
}
SendUrlToPanel();
}
catch (Exception ex)
{
this.LogError("Exception in panel online: {message}", ex.Message);
this.LogDebug(ex, "Stack Trace: ");
}
}
private void SendUrlToPanel()
{
var appUrl = GetUrlWithCorrectIp(_appUrl);
this.LogInformation("Sending {appUrl} on join 1", AppUrlFeedback.StringValue);
if (Panel.StringInput[1].StringValue == appUrl)
{
this.LogInformation("App URL already set to {appUrl}, no update needed", AppUrlFeedback.StringValue);
return;
}
Panel.StringInput[1].StringValue = appUrl;
SetAppUrl(appUrl);
}
private void Panel_IpInformationChange(GenericBase sender, ConnectedIpEventArgs args)
{
if (args.Connected)
{
this.LogVerbose("Connection from IP: {ip}", args.DeviceIpAddress);
SendUrlToPanel();
}
else
{
this.LogVerbose("Disconnection from IP: {ip}", args.DeviceIpAddress);
}
};
}
/// <summary>

View File

@@ -1,7 +1,6 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.AppServer;
using PepperDash.Essentials.AppServer.Messengers;
@@ -30,17 +29,17 @@ namespace PepperDash.Essentials.Touchpanel
{
AddAction("/fullStatus", (id, content) =>
{
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme }, id);
PostStatusMessage(new ThemeUpdateMessage { Theme = _tpDevice.Theme });
});
AddAction("/saveTheme", (id, content) =>
{
var theme = content.ToObject<MobileControlSimpleContent<string>>();
this.LogInformation("Setting theme to {theme}", theme.Value);
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Setting theme to {theme}", this, theme.Value);
_tpDevice.UpdateTheme(theme.Value);
PostStatusMessage(JToken.FromObject(new { theme = theme.Value }), clientId: id);
PostStatusMessage(JToken.FromObject(new { theme = theme.Value }));
});
}
}

View File

@@ -385,9 +385,9 @@ namespace PepperDash.Essentials.WebSocketServer
var appUrl = $"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}";
this.LogVerbose("Sending URL {appUrl} to touchpanel {touchpanelKey}", appUrl, touchpanel.Touchpanel.Key);
this.LogVerbose("Sending URL {appUrl}", appUrl);
touchpanel.Touchpanel.SetAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
touchpanel.Messenger.UpdateAppUrl($"http://{ip}:{_parent.Config.DirectServer.Port}/mc/app?token={touchpanel.Key}");
}
}
@@ -723,7 +723,7 @@ namespace PepperDash.Essentials.WebSocketServer
private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
{
var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token, token.TouchpanelKey);
var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token);
this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id);
c.Controller = _parent;
c.RoomKey = roomKey;

View File

@@ -31,11 +31,6 @@ namespace PepperDash.Essentials.WebSocketServer
/// </summary>
public string Token { get; private set; }
/// <summary>
/// Touchpanel Key associated with this client
/// </summary>
public string TouchpanelKey { get; private set; }
/// <summary>
/// Gets or sets the mobile control system controller that handles this client's messages
/// </summary>
@@ -80,13 +75,11 @@ namespace PepperDash.Essentials.WebSocketServer
/// <param name="key">The unique key to identify this client</param>
/// <param name="id">The client ID used by the client for this connection</param>
/// <param name="token">The token associated with this client</param>
/// <param name="touchpanelKey">The touchpanel key associated with this client</param>
public UiClient(string key, string id, string token, string touchpanelKey = "")
public UiClient(string key, string id, string token)
{
Key = key;
Id = id;
Token = token;
TouchpanelKey = touchpanelKey;
}
/// <inheritdoc />
@@ -112,7 +105,6 @@ namespace PepperDash.Essentials.WebSocketServer
{
clientId = Id,
roomKey = RoomKey,
touchpanelKey = TouchpanelKey ?? string.Empty,
})
};

View File

@@ -104,7 +104,7 @@ namespace PepperDash.Essentials
CrestronConsole.ConsoleCommandResponse
("Current running configuration. This is the merged system and template configuration" + CrestronEnvironment.NewLine);
CrestronConsole.ConsoleCommandResponse(Newtonsoft.Json.JsonConvert.SerializeObject
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented).Replace(Environment.NewLine, "\r\n"));
(ConfigReader.ConfigObject, Newtonsoft.Json.Formatting.Indented));
}, "showconfig", "Shows the current running merged config", ConsoleAccessLevelEnum.AccessOperator);
CrestronConsole.AddNewConsoleCommand(s =>