mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-02-13 11:44:54 +00:00
Compare commits
56 Commits
feature/ge
...
v2.25.0-xm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7c4e2fd60 | ||
|
|
4fa7a42330 | ||
|
|
9bad3ae21b | ||
|
|
3fb30d5561 | ||
|
|
57cd77f019 | ||
|
|
7f2bb078c8 | ||
|
|
316bb849b4 | ||
|
|
a983e2c87f | ||
|
|
babb9a77df | ||
|
|
7629921113 | ||
|
|
3878c85a7a | ||
|
|
7ad8218af0 | ||
|
|
0c4aec14d1 | ||
|
|
7d3f871460 | ||
|
|
78c9381108 | ||
|
|
a7ff2e8903 | ||
|
|
ae0b2fe086 | ||
|
|
bd11c827da | ||
|
|
7ea1efbabf | ||
|
|
9d6aaa2a0e | ||
|
|
53e7a30224 | ||
|
|
39c1f60a4d | ||
|
|
2cc37a4e40 | ||
|
|
076e5dfa9d | ||
|
|
092896bb25 | ||
|
|
7c8f0586e6 | ||
|
|
c5b0872a4c | ||
|
|
a7b88ec38d | ||
|
|
210b398a13 | ||
|
|
bce1e3610e | ||
|
|
6a33e7c99d | ||
|
|
2048e3f65d | ||
|
|
81df2738de | ||
|
|
08fbec416f | ||
|
|
7594b22096 | ||
|
|
d1babf6b9b | ||
|
|
2187c9fb0d | ||
|
|
a5e6059160 | ||
|
|
9ef4aedcce | ||
|
|
f7c7160bf0 | ||
|
|
dbf5740841 | ||
|
|
c07e099a79 | ||
|
|
06cb508f3a | ||
|
|
e93b5b34cc | ||
|
|
4f5d4ef87a | ||
|
|
636da8cc8c | ||
|
|
5de1e2d7bb | ||
|
|
03bbb84894 | ||
|
|
d17394cdd7 | ||
|
|
8467afde38 | ||
|
|
5c016fb4b8 | ||
|
|
2fbc32947c | ||
|
|
c06b57a5f9 | ||
|
|
6d64fffc50 | ||
|
|
0c4ebdaf1d | ||
|
|
2c49fb9321 |
13
.config/dotnet-tools.json
Normal file
13
.config/dotnet-tools.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"csharpier": {
|
||||||
|
"version": "1.2.4",
|
||||||
|
"commands": [
|
||||||
|
"csharpier"
|
||||||
|
],
|
||||||
|
"rollForward": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5243
buildFile.txt
Normal file
5243
buildFile.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -15,23 +15,23 @@ namespace PepperDash.Core
|
|||||||
/// Unique Key
|
/// Unique Key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Key { get; protected set; }
|
public string Key { get; protected set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Name
|
/// Gets or sets the Name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; protected set; }
|
public string Name { get; protected set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Enabled { get; protected set; }
|
public bool Enabled { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// A place to store reference to the original config object, if any. These values should
|
// /// A place to store reference to the original config object, if any. These values should
|
||||||
/// NOT be used as properties on the device as they are all publicly-settable values.
|
// /// NOT be used as properties on the device as they are all publicly-settable values.
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
//public DeviceConfig Config { get; private set; }
|
//public DeviceConfig Config { get; private set; }
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Helper method to check if Config exists
|
// /// Helper method to check if Config exists
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
//public bool HasConfig { get { return Config != null; } }
|
//public bool HasConfig { get { return Config != null; } }
|
||||||
|
|
||||||
List<Action> _PreActivationActions;
|
List<Action> _PreActivationActions;
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ namespace PepperDash.Core
|
|||||||
CrestronConsole.PrintLine(message);
|
CrestronConsole.PrintLine(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for DebugConsoleSink
|
||||||
|
/// </summary>
|
||||||
public DebugConsoleSink(ITextFormatter formatProvider )
|
public DebugConsoleSink(ITextFormatter formatProvider )
|
||||||
{
|
{
|
||||||
_textFormatter = formatProvider ?? new JsonFormatter();
|
_textFormatter = formatProvider ?? new JsonFormatter();
|
||||||
@@ -48,6 +51,9 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for DebugConsoleSink
|
||||||
|
/// </summary>
|
||||||
public static class DebugConsoleSinkExtensions
|
public static class DebugConsoleSinkExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ namespace PepperDash.Core.Logging
|
|||||||
CrestronLogger.WriteToLog(message, (uint)logEvent.Level);
|
CrestronLogger.WriteToLog(message, (uint)logEvent.Level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for DebugCrestronLoggerSink
|
||||||
|
/// </summary>
|
||||||
public DebugCrestronLoggerSink()
|
public DebugCrestronLoggerSink()
|
||||||
{
|
{
|
||||||
CrestronLogger.Initialize(1, LoggerModeEnum.RM);
|
CrestronLogger.Initialize(1, LoggerModeEnum.RM);
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ namespace PepperDash.Core.Logging
|
|||||||
handler(message);
|
handler(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for DebugErrorLogSink
|
||||||
|
/// </summary>
|
||||||
public DebugErrorLogSink(ITextFormatter formatter = null)
|
public DebugErrorLogSink(ITextFormatter formatter = null)
|
||||||
{
|
{
|
||||||
_formatter = formatter;
|
_formatter = formatter;
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ using Log = PepperDash.Core.Debug;
|
|||||||
|
|
||||||
namespace PepperDash.Core.Logging
|
namespace PepperDash.Core.Logging
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for logging on IKeyed objects
|
||||||
|
/// </summary>
|
||||||
public static class DebugExtensions
|
public static class DebugExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ namespace PepperDash.Core
|
|||||||
private const string _certificateName = "selfCres";
|
private const string _certificateName = "selfCres";
|
||||||
private const string _certificatePassword = "cres12345";
|
private const string _certificatePassword = "cres12345";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Port
|
||||||
|
/// </summary>
|
||||||
public int Port
|
public int Port
|
||||||
{ get
|
{ get
|
||||||
{
|
{
|
||||||
@@ -41,6 +44,9 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Url
|
||||||
|
/// </summary>
|
||||||
public string Url
|
public string Url
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -58,6 +64,9 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
private readonly ITextFormatter _textFormatter;
|
private readonly ITextFormatter _textFormatter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for DebugWebsocketSink
|
||||||
|
/// </summary>
|
||||||
public DebugWebsocketSink(ITextFormatter formatProvider)
|
public DebugWebsocketSink(ITextFormatter formatProvider)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -217,6 +226,9 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides extension methods for DebugWebsocketSink
|
||||||
|
/// </summary>
|
||||||
public static class DebugWebsocketSinkExtensions
|
public static class DebugWebsocketSinkExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -237,6 +249,9 @@ namespace PepperDash.Core
|
|||||||
{
|
{
|
||||||
private DateTime _connectionTime;
|
private DateTime _connectionTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the ConnectedDuration
|
||||||
|
/// </summary>
|
||||||
public TimeSpan ConnectedDuration
|
public TimeSpan ConnectedDuration
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -252,11 +267,17 @@ namespace PepperDash.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor for DebugClient
|
||||||
|
/// </summary>
|
||||||
public DebugClient()
|
public DebugClient()
|
||||||
{
|
{
|
||||||
Debug.Console(0, "DebugClient Created");
|
Debug.Console(0, "DebugClient Created");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OnOpen method
|
||||||
|
/// </summary>
|
||||||
protected override void OnOpen()
|
protected override void OnOpen()
|
||||||
{
|
{
|
||||||
base.OnOpen();
|
base.OnOpen();
|
||||||
@@ -267,6 +288,9 @@ namespace PepperDash.Core
|
|||||||
_connectionTime = DateTime.Now;
|
_connectionTime = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OnMessage method
|
||||||
|
/// </summary>
|
||||||
protected override void OnMessage(MessageEventArgs e)
|
protected override void OnMessage(MessageEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnMessage(e);
|
base.OnMessage(e);
|
||||||
@@ -274,6 +298,9 @@ namespace PepperDash.Core
|
|||||||
Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
|
Debug.Console(0, "WebSocket UiClient Message: {0}", e.Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OnClose method
|
||||||
|
/// </summary>
|
||||||
protected override void OnClose(CloseEventArgs e)
|
protected override void OnClose(CloseEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnClose(e);
|
base.OnClose(e);
|
||||||
@@ -282,6 +309,9 @@ namespace PepperDash.Core
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OnError method
|
||||||
|
/// </summary>
|
||||||
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
protected override void OnError(WebSocketSharp.ErrorEventArgs e)
|
||||||
{
|
{
|
||||||
base.OnError(e);
|
base.OnError(e);
|
||||||
|
|||||||
@@ -5,9 +5,16 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace PepperDash.Core.Web.RequestHandlers
|
namespace PepperDash.Core.Web.RequestHandlers
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CWS Base Async Handler, implements IHttpCwsHandler
|
||||||
|
/// </summary>
|
||||||
public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
|
public abstract class WebApiBaseRequestAsyncHandler:IHttpCwsHandler
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Func<HttpCwsContext, Task>> _handlers;
|
private readonly Dictionary<string, Func<HttpCwsContext, Task>> _handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether CORS is enabled
|
||||||
|
/// </summary>
|
||||||
protected readonly bool EnableCors;
|
protected readonly bool EnableCors;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -10,6 +10,10 @@ namespace PepperDash.Core.Web.RequestHandlers
|
|||||||
public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
public abstract class WebApiBaseRequestHandler : IHttpCwsHandler
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Action<HttpCwsContext>> _handlers;
|
private readonly Dictionary<string, Action<HttpCwsContext>> _handlers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether CORS is enabled
|
||||||
|
/// </summary>
|
||||||
protected readonly bool EnableCors;
|
protected readonly bool EnableCors;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ namespace PepperDash.Core.Web
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsRegistered { get; private set; }
|
public bool IsRegistered { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Http request handler
|
// /// Http request handler
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
//public IHttpCwsHandler HttpRequestHandler
|
//public IHttpCwsHandler HttpRequestHandler
|
||||||
//{
|
//{
|
||||||
// get { return _server.HttpRequestHandler; }
|
// get { return _server.HttpRequestHandler; }
|
||||||
@@ -58,9 +58,9 @@ namespace PepperDash.Core.Web
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Received request event handler
|
// /// Received request event handler
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
//public event EventHandler<HttpCwsRequestEventArgs> ReceivedRequestEvent
|
//public event EventHandler<HttpCwsRequestEventArgs> ReceivedRequestEvent
|
||||||
//{
|
//{
|
||||||
// add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); }
|
// add { _server.ReceivedRequestEvent += new HttpCwsRequestEventHandler(value); }
|
||||||
|
|||||||
@@ -2,25 +2,29 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using Crestron.SimplSharpPro;
|
using Crestron.SimplSharpPro;
|
||||||
using Crestron.SimplSharpPro.DeviceSupport;
|
using Crestron.SimplSharpPro.DeviceSupport;
|
||||||
using Crestron.SimplSharpPro.EthernetCommunication;
|
using Crestron.SimplSharpPro.EthernetCommunication;
|
||||||
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 Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
//using PepperDash.Essentials.Devices.Common.Cameras;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Bridges
|
namespace PepperDash.Essentials.Core.Bridges
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for bridge API variants
|
/// Base class for bridge API variants
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Will be removed in v3.0.0")]
|
||||||
public abstract class BridgeApi : EssentialsDevice
|
public abstract class BridgeApi : EssentialsDevice
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Device key</param>
|
||||||
protected BridgeApi(string key) :
|
protected BridgeApi(string key) :
|
||||||
base(key)
|
base(key)
|
||||||
{
|
{
|
||||||
@@ -29,23 +33,36 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a EiscApiAdvanced
|
/// Class to link devices and rooms to an EISC Instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor
|
public class EiscApiAdvanced : BridgeApi, ICommunicationMonitor
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the PropertiesConfig
|
||||||
|
/// </summary>
|
||||||
public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
|
public EiscApiPropertiesConfig PropertiesConfig { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the JoinMaps dictionary
|
||||||
|
/// </summary>
|
||||||
public Dictionary<string, JoinMapBaseAdvanced> JoinMaps { get; private set; }
|
public Dictionary<string, JoinMapBaseAdvanced> JoinMaps { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the EISC instance
|
||||||
|
/// </summary>
|
||||||
public BasicTriList Eisc { get; private set; }
|
public BasicTriList Eisc { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dc">Device configuration</param>
|
||||||
|
/// <param name="eisc">EISC instance</param>
|
||||||
public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :
|
public EiscApiAdvanced(DeviceConfig dc, BasicTriList eisc) :
|
||||||
base(dc.Key)
|
base(dc.Key)
|
||||||
{
|
{
|
||||||
JoinMaps = new Dictionary<string, JoinMapBaseAdvanced>();
|
JoinMaps = new Dictionary<string, JoinMapBaseAdvanced>();
|
||||||
|
|
||||||
PropertiesConfig = dc.Properties.ToObject<EiscApiPropertiesConfig>();
|
PropertiesConfig = dc.Properties.ToObject<EiscApiPropertiesConfig>();
|
||||||
//PropertiesConfig = JsonConvert.DeserializeObject<EiscApiPropertiesConfig>(dc.Properties.ToString());
|
|
||||||
|
|
||||||
Eisc = eisc;
|
Eisc = eisc;
|
||||||
|
|
||||||
@@ -60,8 +77,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CustomActivate method
|
/// CustomActivate method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc />
|
|
||||||
public override bool CustomActivate()
|
public override bool CustomActivate()
|
||||||
{
|
{
|
||||||
CommunicationMonitor.Start();
|
CommunicationMonitor.Start();
|
||||||
@@ -83,7 +99,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
|
|
||||||
if (PropertiesConfig.Devices == null)
|
if (PropertiesConfig.Devices == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "No devices linked to this bridge");
|
this.LogDebug("No devices linked to this bridge");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,9 +120,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, this,
|
this.LogWarning("{deviceKey} is not compatible with this bridge type. Please update the device.", device.Key);
|
||||||
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
|
|
||||||
device.Key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,34 +135,31 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
|
|
||||||
if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)
|
if (registerResult != eDeviceRegistrationUnRegistrationResponse.Success)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Registration result: {0}", registerResult);
|
this.LogVerbose("Registration result: {registerResult}", registerResult);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "EISC registration successful");
|
this.LogDebug("EISC registration successful");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// LinkRooms method
|
/// Link rooms to this EISC. Rooms MUST implement IBridgeAdvanced
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void LinkRooms()
|
public void LinkRooms()
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Rooms...");
|
this.LogDebug("Linking Rooms...");
|
||||||
|
|
||||||
if (PropertiesConfig.Rooms == null)
|
if (PropertiesConfig.Rooms == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "No rooms linked to this bridge.");
|
this.LogDebug("No rooms linked to this bridge.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var room in PropertiesConfig.Rooms)
|
foreach (var room in PropertiesConfig.Rooms)
|
||||||
{
|
{
|
||||||
var rm = DeviceManager.GetDeviceForKey(room.RoomKey) as IBridgeAdvanced;
|
if (!(DeviceManager.GetDeviceForKey(room.RoomKey) is IBridgeAdvanced rm))
|
||||||
|
|
||||||
if (rm == null)
|
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this,
|
this.LogDebug("Room {roomKey} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
|
||||||
"Room {0} does not implement IBridgeAdvanced. Skipping...", room.RoomKey);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,11 +170,8 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a join map
|
/// Adds a join map
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="deviceKey"></param>
|
/// <param name="deviceKey">The key of the device to add the join map for</param>
|
||||||
/// <param name="joinMap"></param>
|
/// <param name="joinMap">The join map to add</param>
|
||||||
/// <summary>
|
|
||||||
/// AddJoinMap method
|
|
||||||
/// </summary>
|
|
||||||
public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)
|
public void AddJoinMap(string deviceKey, JoinMapBaseAdvanced joinMap)
|
||||||
{
|
{
|
||||||
if (!JoinMaps.ContainsKey(deviceKey))
|
if (!JoinMaps.ContainsKey(deviceKey))
|
||||||
@@ -172,14 +180,13 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Unable to add join map with key '{0}'. Key already exists in JoinMaps dictionary", deviceKey);
|
this.LogWarning("Unable to add join map with key '{deviceKey}'. Key already exists in JoinMaps dictionary", deviceKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// PrintJoinMaps method
|
/// PrintJoinMaps method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void PrintJoinMaps()
|
public virtual void PrintJoinMaps()
|
||||||
{
|
{
|
||||||
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
|
CrestronConsole.ConsoleCommandResponse("Join Maps for EISC IPID: {0}\r\n", Eisc.ID.ToString("X"));
|
||||||
@@ -190,17 +197,17 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
joinMap.Value.PrintJoinMapInfo();
|
joinMap.Value.PrintJoinMapInfo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MarkdownForBridge method
|
/// MarkdownForBridge method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual void MarkdownForBridge(string bridgeKey)
|
public virtual void MarkdownForBridge(string bridgeKey)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Writing Joinmaps to files for EISC IPID: {0}", Eisc.ID.ToString("X"));
|
this.LogInformation("Writing Joinmaps to files for EISC IPID: {eiscId}", Eisc.ID.ToString("X"));
|
||||||
|
|
||||||
foreach (var joinMap in JoinMaps)
|
foreach (var joinMap in JoinMaps)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Generating markdown for device '{0}':", joinMap.Key);
|
this.LogInformation("Generating markdown for device '{deviceKey}':", joinMap.Key);
|
||||||
joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);
|
joinMap.Value.MarkdownJoinMapInfo(joinMap.Key, bridgeKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -208,53 +215,45 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints the join map for a device by key
|
/// Prints the join map for a device by key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="deviceKey"></param>
|
/// <param name="deviceKey">The key of the device to print the join map for</param>
|
||||||
/// <summary>
|
|
||||||
/// PrintJoinMapForDevice method
|
|
||||||
/// </summary>
|
|
||||||
public void PrintJoinMapForDevice(string deviceKey)
|
public void PrintJoinMapForDevice(string deviceKey)
|
||||||
{
|
{
|
||||||
var joinMap = JoinMaps[deviceKey];
|
var joinMap = JoinMaps[deviceKey];
|
||||||
|
|
||||||
if (joinMap == null)
|
if (joinMap == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
|
this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
|
this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
|
||||||
joinMap.PrintJoinMapInfo();
|
joinMap.PrintJoinMapInfo();
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints the join map for a device by key
|
/// Prints the join map for a device by key in Markdown format
|
||||||
/// </summary>
|
|
||||||
/// <param name="deviceKey"></param>
|
|
||||||
/// <summary>
|
|
||||||
/// MarkdownJoinMapForDevice method
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="deviceKey">The key of the device to print the join map for</param>
|
||||||
|
/// <param name="bridgeKey">The key of the bridge to use for the Markdown output</param>
|
||||||
public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)
|
public void MarkdownJoinMapForDevice(string deviceKey, string bridgeKey)
|
||||||
{
|
{
|
||||||
var joinMap = JoinMaps[deviceKey];
|
var joinMap = JoinMaps[deviceKey];
|
||||||
|
|
||||||
if (joinMap == null)
|
if (joinMap == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Unable to find joinMap for device with key: '{0}'", deviceKey);
|
this.LogInformation("Unable to find joinMap for device with key: '{deviceKey}'", deviceKey);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Join map for device '{0}' on EISC '{1}':", deviceKey, Key);
|
this.LogInformation("Join map for device '{deviceKey}' on EISC '{eiscKey}':", deviceKey, Key);
|
||||||
joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);
|
joinMap.MarkdownJoinMapInfo(deviceKey, bridgeKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for debugging to trigger an action based on a join number and type
|
/// Used for debugging to trigger an action based on a join number and type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="join"></param>
|
/// <param name="join">The join number to execute the action for</param>
|
||||||
/// <param name="type"></param>
|
/// <param name="type">The type of join (digital, analog, serial)</param>
|
||||||
/// <param name="state"></param>
|
/// <param name="state">The state to pass to the action</param>
|
||||||
/// <summary>
|
|
||||||
/// ExecuteJoinAction method
|
|
||||||
/// </summary>
|
|
||||||
public void ExecuteJoinAction(uint join, string type, object state)
|
public void ExecuteJoinAction(uint join, string type, object state)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -263,78 +262,87 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
{
|
{
|
||||||
case "digital":
|
case "digital":
|
||||||
{
|
{
|
||||||
var uo = Eisc.BooleanOutput[join].UserObject as Action<bool>;
|
if (Eisc.BooleanOutput[join].UserObject is Action<bool> userObject)
|
||||||
if (uo != null)
|
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
|
this.LogVerbose("Executing Boolean Action");
|
||||||
uo(Convert.ToBoolean(state));
|
userObject(Convert.ToBoolean(state));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
|
this.LogVerbose("User Object is null. Nothing to Execute");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "analog":
|
case "analog":
|
||||||
{
|
{
|
||||||
var uo = Eisc.BooleanOutput[join].UserObject as Action<ushort>;
|
if (Eisc.UShortOutput[join].UserObject is Action<ushort> userObject)
|
||||||
if (uo != null)
|
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
|
this.LogVerbose("Executing Analog Action");
|
||||||
uo(Convert.ToUInt16(state));
|
userObject(Convert.ToUInt16(state));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute"); break;
|
this.LogVerbose("User Object is null. Nothing to Execute");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case "serial":
|
case "serial":
|
||||||
{
|
{
|
||||||
var uo = Eisc.BooleanOutput[join].UserObject as Action<string>;
|
if (Eisc.StringOutput[join].UserObject is Action<string> userObject)
|
||||||
if (uo != null)
|
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Executing Action: {0}", uo.ToString());
|
this.LogVerbose("Executing Serial Action");
|
||||||
uo(Convert.ToString(state));
|
userObject(Convert.ToString(state));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "User Action is null. Nothing to Execute");
|
this.LogVerbose("User Object is null. Nothing to Execute");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "Unknown join type. Use digital/serial/analog");
|
this.LogVerbose("Unknown join type. Use digital/serial/analog");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "Error: {0}", e);
|
this.LogError("ExecuteJoinAction error: {message}", e.Message);
|
||||||
|
this.LogDebug(e, "Stack Trace: ");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles incoming sig changes
|
/// Handle incoming sig changes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="currentDevice"></param>
|
/// <param name="currentDevice">BasicTriList device that triggered the event</param>
|
||||||
/// <param name="args"></param>
|
/// <param name="args">Event arguments containing the signal information</param>
|
||||||
protected void Eisc_SigChange(object currentDevice, SigEventArgs args)
|
protected void Eisc_SigChange(object currentDevice, SigEventArgs args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "EiscApiAdvanced change: {0} {1}={2}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
|
this.LogVerbose("EiscApiAdvanced change: {type} {number}={value}", args.Sig.Type, args.Sig.Number, args.Sig.StringValue);
|
||||||
var uo = args.Sig.UserObject;
|
var userObject = args.Sig.UserObject;
|
||||||
|
|
||||||
if (uo == null) return;
|
if (userObject == null) return;
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "Executing Action: {0}", uo.ToString());
|
|
||||||
if (uo is Action<bool>)
|
if (userObject is Action<bool>)
|
||||||
(uo as Action<bool>)(args.Sig.BoolValue);
|
{
|
||||||
else if (uo is Action<ushort>)
|
this.LogDebug("Executing Boolean Action");
|
||||||
(uo as Action<ushort>)(args.Sig.UShortValue);
|
(userObject as Action<bool>)(args.Sig.BoolValue);
|
||||||
else if (uo is Action<string>)
|
}
|
||||||
(uo as Action<string>)(args.Sig.StringValue);
|
else if (userObject is Action<ushort>)
|
||||||
|
{
|
||||||
|
this.LogDebug("Executing Analog Action");
|
||||||
|
(userObject as Action<ushort>)(args.Sig.UShortValue);
|
||||||
|
}
|
||||||
|
else if (userObject is Action<string>)
|
||||||
|
{
|
||||||
|
this.LogDebug("Executing Serial Action");
|
||||||
|
(userObject as Action<string>)(args.Sig.StringValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Error in Eisc_SigChange handler: {0}", e);
|
this.LogError("Eisc_SigChange handler error: {message}", e.Message);
|
||||||
|
this.LogDebug(e, "Stack Trace: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -423,22 +431,33 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a EiscApiAdvancedFactory
|
/// Factory class for EiscApiAdvanced devices
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Supported types:
|
||||||
|
/// eiscapiadv - Create a standard EISC client over TCP/IP
|
||||||
|
/// eiscapiadvanced - Create a standard EISC client over TCP/IP
|
||||||
|
/// eiscapiadvancedserver - Create an EISC server
|
||||||
|
/// eiscapiadvancedclient - Create an EISC client
|
||||||
|
/// vceiscapiadv - Create a VC-4 EISC client
|
||||||
|
/// vceiscapiadvanced - Create a VC-4 EISC client
|
||||||
|
/// eiscapiadvudp - Create a standard EISC client over UDP
|
||||||
|
/// eiscapiadvancedudp - Create a standard EISC client over UDP
|
||||||
|
/// </remarks>
|
||||||
public class EiscApiAdvancedFactory : EssentialsDeviceFactory<EiscApiAdvanced>
|
public class EiscApiAdvancedFactory : EssentialsDeviceFactory<EiscApiAdvanced>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public EiscApiAdvancedFactory()
|
public EiscApiAdvancedFactory()
|
||||||
{
|
{
|
||||||
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced" };
|
TypeNames = new List<string> { "eiscapiadv", "eiscapiadvanced", "eiscapiadvancedserver", "eiscapiadvancedclient", "vceiscapiadv", "vceiscapiadvanced", "eiscapiadvudp", "eiscapiadvancedudp" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BuildDevice method
|
|
||||||
/// </summary>
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new EiscApiAdvanced Device");
|
Debug.LogDebug("Attempting to create new EiscApiAdvanced Device");
|
||||||
|
|
||||||
var controlProperties = CommFactory.GetControlPropertiesConfig(dc);
|
var controlProperties = CommFactory.GetControlPropertiesConfig(dc);
|
||||||
|
|
||||||
@@ -446,6 +465,13 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
|
|
||||||
switch (dc.Type.ToLower())
|
switch (dc.Type.ToLower())
|
||||||
{
|
{
|
||||||
|
case "eiscapiadvudp":
|
||||||
|
case "eiscapiadvancedudp":
|
||||||
|
{
|
||||||
|
eisc = new EthernetIntersystemCommunications(controlProperties.IpIdInt,
|
||||||
|
controlProperties.TcpSshProperties.Address, Global.ControlSystem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "eiscapiadv":
|
case "eiscapiadv":
|
||||||
case "eiscapiadvanced":
|
case "eiscapiadvanced":
|
||||||
{
|
{
|
||||||
@@ -468,7 +494,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
|||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(controlProperties.RoomId))
|
if (string.IsNullOrEmpty(controlProperties.RoomId))
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Information, "Unable to build VC-4 EISC Client for device {0}. Room ID is missing or empty", dc.Key);
|
Debug.LogInformation("Unable to build VC-4 EISC Client for device {deviceKey}. Room ID is missing or empty", dc.Key);
|
||||||
eisc = null;
|
eisc = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ using PepperDash.Core;
|
|||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Config
|
namespace PepperDash.Essentials.Core.Config
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the ConfigObject from the file
|
/// Loads the ConfigObject from the file
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsConfig : BasicConfig
|
public class EssentialsConfig : BasicConfig
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the SystemUrl
|
/// Gets or sets the SystemUrl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -32,24 +32,33 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
/// Gets the SystemUuid extracted from the SystemUrl
|
/// Gets the SystemUuid extracted from the SystemUrl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("systemUuid")]
|
[JsonProperty("systemUuid")]
|
||||||
public string SystemUuid
|
public string SystemUuid
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(SystemUrl))
|
string uuid;
|
||||||
return "missing url";
|
|
||||||
|
|
||||||
if (SystemUrl.Contains("#"))
|
if (string.IsNullOrEmpty(SystemUrl))
|
||||||
{
|
{
|
||||||
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
|
uuid = "missing url";
|
||||||
string uuid = result.Groups[1].Value;
|
|
||||||
return uuid;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
|
|
||||||
string uuid = result.Groups[1].Value;
|
|
||||||
return uuid;
|
|
||||||
}
|
}
|
||||||
|
else if (SystemUrl.Contains("#"))
|
||||||
|
{
|
||||||
|
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/#.*");
|
||||||
|
uuid = result.Groups[1].Value;
|
||||||
|
}
|
||||||
|
else if (SystemUrl.Contains("detail"))
|
||||||
|
{
|
||||||
|
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/detail\/(.*)\/.*");
|
||||||
|
uuid = result.Groups[1].Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = Regex.Match(SystemUrl, @"https?:\/\/.*\/systems\/(.*)\/.*");
|
||||||
|
uuid = result.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,24 +66,33 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
/// Gets the TemplateUuid extracted from the TemplateUrl
|
/// Gets the TemplateUuid extracted from the TemplateUrl
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("templateUuid")]
|
[JsonProperty("templateUuid")]
|
||||||
public string TemplateUuid
|
public string TemplateUuid
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(TemplateUrl))
|
string uuid;
|
||||||
return "missing template url";
|
|
||||||
|
|
||||||
if (TemplateUrl.Contains("#"))
|
if (string.IsNullOrEmpty(TemplateUrl))
|
||||||
{
|
{
|
||||||
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
|
uuid = "missing template url";
|
||||||
string uuid = result.Groups[1].Value;
|
|
||||||
return uuid;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
|
|
||||||
string uuid = result.Groups[2].Value;
|
|
||||||
return uuid;
|
|
||||||
}
|
}
|
||||||
|
else if (TemplateUrl.Contains("#"))
|
||||||
|
{
|
||||||
|
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/templates\/(.*)\/#.*");
|
||||||
|
uuid = result.Groups[1].Value;
|
||||||
|
}
|
||||||
|
else if (TemplateUrl.Contains("detail"))
|
||||||
|
{
|
||||||
|
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/detail\/(.*)\/system-template-versions\/detail\/(.*)\/.*");
|
||||||
|
uuid = result.Groups[2].Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = Regex.Match(TemplateUrl, @"https?:\/\/.*\/system-templates\/(.*)\/system-template-versions\/(.*)\/.*");
|
||||||
|
uuid = result.Groups[2].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uuid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +115,7 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
{
|
{
|
||||||
Rooms = new List<DeviceConfig>();
|
Rooms = new List<DeviceConfig>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents version data for Essentials and its packages
|
/// Represents version data for Essentials and its packages
|
||||||
@@ -147,7 +165,7 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
/// Represents a SystemTemplateConfigs
|
/// Represents a SystemTemplateConfigs
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SystemTemplateConfigs
|
public class SystemTemplateConfigs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the System
|
/// Gets or sets the System
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -157,5 +175,5 @@ namespace PepperDash.Essentials.Core.Config
|
|||||||
/// Gets or sets the Template
|
/// Gets or sets the Template
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public EssentialsConfig Template { get; set; }
|
public EssentialsConfig Template { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines minimum functionality for an audio zone
|
||||||
|
/// </summary>
|
||||||
|
public interface IAudioZone : IBasicVolumeWithFeedback
|
||||||
|
{
|
||||||
|
void SelectInput(ushort input);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies a device that contains audio zones
|
||||||
|
/// </summary>
|
||||||
|
public interface IAudioZones : IRouting
|
||||||
|
{
|
||||||
|
Dictionary<uint, IAudioZone> Zone { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using PepperDash.Core;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines minimal volume and mute control methods
|
||||||
|
/// </summary>
|
||||||
|
public interface IBasicVolumeControls : IKeyName
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Increases the volume
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pressRelease">Indicates whether the volume change is a press and hold action</param>
|
||||||
|
void VolumeUp(bool pressRelease);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Decreases the volume
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pressRelease">Indicates whether the volume change is a press and hold action</param>
|
||||||
|
void VolumeDown(bool pressRelease);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles the mute state
|
||||||
|
/// </summary>
|
||||||
|
void MuteToggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IBasicVolumeWithFeedback
|
||||||
|
/// </summary>
|
||||||
|
public interface IBasicVolumeWithFeedback : IBasicVolumeControls
|
||||||
|
{
|
||||||
|
BoolFeedback MuteFeedback { get; }
|
||||||
|
void MuteOn();
|
||||||
|
void MuteOff();
|
||||||
|
void SetVolume(ushort level);
|
||||||
|
IntFeedback VolumeLevelFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IBasicVolumeWithFeedbackAdvanced
|
||||||
|
/// </summary>
|
||||||
|
public interface IBasicVolumeWithFeedbackAdvanced : IBasicVolumeWithFeedback
|
||||||
|
{
|
||||||
|
int RawVolumeLevel { get; }
|
||||||
|
|
||||||
|
eVolumeLevelUnits Units { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IFullAudioSettings
|
||||||
|
/// </summary>
|
||||||
|
public interface IFullAudioSettings : IBasicVolumeWithFeedback
|
||||||
|
{
|
||||||
|
void SetBalance(ushort level);
|
||||||
|
void BalanceLeft(bool pressRelease);
|
||||||
|
void BalanceRight(bool pressRelease);
|
||||||
|
|
||||||
|
void SetBass(ushort level);
|
||||||
|
void BassUp(bool pressRelease);
|
||||||
|
void BassDown(bool pressRelease);
|
||||||
|
|
||||||
|
void SetTreble(ushort level);
|
||||||
|
void TrebleUp(bool pressRelease);
|
||||||
|
void TrebleDown(bool pressRelease);
|
||||||
|
|
||||||
|
bool hasMaxVolume { get; }
|
||||||
|
void SetMaxVolume(ushort level);
|
||||||
|
void MaxVolumeUp(bool pressRelease);
|
||||||
|
void MaxVolumeDown(bool pressRelease);
|
||||||
|
|
||||||
|
bool hasDefaultVolume { get; }
|
||||||
|
void SetDefaultVolume(ushort level);
|
||||||
|
void DefaultVolumeUp(bool pressRelease);
|
||||||
|
void DefaultVolumeDown(bool pressRelease);
|
||||||
|
|
||||||
|
void LoudnessToggle();
|
||||||
|
void MonoToggle();
|
||||||
|
|
||||||
|
BoolFeedback LoudnessFeedback { get; }
|
||||||
|
BoolFeedback MonoFeedback { get; }
|
||||||
|
IntFeedback BalanceFeedback { get; }
|
||||||
|
IntFeedback BassFeedback { get; }
|
||||||
|
IntFeedback TrebleFeedback { get; }
|
||||||
|
IntFeedback MaxVolumeFeedback { get; }
|
||||||
|
IntFeedback DefaultVolumeFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IHasCurrentVolumeControls
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasCurrentVolumeControls
|
||||||
|
{
|
||||||
|
IBasicVolumeControls CurrentVolumeControls { get; }
|
||||||
|
event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
|
||||||
|
|
||||||
|
void SetDefaultLevels();
|
||||||
|
|
||||||
|
bool ZeroVolumeWhenSwtichingVolumeDevices { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines basic mute control methods
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasMuteControl
|
||||||
|
{
|
||||||
|
void MuteToggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines mute control methods and properties with feedback
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasMuteControlWithFeedback : IHasMuteControl
|
||||||
|
{
|
||||||
|
BoolFeedback MuteFeedback { get; }
|
||||||
|
void MuteOn();
|
||||||
|
void MuteOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IHasVolumeControl
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasVolumeControl
|
||||||
|
{
|
||||||
|
void VolumeUp(bool pressRelease);
|
||||||
|
void VolumeDown(bool pressRelease);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines volume control methods and properties with feedback
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasVolumeControlWithFeedback : IHasVolumeControl
|
||||||
|
{
|
||||||
|
void SetVolume(ushort level);
|
||||||
|
IntFeedback VolumeLevelFeedback { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the contract for IHasVolumeDevice
|
||||||
|
/// </summary>
|
||||||
|
public interface IHasVolumeDevice
|
||||||
|
{
|
||||||
|
IBasicVolumeControls VolumeDevice { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace PepperDash.Essentials.Core
|
||||||
|
{
|
||||||
|
public enum eVolumeLevelUnits
|
||||||
|
{
|
||||||
|
Decibels,
|
||||||
|
Percent,
|
||||||
|
Relative,
|
||||||
|
Absolute
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines minimal volume and mute control methods
|
|
||||||
/// </summary>
|
|
||||||
public interface IBasicVolumeControls
|
|
||||||
{
|
|
||||||
void VolumeUp(bool pressRelease);
|
|
||||||
void VolumeDown(bool pressRelease);
|
|
||||||
void MuteToggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IHasVolumeControl
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasVolumeControl
|
|
||||||
{
|
|
||||||
void VolumeUp(bool pressRelease);
|
|
||||||
void VolumeDown(bool pressRelease);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines volume control methods and properties with feedback
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasVolumeControlWithFeedback : IHasVolumeControl
|
|
||||||
{
|
|
||||||
void SetVolume(ushort level);
|
|
||||||
IntFeedback VolumeLevelFeedback { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines basic mute control methods
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasMuteControl
|
|
||||||
{
|
|
||||||
void MuteToggle();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines mute control methods and properties with feedback
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasMuteControlWithFeedback : IHasMuteControl
|
|
||||||
{
|
|
||||||
BoolFeedback MuteFeedback { get; }
|
|
||||||
void MuteOn();
|
|
||||||
void MuteOff();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IBasicVolumeWithFeedback
|
|
||||||
/// </summary>
|
|
||||||
public interface IBasicVolumeWithFeedback : IBasicVolumeControls
|
|
||||||
{
|
|
||||||
BoolFeedback MuteFeedback { get; }
|
|
||||||
void MuteOn();
|
|
||||||
void MuteOff();
|
|
||||||
void SetVolume(ushort level);
|
|
||||||
IntFeedback VolumeLevelFeedback { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IBasicVolumeWithFeedbackAdvanced
|
|
||||||
/// </summary>
|
|
||||||
public interface IBasicVolumeWithFeedbackAdvanced : IBasicVolumeWithFeedback
|
|
||||||
{
|
|
||||||
int RawVolumeLevel { get; }
|
|
||||||
|
|
||||||
eVolumeLevelUnits Units { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum eVolumeLevelUnits
|
|
||||||
{
|
|
||||||
Decibels,
|
|
||||||
Percent,
|
|
||||||
Relative,
|
|
||||||
Absolute
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IHasCurrentVolumeControls
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasCurrentVolumeControls
|
|
||||||
{
|
|
||||||
IBasicVolumeControls CurrentVolumeControls { get; }
|
|
||||||
event EventHandler<VolumeDeviceChangeEventArgs> CurrentVolumeDeviceChange;
|
|
||||||
|
|
||||||
void SetDefaultLevels();
|
|
||||||
|
|
||||||
bool ZeroVolumeWhenSwtichingVolumeDevices { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IFullAudioSettings
|
|
||||||
/// </summary>
|
|
||||||
public interface IFullAudioSettings : IBasicVolumeWithFeedback
|
|
||||||
{
|
|
||||||
void SetBalance(ushort level);
|
|
||||||
void BalanceLeft(bool pressRelease);
|
|
||||||
void BalanceRight(bool pressRelease);
|
|
||||||
|
|
||||||
void SetBass(ushort level);
|
|
||||||
void BassUp(bool pressRelease);
|
|
||||||
void BassDown(bool pressRelease);
|
|
||||||
|
|
||||||
void SetTreble(ushort level);
|
|
||||||
void TrebleUp(bool pressRelease);
|
|
||||||
void TrebleDown(bool pressRelease);
|
|
||||||
|
|
||||||
bool hasMaxVolume { get; }
|
|
||||||
void SetMaxVolume(ushort level);
|
|
||||||
void MaxVolumeUp(bool pressRelease);
|
|
||||||
void MaxVolumeDown(bool pressRelease);
|
|
||||||
|
|
||||||
bool hasDefaultVolume { get; }
|
|
||||||
void SetDefaultVolume(ushort level);
|
|
||||||
void DefaultVolumeUp(bool pressRelease);
|
|
||||||
void DefaultVolumeDown(bool pressRelease);
|
|
||||||
|
|
||||||
void LoudnessToggle();
|
|
||||||
void MonoToggle();
|
|
||||||
|
|
||||||
BoolFeedback LoudnessFeedback { get; }
|
|
||||||
BoolFeedback MonoFeedback { get; }
|
|
||||||
IntFeedback BalanceFeedback { get; }
|
|
||||||
IntFeedback BassFeedback { get; }
|
|
||||||
IntFeedback TrebleFeedback { get; }
|
|
||||||
IntFeedback MaxVolumeFeedback { get; }
|
|
||||||
IntFeedback DefaultVolumeFeedback { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the contract for IHasVolumeDevice
|
|
||||||
/// </summary>
|
|
||||||
public interface IHasVolumeDevice
|
|
||||||
{
|
|
||||||
IBasicVolumeControls VolumeDevice { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Identifies a device that contains audio zones
|
|
||||||
/// </summary>
|
|
||||||
public interface IAudioZones : IRouting
|
|
||||||
{
|
|
||||||
Dictionary<uint, IAudioZone> Zone { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Defines minimum functionality for an audio zone
|
|
||||||
/// </summary>
|
|
||||||
public interface IAudioZone : IBasicVolumeWithFeedback
|
|
||||||
{
|
|
||||||
void SelectInput(ushort input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -77,9 +77,6 @@ namespace PepperDash.Essentials.Core
|
|||||||
/// A name that will override the source's name on the UI
|
/// A name that will override the source's name on the UI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("name")]
|
[JsonProperty("name")]
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Name
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Crestron.SimplSharp;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Essentials.Core;
|
|
||||||
using PepperDash.Essentials.Core.Config;
|
|
||||||
using PepperDash.Essentials.Core.Devices;
|
using PepperDash.Essentials.Core.Devices;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
@@ -25,17 +19,25 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the room info and custom properties from Fusion and updates the system properties aa needed
|
/// Evaluates the room info and custom properties from Fusion and updates the system properties aa needed
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="roomInfo"></param>
|
/// <param name="room">The room associated with this Fusion instance</param>
|
||||||
public void EvaluateRoomInfo(string roomKey, RoomInformation roomInfo)
|
/// <param name="roomInfo">The room information from Fusion</param>
|
||||||
|
/// <param name="useFusionRoomName"></param>
|
||||||
|
public void EvaluateRoomInfo(IEssentialsRoom room, RoomInformation roomInfo, bool useFusionRoomName)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var reconfigurableDevices = DeviceManager.AllDevices.Where(d => d is ReconfigurableDevice);
|
var reconfigurableDevices = DeviceManager.AllDevices.OfType<ReconfigurableDevice>();
|
||||||
|
|
||||||
foreach (var device in reconfigurableDevices)
|
foreach (var device in reconfigurableDevices)
|
||||||
{
|
{
|
||||||
// Get the current device config so new values can be overwritten over existing
|
// Get the current device config so new values can be overwritten over existing
|
||||||
var deviceConfig = (device as ReconfigurableDevice).Config;
|
var deviceConfig = device.Config;
|
||||||
|
|
||||||
|
if (device is IEssentialsRoom)
|
||||||
|
{
|
||||||
|
// Skipping room name as this will affect ALL room instances in the configuration and cause unintended consequences when multiple rooms are present and multiple Fusion instances are used
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (device is RoomOnToDefaultSourceWhenOccupied)
|
if (device is RoomOnToDefaultSourceWhenOccupied)
|
||||||
{
|
{
|
||||||
@@ -85,36 +87,49 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
|
|
||||||
deviceConfig.Properties = JToken.FromObject(devProps);
|
deviceConfig.Properties = JToken.FromObject(devProps);
|
||||||
}
|
}
|
||||||
else if (device is IEssentialsRoom)
|
|
||||||
{
|
|
||||||
// Set the room name
|
|
||||||
if (!string.IsNullOrEmpty(roomInfo.Name))
|
|
||||||
{
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Current Room Name: {0}. New Room Name: {1}", deviceConfig.Name, roomInfo.Name);
|
|
||||||
// Set the name in config
|
|
||||||
deviceConfig.Name = roomInfo.Name;
|
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Room Name Successfully Changed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the help message
|
|
||||||
var helpMessage = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomHelpMessage"));
|
|
||||||
if (helpMessage != null)
|
|
||||||
{
|
|
||||||
//Debug.LogMessage(LogEventLevel.Debug, "Current Help Message: {0}. New Help Message: {1}", deviceConfig.Properties["help"]["message"].Value<string>(ToString()), helpMessage.CustomFieldValue);
|
|
||||||
deviceConfig.Properties["helpMessage"] = (string)helpMessage.CustomFieldValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the config on the device
|
// Set the config on the device
|
||||||
(device as ReconfigurableDevice).SetConfig(deviceConfig);
|
device.SetConfig(deviceConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(room is ReconfigurableDevice reconfigurable))
|
||||||
|
{
|
||||||
|
Debug.LogWarning("FusionCustomPropertiesBridge: Room is not a ReconfigurableDevice. Cannot map custom properties.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var roomConfig = reconfigurable.Config;
|
||||||
|
|
||||||
|
var updateConfig = false;
|
||||||
|
|
||||||
|
// Set the room name
|
||||||
|
if (!string.IsNullOrEmpty(roomInfo.Name) && useFusionRoomName)
|
||||||
|
{
|
||||||
|
Debug.LogDebug("Current Room Name: {currentName}. New Room Name: {fusionName}", roomConfig.Name, roomInfo.Name);
|
||||||
|
// Set the name in config
|
||||||
|
roomConfig.Name = roomInfo.Name;
|
||||||
|
updateConfig = true;
|
||||||
|
|
||||||
|
Debug.LogDebug("Room Name Successfully Changed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the help message
|
||||||
|
var helpMessage = roomInfo.FusionCustomProperties.FirstOrDefault(p => p.ID.Equals("RoomHelpMessage"));
|
||||||
|
if (helpMessage != null)
|
||||||
|
{
|
||||||
|
roomConfig.Properties["helpMessage"] = helpMessage.CustomFieldValue;
|
||||||
|
updateConfig = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateConfig)
|
||||||
|
{
|
||||||
|
reconfigurable.SetConfig(roomConfig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "FusionCustomPropetiesBridge: Error mapping properties: {0}", e);
|
Debug.LogError("FusionCustomPropetiesBridge: Exception mapping properties for {roomKey}: {message}", room.Key, e.Message);
|
||||||
|
Debug.LogDebug(e, "Stack Trace: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
using Crestron.SimplSharp;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Timers;
|
||||||
|
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;
|
||||||
@@ -10,17 +15,13 @@ 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;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Fusion
|
namespace PepperDash.Essentials.Core.Fusion
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a EssentialsHuddleSpaceFusionSystemControllerBase
|
/// Represents a EssentialsHuddleSpaceFusionSystemControllerBase
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest
|
public class IEssentialsRoomFusionController : EssentialsDevice, IOccupancyStatusProvider, IFusionHelpRequest, IHasFeedback
|
||||||
{
|
{
|
||||||
private IEssentialsRoomFusionControllerPropertiesConfig _config;
|
private IEssentialsRoomFusionControllerPropertiesConfig _config;
|
||||||
|
|
||||||
@@ -87,15 +88,17 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public StringFeedback HelpRequestStatusFeedback { get; private set; }
|
public StringFeedback HelpRequestStatusFeedback { get; private set; }
|
||||||
|
|
||||||
|
private Timer _helpRequestTimeoutTimer;
|
||||||
|
|
||||||
#region System Info Sigs
|
/// <summary>
|
||||||
|
/// Gets the DefaultHelpRequestTimeoutMs
|
||||||
|
/// </summary>
|
||||||
|
public int HelpRequestTimeoutMs => _config.HelpRequestTimeoutMs;
|
||||||
|
|
||||||
//StringSigData SystemName;
|
/// <summary>
|
||||||
//StringSigData Model;
|
/// Gets whether to use a timer for help requests
|
||||||
//StringSigData SerialNumber;
|
/// </summary>
|
||||||
//StringSigData Uptime;
|
public bool UseHelpRequestTimer => _config.UseTimeoutForHelpRequests;
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Processor Info Sigs
|
#region Processor Info Sigs
|
||||||
|
|
||||||
@@ -240,6 +243,19 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
|
|
||||||
this.LogDebug("Occupancy setup complete");
|
this.LogDebug("Occupancy setup complete");
|
||||||
|
|
||||||
|
HelpRequestResponseFeedback = new StringFeedback("HelpRequestResponse", () => FusionRoom.Help.OutputSig.StringValue);
|
||||||
|
|
||||||
|
HelpRequestSentFeedback = new BoolFeedback("HelpRequestSent", () => _helpRequestSent);
|
||||||
|
HelpRequestStatusFeedback = new StringFeedback("HelpRequestStatus", () => _helpRequestStatus.ToString());
|
||||||
|
|
||||||
|
Feedbacks.Add(HelpRequestResponseFeedback);
|
||||||
|
Feedbacks.Add(HelpRequestSentFeedback);
|
||||||
|
Feedbacks.Add(HelpRequestStatusFeedback);
|
||||||
|
if (RoomOccupancyRemoteStringFeedback != null)
|
||||||
|
Feedbacks.Add(RoomOccupancyRemoteStringFeedback);
|
||||||
|
if (RoomIsOccupiedFeedback != null)
|
||||||
|
Feedbacks.Add(RoomIsOccupiedFeedback);
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -303,10 +319,6 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
|
|
||||||
FusionRVI.GenerateFileForAllFusionDevices();
|
FusionRVI.GenerateFileForAllFusionDevices();
|
||||||
|
|
||||||
HelpRequestResponseFeedback = new StringFeedback("HelpRequestResponse", () => FusionRoom.Help.OutputSig.StringValue);
|
|
||||||
|
|
||||||
HelpRequestSentFeedback = new BoolFeedback("HelpRequestSent", () => _helpRequestSent);
|
|
||||||
HelpRequestStatusFeedback = new StringFeedback("HelpRequestStatus", () => _helpRequestStatus.ToString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -339,6 +351,11 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public FeedbackCollection<Feedback> Feedbacks { get; private set; } = new FeedbackCollection<Feedback>();
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ScheduleChange event
|
/// ScheduleChange event
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1074,7 +1091,7 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
}
|
}
|
||||||
RoomInfoChange?.Invoke(this, new EventArgs());
|
RoomInfoChange?.Invoke(this, new EventArgs());
|
||||||
|
|
||||||
CustomPropertiesBridge.EvaluateRoomInfo(Room.Key, roomInformation);
|
CustomPropertiesBridge.EvaluateRoomInfo(Room, roomInformation, _config.UseFusionRoomName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -1772,7 +1789,7 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
{
|
{
|
||||||
if (args.EventId == FusionEventIds.HelpMessageReceivedEventId)
|
if (args.EventId == FusionEventIds.HelpMessageReceivedEventId)
|
||||||
{
|
{
|
||||||
this.LogInformation( "Help message received from Fusion for room '{0}'",
|
this.LogInformation("Help message received from Fusion for room '{0}'",
|
||||||
Room.Name);
|
Room.Name);
|
||||||
|
|
||||||
this.LogDebug("Help message content: {0}", FusionRoom.Help.OutputSig.StringValue);
|
this.LogDebug("Help message content: {0}", FusionRoom.Help.OutputSig.StringValue);
|
||||||
@@ -1791,7 +1808,7 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
break;
|
break;
|
||||||
case "Please call the helpdesk.":
|
case "Please call the helpdesk.":
|
||||||
// this.LogInformation("Please call the helpdesk.");
|
// this.LogInformation("Please call the helpdesk.");
|
||||||
// _helpRequestStatus = eFusionHelpResponse.CallHelpDesk;
|
_helpRequestStatus = eFusionHelpResponse.CallHelpDesk;
|
||||||
break;
|
break;
|
||||||
case "Please wait, I will reschedule your meeting to a different room.":
|
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.",
|
// this.LogInformation("Please wait, I will reschedule your meeting to a different room.",
|
||||||
@@ -1818,13 +1835,21 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
_helpRequestStatus = eFusionHelpResponse.None;
|
_helpRequestStatus = eFusionHelpResponse.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_helpRequestStatus == eFusionHelpResponse.None)
|
if (_helpRequestStatus == eFusionHelpResponse.None)
|
||||||
{
|
{
|
||||||
_helpRequestSent = false;
|
_helpRequestSent = false;
|
||||||
HelpRequestSentFeedback.FireUpdate();
|
HelpRequestSentFeedback.FireUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpRequestStatusFeedback.FireUpdate();
|
HelpRequestStatusFeedback.FireUpdate();
|
||||||
|
|
||||||
|
if (_helpRequestTimeoutTimer != null)
|
||||||
|
{
|
||||||
|
_helpRequestTimeoutTimer.Stop();
|
||||||
|
_helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;
|
||||||
|
_helpRequestTimeoutTimer.Dispose();
|
||||||
|
_helpRequestTimeoutTimer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1895,10 +1920,34 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
_helpRequestSent = true;
|
_helpRequestSent = true;
|
||||||
HelpRequestSentFeedback.FireUpdate();
|
HelpRequestSentFeedback.FireUpdate();
|
||||||
|
|
||||||
|
if (UseHelpRequestTimer)
|
||||||
|
{
|
||||||
|
if (_helpRequestTimeoutTimer == null)
|
||||||
|
{
|
||||||
|
_helpRequestTimeoutTimer = new Timer(HelpRequestTimeoutMs);
|
||||||
|
_helpRequestTimeoutTimer.AutoReset = false;
|
||||||
|
_helpRequestTimeoutTimer.Enabled = true;
|
||||||
|
|
||||||
|
_helpRequestTimeoutTimer.Elapsed += OnTimedEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_helpRequestTimeoutTimer.Interval = HelpRequestTimeoutMs;
|
||||||
|
_helpRequestTimeoutTimer.Start();
|
||||||
|
|
||||||
|
this.LogDebug("Help request timeout timer started for room '{0}' with timeout of {1} ms.",
|
||||||
|
Room.Name, HelpRequestTimeoutMs);
|
||||||
|
}
|
||||||
|
|
||||||
_helpRequestStatus = eFusionHelpResponse.HelpRequested;
|
_helpRequestStatus = eFusionHelpResponse.HelpRequested;
|
||||||
HelpRequestStatusFeedback.FireUpdate();
|
HelpRequestStatusFeedback.FireUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnTimedEvent(object source, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
this.LogInformation("Help request timeout reached for room '{0}'. Cancelling help request.", Room.Name);
|
||||||
|
CancelHelpRequest();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void CancelHelpRequest()
|
public void CancelHelpRequest()
|
||||||
{
|
{
|
||||||
@@ -1909,7 +1958,16 @@ namespace PepperDash.Essentials.Core.Fusion
|
|||||||
HelpRequestSentFeedback.FireUpdate();
|
HelpRequestSentFeedback.FireUpdate();
|
||||||
_helpRequestStatus = eFusionHelpResponse.None;
|
_helpRequestStatus = eFusionHelpResponse.None;
|
||||||
HelpRequestStatusFeedback.FireUpdate();
|
HelpRequestStatusFeedback.FireUpdate();
|
||||||
Debug.LogMessage(LogEventLevel.Information, this, "Help request cancelled in Fusion for room '{0}'", Room.Name);
|
Debug.LogMessage(LogEventLevel.Information, this, "Help request cancelled for room '{0}'", Room.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_helpRequestTimeoutTimer != null)
|
||||||
|
{
|
||||||
|
_helpRequestTimeoutTimer.Stop();
|
||||||
|
_helpRequestTimeoutTimer.Elapsed -= OnTimedEvent;
|
||||||
|
_helpRequestTimeoutTimer.Dispose();
|
||||||
|
_helpRequestTimeoutTimer = null;
|
||||||
|
this.LogDebug("Help request timeout timer stopped for room '{0}'.", Room.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class IEssentialsRoomFusionControllerPropertiesConfig
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogWarning( "Failed to parse IpId '{0}' as UInt16", IpId);
|
Debug.LogWarning("Failed to parse IpId '{0}' as UInt16", IpId);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -45,6 +45,13 @@ public class IEssentialsRoomFusionControllerPropertiesConfig
|
|||||||
[JsonProperty("roomKey")]
|
[JsonProperty("roomKey")]
|
||||||
public string RoomKey { get; set; }
|
public string RoomKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to use the Fusion room name for this room
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Defaults to true to preserve current behavior. Set to false to skip updating the room name from Fusion</remarks>
|
||||||
|
[JsonProperty("useFusionRoomName")]
|
||||||
|
public bool UseFusionRoomName { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets whether to use HTML format for help requests
|
/// Gets or sets whether to use HTML format for help requests
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,4 +63,16 @@ public class IEssentialsRoomFusionControllerPropertiesConfig
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("use24HourTimeFormat")]
|
[JsonProperty("use24HourTimeFormat")]
|
||||||
public bool Use24HourTimeFormat { get; set; } = false;
|
public bool Use24HourTimeFormat { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether to use a timeout for help requests
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("useTimeoutForHelpRequests")]
|
||||||
|
public bool UseTimeoutForHelpRequests { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the timeout duration for help requests in milliseconds
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("helpRequestTimeoutMs")]
|
||||||
|
public int HelpRequestTimeoutMs { get; set; } = 30000;
|
||||||
}
|
}
|
||||||
@@ -105,12 +105,21 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomPropertiesConfig
|
public class EssentialsRoomPropertiesConfig
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Addresses
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("addresses")]
|
[JsonProperty("addresses")]
|
||||||
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
|
public EssentialsRoomAddressPropertiesConfig Addresses { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Description
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("description")]
|
[JsonProperty("description")]
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Emergency
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("emergency")]
|
[JsonProperty("emergency")]
|
||||||
public EssentialsRoomEmergencyConfig Emergency { get; set; }
|
public EssentialsRoomEmergencyConfig Emergency { get; set; }
|
||||||
|
|
||||||
@@ -226,11 +235,11 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// Indicates if this room represents a combination of other rooms
|
/// Indicates if this room represents a combination of other rooms
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("isRoomCombinationScenario")]
|
[JsonProperty("isRoomCombinationScenario")]
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the IsRoomCombinationScenario
|
|
||||||
/// </summary>
|
|
||||||
public bool IsRoomCombinationScenario { get; set; }
|
public bool IsRoomCombinationScenario { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public EssentialsRoomPropertiesConfig()
|
public EssentialsRoomPropertiesConfig()
|
||||||
{
|
{
|
||||||
LogoLight = new EssentialsLogoPropertiesConfig();
|
LogoLight = new EssentialsLogoPropertiesConfig();
|
||||||
@@ -243,10 +252,10 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomUiBehaviorConfig
|
public class EssentialsRoomUiBehaviorConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("disableActivityButtonsWhileWarmingCooling")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DisableActivityButtonsWhileWarmingCooling
|
/// Gets or sets the DisableActivityButtonsWhileWarmingCooling
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("disableActivityButtonsWhileWarmingCooling")]
|
||||||
public bool DisableActivityButtonsWhileWarmingCooling { get; set; }
|
public bool DisableActivityButtonsWhileWarmingCooling { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,74 +264,86 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig
|
public class EssentialsAvRoomPropertiesConfig : EssentialsRoomPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("defaultAudioKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DefaultAudioKey
|
/// Gets or sets the DefaultAudioKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("defaultAudioKey")]
|
||||||
public string DefaultAudioKey { get; set; }
|
public string DefaultAudioKey { get; set; }
|
||||||
[JsonProperty("sourceListKey")]
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the DefaultOnDspPresetKey
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("defaultOnDspPresetKey")]
|
||||||
|
public string DefaultOnDspPresetKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the DefaultOffDspPresetKey
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("defaultOffDspPresetKey")]
|
||||||
|
public string DefaultOffDspPresetKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the SourceListKey
|
/// Gets or sets the SourceListKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("sourceListKey")]
|
||||||
public string SourceListKey { get; set; }
|
public string SourceListKey { get; set; }
|
||||||
[JsonProperty("destinationListKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DestinationListKey
|
/// Gets or sets the DestinationListKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("destinationListKey")]
|
||||||
public string DestinationListKey { get; set; }
|
public string DestinationListKey { get; set; }
|
||||||
[JsonProperty("audioControlPointListKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the AudioControlPointListKey
|
/// Gets or sets the AudioControlPointListKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("audioControlPointListKey")]
|
||||||
public string AudioControlPointListKey { get; set; }
|
public string AudioControlPointListKey { get; set; }
|
||||||
[JsonProperty("cameraListKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the CameraListKey
|
/// Gets or sets the CameraListKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("cameraListKey")]
|
||||||
public string CameraListKey { get; set; }
|
public string CameraListKey { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[JsonProperty("defaultSourceItem")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DefaultSourceItem
|
/// Gets or sets the DefaultSourceItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("defaultSourceItem")]
|
||||||
public string DefaultSourceItem { get; set; }
|
public string DefaultSourceItem { get; set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if the room supports advanced sharing
|
/// Indicates if the room supports advanced sharing
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("supportsAdvancedSharing")]
|
[JsonProperty("supportsAdvancedSharing")]
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the SupportsAdvancedSharing
|
|
||||||
/// </summary>
|
|
||||||
public bool SupportsAdvancedSharing { get; set; }
|
public bool SupportsAdvancedSharing { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates if non-tech users can change the share mode
|
/// Indicates if non-tech users can change the share mode
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("userCanChangeShareMode")]
|
[JsonProperty("userCanChangeShareMode")]
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the UserCanChangeShareMode
|
|
||||||
/// </summary>
|
|
||||||
public bool UserCanChangeShareMode { get; set; }
|
public bool UserCanChangeShareMode { get; set; }
|
||||||
|
|
||||||
|
|
||||||
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the MatrixRoutingKey
|
/// Gets or sets the MatrixRoutingKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("matrixRoutingKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string MatrixRoutingKey { get; set; }
|
public string MatrixRoutingKey { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a EssentialsConferenceRoomPropertiesConfig
|
||||||
|
/// </summary>
|
||||||
public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig
|
public class EssentialsConferenceRoomPropertiesConfig : EssentialsAvRoomPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("videoCodecKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the VideoCodecKey
|
/// Gets or sets the VideoCodecKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("videoCodecKey")]
|
||||||
public string VideoCodecKey { get; set; }
|
public string VideoCodecKey { get; set; }
|
||||||
[JsonProperty("audioCodecKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the AudioCodecKey
|
/// Gets or sets the AudioCodecKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("audioCodecKey")]
|
||||||
public string AudioCodecKey { get; set; }
|
public string AudioCodecKey { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -337,12 +358,15 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
|
|
||||||
[JsonProperty("deviceKeys")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DeviceKeys
|
/// Gets or sets the DeviceKeys
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("deviceKeys")]
|
||||||
public List<string> DeviceKeys { get; set; }
|
public List<string> DeviceKeys { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public EssentialsEnvironmentPropertiesConfig()
|
public EssentialsEnvironmentPropertiesConfig()
|
||||||
{
|
{
|
||||||
DeviceKeys = new List<string>();
|
DeviceKeys = new List<string>();
|
||||||
@@ -355,6 +379,9 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomFusionConfig
|
public class EssentialsRoomFusionConfig
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the the IpId as a UInt16
|
||||||
|
/// </summary>
|
||||||
public uint IpIdInt
|
public uint IpIdInt
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -371,16 +398,16 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty("ipId")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the IpId
|
/// Gets or sets the IpId
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("ipId")]
|
||||||
public string IpId { get; set; }
|
public string IpId { get; set; }
|
||||||
|
|
||||||
[JsonProperty("joinMapKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the JoinMapKey
|
/// Gets or sets the JoinMapKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("joinMapKey")]
|
||||||
public string JoinMapKey { get; set; }
|
public string JoinMapKey { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -390,16 +417,16 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomMicrophonePrivacyConfig
|
public class EssentialsRoomMicrophonePrivacyConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("deviceKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DeviceKey
|
/// Gets or sets the DeviceKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("deviceKey")]
|
||||||
public string DeviceKey { get; set; }
|
public string DeviceKey { get; set; }
|
||||||
|
|
||||||
[JsonProperty("behaviour")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Behaviour
|
/// Gets or sets the Behaviour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("behaviour")]
|
||||||
public string Behaviour { get; set; }
|
public string Behaviour { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,12 +435,15 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsHelpPropertiesConfig
|
public class EssentialsHelpPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("message")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Message
|
/// Gets or sets the Message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("message")]
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the ShowCallButton
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("showCallButton")]
|
[JsonProperty("showCallButton")]
|
||||||
public bool ShowCallButton { get; set; }
|
public bool ShowCallButton { get; set; }
|
||||||
|
|
||||||
@@ -421,11 +451,11 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// Defaults to "Call Help Desk"
|
/// Defaults to "Call Help Desk"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("callButtonText")]
|
[JsonProperty("callButtonText")]
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the CallButtonText
|
|
||||||
/// </summary>
|
|
||||||
public string CallButtonText { get; set; }
|
public string CallButtonText { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
public EssentialsHelpPropertiesConfig()
|
public EssentialsHelpPropertiesConfig()
|
||||||
{
|
{
|
||||||
CallButtonText = "Call Help Desk";
|
CallButtonText = "Call Help Desk";
|
||||||
@@ -437,22 +467,28 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsOneButtonMeetingPropertiesConfig
|
public class EssentialsOneButtonMeetingPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("enable")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Enable
|
/// Gets or sets the Enable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("enable")]
|
||||||
public bool Enable { get; set; }
|
public bool Enable { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a EssentialsRoomAddressPropertiesConfig
|
||||||
|
/// </summary>
|
||||||
public class EssentialsRoomAddressPropertiesConfig
|
public class EssentialsRoomAddressPropertiesConfig
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the PhoneNumber
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("phoneNumber")]
|
[JsonProperty("phoneNumber")]
|
||||||
public string PhoneNumber { get; set; }
|
public string PhoneNumber { get; set; }
|
||||||
|
|
||||||
[JsonProperty("sipAddress")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the SipAddress
|
/// Gets or sets the SipAddress
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("sipAddress")]
|
||||||
public string SipAddress { get; set; }
|
public string SipAddress { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,14 +498,18 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsLogoPropertiesConfig
|
public class EssentialsLogoPropertiesConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("type")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Type
|
/// Gets or sets the Type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("type")]
|
||||||
public string Type { get; set; }
|
public string Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Url
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("url")]
|
[JsonProperty("url")]
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// GetLogoUrlLight method
|
/// GetLogoUrlLight method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -502,22 +542,28 @@ namespace PepperDash.Essentials.Room.Config
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EssentialsRoomOccSensorConfig
|
public class EssentialsRoomOccSensorConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("deviceKey")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the DeviceKey
|
/// Gets or sets the DeviceKey
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("deviceKey")]
|
||||||
public string DeviceKey { get; set; }
|
public string DeviceKey { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the TimeoutMinutes
|
||||||
|
/// </summary>
|
||||||
[JsonProperty("timeoutMinutes")]
|
[JsonProperty("timeoutMinutes")]
|
||||||
public int TimeoutMinutes { get; set; }
|
public int TimeoutMinutes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a EssentialsRoomTechConfig
|
||||||
|
/// </summary>
|
||||||
public class EssentialsRoomTechConfig
|
public class EssentialsRoomTechConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("password")]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Password
|
/// Gets or sets the Password
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[JsonProperty("password")]
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using Crestron.SimplSharpPro.Keypads;
|
using System;
|
||||||
using PepperDash.Essentials.Core.Queues;
|
|
||||||
using PepperDash.Essentials.Core.Routing;
|
|
||||||
using Serilog.Events;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Crestron.SimplSharpPro.Keypads;
|
||||||
|
using PepperDash.Essentials.Core.Queues;
|
||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
|
using Serilog.Events;
|
||||||
using Debug = PepperDash.Core.Debug;
|
using Debug = PepperDash.Core.Debug;
|
||||||
|
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
|
public static (RouteDescriptor, RouteDescriptor) GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
|
||||||
{
|
{
|
||||||
// if it's a single signal type, find the route
|
// if it's a single signal type, find the route
|
||||||
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) &&
|
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo) &&
|
||||||
!(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio)))
|
!(signalType.HasFlag(eRoutingSignalType.Video) && signalType.HasFlag(eRoutingSignalType.SecondaryAudio)))
|
||||||
{
|
{
|
||||||
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType);
|
var singleTypeRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, signalType);
|
||||||
@@ -134,14 +134,15 @@ namespace PepperDash.Essentials.Core
|
|||||||
}
|
}
|
||||||
// otherwise, audioVideo needs to be handled as two steps.
|
// otherwise, audioVideo needs to be handled as two steps.
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {sourceKey} of type {type}", destination, source.Key);
|
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {destinationKey} to {sourceKey} of type {type}", destination, source.Key, signalType);
|
||||||
|
|
||||||
RouteDescriptor audioRouteDescriptor;
|
RouteDescriptor audioRouteDescriptor;
|
||||||
|
|
||||||
if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio))
|
if (signalType.HasFlag(eRoutingSignalType.SecondaryAudio))
|
||||||
{
|
{
|
||||||
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.SecondaryAudio);
|
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.SecondaryAudio);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio);
|
audioRouteDescriptor = new RouteDescriptor(source, destination, destinationPort, eRoutingSignalType.Audio);
|
||||||
}
|
}
|
||||||
@@ -199,13 +200,13 @@ namespace PepperDash.Essentials.Core
|
|||||||
Source = source,
|
Source = source,
|
||||||
SourcePort = sourcePort,
|
SourcePort = sourcePort,
|
||||||
SignalType = signalType
|
SignalType = signalType
|
||||||
};
|
};
|
||||||
|
|
||||||
var coolingDevice = destination as IWarmingCooling;
|
var coolingDevice = destination as IWarmingCooling;
|
||||||
|
|
||||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||||
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
{
|
{
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||||
|
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
@@ -219,7 +220,7 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
//New Request
|
//New Request
|
||||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||||
{
|
{
|
||||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||||
|
|
||||||
RouteRequests.Add(destination.Key, routeRequest);
|
RouteRequests.Add(destination.Key, routeRequest);
|
||||||
@@ -239,9 +240,9 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false));
|
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, destinationPort?.Key ?? string.Empty, false));
|
||||||
|
|
||||||
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
|
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -272,7 +273,8 @@ namespace PepperDash.Essentials.Core
|
|||||||
|
|
||||||
audioOrSingleRoute.ExecuteRoutes();
|
audioOrSingleRoute.ExecuteRoutes();
|
||||||
videoRoute?.ExecuteRoutes();
|
videoRoute?.ExecuteRoutes();
|
||||||
} catch(Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
||||||
}
|
}
|
||||||
@@ -305,9 +307,10 @@ namespace PepperDash.Essentials.Core
|
|||||||
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
||||||
current.ReleaseRoutes(clearRoute);
|
current.ReleaseRoutes(clearRoute);
|
||||||
}
|
}
|
||||||
} catch (Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'",null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
Debug.LogMessage(ex, "Exception releasing route for '{destination}':'{inputPortKey}'", null, destination?.Key ?? null, string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using PepperDash.Core;
|
using System;
|
||||||
using PepperDash.Essentials.Core.Config;
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Essentials.Core.Config;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Core.Routing
|
namespace PepperDash.Essentials.Core.Routing
|
||||||
{
|
{
|
||||||
@@ -9,20 +9,20 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// Manages routing feedback by subscribing to route changes on midpoint and sink devices,
|
/// Manages routing feedback by subscribing to route changes on midpoint and sink devices,
|
||||||
/// tracing the route back to the original source, and updating the CurrentSourceInfo on sink devices.
|
/// tracing the route back to the original source, and updating the CurrentSourceInfo on sink devices.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RoutingFeedbackManager:EssentialsDevice
|
public class RoutingFeedbackManager : EssentialsDevice
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RoutingFeedbackManager"/> class.
|
/// Initializes a new instance of the <see cref="RoutingFeedbackManager"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The unique key for this manager device.</param>
|
/// <param name="key">The unique key for this manager device.</param>
|
||||||
/// <param name="name">The name of this manager device.</param>
|
/// <param name="name">The name of this manager device.</param>
|
||||||
public RoutingFeedbackManager(string key, string name): base(key, name)
|
public RoutingFeedbackManager(string key, string name)
|
||||||
{
|
: base(key, name)
|
||||||
|
{
|
||||||
AddPreActivationAction(SubscribeForMidpointFeedback);
|
AddPreActivationAction(SubscribeForMidpointFeedback);
|
||||||
AddPreActivationAction(SubscribeForSinkFeedback);
|
AddPreActivationAction(SubscribeForSinkFeedback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Subscribes to the RouteChanged event on all devices implementing <see cref="IRoutingWithFeedback"/>.
|
/// Subscribes to the RouteChanged event on all devices implementing <see cref="IRoutingWithFeedback"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -41,12 +41,13 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void SubscribeForSinkFeedback()
|
private void SubscribeForSinkFeedback()
|
||||||
{
|
{
|
||||||
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
var sinkDevices =
|
||||||
|
DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||||
|
|
||||||
foreach (var device in sinkDevices)
|
foreach (var device in sinkDevices)
|
||||||
{
|
{
|
||||||
device.InputChanged += HandleSinkUpdate;
|
device.InputChanged += HandleSinkUpdate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -55,11 +56,15 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="midpoint">The midpoint device that reported a route change.</param>
|
/// <param name="midpoint">The midpoint device that reported a route change.</param>
|
||||||
/// <param name="newRoute">The descriptor of the new route.</param>
|
/// <param name="newRoute">The descriptor of the new route.</param>
|
||||||
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
|
private void HandleMidpointUpdate(
|
||||||
|
IRoutingWithFeedback midpoint,
|
||||||
|
RouteSwitchDescriptor newRoute
|
||||||
|
)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var devices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
var devices =
|
||||||
|
DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||||
|
|
||||||
foreach (var device in devices)
|
foreach (var device in devices)
|
||||||
{
|
{
|
||||||
@@ -68,7 +73,13 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Error handling midpoint update from {midpointKey}:{Exception}", this, midpoint.Key, ex);
|
Debug.LogMessage(
|
||||||
|
ex,
|
||||||
|
"Error handling midpoint update from {midpointKey}:{Exception}",
|
||||||
|
this,
|
||||||
|
midpoint.Key,
|
||||||
|
ex
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +89,10 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender">The sink device that reported an input change.</param>
|
/// <param name="sender">The sink device that reported an input change.</param>
|
||||||
/// <param name="currentInputPort">The new input port selected on the sink device.</param>
|
/// <param name="currentInputPort">The new input port selected on the sink device.</param>
|
||||||
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
|
private void HandleSinkUpdate(
|
||||||
|
IRoutingSinkWithSwitching sender,
|
||||||
|
RoutingInputPort currentInputPort
|
||||||
|
)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -86,7 +100,13 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Error handling Sink update from {senderKey}:{Exception}", this, sender.Key, ex);
|
Debug.LogMessage(
|
||||||
|
ex,
|
||||||
|
"Error handling Sink update from {senderKey}:{Exception}",
|
||||||
|
this,
|
||||||
|
sender.Key,
|
||||||
|
ex
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,13 +116,27 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="destination">The destination sink device to update.</param>
|
/// <param name="destination">The destination sink device to update.</param>
|
||||||
/// <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,
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this, destination?.Key, inputPort?.Key);
|
RoutingInputPort inputPort
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"Updating destination {destination} with inputPort {inputPort}",
|
||||||
|
this,
|
||||||
|
destination?.Key,
|
||||||
|
inputPort?.Key
|
||||||
|
);
|
||||||
|
|
||||||
if(inputPort == null)
|
if (inputPort == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Destination {destination} has not reported an input port yet", this,destination.Key);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"Destination {destination} has not reported an input port yet",
|
||||||
|
this,
|
||||||
|
destination.Key
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,11 +145,19 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
{
|
{
|
||||||
var tieLines = TieLineCollection.Default;
|
var tieLines = TieLineCollection.Default;
|
||||||
|
|
||||||
firstTieLine = tieLines.FirstOrDefault(tl => tl.DestinationPort.Key == inputPort.Key && tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);
|
firstTieLine = tieLines.FirstOrDefault(tl =>
|
||||||
|
tl.DestinationPort.Key == inputPort.Key
|
||||||
|
&& tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key
|
||||||
|
);
|
||||||
|
|
||||||
if (firstTieLine == null)
|
if (firstTieLine == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No tieline found for inputPort {inputPort}. Clearing current source", this, inputPort);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No tieline found for inputPort {inputPort}. Clearing current source",
|
||||||
|
this,
|
||||||
|
inputPort
|
||||||
|
);
|
||||||
|
|
||||||
var tempSourceListItem = new SourceListItem
|
var tempSourceListItem = new SourceListItem
|
||||||
{
|
{
|
||||||
@@ -123,12 +165,13 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
Name = inputPort.Key,
|
Name = inputPort.Key,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
destination.CurrentSourceInfo = tempSourceListItem;
|
||||||
destination.CurrentSourceInfo = tempSourceListItem; ;
|
;
|
||||||
destination.CurrentSourceInfoKey = "$transient";
|
destination.CurrentSourceInfoKey = "$transient";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Error getting first tieline: {Exception}", this, ex);
|
Debug.LogMessage(ex, "Error getting first tieline: {Exception}", this, ex);
|
||||||
return;
|
return;
|
||||||
@@ -143,7 +186,12 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
|
|
||||||
if (sourceTieLine == null)
|
if (sourceTieLine == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route found to source for inputPort {inputPort}. Clearing current source", this, inputPort);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No route found to source for inputPort {inputPort}. Clearing current source",
|
||||||
|
this,
|
||||||
|
inputPort
|
||||||
|
);
|
||||||
|
|
||||||
var tempSourceListItem = new SourceListItem
|
var tempSourceListItem = new SourceListItem
|
||||||
{
|
{
|
||||||
@@ -155,32 +203,45 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
destination.CurrentSourceInfoKey = string.Empty;
|
destination.CurrentSourceInfoKey = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch(Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Error getting sourceTieLine: {Exception}", this, ex);
|
Debug.LogMessage(ex, "Error getting sourceTieLine: {Exception}", this, ex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root TieLine {tieLine}", this, sourceTieLine);
|
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root TieLine {tieLine}", this, sourceTieLine);
|
||||||
|
|
||||||
// Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.
|
// Does not handle combinable scenarios or other scenarios where a display might be part of multiple rooms yet.
|
||||||
var room = DeviceManager.AllDevices.OfType<IEssentialsRoom>().FirstOrDefault((r) => {
|
var room = DeviceManager
|
||||||
if(r is IHasMultipleDisplays roomMultipleDisplays)
|
.AllDevices.OfType<IEssentialsRoom>()
|
||||||
{
|
.FirstOrDefault(
|
||||||
return roomMultipleDisplays.Displays.Any(d => d.Value.Key == destination.Key);
|
(r) =>
|
||||||
}
|
{
|
||||||
|
if (r is IHasMultipleDisplays roomMultipleDisplays)
|
||||||
|
{
|
||||||
|
return roomMultipleDisplays.Displays.Any(d =>
|
||||||
|
d.Value.Key == destination.Key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if(r is IHasDefaultDisplay roomDefaultDisplay)
|
if (r is IHasDefaultDisplay roomDefaultDisplay)
|
||||||
{
|
{
|
||||||
return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
|
return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
}
|
||||||
|
);
|
||||||
if(room == null)
|
|
||||||
|
if (room == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No room found for display {destination}", this, destination.Key);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No room found for display {destination}",
|
||||||
|
this,
|
||||||
|
destination.Key
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,29 +251,45 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
|
|
||||||
if (sourceList == null)
|
if (sourceList == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}", this, room.SourceListKey, sourceTieLine);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No source list found for source list key {key}. Unable to find source for tieLine {sourceTieLine}",
|
||||||
|
this,
|
||||||
|
room.SourceListKey,
|
||||||
|
sourceTieLine
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
|
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
|
||||||
|
|
||||||
var sourceListItem = sourceList.FirstOrDefault(sli => {
|
var sourceListItem = sourceList.FirstOrDefault(sli =>
|
||||||
//// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
|
{
|
||||||
// "SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
|
//// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
|
||||||
// this,
|
// "SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
|
||||||
// sli.Key,
|
// this,
|
||||||
// sli.Value.SourceKey,
|
// sli.Key,
|
||||||
// sourceTieLine.SourcePort.ParentDevice.Key);
|
// sli.Value.SourceKey,
|
||||||
|
// sourceTieLine.SourcePort.ParentDevice.Key);
|
||||||
|
|
||||||
return sli.Value.SourceKey.Equals(sourceTieLine.SourcePort.ParentDevice.Key,StringComparison.InvariantCultureIgnoreCase);
|
return sli.Value.SourceKey.Equals(
|
||||||
});
|
sourceTieLine.SourcePort.ParentDevice.Key,
|
||||||
|
StringComparison.InvariantCultureIgnoreCase
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
var source = sourceListItem.Value;
|
var source = sourceListItem.Value;
|
||||||
var sourceKey = sourceListItem.Key;
|
var sourceKey = sourceListItem.Key;
|
||||||
|
|
||||||
if (source == null)
|
if (source == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No source found for device {key}. Creating transient source for {destination}", this, sourceTieLine.SourcePort.ParentDevice.Key, destination);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No source found for device {key}. Creating transient source for {destination}",
|
||||||
|
this,
|
||||||
|
sourceTieLine.SourcePort.ParentDevice.Key,
|
||||||
|
destination
|
||||||
|
);
|
||||||
|
|
||||||
var tempSourceListItem = new SourceListItem
|
var tempSourceListItem = new SourceListItem
|
||||||
{
|
{
|
||||||
@@ -221,7 +298,7 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
};
|
};
|
||||||
|
|
||||||
destination.CurrentSourceInfoKey = "$transient";
|
destination.CurrentSourceInfoKey = "$transient";
|
||||||
destination.CurrentSourceInfo = tempSourceListItem;
|
destination.CurrentSourceInfo = tempSourceListItem;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,7 +306,6 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
|
|
||||||
destination.CurrentSourceInfoKey = sourceKey;
|
destination.CurrentSourceInfoKey = sourceKey;
|
||||||
destination.CurrentSourceInfo = source;
|
destination.CurrentSourceInfo = source;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -249,29 +325,49 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
{
|
{
|
||||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
|
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
|
||||||
|
|
||||||
if(midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
|
if (midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Midpoint {midpointKey} has no routes",this, midpoint.Key);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"Midpoint {midpointKey} has no routes",
|
||||||
|
this,
|
||||||
|
midpoint.Key
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => {
|
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route =>
|
||||||
|
{
|
||||||
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
|
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
|
||||||
|
|
||||||
return route.OutputPort != null && route.InputPort != null && route.OutputPort?.Key == tieLine.SourcePort.Key && route.OutputPort?.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
|
return route.OutputPort != null
|
||||||
|
&& route.InputPort != null
|
||||||
|
&& route.OutputPort?.Key == tieLine.SourcePort.Key
|
||||||
|
&& route.OutputPort?.ParentDevice.Key
|
||||||
|
== tieLine.SourcePort.ParentDevice.Key;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (currentRoute == null)
|
if (currentRoute == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route through midpoint {midpoint} for outputPort {outputPort}", this, midpoint.Key, tieLine.SourcePort);
|
Debug.LogMessage(
|
||||||
|
Serilog.Events.LogEventLevel.Debug,
|
||||||
|
"No route through midpoint {midpoint} for outputPort {outputPort}",
|
||||||
|
this,
|
||||||
|
midpoint.Key,
|
||||||
|
tieLine.SourcePort
|
||||||
|
);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
|
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
|
||||||
|
|
||||||
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => {
|
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl =>
|
||||||
|
{
|
||||||
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", tl.DestinationPort.Key, currentRoute.InputPort.Key);
|
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", tl.DestinationPort.Key, currentRoute.InputPort.Key);
|
||||||
return tl.DestinationPort.Key == currentRoute.InputPort.Key && tl.DestinationPort.ParentDevice.Key == currentRoute.InputPort.ParentDevice.Key; });
|
return tl.DestinationPort.Key == currentRoute.InputPort.Key
|
||||||
|
&& tl.DestinationPort.ParentDevice.Key
|
||||||
|
== currentRoute.InputPort.ParentDevice.Key;
|
||||||
|
});
|
||||||
|
|
||||||
if (nextTieLine != null)
|
if (nextTieLine != null)
|
||||||
{
|
{
|
||||||
@@ -286,19 +382,26 @@ namespace PepperDash.Essentials.Core.Routing
|
|||||||
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLIne Source Device {sourceDeviceKey} is IRoutingSource: {isIRoutingSource}", this, tieLine.SourcePort.ParentDevice.Key, tieLine.SourcePort.ParentDevice is IRoutingSource);
|
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLIne Source Device {sourceDeviceKey} is IRoutingSource: {isIRoutingSource}", this, tieLine.SourcePort.ParentDevice.Key, tieLine.SourcePort.ParentDevice is IRoutingSource);
|
||||||
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source Device interfaces: {typeFullName}:{interfaces}", this, tieLine.SourcePort.ParentDevice.GetType().FullName, tieLine.SourcePort.ParentDevice.GetType().GetInterfaces().Select(i => i.Name));
|
//Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source Device interfaces: {typeFullName}:{interfaces}", this, tieLine.SourcePort.ParentDevice.GetType().FullName, tieLine.SourcePort.ParentDevice.GetType().GetInterfaces().Select(i => i.Name));
|
||||||
|
|
||||||
if (tieLine.SourcePort.ParentDevice is IRoutingSource || tieLine.SourcePort.ParentDevice is IRoutingOutputs) //end of the chain
|
if (
|
||||||
|
tieLine.SourcePort.ParentDevice is IRoutingSource
|
||||||
|
|| tieLine.SourcePort.ParentDevice is IRoutingOutputs
|
||||||
|
) //end of the chain
|
||||||
{
|
{
|
||||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root: {tieLine}", this, tieLine);
|
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root: {tieLine}", this, tieLine);
|
||||||
return tieLine;
|
return tieLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.DestinationPort.Key == tieLine.SourcePort.Key && tl.DestinationPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key );
|
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl =>
|
||||||
|
tl.DestinationPort.Key == tieLine.SourcePort.Key
|
||||||
|
&& tl.DestinationPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key
|
||||||
|
);
|
||||||
|
|
||||||
if (nextTieLine != null)
|
if (nextTieLine != null)
|
||||||
{
|
{
|
||||||
return GetRootTieLine(nextTieLine);
|
return GetRootTieLine(nextTieLine);
|
||||||
}
|
}
|
||||||
} catch (Exception ex)
|
}
|
||||||
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(ex, "Error walking tieLines: {Exception}", this, ex);
|
Debug.LogMessage(ex, "Error walking tieLines: {Exception}", this, ex);
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -61,13 +61,18 @@ namespace PepperDash.Essentials.Devices.Common.DSP
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base class for DSP control points
|
/// Base class for DSP control points
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class DspControlPoint : IKeyed
|
public abstract class DspControlPoint : IKeyName
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the Key
|
/// Gets or sets the Key
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Key { get; }
|
public string Key { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the Name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the DspControlPoint class
|
/// Initializes a new instance of the DspControlPoint class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,15 +1,27 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Timers;
|
||||||
using Crestron.SimplSharp;
|
using Crestron.SimplSharp;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Core.Logging;
|
||||||
using PepperDash.Essentials.Core;
|
using PepperDash.Essentials.Core;
|
||||||
using PepperDash.Essentials.Core.Config;
|
using PepperDash.Essentials.Core.Config;
|
||||||
using PepperDash.Essentials.Core.CrestronIO;
|
using PepperDash.Essentials.Core.CrestronIO;
|
||||||
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
using PepperDash.Essentials.Core.DeviceTypeInterfaces;
|
||||||
using Serilog.Events;
|
using PepperDash.Essentials.Devices.Common.Displays;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Devices.Common.Shades
|
namespace PepperDash.Essentials.Devices.Common.Shades
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enumeration for requested state
|
||||||
|
/// </summary>
|
||||||
|
enum RequestedState
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Raise,
|
||||||
|
Lower
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Controls a single shade using three relays
|
/// Controls a single shade using three relays
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -20,11 +32,16 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
readonly ScreenLiftRelaysConfig LowerRelayConfig;
|
readonly ScreenLiftRelaysConfig LowerRelayConfig;
|
||||||
readonly ScreenLiftRelaysConfig LatchedRelayConfig;
|
readonly ScreenLiftRelaysConfig LatchedRelayConfig;
|
||||||
|
|
||||||
Displays.DisplayBase DisplayDevice;
|
DisplayBase DisplayDevice;
|
||||||
ISwitchedOutput RaiseRelay;
|
ISwitchedOutput RaiseRelay;
|
||||||
ISwitchedOutput LowerRelay;
|
ISwitchedOutput LowerRelay;
|
||||||
ISwitchedOutput LatchedRelay;
|
ISwitchedOutput LatchedRelay;
|
||||||
|
|
||||||
|
private bool _isMoving;
|
||||||
|
private RequestedState _requestedState;
|
||||||
|
private RequestedState _currentMovement;
|
||||||
|
private Timer _movementTimer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the InUpPosition
|
/// Gets or sets the InUpPosition
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -80,6 +97,11 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
|
|
||||||
IsInUpPosition = new BoolFeedback("isInUpPosition", () => _isInUpPosition);
|
IsInUpPosition = new BoolFeedback("isInUpPosition", () => _isInUpPosition);
|
||||||
|
|
||||||
|
// Initialize movement timer for reuse
|
||||||
|
_movementTimer = new Timer();
|
||||||
|
_movementTimer.Elapsed += OnMovementComplete;
|
||||||
|
_movementTimer.AutoReset = false;
|
||||||
|
|
||||||
switch (Mode)
|
switch (Mode)
|
||||||
{
|
{
|
||||||
case eScreenLiftControlMode.momentary:
|
case eScreenLiftControlMode.momentary:
|
||||||
@@ -129,25 +151,25 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
{
|
{
|
||||||
case eScreenLiftControlMode.momentary:
|
case eScreenLiftControlMode.momentary:
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting relays for {Mode}");
|
this.LogDebug("Getting relays for {mode}", Mode);
|
||||||
RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey);
|
RaiseRelay = GetSwitchedOutputFromDevice(RaiseRelayConfig.DeviceKey);
|
||||||
LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey);
|
LowerRelay = GetSwitchedOutputFromDevice(LowerRelayConfig.DeviceKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eScreenLiftControlMode.latched:
|
case eScreenLiftControlMode.latched:
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting relays for {Mode}");
|
this.LogDebug("Getting relays for {mode}", Mode);
|
||||||
LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey);
|
LatchedRelay = GetSwitchedOutputFromDevice(LatchedRelayConfig.DeviceKey);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Getting display with key {DisplayDeviceKey}");
|
this.LogDebug("Getting display with key {displayKey}", DisplayDeviceKey);
|
||||||
DisplayDevice = GetDisplayBaseFromDevice(DisplayDeviceKey);
|
DisplayDevice = GetDisplayBaseFromDevice(DisplayDeviceKey);
|
||||||
|
|
||||||
if (DisplayDevice != null)
|
if (DisplayDevice != null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Subscribing to {DisplayDeviceKey} feedbacks");
|
this.LogDebug("Subscribing to {displayKey} feedbacks", DisplayDeviceKey);
|
||||||
|
|
||||||
DisplayDevice.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;
|
DisplayDevice.IsWarmingUpFeedback.OutputChange += IsWarmingUpFeedback_OutputChange;
|
||||||
DisplayDevice.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;
|
DisplayDevice.IsCoolingDownFeedback.OutputChange += IsCoolingDownFeedback_OutputChange;
|
||||||
@@ -163,22 +185,49 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
{
|
{
|
||||||
if (RaiseRelay == null && LatchedRelay == null) return;
|
if (RaiseRelay == null && LatchedRelay == null) return;
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Raising {Type}");
|
this.LogDebug("Raise called for {type}", Type);
|
||||||
|
|
||||||
|
// If device is moving, bank the command
|
||||||
|
if (_isMoving)
|
||||||
|
{
|
||||||
|
this.LogDebug("Device is moving, banking Raise command");
|
||||||
|
_requestedState = RequestedState.Raise;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogDebug("Raising {type}", Type);
|
||||||
|
|
||||||
switch (Mode)
|
switch (Mode)
|
||||||
{
|
{
|
||||||
case eScreenLiftControlMode.momentary:
|
case eScreenLiftControlMode.momentary:
|
||||||
{
|
{
|
||||||
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
|
PulseOutput(RaiseRelay, RaiseRelayConfig.PulseTimeInMs);
|
||||||
|
|
||||||
|
// Set moving flag and start timer if movement time is configured
|
||||||
|
if (RaiseRelayConfig.MoveTimeInMs > 0)
|
||||||
|
{
|
||||||
|
_isMoving = true;
|
||||||
|
_currentMovement = RequestedState.Raise;
|
||||||
|
if (_movementTimer.Enabled)
|
||||||
|
{
|
||||||
|
_movementTimer.Stop();
|
||||||
|
}
|
||||||
|
_movementTimer.Interval = RaiseRelayConfig.MoveTimeInMs;
|
||||||
|
_movementTimer.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InUpPosition = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eScreenLiftControlMode.latched:
|
case eScreenLiftControlMode.latched:
|
||||||
{
|
{
|
||||||
LatchedRelay.Off();
|
LatchedRelay.Off();
|
||||||
|
InUpPosition = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InUpPosition = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -188,59 +237,145 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
{
|
{
|
||||||
if (LowerRelay == null && LatchedRelay == null) return;
|
if (LowerRelay == null && LatchedRelay == null) return;
|
||||||
|
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, $"Lowering {Type}");
|
this.LogDebug("Lower called for {type}", Type);
|
||||||
|
|
||||||
|
// If device is moving, bank the command
|
||||||
|
if (_isMoving)
|
||||||
|
{
|
||||||
|
this.LogDebug("Device is moving, banking Lower command");
|
||||||
|
_requestedState = RequestedState.Lower;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogDebug("Lowering {type}", Type);
|
||||||
|
|
||||||
switch (Mode)
|
switch (Mode)
|
||||||
{
|
{
|
||||||
case eScreenLiftControlMode.momentary:
|
case eScreenLiftControlMode.momentary:
|
||||||
{
|
{
|
||||||
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
|
PulseOutput(LowerRelay, LowerRelayConfig.PulseTimeInMs);
|
||||||
|
|
||||||
|
// Set moving flag and start timer if movement time is configured
|
||||||
|
if (LowerRelayConfig.MoveTimeInMs > 0)
|
||||||
|
{
|
||||||
|
_isMoving = true;
|
||||||
|
_currentMovement = RequestedState.Lower;
|
||||||
|
if (_movementTimer.Enabled)
|
||||||
|
{
|
||||||
|
_movementTimer.Stop();
|
||||||
|
}
|
||||||
|
_movementTimer.Interval = LowerRelayConfig.MoveTimeInMs;
|
||||||
|
_movementTimer.Start();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
InUpPosition = false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case eScreenLiftControlMode.latched:
|
case eScreenLiftControlMode.latched:
|
||||||
{
|
{
|
||||||
LatchedRelay.On();
|
LatchedRelay.On();
|
||||||
|
InUpPosition = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InUpPosition = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PulseOutput(ISwitchedOutput output, int pulseTime)
|
private void DisposeMovementTimer()
|
||||||
{
|
{
|
||||||
output.On();
|
if (_movementTimer != null)
|
||||||
CTimer pulseTimer = new CTimer(new CTimerCallbackFunction((o) => output.Off()), pulseTime);
|
{
|
||||||
|
_movementTimer.Stop();
|
||||||
|
_movementTimer.Elapsed -= OnMovementComplete;
|
||||||
|
_movementTimer.Dispose();
|
||||||
|
_movementTimer = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to get the port on teh specified device from config
|
/// Called when movement timer completes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="relayKey"></param>
|
private void OnMovementComplete(object sender, ElapsedEventArgs e)
|
||||||
/// <returns></returns>
|
|
||||||
ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey)
|
|
||||||
{
|
{
|
||||||
var portDevice = DeviceManager.GetDeviceForKey(relayKey);
|
this.LogDebug("Movement complete");
|
||||||
|
|
||||||
|
// Update position based on completed movement
|
||||||
|
if (_currentMovement == RequestedState.Raise)
|
||||||
|
{
|
||||||
|
InUpPosition = true;
|
||||||
|
}
|
||||||
|
else if (_currentMovement == RequestedState.Lower)
|
||||||
|
{
|
||||||
|
InUpPosition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isMoving = false;
|
||||||
|
_currentMovement = RequestedState.None;
|
||||||
|
|
||||||
|
// Execute banked command if one exists
|
||||||
|
if (_requestedState != RequestedState.None)
|
||||||
|
{
|
||||||
|
this.LogDebug("Executing next command: {command}", _requestedState);
|
||||||
|
|
||||||
|
var commandToExecute = _requestedState;
|
||||||
|
_requestedState = RequestedState.None;
|
||||||
|
|
||||||
|
// Check if current state matches what the banked command would do and execute if different
|
||||||
|
switch (commandToExecute)
|
||||||
|
{
|
||||||
|
case RequestedState.Raise:
|
||||||
|
Raise();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RequestedState.Lower:
|
||||||
|
Lower();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PulseOutput(ISwitchedOutput output, int pulseTime)
|
||||||
|
{
|
||||||
|
output.On();
|
||||||
|
|
||||||
|
var timer = new Timer(pulseTime)
|
||||||
|
{
|
||||||
|
AutoReset = false
|
||||||
|
};
|
||||||
|
|
||||||
|
timer.Elapsed += (sender, e) =>
|
||||||
|
{
|
||||||
|
output.Off();
|
||||||
|
timer.Dispose();
|
||||||
|
};
|
||||||
|
timer.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISwitchedOutput GetSwitchedOutputFromDevice(string relayKey)
|
||||||
|
{
|
||||||
|
var portDevice = DeviceManager.GetDeviceForKey<ISwitchedOutput>(relayKey);
|
||||||
if (portDevice != null)
|
if (portDevice != null)
|
||||||
{
|
{
|
||||||
return portDevice as ISwitchedOutput;
|
return portDevice;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "Error: Unable to get relay device with key '{0}'", relayKey);
|
this.LogWarning("Error: Unable to get relay device with key '{relayKey}'", relayKey);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Displays.DisplayBase GetDisplayBaseFromDevice(string displayKey)
|
private DisplayBase GetDisplayBaseFromDevice(string displayKey)
|
||||||
{
|
{
|
||||||
var displayDevice = DeviceManager.GetDeviceForKey(displayKey);
|
var displayDevice = DeviceManager.GetDeviceForKey<DisplayBase>(displayKey);
|
||||||
if (displayDevice != null)
|
if (displayDevice != null)
|
||||||
{
|
{
|
||||||
return displayDevice as Displays.DisplayBase;
|
return displayDevice;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, this, "Error: Unable to get display device with key '{0}'", displayKey);
|
this.LogWarning("Error: Unable to get display device with key '{displayKey}'", displayKey);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,7 +383,7 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a ScreenLiftControllerFactory
|
/// Factory for ScreenLiftController devices
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade>
|
public class ScreenLiftControllerFactory : EssentialsDeviceFactory<RelayControlledShade>
|
||||||
{
|
{
|
||||||
@@ -260,14 +395,11 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
TypeNames = new List<string>() { "screenliftcontroller" };
|
TypeNames = new List<string>() { "screenliftcontroller" };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BuildDevice method
|
|
||||||
/// </summary>
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Generic Comm Device");
|
Debug.LogDebug("Factory Attempting to create new ScreenLiftController Device");
|
||||||
var props = Newtonsoft.Json.JsonConvert.DeserializeObject<ScreenLiftControllerConfigProperties>(dc.Properties.ToString());
|
var props = dc.Properties.ToObject<ScreenLiftControllerConfigProperties>();
|
||||||
|
|
||||||
return new ScreenLiftController(dc.Key, dc.Name, props);
|
return new ScreenLiftController(dc.Key, dc.Name, props);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,5 +18,11 @@ namespace PepperDash.Essentials.Devices.Common.Shades
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("pulseTimeInMs")]
|
[JsonProperty("pulseTimeInMs")]
|
||||||
public int PulseTimeInMs { get; set; }
|
public int PulseTimeInMs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the MoveTimeInMs - time in milliseconds for the movement to complete
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("moveTimeInMs")]
|
||||||
|
public int MoveTimeInMs { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using System.Collections.Generic;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
|
using PepperDash.Core.Logging;
|
||||||
using PepperDash.Essentials.Core;
|
using PepperDash.Essentials.Core;
|
||||||
using PepperDash.Essentials.Core.Config;
|
using PepperDash.Essentials.Core.Config;
|
||||||
|
using PepperDash.Essentials.Core.Routing;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace PepperDash.Essentials.Devices.Common.Generic
|
namespace PepperDash.Essentials.Devices.Common.Generic
|
||||||
@@ -9,8 +12,17 @@ namespace PepperDash.Essentials.Devices.Common.Generic
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a GenericSink
|
/// Represents a GenericSink
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort
|
public class GenericSink : EssentialsDevice, IRoutingSinkWithSwitchingWithInputPort, ICurrentSources
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Dictionary<eRoutingSignalType, SourceListItem> CurrentSources { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Dictionary<eRoutingSignalType, string> CurrentSourceKeys { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public event EventHandler CurrentSourcesChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the GenericSink class
|
/// Initializes a new instance of the GenericSink class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -23,6 +35,49 @@ namespace PepperDash.Essentials.Devices.Common.Generic
|
|||||||
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this);
|
var inputPort = new RoutingInputPort(RoutingPortNames.AnyVideoIn, eRoutingSignalType.AudioVideo | eRoutingSignalType.SecondaryAudio, eRoutingPortConnectionType.Hdmi, null, this);
|
||||||
|
|
||||||
InputPorts.Add(inputPort);
|
InputPorts.Add(inputPort);
|
||||||
|
|
||||||
|
CurrentSources = new Dictionary<eRoutingSignalType, SourceListItem>
|
||||||
|
{
|
||||||
|
{ eRoutingSignalType.Audio, null },
|
||||||
|
{ eRoutingSignalType.Video, null },
|
||||||
|
};
|
||||||
|
|
||||||
|
CurrentSourceKeys = new Dictionary<eRoutingSignalType, string>
|
||||||
|
{
|
||||||
|
{ eRoutingSignalType.Audio, string.Empty },
|
||||||
|
{ eRoutingSignalType.Video, string.Empty },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void SetCurrentSource(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
|
||||||
|
{
|
||||||
|
foreach (eRoutingSignalType type in Enum.GetValues(typeof(eRoutingSignalType)))
|
||||||
|
{
|
||||||
|
var flagValue = Convert.ToInt32(type);
|
||||||
|
// Skip if flagValue is 0 or not a power of two (i.e., not a single-bit flag).
|
||||||
|
// (flagValue & (flagValue - 1)) != 0 checks if more than one bit is set.
|
||||||
|
if (flagValue == 0 || (flagValue & (flagValue - 1)) != 0)
|
||||||
|
{
|
||||||
|
this.LogDebug("Skipping {type}", type);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogDebug("setting {type}", type);
|
||||||
|
|
||||||
|
if (signalType.HasFlag(type))
|
||||||
|
{
|
||||||
|
UpdateCurrentSources(type, sourceListKey, sourceListItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Raise the CurrentSourcesChanged event
|
||||||
|
CurrentSourcesChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCurrentSources(eRoutingSignalType signalType, string sourceListKey, SourceListItem sourceListItem)
|
||||||
|
{
|
||||||
|
CurrentSources[signalType] = sourceListItem;
|
||||||
|
CurrentSourceKeys[signalType] = sourceListKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -73,7 +128,7 @@ namespace PepperDash.Essentials.Devices.Common.Generic
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void ExecuteSwitch(object inputSelector)
|
public void ExecuteSwitch(object inputSelector)
|
||||||
{
|
{
|
||||||
throw new System.NotImplementedException();
|
this.LogDebug("GenericSink Executing Switch to: {inputSelector}", inputSelector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
@@ -39,10 +40,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
|
|
||||||
sourceDevice.CurrentSourcesChanged += (sender, e) =>
|
sourceDevice.CurrentSourcesChanged += (sender, e) =>
|
||||||
{
|
{
|
||||||
|
// need to copy the dictionaries to avoid enumeration issues
|
||||||
|
var currentSourceKeys = sourceDevice.CurrentSourceKeys.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
var currentSources = sourceDevice.CurrentSources.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
|
||||||
|
|
||||||
PostStatusMessage(JToken.FromObject(new
|
PostStatusMessage(JToken.FromObject(new
|
||||||
{
|
{
|
||||||
currentSourceKeys = sourceDevice.CurrentSourceKeys,
|
currentSourceKeys,
|
||||||
currentSources = sourceDevice.CurrentSources
|
currentSources,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
/// <param name="messagePath">The message path.</param>
|
/// <param name="messagePath">The message path.</param>
|
||||||
/// <param name="device">The device.</param>
|
/// <param name="device">The device.</param>
|
||||||
public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeControls device)
|
public DeviceVolumeMessenger(string key, string messagePath, IBasicVolumeControls device)
|
||||||
: base(key, messagePath, device as IKeyName)
|
: base(key, messagePath, device)
|
||||||
{
|
{
|
||||||
this.device = device;
|
this.device = device;
|
||||||
}
|
}
|
||||||
@@ -130,34 +130,33 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
|
|
||||||
feedback.MuteFeedback.OutputChange += (sender, args) =>
|
feedback.MuteFeedback.OutputChange += (sender, args) =>
|
||||||
{
|
{
|
||||||
PostStatusMessage(JToken.FromObject(
|
var message = new VolumeStateMessage
|
||||||
new
|
{
|
||||||
{
|
Volume = new Volume
|
||||||
volume = new
|
{
|
||||||
{
|
Muted = args.BoolValue
|
||||||
muted = args.BoolValue
|
}
|
||||||
}
|
};
|
||||||
})
|
|
||||||
);
|
PostStatusMessage(JToken.FromObject(message));
|
||||||
};
|
};
|
||||||
|
|
||||||
feedback.VolumeLevelFeedback.OutputChange += (sender, args) =>
|
feedback.VolumeLevelFeedback.OutputChange += (sender, args) =>
|
||||||
{
|
{
|
||||||
var rawValue = "";
|
var message = new VolumeStateMessage
|
||||||
if (feedback is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
|
|
||||||
{
|
{
|
||||||
rawValue = volumeAdvanced.RawVolumeLevel.ToString();
|
Volume = new Volume
|
||||||
}
|
|
||||||
|
|
||||||
var message = new
|
|
||||||
{
|
|
||||||
volume = new
|
|
||||||
{
|
{
|
||||||
level = args.IntValue,
|
Level = args.IntValue,
|
||||||
rawValue
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (device is IBasicVolumeWithFeedbackAdvanced volumeAdvanced)
|
||||||
|
{
|
||||||
|
message.Volume.RawValue = volumeAdvanced.RawVolumeLevel.ToString();
|
||||||
|
message.Volume.Units = volumeAdvanced.Units;
|
||||||
|
}
|
||||||
|
|
||||||
PostStatusMessage(JToken.FromObject(message));
|
PostStatusMessage(JToken.FromObject(message));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,14 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))
|
inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inputSlot.IsOnline.OutputChange += (sender, args) =>
|
||||||
|
{
|
||||||
|
PostStatusMessage(JToken.FromObject(new
|
||||||
|
{
|
||||||
|
inputs = matrixDevice.InputSlots.ToDictionary(kvp => kvp.Key, kvp => new RoutingInput(kvp.Value))
|
||||||
|
}));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,7 +194,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
{
|
{
|
||||||
if (!enableMessengerSubscriptions)
|
if (!enableMessengerSubscriptions)
|
||||||
{
|
{
|
||||||
this.LogWarning("Messenger subscriptions not enabled");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +217,6 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
{
|
{
|
||||||
if (!enableMessengerSubscriptions)
|
if (!enableMessengerSubscriptions)
|
||||||
{
|
{
|
||||||
this.LogWarning("Messenger subscriptions not enabled");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,7 +236,7 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.LogInformation("Client with ID {clientId} unsubscribed", clientId);
|
this.LogDebug("Client with ID {clientId} unsubscribed", clientId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -272,7 +270,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.LogError(ex, "Exception posting status message for {messagePath} to {clientId}", MessagePath, clientId ?? "all clients");
|
this.LogError("Exception posting status message for {messagePath} to {clientId}: {message}", MessagePath, clientId ?? "all clients", ex.Message);
|
||||||
|
this.LogDebug(ex, "Stack trace: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +300,8 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.LogError(ex, "Exception posting status message for {type} to {clientId}", type, clientId ?? "all clients");
|
this.LogError("Exception posting status message for {type} to {clientId}: {message}", type, clientId ?? "all clients", ex.Message);
|
||||||
|
this.LogDebug(ex, "Stack trace: ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1312,6 +1312,11 @@ namespace PepperDash.Essentials
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
if (!Config.EnableMessengerSubscriptions)
|
||||||
|
{
|
||||||
|
this.LogWarning("Messenger subscriptions disabled. add \"enableMessengerSubscriptions\": true to config for {key} to enable.", Key);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var messenger in _messengers)
|
foreach (var messenger in _messengers)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -1743,7 +1748,7 @@ namespace PepperDash.Essentials
|
|||||||
var clientNo = 1;
|
var clientNo = 1;
|
||||||
foreach (var clientContext in _directServer.UiClientContexts)
|
foreach (var clientContext in _directServer.UiClientContexts)
|
||||||
{
|
{
|
||||||
var clients = _directServer.UiClients.Values.Where(c => c.Token == clientContext.Value.Token.Token);
|
var clients = _directServer.UiClients.Values.Where(c => c.TokenKey == clientContext.Key);
|
||||||
|
|
||||||
CrestronConsole.ConsoleCommandResponse(
|
CrestronConsole.ConsoleCommandResponse(
|
||||||
$"\r\nClient {clientNo}:\r\n" +
|
$"\r\nClient {clientNo}:\r\n" +
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading;
|
||||||
using PepperDash.Core;
|
using PepperDash.Core;
|
||||||
using PepperDash.Core.Logging;
|
using PepperDash.Core.Logging;
|
||||||
using WebSocketSharp;
|
using WebSocketSharp;
|
||||||
@@ -12,13 +13,12 @@ namespace PepperDash.Essentials
|
|||||||
private static int nextClientId = 0;
|
private static int nextClientId = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the next unique client ID
|
/// Get the next unique client ID (thread-safe)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Client ID</returns>
|
/// <returns>Client ID</returns>
|
||||||
public static int GetNextClientId()
|
public static int GetNextClientId()
|
||||||
{
|
{
|
||||||
nextClientId++;
|
return Interlocked.Increment(ref nextClientId);
|
||||||
return nextClientId;
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a WebSocketServer LogData object to Essentials logging calls.
|
/// Converts a WebSocketServer LogData object to Essentials logging calls.
|
||||||
|
|||||||
@@ -64,6 +64,12 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
[JsonProperty("userAppUrl")]
|
[JsonProperty("userAppUrl")]
|
||||||
public string UserAppUrl { get; set; }
|
public string UserAppUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the WebSocketUrl with clientId query parameter
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("webSocketUrl")]
|
||||||
|
public string WebSocketUrl { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the EnableDebug
|
/// Gets or sets the EnableDebug
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@@ -59,12 +60,25 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<string, UiClientContext> UiClientContexts { get; private set; }
|
public Dictionary<string, UiClientContext> UiClientContexts { get; private set; }
|
||||||
|
|
||||||
private readonly Dictionary<string, UiClient> uiClients = new Dictionary<string, UiClient>();
|
private readonly ConcurrentDictionary<string, UiClient> uiClients = new ConcurrentDictionary<string, UiClient>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores pending client registrations using composite key: token-clientId
|
||||||
|
/// This ensures the correct client ID is matched even when connections establish out of order
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<string, string> pendingClientRegistrations = new ConcurrentDictionary<string, string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores pending client registrations with timestamp for legacy clients
|
||||||
|
/// Key is token, Value is list of (clientId, timestamp) tuples
|
||||||
|
/// Most recent registration is used to handle duplicate join requests
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<string, ConcurrentBag<(string clientId, DateTime timestamp)>> legacyClientRegistrations = new ConcurrentDictionary<string, ConcurrentBag<(string, DateTime)>>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the collection of UI clients
|
/// Gets the collection of UI clients
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReadOnlyDictionary<string, UiClient> UiClients => new ReadOnlyDictionary<string, UiClient>(uiClients);
|
public IReadOnlyDictionary<string, UiClient> UiClients => uiClients;
|
||||||
|
|
||||||
private readonly MobileControlSystemController _parent;
|
private readonly MobileControlSystemController _parent;
|
||||||
|
|
||||||
@@ -723,23 +737,155 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
|
|
||||||
private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
|
private UiClient BuildUiClient(string roomKey, JoinToken token, string key)
|
||||||
{
|
{
|
||||||
var c = new UiClient($"uiclient-{key}-{roomKey}-{token.Id}", token.Id, token.Token, token.TouchpanelKey);
|
// Get the most recent unused clientId for this token (legacy support)
|
||||||
this.LogInformation("Constructing UiClient with key {key} and ID {id}", key, token.Id);
|
// New clients will override this ID in OnOpen with the validated query parameter value
|
||||||
|
var clientId = "pending";
|
||||||
|
if (legacyClientRegistrations.TryGetValue(key, out var registrations))
|
||||||
|
{
|
||||||
|
// Get most recent registration
|
||||||
|
var sorted = registrations.OrderByDescending(r => r.timestamp).ToList();
|
||||||
|
if (sorted.Any())
|
||||||
|
{
|
||||||
|
clientId = sorted.First().clientId;
|
||||||
|
// Remove it from the bag
|
||||||
|
var newBag = new ConcurrentBag<(string, DateTime)>(sorted.Skip(1));
|
||||||
|
legacyClientRegistrations.TryUpdate(key, newBag, registrations);
|
||||||
|
this.LogVerbose("Assigned most recent legacy clientId {clientId} for token {token}", clientId, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var c = new UiClient($"uiclient-{key}-{roomKey}-{clientId}", clientId, token.Token, token.TouchpanelKey);
|
||||||
|
this.LogInformation("Constructing UiClient with key {key} and temporary ID (will be set from query param)", key);
|
||||||
c.Controller = _parent;
|
c.Controller = _parent;
|
||||||
c.RoomKey = roomKey;
|
c.RoomKey = roomKey;
|
||||||
|
c.TokenKey = key; // Store the URL token key for filtering
|
||||||
|
c.Server = this; // Give UiClient access to server for ID registration
|
||||||
|
|
||||||
if (uiClients.ContainsKey(token.Id))
|
// Don't add to uiClients yet - will be added in OnOpen after ID is set from query param
|
||||||
|
|
||||||
|
c.ConnectionClosed += (o, a) =>
|
||||||
{
|
{
|
||||||
this.LogWarning("removing client with duplicate id {id}", token.Id);
|
uiClients.TryRemove(a.ClientId, out _);
|
||||||
uiClients.Remove(token.Id);
|
// Clean up any pending registrations for this token
|
||||||
}
|
var keysToRemove = pendingClientRegistrations.Keys
|
||||||
uiClients.Add(token.Id, c);
|
.Where(k => k.StartsWith($"{key}-"))
|
||||||
// UiClients[key].SetClient(c);
|
.ToList();
|
||||||
c.ConnectionClosed += (o, a) => uiClients.Remove(a.ClientId);
|
foreach (var k in keysToRemove)
|
||||||
token.Id = null;
|
{
|
||||||
|
pendingClientRegistrations.TryRemove(k, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean up legacy registrations if empty
|
||||||
|
if (legacyClientRegistrations.TryGetValue(key, out var legacyBag) && legacyBag.IsEmpty)
|
||||||
|
{
|
||||||
|
legacyClientRegistrations.TryRemove(key, out _);
|
||||||
|
}
|
||||||
|
};
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a UiClient with its validated client ID after WebSocket connection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">The UiClient to register</param>
|
||||||
|
/// <param name="clientId">The validated client ID</param>
|
||||||
|
/// <param name="tokenKey">The token key for validation</param>
|
||||||
|
/// <returns>True if registration successful, false if validation failed</returns>
|
||||||
|
public bool RegisterUiClient(UiClient client, string clientId, string tokenKey)
|
||||||
|
{
|
||||||
|
var registrationKey = $"{tokenKey}-{clientId}";
|
||||||
|
|
||||||
|
// Verify this clientId was generated during a join request for this token
|
||||||
|
if (!pendingClientRegistrations.TryRemove(registrationKey, out _))
|
||||||
|
{
|
||||||
|
this.LogWarning("Client attempted to connect with unregistered or expired clientId {clientId} for token {token}", clientId, tokenKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registration is valid - add to active clients
|
||||||
|
uiClients.AddOrUpdate(clientId, client, (id, existingClient) =>
|
||||||
|
{
|
||||||
|
this.LogWarning("Replacing existing client with duplicate id {id}", id);
|
||||||
|
return client;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.LogInformation("Successfully registered UiClient with ID {clientId} for token {token}", clientId, tokenKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a client's ID when a mismatch is detected between stored ID and message ID
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="oldClientId">The current/old client ID</param>
|
||||||
|
/// <param name="newClientId">The new client ID from the message</param>
|
||||||
|
/// <param name="tokenKey">The token key for validation</param>
|
||||||
|
/// <returns>True if update successful, false otherwise</returns>
|
||||||
|
public bool UpdateClientId(string oldClientId, string newClientId, string tokenKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(oldClientId) || string.IsNullOrEmpty(newClientId))
|
||||||
|
{
|
||||||
|
this.LogWarning("Cannot update client ID with null or empty values");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldClientId == newClientId)
|
||||||
|
{
|
||||||
|
return true; // No update needed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the new clientId was registered for this token
|
||||||
|
var registrationKey = $"{tokenKey}-{newClientId}";
|
||||||
|
if (!pendingClientRegistrations.TryRemove(registrationKey, out _))
|
||||||
|
{
|
||||||
|
this.LogWarning("Cannot update to unregistered clientId {newClientId} for token {token}", newClientId, tokenKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the existing client
|
||||||
|
if (!uiClients.TryRemove(oldClientId, out var client))
|
||||||
|
{
|
||||||
|
this.LogWarning("Cannot find client with old ID {oldClientId}", oldClientId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the client's ID
|
||||||
|
client.UpdateId(newClientId);
|
||||||
|
|
||||||
|
// Re-add with new ID
|
||||||
|
if (!uiClients.TryAdd(newClientId, client))
|
||||||
|
{
|
||||||
|
// If add fails, try to restore old entry
|
||||||
|
uiClients.TryAdd(oldClientId, client);
|
||||||
|
client.UpdateId(oldClientId);
|
||||||
|
this.LogError("Failed to update client ID from {oldClientId} to {newClientId}", oldClientId, newClientId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogInformation("Successfully updated client ID from {oldClientId} to {newClientId}", oldClientId, newClientId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a UiClient using legacy flow (for backwards compatibility with older clients)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="client">The UiClient to register</param>
|
||||||
|
public void RegisterLegacyUiClient(UiClient client)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(client.Id))
|
||||||
|
{
|
||||||
|
this.LogError("Cannot register client with null or empty ID");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiClients.AddOrUpdate(client.Id, client, (id, existingClient) =>
|
||||||
|
{
|
||||||
|
this.LogWarning("Replacing existing client with duplicate id {id} (legacy flow)", id);
|
||||||
|
return client;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.LogInformation("Successfully registered UiClient with ID {clientId} using legacy flow", client.Id);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prints out the session data for each path
|
/// Prints out the session data for each path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1046,10 +1192,23 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Generate a client ID for this join request
|
||||||
var clientId = $"{Utilities.GetNextClientId()}";
|
var clientId = $"{Utilities.GetNextClientId()}";
|
||||||
clientContext.Token.Id = clientId;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
this.LogVerbose("Assigning ClientId: {clientId}", clientId);
|
// Store in pending registrations for new clients that send clientId via query param
|
||||||
|
var registrationKey = $"{token}-{clientId}";
|
||||||
|
pendingClientRegistrations.TryAdd(registrationKey, clientId);
|
||||||
|
|
||||||
|
// For legacy clients, store with timestamp instead of FIFO queue
|
||||||
|
var legacyBag = legacyClientRegistrations.GetOrAdd(token, _ => new ConcurrentBag<(string, DateTime)>());
|
||||||
|
legacyBag.Add((clientId, now));
|
||||||
|
|
||||||
|
this.LogVerbose("Assigning ClientId: {clientId} for token: {token} at {timestamp}", clientId, token, now);
|
||||||
|
|
||||||
|
// Construct WebSocket URL with clientId query parameter
|
||||||
|
var wsProtocol = "ws";
|
||||||
|
var wsUrl = $"{wsProtocol}://{CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0)}:{Port}{_wsPath}{token}?clientId={clientId}";
|
||||||
|
|
||||||
// Construct the response object
|
// Construct the response object
|
||||||
JoinResponse jRes = new JoinResponse
|
JoinResponse jRes = new JoinResponse
|
||||||
@@ -1064,6 +1223,7 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
UserAppUrl = string.Format("http://{0}:{1}/mc/app",
|
UserAppUrl = string.Format("http://{0}:{1}/mc/app",
|
||||||
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
|
CrestronEthernetHelper.GetEthernetParameter(CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0),
|
||||||
Port),
|
Port),
|
||||||
|
WebSocketUrl = wsUrl,
|
||||||
EnableDebug = false,
|
EnableDebug = false,
|
||||||
DeviceInterfaceSupport = deviceInterfaces
|
DeviceInterfaceSupport = deviceInterfaces
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -26,11 +26,25 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; private set; }
|
public string Id { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the client ID - only accessible from within the assembly (e.g., by the server)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newId">The new client ID</param>
|
||||||
|
internal void UpdateId(string newId)
|
||||||
|
{
|
||||||
|
Id = newId;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Token associated with this client
|
/// Token associated with this client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Token { get; private set; }
|
public string Token { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The URL token key used to connect (from UiClientContexts dictionary key)
|
||||||
|
/// </summary>
|
||||||
|
public string TokenKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Touchpanel Key associated with this client
|
/// Touchpanel Key associated with this client
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -41,6 +55,11 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public MobileControlSystemController Controller { get; set; }
|
public MobileControlSystemController Controller { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the server instance for client registration
|
||||||
|
/// </summary>
|
||||||
|
public MobileControlWebsocketServer Server { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the room key that this client is associated with
|
/// Gets or sets the room key that this client is associated with
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -99,6 +118,50 @@ namespace PepperDash.Essentials.WebSocketServer
|
|||||||
Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
|
Log.Output = (data, message) => Utilities.ConvertWebsocketLog(data, message, this);
|
||||||
Log.Level = LogLevel.Trace;
|
Log.Level = LogLevel.Trace;
|
||||||
|
|
||||||
|
// Get clientId from query parameter
|
||||||
|
var queryString = Context.QueryString;
|
||||||
|
var clientId = queryString["clientId"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(clientId))
|
||||||
|
{
|
||||||
|
// New behavior: Validate and register with the server using provided clientId
|
||||||
|
if (Server == null || !Server.RegisterUiClient(this, clientId, TokenKey))
|
||||||
|
{
|
||||||
|
this.LogError("Failed to register client with ID {clientId}. Invalid or expired registration.", clientId);
|
||||||
|
Context.WebSocket.Close(CloseStatusCode.PolicyViolation, "Invalid or expired clientId");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update this client's ID to the validated one
|
||||||
|
Id = clientId;
|
||||||
|
Key = $"uiclient-{TokenKey}-{RoomKey}-{clientId}";
|
||||||
|
|
||||||
|
this.LogInformation("Client {clientId} successfully connected and registered (new flow)", clientId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Legacy behavior: Use clientId from Token.Id (generated in HandleJoinRequest)
|
||||||
|
this.LogInformation("Client connected without clientId query parameter. Using legacy registration flow.");
|
||||||
|
|
||||||
|
// Id is already set from Token in constructor, use it
|
||||||
|
if (string.IsNullOrEmpty(Id))
|
||||||
|
{
|
||||||
|
this.LogError("Legacy client has no ID from token. Connection will be closed.");
|
||||||
|
Context.WebSocket.Close(CloseStatusCode.PolicyViolation, "No client ID available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Key = $"uiclient-{TokenKey}-{RoomKey}-{Id}";
|
||||||
|
|
||||||
|
// Register directly to active clients (legacy flow)
|
||||||
|
if (Server != null)
|
||||||
|
{
|
||||||
|
Server.RegisterLegacyUiClient(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.LogInformation("Client {clientId} registered using legacy flow", Id);
|
||||||
|
}
|
||||||
|
|
||||||
if (Controller == null)
|
if (Controller == null)
|
||||||
{
|
{
|
||||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Controller is null");
|
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Controller is null");
|
||||||
|
|||||||
@@ -662,6 +662,7 @@ namespace PepperDash.Essentials
|
|||||||
|
|
||||||
if (jsonFiles.Length > 1)
|
if (jsonFiles.Length > 1)
|
||||||
{
|
{
|
||||||
|
Debug.LogError("Multiple configuration files found in application directory: {@jsonFiles}", jsonFiles.Select(f => f.FullName).ToArray());
|
||||||
throw new Exception("Multiple configuration files found. Cannot continue.");
|
throw new Exception("Multiple configuration files found. Cannot continue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user