Compare commits

..

4 Commits

Author SHA1 Message Date
Jonathan Arndt
ea983a6f37 Merge pull request #1333 from PepperDash:main
feat: merge main into custom branch
2025-09-22 13:47:03 -07:00
Jonathan Arndt
937a64f996 feat: updated Crestron db 2025-09-22 13:39:58 -07:00
Jonathan Arndt
54328536a8 feat: updated Crestron db 2025-09-22 13:33:04 -07:00
Jonathan Arndt
591f12f086 feature: update Crestron.SimplSharp.SDK.ProgramLibrary version to 2.21.157 across multiple projects 2025-09-17 15:53:08 -07:00
269 changed files with 7385 additions and 11711 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@@ -23,32 +23,23 @@
<FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName> <FileName>$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz</FileName>
</PropertyGroup> </PropertyGroup>
<Target Name="DeleteCLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Library' And $(TargetDir) != ''"> <Target Name="DeleteCLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Library' And $(TargetDir) != '' And Exists($(FileName))">
<ItemGroup> <Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).clz">
<OldCLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).clz" />
</ItemGroup>
<Delete Files="@(OldCLZFiles)" Condition="@(OldCLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted old CLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" /> <Message Text="Deleted files: '@(DeletedList)'" />
</Target> </Target>
<Target Name="DeleteCPZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'Program' And $(TargetDir) != ''"> <Target Name="DeleteCPZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'Program' And $(TargetDir) != '' And Exists($(FileName))">
<ItemGroup> <Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cpz">
<OldCPZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cpz" />
</ItemGroup>
<Delete Files="@(OldCPZFiles)" Condition="@(OldCPZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted old CPZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" /> <Message Text="Deleted files: '@(DeletedList)'" />
</Target> </Target>
<Target Name="DeleteCPLZ" BeforeTargets="CoreBuild" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''"> <Target Name="DeleteCPLZ" BeforeTargets="PreBuildEvent" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != '' And Exists($(FileName))">
<ItemGroup> <Delete Files="$(TargetDir)$(TargetName).$(Version).$(TargetFramework).cplz">
<OldCPLZFiles Include="$(TargetDir)$(TargetName).*.$(TargetFramework).cplz" />
</ItemGroup>
<Delete Files="@(OldCPLZFiles)" Condition="@(OldCPLZFiles) != ''">
<Output TaskParameter="DeletedFiles" ItemName="DeletedList"/> <Output TaskParameter="DeletedFiles" ItemName="DeletedList"/>
</Delete> </Delete>
<Message Text="Deleted old CPLZ files: '@(DeletedList)'" Condition="@(DeletedList) != ''" /> <Message Text="Deleted files: '@(DeletedList)'" />
</Target> </Target>
<Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ"> <Target Name="CreateCPLZ" AfterTargets="Build" Condition="$(ProjectType) == 'ProgramLibrary' And $(TargetDir) != ''" DependsOnTargets="DeleteCPLZ">

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

@@ -426,7 +426,10 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) 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)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
@@ -434,7 +437,10 @@ namespace PepperDash.Core
{ {
var str = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); 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)); textHandler(this, new GenericCommMethodReceiveTextArgs(str));
} }
@@ -450,7 +456,8 @@ namespace PepperDash.Core
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(text); var bytes = Encoding.GetEncoding(28591).GetBytes(text);
// Check debug level before processing byte array // Check debug level before processing byte array
this.PrintSentText(text); if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.Console(0, this, "Sending {0} characters of text: '{1}'", text.Length, ComTextHelper.GetDebugText(text));
if (_client != null) if (_client != null)
_client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }
@@ -477,7 +484,8 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void SendBytes(byte[] bytes) 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 (_client != null) if (_client != null)
_client.SendData(bytes, bytes.Length); _client.SendData(bytes, bytes.Length);
} }
@@ -547,12 +555,6 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public int AutoReconnectIntervalMs { get; set; } public int AutoReconnectIntervalMs { get; set; }
/// <summary>
/// When true, turns off echo for the SSH session
/// </summary>
[JsonProperty("disableSshEcho")]
public bool DisableSshEcho { get; set; }
/// <summary> /// <summary>
/// Default constructor /// Default constructor
/// </summary> /// </summary>
@@ -563,7 +565,8 @@ namespace PepperDash.Core
AutoReconnectIntervalMs = 5000; AutoReconnectIntervalMs = 5000;
Username = ""; Username = "";
Password = ""; Password = "";
DisableSshEcho = false;
} }
} }
} }

View File

@@ -131,14 +131,14 @@ namespace PepperDash.Core
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="address"></param> /// <param name="address"></param>
/// <param name="port"></param> /// <param name="port"></param>
/// <param name="bufferSize"></param> /// <param name="buffefSize"></param>
public GenericUdpServer(string key, string address, int port, int bufferSize) public GenericUdpServer(string key, string address, int port, int buffefSize)
: base(key) : base(key)
{ {
StreamDebugging = new CommunicationStreamDebugging(key); StreamDebugging = new CommunicationStreamDebugging(key);
Hostname = address; Hostname = address;
Port = port; Port = port;
BufferSize = bufferSize; BufferSize = buffefSize;
CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler); CrestronEnvironment.ProgramStatusEventHandler += new ProgramStatusEventHandler(CrestronEnvironment_ProgramStatusEventHandler);
CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler); CrestronEnvironment.EthernetEventHandler += new EthernetEventHandler(CrestronEnvironment_EthernetEventHandler);
@@ -194,23 +194,9 @@ namespace PepperDash.Core
{ {
if (Server == null) if (Server == null)
{ {
try
{
var address = IPAddress.Parse(Hostname);
Server = new UDPServer(address, Port, BufferSize);
}
catch (Exception ex)
{
this.LogError("Error parsing IP Address '{ipAddress}': message: {message}", Hostname, ex.Message);
this.LogInformation("Creating UDPServer with default buffersize");
Server = new UDPServer(); Server = new UDPServer();
} }
}
if (string.IsNullOrEmpty(Hostname)) if (string.IsNullOrEmpty(Hostname))
{ {
Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key); Debug.Console(1, Debug.ErrorLogLevel.Warning, "GenericUdpServer '{0}': No address set", Key);
@@ -243,7 +229,7 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void Disconnect() public void Disconnect()
{ {
if (Server != null) if(Server != null)
Server.DisableUDPServer(); Server.DisableUDPServer();
IsConnected = false; IsConnected = false;
@@ -281,13 +267,17 @@ namespace PepperDash.Core
var bytesHandler = BytesReceived; var bytesHandler = BytesReceived;
if (bytesHandler != null) 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)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) 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)); textHandler(this, new GenericCommMethodReceiveTextArgs(str));
} }
} }
@@ -314,7 +304,8 @@ namespace PepperDash.Core
if (IsConnected && Server != null) 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); Server.SendData(bytes, bytes.Length);
} }
@@ -329,7 +320,8 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public void SendBytes(byte[] bytes) 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) if (IsConnected && Server != null)
Server.SendData(bytes, bytes.Length); Server.SendData(bytes, bytes.Length);

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> /// <summary>
/// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction /// Used when comms needs to be handled in SIMPL and bridged opposite the normal direction
/// </summary> /// </summary>
ComBridge, ComBridge
/// <summary>
/// InfinetEX control
/// </summary>
InfinetEx
} }
} }

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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronSockets; using Crestron.SimplSharp.CrestronSockets;
using System.Text.RegularExpressions;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace PepperDash.Core namespace PepperDash.Core
@@ -64,7 +67,7 @@ namespace PepperDash.Core
/// <summary> /// <summary>
/// Represents a device with stream debugging capablities /// Represents a device with stream debugging capablities
/// </summary> /// </summary>
public interface IStreamDebugging : IKeyed public interface IStreamDebugging
{ {
/// <summary> /// <summary>
/// Object to enable stream debugging /// Object to enable stream debugging
@@ -192,7 +195,7 @@ namespace PepperDash.Core
/// <param name="text"></param> /// <param name="text"></param>
/// <param name="delimiter"></param> /// <param name="delimiter"></param>
public GenericCommMethodReceiveTextArgs(string text, string delimiter) public GenericCommMethodReceiveTextArgs(string text, string delimiter)
: this(text) :this(text)
{ {
Delimiter = delimiter; Delimiter = delimiter;
} }
@@ -202,4 +205,49 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public GenericCommMethodReceiveTextArgs() { } public GenericCommMethodReceiveTextArgs() { }
} }
/// <summary>
///
/// </summary>
public class ComTextHelper
{
/// <summary>
/// Gets escaped text for a byte array
/// </summary>
/// <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,30 +9,11 @@ using Serilog.Events;
namespace PepperDash.Core.Config namespace PepperDash.Core.Config
{ {
/// <summary> /// <summary>
/// Reads a Portal formatted config file /// Reads a Portal formatted config file
/// </summary> /// </summary>
public class PortalConfigReader 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> /// <summary>
/// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object. /// Reads the config file, checks if it needs a merge, merges and saves, then returns the merged Object.
/// </summary> /// </summary>
@@ -43,25 +24,25 @@ namespace PepperDash.Core.Config
{ {
if (!File.Exists(filePath)) 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); "ERROR: Configuration file not present. Please load file to {0} and reset program", filePath);
} }
using (StreamReader fs = new StreamReader(filePath)) using (StreamReader fs = new StreamReader(filePath))
{ {
var jsonObj = JObject.Parse(fs.ReadToEnd()); 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. // it's a double-config, merge it.
var merged = MergeConfigs(jsonObj); 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; jsonObj = merged;
@@ -96,62 +77,62 @@ namespace PepperDash.Core.Config
var merged = new JObject(); var merged = new JObject();
// Put together top-level objects // Put together top-level objects
if (system[info] != null) if (system["info"] != null)
merged.Add(info, Merge(template[info], system[info], info)); merged.Add("info", Merge(template["info"], system["info"], "infO"));
else else
merged.Add(info, template[info]); merged.Add("info", template["info"]);
merged.Add(devices, MergeArraysOnTopLevelProperty(template[devices] as JArray, merged.Add("devices", MergeArraysOnTopLevelProperty(template["devices"] as JArray,
system[devices] as JArray, "key", devices)); system["devices"] as JArray, "key", "devices"));
if (system[rooms] == null) if (system["rooms"] == null)
merged.Add(rooms, template[rooms]); merged.Add("rooms", template["rooms"]);
else else
merged.Add(rooms, MergeArraysOnTopLevelProperty(template[rooms] as JArray, merged.Add("rooms", MergeArraysOnTopLevelProperty(template["rooms"] as JArray,
system[rooms] as JArray, "key", rooms)); system["rooms"] as JArray, "key", "rooms"));
if (system[sourceLists] == null) if (system["sourceLists"] == null)
merged.Add(sourceLists, template[sourceLists]); merged.Add("sourceLists", template["sourceLists"]);
else else
merged.Add(sourceLists, Merge(template[sourceLists], system[sourceLists], sourceLists)); merged.Add("sourceLists", Merge(template["sourceLists"], system["sourceLists"], "sourceLists"));
if (system[destinationLists] == null) if (system["destinationLists"] == null)
merged.Add(destinationLists, template[destinationLists]); merged.Add("destinationLists", template["destinationLists"]);
else else
merged.Add(destinationLists, merged.Add("destinationLists",
Merge(template[destinationLists], system[destinationLists], destinationLists)); Merge(template["destinationLists"], system["destinationLists"], "destinationLists"));
if (system[cameraLists] == null) if (system["cameraLists"] == null)
merged.Add(cameraLists, template[cameraLists]); merged.Add("cameraLists", template["cameraLists"]);
else else
merged.Add(cameraLists, Merge(template[cameraLists], system[cameraLists], cameraLists)); merged.Add("cameraLists", Merge(template["cameraLists"], system["cameraLists"], "cameraLists"));
if (system[audioControlPointLists] == null) if (system["audioControlPointLists"] == null)
merged.Add(audioControlPointLists, template[audioControlPointLists]); merged.Add("audioControlPointLists", template["audioControlPointLists"]);
else else
merged.Add(audioControlPointLists, merged.Add("audioControlPointLists",
Merge(template[audioControlPointLists], system[audioControlPointLists], audioControlPointLists)); Merge(template["audioControlPointLists"], system["audioControlPointLists"], "audioControlPointLists"));
// Template tie lines take precedence. Config tool doesn't do them at system // Template tie lines take precedence. Config tool doesn't do them at system
// level anyway... // level anyway...
if (template[tieLines] != null) if (template["tieLines"] != null)
merged.Add(tieLines, template[tieLines]); merged.Add("tieLines", template["tieLines"]);
else if (system[tieLines] != null) else if (system["tieLines"] != null)
merged.Add(tieLines, system[tieLines]); merged.Add("tieLines", system["tieLines"]);
else else
merged.Add(tieLines, new JArray()); merged.Add("tieLines", new JArray());
if (template[joinMaps] != null) if (template["joinMaps"] != null)
merged.Add(joinMaps, template[joinMaps]); merged.Add("joinMaps", template["joinMaps"]);
else else
merged.Add(joinMaps, new JObject()); merged.Add("joinMaps", new JObject());
if (system[global] != null) if (system["global"] != null)
merged.Add(global, Merge(template[global], system[global], global)); merged.Add("global", Merge(template["global"], system["global"], "global"));
else else
merged.Add(global, template[global]); merged.Add("global", template["global"]);
//Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged); //Debug.Console(2, "MERGED CONFIG RESULT: \x0d\x0a{0}", merged);
return merged; return merged;
@@ -247,7 +228,7 @@ namespace PepperDash.Core.Config
} }
catch (Exception e) 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

@@ -40,20 +40,20 @@ namespace PepperDash.Core
private static ILogger _logger; private static ILogger _logger;
private static readonly LoggingLevelSwitch _consoleLogLevelSwitch; private static readonly LoggingLevelSwitch _consoleLoggingLevelSwitch;
private static readonly LoggingLevelSwitch _websocketLogLevelSwitch; private static readonly LoggingLevelSwitch _websocketLoggingLevelSwitch;
private static readonly LoggingLevelSwitch _errorLogLevelSwitch; private static readonly LoggingLevelSwitch _errorLogLevelSwitch;
private static readonly LoggingLevelSwitch _fileLogLevelSwitch; private static readonly LoggingLevelSwitch _fileLevelSwitch;
/// <summary> /// <summary>
/// Gets the minimum log level for the websocket sink. /// Gets the minimum log level for the websocket sink.
/// </summary> /// </summary>
public static LogEventLevel WebsocketMinimumLogLevel public static LogEventLevel WebsocketMinimumLogLevel
{ {
get { return _websocketLogLevelSwitch.MinimumLevel; } get { return _websocketLoggingLevelSwitch.MinimumLevel; }
} }
private static readonly DebugWebsocketSink _websocketSink; private static readonly DebugWebsocketSink _websocketSink;
@@ -138,13 +138,13 @@ namespace PepperDash.Core
var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey); var defaultFileLogLevel = GetStoredLogEventLevel(FileLevelStoreKey);
_consoleLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel); _consoleLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultConsoleLevel);
_websocketLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel); _websocketLoggingLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultWebsocketLevel);
_errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel); _errorLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultErrorLogLevel);
_fileLogLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel); _fileLevelSwitch = new LoggingLevelSwitch(initialMinimumLevel: defaultFileLogLevel);
_websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true)); _websocketSink = new DebugWebsocketSink(new JsonFormatter(renderMessage: true));
@@ -162,14 +162,14 @@ namespace PepperDash.Core
.MinimumLevel.Verbose() .MinimumLevel.Verbose()
.Enrich.FromLogContext() .Enrich.FromLogContext()
.Enrich.With(new CrestronEnricher()) .Enrich.With(new CrestronEnricher())
.WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLogLevelSwitch) .WriteTo.Sink(new DebugConsoleSink(new ExpressionTemplate("[{@t:yyyy-MM-dd HH:mm:ss.fff}][{@l:u4}][{App}]{#if Key is not null}[{Key}]{#end} {@m}{#if @x is not null}\r\n{@x}{#end}")), levelSwitch: _consoleLoggingLevelSwitch)
.WriteTo.Sink(_websocketSink, levelSwitch: _websocketLogLevelSwitch) .WriteTo.Sink(_websocketSink, levelSwitch: _websocketLoggingLevelSwitch)
.WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch) .WriteTo.Sink(new DebugErrorLogSink(new ExpressionTemplate(errorLogTemplate)), levelSwitch: _errorLogLevelSwitch)
.WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath, .WriteTo.File(new RenderedCompactJsonFormatter(), logFilePath,
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
restrictedToMinimumLevel: LogEventLevel.Debug, restrictedToMinimumLevel: LogEventLevel.Debug,
retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 7 : 14, retainedFileCountLimit: CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? 30 : 60,
levelSwitch: _fileLogLevelSwitch levelSwitch: _fileLevelSwitch
); );
try try
@@ -237,13 +237,10 @@ namespace PepperDash.Core
if (DoNotLoadConfigOnNextBoot) if (DoNotLoadConfigOnNextBoot)
CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber)); CrestronConsole.PrintLine(string.Format("Program {0} will not load config after next boot. Use console command go:{0} to load the config manually", InitialParametersClass.ApplicationNumber));
_errorLogLevelSwitch.MinimumLevelChanged += (sender, args) => _consoleLoggingLevelSwitch.MinimumLevelChanged += (sender, args) =>
{ {
LogMessage(LogEventLevel.Information, "Error log debug level set to {minimumLevel}", _errorLogLevelSwitch.MinimumLevel); LogMessage(LogEventLevel.Information, "Console debug level set to {minimumLevel}", _consoleLoggingLevelSwitch.MinimumLevel);
}; };
// Set initial error log level based on platform && stored level. If appliance, use stored level, otherwise default to verbose
SetErrorLogMinimumDebugLevel(CrestronEnvironment.DevicePlatform == eDevicePlatform.Appliance ? _errorLogLevelSwitch.MinimumLevel : LogEventLevel.Verbose);
} }
/// <summary> /// <summary>
@@ -276,9 +273,9 @@ namespace PepperDash.Core
{ {
CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n"); CrestronConsole.Print($"Unable to retrieve stored log level for {levelStoreKey}.\r\nError: {result}.\r\nSetting level to {LogEventLevel.Information}\r\n");
CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, levelStoreKey == ErrorLogLevelStoreKey ? (int)LogEventLevel.Warning : (int)LogEventLevel.Information); CrestronDataStoreStatic.SetLocalIntValue(levelStoreKey, (int)LogEventLevel.Information);
return levelStoreKey == ErrorLogLevelStoreKey ? LogEventLevel.Warning : LogEventLevel.Information; return LogEventLevel.Information;
} }
if (logLevel < 0 || logLevel > 5) if (logLevel < 0 || logLevel > 5)
@@ -287,8 +284,6 @@ namespace PepperDash.Core
return LogEventLevel.Information; return LogEventLevel.Information;
} }
CrestronConsole.PrintLine($"Stored log level for {levelStoreKey} is {logLevel}");
return (LogEventLevel)logLevel; return (LogEventLevel)logLevel;
} }
catch (Exception ex) catch (Exception ex)
@@ -354,11 +349,7 @@ namespace PepperDash.Core
if (levelString.Trim() == "?") if (levelString.Trim() == "?")
{ {
CrestronConsole.ConsoleCommandResponse( CrestronConsole.ConsoleCommandResponse(
"Used to set the minimum level of debug messages:\r\n" + "Used to set the minimum level of debug messages to be printed to the console:\r\n" +
"Usage: appdebug:P [sink] [level]\r\n" +
" sink: console (default), errorlog, file, all\r\n" +
" all: sets all sinks to the specified level\r\n" +
" level: 0-5 or LogEventLevel name\r\n" +
$"{_logLevels[0]} = 0\r\n" + $"{_logLevels[0]} = 0\r\n" +
$"{_logLevels[1]} = 1\r\n" + $"{_logLevels[1]} = 1\r\n" +
$"{_logLevels[2]} = 2\r\n" + $"{_logLevels[2]} = 2\r\n" +
@@ -370,88 +361,32 @@ namespace PepperDash.Core
if (string.IsNullOrEmpty(levelString.Trim())) if (string.IsNullOrEmpty(levelString.Trim()))
{ {
CrestronConsole.ConsoleCommandResponse("Console log level = {0}\r\n", _consoleLogLevelSwitch.MinimumLevel); CrestronConsole.ConsoleCommandResponse("AppDebug level = {0}", _consoleLoggingLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("File log level = {0}\r\n", _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse("Error log level = {0}\r\n", _errorLogLevelSwitch.MinimumLevel);
return; return;
} }
// Parse tokens: first token is sink (defaults to console), second token is level if (int.TryParse(levelString, out var levelInt))
var tokens = levelString.Trim().Split(new char[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
string sinkName;
string levelToken;
if (tokens.Length == 1)
{
// Single token - assume it's a level for console sink
sinkName = "console";
levelToken = tokens[0];
}
else if (tokens.Length == 2)
{
// Two tokens - first is sink, second is level
sinkName = tokens[0].ToLowerInvariant();
levelToken = tokens[1];
}
else
{
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]");
return;
}
// Parse the level using the same logic as before
LogEventLevel level;
if (int.TryParse(levelToken, out var levelInt))
{ {
if (levelInt < 0 || levelInt > 5) if (levelInt < 0 || levelInt > 5)
{ {
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level. If using a number, value must be between 0-5"); CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level. If using a number, value must be between 0-5");
return;
}
SetDebugLevel((uint)levelInt);
return; return;
} }
if (!_logLevels.TryGetValue((uint)levelInt, out level)) if (Enum.TryParse<LogEventLevel>(levelString, true, out var levelEnum))
{ {
level = LogEventLevel.Information; SetDebugLevel(levelEnum);
CrestronConsole.ConsoleCommandResponse($"{levelInt} not valid. Setting level to {level}");
}
}
else if (Enum.TryParse(levelToken, true, out level))
{
// Successfully parsed as LogEventLevel enum
}
else
{
CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelToken} to valid log level");
return; return;
} }
// Set the level for the specified sink CrestronConsole.ConsoleCommandResponse($"Error: Unable to parse {levelString} to valid log level");
switch (sinkName)
{
case "console":
SetDebugLevel(level);
break;
case "errorlog":
SetErrorLogMinimumDebugLevel(level);
break;
case "file":
SetFileMinimumDebugLevel(level);
break;
case "all":
SetDebugLevel(level);
SetErrorLogMinimumDebugLevel(level);
SetFileMinimumDebugLevel(level);
break;
default:
CrestronConsole.ConsoleCommandResponse($"Error: Unknown sink '{sinkName}'. Valid sinks: console, errorlog, file");
break;
}
} }
catch catch
{ {
CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [sink] [level]"); CrestronConsole.ConsoleCommandResponse("Usage: appdebug:P [0-5]");
} }
} }
@@ -481,10 +416,10 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetDebugLevel(LogEventLevel level) public static void SetDebugLevel(LogEventLevel level)
{ {
_consoleLogLevelSwitch.MinimumLevel = level; _consoleLoggingLevelSwitch.MinimumLevel = level;
CrestronConsole.ConsoleCommandResponse("[Application {0}] Debug level set to {1}\r\n", CrestronConsole.ConsoleCommandResponse("[Application {0}], Debug level set to {1}\r\n",
InitialParametersClass.ApplicationNumber, _consoleLogLevelSwitch.MinimumLevel); InitialParametersClass.ApplicationNumber, _consoleLoggingLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}"); CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
@@ -501,14 +436,14 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetWebSocketMinimumDebugLevel(LogEventLevel level) public static void SetWebSocketMinimumDebugLevel(LogEventLevel level)
{ {
_websocketLogLevelSwitch.MinimumLevel = level; _websocketLoggingLevelSwitch.MinimumLevel = level;
var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level); var err = CrestronDataStoreStatic.SetLocalUintValue(WebSocketLevelStoreKey, (uint)level);
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err); LogMessage(LogEventLevel.Information, "Error saving websocket debug level setting: {erro}", err);
LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLogLevelSwitch.MinimumLevel); LogMessage(LogEventLevel.Information, "Websocket debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>
@@ -518,17 +453,12 @@ namespace PepperDash.Core
{ {
_errorLogLevelSwitch.MinimumLevel = level; _errorLogLevelSwitch.MinimumLevel = level;
CrestronConsole.ConsoleCommandResponse("[Application {0}] Error log level set to {1}\r\n", var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
InitialParametersClass.ApplicationNumber, _errorLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(ErrorLogLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
CrestronConsole.PrintLine($"Error saving error log debug level setting: {err}"); LogMessage(LogEventLevel.Information, "Error saving Error Log debug level setting: {error}", err);
LogMessage(LogEventLevel.Information, "Error log debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>
@@ -536,19 +466,14 @@ namespace PepperDash.Core
/// </summary> /// </summary>
public static void SetFileMinimumDebugLevel(LogEventLevel level) public static void SetFileMinimumDebugLevel(LogEventLevel level)
{ {
_fileLogLevelSwitch.MinimumLevel = level; _errorLogLevelSwitch.MinimumLevel = level;
CrestronConsole.ConsoleCommandResponse("[Application {0}] File log level set to {1}\r\n", var err = CrestronDataStoreStatic.SetLocalUintValue(ErrorLogLevelStoreKey, (uint)level);
InitialParametersClass.ApplicationNumber, _fileLogLevelSwitch.MinimumLevel);
CrestronConsole.ConsoleCommandResponse($"Storing level {level}:{(int)level}");
var err = CrestronDataStoreStatic.SetLocalIntValue(FileLevelStoreKey, (int)level);
CrestronConsole.ConsoleCommandResponse($"Store result: {err}:{(int)level}");
if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS) if (err != CrestronDataStore.CDS_ERROR.CDS_SUCCESS)
CrestronConsole.PrintLine($"Error saving file debug level setting: {err}"); LogMessage(LogEventLevel.Information, "Error saving File debug level setting: {error}", err);
LogMessage(LogEventLevel.Information, "File debug level set to {0}", _websocketLoggingLevelSwitch.MinimumLevel);
} }
/// <summary> /// <summary>
@@ -1081,6 +1006,9 @@ namespace PepperDash.Core
/// Logs to Console when at-level, and all messages to error log /// Logs to Console when at-level, and all messages to error log
/// </summary> /// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] [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, public static void Console(uint level, ErrorLogLevel errorLogLevel,
string format, params object[] items) string format, params object[] items)
{ {
@@ -1093,6 +1021,9 @@ namespace PepperDash.Core
/// it will only be written to the log. /// it will only be written to the log.
/// </summary> /// </summary>
[Obsolete("Use LogMessage methods, Will be removed in 2.2.0 and later versions")] [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) public static void ConsoleWithLog(uint level, string format, params object[] items)
{ {
LogMessage(level, format, items); LogMessage(level, format, items);

View File

@@ -43,7 +43,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" /> <PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
<PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.90" /> <PackageReference Include="Crestron.SimplSharp.SDK.Library" Version="2.21.157" />
<PackageReference Include="Serilog" Version="3.1.1" /> <PackageReference Include="Serilog" Version="3.1.1" />
<PackageReference Include="Serilog.Expressions" Version="4.0.0" /> <PackageReference Include="Serilog.Expressions" Version="4.0.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" /> <PackageReference Include="Serilog.Formatting.Compact" Version="2.0.0" />

View File

@@ -33,7 +33,7 @@ namespace PepperDash.Essentials.Core
ICec Port; ICec Port;
public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc, public CecPortController(string key, Func<EssentialsControlPropertiesConfig, ICec> postActivationFunc,
EssentialsControlPropertiesConfig config) : base(key) EssentialsControlPropertiesConfig config):base(key)
{ {
StreamDebugging = new CommunicationStreamDebugging(key); StreamDebugging = new CommunicationStreamDebugging(key);
@@ -58,7 +58,7 @@ namespace PepperDash.Essentials.Core
if (args.EventId == CecEventIds.CecMessageReceivedEventId) if (args.EventId == CecEventIds.CecMessageReceivedEventId)
OnDataReceived(cecDevice.Received.StringValue); OnDataReceived(cecDevice.Received.StringValue);
else if (args.EventId == CecEventIds.ErrorFeedbackEventId) else if (args.EventId == CecEventIds.ErrorFeedbackEventId)
if (cecDevice.ErrorFeedback.BoolValue) if(cecDevice.ErrorFeedback.BoolValue)
Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error"); Debug.LogMessage(LogEventLevel.Verbose, this, "CEC NAK Error");
} }
@@ -68,13 +68,15 @@ namespace PepperDash.Essentials.Core
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(s); 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)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
this.PrintReceivedText(s); if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
} }
} }
@@ -88,7 +90,8 @@ namespace PepperDash.Essentials.Core
{ {
if (Port == null) if (Port == null)
return; 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; Port.StreamCec.Send.StringValue = text;
} }
@@ -100,7 +103,7 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); 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)); Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} bytes: '{1}'", bytes.Length, ComTextHelper.GetEscapedText(bytes));
Port.StreamCec.Send.StringValue = text; Port.StreamCec.Send.StringValue = text;
} }

View File

@@ -1,11 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.GeneralIO;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using Serilog.Events; using Serilog.Events;
@@ -21,14 +22,7 @@ namespace PepperDash.Essentials.Core
/// </summary> /// </summary>
public CommunicationStreamDebugging StreamDebugging { get; private set; } public CommunicationStreamDebugging StreamDebugging { get; private set; }
/// <summary>
/// Event fired when bytes are received
/// </summary>
public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived; public event EventHandler<GenericCommMethodReceiveBytesArgs> BytesReceived;
/// <summary>
/// Event fired when text is received
/// </summary>
public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived; public event EventHandler<GenericCommMethodReceiveTextArgs> TextReceived;
/// <summary> /// <summary>
@@ -39,13 +33,6 @@ namespace PepperDash.Essentials.Core
ComPort Port; ComPort Port;
ComPort.ComPortSpec Spec; 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, public ComPortController(string key, Func<EssentialsControlPropertiesConfig, ComPort> postActivationFunc,
ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key) ComPort.ComPortSpec spec, EssentialsControlPropertiesConfig config) : base(key)
{ {
@@ -61,12 +48,6 @@ namespace PepperDash.Essentials.Core
}); });
} }
/// <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) public ComPortController(string key, ComPort port, ComPort.ComPortSpec spec)
: base(key) : base(key)
{ {
@@ -87,36 +68,28 @@ namespace PepperDash.Essentials.Core
{ {
if (Port == null) if (Port == null)
{ {
this.LogInformation($"Configured {Port.Parent.GetType().Name}-comport-{Port.ID} for {Key} does not exist."); Debug.LogMessage(LogEventLevel.Information, this, "Configured com Port for this device does not exist.");
return; return;
} }
if (Port.Parent is CrestronControlSystem)
if (Port.Parent is CrestronControlSystem || Port.Parent is CenIoCom102)
{ {
var result = Port.Register(); var result = Port.Register();
if (result != eDeviceRegistrationUnRegistrationResponse.Success) if (result != eDeviceRegistrationUnRegistrationResponse.Success)
{ {
this.LogError($"Cannot register {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})"); Debug.LogMessage(LogEventLevel.Information, this, "ERROR: Cannot register Com port: {0}", result);
return; return; // false
} }
this.LogInformation($"Successfully registered {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {result})");
} }
var specResult = Port.SetComPortSpec(Spec); var specResult = Port.SetComPortSpec(Spec);
if (specResult != 0) if (specResult != 0)
{ {
this.LogError($"Cannot set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})"); Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Cannot set comspec");
return; return;
} }
this.LogInformation($"Successfully set comspec for {Key} using {Port.Parent.GetType().Name}-comport-{Port.ID} (result == {specResult})");
Port.SerialDataReceived += Port_SerialDataReceived; Port.SerialDataReceived += Port_SerialDataReceived;
} }
/// <summary>
/// Destructor
/// </summary>
~ComPortController() ~ComPortController()
{ {
Port.SerialDataReceived -= Port_SerialDataReceived; Port.SerialDataReceived -= Port_SerialDataReceived;
@@ -135,19 +108,21 @@ namespace PepperDash.Essentials.Core
if (bytesHandler != null) if (bytesHandler != null)
{ {
var bytes = Encoding.GetEncoding(28591).GetBytes(s); 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)); bytesHandler(this, new GenericCommMethodReceiveBytesArgs(bytes));
eventSubscribed = true; eventSubscribed = true;
} }
var textHandler = TextReceived; var textHandler = TextReceived;
if (textHandler != null) if (textHandler != null)
{ {
this.PrintReceivedText(s); if (StreamDebugging.RxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Received: '{0}'", s);
textHandler(this, new GenericCommMethodReceiveTextArgs(s)); textHandler(this, new GenericCommMethodReceiveTextArgs(s));
eventSubscribed = true; 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> /// <summary>
@@ -169,7 +144,8 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
this.PrintSentText(text); if (StreamDebugging.TxStreamDebuggingIsEnabled)
Debug.LogMessage(LogEventLevel.Information, this, "Sending {0} characters of text: '{1}'", text.Length, text);
Port.Send(text); Port.Send(text);
} }
@@ -181,7 +157,8 @@ namespace PepperDash.Essentials.Core
if (Port == null) if (Port == null)
return; return;
var text = Encoding.GetEncoding(28591).GetString(bytes, 0, bytes.Length); 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); Port.Send(text);
} }

View File

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

View File

@@ -127,32 +127,19 @@ namespace PepperDash.Essentials.Core.Config
} }
else else
{ {
var parsedConfig = JObject.Parse(fs.ReadToEnd()); var doubleObj = JObject.Parse(fs.ReadToEnd());
ConfigObject = PortalConfigReader.MergeConfigs(doubleObj).ToObject<EssentialsConfig>();
// 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;
}
// Extract SystemUrl and TemplateUrl into final config output // 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

@@ -16,21 +16,13 @@ namespace PepperDash.Essentials.Core.Config
/// </summary> /// </summary>
public class EssentialsConfig : BasicConfig public class EssentialsConfig : BasicConfig
{ {
/// <summary>
/// Gets or sets the SystemUrl
/// </summary>
[JsonProperty("system_url")] [JsonProperty("system_url")]
public string SystemUrl { get; set; } public string SystemUrl { get; set; }
/// <summary>
/// Gets or sets the TemplateUrl
/// </summary>
[JsonProperty("template_url")] [JsonProperty("template_url")]
public string TemplateUrl { get; set; } public string TemplateUrl { get; set; }
/// <summary>
/// Gets the SystemUuid extracted from the SystemUrl
/// </summary>
[JsonProperty("systemUuid")] [JsonProperty("systemUuid")]
public string SystemUuid public string SystemUuid
{ {
@@ -53,9 +45,6 @@ namespace PepperDash.Essentials.Core.Config
} }
} }
/// <summary>
/// Gets the TemplateUuid extracted from the TemplateUrl
/// </summary>
[JsonProperty("templateUuid")] [JsonProperty("templateUuid")]
public string TemplateUuid public string TemplateUuid
{ {
@@ -78,20 +67,13 @@ namespace PepperDash.Essentials.Core.Config
} }
} }
[JsonProperty("rooms")]
/// <summary> /// <summary>
/// Gets or sets the Rooms /// Gets or sets the Rooms
/// </summary> /// </summary>
[JsonProperty("rooms")]
public List<DeviceConfig> Rooms { get; set; } 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() public EssentialsConfig()
: base() : base()
{ {
@@ -99,50 +81,6 @@ namespace PepperDash.Essentials.Core.Config
} }
} }
/// <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> /// <summary>
/// Represents a SystemTemplateConfigs /// Represents a SystemTemplateConfigs
/// </summary> /// </summary>
@@ -153,9 +91,6 @@ namespace PepperDash.Essentials.Core.Config
/// </summary> /// </summary>
public EssentialsConfig System { get; set; } public EssentialsConfig System { get; set; }
/// <summary>
/// Gets or sets the Template
/// </summary>
public EssentialsConfig Template { get; set; } public EssentialsConfig Template { get; set; }
} }
} }

View File

@@ -2,54 +2,59 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
[Description("Wrapper class for Digital Input")]
/// <summary> /// <summary>
/// Represents a GenericDigitalInputDevice /// Represents a GenericDigitalInputDevice
/// </summary> /// </summary>
/// [Description("Wrapper class for Digital Input")] public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput
public class GenericDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IHasFeedback
{ {
private DigitalInput inputPort; /// <summary>
/// Gets or sets the InputPort
/// </summary>
public DigitalInput InputPort { get; private set; }
/// <summary> /// <summary>
/// Gets or sets the InputStateFeedback /// Gets or sets the InputStateFeedback
/// </summary> /// </summary>
public BoolFeedback InputStateFeedback { get; private set; } public BoolFeedback InputStateFeedback { get; private set; }
/// <inheritdoc /> Func<bool> InputStateFeedbackFunc
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>(); {
get
{
return () => InputPort.State;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericDigitalInputDevice"/> class.
/// </summary>
/// <param name="key">key for device</param>
/// <param name="name">name for device</param>
/// <param name="postActivationFunc">function to call after activation. Should return the DigitalInput</param>
/// <param name="config">config for device</param>
public GenericDigitalInputDevice(string key, string name, Func<IOPortConfig, DigitalInput> postActivationFunc, public GenericDigitalInputDevice(string key, string name, Func<IOPortConfig, DigitalInput> postActivationFunc,
IOPortConfig config) IOPortConfig config)
: base(key, name) : base(key, name)
{ {
InputStateFeedback = new BoolFeedback("inputState", () => inputPort.State); InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
inputPort = postActivationFunc(config); InputPort = postActivationFunc(config);
inputPort.Register(); InputPort.Register();
InputPort.StateChange += InputPort_StateChange;
inputPort.StateChange += InputPort_StateChange;
}); });
} }
@@ -66,31 +71,41 @@ namespace PepperDash.Essentials.Core.CrestronIO
private static DigitalInput GetDigitalInput(IOPortConfig dc) private static DigitalInput GetDigitalInput(IOPortConfig dc)
{ {
IDigitalInputPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsDigitalInput) if (!Global.ControlSystem.SupportsDigitalInput)
{ {
Debug.LogError("GetDigitalInput: Processor does not support Digital Inputs"); Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Processor does not support Digital Inputs");
return null; return null;
} }
ioPortDevice = Global.ControlSystem;
return Global.ControlSystem.DigitalInputPorts[dc.PortNumber];
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IDigitalInputPorts ioPortDevice))
{ {
Debug.LogError("GetDigitalInput: Device {key} is not a valid device", dc.PortDeviceKey); var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IDigitalInputPorts;
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device '0' is not a valid IDigitalInputPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfDigitalInputPorts) if (dc.PortNumber > ioPortDevice.NumberOfDigitalInputPorts)
{ {
Debug.LogError("GetDigitalInput: Device {key} does not contain a digital input port {port}", dc.PortDeviceKey, dc.PortNumber); Debug.LogMessage(LogEventLevel.Information, "GetDigitalInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null;
} }
return ioPortDevice.DigitalInputPorts[dc.PortNumber]; return ioPortDevice.DigitalInputPorts[dc.PortNumber];
} }
#endregion #endregion
@@ -116,20 +131,20 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X")); Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]); InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);
} }
catch (Exception e) catch (Exception e)
{ {
this.LogError("Unable to link device {key}. {message}", Key, e.Message); Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key);
this.LogDebug(e, "Stack Trace: "); Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
} }
} }
@@ -138,22 +153,22 @@ namespace PepperDash.Essentials.Core.CrestronIO
#region Factory #region Factory
/// <summary> /// <summary>
/// Factory for creating GenericDigitalInputDevice devices /// Represents a GenericDigitalInputDeviceFactory
/// </summary> /// </summary>
public class GenericDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericDigitalInputDevice> public class GenericDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericDigitalInputDevice>
{ {
/// <summary>
/// Constructor for GenericDigitalInputDeviceFactory
/// </summary>
public GenericDigitalInputDeviceFactory() public GenericDigitalInputDeviceFactory()
{ {
TypeNames = new List<string>() { "digitalinput" }; TypeNames = new List<string>() { "digitalinput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogDebug("Factory Attempting to create new Generic Digital Input Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Digital Input Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,64 +2,66 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json;
using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput, IHasFeedback public class GenericVersiportAnalogInputDevice : EssentialsBridgeableDevice, IAnalogInput
{ {
private Versiport inputPort; public Versiport InputPort { get; private set; }
/// <inheritdoc />
public IntFeedback InputValueFeedback { get; private set; } public IntFeedback InputValueFeedback { get; private set; }
/// <summary>
/// Get the InputMinimumChangeFeedback
/// </summary>
/// <remarks>
/// Updates when the analog input minimum change value changes
/// </remarks>
public IntFeedback InputMinimumChangeFeedback { get; private set; } public IntFeedback InputMinimumChangeFeedback { get; private set; }
/// <inheritdoc /> Func<int> InputValueFeedbackFunc
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>(); {
get
{
return () => InputPort.AnalogIn;
}
}
Func<int> InputMinimumChangeFeedbackFunc
{
get { return () => InputPort.AnalogMinChange; }
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportAnalogInputDevice"/> class.
/// </summary>
/// <param name="key">key for the device</param>
/// <param name="name">name for the device</param>
/// <param name="postActivationFunc">function to call after activation</param>
/// <param name="config">IO port configuration</param>
public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportAnalogInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
InputValueFeedback = new IntFeedback("inputValue", () => inputPort.AnalogIn); InputValueFeedback = new IntFeedback(InputValueFeedbackFunc);
InputMinimumChangeFeedback = new IntFeedback("inputMinimumChange", () => inputPort.AnalogMinChange); InputMinimumChangeFeedback = new IntFeedback(InputMinimumChangeFeedbackFunc);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
inputPort = postActivationFunc(config); InputPort = postActivationFunc(config);
inputPort.Register(); InputPort.Register();
inputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput); InputPort.SetVersiportConfiguration(eVersiportConfiguration.AnalogInput);
inputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655); InputPort.AnalogMinChange = (ushort)(config.MinimumChange > 0 ? config.MinimumChange : 655);
if (config.DisablePullUpResistor) if (config.DisablePullUpResistor)
inputPort.DisablePullUpResistor = true; InputPort.DisablePullUpResistor = true;
inputPort.VersiportChange += InputPort_VersiportChange; InputPort.VersiportChange += InputPort_VersiportChange;
Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportAnalogInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
this.LogDebug("Created GenericVersiportAnalogInputDevice on port {port}. DisablePullUpResistor: {pullUpResistorDisabled}", config.PortNumber, inputPort.DisablePullUpResistor);
}); });
} }
@@ -68,16 +70,19 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// Set minimum voltage change for device to update voltage changed method /// Set minimum voltage change for device to update voltage changed method
/// </summary> /// </summary>
/// <param name="value">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details</param> /// <param name="value">valid values range from 0 - 65535, representing the full 100% range of the processor voltage source. Check processor documentation for details</param>
/// <summary>
/// SetMinimumChange method
/// </summary>
public void SetMinimumChange(ushort value) public void SetMinimumChange(ushort value)
{ {
inputPort.AnalogMinChange = value; InputPort.AnalogMinChange = value;
} }
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
this.LogDebug("Versiport change: {event}", args.Event); Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
if (args.Event == eVersiportEvent.AnalogInChange) if(args.Event == eVersiportEvent.AnalogInChange)
InputValueFeedback.FireUpdate(); InputValueFeedback.FireUpdate();
if (args.Event == eVersiportEvent.AnalogMinChangeChange) if (args.Event == eVersiportEvent.AnalogMinChangeChange)
InputMinimumChangeFeedback.FireUpdate(); InputMinimumChangeFeedback.FireUpdate();
@@ -86,6 +91,9 @@ namespace PepperDash.Essentials.Core.CrestronIO
#region Bridge Linking #region Bridge Linking
/// <summary>
/// LinkToApi method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge) public override void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
{ {
@@ -102,12 +110,12 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
this.LogDebug("Linking to Trilist '{trilistId}'", trilist.ID.ToString("X")); Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]); InputValueFeedback.LinkInputSig(trilist.UShortInput[joinMap.InputValue.JoinNumber]);
@@ -117,8 +125,8 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
catch (Exception e) catch (Exception e)
{ {
this.LogError("Unable to link device {key}: {message}", Key, e.Message); Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key);
this.LogDebug(e, "Stack Trace: "); Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
} }
trilist.OnlineStatusChange += (d, args) => trilist.OnlineStatusChange += (d, args) =>
@@ -130,6 +138,11 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
void trilist_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{
throw new NotImplementedException();
}
#endregion #endregion
@@ -138,55 +151,70 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalInput(IOPortConfig dc) public static Versiport GetVersiportDigitalInput(IOPortConfig dc)
{ {
IIOPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) if (!Global.ControlSystem.SupportsVersiport)
{ {
Debug.LogError("GetVersiportAnalogInput: Processor does not support Versiports"); Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Processor does not support Versiports");
return null; return null;
} }
return Global.ControlSystem.VersiPorts[dc.PortNumber]; ioPortDevice = Global.ControlSystem;
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
Debug.LogError("GetVersiportAnalogInput: Device {key} is not a valid device", dc.PortDeviceKey); var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{ {
Debug.LogError("GetVersiportAnalogInput: Device {key} does not contain a port {port}", dc.PortDeviceKey, dc.PortNumber); Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null; return null;
} }
if (!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput) if(!ioPortDevice.VersiPorts[dc.PortNumber].SupportsAnalogInput)
{ {
Debug.LogError("GetVersiportAnalogInput: Device {key} does not support AnalogInput on port {port}", dc.PortDeviceKey, dc.PortNumber); Debug.LogMessage(LogEventLevel.Information, "GetVersiportAnalogInput: Device {0} does not support AnalogInput on port {1}", dc.PortDeviceKey, dc.PortNumber);
return null; return null;
} }
return ioPortDevice.VersiPorts[dc.PortNumber]; return ioPortDevice.VersiPorts[dc.PortNumber];
} }
} }
/// <summary> /// <summary>
/// Factory for creating GenericVersiportAnalogInputDevice devices /// Represents a GenericVersiportAbalogInputDeviceFactory
/// </summary> /// </summary>
public class GenericVersiportAnalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice> public class GenericVersiportAbalogInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportAnalogInputDevice>
{ {
/// <summary> public GenericVersiportAbalogInputDeviceFactory()
/// Constructor for GenericVersiportAnalogInputDeviceFactory
/// </summary>
public GenericVersiportAnalogInputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportanaloginput" }; TypeNames = new List<string>() { "versiportanaloginput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogDebug("Factory Attempting to create new Generic Versiport Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,82 +2,78 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json;
using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
{ {
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider, IHasFeedback public class GenericVersiportDigitalInputDevice : EssentialsBridgeableDevice, IDigitalInput, IPartitionStateProvider
{ {
private Versiport inputPort; public Versiport InputPort { get; private set; }
/// <summary>
/// Gets or sets the InputStateFeedback
/// </summary>
public BoolFeedback InputStateFeedback { get; private set; } public BoolFeedback InputStateFeedback { get; private set; }
/// <inheritdoc /> Func<bool> InputStateFeedbackFunc
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>(); {
get
{
return () => InputPort.DigitalIn;
}
}
/// <summary> /// <summary>
/// Gets or sets the PartitionPresentFeedback /// Gets or sets the PartitionPresentFeedback
/// </summary> /// </summary>
public BoolFeedback PartitionPresentFeedback { get; } public BoolFeedback PartitionPresentFeedback { get; }
/// <summary> public bool PartitionPresent => !InputStateFeedbackFunc();
/// Get partition state
/// </summary>
public bool PartitionPresent => !inputPort.DigitalIn;
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportDigitalInputDevice"/> class.
/// </summary>
/// <param name="key">key for device</param>
/// <param name="name">name for device</param>
/// <param name="postActivationFunc">function to call after activation. Should return the Versiport</param>
/// <param name="config">config for device</param>
public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportDigitalInputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
InputStateFeedback = new BoolFeedback("inputState", () => inputPort.DigitalIn); InputStateFeedback = new BoolFeedback(InputStateFeedbackFunc);
PartitionPresentFeedback = new BoolFeedback("partitionPresent", () => !inputPort.DigitalIn); PartitionPresentFeedback = new BoolFeedback(() => !InputStateFeedbackFunc());
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
inputPort = postActivationFunc(config); InputPort = postActivationFunc(config);
inputPort.Register(); InputPort.Register();
inputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput); InputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalInput);
if (config.DisablePullUpResistor) if (config.DisablePullUpResistor)
inputPort.DisablePullUpResistor = true; InputPort.DisablePullUpResistor = true;
inputPort.VersiportChange += InputPort_VersiportChange; InputPort.VersiportChange += InputPort_VersiportChange;
InputStateFeedback.FireUpdate(); InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
this.LogDebug("Created GenericVersiportDigitalInputDevice for port {port}. DisablePullUpResistor: {pullUpResistorDisable}", config.PortNumber, inputPort.DisablePullUpResistor); Debug.LogMessage(LogEventLevel.Debug, this, "Created GenericVersiportDigitalInputDevice on port '{0}'. DisablePullUpResistor: '{1}'", config.PortNumber, InputPort.DisablePullUpResistor);
}); });
Feedbacks.Add(InputStateFeedback);
Feedbacks.Add(PartitionPresentFeedback);
} }
void InputPort_VersiportChange(Versiport port, VersiportEventArgs args) void InputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
this.LogDebug("Versiport change: {0}", args.Event); Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
if (args.Event == eVersiportEvent.DigitalInChange) if(args.Event == eVersiportEvent.DigitalInChange)
{ {
InputStateFeedback.FireUpdate(); InputStateFeedback.FireUpdate();
PartitionPresentFeedback.FireUpdate(); PartitionPresentFeedback.FireUpdate();
@@ -106,20 +102,20 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X")); Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]); InputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.InputState.JoinNumber]);
} }
catch (Exception e) catch (Exception e)
{ {
this.LogError("Unable to link device {key}. Input is null. {message}", Key, e.Message); Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key);
this.LogDebug(e, "Stack Trace: "); Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
} }
} }
@@ -131,50 +127,63 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalInput(IOPortConfig dc) public static Versiport GetVersiportDigitalInput(IOPortConfig dc)
{ {
IIOPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) if (!Global.ControlSystem.SupportsVersiport)
{ {
Debug.LogError("GetVersiportDigitalInput: Processor does not support Versiports"); Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Processor does not support Versiports");
return null; return null;
} }
return Global.ControlSystem.VersiPorts[dc.PortNumber]; ioPortDevice = Global.ControlSystem;
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
Debug.LogError("GetVersiportDigitalInput: Device {key} is not a valid device", dc.PortDeviceKey); var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device '0' is not a valid IIOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{ {
Debug.LogError("GetVersiportDigitalInput: Device {key} does not contain versiport {port}", dc.PortDeviceKey, dc.PortNumber); Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalInput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null;
} }
return ioPortDevice.VersiPorts[dc.PortNumber]; return ioPortDevice.VersiPorts[dc.PortNumber];
} }
} }
/// <summary> /// <summary>
/// Factory class for GenericVersiportDigitalInputDevice /// Represents a GenericVersiportDigitalInputDeviceFactory
/// </summary> /// </summary>
public class GenericVersiportDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice> public class GenericVersiportDigitalInputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>
{ {
/// <summary>
/// Constructor for GenericVersiportDigitalInputDeviceFactory
/// </summary>
public GenericVersiportDigitalInputDeviceFactory() public GenericVersiportDigitalInputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportinput" }; TypeNames = new List<string>() { "versiportinput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogDebug("Factory Attempting to create new Generic Versiport Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -2,13 +2,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Crestron.SimplSharpPro; using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using Newtonsoft.Json;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core.CrestronIO namespace PepperDash.Essentials.Core.CrestronIO
@@ -16,52 +21,52 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// <summary> /// <summary>
/// Represents a generic digital input deviced tied to a versiport /// Represents a generic digital input deviced tied to a versiport
/// </summary> /// </summary>
public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput, IHasFeedback public class GenericVersiportDigitalOutputDevice : EssentialsBridgeableDevice, IDigitalOutput
{ {
private Versiport outputPort; public Versiport OutputPort { get; private set; }
/// <summary>
/// Gets or sets the OutputStateFeedback
/// </summary>
public BoolFeedback OutputStateFeedback { get; private set; } public BoolFeedback OutputStateFeedback { get; private set; }
/// <inheritdoc /> Func<bool> OutputStateFeedbackFunc
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>(); {
get
{
return () => OutputPort.DigitalOut;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="GenericVersiportDigitalOutputDevice"/> class.
/// </summary>
public GenericVersiportDigitalOutputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) : public GenericVersiportDigitalOutputDevice(string key, string name, Func<IOPortConfig, Versiport> postActivationFunc, IOPortConfig config) :
base(key, name) base(key, name)
{ {
OutputStateFeedback = new BoolFeedback("outputState", () => outputPort.DigitalOut); OutputStateFeedback = new BoolFeedback(OutputStateFeedbackFunc);
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
outputPort = postActivationFunc(config); OutputPort = postActivationFunc(config);
outputPort.Register(); OutputPort.Register();
if (!outputPort.SupportsDigitalOutput) if (!OutputPort.SupportsDigitalOutput)
{ {
this.LogError("Device does not support configuration as a Digital Output"); Debug.LogMessage(LogEventLevel.Information, this, "Device does not support configuration as a Digital Output");
return; return;
} }
outputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput); OutputPort.SetVersiportConfiguration(eVersiportConfiguration.DigitalOutput);
outputPort.VersiportChange += OutputPort_VersiportChange; OutputPort.VersiportChange += OutputPort_VersiportChange;
}); });
} }
void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args) void OutputPort_VersiportChange(Versiport port, VersiportEventArgs args)
{ {
this.LogDebug("Versiport change: {event}", args.Event); Debug.LogMessage(LogEventLevel.Debug, this, "Versiport change: {0}", args.Event);
if (args.Event == eVersiportEvent.DigitalOutChange) if(args.Event == eVersiportEvent.DigitalOutChange)
OutputStateFeedback.FireUpdate(); OutputStateFeedback.FireUpdate();
} }
@@ -69,15 +74,23 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// Set value of the versiport digital output /// Set value of the versiport digital output
/// </summary> /// </summary>
/// <param name="state">value to set the output to</param> /// <param name="state">value to set the output to</param>
/// <summary>
/// SetOutput method
/// </summary>
public void SetOutput(bool state) public void SetOutput(bool state)
{ {
if (!outputPort.SupportsDigitalOutput) if (OutputPort.SupportsDigitalOutput)
{ {
this.LogError("Versiport does not support Digital Output Mode"); Debug.LogMessage(LogEventLevel.Information, this, "Passed the Check");
return;
OutputPort.DigitalOut = state;
}
else
{
Debug.LogMessage(LogEventLevel.Information, this, "Versiport does not support Digital Output Mode");
} }
outputPort.DigitalOut = state;
} }
#region Bridge Linking #region Bridge Linking
@@ -101,12 +114,12 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
else else
{ {
this.LogWarning("Please update config to use 'eiscapiadvanced' to get all join map features for this device."); Debug.LogMessage(LogEventLevel.Information, this, "Please update config to use 'eiscapiadvanced' to get all join map features for this device.");
} }
try try
{ {
this.LogDebug("Linking to Trilist '{0}'", trilist.ID.ToString("X")); Debug.LogMessage(LogEventLevel.Debug, this, "Linking to Trilist '{0}'", trilist.ID.ToString("X"));
// Link feedback for input state // Link feedback for input state
OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]); OutputStateFeedback.LinkInputSig(trilist.BooleanInput[joinMap.OutputState.JoinNumber]);
@@ -114,8 +127,8 @@ namespace PepperDash.Essentials.Core.CrestronIO
} }
catch (Exception e) catch (Exception e)
{ {
this.LogError("Unable to link device: {message}", e.Message); Debug.LogMessage(LogEventLevel.Debug, this, "Unable to link device '{0}'. Input is null", Key);
this.LogDebug(e, "Stack Trace: "); Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
} }
} }
@@ -127,28 +140,41 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public static Versiport GetVersiportDigitalOutput(IOPortConfig dc) public static Versiport GetVersiportDigitalOutput(IOPortConfig dc)
{ {
IIOPorts ioPortDevice;
if (dc.PortDeviceKey.Equals("processor")) if (dc.PortDeviceKey.Equals("processor"))
{ {
if (!Global.ControlSystem.SupportsVersiport) if (!Global.ControlSystem.SupportsVersiport)
{ {
Debug.LogError("GetVersiportDigitalOutput: Processor does not support Versiports"); Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Processor does not support Versiports");
return null; return null;
} }
return Global.ControlSystem.VersiPorts[dc.PortNumber]; ioPortDevice = Global.ControlSystem;
} }
else
if (!(DeviceManager.GetDeviceForKey(dc.PortDeviceKey) is IIOPorts ioPortDevice))
{ {
Debug.LogError("GetVersiportDigitalOutput: Device {key} is not a valid device", dc.PortDeviceKey); var ioPortDev = DeviceManager.GetDeviceForKey(dc.PortDeviceKey) as IIOPorts;
if (ioPortDev == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} is not a valid device", dc.PortDeviceKey);
return null;
}
ioPortDevice = ioPortDev;
}
if (ioPortDevice == null)
{
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device '0' is not a valid IOPorts Device", dc.PortDeviceKey);
return null; return null;
} }
if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts) if (dc.PortNumber > ioPortDevice.NumberOfVersiPorts)
{ {
Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOutput: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber); Debug.LogMessage(LogEventLevel.Information, "GetVersiportDigitalOuptut: Device {0} does not contain a port {1}", dc.PortDeviceKey, dc.PortNumber);
return null;
} }
return ioPortDevice.VersiPorts[dc.PortNumber]; var port = ioPortDevice.VersiPorts[dc.PortNumber];
return port;
} }
} }
@@ -158,18 +184,18 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice> public class GenericVersiportDigitalOutputDeviceFactory : EssentialsDeviceFactory<GenericVersiportDigitalInputDevice>
{ {
/// <summary>
/// Initialize a new instance of the <see cref="GenericVersiportDigitalOutputDeviceFactory"/> class.
/// </summary>
public GenericVersiportDigitalOutputDeviceFactory() public GenericVersiportDigitalOutputDeviceFactory()
{ {
TypeNames = new List<string>() { "versiportoutput" }; TypeNames = new List<string>() { "versiportoutput" };
} }
/// <summary>
/// BuildDevice method
/// </summary>
/// <inheritdoc /> /// <inheritdoc />
public override EssentialsDevice BuildDevice(DeviceConfig dc) public override EssentialsDevice BuildDevice(DeviceConfig dc)
{ {
Debug.LogDebug("Factory Attempting to create new Generic Versiport Device"); Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Versiport Device");
var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString()); var props = JsonConvert.DeserializeObject<IOPortConfig>(dc.Properties.ToString());

View File

@@ -12,12 +12,6 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public interface IAnalogInput public interface IAnalogInput
{ {
/// <summary>
/// Get the InputValueFeedback.
/// </summary>
/// <remarks>
/// Updates when the analog input value changes
/// </remarks>
IntFeedback InputValueFeedback { get; } IntFeedback InputValueFeedback { get; }
} }
} }

View File

@@ -14,28 +14,25 @@ namespace PepperDash.Essentials.Core.CrestronIO
/// </summary> /// </summary>
public class IOPortConfig public class IOPortConfig
{ {
[JsonProperty("portDeviceKey")]
/// <summary> /// <summary>
/// Gets or sets the PortDeviceKey /// Gets or sets the PortDeviceKey
/// </summary> /// </summary>
[JsonProperty("portDeviceKey")]
public string PortDeviceKey { get; set; } public string PortDeviceKey { get; set; }
[JsonProperty("portNumber")]
/// <summary> /// <summary>
/// Gets or sets the PortNumber /// Gets or sets the PortNumber
/// </summary> /// </summary>
[JsonProperty("portNumber")]
public uint PortNumber { get; set; } public uint PortNumber { get; set; }
[JsonProperty("disablePullUpResistor")]
/// <summary> /// <summary>
/// Gets or sets the DisablePullUpResistor /// Gets or sets the DisablePullUpResistor
/// </summary> /// </summary>
[JsonProperty("disablePullUpResistor")]
public bool DisablePullUpResistor { get; set; } public bool DisablePullUpResistor { get; set; }
[JsonProperty("minimumChange")]
/// <summary> /// <summary>
/// Gets or sets the MinimumChange /// Gets or sets the MinimumChange
/// </summary> /// </summary>
[JsonProperty("minimumChange")]
public int MinimumChange { get; set; } public int MinimumChange { get; set; }
} }
} }

View File

@@ -1,11 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Use this interface on a device or room if it uses custom Mobile Control messengers
/// </summary>
public interface ICustomMobileControl : IKeyed
{
}
}

View File

@@ -1,72 +1,155 @@
using System; using System;
using System.Collections.ObjectModel;
using Crestron.SimplSharpPro;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using PepperDash.Core; using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{ {
/// <summary>
/// Use this interface on a device or room if it uses custom Mobile Control messengers
/// </summary>
public interface ICustomMobileControl : IKeyed
{
}
/*/// <summary>
/// Describes a MobileControlSystemController
/// </summary>
public interface IMobileControl : IKeyed
{
void CreateMobileControlRoomBridge(IEssentialsRoom room, IMobileControl parent);
void LinkSystemMonitorToAppServer();
}*/
/// <summary> /// <summary>
/// Defines the contract for IMobileControl /// Defines the contract for IMobileControl
/// </summary> /// </summary>
public interface IMobileControl : IKeyed public interface IMobileControl : IKeyed
{ {
/// <summary>
/// Gets the Host
/// </summary>
string Host { get; } string Host { get; }
/// <summary>
/// Gets the Client App URL
/// </summary>
string ClientAppUrl { get; } string ClientAppUrl { get; }
/// <summary>
/// Gets the System UUID
/// </summary>
string SystemUuid { get; } string SystemUuid { get; }
/// <summary>
/// Gets the ApiOnlineAndAuthorized feedback
/// </summary>
BoolFeedback ApiOnlineAndAuthorized { get; } BoolFeedback ApiOnlineAndAuthorized { get; }
/// <summary>
/// Sends the message object to the AppServer
/// </summary>
/// <param name="o">Message to send</param>
void SendMessageObject(IMobileControlMessage o); void SendMessageObject(IMobileControlMessage o);
/// <summary>
/// Adds an action for a messenger
/// </summary>
/// <typeparam name="T">Messenger type. Must implement IMobileControlMessenger</typeparam>
/// <param name="messenger">messenger to register</param>
/// <param name="action">action to add</param>
void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger; void AddAction<T>(T messenger, Action<string, string, JToken> action) where T : IMobileControlMessenger;
/// <summary>
/// Removes an action for a messenger
/// </summary>
/// <param name="key">key for action</param>
void RemoveAction(string key); void RemoveAction(string key);
/// <summary>
/// Adds a device messenger
/// </summary>
/// <param name="messenger">Messenger to add</param>
void AddDeviceMessenger(IMobileControlMessenger messenger); void AddDeviceMessenger(IMobileControlMessenger messenger);
/// <summary>
/// Check if a device messenger exists
/// </summary>
/// <param name="key">Messenger key to find</param>
bool CheckForDeviceMessenger(string key); bool CheckForDeviceMessenger(string key);
/// <summary>
/// Get a Room Messenger by key
/// </summary>
/// <param name="key">messenger key to find</param>
/// <returns>Messenger if found, null otherwise</returns>
IMobileControlRoomMessenger GetRoomMessenger(string key); IMobileControlRoomMessenger GetRoomMessenger(string key);
}
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessenger : IKeyed
{
IMobileControl AppServerController { get; }
string MessagePath { get; }
string DeviceKey { get; }
void RegisterWithAppServer(IMobileControl appServerController);
}
public interface IMobileControlMessage
{
[JsonProperty("type")]
string Type { get; }
[JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)]
string ClientId { get; }
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
JToken Content { get; }
}
/// <summary>
/// Defines the contract for IMobileControlRoomMessenger
/// </summary>
public interface IMobileControlRoomMessenger : IKeyed
{
event EventHandler<EventArgs> UserCodeChanged;
event EventHandler<EventArgs> UserPromptedForCode;
event EventHandler<EventArgs> ClientJoined;
event EventHandler<EventArgs> AppUrlChanged;
string UserCode { get; }
string QrCodeUrl { get; }
string QrCodeChecksum { get; }
string McServerUrl { get; }
string RoomName { get; }
string AppUrl { get; }
void UpdateAppUrl(string url);
}
/// <summary>
/// Defines the contract for IMobileControlAction
/// </summary>
public interface IMobileControlAction
{
IMobileControlMessenger Messenger { get; }
Action<string, string, JToken> Action { get; }
}
/// <summary>
/// Defines the contract for IMobileControlTouchpanelController
/// </summary>
public interface IMobileControlTouchpanelController : IKeyed
{
/// <summary>
/// The default room key for the controller
/// </summary>
string DefaultRoomKey { get; }
/// <summary>
/// Sets the application URL for the controller
/// </summary>
/// <param name="url">The application URL</param>
void SetAppUrl(string url);
/// <summary>
/// Indicates whether the controller uses a direct server connection
/// </summary>
bool UseDirectServer { get; }
/// <summary>
/// Indicates whether the controller is a Zoom Room controller
/// </summary>
bool ZoomRoomController { get; }
}
/// <summary>
/// Describes a MobileControl Crestron Touchpanel Controller
/// This interface extends the IMobileControlTouchpanelController to include connected IP information
/// </summary>
public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController
{
/// <summary>
/// Gets a collection of connected IP information for the touchpanel controller
/// </summary>
ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }
} }
} }

View File

@@ -1,15 +0,0 @@
using System;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlAction
/// </summary>
public interface IMobileControlAction
{
IMobileControlMessenger Messenger { get; }
Action<string, string, JToken> Action { get; }
}
}

View File

@@ -1,17 +0,0 @@
using System.Collections.ObjectModel;
using Crestron.SimplSharpPro;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Describes a MobileControl Crestron Touchpanel Controller
/// This interface extends the IMobileControlTouchpanelController to include connected IP information
/// </summary>
public interface IMobileControlCrestronTouchpanelController : IMobileControlTouchpanelController
{
/// <summary>
/// Gets a collection of connected IP information for the touchpanel controller
/// </summary>
ReadOnlyCollection<ConnectedIpInformation> ConnectedIps { get; }
}
}

View File

@@ -1,18 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
public interface IMobileControlMessage
{
[JsonProperty("type")]
string Type { get; }
[JsonProperty("clientId", NullValueHandling = NullValueHandling.Ignore)]
string ClientId { get; }
[JsonProperty("content", NullValueHandling = NullValueHandling.Ignore)]
JToken Content { get; }
}
}

View File

@@ -1,31 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessenger : IKeyed
{
/// <summary>
/// Parent controller for this messenger
/// </summary>
IMobileControl AppServerController { get; }
/// <summary>
/// Path to listen for messages
/// </summary>
string MessagePath { get; }
/// <summary>
/// Key of the device this messenger is associated with
/// </summary>
string DeviceKey { get; }
/// <summary>
/// Register this messenger with the AppServerController
/// </summary>
/// <param name="appServerController"></param>
void RegisterWithAppServer(IMobileControl appServerController);
}
}

View File

@@ -1,23 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlMessenger
/// </summary>
public interface IMobileControlMessengerWithSubscriptions : IMobileControlMessenger
{
/// <summary>
/// Unsubscribe a client from this messenger
/// </summary>
/// <param name="clientId"></param>
void UnsubscribeClient(string clientId);
/// <summary>
/// Register this messenger with the AppServerController
/// </summary>
/// <param name="appServerController">parent for this messenger</param>
/// <param name="enableMessengerSubscriptions">Enable messenger subscriptions</param>
void RegisterWithAppServer(IMobileControl appServerController, bool enableMessengerSubscriptions);
}
}

View File

@@ -1,33 +0,0 @@
using System;
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlRoomMessenger
/// </summary>
public interface IMobileControlRoomMessenger : IKeyed
{
event EventHandler<EventArgs> UserCodeChanged;
event EventHandler<EventArgs> UserPromptedForCode;
event EventHandler<EventArgs> ClientJoined;
event EventHandler<EventArgs> AppUrlChanged;
string UserCode { get; }
string QrCodeUrl { get; }
string QrCodeChecksum { get; }
string McServerUrl { get; }
string RoomName { get; }
string AppUrl { get; }
void UpdateAppUrl(string url);
}
}

View File

@@ -1,31 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
{
/// <summary>
/// Defines the contract for IMobileControlTouchpanelController
/// </summary>
public interface IMobileControlTouchpanelController : IKeyed
{
/// <summary>
/// The default room key for the controller
/// </summary>
string DefaultRoomKey { get; }
/// <summary>
/// Sets the application URL for the controller
/// </summary>
/// <param name="url">The application URL</param>
void SetAppUrl(string url);
/// <summary>
/// Indicates whether the controller uses a direct server connection
/// </summary>
bool UseDirectServer { get; }
/// <summary>
/// Indicates whether the controller is a Zoom Room controller
/// </summary>
bool ZoomRoomController { get; }
}
}

View File

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

View File

@@ -18,7 +18,7 @@ namespace PepperDash.Essentials.Core
public List<string> TypeNames { get; protected set; } public List<string> TypeNames { get; protected set; }
/// <summary> /// <summary>
/// Build the device using the configuration /// The method that will build the device
/// </summary> /// </summary>
/// <param name="dc">The device config</param> /// <param name="dc">The device config</param>
/// <returns>An instance of the device</returns> /// <returns>An instance of the device</returns>

View File

@@ -12,7 +12,7 @@ namespace PepperDash.Essentials.Core
public interface IHasFeedback : IKeyed public interface IHasFeedback : IKeyed
{ {
/// <summary> /// <summary>
/// This method returns a list of all Output objects on a device, /// This method shall return a list of all Output objects on a device,
/// including all "aggregate" devices. /// including all "aggregate" devices.
/// </summary> /// </summary>
FeedbackCollection<Feedback> Feedbacks { get; } FeedbackCollection<Feedback> Feedbacks { get; }

View File

@@ -1,4 +1,6 @@
using Crestron.SimplSharp; 
using Crestron.SimplSharp;
using Crestron.SimplSharp.CrestronIO; using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharp.CrestronXml; using Crestron.SimplSharp.CrestronXml;
using Crestron.SimplSharp.CrestronXml.Serialization; using Crestron.SimplSharp.CrestronXml.Serialization;
@@ -6,7 +8,6 @@ using Crestron.SimplSharpPro;
using Crestron.SimplSharpPro.Fusion; using Crestron.SimplSharpPro.Fusion;
using Newtonsoft.Json; using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.DeviceTypeInterfaces; using PepperDash.Essentials.Core.DeviceTypeInterfaces;
using Serilog.Events; using Serilog.Events;
@@ -20,42 +21,26 @@ namespace PepperDash.Essentials.Core.Fusion
/// <summary> /// <summary>
/// Represents a EssentialsHuddleSpaceFusionSystemControllerBase /// Represents a EssentialsHuddleSpaceFusionSystemControllerBase
/// </summary> /// </summary>
public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest public class EssentialsHuddleSpaceFusionSystemControllerBase : Device, IOccupancyStatusProvider
{ {
private IEssentialsRoomFusionControllerPropertiesConfig _config; private readonly EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;
private EssentialsHuddleSpaceRoomFusionRoomJoinMap JoinMap;
private const string RemoteOccupancyXml = "<Occupancy><Type>Local</Type><State>{0}</State></Occupancy>"; 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 = private readonly Dictionary<Device, BoolInputSig> _sourceToFeedbackSigs =
new Dictionary<Device, BoolInputSig>(); new Dictionary<Device, BoolInputSig>();
/// <summary>
/// Gets or sets the CurrentRoomSourceNameSig
/// </summary>
protected StringSigData CurrentRoomSourceNameSig; protected StringSigData CurrentRoomSourceNameSig;
private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge(); private readonly FusionCustomPropertiesBridge CustomPropertiesBridge = new FusionCustomPropertiesBridge();
/// <summary>
/// Gets or sets the FusionOccSensor
/// </summary>
protected FusionOccupancySensorAsset FusionOccSensor; protected FusionOccupancySensorAsset FusionOccSensor;
private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor; private readonly FusionRemoteOccupancySensor FusionRemoteOccSensor;
/// <summary>
/// Gets or sets the FusionRoom
/// </summary>
protected FusionRoom FusionRoom; protected FusionRoom FusionRoom;
/// <summary>
/// Gets or sets the FusionStaticAssets
/// </summary>
protected Dictionary<int, FusionAsset> FusionStaticAssets; protected Dictionary<int, FusionAsset> FusionStaticAssets;
private readonly long PushNotificationTimeout = 5000; private readonly long PushNotificationTimeout = 5000;
private IEssentialsRoom Room; private readonly IEssentialsRoom Room;
private readonly long SchedulePollInterval = 300000; private readonly long SchedulePollInterval = 300000;
private Event _currentMeeting; private Event _currentMeeting;
@@ -63,7 +48,8 @@ namespace PepperDash.Essentials.Core.Fusion
private CTimer _dailyTimeRequestTimer; private CTimer _dailyTimeRequestTimer;
private StatusMonitorCollection _errorMessageRollUp; private StatusMonitorCollection _errorMessageRollUp;
private FusionRoomGuids _guids; private FusionRoomGuids _guiDs;
private uint _ipId;
private bool _isRegisteredForSchedulePushNotifications; private bool _isRegisteredForSchedulePushNotifications;
private Event _nextMeeting; private Event _nextMeeting;
@@ -74,20 +60,6 @@ namespace PepperDash.Essentials.Core.Fusion
private string _roomOccupancyRemoteString; private string _roomOccupancyRemoteString;
private bool _helpRequestSent;
private eFusionHelpResponse _helpRequestStatus;
/// <inheritdoc />
public StringFeedback HelpRequestResponseFeedback { get; private set; }
/// <inheritdoc />
public BoolFeedback HelpRequestSentFeedback { get; private set; }
/// <inheritdoc />
public StringFeedback HelpRequestStatusFeedback { get; private set; }
#region System Info Sigs #region System Info Sigs
//StringSigData SystemName; //StringSigData SystemName;
@@ -121,88 +93,17 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion #endregion
/// <summary> public EssentialsHuddleSpaceFusionSystemControllerBase(IEssentialsRoom room, uint ipId, string joinMapKey)
/// 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)
: base(room.Key + "-fusion") : 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 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); JoinMap = new EssentialsHuddleSpaceRoomFusionRoomJoinMap(1);
this.LogDebug("JoinMap created"); CrestronConsole.AddNewConsoleCommand((o) => JoinMap.PrintJoinMapInfo(), string.Format("ptjnmp-{0}", Key), "Prints Attribute Join Map", ConsoleAccessLevelEnum.AccessOperator);
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);
if (!string.IsNullOrEmpty(joinMapKey)) if (!string.IsNullOrEmpty(joinMapKey))
{ {
// this.LogDebug("Attempting to get custom join map for key: {0}", joinMapKey);
var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey); var customJoins = JoinMapHelper.TryGetJoinMapAdvancedForDevice(joinMapKey);
if (customJoins != null) if (customJoins != null)
{ {
@@ -212,43 +113,12 @@ namespace PepperDash.Essentials.Core.Fusion
Room = room; Room = room;
this.LogDebug("Room found: {0}", Room.Key); _ipId = ipId;
FusionStaticAssets = new Dictionary<int, FusionAsset>(); FusionStaticAssets = new Dictionary<int, FusionAsset>();
this.LogDebug("FusionStaticAssets dictionary created"); _guiDs = new FusionRoomGuids();
_guids = new FusionRoomGuids();
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)
{
SetUpRemoteOccupancy();
}
else
{
SetUpLocalOccupancy();
}
}
}
this.LogDebug("Occupancy setup complete");
}
catch (Exception e)
{
Debug.LogMessage(LogEventLevel.Information, this, "Error Building Fusion System Controller: {0}", e);
}
}
private string GetGuidFilePath(uint ipId)
{
var mac = var mac =
CrestronEthernetHelper.GetEthernetParameter( CrestronEthernetHelper.GetEthernetParameter(
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0); CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_MAC_ADDRESS, 0);
@@ -256,7 +126,7 @@ namespace PepperDash.Essentials.Core.Fusion
var slot = Global.ControlSystem.ProgramNumber; var slot = Global.ControlSystem.ProgramNumber;
var guidFilePath = Global.FilePathPrefix + var guidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _config.IpIdInt); string.Format(@"{0}-FusionGuids-{1:X2}.json", InitialParametersClass.ProgramIDTag, _ipId);
var oldGuidFilePath = Global.FilePathPrefix + var oldGuidFilePath = Global.FilePathPrefix +
string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag); string.Format(@"{0}-FusionGuids.json", InitialParametersClass.ProgramIDTag);
@@ -276,7 +146,7 @@ namespace PepperDash.Essentials.Core.Fusion
if (!_guidFileExists) if (!_guidFileExists)
{ {
// Does not exist. Create GUIDs // Does not exist. Create GUIDs
_guids = new FusionRoomGuids(Room.Name, ipId, _guids.GenerateNewRoomGuid(slot, mac), _guiDs = new FusionRoomGuids(Room.Name, ipId, _guiDs.GenerateNewRoomGuid(slot, mac),
FusionStaticAssets); FusionStaticAssets);
} }
else else
@@ -285,16 +155,34 @@ namespace PepperDash.Essentials.Core.Fusion
ReadGuidFile(guidFilePath); ReadGuidFile(guidFilePath);
} }
return guidFilePath;
if (Room is IRoomOccupancy occupancyRoom)
{
if (occupancyRoom.RoomOccupancy != null)
{
if (occupancyRoom.OccupancyStatusProviderIsRemote)
{
SetUpRemoteOccupancy();
}
else
{
SetUpLocalOccupancy();
}
}
} }
/// <inheritdoc />
public override void Initialize() AddPostActivationAction(() => PostActivate(guidFilePath));
}
catch (Exception e)
{ {
Debug.LogMessage(LogEventLevel.Information, this, "Error Building Fusion System Controller: {0}", e);
}
}
GenerateGuidFile(GetGuidFilePath(_config.IpIdInt)); private void PostActivate(string guidFilePath)
{
CreateSymbolAndBasicSigs(_config.IpIdInt); CreateSymbolAndBasicSigs(_ipId);
SetUpSources(); SetUpSources();
SetUpCommunitcationMonitors(); SetUpCommunitcationMonitors();
SetUpDisplay(); SetUpDisplay();
@@ -303,18 +191,12 @@ namespace PepperDash.Essentials.Core.Fusion
FusionRVI.GenerateFileForAllFusionDevices(); FusionRVI.GenerateFileForAllFusionDevices();
HelpRequestResponseFeedback = new StringFeedback("HelpRequestResponse", () => FusionRoom.Help.OutputSig.StringValue); GenerateGuidFile(guidFilePath);
HelpRequestSentFeedback = new BoolFeedback("HelpRequestSent", () => _helpRequestSent);
HelpRequestStatusFeedback = new StringFeedback("HelpRequestStatus", () => _helpRequestStatus.ToString());
} }
/// <summary>
/// Gets the RoomGuid
/// </summary>
protected string RoomGuid protected string RoomGuid
{ {
get { return _guids.RoomGuid; } get { return _guiDs.RoomGuid; }
} }
/// <summary> /// <summary>
@@ -322,9 +204,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; } public StringFeedback RoomOccupancyRemoteStringFeedback { get; private set; }
/// <summary>
/// Gets the RoomIsOccupiedFeedbackFunc
/// </summary>
protected Func<bool> RoomIsOccupiedFeedbackFunc protected Func<bool> RoomIsOccupiedFeedbackFunc
{ {
get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; } get { return () => FusionRemoteOccSensor.RoomOccupied.OutputSig.BoolValue; }
@@ -339,16 +218,10 @@ namespace PepperDash.Essentials.Core.Fusion
#endregion #endregion
/// <summary>
/// ScheduleChange event
/// </summary>
public event EventHandler<ScheduleChangeEventArgs> ScheduleChange; public event EventHandler<ScheduleChangeEventArgs> ScheduleChange;
//public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning; //public event EventHandler<MeetingChangeEventArgs> MeetingEndWarning;
//public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning; //public event EventHandler<MeetingChangeEventArgs> NextMeetingBeginWarning;
/// <summary>
/// RoomInfoChange event
/// </summary>
public event EventHandler<EventArgs> RoomInfoChange; public event EventHandler<EventArgs> RoomInfoChange;
//ScheduleResponseEvent NextMeeting; //ScheduleResponseEvent NextMeeting;
@@ -385,11 +258,11 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file"); Debug.LogMessage(LogEventLevel.Debug, this, "Writing GUIDs to file");
_guids = FusionOccSensor == null _guiDs = FusionOccSensor == null
? new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets) ? new FusionRoomGuids(Room.Name, _ipId, RoomGuid, FusionStaticAssets)
: new FusionRoomGuids(Room.Name, _config.IpIdInt, RoomGuid, FusionStaticAssets, FusionOccSensor); : 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)) using (var sw = new StreamWriter(filePath))
{ {
@@ -439,17 +312,17 @@ namespace PepperDash.Essentials.Core.Fusion
{ {
var json = File.ReadToEnd(filePath, Encoding.ASCII); 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}", Debug.LogMessage(LogEventLevel.Information, this, "Fusion Guids successfully read from file: {0}",
filePath); 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) foreach (var item in FusionStaticAssets)
{ {
@@ -470,10 +343,6 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// CreateSymbolAndBasicSigs method
/// </summary>
/// <param name="ipId"></param>
protected virtual void CreateSymbolAndBasicSigs(uint ipId) 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); Debug.LogMessage(LogEventLevel.Information, this, "Creating Fusion Room symbol with GUID: {0} and IP-ID {1:X2}", RoomGuid, ipId);
@@ -536,10 +405,6 @@ namespace PepperDash.Essentials.Core.Fusion
CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler; CrestronEnvironment.EthernetEventHandler += CrestronEnvironment_EthernetEventHandler;
} }
/// <summary>
/// CrestronEnvironment_EthernetEventHandler method
/// </summary>
/// <param name="ethernetEventArgs"></param>
protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs) protected void CrestronEnvironment_EthernetEventHandler(EthernetEventArgs ethernetEventArgs)
{ {
if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp) if (ethernetEventArgs.EthernetEventType == eEthernetEventType.LinkUp)
@@ -548,9 +413,6 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// GetSystemInfo method
/// </summary>
protected void GetSystemInfo() protected void GetSystemInfo()
{ {
//SystemName.InputSig.StringValue = Room.Name; //SystemName.InputSig.StringValue = Room.Name;
@@ -564,9 +426,6 @@ namespace PepperDash.Essentials.Core.Fusion
() => CrestronConsole.SendControlSystemCommand("reboot", ref response)); () => CrestronConsole.SendControlSystemCommand("reboot", ref response));
} }
/// <summary>
/// SetUpEthernetValues method
/// </summary>
protected void SetUpEthernetValues() protected void SetUpEthernetValues()
{ {
_ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly); _ip1 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorIp1.JoinNumber, JoinMap.ProcessorIp1.AttributeName, eSigIoMask.InputSigOnly);
@@ -582,9 +441,6 @@ namespace PepperDash.Essentials.Core.Fusion
_netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly); _netMask2 = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorNetMask2.JoinNumber, JoinMap.ProcessorNetMask2.AttributeName, eSigIoMask.InputSigOnly);
} }
/// <summary>
/// GetProcessorEthernetValues method
/// </summary>
protected void GetProcessorEthernetValues() protected void GetProcessorEthernetValues()
{ {
_ip1.InputSig.StringValue = _ip1.InputSig.StringValue =
@@ -633,9 +489,6 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// GetProcessorInfo method
/// </summary>
protected void GetProcessorInfo() protected void GetProcessorInfo()
{ {
_firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly); _firmware = FusionRoom.CreateOffsetStringSig(JoinMap.ProcessorFirmware.JoinNumber, JoinMap.ProcessorFirmware.AttributeName, eSigIoMask.InputSigOnly);
@@ -646,7 +499,7 @@ namespace PepperDash.Essentials.Core.Fusion
{ {
var join = JoinMap.ProgramNameStart.JoinNumber + i; var join = JoinMap.ProgramNameStart.JoinNumber + i;
var progNum = i + 1; 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); string.Format("{0} {1}", JoinMap.ProgramNameStart.AttributeName, progNum), eSigIoMask.InputSigOnly);
} }
} }
@@ -654,9 +507,6 @@ namespace PepperDash.Essentials.Core.Fusion
_firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion; _firmware.InputSig.StringValue = InitialParametersClass.FirmwareVersion;
} }
/// <summary>
/// GetCustomProperties method
/// </summary>
protected void GetCustomProperties() protected void GetCustomProperties()
{ {
if (FusionRoom.IsOnline) if (FusionRoom.IsOnline)
@@ -674,16 +524,11 @@ namespace PepperDash.Essentials.Core.Fusion
// TODO: Get IP and Project Name from TP // 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) protected void FusionRoom_OnlineStatusChange(GenericBase currentDevice, OnlineOfflineEventArgs args)
{ {
if (args.DeviceOnLine) if (args.DeviceOnLine)
{ {
CrestronInvoke.BeginInvoke((o) => CrestronInvoke.BeginInvoke( (o) =>
{ {
CrestronEnvironment.Sleep(200); CrestronEnvironment.Sleep(200);
@@ -831,7 +676,7 @@ namespace PepperDash.Essentials.Core.Fusion
var extendTime = _currentMeeting.dtEnd - DateTime.Now; var extendTime = _currentMeeting.dtEnd - DateTime.Now;
var extendMinutesRaw = extendTime.TotalMinutes; var extendMinutesRaw = extendTime.TotalMinutes;
extendMinutes += (int)Math.Round(extendMinutesRaw); extendMinutes += (int) Math.Round(extendMinutesRaw);
} }
@@ -1000,9 +845,9 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime); Debug.LogMessage(LogEventLevel.Debug, this, "DateTime from Fusion Server: {0}", currentTime);
// Parse time and date from response and insert values // Parse time and date from response and insert values
CrestronEnvironment.SetTimeAndDate((ushort)currentTime.Hour, (ushort)currentTime.Minute, CrestronEnvironment.SetTimeAndDate((ushort) currentTime.Hour, (ushort) currentTime.Minute,
(ushort)currentTime.Second, (ushort)currentTime.Month, (ushort)currentTime.Day, (ushort) currentTime.Second, (ushort) currentTime.Month, (ushort) currentTime.Day,
(ushort)currentTime.Year); (ushort) currentTime.Year);
Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime()); Debug.LogMessage(LogEventLevel.Debug, this, "Processor time set to {0}", CrestronEnvironment.GetLocalTime());
} }
@@ -1220,9 +1065,6 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// SetUpSources method
/// </summary>
protected virtual void SetUpSources() protected virtual void SetUpSources()
{ {
// Sources // Sources
@@ -1232,10 +1074,10 @@ namespace PepperDash.Essentials.Core.Fusion
// NEW PROCESS: // NEW PROCESS:
// Make these lists and insert the fusion attributes by iterating these // Make these lists and insert the fusion attributes by iterating these
var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls); var setTopBoxes = dict.Where(d => d.Value.SourceDevice is ISetTopBoxControls);
uint i = 0; uint i = 1;
foreach (var kvp in setTopBoxes) 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++; i++;
if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots if (i > JoinMap.Display1SetTopBoxSourceStart.JoinSpan) // We only have five spots
{ {
@@ -1244,10 +1086,10 @@ namespace PepperDash.Essentials.Core.Fusion
} }
var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls); var discPlayers = dict.Where(d => d.Value.SourceDevice is IDiscPlayerControls);
i = 0; i = 1;
foreach (var kvp in discPlayers) 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++; i++;
if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots if (i > JoinMap.Display1DiscPlayerSourceStart.JoinSpan) // We only have five spots
{ {
@@ -1256,10 +1098,10 @@ namespace PepperDash.Essentials.Core.Fusion
} }
var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource); var laptops = dict.Where(d => d.Value.SourceDevice is IRoutingSource);
i = 0; i = 1;
foreach (var kvp in laptops) 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++; i++;
if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots??? if (i > JoinMap.Display1LaptopSourceStart.JoinSpan) // We only have ten spots???
{ {
@@ -1269,7 +1111,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var usageDevice in dict.Select(kvp => kvp.Value.SourceDevice).OfType<IUsageTracking>()) 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; usageDevice.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
} }
} }
@@ -1315,31 +1157,17 @@ namespace PepperDash.Essentials.Core.Fusion
Debug.LogMessage(LogEventLevel.Debug, this, "Device usage string: {0}", deviceUsage); 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) 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); attrName, attrNum, pSrc.Key);
try try
{ {
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig); var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputOutputSig);
// Need feedback when this source is selected // Need feedback when this source is selected
// Event handler, added below, will compare source changes with this sig dict // Event handler, added below, will compare source changes with this sig dict
if (!_sourceToFeedbackSigs.ContainsKey(pSrc))
{
_sourceToFeedbackSigs.Add(pSrc, sigD.InputSig); _sourceToFeedbackSigs.Add(pSrc, sigD.InputSig);
}
else
{
this.LogWarning("Source '{0}' already has a feedback sig mapped. Overwriting.", pSrc.Key);
_sourceToFeedbackSigs[pSrc] = sigD.InputSig;
}
// And respond to selection in Fusion // And respond to selection in Fusion
sigD.OutputSig.SetSigFalseAction(() => sigD.OutputSig.SetSigFalseAction(() =>
@@ -1352,12 +1180,14 @@ namespace PepperDash.Essentials.Core.Fusion
} }
catch (Exception) 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); attrNum, attrName, pSrc.Key);
} }
} }
/// <summary>
///
/// </summary>
private void SetUpCommunitcationMonitors() private void SetUpCommunitcationMonitors()
{ {
uint displayNum = 0; uint displayNum = 0;
@@ -1444,8 +1274,6 @@ namespace PepperDash.Essentials.Core.Fusion
if (attrName != null) 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 // Link comm status to sig and update
var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly); var sigD = FusionRoom.CreateOffsetBoolSig(attrNum, attrName, eSigIoMask.InputSigOnly);
var smd = dev as ICommunicationMonitor; var smd = dev as ICommunicationMonitor;
@@ -1457,9 +1285,6 @@ namespace PepperDash.Essentials.Core.Fusion
} }
} }
/// <summary>
/// SetUpDisplay method
/// </summary>
protected virtual void SetUpDisplay() protected virtual void SetUpDisplay()
{ {
try try
@@ -1472,7 +1297,7 @@ namespace PepperDash.Essentials.Core.Fusion
foreach (var display in displays.Cast<IDisplay>()) 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; display.UsageTracker.DeviceUsageEnded += UsageTracker_DeviceUsageEnded;
} }
@@ -1585,7 +1410,7 @@ namespace PepperDash.Essentials.Core.Fusion
// Power on // Power on
var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint)joinOffset, displayName + "Power On", var defaultDisplayPowerOn = FusionRoom.CreateOffsetBoolSig((uint) joinOffset, displayName + "Power On",
eSigIoMask.InputOutputSig); eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b => defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1596,7 +1421,7 @@ namespace PepperDash.Essentials.Core.Fusion
}); });
// Power Off // Power Off
var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 1, displayName + "Power Off", var defaultDisplayPowerOff = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 1, displayName + "Power Off",
eSigIoMask.InputOutputSig); eSigIoMask.InputOutputSig);
defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b => defaultDisplayPowerOn.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1614,7 +1439,7 @@ namespace PepperDash.Essentials.Core.Fusion
} }
// Current Source // Current Source
var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint)joinOffset + 8, var defaultDisplaySourceNone = FusionRoom.CreateOffsetBoolSig((uint) joinOffset + 8,
displayName + "Source None", eSigIoMask.InputOutputSig); displayName + "Source None", eSigIoMask.InputOutputSig);
defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b => defaultDisplaySourceNone.OutputSig.UserObject = new Action<bool>(b =>
{ {
@@ -1682,7 +1507,7 @@ namespace PepperDash.Essentials.Core.Fusion
//if (Room.OccupancyObj != null) //if (Room.OccupancyObj != null)
//{ //{
var tempOccAsset = _guids.OccupancyAsset; var tempOccAsset = _guiDs.OccupancyAsset;
if (tempOccAsset == null) if (tempOccAsset == null)
{ {
@@ -1763,74 +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) 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();
}
// The sig/UO method: Need separate handlers for fixed and user sigs, all flavors, // The sig/UO method: Need separate handlers for fixed and user sigs, all flavors,
// even though they all contain sigs. // even though they all contain sigs.
BoolOutputSig outSig; BoolOutputSig outSig;
if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData) if (args.UserConfiguredSigDetail is BooleanSigDataFixedName sigData)
{ {
@@ -1869,69 +1632,9 @@ namespace PepperDash.Essentials.Core.Fusion
(outSig.UserObject as Action<string>).Invoke(outSig.StringValue); (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();
_helpRequestStatus = eFusionHelpResponse.HelpRequested;
HelpRequestStatusFeedback.FireUpdate();
}
/// <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 in Fusion 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 public static class FusionRoomExtensions
{ {
/// <summary> /// <summary>
@@ -1945,8 +1648,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static BooleanSigData CreateOffsetBoolSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) 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) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -1967,8 +1668,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static UShortSigData CreateOffsetUshortSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) 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) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -1989,8 +1688,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public static StringSigData CreateOffsetStringSig(this FusionRoom fr, uint number, string name, eSigIoMask mask) 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) if (number < 50)
{ {
throw new ArgumentOutOfRangeException("number", "Cannot be less than 50"); throw new ArgumentOutOfRangeException("number", "Cannot be less than 50");
@@ -2106,9 +1803,6 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public class RoomInformation public class RoomInformation
{ {
/// <summary>
/// Constructor
/// </summary>
public RoomInformation() public RoomInformation()
{ {
FusionCustomProperties = new List<FusionCustomProperty>(); FusionCustomProperties = new List<FusionCustomProperty>();
@@ -2161,17 +1855,10 @@ namespace PepperDash.Essentials.Core.Fusion
/// </summary> /// </summary>
public class FusionCustomProperty public class FusionCustomProperty
{ {
/// <summary>
/// Constructor
/// </summary>
public FusionCustomProperty() public FusionCustomProperty()
{ {
} }
/// <summary>
/// Constructor with id
/// </summary>
/// <param name="id"></param>
public FusionCustomProperty(string id) public FusionCustomProperty(string id)
{ {
ID = id; ID = id;

View File

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

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

@@ -25,7 +25,7 @@
<DocumentationFile>bin\$(Configuration)\PepperDash_Essentials_Core.xml</DocumentationFile> <DocumentationFile>bin\$(Configuration)\PepperDash_Essentials_Core.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.90" /> <PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.21.157" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" /> <None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />

View File

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

View File

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

View File

@@ -98,7 +98,7 @@ namespace PepperDash.Essentials.Core.Routing
/// <param name="inputPort">The currently selected input port on the destination device.</param> /// <param name="inputPort">The currently selected input port on the destination device.</param>
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort) 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) if(inputPort == null)
{ {

View File

@@ -1,22 +1,20 @@
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Crestron.SimplSharp.CrestronIO; using System.Text;
using Crestron.SimplSharpPro; using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Core.Logging; using Crestron.SimplSharpPro.UI;
using Crestron.SimplSharp.CrestronIO;
using Crestron.SimplSharpPro;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Core.UI namespace PepperDash.Essentials.Core.UI
{ {
/// <summary> public abstract class TouchpanelBase: EssentialsDevice, IHasBasicTriListWithSmartObject
/// Base class for Touchpanel devices
/// </summary>
public abstract class TouchpanelBase : EssentialsDevice, IHasBasicTriListWithSmartObject
{ {
/// <summary>
/// Gets or sets the configuration for the Crestron touchpanel.
/// </summary>
protected CrestronTouchpanelPropertiesConfig _config; protected CrestronTouchpanelPropertiesConfig _config;
/// <summary> /// <summary>
/// Gets or sets the Panel /// Gets or sets the Panel
@@ -30,10 +28,11 @@ namespace PepperDash.Essentials.Core.UI
/// </summary> /// </summary>
/// <param name="key">Essentials Device Key</param> /// <param name="key">Essentials Device Key</param>
/// <param name="name">Essentials Device Name</param> /// <param name="name">Essentials Device Name</param>
/// <param name="panel">Crestron Touchpanel Device</param> /// <param name="type">Touchpanel Type to build</param>
/// <param name="config">Touchpanel Configuration</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) protected TouchpanelBase(string key, string name, BasicTriListWithSmartObject panel, CrestronTouchpanelPropertiesConfig config)
: base(key, name) :base(key, name)
{ {
if (panel == null) if (panel == null)
@@ -58,19 +57,21 @@ namespace PepperDash.Essentials.Core.UI
_config = config; _config = config;
AddPreActivationAction(() => AddPreActivationAction(() => {
{ if (Panel.Register() != eDeviceRegistrationUnRegistrationResponse.Success)
Debug.LogMessage(LogEventLevel.Information, this, "WARNING: Registration failed. Continuing, but panel may not function: {0}", Panel.RegistrationFailureReason);
// Give up cleanly if SGD is not present. // Give up cleanly if SGD is not present.
var sgdName = Global.FilePathPrefix + "sgd" + Global.DirectorySeparator + _config.SgdFile; var sgdName = Global.FilePathPrefix + "sgd" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName)) 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; sgdName = Global.ApplicationDirectoryPathPrefix + Global.DirectorySeparator + "SGD" + Global.DirectorySeparator + _config.SgdFile;
if (!File.Exists(sgdName)) 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; return;
} }
} }
@@ -81,11 +82,12 @@ namespace PepperDash.Essentials.Core.UI
AddPostActivationAction(() => AddPostActivationAction(() =>
{ {
// Check for IEssentialsRoomCombiner in DeviceManager and if found, subscribe to its event // 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 // Subscribe to the even
roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(RoomCombiner_RoomCombinationScenarioChanged); roomCombiner.RoomCombinationScenarioChanged += new EventHandler<EventArgs>(roomCombiner_RoomCombinationScenarioChanged);
// Connect to the initial roomKey // Connect to the initial roomKey
if (roomCombiner.CurrentScenario != null) if (roomCombiner.CurrentScenario != null)
@@ -104,11 +106,6 @@ namespace PepperDash.Essentials.Core.UI
// No room combiner, use the default key // No room combiner, use the default key
SetupPanelDrivers(_config.DefaultRoomKey); 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> /// <param name="roomKey">Room Key for this panel</param>
protected abstract void SetupPanelDrivers(string roomKey); protected abstract void SetupPanelDrivers(string roomKey);
/// <summary> /// <summary>
/// Event handler for System Extender Events /// Event handler for System Extender Events
/// </summary> /// </summary>
@@ -131,7 +129,7 @@ namespace PepperDash.Essentials.Core.UI
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
/// <param name="e"></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; var roomCombiner = sender as IEssentialsRoomCombiner;
@@ -158,9 +156,9 @@ namespace PepperDash.Essentials.Core.UI
SetupPanelDrivers(newRoomKey); SetupPanelDrivers(newRoomKey);
} }
private void Panel_SigChange(object currentDevice, SigEventArgs args) private void Panel_SigChange(object currentDevice, Crestron.SimplSharpPro.SigEventArgs args)
{ {
this.LogVerbose("Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue); Debug.LogMessage(LogEventLevel.Verbose, this, "Sig change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
var uo = args.Sig.UserObject; var uo = args.Sig.UserObject;
if (uo is Action<bool>) if (uo is Action<bool>)
(uo as Action<bool>)(args.Sig.BoolValue); (uo as Action<bool>)(args.Sig.BoolValue);
@@ -173,7 +171,7 @@ namespace PepperDash.Essentials.Core.UI
private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args) private void Tsw_ButtonStateChange(GenericBase device, ButtonEventArgs args)
{ {
var uo = args.Button.UserObject; var uo = args.Button.UserObject;
if (uo is Action<bool>) if(uo is Action<bool>)
(uo as Action<bool>)(args.Button.State == eButtonState.Pressed); (uo as Action<bool>)(args.Button.State == eButtonState.Pressed);
} }
} }

View File

@@ -134,7 +134,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
eStreamDebuggingSetting debugSetting; eStreamDebuggingSetting debugSetting;
try try
{ {
debugSetting = (eStreamDebuggingSetting)Enum.Parse(typeof(eStreamDebuggingSetting), body.Setting, true); debugSetting = (eStreamDebuggingSetting) Enum.Parse(typeof (eStreamDebuggingSetting), body.Setting, true);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -1,4 +1,8 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
@@ -13,23 +17,11 @@ namespace PepperDash.Essentials.Devices.Common
/// </summary> /// </summary>
public class GenericAudioOut : EssentialsDevice, IRoutingSink public class GenericAudioOut : EssentialsDevice, IRoutingSink
{ {
/// <summary>
/// Gets the current input port
/// </summary>
public RoutingInputPort CurrentInputPort => AnyAudioIn; public RoutingInputPort CurrentInputPort => AnyAudioIn;
/// <summary>
/// Event fired when the current source changes
/// </summary>
public event SourceInfoChangeHandler CurrentSourceChange; public event SourceInfoChangeHandler CurrentSourceChange;
/// <summary>
/// Gets or sets the current source info key
/// </summary>
public string CurrentSourceInfoKey { get; set; } public string CurrentSourceInfoKey { get; set; }
/// <summary>
/// Gets or sets the current source info
/// </summary>
public SourceListItem CurrentSourceInfo public SourceListItem CurrentSourceInfo
{ {
get get
@@ -58,11 +50,6 @@ namespace PepperDash.Essentials.Devices.Common
/// </summary> /// </summary>
public RoutingInputPort AnyAudioIn { get; private set; } public RoutingInputPort AnyAudioIn { get; private set; }
/// <summary>
/// Constructor for GenericAudioOut
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
public GenericAudioOut(string key, string name) public GenericAudioOut(string key, string name)
: base(key, name) : base(key, name)
{ {
@@ -72,9 +59,6 @@ namespace PepperDash.Essentials.Devices.Common
#region IRoutingInputs Members #region IRoutingInputs Members
/// <summary>
/// Gets the collection of input ports
/// </summary>
public RoutingPortCollection<RoutingInputPort> InputPorts public RoutingPortCollection<RoutingInputPort> InputPorts
{ {
get { return new RoutingPortCollection<RoutingInputPort> { AnyAudioIn }; } get { return new RoutingPortCollection<RoutingInputPort> { AnyAudioIn }; }
@@ -89,18 +73,9 @@ namespace PepperDash.Essentials.Devices.Common
/// </summary> /// </summary>
public class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice public class GenericAudioOutWithVolume : GenericAudioOut, IHasVolumeDevice
{ {
/// <summary>
/// Gets the volume device key
/// </summary>
public string VolumeDeviceKey { get; private set; } public string VolumeDeviceKey { get; private set; }
/// <summary>
/// Gets the volume zone
/// </summary>
public uint VolumeZone { get; private set; } public uint VolumeZone { get; private set; }
/// <summary>
/// Gets the volume device
/// </summary>
public IBasicVolumeControls VolumeDevice public IBasicVolumeControls VolumeDevice
{ {
get get
@@ -128,14 +103,8 @@ namespace PepperDash.Essentials.Devices.Common
} }
/// <summary>
/// Factory for creating GenericAudioOutWithVolume devices
/// </summary>
public class GenericAudioOutWithVolumeFactory : EssentialsDeviceFactory<GenericAudioOutWithVolume> public class GenericAudioOutWithVolumeFactory : EssentialsDeviceFactory<GenericAudioOutWithVolume>
{ {
/// <summary>
/// Constructor for GenericAudioOutWithVolumeFactory
/// </summary>
public GenericAudioOutWithVolumeFactory() public GenericAudioOutWithVolumeFactory()
{ {
TypeNames = new List<string>() { "genericaudiooutwithvolume" }; TypeNames = new List<string>() { "genericaudiooutwithvolume" };

View File

@@ -1,20 +1,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.Codec; using PepperDash.Essentials.Devices.Common.Codec;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary>
/// Abstract base class for audio codec devices
/// </summary>
public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo public abstract class AudioCodecBase : EssentialsDevice, IHasDialer, IUsageTracking, IAudioCodecInfo
{ {
/// <summary>
/// Event fired when call status changes
/// </summary>
public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange; public event EventHandler<CodecCallStatusItemChangeEventArgs> CallStatusChange;
/// <summary> /// <summary>
@@ -54,11 +52,6 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public List<CodecActiveCallItem> ActiveCalls { get; set; } public List<CodecActiveCallItem> ActiveCalls { get; set; }
/// <summary>
/// Constructor for AudioCodecBase
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
public AudioCodecBase(string key, string name) public AudioCodecBase(string key, string name)
: base(key, name) : base(key, name)
{ {
@@ -77,9 +70,11 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
} }
/// <summary> /// <summary>
/// Handles call status change events ///
/// </summary> /// </summary>
/// <param name="item">The call item that changed status</param> /// <param name="previousStatus"></param>
/// <param name="newStatus"></param>
/// <param name="item"></param>
protected void OnCallStatusChange(CodecActiveCallItem item) protected void OnCallStatusChange(CodecActiveCallItem item)
{ {
var handler = CallStatusChange; var handler = CallStatusChange;
@@ -97,22 +92,16 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
#region IHasDialer Members #region IHasDialer Members
/// <inheritdoc />
public abstract void Dial(string number); public abstract void Dial(string number);
/// <inheritdoc />
public abstract void EndCall(CodecActiveCallItem activeCall); public abstract void EndCall(CodecActiveCallItem activeCall);
/// <inheritdoc />
public abstract void EndAllCalls(); public abstract void EndAllCalls();
/// <inheritdoc />
public abstract void AcceptCall(CodecActiveCallItem item); public abstract void AcceptCall(CodecActiveCallItem item);
/// <inheritdoc />
public abstract void RejectCall(CodecActiveCallItem item); public abstract void RejectCall(CodecActiveCallItem item);
/// <inheritdoc />
public abstract void SendDtmf(string digit); public abstract void SendDtmf(string digit);
#endregion #endregion

View File

@@ -1,13 +1,16 @@
namespace PepperDash.Essentials.Devices.Common.AudioCodec using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary> /// <summary>
/// Implements a common set of data about a codec /// Implements a common set of data about a codec
/// </summary> /// </summary>
public interface IAudioCodecInfo public interface IAudioCodecInfo
{ {
/// <summary>
/// Gets the codec information
/// </summary>
AudioCodecInfo CodecInfo { get; } AudioCodecInfo CodecInfo { get; }
} }
@@ -16,9 +19,6 @@
/// </summary> /// </summary>
public abstract class AudioCodecInfo public abstract class AudioCodecInfo
{ {
/// <summary>
/// Gets or sets the phone number
/// </summary>
public abstract string PhoneNumber { get; set; } public abstract string PhoneNumber { get; set; }
} }
} }

View File

@@ -1,17 +1,23 @@
using PepperDash.Essentials.Core; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
/// <summary> /// <summary>
/// For rooms that have audio codec /// For rooms that have audio codec
/// </summary> /// </summary>
public interface IHasAudioCodec : IHasInCallFeedback public interface IHasAudioCodec:IHasInCallFeedback
{ {
/// <summary>
/// Gets the audio codec device
/// </summary>
AudioCodecBase AudioCodec { get; } AudioCodecBase AudioCodec { get; }
/// <summary>
/// Make this more specific
/// </summary>
//List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem> ActiveCalls { get; } //List<PepperDash.Essentials.Devices.Common.Codec.CodecActiveCallItem> ActiveCalls { get; }
} }
} }

View File

@@ -1,4 +1,8 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
@@ -13,12 +17,6 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockAC : AudioCodecBase public class MockAC : AudioCodecBase
{ {
/// <summary>
/// Constructor for MockAC
/// </summary>
/// <param name="key">Device key</param>
/// <param name="name">Device name</param>
/// <param name="props">MockAC properties configuration</param>
public MockAC(string key, string name, MockAcPropertiesConfig props) public MockAC(string key, string name, MockAcPropertiesConfig props)
: base(key, name) : base(key, name)
{ {
@@ -111,10 +109,13 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
Debug.LogMessage(LogEventLevel.Debug, this, "BEEP BOOP SendDTMF: {0}", s); Debug.LogMessage(LogEventLevel.Debug, this, "BEEP BOOP SendDTMF: {0}", s);
} }
/// <summary>
///
/// </summary>
/// <param name="url"></param>
/// <summary> /// <summary>
/// TestIncomingAudioCall method /// TestIncomingAudioCall method
/// </summary> /// </summary>
/// <param name="number">Phone number to call from</param>
public void TestIncomingAudioCall(string number) public void TestIncomingAudioCall(string number)
{ {
Debug.LogMessage(LogEventLevel.Debug, this, "TestIncomingAudioCall from {0}", number); Debug.LogMessage(LogEventLevel.Debug, this, "TestIncomingAudioCall from {0}", number);
@@ -132,7 +133,6 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
string _phoneNumber; string _phoneNumber;
/// <inheritdoc />
public override string PhoneNumber public override string PhoneNumber
{ {
get get
@@ -151,9 +151,6 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockACFactory : EssentialsDeviceFactory<MockAC> public class MockACFactory : EssentialsDeviceFactory<MockAC>
{ {
/// <summary>
/// Constructor for MockACFactory
/// </summary>
public MockACFactory() public MockACFactory()
{ {
TypeNames = new List<string>() { "mockac" }; TypeNames = new List<string>() { "mockac" };

View File

@@ -1,4 +1,12 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.AudioCodec namespace PepperDash.Essentials.Devices.Common.AudioCodec
{ {
@@ -7,10 +15,10 @@ namespace PepperDash.Essentials.Devices.Common.AudioCodec
/// </summary> /// </summary>
public class MockAcPropertiesConfig public class MockAcPropertiesConfig
{ {
[JsonProperty("phoneNumber")]
/// <summary> /// <summary>
/// Gets or sets the PhoneNumber /// Gets or sets the PhoneNumber
/// </summary> /// </summary>
[JsonProperty("phoneNumber")]
public string PhoneNumber { get; set; } public string PhoneNumber { get; set; }
} }
} }

View File

@@ -3,14 +3,20 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Crestron.SimplSharp;
using System.Reflection;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Devices; using PepperDash.Essentials.Core.Devices;
using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Presets; using PepperDash.Essentials.Core.Presets;
using PepperDash.Essentials.Devices.Common.Codec;
using Newtonsoft.Json;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
@@ -20,52 +26,31 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public enum eCameraCapabilities public enum eCameraCapabilities
{ {
/// <summary>
/// No camera capabilities
/// </summary>
None = 0, None = 0,
/// <summary>
/// Camera supports pan movement
/// </summary>
Pan = 1, Pan = 1,
/// <summary>
/// Camera supports tilt movement
/// </summary>
Tilt = 2, Tilt = 2,
/// <summary>
/// Camera supports zoom functionality
/// </summary>
Zoom = 4, Zoom = 4,
/// <summary>
/// Camera supports focus adjustment
/// </summary>
Focus = 8 Focus = 8
} }
/// <summary>
/// Abstract base class for camera devices that provides common camera functionality and capabilities
/// </summary>
public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs public abstract class CameraBase : ReconfigurableDevice, IRoutingOutputs
{ {
[JsonProperty("controlMode", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the ControlMode /// Gets or sets the ControlMode
/// </summary> /// </summary>
[JsonProperty("controlMode", NullValueHandling = NullValueHandling.Ignore)]
public eCameraControlMode ControlMode { get; protected set; } public eCameraControlMode ControlMode { get; protected set; }
#region IRoutingOutputs Members #region IRoutingOutputs Members
[JsonIgnore]
/// <summary> /// <summary>
/// Gets or sets the OutputPorts /// Gets or sets the OutputPorts
/// </summary> /// </summary>
[JsonIgnore]
public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; protected set; } public RoutingPortCollection<RoutingOutputPort> OutputPorts { get; protected set; }
#endregion #endregion
/// <summary>
/// Gets a value indicating whether this camera supports pan movement
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
public bool CanPan public bool CanPan
{ {
@@ -74,10 +59,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan; return (Capabilities & eCameraCapabilities.Pan) == eCameraCapabilities.Pan;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports tilt movement
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
public bool CanTilt public bool CanTilt
{ {
@@ -86,10 +67,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt; return (Capabilities & eCameraCapabilities.Tilt) == eCameraCapabilities.Tilt;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports zoom functionality
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
public bool CanZoom public bool CanZoom
{ {
@@ -98,10 +75,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom; return (Capabilities & eCameraCapabilities.Zoom) == eCameraCapabilities.Zoom;
} }
} }
/// <summary>
/// Gets a value indicating whether this camera supports focus adjustment
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
public bool CanFocus public bool CanFocus
{ {
@@ -111,15 +84,9 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
} }
/// <summary> // A bitmasked value to indicate the movement capabilites of this camera
/// Gets or sets a bitmasked value to indicate the movement capabilities of this camera
/// </summary>
protected eCameraCapabilities Capabilities { get; set; } protected eCameraCapabilities Capabilities { get; set; }
/// <summary>
/// Initializes a new instance of the CameraBase class with the specified device configuration
/// </summary>
/// <param name="config">The device configuration</param>
protected CameraBase(DeviceConfig config) : base(config) protected CameraBase(DeviceConfig config) : base(config)
{ {
OutputPorts = new RoutingPortCollection<RoutingOutputPort>(); OutputPorts = new RoutingPortCollection<RoutingOutputPort>();
@@ -128,25 +95,12 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
/// <summary>
/// Initializes a new instance of the CameraBase class with the specified key and name
/// </summary>
/// <param name="key">The unique key for this camera device</param>
/// <param name="name">The friendly name for this camera device</param>
protected CameraBase(string key, string name) : protected CameraBase(string key, string name) :
this(new DeviceConfig { Name = name, Key = key }) this (new DeviceConfig{Name = name, Key = key})
{ {
} }
/// <summary>
/// Links the camera device to the API bridge for control and feedback
/// </summary>
/// <param name="cameraDevice">The camera device to link</param>
/// <param name="trilist">The trilist for communication</param>
/// <param name="joinStart">The starting join number for the camera controls</param>
/// <param name="joinMapKey">The join map key for custom join mappings</param>
/// <param name="bridge">The EiscApiAdvanced bridge for advanced join mapping</param>
protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey, protected void LinkCameraToApi(CameraBase cameraDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
EiscApiAdvanced bridge) EiscApiAdvanced bridge)
{ {
@@ -286,13 +240,13 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
int tempNum = i; int tempNum = i;
trilist.SetSigTrueAction((ushort)(joinMap.PresetRecallStart.JoinNumber + tempNum), () => trilist.SetSigTrueAction((ushort) (joinMap.PresetRecallStart.JoinNumber + tempNum), () =>
{ {
presetsCamera.PresetSelect(tempNum); presetsCamera.PresetSelect(tempNum);
}); });
trilist.SetSigTrueAction((ushort)(joinMap.PresetSaveStart.JoinNumber + tempNum), () => trilist.SetSigTrueAction((ushort) (joinMap.PresetSaveStart.JoinNumber + tempNum), () =>
{ {
var label = trilist.GetString((ushort)(joinMap.PresetLabelStart.JoinNumber + tempNum)); var label = trilist.GetString((ushort) (joinMap.PresetLabelStart.JoinNumber + tempNum));
presetsCamera.PresetStore(tempNum, label); presetsCamera.PresetStore(tempNum, label);
}); });
@@ -331,13 +285,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public class CameraPreset : PresetBase public class CameraPreset : PresetBase
{ {
/// <summary>
/// Initializes a new instance of the CameraPreset class
/// </summary>
/// <param name="id">The preset ID</param>
/// <param name="description">The preset description</param>
/// <param name="isDefined">Whether the preset is defined</param>
/// <param name="isDefinable">Whether the preset can be defined</param>
public CameraPreset(int id, string description, bool isDefined, bool isDefinable) public CameraPreset(int id, string description, bool isDefined, bool isDefinable)
: base(id, description, isDefined, isDefinable) : base(id, description, isDefined, isDefinable)
{ {
@@ -361,22 +308,22 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public ControlPropertiesConfig Control { get; set; } public ControlPropertiesConfig Control { get; set; }
[JsonProperty("supportsAutoMode")]
/// <summary> /// <summary>
/// Gets or sets the SupportsAutoMode /// Gets or sets the SupportsAutoMode
/// </summary> /// </summary>
[JsonProperty("supportsAutoMode")]
public bool SupportsAutoMode { get; set; } public bool SupportsAutoMode { get; set; }
[JsonProperty("supportsOffMode")]
/// <summary> /// <summary>
/// Gets or sets the SupportsOffMode /// Gets or sets the SupportsOffMode
/// </summary> /// </summary>
[JsonProperty("supportsOffMode")]
public bool SupportsOffMode { get; set; } public bool SupportsOffMode { get; set; }
[JsonProperty("presets")]
/// <summary> /// <summary>
/// Gets or sets the Presets /// Gets or sets the Presets
/// </summary> /// </summary>
[JsonProperty("presets")]
public List<CameraPreset> Presets { get; set; } public List<CameraPreset> Presets { get; set; }
} }
} }

View File

@@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Enum for camera control modes
/// </summary>
public enum eCameraControlMode
{
/// <summary>
/// Manual control mode, where the camera is controlled directly by the user or system
/// </summary>
Manual = 0,
/// <summary>
/// Off control mode, where the camera is turned off or disabled
/// </summary>
Off,
/// <summary>
/// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions
/// </summary>
Auto
}
/// <summary>
/// Interface for devices that have cameras
/// </summary>
public interface IHasCameras : IKeyName
{
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs> CameraSelected;
/// <summary>
/// List of cameras on the device. This should be a list of CameraBase objects
/// </summary>
List<CameraBase> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be a CameraBase object
/// </summary>
CameraBase SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key">The unique identifier or name of the camera to select.</param>
void SelectCamera(string key);
}
/// <summary>
/// Defines the contract for IHasCodecCameras
/// </summary>
public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl
{
}
/// <summary>
/// To be implmented on codecs that can disable their camera(s) to blank the near end video
/// </summary>
public interface IHasCameraOff
{
/// <summary>
/// Feedback that indicates whether the camera is off
/// </summary>
BoolFeedback CameraIsOffFeedback { get; }
/// <summary>
/// Turns the camera off, blanking the near end video
/// </summary>
void CameraOff();
}
/// <summary>
/// Describes the ability to mute and unmute camera video
/// </summary>
public interface IHasCameraMute
{
/// <summary>
/// Feedback that indicates whether the camera is muted
/// </summary>
BoolFeedback CameraIsMutedFeedback { get; }
/// <summary>
/// Mutes the camera video, preventing it from being sent to the far end
/// </summary>
void CameraMuteOn();
/// <summary>
/// Unmutes the camera video, allowing it to be sent to the far end
/// </summary>
void CameraMuteOff();
/// <summary>
/// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa.
/// </summary>
void CameraMuteToggle();
}
/// <summary>
/// Interface for devices that can mute and unmute their camera video, with an event for unmute requests
/// </summary>
public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute
{
/// <summary>
/// Event that is raised when a video unmute is requested, typically by the far end
/// </summary>
event EventHandler VideoUnmuteRequested;
}
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
public class CameraSelectedEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the SelectedCamera
/// </summary>
public CameraBase SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(CameraBase camera)
{
SelectedCamera = camera;
}
}
/// <summary>
/// Interface for devices that have a far end camera control
/// </summary>
public interface IHasFarEndCameraControl
{
/// <summary>
/// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call
/// </summary>
CameraBase FarEndCamera { get; }
/// <summary>
/// Feedback that indicates whether the far end camera is being controlled
/// </summary>
BoolFeedback ControllingFarEndCameraFeedback { get; }
}
/// <summary>
/// Defines the contract for IAmFarEndCamera
/// </summary>
public interface IAmFarEndCamera
{
}
/// <summary>
/// Interface for devices that have camera controls
/// </summary>
public interface IHasCameraControls
{
}
/// <summary>
/// Defines the contract for IHasCameraPtzControl
/// </summary>
public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl
{
/// <summary>
/// Resets the camera position
/// </summary>
void PositionHome();
}
/// <summary>
/// Interface for camera pan control
/// </summary>
public interface IHasCameraPanControl : IHasCameraControls
{
/// <summary>
/// Pans the camera left
/// </summary>
void PanLeft();
/// <summary>
/// Pans the camera right
/// </summary>
void PanRight();
/// <summary>
/// Stops the camera pan movement
/// </summary>
void PanStop();
}
/// <summary>
/// Defines the contract for IHasCameraTiltControl
/// </summary>
public interface IHasCameraTiltControl : IHasCameraControls
{
/// <summary>
/// Tilts the camera down
/// </summary>
void TiltDown();
/// <summary>
/// Tilts the camera up
/// </summary>
void TiltUp();
/// <summary>
/// Stops the camera tilt movement
/// </summary>
void TiltStop();
}
/// <summary>
/// Defines the contract for IHasCameraZoomControl
/// </summary>
public interface IHasCameraZoomControl : IHasCameraControls
{
/// <summary>
/// Zooms the camera in
/// </summary>
void ZoomIn();
/// <summary>
/// Zooms the camera out
/// </summary>
void ZoomOut();
/// <summary>
/// Stops the camera zoom movement
/// </summary>
void ZoomStop();
}
/// <summary>
/// Defines the contract for IHasCameraFocusControl
/// </summary>
public interface IHasCameraFocusControl : IHasCameraControls
{
/// <summary>
/// Focuses the camera near
/// </summary>
void FocusNear();
/// <summary>
/// Focuses the camera far
/// </summary>
void FocusFar();
/// <summary>
/// Stops the camera focus movement
/// </summary>
void FocusStop();
/// <summary>
/// Triggers the camera's auto focus functionality, if available.
/// </summary>
void TriggerAutoFocus();
}
/// <summary>
/// Interface for devices that have auto focus mode control
/// </summary>
public interface IHasAutoFocusMode
{
/// <summary>
/// Sets the focus mode to auto or manual, or toggles between them.
/// </summary>
void SetFocusModeAuto();
/// <summary>
/// Sets the focus mode to manual, allowing for manual focus adjustments.
/// </summary>
void SetFocusModeManual();
/// <summary>
/// Toggles the focus mode between auto and manual.
/// </summary>
void ToggleFocusMode();
}
/// <summary>
/// Interface for devices that have camera auto mode control
/// </summary>
public interface IHasCameraAutoMode : IHasCameraControls
{
/// <summary>
/// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings.
/// </summary>
void CameraAutoModeOn();
/// <summary>
/// Disables the camera's auto mode, allowing for manual control of camera settings.
/// </summary>
void CameraAutoModeOff();
/// <summary>
/// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa.
/// </summary>
void CameraAutoModeToggle();
/// <summary>
/// Feedback that indicates whether the camera's auto mode is currently enabled.
/// </summary>
BoolFeedback CameraAutoModeIsOnFeedback { get; }
}
}

View File

@@ -3,13 +3,18 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using Crestron.SimplSharp; using Crestron.SimplSharp;
using Crestron.SimplSharpPro.DeviceSupport; using Crestron.SimplSharpPro.DeviceSupport;
using Newtonsoft.Json;
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using PepperDash.Essentials.Core.Bridges; using PepperDash.Essentials.Core.Bridges;
using PepperDash.Essentials.Core.Config; using PepperDash.Essentials.Core.Config;
using PepperDash.Essentials.Devices.Common.Codec;
using System.Text.RegularExpressions;
using System.Reflection;
using Newtonsoft.Json;
using Serilog.Events; using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
@@ -40,40 +45,19 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// Camera ID (Default 1) /// Camera ID (Default 1)
/// </summary> /// </summary>
public byte ID = 0x01; public byte ID = 0x01;
/// <summary>
/// Response ID used for VISCA communication
/// </summary>
public byte ResponseID; public byte ResponseID;
/// <summary>
/// Slow speed value for pan movement
/// </summary>
public byte PanSpeedSlow = 0x10;
/// <summary> public byte PanSpeedSlow = 0x10;
/// Slow speed value for tilt movement
/// </summary>
public byte TiltSpeedSlow = 0x10; public byte TiltSpeedSlow = 0x10;
/// <summary>
/// Fast speed value for pan movement
/// </summary>
public byte PanSpeedFast = 0x13; public byte PanSpeedFast = 0x13;
/// <summary>
/// Fast speed value for tilt movement
/// </summary>
public byte TiltSpeedFast = 0x13; public byte TiltSpeedFast = 0x13;
// private bool IsMoving; // private bool IsMoving;
private bool IsZooming; private bool IsZooming;
bool _powerIsOn; bool _powerIsOn;
/// <summary>
/// Gets or sets a value indicating whether the camera power is on
/// </summary>
public bool PowerIsOn public bool PowerIsOn
{ {
get get
@@ -104,19 +88,8 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
long FastSpeedHoldTimeMs = 2000; long FastSpeedHoldTimeMs = 2000;
byte[] IncomingBuffer = new byte[] { }; byte[] IncomingBuffer = new byte[] { };
/// <summary>
/// Feedback indicating whether the camera power is on
/// </summary>
public BoolFeedback PowerIsOnFeedback { get; private set; } public BoolFeedback PowerIsOnFeedback { get; private set; }
/// <summary>
/// Initializes a new instance of the CameraVisca class
/// </summary>
/// <param name="key">The unique key for this camera device</param>
/// <param name="name">The friendly name for this camera device</param>
/// <param name="comm">The communication interface for VISCA protocol</param>
/// <param name="props">The camera properties configuration</param>
public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) : public CameraVisca(string key, string name, IBasicCommunication comm, CameraViscaPropertiesConfig props) :
base(key, name) base(key, name)
{ {
@@ -148,8 +121,8 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
} }
Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived); Communication.BytesReceived += new EventHandler<GenericCommMethodReceiveBytesArgs>(Communication_BytesReceived);
PowerIsOnFeedback = new BoolFeedback("powerIsOn", () => { return PowerIsOn; }); PowerIsOnFeedback = new BoolFeedback(() => { return PowerIsOn; });
CameraIsOffFeedback = new BoolFeedback("cameraIsOff", () => { return !PowerIsOn; }); CameraIsOffFeedback = new BoolFeedback(() => { return !PowerIsOn; });
if (props.CommunicationMonitorProperties != null) if (props.CommunicationMonitorProperties != null)
{ {
@@ -382,9 +355,9 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// <summary> /// <summary>
/// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed. /// Sends a pan/tilt command. If the command is not for fastSpeed then it starts a timer to initiate fast speed.
/// </summary> /// </summary>
/// <param name="cmd">The VISCA command to send</param> /// <param name="cmd"></param>
/// <param name="fastSpeedEnabled">Whether fast speed is enabled for this command</param> /// <param name="fastSpeed"></param>
private void SendPanTiltCommand(byte[] cmd, bool fastSpeedEnabled) private void SendPanTiltCommand (byte[] cmd, bool fastSpeedEnabled)
{ {
SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled)); SendBytes(GetPanTiltCommand(cmd, fastSpeedEnabled));
@@ -482,7 +455,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public void PowerOff() public void PowerOff()
{ {
SendBytes(new byte[] { ID, 0x01, 0x04, 0x00, 0x03, 0xFF }); SendBytes(new byte[] {ID, 0x01, 0x04, 0x00, 0x03, 0xFF});
SendPowerQuery(); SendPowerQuery();
} }
@@ -502,7 +475,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public void PanLeft() public void PanLeft()
{ {
SendPanTiltCommand(new byte[] { 0x01, 0x03 }, false); SendPanTiltCommand(new byte[] {0x01, 0x03}, false);
// IsMoving = true; // IsMoving = true;
} }
/// <summary> /// <summary>
@@ -544,9 +517,9 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
Stop(); Stop();
} }
private void SendZoomCommand(byte cmd) private void SendZoomCommand (byte cmd)
{ {
SendBytes(new byte[] { ID, 0x01, 0x04, 0x07, cmd, 0xFF }); SendBytes(new byte[] {ID, 0x01, 0x04, 0x07, cmd, 0xFF} );
} }
@@ -604,7 +577,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public void RecallPreset(int presetNumber) public void RecallPreset(int presetNumber)
{ {
SendBytes(new byte[] { ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF }); SendBytes(new byte[] {ID, 0x01, 0x04, 0x3F, 0x02, (byte)presetNumber, 0xFF} );
} }
/// <summary> /// <summary>
/// SavePreset method /// SavePreset method
@@ -616,14 +589,8 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
#region IHasCameraPresets Members #region IHasCameraPresets Members
/// <summary>
/// Event that is raised when the presets list has changed
/// </summary>
public event EventHandler<EventArgs> PresetsListHasChanged; public event EventHandler<EventArgs> PresetsListHasChanged;
/// <summary>
/// Raises the PresetsListHasChanged event
/// </summary>
protected void OnPresetsListHasChanged() protected void OnPresetsListHasChanged()
{ {
var handler = PresetsListHasChanged; var handler = PresetsListHasChanged;
@@ -774,9 +741,6 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public class CameraViscaFactory : EssentialsDeviceFactory<CameraVisca> public class CameraViscaFactory : EssentialsDeviceFactory<CameraVisca>
{ {
/// <summary>
/// Initializes a new instance of the CameraViscaFactory class
/// </summary>
public CameraViscaFactory() public CameraViscaFactory()
{ {
TypeNames = new List<string>() { "cameravisca" }; TypeNames = new List<string>() { "cameravisca" };
@@ -804,9 +768,11 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
/// <summary> /// <summary>
/// Control ID of the camera (1-7) /// Control ID of the camera (1-7)
/// Gets or sets the Id
/// </summary> /// </summary>
[JsonProperty("id")] [JsonProperty("id")]
/// <summary>
/// Gets or sets the Id
/// </summary>
public uint Id { get; set; } public uint Id { get; set; }
/// <summary> /// <summary>

View File

@@ -1,5 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Cameras namespace PepperDash.Essentials.Devices.Common.Cameras
{ {
@@ -8,27 +11,12 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
/// </summary> /// </summary>
public interface IHasCameraPresets public interface IHasCameraPresets
{ {
/// <summary>
/// Event that is raised when the presets list has changed
/// </summary>
event EventHandler<EventArgs> PresetsListHasChanged; event EventHandler<EventArgs> PresetsListHasChanged;
/// <summary>
/// Gets the list of camera presets
/// </summary>
List<CameraPreset> Presets { get; } List<CameraPreset> Presets { get; }
/// <summary>
/// Selects the specified preset
/// </summary>
/// <param name="preset">The preset number to select</param>
void PresetSelect(int preset); void PresetSelect(int preset);
/// <summary>
/// Stores a preset at the specified location with the given description
/// </summary>
/// <param name="preset">The preset number to store</param>
/// <param name="description">The description for the preset</param>
void PresetStore(int preset, string description); void PresetStore(int preset, string description);
} }
} }

View File

@@ -1,49 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
[Obsolete("Use CameraSelectedEventArgs<T> instead. This class will be removed in a future version")]
public class CameraSelectedEventArgs : EventArgs
{
/// Gets or sets the SelectedCamera
/// </summary>
public CameraBase SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(CameraBase camera)
{
SelectedCamera = camera;
}
}
/// <summary>
/// Event arguments for the CameraSelected event
/// </summary>
/// <typeparam name="T"></typeparam>
public class CameraSelectedEventArgs<T> : EventArgs
{
/// <summary>
/// Gets or sets the SelectedCamera
/// </summary>
public T SelectedCamera { get; private set; }
/// <summary>
/// Constructor for CameraSelectedEventArgs
/// </summary>
/// <param name="camera"></param>
public CameraSelectedEventArgs(T camera)
{
SelectedCamera = camera;
}
}
}

View File

@@ -1,12 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IAmFarEndCamera
/// </summary>
public interface IAmFarEndCamera : IKeyName
{
}
}

View File

@@ -1,86 +0,0 @@
using Newtonsoft.Json;
using PepperDash.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for camera capabilities
/// </summary>
public interface ICameraCapabilities: IKeyName
{
/// <summary>
/// Indicates whether the camera can pan
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
bool CanPan { get; }
/// <summary>
/// Indicates whether the camera can tilt
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
bool CanTilt { get; }
/// <summary>
/// Indicates whether the camera can zoom
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
bool CanZoom { get; }
/// <summary>
/// Indicates whether the camera can focus
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
bool CanFocus { get; }
}
/// <summary>
/// Indicates the capabilities of a camera
/// </summary>
public class CameraCapabilities : ICameraCapabilities
{
/// <summary>
/// Unique Key
/// </summary>
[JsonProperty("key", NullValueHandling = NullValueHandling.Ignore)]
public string Key { get; set; }
/// <summary>
/// Isn't it obvious :)
/// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
/// <summary>
/// Indicates whether the camera can pan
/// </summary>
[JsonProperty("canPan", NullValueHandling = NullValueHandling.Ignore)]
public bool CanPan { get; set; }
/// <summary>
/// Indicates whether the camera can tilt
/// </summary>
[JsonProperty("canTilt", NullValueHandling = NullValueHandling.Ignore)]
public bool CanTilt { get; set; }
/// <summary>
/// Indicates whether the camera can zoom
/// </summary>
[JsonProperty("canZoom", NullValueHandling = NullValueHandling.Ignore)]
public bool CanZoom { get; set; }
/// <summary>
/// Indicates whether the camera can focus
/// </summary>
[JsonProperty("canFocus", NullValueHandling = NullValueHandling.Ignore)]
public bool CanFocus { get; set; }
}
}

View File

@@ -1,24 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have auto focus mode control
/// </summary>
public interface IHasAutoFocusMode : IHasCameraControls
{
/// <summary>
/// Sets the focus mode to auto or manual, or toggles between them.
/// </summary>
void SetFocusModeAuto();
/// <summary>
/// Sets the focus mode to manual, allowing for manual focus adjustments.
/// </summary>
void SetFocusModeManual();
/// <summary>
/// Toggles the focus mode between auto and manual.
/// </summary>
void ToggleFocusMode();
}
}

View File

@@ -1,31 +0,0 @@
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have camera auto mode control
/// </summary>
public interface IHasCameraAutoMode : IHasCameraControls
{
/// <summary>
/// Enables or disables the camera's auto mode, which may include automatic adjustments for focus, exposure, and other settings.
/// </summary>
void CameraAutoModeOn();
/// <summary>
/// Disables the camera's auto mode, allowing for manual control of camera settings.
/// </summary>
void CameraAutoModeOff();
/// <summary>
/// Toggles the camera's auto mode state. If the camera is in auto mode, it will switch to manual mode, and vice versa.
/// </summary>
void CameraAutoModeToggle();
/// <summary>
/// Feedback that indicates whether the camera's auto mode is currently enabled.
/// </summary>
BoolFeedback CameraAutoModeIsOnFeedback { get; }
}
}

View File

@@ -1,13 +0,0 @@
using PepperDash.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have camera controls
/// </summary>
public interface IHasCameraControls : IKeyName
{
}
}

View File

@@ -1,29 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraFocusControl
/// </summary>
public interface IHasCameraFocusControl : IHasCameraControls
{
/// <summary>
/// Focuses the camera near
/// </summary>
void FocusNear();
/// <summary>
/// Focuses the camera far
/// </summary>
void FocusFar();
/// <summary>
/// Stops the camera focus movement
/// </summary>
void FocusStop();
/// <summary>
/// Triggers the camera's auto focus functionality, if available.
/// </summary>
void TriggerAutoFocus();
}
}

View File

@@ -1,31 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Describes the ability to mute and unmute camera video
/// </summary>
public interface IHasCameraMute : IKeyName
{
/// <summary>
/// Feedback that indicates whether the camera is muted
/// </summary>
BoolFeedback CameraIsMutedFeedback { get; }
/// <summary>
/// Mutes the camera video, preventing it from being sent to the far end
/// </summary>
void CameraMuteOn();
/// <summary>
/// Unmutes the camera video, allowing it to be sent to the far end
/// </summary>
void CameraMuteOff();
/// <summary>
/// Toggles the camera mute state. If the camera is muted, it will be unmuted, and vice versa.
/// </summary>
void CameraMuteToggle();
}
}

View File

@@ -1,16 +0,0 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that can mute and unmute their camera video, with an event for unmute requests
/// </summary>
public interface IHasCameraMuteWithUnmuteReqeust : IHasCameraMute
{
/// <summary>
/// Event that is raised when a video unmute is requested, typically by the far end
/// </summary>
event EventHandler VideoUnmuteRequested;
}
}

View File

@@ -1,27 +0,0 @@
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// To be implmented on codecs that can disable their camera(s) to blank the near end video
/// </summary>
public interface IHasCameraOff : IHasCameraControls
{
/// <summary>
/// Feedback that indicates whether the camera is off
/// </summary>
BoolFeedback CameraIsOffFeedback { get; }
/// <summary>
/// Turns the camera off, blanking the near end video
/// </summary>
void CameraOff();
}
}

View File

@@ -1,24 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for camera pan control
/// </summary>
public interface IHasCameraPanControl : IHasCameraControls
{
/// <summary>
/// Pans the camera left
/// </summary>
void PanLeft();
/// <summary>
/// Pans the camera right
/// </summary>
void PanRight();
/// <summary>
/// Stops the camera pan movement
/// </summary>
void PanStop();
}
}

View File

@@ -1,14 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraPtzControl
/// </summary>
public interface IHasCameraPtzControl : IHasCameraPanControl, IHasCameraTiltControl, IHasCameraZoomControl
{
/// <summary>
/// Resets the camera position
/// </summary>
void PositionHome();
}
}

View File

@@ -1,24 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraTiltControl
/// </summary>
public interface IHasCameraTiltControl : IHasCameraControls
{
/// <summary>
/// Tilts the camera down
/// </summary>
void TiltDown();
/// <summary>
/// Tilts the camera up
/// </summary>
void TiltUp();
/// <summary>
/// Stops the camera tilt movement
/// </summary>
void TiltStop();
}
}

View File

@@ -1,24 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCameraZoomControl
/// </summary>
public interface IHasCameraZoomControl : IHasCameraControls
{
/// <summary>
/// Zooms the camera in
/// </summary>
void ZoomIn();
/// <summary>
/// Zooms the camera out
/// </summary>
void ZoomOut();
/// <summary>
/// Stops the camera zoom movement
/// </summary>
void ZoomStop();
}
}

View File

@@ -1,41 +0,0 @@
using Newtonsoft.Json;
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have cameras
/// </summary>
[Obsolete("Use IHasCamerasWithControls instead. This interface will be removed in a future version")]
public interface IHasCameras : IKeyName
{
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs> CameraSelected;
/// <summary>
/// List of cameras on the device. This should be a list of CameraBase objects
/// </summary>
List<CameraBase> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be a CameraBase object
/// </summary>
CameraBase SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key">The unique identifier or name of the camera to select.</param>
void SelectCamera(string key);
}
}

View File

@@ -1,40 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have cameras with controls
/// </summary>
public interface IHasCamerasWithControls : IKeyName, IKeyed
{
/// <summary>
/// List of cameras on the device. This should be a list of IHasCameraControls objects
/// </summary>
List<IHasCameraControls> Cameras { get; }
/// <summary>
/// The currently selected camera. This should be an IHasCameraControls object
/// </summary>
IHasCameraControls SelectedCamera { get; }
/// <summary>
/// Feedback that indicates the currently selected camera
/// </summary>
StringFeedback SelectedCameraFeedback { get; }
/// <summary>
/// Event that is raised when a camera is selected
/// </summary>
event EventHandler<CameraSelectedEventArgs<IHasCameraControls>> CameraSelected;
/// <summary>
/// Selects a camera from the list of available cameras based on the provided key.
/// </summary>
/// <param name="key"></param>
void SelectCamera(string key);
}
}

View File

@@ -1,13 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Defines the contract for IHasCodecCameras
/// </summary>
public interface IHasCodecCameras : IHasCameras, IHasFarEndCameraControl
{
}
}

View File

@@ -1,23 +0,0 @@
using PepperDash.Core;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Interface for devices that have a far end camera control
/// </summary>
public interface IHasFarEndCameraControl : IKeyName
{
/// <summary>
/// Gets the far end camera, which is typically a CameraBase object that represents the camera at the far end of a call
/// </summary>
CameraBase FarEndCamera { get; }
/// <summary>
/// Feedback that indicates whether the far end camera is being controlled
/// </summary>
BoolFeedback ControllingFarEndCameraFeedback { get; }
}
}

View File

@@ -1,24 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Cameras
{
/// <summary>
/// Enum for camera control modes
/// </summary>
public enum eCameraControlMode
{
/// <summary>
/// Manual control mode, where the camera is controlled directly by the user or system
/// </summary>
Manual = 0,
/// <summary>
/// Off control mode, where the camera is turned off or disabled
/// </summary>
Off,
/// <summary>
/// Auto control mode, where the camera automatically adjusts settings based on the environment or conditions
/// </summary>
Auto
}
}

View File

@@ -1,26 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a Call
/// </summary>
public class Call
{
/// <summary>
/// Gets or sets the Number
/// </summary>
public string Number { get; set; }
/// <summary>
/// Gets or sets the Protocol
/// </summary>
public string Protocol { get; set; }
/// <summary>
/// Gets or sets the CallRate
/// </summary>
public string CallRate { get; set; }
/// <summary>
/// Gets or sets the CallType
/// </summary>
public string CallType { get; set; }
}
}

View File

@@ -1,5 +1,10 @@
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{ {

View File

@@ -1,5 +1,10 @@
using PepperDash.Core; using PepperDash.Core;
using PepperDash.Essentials.Core; using PepperDash.Essentials.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
{ {

View File

@@ -1,6 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Converters; using Newtonsoft.Json.Converters;
@@ -13,55 +17,55 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public class CodecActiveCallItem public class CodecActiveCallItem
{ {
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Name /// Gets or sets the Name
/// </summary> /// </summary>
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; } public string Name { get; set; }
[JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Number /// Gets or sets the Number
/// </summary> /// </summary>
[JsonProperty("number", NullValueHandling = NullValueHandling.Ignore)]
public string Number { get; set; } public string Number { get; set; }
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Type /// Gets or sets the Type
/// </summary> /// </summary>
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallType Type { get; set; } public eCodecCallType Type { get; set; }
[JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Status /// Gets or sets the Status
/// </summary> /// </summary>
[JsonProperty("status", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallStatus Status { get; set; } public eCodecCallStatus Status { get; set; }
[JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
/// <summary> /// <summary>
/// Gets or sets the Direction /// Gets or sets the Direction
/// </summary> /// </summary>
[JsonProperty("direction", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(StringEnumConverter))]
public eCodecCallDirection Direction { get; set; } public eCodecCallDirection Direction { get; set; }
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Id /// Gets or sets the Id
/// </summary> /// </summary>
[JsonProperty("id", NullValueHandling = NullValueHandling.Ignore)]
public string Id { get; set; } public string Id { get; set; }
[JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the IsOnHold /// Gets or sets the IsOnHold
/// </summary> /// </summary>
[JsonProperty("isOnHold", NullValueHandling = NullValueHandling.Ignore)]
public bool IsOnHold { get; set; } public bool IsOnHold { get; set; }
[JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)]
/// <summary> /// <summary>
/// Gets or sets the Duration /// Gets or sets the Duration
/// </summary> /// </summary>
[JsonProperty("duration", NullValueHandling = NullValueHandling.Ignore)]
public TimeSpan Duration { get; set; } public TimeSpan Duration { get; set; }
//public object CallMetaData { get; set; } //public object CallMetaData { get; set; }
@@ -93,10 +97,6 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public CodecActiveCallItem CallItem { get; private set; } public CodecActiveCallItem CallItem { get; private set; }
/// <summary>
/// Initializes a new instance of the CodecCallStatusItemChangeEventArgs class
/// </summary>
/// <param name="item">The call item that changed</param>
public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item) public CodecCallStatusItemChangeEventArgs(CodecActiveCallItem item)
{ {
CallItem = item; CallItem = item;

View File

@@ -1,121 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a codec directory
/// </summary>
public class CodecDirectory
{
/// <summary>
/// Represents the contents of the directory
/// We don't want to serialize this for messages to MobileControl. MC can combine Contacts and Folders to get the same data
/// </summary>
[JsonIgnore]
public List<DirectoryItem> CurrentDirectoryResults { get; private set; }
/// <summary>
/// Gets the Contacts in the CurrentDirectoryResults
/// </summary>
[JsonProperty("contacts")]
public List<DirectoryItem> Contacts
{
get
{
return CurrentDirectoryResults.OfType<DirectoryContact>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Gets the Folders in the CurrentDirectoryResults
/// </summary>
[JsonProperty("folders")]
public List<DirectoryItem> Folders
{
get
{
return CurrentDirectoryResults.OfType<DirectoryFolder>().Cast<DirectoryItem>().ToList();
}
}
/// <summary>
/// Used to store the ID of the current folder for CurrentDirectoryResults
/// Gets or sets the ResultsFolderId
/// </summary>
[JsonProperty("resultsFolderId")]
public string ResultsFolderId { get; set; }
/// <summary>
/// Constructor for <see cref="CodecDirectory"/>
/// </summary>
public CodecDirectory()
{
CurrentDirectoryResults = new List<DirectoryItem>();
}
/// <summary>
/// Adds folders to the directory
/// </summary>
/// <param name="folders"></param>
public void AddFoldersToDirectory(List<DirectoryItem> folders)
{
if (folders != null)
CurrentDirectoryResults.AddRange(folders);
SortDirectory();
}
/// <summary>
/// Adds contacts to the directory
/// </summary>
/// <param name="contacts"></param>
public void AddContactsToDirectory(List<DirectoryItem> contacts)
{
if (contacts != null)
CurrentDirectoryResults.AddRange(contacts);
SortDirectory();
}
/// <summary>
/// Filters the CurrentDirectoryResults by the predicate
/// </summary>
/// <param name="predicate"></param>
public void FilterContacts(Func<DirectoryItem, bool> predicate)
{
CurrentDirectoryResults = CurrentDirectoryResults.Where(predicate).ToList();
}
/// <summary>
/// Sorts the DirectoryResults list to display all folders alphabetically, then all contacts alphabetically
/// </summary>
private void SortDirectory()
{
var sortedFolders = new List<DirectoryItem>();
sortedFolders.AddRange(CurrentDirectoryResults.Where(f => f is DirectoryFolder));
sortedFolders.OrderBy(f => f.Name);
var sortedContacts = new List<DirectoryItem>();
sortedContacts.AddRange(CurrentDirectoryResults.Where(c => c is DirectoryContact));
sortedFolders.OrderBy(c => c.Name);
CurrentDirectoryResults.Clear();
CurrentDirectoryResults.AddRange(sortedFolders);
CurrentDirectoryResults.AddRange(sortedContacts);
}
}
}

View File

@@ -1,146 +0,0 @@
using System;
using System.Collections.Generic;
using Crestron.SimplSharp;
using PepperDash.Core;
using Serilog.Events;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a CodecScheduleAwareness
/// </summary>
public class CodecScheduleAwareness
{
List<Meeting> _meetings;
/// <summary>
/// Event that is raised when a meeting event changes
/// </summary>
public event EventHandler<MeetingEventArgs> MeetingEventChange;
/// <summary>
/// Event that is raised when the meetings list has changed
/// </summary>
public event EventHandler<EventArgs> MeetingsListHasChanged;
private int _meetingWarningMinutes = 5;
//private Meeting _previousChangedMeeting;
//private eMeetingEventChangeType _previousChangeType = eMeetingEventChangeType.Unknown;
/// <summary>
/// Gets or sets the number of minutes before a meeting to issue a warning
/// </summary>
public int MeetingWarningMinutes
{
get { return _meetingWarningMinutes; }
set { _meetingWarningMinutes = value; }
}
/// <summary>
/// Setter triggers MeetingsListHasChanged event
/// </summary>
public List<Meeting> Meetings
{
get
{
return _meetings;
}
set
{
_meetings = value;
MeetingsListHasChanged?.Invoke(this, new EventArgs());
}
}
private readonly CTimer _scheduleChecker;
/// <summary>
/// Initializes a new instance of the CodecScheduleAwareness class with default poll time
/// </summary>
public CodecScheduleAwareness()
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, 1000, 1000);
}
/// <summary>
/// Initializes a new instance of the CodecScheduleAwareness class with specified poll time
/// </summary>
/// <param name="pollTime">The poll time in milliseconds for checking schedule changes</param>
public CodecScheduleAwareness(long pollTime)
{
Meetings = new List<Meeting>();
_scheduleChecker = new CTimer(CheckSchedule, null, pollTime, pollTime);
}
/// <summary>
/// Helper method to fire MeetingEventChange. Should only fire once for each changeType on each meeting
/// </summary>
/// <param name="changeType"></param>
/// <param name="meeting"></param>
private void OnMeetingChange(eMeetingEventChangeType changeType, Meeting meeting)
{
Debug.LogMessage(LogEventLevel.Verbose, "*****************OnMeetingChange. id: {0} changeType: {1}**********************", meeting.Id, changeType);
if (changeType != (changeType & meeting.NotifiedChangeTypes))
{
// Add this change type to the NotifiedChangeTypes
meeting.NotifiedChangeTypes |= changeType;
MeetingEventChange?.Invoke(this, new MeetingEventArgs() { ChangeType = changeType, Meeting = meeting });
}
else
{
Debug.LogMessage(LogEventLevel.Verbose, "Meeting: {0} already notified of changeType: {1}", meeting.Id, changeType);
}
}
/// <summary>
/// Checks the schedule to see if any MeetingEventChange updates should be fired
/// </summary>
/// <param name="o"></param>
private void CheckSchedule(object o)
{
// Iterate the meeting list and check if any meeting need to do anything
const double meetingTimeEpsilon = 0.05;
foreach (var m in Meetings)
{
var changeType = eMeetingEventChangeType.Unknown;
if (eMeetingEventChangeType.MeetingStartWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStartWarning) && m.TimeToMeetingStart.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingStart.Seconds > 0) // Meeting is about to start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStartWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingStart.TotalMinutes, m.TimeToMeetingStart.Seconds);
changeType = eMeetingEventChangeType.MeetingStartWarning;
}
else if (eMeetingEventChangeType.MeetingStart != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingStart) && Math.Abs(m.TimeToMeetingStart.TotalMinutes) < meetingTimeEpsilon) // Meeting Start
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingStart");
changeType = eMeetingEventChangeType.MeetingStart;
}
else if (eMeetingEventChangeType.MeetingEndWarning != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEndWarning) && m.TimeToMeetingEnd.TotalMinutes <= m.MeetingWarningMinutes.TotalMinutes && m.TimeToMeetingEnd.Seconds > 0) // Meeting is about to end
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEndWarning. TotalMinutes: {0} Seconds: {1}", m.TimeToMeetingEnd.TotalMinutes, m.TimeToMeetingEnd.Seconds);
changeType = eMeetingEventChangeType.MeetingEndWarning;
}
else if (eMeetingEventChangeType.MeetingEnd != (m.NotifiedChangeTypes & eMeetingEventChangeType.MeetingEnd) && Math.Abs(m.TimeToMeetingEnd.TotalMinutes) < meetingTimeEpsilon) // Meeting has ended
{
Debug.LogMessage(LogEventLevel.Verbose, "********************* MeetingEnd");
changeType = eMeetingEventChangeType.MeetingEnd;
}
if (changeType != eMeetingEventChangeType.Unknown)
{
OnMeetingChange(changeType, m);
}
}
}
}
}

View File

@@ -1,37 +0,0 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a ContactMethod
/// </summary>
public class ContactMethod
{
/// <summary>
/// Gets or sets the ContactMethodId
/// </summary>
[JsonProperty("contactMethodId")]
public string ContactMethodId { get; set; }
/// <summary>
/// Gets or sets the Number
/// </summary>
[JsonProperty("number")]
public string Number { get; set; }
/// <summary>
/// Gets or sets the Device
/// </summary>
[JsonProperty("device")]
[JsonConverter(typeof(StringEnumConverter))]
public eContactMethodDevice Device { get; set; }
/// <summary>
/// Gets or sets the CallType
/// </summary>
[JsonProperty("callType")]
[JsonConverter(typeof(StringEnumConverter))]
public eContactMethodCallType CallType { get; set; }
}
}

View File

@@ -1,39 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryContact
/// </summary>
public class DirectoryContact : DirectoryItem
{
/// <summary>
/// Gets or sets the ContactId
/// </summary>
[JsonProperty("contactId")]
public string ContactId { get; set; }
/// <summary>
/// Gets or sets the Title
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the ContactMethods
/// </summary>
[JsonProperty("contactMethods")]
public List<ContactMethod> ContactMethods { get; set; }
/// <summary>
/// Constructor for <see cref="DirectoryContact"/>
/// </summary>
public DirectoryContact()
{
ContactMethods = new List<ContactMethod>();
}
}
}

View File

@@ -1,21 +0,0 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryEventArgs
/// </summary>
public class DirectoryEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the Directory
/// </summary>
public CodecDirectory Directory { get; set; }
/// <summary>
/// Gets or sets the DirectoryIsOnRoot
/// </summary>
public bool DirectoryIsOnRoot { get; set; }
}
}

View File

@@ -1,27 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryFolder
/// </summary>
public class DirectoryFolder : DirectoryItem
{
/// <summary>
/// Gets or sets the Contacts
/// </summary>
[JsonProperty("contacts")]
public List<DirectoryContact> Contacts { get; set; }
/// <summary>
/// Constructor for <see cref="DirectoryFolder"/>
/// </summary>
public DirectoryFolder()
{
Contacts = new List<DirectoryContact>();
}
}
}

View File

@@ -1,41 +0,0 @@
using System;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a DirectoryItem
/// </summary>
public class DirectoryItem : ICloneable
{
/// <summary>
/// Clone method
/// </summary>
public object Clone()
{
return MemberwiseClone();
}
/// <summary>
/// Gets or sets the FolderId
/// </summary>
[JsonProperty("folderId")]
public string FolderId { get; set; }
/// <summary>
/// Gets or sets the Name
/// </summary>
[JsonProperty("name")]
public string Name { get; set; }
/// <summary>
/// Gets or sets the ParentFolderId
/// </summary>
[JsonProperty("parentFolderId")]
public string ParentFolderId { get; set; }
}
}

View File

@@ -1,65 +1,31 @@
namespace PepperDash.Essentials.Devices.Common.Codec using Crestron.SimplSharpPro;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Describes a cisco codec device that can allow configuration of cameras /// Describes a cisco codec device that can allow configuration of cameras
/// </summary> /// </summary>
public interface ICiscoCodecCameraConfig public interface ICiscoCodecCameraConfig
{ {
/// <summary>
/// Sets the assigned serial number for the specified camera
/// </summary>
/// <param name="cameraId">The camera identifier</param>
/// <param name="serialNumber">The serial number to assign</param>
void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber); void SetCameraAssignedSerialNumber(uint cameraId, string serialNumber);
/// <summary>
/// Sets the name for the camera on the specified video connector
/// </summary>
/// <param name="videoConnectorId">The video connector identifier</param>
/// <param name="name">The name to assign</param>
void SetCameraName(uint videoConnectorId, string name); void SetCameraName(uint videoConnectorId, string name);
/// <summary>
/// Sets the input source type for the specified video connector
/// </summary>
/// <param name="videoConnectorId">The video connector identifier</param>
/// <param name="sourceType">The source type to set</param>
void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType); void SetInputSourceType(uint videoConnectorId, eCiscoCodecInputSourceType sourceType);
} }
/// <summary>
/// Enumeration of Cisco codec input source types
/// </summary>
public enum eCiscoCodecInputSourceType public enum eCiscoCodecInputSourceType
{ {
/// <summary>
/// PC source type
/// </summary>
PC, PC,
/// <summary>
/// Camera source type
/// </summary>
camera, camera,
/// <summary>
/// Document camera source type
/// </summary>
document_camera, document_camera,
/// <summary>
/// Media player source type
/// </summary>
mediaplayer, mediaplayer,
/// <summary>
/// Other source type
/// </summary>
other, other,
/// <summary>
/// Whiteboard source type
/// </summary>
whiteboard whiteboard
} }
} }

View File

@@ -1,4 +1,10 @@
namespace PepperDash.Essentials.Devices.Common.Codec using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
namespace PepperDash.Essentials.Devices.Common.Codec
{ {
/// <summary> /// <summary>
/// Defines the contract for IHasCallHold /// Defines the contract for IHasCallHold

View File

@@ -1,14 +0,0 @@
using System.Collections.Generic;
using PepperDash.Essentials.Devices.Common.Codec;
/// <summary>
/// Defines the contract for IHasDirectoryHistoryStack
/// </summary>
public interface IHasDirectoryHistoryStack : IHasDirectory
{
/// <summary>
/// Gets the DirectoryBrowseHistoryStack
/// </summary>
Stack<CodecDirectory> DirectoryBrowseHistoryStack { get; }
}

View File

@@ -1,4 +1,10 @@
using PepperDash.Essentials.Core; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
{ {
@@ -28,9 +34,6 @@ namespace PepperDash.Essentials.Devices.Common.Codec
void ToggleDoNotDisturbMode(); void ToggleDoNotDisturbMode();
} }
/// <summary>
/// Defines the contract for devices that support Do Not Disturb mode with timeout functionality
/// </summary>
public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode public interface IHasDoNotDisturbModeWithTimeout : IHasDoNotDisturbMode
{ {
/// <summary> /// <summary>

View File

@@ -1,4 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Crestron.SimplSharp;
using PepperDash.Essentials.Core;
using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco; using PepperDash.Essentials.Devices.Common.VideoCodec.Cisco;
namespace PepperDash.Essentials.Devices.Common.Codec namespace PepperDash.Essentials.Devices.Common.Codec
@@ -8,47 +13,13 @@ namespace PepperDash.Essentials.Devices.Common.Codec
/// </summary> /// </summary>
public interface IHasExternalSourceSwitching public interface IHasExternalSourceSwitching
{ {
/// <summary>
/// Gets a value indicating whether the external source list is enabled
/// </summary>
bool ExternalSourceListEnabled { get; } bool ExternalSourceListEnabled { get; }
/// <summary>
/// Gets the external source input port identifier
/// </summary>
string ExternalSourceInputPort { get; } string ExternalSourceInputPort { get; }
/// <summary>
/// Adds an external source to the available sources
/// </summary>
/// <param name="connectorId">The connector identifier</param>
/// <param name="key">The unique key for the source</param>
/// <param name="name">The display name for the source</param>
/// <param name="type">The type of external source</param>
void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type); void AddExternalSource(string connectorId, string key, string name, eExternalSourceType type);
/// <summary>
/// Sets the state of the specified external source
/// </summary>
/// <param name="key">The unique key of the external source</param>
/// <param name="mode">The mode to set for the source</param>
void SetExternalSourceState(string key, eExternalSourceMode mode); void SetExternalSourceState(string key, eExternalSourceMode mode);
/// <summary>
/// Clears all external sources from the list
/// </summary>
void ClearExternalSources(); void ClearExternalSources();
/// <summary>
/// Sets the selected source by its key
/// </summary>
/// <param name="key">The unique key of the source to select</param>
void SetSelectedSource(string key); void SetSelectedSource(string key);
Action<string, string> RunRouteAction { set;}
/// <summary>
/// Sets the action to run when routing between sources
/// </summary>
Action<string, string> RunRouteAction { set; }
} }
} }

View File

@@ -1,13 +0,0 @@
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Defines the contract for IInvitableContact
/// </summary>
public interface IInvitableContact
{
/// <summary>
/// Gets a value indicating whether this contact is invitable
/// </summary>
bool IsInvitableContact { get; }
}
}

View File

@@ -1,22 +0,0 @@
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents an InvitableDirectoryContact
/// </summary>
public class InvitableDirectoryContact : DirectoryContact, IInvitableContact
{
/// <summary>
/// Gets a value indicating whether this contact is invitable
/// </summary>
[JsonProperty("isInvitableContact")]
public bool IsInvitableContact
{
get
{
return this is IInvitableContact;
}
}
}
}

View File

@@ -1,182 +0,0 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a Meeting
/// </summary>
public class Meeting
{
/// <summary>
/// Minutes before the meeting to show warning
/// </summary>
[JsonProperty("minutesBeforeMeeting")]
public int MinutesBeforeMeeting;
/// <summary>
/// Gets or sets the meeting ID
/// </summary>
[JsonProperty("id")]
public string Id { get; set; }
/// <summary>
/// Gets or sets the meeting organizer
/// </summary>
[JsonProperty("organizer")]
public string Organizer { get; set; }
/// <summary>
/// Gets or sets the Title
/// </summary>
[JsonProperty("title")]
public string Title { get; set; }
/// <summary>
/// Gets or sets the Agenda
/// </summary>
[JsonProperty("agenda")]
public string Agenda { get; set; }
/// <summary>
/// Gets the meeting warning time span in minutes before the meeting starts
/// </summary>
[JsonProperty("meetingWarningMinutes")]
public TimeSpan MeetingWarningMinutes
{
get { return TimeSpan.FromMinutes(MinutesBeforeMeeting); }
}
/// <summary>
/// Gets the time remaining until the meeting starts
/// </summary>
[JsonProperty("timeToMeetingStart")]
public TimeSpan TimeToMeetingStart
{
get
{
return StartTime - DateTime.Now;
}
}
/// <summary>
/// Gets the time remaining until the meeting ends
/// </summary>
[JsonProperty("timeToMeetingEnd")]
public TimeSpan TimeToMeetingEnd
{
get
{
return EndTime - DateTime.Now;
}
}
/// <summary>
/// Gets or sets the StartTime
/// </summary>
[JsonProperty("startTime")]
public DateTime StartTime { get; set; }
/// <summary>
/// Gets or sets the EndTime
/// </summary>
[JsonProperty("endTime")]
public DateTime EndTime { get; set; }
/// <summary>
/// Gets the duration of the meeting
/// </summary>
[JsonProperty("duration")]
public TimeSpan Duration
{
get
{
return EndTime - StartTime;
}
}
/// <summary>
/// Gets or sets the Privacy
/// </summary>
[JsonProperty("privacy")]
public eMeetingPrivacy Privacy { get; set; }
/// <summary>
/// Gets a value indicating whether the meeting can be joined
/// </summary>
[JsonProperty("joinable")]
public bool Joinable
{
get
{
var joinable = StartTime.AddMinutes(-MinutesBeforeMeeting) <= DateTime.Now
&& DateTime.Now <= EndTime.AddSeconds(-_joinableCooldownSeconds);
//Debug.LogMessage(LogEventLevel.Verbose, "Meeting Id: {0} joinable: {1}", Id, joinable);
return joinable;
}
}
/// <summary>
/// Gets or sets the Dialable
/// </summary>
[JsonProperty("dialable")]
public bool Dialable { get; set; }
//public string ConferenceNumberToDial { get; set; }
/// <summary>
/// Gets or sets the ConferencePassword
/// </summary>
[JsonProperty("conferencePassword")]
public string ConferencePassword { get; set; }
/// <summary>
/// Gets or sets the IsOneButtonToPushMeeting
/// </summary>
[JsonProperty("isOneButtonToPushMeeting")]
public bool IsOneButtonToPushMeeting { get; set; }
/// <summary>
/// Gets or sets the Calls
/// </summary>
[JsonProperty("calls")]
public List<Call> Calls { get; private set; }
/// <summary>
/// Tracks the change types that have already been notified for
/// Gets or sets the NotifiedChangeTypes
/// </summary>
[JsonIgnore]
public eMeetingEventChangeType NotifiedChangeTypes { get; set; }
[JsonIgnore] private readonly int _joinableCooldownSeconds;
/// <summary>
/// Constructor for Meeting <see cref="Meeting"/>
/// </summary>
public Meeting()
{
Calls = new List<Call>();
_joinableCooldownSeconds = 300;
}
/// <summary>
/// Constructor for Meeting <see cref="Meeting"/>
/// </summary>
/// <param name="joinableCooldownSeconds">Number of seconds after meeting start when it is no longer joinable</param>
public Meeting(int joinableCooldownSeconds)
{
Calls = new List<Call>();
_joinableCooldownSeconds = joinableCooldownSeconds;
}
#region Overrides of Object
/// <summary>
/// ToString method
/// </summary>
/// <inheritdoc />
public override string ToString()
{
return string.Format("{0}:{1}: {2}-{3}", Title, Agenda, StartTime, EndTime);
}
#endregion
}
}

View File

@@ -1,22 +0,0 @@
using System;
namespace PepperDash.Essentials.Devices.Common.Codec
{
/// <summary>
/// Represents a MeetingEventArgs
/// </summary>
public class MeetingEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the ChangeType
/// </summary>
public eMeetingEventChangeType ChangeType { get; set; }
/// <summary>
/// Gets or sets the Meeting
/// </summary>
public Meeting Meeting { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More