mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-12 03:54:51 +00:00
Compare commits
27 Commits
logging-im
...
v2.7.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d55615837 | ||
|
|
19e799f11d | ||
|
|
a3c1c444b7 | ||
|
|
c9b3205736 | ||
|
|
253b2cddaf | ||
|
|
d96edfa8d0 | ||
|
|
95c1c01396 | ||
|
|
9c94806e4f | ||
|
|
183879f1c4 | ||
|
|
f3159738ce | ||
|
|
2c5cae9f41 | ||
|
|
7178d8e284 | ||
|
|
af98a92f8c | ||
|
|
0a6896910d | ||
|
|
9b1dd099f6 | ||
|
|
3f5269de2f | ||
|
|
60f1adcd35 | ||
|
|
12c8660015 | ||
|
|
e7c3fcbbd9 | ||
|
|
0c7ec82529 | ||
|
|
feb99ecbb6 | ||
|
|
d78b9ea313 | ||
|
|
15172a5509 | ||
|
|
a4de9f2241 | ||
|
|
13cd84b73d | ||
|
|
81a01b7960 | ||
|
|
d9dc70bea2 |
@@ -11,35 +11,35 @@ namespace PepperDash.Core
|
||||
public class Device : IKeyName
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Unique Key
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Unique Key
|
||||
/// </summary>
|
||||
public string Key { get; protected set; }
|
||||
/// <summary>
|
||||
/// Name of the devie
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public string Name { get; protected set; }
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public bool Enabled { get; protected set; }
|
||||
|
||||
///// <summary>
|
||||
///// 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.
|
||||
///// </summary>
|
||||
//public DeviceConfig Config { get; private set; }
|
||||
///// <summary>
|
||||
///// Helper method to check if Config exists
|
||||
///// </summary>
|
||||
//public bool HasConfig { get { return Config != null; } }
|
||||
///// <summary>
|
||||
///// 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.
|
||||
///// </summary>
|
||||
//public DeviceConfig Config { get; private set; }
|
||||
///// <summary>
|
||||
///// Helper method to check if Config exists
|
||||
///// </summary>
|
||||
//public bool HasConfig { get { return Config != null; } }
|
||||
|
||||
List<Action> _PreActivationActions;
|
||||
List<Action> _PostActivationActions;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static Device DefaultDevice { get { return _DefaultDevice; } }
|
||||
static Device _DefaultDevice = new Device("Default", "Default");
|
||||
|
||||
@@ -54,27 +54,27 @@ namespace PepperDash.Core
|
||||
Name = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with key and name
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <summary>
|
||||
/// Constructor with key and name
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="name"></param>
|
||||
public Device(string key, string name) : this(key)
|
||||
{
|
||||
Name = name;
|
||||
|
||||
}
|
||||
|
||||
//public Device(DeviceConfig config)
|
||||
// : this(config.Key, config.Name)
|
||||
//{
|
||||
// Config = config;
|
||||
//}
|
||||
//public Device(DeviceConfig config)
|
||||
// : this(config.Key, config.Name)
|
||||
//{
|
||||
// Config = config;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pre activation action
|
||||
/// </summary>
|
||||
/// <param name="act"></param>
|
||||
/// <summary>
|
||||
/// Adds a pre activation action
|
||||
/// </summary>
|
||||
/// <param name="act"></param>
|
||||
public void AddPreActivationAction(Action act)
|
||||
{
|
||||
if (_PreActivationActions == null)
|
||||
@@ -82,10 +82,10 @@ namespace PepperDash.Core
|
||||
_PreActivationActions.Add(act);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a post activation action
|
||||
/// </summary>
|
||||
/// <param name="act"></param>
|
||||
/// <summary>
|
||||
/// Adds a post activation action
|
||||
/// </summary>
|
||||
/// <param name="act"></param>
|
||||
public void AddPostActivationAction(Action act)
|
||||
{
|
||||
if (_PostActivationActions == null)
|
||||
@@ -93,55 +93,58 @@ namespace PepperDash.Core
|
||||
_PostActivationActions.Add(act);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the preactivation actions
|
||||
/// </summary>
|
||||
public void PreActivate()
|
||||
{
|
||||
if (_PreActivationActions != null)
|
||||
_PreActivationActions.ForEach(a => {
|
||||
/// <summary>
|
||||
/// Executes the preactivation actions
|
||||
/// </summary>
|
||||
public void PreActivate()
|
||||
{
|
||||
if (_PreActivationActions != null)
|
||||
_PreActivationActions.ForEach(a =>
|
||||
{
|
||||
try
|
||||
{
|
||||
a.Invoke();
|
||||
} catch (Exception e)
|
||||
{
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(e, "Error in PreActivationAction: " + e.Message, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets this device ready to be used in the system. Runs any added pre-activation items, and
|
||||
/// all post-activation at end. Classes needing additional logic to
|
||||
/// run should override CustomActivate()
|
||||
/// </summary>
|
||||
public bool Activate()
|
||||
public bool Activate()
|
||||
{
|
||||
//if (_PreActivationActions != null)
|
||||
// _PreActivationActions.ForEach(a => a.Invoke());
|
||||
//if (_PreActivationActions != null)
|
||||
// _PreActivationActions.ForEach(a => a.Invoke());
|
||||
var result = CustomActivate();
|
||||
//if(result && _PostActivationActions != null)
|
||||
// _PostActivationActions.ForEach(a => a.Invoke());
|
||||
return result;
|
||||
//if(result && _PostActivationActions != null)
|
||||
// _PostActivationActions.ForEach(a => a.Invoke());
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the postactivation actions
|
||||
/// </summary>
|
||||
public void PostActivate()
|
||||
{
|
||||
if (_PostActivationActions != null)
|
||||
_PostActivationActions.ForEach(a => {
|
||||
try
|
||||
{
|
||||
a.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
/// <summary>
|
||||
/// Executes the postactivation actions
|
||||
/// </summary>
|
||||
public void PostActivate()
|
||||
{
|
||||
if (_PostActivationActions != null)
|
||||
_PostActivationActions.ForEach(a =>
|
||||
{
|
||||
try
|
||||
{
|
||||
a.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogMessage(e, "Error in PostActivationAction: " + e.Message, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called in between Pre and PostActivationActions when Activate() is called.
|
||||
@@ -158,14 +161,14 @@ namespace PepperDash.Core
|
||||
/// <returns></returns>
|
||||
public virtual bool Deactivate() { return true; }
|
||||
|
||||
/// <summary>
|
||||
/// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
|
||||
/// </summary>
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Call this method to start communications with a device. Overriding classes do not need to call base.Initialize()
|
||||
/// </summary>
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Helper method to check object for bool value false and fire an Action method
|
||||
/// </summary>
|
||||
/// <param name="o">Should be of type bool, others will be ignored</param>
|
||||
@@ -175,5 +178,15 @@ namespace PepperDash.Core
|
||||
if (o is bool && !(bool)o) a();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the object, including its key and name.
|
||||
/// </summary>
|
||||
/// <remarks>The returned string is formatted as "{Key} - {Name}". If the <c>Name</c> property is
|
||||
/// null or empty, "---" is used in place of the name.</remarks>
|
||||
/// <returns>A string that represents the object, containing the key and name in the format "{Key} - {Name}".</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} - {1}", Key, string.IsNullOrEmpty(Name) ? "---" : Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// This defines a device that has screens with layouts
|
||||
/// Simply decorative
|
||||
/// </summary>
|
||||
public interface IHasScreensWithLayouts
|
||||
{
|
||||
/// <summary>
|
||||
/// A dictionary of screens, keyed by screen ID, that contains information about each screen and its layouts.
|
||||
/// </summary>
|
||||
Dictionary<uint, ScreenInfo> Screens { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies a specific layout to a screen based on the provided screen ID and layout index.
|
||||
/// </summary>
|
||||
/// <param name="screenId"></param>
|
||||
/// <param name="layoutIndex"></param>
|
||||
void ApplyLayout(uint screenId, uint layoutIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about a screen and its layouts.
|
||||
/// </summary>
|
||||
public class ScreenInfo
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the screen is enabled or not.
|
||||
/// </summary>
|
||||
[JsonProperty("enabled")]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of the screen.
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of the screen.
|
||||
/// </summary>
|
||||
[JsonProperty("screenIndex")]
|
||||
public int ScreenIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary of layout information for the screen, keyed by layout ID.
|
||||
/// </summary>
|
||||
[JsonProperty("layouts")]
|
||||
public Dictionary<uint, LayoutInfo> Layouts { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents information about a layout on a screen.
|
||||
/// </summary>
|
||||
public class LayoutInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the layout.
|
||||
/// </summary>
|
||||
[JsonProperty("layoutName")]
|
||||
public string LayoutName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The index of the layout.
|
||||
/// </summary>
|
||||
[JsonProperty("layoutIndex")]
|
||||
public int LayoutIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the layout, which can be "single", "double", "triple", or "quad".
|
||||
/// </summary>
|
||||
[JsonProperty("layoutType")]
|
||||
public string LayoutType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary of window configurations for the layout, keyed by window ID.
|
||||
/// </summary>
|
||||
[JsonProperty("windows")]
|
||||
public Dictionary<uint, WindowConfig> Windows { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the configuration of a window within a layout on a screen.
|
||||
/// </summary>
|
||||
public class WindowConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The display label for the window
|
||||
/// </summary>
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The input for the window
|
||||
/// </summary>
|
||||
[JsonProperty("input")]
|
||||
public string Input { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -248,7 +248,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
foreach (var dev in Devices.Values.OfType<ICommunicationMonitor>())
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}{Environment.NewLine}");
|
||||
CrestronConsole.ConsoleCommandResponse($"{dev}: {dev.CommunicationMonitor.Status}\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,19 +20,20 @@ namespace PepperDash.Essentials.Core
|
||||
public event EventHandler Initialized;
|
||||
|
||||
private bool _isInitialized;
|
||||
public bool IsInitialized {
|
||||
public bool IsInitialized
|
||||
{
|
||||
get { return _isInitialized; }
|
||||
private set
|
||||
{
|
||||
private set
|
||||
{
|
||||
if (_isInitialized == value) return;
|
||||
|
||||
|
||||
_isInitialized = value;
|
||||
|
||||
if (_isInitialized)
|
||||
{
|
||||
Initialized?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected EssentialsDevice(string key)
|
||||
@@ -80,8 +81,9 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// Override this method to build and create custom Mobile Control Messengers during the Activation phase
|
||||
/// </summary>
|
||||
protected virtual void CreateMobileControlMessengers() {
|
||||
|
||||
protected virtual void CreateMobileControlMessengers()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for IPAddress to provide additional functionality such as getting broadcast address, network address, and checking if two addresses are in the same subnet.
|
||||
/// </summary>
|
||||
public static class IPAddressExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the broadcast address for a given IP address and subnet mask.
|
||||
/// </summary>
|
||||
/// <param name="address">Address to check</param>
|
||||
/// <param name="subnetMask">Subnet mask in a.b.c.d format</param>
|
||||
/// <returns>Broadcast address</returns>
|
||||
/// <remarks>
|
||||
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the broadcast address will be 192.168.1.255
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static IPAddress GetBroadcastAddress(this IPAddress address, IPAddress subnetMask)
|
||||
{
|
||||
byte[] ipAdressBytes = address.GetAddressBytes();
|
||||
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
|
||||
|
||||
if (ipAdressBytes.Length != subnetMaskBytes.Length)
|
||||
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
|
||||
|
||||
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
|
||||
for (int i = 0; i < broadcastAddress.Length; i++)
|
||||
{
|
||||
broadcastAddress[i] = (byte)(ipAdressBytes[i] | (subnetMaskBytes[i] ^ 255));
|
||||
}
|
||||
return new IPAddress(broadcastAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the network address for a given IP address and subnet mask.
|
||||
/// </summary>
|
||||
/// <param name="address">Address to check</param>
|
||||
/// <param name="subnetMask">Subnet mask in a.b.c.d</param>
|
||||
/// <returns>Network Address</returns>
|
||||
/// /// <remarks>
|
||||
/// If the input IP address is 192.168.1.100 and the subnet mask is 255.255.255.0, the network address will be 192.168.1.0
|
||||
/// </remarks>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
public static IPAddress GetNetworkAddress(this IPAddress address, IPAddress subnetMask)
|
||||
{
|
||||
byte[] ipAdressBytes = address.GetAddressBytes();
|
||||
byte[] subnetMaskBytes = subnetMask.GetAddressBytes();
|
||||
|
||||
if (ipAdressBytes.Length != subnetMaskBytes.Length)
|
||||
throw new ArgumentException("Lengths of IP address and subnet mask do not match.");
|
||||
|
||||
byte[] broadcastAddress = new byte[ipAdressBytes.Length];
|
||||
for (int i = 0; i < broadcastAddress.Length; i++)
|
||||
{
|
||||
broadcastAddress[i] = (byte)(ipAdressBytes[i] & (subnetMaskBytes[i]));
|
||||
}
|
||||
return new IPAddress(broadcastAddress);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if two IP addresses are in the same subnet.
|
||||
/// </summary>
|
||||
/// <param name="address2">Address to check</param>
|
||||
/// <param name="address">Second address to check</param>
|
||||
/// <param name="subnetMask">Subnet mask to use to compare the 2 IP Address</param>
|
||||
/// <returns>True if addresses are in the same subnet</returns>
|
||||
/// <remarks>
|
||||
/// If the input IP addresses are 192.168.1.100 and 192.168.1.200, and the subnet mask is 255.255.255.0, this will return true.
|
||||
/// If the input IP addresses are 10.1.1.100 and 192.168.1.100, and the subnet mask is 255.255.255.0, this will return false.
|
||||
/// </remarks>
|
||||
public static bool IsInSameSubnet(this IPAddress address2, IPAddress address, IPAddress subnetMask)
|
||||
{
|
||||
IPAddress network1 = address.GetNetworkAddress(subnetMask);
|
||||
IPAddress network2 = address2.GetNetworkAddress(subnetMask);
|
||||
|
||||
return network1.Equals(network2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,15 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Stores pending route requests, keyed by the destination device key.
|
||||
/// Used primarily to handle routing requests while a device is cooling down.
|
||||
/// </summary>
|
||||
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// A queue to process route requests and releases sequentially.
|
||||
/// </summary>
|
||||
private static readonly GenericQueue routeRequestQueue = new GenericQueue("routingQueue");
|
||||
|
||||
/// <summary>
|
||||
@@ -38,16 +45,49 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set
|
||||
/// </summary>
|
||||
/// <param name="destination">destination to clear</param>
|
||||
public static void ReleaseRoute(this IRoutingInputs destination)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route to the destination, if a route is found. This does not CLEAR the route, only stop counting usage time on any output ports that have a usage tracker set
|
||||
/// </summary>
|
||||
/// <param name="destination">destination to clear</param>
|
||||
/// <param name="inputPortKey">Input to use to find existing route</param>
|
||||
public static void ReleaseRoute(this IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, false));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the route on the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destination">Destination</param>
|
||||
public static void ClearRoute(this IRoutingInputs destination)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, string.Empty, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the route on the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destination">destination</param>
|
||||
/// <param name="inputPortKey">input to use to find existing route</param>
|
||||
public static void ClearRoute(this IRoutingInputs destination, string inputPortKey)
|
||||
{
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination, inputPortKey, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the route request for the destination. This will remove any routes that are currently in use
|
||||
/// </summary>
|
||||
/// <param name="destinationKey">destination device key</param>
|
||||
public static void RemoveRouteRequestForDestination(string destinationKey)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Removing route request for {destination}", null, destinationKey);
|
||||
@@ -130,6 +170,15 @@ namespace PepperDash.Essentials.Core
|
||||
return (audioRouteDescriptor, videoRouteDescriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to handle the logic for releasing an existing route and making a new one.
|
||||
/// Handles devices with cooling states by queueing the request.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="signalType">The type of signal to route.</param>
|
||||
/// <param name="destinationPort">The specific destination input port (optional).</param>
|
||||
/// <param name="sourcePort">The specific source output port (optional).</param>
|
||||
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
|
||||
{
|
||||
if (destination == null) throw new ArgumentNullException(nameof(destination));
|
||||
@@ -184,11 +233,16 @@ namespace PepperDash.Essentials.Core
|
||||
Debug.LogMessage(LogEventLevel.Information, "Device: {destination} is NOT cooling down. Removing stored route request and routing to source key: {sourceKey}", null, destination.Key, routeRequest.Source.Key);
|
||||
}
|
||||
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty));
|
||||
routeRequestQueue.Enqueue(new ReleaseRouteQueueItem(ReleaseRouteInternal, destination,destinationPort?.Key ?? string.Empty, false));
|
||||
|
||||
routeRequestQueue.Enqueue(new RouteRequestQueueItem(RunRouteRequest, routeRequest));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the actual routing based on a <see cref="RouteRequest"/>.
|
||||
/// Finds the route path, adds it to the collection, and executes the switches.
|
||||
/// </summary>
|
||||
/// <param name="request">The route request details.</param>
|
||||
private static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
try
|
||||
@@ -216,14 +270,15 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
Debug.LogMessage(ex, "Exception Running Route Request {request}", null, request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// Will release the existing route on the destination, if it is found in RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey)
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="inputPortKey"> The input port key to use to find the route. If empty, will use the first available input port</param>
|
||||
/// <param name="clearRoute"> If true, will clear the route on the destination. This will remove any routes that are currently in use</param>
|
||||
private static void ReleaseRouteInternal(IRoutingInputs destination, string inputPortKey, bool clearRoute)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -242,7 +297,7 @@ namespace PepperDash.Essentials.Core
|
||||
if (current != null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Releasing current route: {0}", destination, current.Source.Key);
|
||||
current.ReleaseRoutes();
|
||||
current.ReleaseRoutes(clearRoute);
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||
/// Marker interface to identify a device that acts as the origin of a signal path (<see cref="IRoutingOutputs"/>).
|
||||
/// </summary>
|
||||
public interface IRoutingSource : IRoutingOutputs
|
||||
{
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a routing device (<see cref="IRouting"/>) that supports explicitly clearing a route on an output.
|
||||
/// </summary>
|
||||
public interface IRoutingWithClear : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -3,14 +3,25 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate for handling route change events on devices implementing <see cref="IRoutingWithFeedback"/>.
|
||||
/// </summary>
|
||||
/// <param name="midpoint">The routing device where the change occurred.</param>
|
||||
/// <param name="newRoute">A descriptor of the new route that was established.</param>
|
||||
public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
|
||||
/// <summary>
|
||||
/// Defines an IRouting with a feedback event
|
||||
/// Defines a routing device (<see cref="IRouting"/>) that provides feedback about its current routes.
|
||||
/// </summary>
|
||||
public interface IRoutingWithFeedback : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list describing the currently active routes on this device.
|
||||
/// </summary>
|
||||
List<RouteSwitchDescriptor> CurrentRoutes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Event triggered when a route changes on this device.
|
||||
/// </summary>
|
||||
event RouteChangedEventHandler RouteChanged;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,18 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a routing device (typically a transmitter or source) that provides numeric feedback for its current route.
|
||||
/// Extends <see cref="IRoutingNumeric"/>.
|
||||
/// </summary>
|
||||
public interface ITxRouting : IRoutingNumeric
|
||||
{
|
||||
/// <summary>
|
||||
/// Feedback indicating the currently routed video source by its numeric identifier.
|
||||
/// </summary>
|
||||
IntFeedback VideoSourceNumericFeedback { get; }
|
||||
/// <summary>
|
||||
/// Feedback indicating the currently routed audio source by its numeric identifier.
|
||||
/// </summary>
|
||||
IntFeedback AudioSourceNumericFeedback { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharpPro;
|
||||
|
||||
@@ -9,35 +10,63 @@ using Serilog.Events;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// Represents a collection of individual route steps between a Source and a Destination device for a specific signal type.
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The destination device (sink or midpoint) for the route.
|
||||
/// </summary>
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The specific input port on the destination device used for this route. Can be null if not specified or applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source device for the route.
|
||||
/// </summary>
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal being routed (e.g., Audio, Video). This descriptor represents a single signal type.
|
||||
/// </summary>
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A list of individual switching steps required to establish the route.
|
||||
/// </summary>
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteDescriptor"/> class for a route without a specific destination input port.
|
||||
/// </summary>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="signalType">The type of signal being routed.</param>
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType) : this(source, destination, null, signalType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteDescriptor"/> class for a route with a specific destination input port.
|
||||
/// </summary>
|
||||
/// <param name="source">The source device.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="inputPort">The destination input port (optional).</param>
|
||||
/// <param name="signalType">The signal type for this route.</param>
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, RoutingInputPort inputPort, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
InputPort = inputPort;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
InputPort = inputPort;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// Executes all the switching steps defined in the <see cref="Routes"/> list.
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
@@ -63,15 +92,27 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// Releases the usage tracking for the route and optionally clears the route on the switching devices.
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
/// <param name="clearRoute">If true, attempts to clear the route on the switching devices (e.g., set input to null/0).</param>
|
||||
public void ReleaseRoutes(bool clearRoute = false)
|
||||
{
|
||||
foreach (var route in Routes.Where(r => r.SwitchingDevice is IRouting))
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||
{
|
||||
if(clearRoute)
|
||||
{
|
||||
try
|
||||
{
|
||||
switchingDevice.ExecuteSwitch(null, route.OutputPort.Selector, SignalType);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError("Error executing switch: {exception}", e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
if (route.OutputPort == null)
|
||||
{
|
||||
continue;
|
||||
@@ -90,6 +131,10 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route descriptor, including source, destination, and individual route steps.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the route.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
|
||||
@@ -4,15 +4,42 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a request to establish a route between a source and a destination device.
|
||||
/// </summary>
|
||||
public class RouteRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The specific input port on the destination device to use for the route. Can be null if the port should be automatically determined or is not applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort DestinationPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The specific output port on the source device to use for the route. Can be null if the port should be automatically determined or is not applicable.
|
||||
/// </summary>
|
||||
public RoutingOutputPort SourcePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The destination device (sink or midpoint) for the route.
|
||||
/// </summary>
|
||||
public IRoutingInputs Destination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The source device for the route.
|
||||
/// </summary>
|
||||
public IRoutingOutputs Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal being routed (e.g., Audio, Video, AudioVideo).
|
||||
/// </summary>
|
||||
public eRoutingSignalType SignalType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handles the route request after a device's cooldown period has finished.
|
||||
/// This method is typically subscribed to the IsCoolingDownFeedback.OutputChange event.
|
||||
/// </summary>
|
||||
/// <param name="sender">The object that triggered the event (usually the cooling device).</param>
|
||||
/// <param name="args">Event arguments indicating the cooldown state change.</param>
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
try
|
||||
@@ -39,6 +66,10 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route request.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the source and destination of the route request.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Route {Source?.Key ?? "No Source Device"}:{SourcePort?.Key ?? "auto"} to {Destination?.Key ?? "No Destination Device"}:{DestinationPort?.Key ?? "auto"}";
|
||||
|
||||
@@ -5,17 +5,34 @@ using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an item in the route request queue.
|
||||
/// </summary>
|
||||
public class RouteRequestQueueItem : IQueueMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// The action to perform for the route request.
|
||||
/// </summary>
|
||||
private readonly Action<RouteRequest> action;
|
||||
/// <summary>
|
||||
/// The route request data.
|
||||
/// </summary>
|
||||
private readonly RouteRequest routeRequest;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteRequestQueueItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="routeAction">The action to perform.</param>
|
||||
/// <param name="request">The route request data.</param>
|
||||
public RouteRequestQueueItem(Action<RouteRequest> routeAction, RouteRequest request)
|
||||
{
|
||||
action = routeAction;
|
||||
routeRequest = request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the route request action.
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Dispatching route request {routeRequest}", null, routeRequest);
|
||||
@@ -23,23 +40,50 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an item in the queue for releasing a route.
|
||||
/// </summary>
|
||||
public class ReleaseRouteQueueItem : IQueueMessage
|
||||
{
|
||||
private readonly Action<IRoutingInputs, string> action;
|
||||
/// <summary>
|
||||
/// The action to perform for releasing the route.
|
||||
/// </summary>
|
||||
private readonly Action<IRoutingInputs, string, bool> action;
|
||||
/// <summary>
|
||||
/// The destination device whose route is being released.
|
||||
/// </summary>
|
||||
private readonly IRoutingInputs destination;
|
||||
/// <summary>
|
||||
/// The specific input port key on the destination to release, or null/empty for any/default.
|
||||
/// </summary>
|
||||
private readonly string inputPortKey;
|
||||
/// <summary>
|
||||
/// Indicates whether to clear the route (send null) or just release the usage tracking.
|
||||
/// </summary>
|
||||
private readonly bool clearRoute;
|
||||
|
||||
public ReleaseRouteQueueItem(Action<IRoutingInputs, string> action, IRoutingInputs destination, string inputPortKey)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ReleaseRouteQueueItem"/> class.
|
||||
/// </summary>
|
||||
/// <param name="action">The action to perform.</param>
|
||||
/// <param name="destination">The destination device.</param>
|
||||
/// <param name="inputPortKey">The input port key.</param>
|
||||
/// <param name="clearRoute">True to clear the route, false to just release.</param>
|
||||
public ReleaseRouteQueueItem(Action<IRoutingInputs, string, bool> action, IRoutingInputs destination, string inputPortKey, bool clearRoute)
|
||||
{
|
||||
this.action = action;
|
||||
this.destination = destination;
|
||||
this.inputPortKey = inputPortKey;
|
||||
this.clearRoute = clearRoute;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispatches the release route action.
|
||||
/// </summary>
|
||||
public void Dispatch()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Dispatching release route request for {destination}:{inputPortKey}", null, destination?.Key ?? "no destination", string.IsNullOrEmpty(inputPortKey) ? "auto" : inputPortKey);
|
||||
action(destination, inputPortKey);
|
||||
action(destination, inputPortKey, clearRoute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,47 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// Represents a single switching step within a larger route, detailing the switching device, input port, and optionally the output port.
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
/// <summary>
|
||||
/// The device performing the switch (derived from the InputPort's parent).
|
||||
/// </summary>
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort?.ParentDevice; } }
|
||||
/// <summary>
|
||||
/// The output port being switched from (relevant for matrix switchers). Null for sink devices.
|
||||
/// </summary>
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
/// <summary>
|
||||
/// The input port being switched to.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteSwitchDescriptor"/> class for sink devices (no output port).
|
||||
/// </summary>
|
||||
/// <param name="inputPort">The input port being switched to.</param>
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RouteSwitchDescriptor"/> class for matrix switchers.
|
||||
/// </summary>
|
||||
/// <param name="outputPort">The output port being switched from.</param>
|
||||
/// <param name="inputPort">The input port being switched to.</param>
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the route switch descriptor.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the switch operation.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (SwitchingDevice is IRouting)
|
||||
|
||||
@@ -5,8 +5,17 @@ using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public class RoutingFeedbackManager:EssentialsDevice
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingFeedbackManager"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this manager device.</param>
|
||||
/// <param name="name">The name of this manager device.</param>
|
||||
public RoutingFeedbackManager(string key, string name): base(key, name)
|
||||
{
|
||||
AddPreActivationAction(SubscribeForMidpointFeedback);
|
||||
@@ -14,6 +23,9 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the RouteChanged event on all devices implementing <see cref="IRoutingWithFeedback"/>.
|
||||
/// </summary>
|
||||
private void SubscribeForMidpointFeedback()
|
||||
{
|
||||
var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();
|
||||
@@ -24,6 +36,9 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribes to the InputChanged event on all devices implementing <see cref="IRoutingSinkWithSwitchingWithInputPort"/>.
|
||||
/// </summary>
|
||||
private void SubscribeForSinkFeedback()
|
||||
{
|
||||
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||
@@ -34,6 +49,12 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the RouteChanged event from a midpoint device.
|
||||
/// Triggers an update for all sink devices.
|
||||
/// </summary>
|
||||
/// <param name="midpoint">The midpoint device that reported a route change.</param>
|
||||
/// <param name="newRoute">The descriptor of the new route.</param>
|
||||
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
|
||||
{
|
||||
try
|
||||
@@ -51,6 +72,12 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the InputChanged event from a sink device.
|
||||
/// Triggers an update for the specific sink device.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
|
||||
{
|
||||
try
|
||||
@@ -63,6 +90,12 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the CurrentSourceInfo and CurrentSourceInfoKey properties on a destination (sink) device
|
||||
/// based on its currently selected input port by tracing the route back through tie lines.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination sink device to update.</param>
|
||||
/// <param name="inputPort">The currently selected input port on the destination device.</param>
|
||||
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
|
||||
{
|
||||
// Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
|
||||
@@ -199,6 +232,12 @@ namespace PepperDash.Essentials.Core.Routing
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively traces a route back from a given tie line to find the root source tie line.
|
||||
/// It navigates through midpoint devices (<see cref="IRoutingWithFeedback"/>) by checking their current routes.
|
||||
/// </summary>
|
||||
/// <param name="tieLine">The starting tie line (typically connected to a sink or midpoint).</param>
|
||||
/// <returns>The <see cref="TieLine"/> connected to the original source device, or null if the source cannot be determined.</returns>
|
||||
private TieLine GetRootTieLine(TieLine tieLine)
|
||||
{
|
||||
TieLine nextTieLine = null;
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic RoutingInput with no statuses.
|
||||
/// Represents a basic routing input port on a device.
|
||||
/// </summary>
|
||||
public class RoutingInputPort : RoutingPort
|
||||
{
|
||||
@@ -41,6 +41,10 @@ namespace PepperDash.Essentials.Core
|
||||
ParentDevice = parent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the input port.
|
||||
/// </summary>
|
||||
/// <returns>A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType".</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
||||
/// Will provide video statistics on connected signals
|
||||
/// Represents a routing input port that provides video status feedback (e.g., sync, resolution).
|
||||
/// Suitable for devices like DM transmitters or DM input cards.
|
||||
/// </summary>
|
||||
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
||||
{
|
||||
/// <summary>
|
||||
/// Video statuses attached to this port
|
||||
/// Provides feedback outputs for video statuses associated with this port.
|
||||
/// </summary>
|
||||
public VideoStatusOutputs VideoStatus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// Initializes a new instance of the <see cref="RoutingInputPortWithVideoStatuses"/> class.
|
||||
/// </summary>
|
||||
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
|
||||
/// May be string, number, whatever</param>
|
||||
/// <param name="parent">The IRoutingInputs object this lives on</param>
|
||||
/// <param name="funcs">A VideoStatusFuncsWrapper used to assign the callback funcs that will get
|
||||
/// the values for the various stats</param>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingInputs"/> device this port belongs to.</param>
|
||||
/// <param name="funcs">A <see cref="VideoStatusFuncsWrapper"/> containing delegates to retrieve video status values.</param>
|
||||
public RoutingInputPortWithVideoStatuses(string key,
|
||||
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
|
||||
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
|
||||
|
||||
@@ -3,32 +3,72 @@
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides event arguments for routing changes, potentially including numeric or port object references.
|
||||
/// </summary>
|
||||
public class RoutingNumericEventArgs : EventArgs
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The numeric representation of the output, if applicable.
|
||||
/// </summary>
|
||||
public uint? Output { get; set; }
|
||||
/// <summary>
|
||||
/// The numeric representation of the input, if applicable.
|
||||
/// </summary>
|
||||
public uint? Input { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of signal involved in the routing change.
|
||||
/// </summary>
|
||||
public eRoutingSignalType SigType { get; set; }
|
||||
/// <summary>
|
||||
/// The input port involved in the routing change, if applicable.
|
||||
/// </summary>
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
/// <summary>
|
||||
/// The output port involved in the routing change, if applicable.
|
||||
/// </summary>
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class using numeric identifiers.
|
||||
/// </summary>
|
||||
/// <param name="output">The numeric output identifier.</param>
|
||||
/// <param name="input">The numeric input identifier.</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class using port objects.
|
||||
/// </summary>
|
||||
/// <param name="outputPort">The output port object.</param>
|
||||
/// <param name="inputPort">The input port object.</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
||||
eRoutingSignalType sigType)
|
||||
: this(null, null, outputPort, inputPort, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class with default values.
|
||||
/// </summary>
|
||||
public RoutingNumericEventArgs()
|
||||
: this(null, null, null, null, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingNumericEventArgs"/> class with potentially mixed identifiers.
|
||||
/// </summary>
|
||||
/// <param name="output">The numeric output identifier (optional).</param>
|
||||
/// <param name="input">The numeric input identifier (optional).</param>
|
||||
/// <param name="outputPort">The output port object (optional).</param>
|
||||
/// <param name="inputPort">The input port object (optional).</param>
|
||||
/// <param name="sigType">The signal type.</param>
|
||||
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
||||
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
||||
{
|
||||
|
||||
@@ -4,29 +4,46 @@ using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a basic routing output port on a device.
|
||||
/// </summary>
|
||||
public class RoutingOutputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingOutputs object this port lives on
|
||||
/// The IRoutingOutputs object this port lives on.
|
||||
/// </summary>
|
||||
///
|
||||
[JsonIgnore]
|
||||
public IRoutingOutputs ParentDevice { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Tracks which destinations are currently using this output port.
|
||||
/// </summary>
|
||||
public InUseTracking InUseTracker { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingOutputPort"/> class.
|
||||
/// </summary>
|
||||
/// <param name="selector">An object used to refer to this port in the IRouting device's ExecuteSwitch method.
|
||||
/// May be string, number, whatever</param>
|
||||
/// <param name="parent">The IRoutingOutputs object this port lives on</param>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingOutputs"/> device this port belongs to.</param>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingOutputPort"/> class, potentially marking it as internal.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">An object used to refer to this port in the parent device's ExecuteSwitch method.</param>
|
||||
/// <param name="parent">The <see cref="IRoutingOutputs"/> device this port belongs to.</param>
|
||||
/// <param name="isInternal">True if this port represents an internal connection within a device (e.g., card to backplane).</param>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
@@ -35,6 +52,10 @@ namespace PepperDash.Essentials.Core
|
||||
InUseTracker = new InUseTracking();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the output port.
|
||||
/// </summary>
|
||||
/// <returns>A string in the format "ParentDeviceKey|PortKey|SignalType|ConnectionType".</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
|
||||
@@ -4,18 +4,47 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for RoutingInput and Output ports
|
||||
/// Base class for <see cref="RoutingInputPort"/> and <see cref="RoutingOutputPort"/>.
|
||||
/// </summary>
|
||||
public abstract class RoutingPort : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique key identifying this port within its parent device.
|
||||
/// </summary>
|
||||
public string Key { get; private set; }
|
||||
/// <summary>
|
||||
/// The type of signal this port handles (e.g., Audio, Video, AudioVideo).
|
||||
/// </summary>
|
||||
public eRoutingSignalType Type { get; private set; }
|
||||
/// <summary>
|
||||
/// The physical connection type of this port (e.g., Hdmi, Rca, Dm).
|
||||
/// </summary>
|
||||
public eRoutingPortConnectionType ConnectionType { get; private set; }
|
||||
/// <summary>
|
||||
/// An object (often a number or string) used by the parent routing device to select this port during switching.
|
||||
/// </summary>
|
||||
public readonly object Selector;
|
||||
/// <summary>
|
||||
/// Indicates if this port represents an internal connection within a device (e.g., card to backplane).
|
||||
/// </summary>
|
||||
public bool IsInternal { get; private set; }
|
||||
/// <summary>
|
||||
/// An object used to match feedback values to this port, if applicable.
|
||||
/// </summary>
|
||||
public object FeedbackMatchObject { get; set; }
|
||||
/// <summary>
|
||||
/// A reference to the underlying hardware port object (e.g., SimplSharpPro Port), if applicable.
|
||||
/// </summary>
|
||||
public object Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RoutingPort"/> class.
|
||||
/// </summary>
|
||||
/// <param name="key">The unique key for this port.</param>
|
||||
/// <param name="type">The signal type supported by this port.</param>
|
||||
/// <param name="connType">The physical connection type of this port.</param>
|
||||
/// <param name="selector">The selector object for switching.</param>
|
||||
/// <param name="isInternal">True if this port is internal.</param>
|
||||
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, object selector, bool isInternal)
|
||||
{
|
||||
Key = key;
|
||||
|
||||
@@ -7,7 +7,8 @@ using Crestron.SimplSharp;
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// These should correspond directly with the portNames var in the config tool.
|
||||
/// Defines constant string values for common routing port keys.
|
||||
/// These should correspond directly with the portNames var in the config tool.
|
||||
/// </summary>
|
||||
public class RoutingPortNames
|
||||
{
|
||||
|
||||
@@ -4,14 +4,23 @@ using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a connection (tie line) between a <see cref="RoutingOutputPort"/> and a <see cref="RoutingInputPort"/>.
|
||||
/// </summary>
|
||||
public class TieLine
|
||||
{
|
||||
/// <summary>
|
||||
/// The source output port of the tie line.
|
||||
/// </summary>
|
||||
public RoutingOutputPort SourcePort { get; private set; }
|
||||
/// <summary>
|
||||
/// The destination input port of the tie line.
|
||||
/// </summary>
|
||||
public RoutingInputPort DestinationPort { get; private set; }
|
||||
//public int InUseCount { get { return DestinationUsingThis.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of this tie line. Will either be the type of the desination port
|
||||
/// Gets the type of this tie line. Will either be the type of the destination port
|
||||
/// or the type of OverrideType when it is set.
|
||||
/// </summary>
|
||||
public eRoutingSignalType Type
|
||||
@@ -35,20 +44,27 @@ namespace PepperDash.Essentials.Core
|
||||
//List<IRoutingInputs> DestinationUsingThis = new List<IRoutingInputs>();
|
||||
|
||||
/// <summary>
|
||||
/// For tie lines that represent internal links, like from cards to the matrix in a DM.
|
||||
/// This property is true if SourcePort and DestinationPort IsInternal
|
||||
/// property are both true
|
||||
/// Gets a value indicating whether this tie line represents an internal connection within a device (both source and destination ports are internal).
|
||||
/// </summary>
|
||||
public bool IsInternal { get { return SourcePort.IsInternal && DestinationPort.IsInternal; } }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the signal types of the source and destination ports differ.
|
||||
/// </summary>
|
||||
public bool TypeMismatch { get { return SourcePort.Type != DestinationPort.Type; } }
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the connection types of the source and destination ports differ.
|
||||
/// </summary>
|
||||
public bool ConnectionTypeMismatch { get { return SourcePort.ConnectionType != DestinationPort.ConnectionType; } }
|
||||
/// <summary>
|
||||
/// A descriptive note about any type mismatch, if applicable.
|
||||
/// </summary>
|
||||
public string TypeMismatchNote { get; set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// Initializes a new instance of the <see cref="TieLine"/> class.
|
||||
/// </summary>
|
||||
/// <param name="sourcePort"></param>
|
||||
/// <param name="destinationPort"></param>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort)
|
||||
{
|
||||
if (sourcePort == null || destinationPort == null)
|
||||
@@ -58,9 +74,11 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info.
|
||||
/// </summary>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations.</param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType? overrideType) :
|
||||
this(sourcePort, destinationPort)
|
||||
{
|
||||
@@ -68,9 +86,11 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info
|
||||
/// Creates a tie line with an overriding Type. See help for OverrideType property for info.
|
||||
/// </summary>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type</param>
|
||||
/// <param name="sourcePort">The source output port.</param>
|
||||
/// <param name="destinationPort">The destination input port.</param>
|
||||
/// <param name="overrideType">The signal type to limit the link to. Overrides DestinationPort.Type for routing calculations.</param>
|
||||
public TieLine(RoutingOutputPort sourcePort, RoutingInputPort destinationPort, eRoutingSignalType overrideType) :
|
||||
this(sourcePort, destinationPort)
|
||||
{
|
||||
@@ -78,18 +98,25 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will link up video status from supporting inputs to connected outputs
|
||||
/// Will link up video status from supporting inputs to connected outputs.
|
||||
/// </summary>
|
||||
public void Activate()
|
||||
{
|
||||
// Now does nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates the tie line.
|
||||
/// </summary>
|
||||
public void Deactivate()
|
||||
{
|
||||
// Now does nothing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the tie line.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the source, destination, and type of the tie line.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Tie line: {0}:{1} --> {2}:{3} {4}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
||||
@@ -99,8 +126,14 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
//********************************************************************************
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection of <see cref="TieLine"/> objects.
|
||||
/// </summary>
|
||||
public class TieLineCollection : List<TieLine>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the default singleton instance of the <see cref="TieLineCollection"/>.
|
||||
/// </summary>
|
||||
public static TieLineCollection Default
|
||||
{
|
||||
get
|
||||
@@ -111,6 +144,9 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backing field for the singleton instance.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
private static TieLineCollection _Default;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
@@ -15,15 +13,44 @@ using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the configuration data for a single tie line between two routing ports.
|
||||
/// </summary>
|
||||
public class TieLineConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The key of the source device.
|
||||
/// </summary>
|
||||
public string SourceKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the source card (if applicable, e.g., in a modular chassis).
|
||||
/// </summary>
|
||||
public string SourceCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the source output port.
|
||||
/// </summary>
|
||||
public string SourcePort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination device.
|
||||
/// </summary>
|
||||
public string DestinationKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination card (if applicable).
|
||||
/// </summary>
|
||||
public string DestinationCard { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The key of the destination input port.
|
||||
/// </summary>
|
||||
public string DestinationPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional override for the signal type of the tie line. If set, this overrides the destination port's type for routing calculations.
|
||||
/// </summary>
|
||||
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public eRoutingSignalType? OverrideType { get; set; }
|
||||
@@ -73,11 +100,19 @@ namespace PepperDash.Essentials.Core.Config
|
||||
return new TieLine(sourceOutputPort, destinationInputPort, OverrideType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs an error message related to creating this tie line configuration.
|
||||
/// </summary>
|
||||
/// <param name="msg">The specific error message.</param>
|
||||
void LogError(string msg)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of the tie line configuration.
|
||||
/// </summary>
|
||||
/// <returns>A string describing the source and destination of the configured tie line.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0}.{1}.{2} --> {3}.{4}.{5}", SourceKey, SourceCard, SourcePort,
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace PepperDash.Essentials.Devices.Common.Cameras
|
||||
|
||||
SendCameraPresetNamesToApi(presetsCamera, joinMap, trilist);
|
||||
|
||||
for (int i = 0; i < joinMap.NumberOfPresets.JoinSpan; i++)
|
||||
for (int i = 0; i < joinMap.PresetRecallStart.JoinSpan; i++)
|
||||
{
|
||||
int tempNum = i;
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the available tracking modes for a Cisco codec's Presenter Track feature.
|
||||
/// </summary>
|
||||
public enum ePresenterTrackMode
|
||||
{
|
||||
/// <summary>
|
||||
/// Presenter Track is turned off.
|
||||
/// </summary>
|
||||
Off,
|
||||
/// <summary>
|
||||
/// Presenter Track follows the speaker's movements.
|
||||
/// </summary>
|
||||
Follow,
|
||||
/// <summary>
|
||||
/// Presenter Track is set to background mode, where it tracks the speaker but does not actively follow.
|
||||
/// </summary>
|
||||
Background,
|
||||
/// <summary>
|
||||
/// Presenter Track is set to persistent mode, where it maintains a fixed position or focus on the speaker.
|
||||
/// </summary>
|
||||
Persistent
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Describes the Presenter Track controls for a Cisco codec.
|
||||
/// </summary>
|
||||
public interface IPresenterTrack : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
bool PresenterTrackAvailability { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating whether Presenter Track is available.
|
||||
/// </summary>
|
||||
BoolFeedback PresenterTrackAvailableFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating the current status of Presenter Track is off
|
||||
/// </summary>
|
||||
BoolFeedback PresenterTrackStatusOffFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating the current status of Presenter Track is follow
|
||||
/// </summary>
|
||||
BoolFeedback PresenterTrackStatusFollowFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating the current status of Presenter Track is background
|
||||
/// </summary>
|
||||
BoolFeedback PresenterTrackStatusBackgroundFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating the current status of Presenter Track is persistent
|
||||
/// </summary>
|
||||
BoolFeedback PresenterTrackStatusPersistentFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the current status of Presenter Track.
|
||||
/// </summary>
|
||||
bool PresenterTrackStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Turns off Presenter Track.
|
||||
/// </summary>
|
||||
void PresenterTrackOff();
|
||||
|
||||
/// <summary>
|
||||
/// Turns on Presenter Track in follow mode.
|
||||
/// </summary>
|
||||
void PresenterTrackFollow();
|
||||
|
||||
/// <summary>
|
||||
/// Turns on Presenter Track in background mode.
|
||||
/// </summary>
|
||||
void PresenterTrackBackground();
|
||||
|
||||
/// <summary>
|
||||
/// Turns on Presenter Track in persistent mode.
|
||||
/// </summary>
|
||||
void PresenterTrackPersistent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.Codec.Cisco
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the available tracking modes for a Cisco codec
|
||||
/// </summary>
|
||||
public interface ISpeakerTrack : IKeyed
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether Speaker Track is available on the codec.
|
||||
/// </summary>
|
||||
bool SpeakerTrackAvailability { get; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
BoolFeedback SpeakerTrackAvailableFeedback { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Feedback indicating the current status of Speaker Track is off
|
||||
/// </summary>
|
||||
bool SpeakerTrackStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Turns Speaker Track off
|
||||
/// </summary>
|
||||
void SpeakerTrackOff();
|
||||
/// <summary>
|
||||
/// Turns Speaker Track on
|
||||
/// </summary>
|
||||
void SpeakerTrackOn();
|
||||
}
|
||||
}
|
||||
@@ -18,9 +18,13 @@ namespace PepperDash.Essentials.Devices.Common.DSP
|
||||
|
||||
public Dictionary<string, DspControlPoint> SwitcherControlPoints { get; private set; }
|
||||
|
||||
public DspBase(string key, string name) :
|
||||
base(key, name)
|
||||
{
|
||||
public DspBase(string key, string name) :
|
||||
base(key, name)
|
||||
{
|
||||
|
||||
LevelControlPoints = new Dictionary<string, IBasicVolumeWithFeedback>();
|
||||
DialerControlPoints = new Dictionary<string, DspControlPoint>();
|
||||
SwitcherControlPoints = new Dictionary<string, DspControlPoint>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.Generic
|
||||
{
|
||||
public class GenericSink : EssentialsDevice, IRoutingSink
|
||||
public class GenericSink : EssentialsDevice, IRoutingSinkWithInputPort
|
||||
{
|
||||
public GenericSink(string key, string name) : base(key, name)
|
||||
{
|
||||
|
||||
@@ -8,9 +8,10 @@ using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common.SoftCodec
|
||||
{
|
||||
public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingOutputs, IRoutingSinkWithSwitching
|
||||
public class GenericSoftCodec : EssentialsDevice, IRoutingSource, IRoutingSinkWithSwitchingWithInputPort
|
||||
{
|
||||
private RoutingInputPort _currentInputPort;
|
||||
|
||||
public RoutingInputPort CurrentInputPort {
|
||||
get => _currentInputPort;
|
||||
set
|
||||
|
||||
@@ -12,20 +12,45 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
|
||||
/// </summary>
|
||||
public interface IHasCodecRoomPresets
|
||||
{
|
||||
/// <summary>
|
||||
/// Event that is raised when the list of room presets has changed.
|
||||
/// </summary>
|
||||
event EventHandler<EventArgs> CodecRoomPresetsListHasChanged;
|
||||
|
||||
/// <summary>
|
||||
/// List of near end presets that can be recalled.
|
||||
/// </summary>
|
||||
List<CodecRoomPreset> NearEndPresets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// List of far end presets that can be recalled.
|
||||
/// </summary>
|
||||
List<CodecRoomPreset> FarEndRoomPresets { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Selects a near end preset by its ID.
|
||||
/// </summary>
|
||||
/// <param name="preset"></param>
|
||||
void CodecRoomPresetSelect(int preset);
|
||||
|
||||
void CodecRoomPresetStore(int preset, string description);
|
||||
/// <summary>
|
||||
/// Stores a near end preset with the given ID and description.
|
||||
/// </summary>
|
||||
/// <param name="preset"></param>
|
||||
/// <param name="description"></param>
|
||||
void CodecRoomPresetStore(int preset, string description);
|
||||
|
||||
/// <summary>
|
||||
/// Selects a far end preset by its ID. This is typically used to recall a preset that has been defined on the far end codec.
|
||||
/// </summary>
|
||||
/// <param name="preset"></param>
|
||||
void SelectFarEndPreset(int preset);
|
||||
}
|
||||
|
||||
public static class RoomPresets
|
||||
/// <summary>
|
||||
/// Static class for converting non-generic RoomPresets to generic CameraPresets.
|
||||
/// </summary>
|
||||
public static class RoomPresets
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts non-generic RoomPresets to generic CameraPresets
|
||||
@@ -47,6 +72,13 @@ namespace PepperDash.Essentials.Devices.Common.VideoCodec
|
||||
/// </summary>
|
||||
public class CodecRoomPreset : PresetBase
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="description"></param>
|
||||
/// <param name="def"></param>
|
||||
/// <param name="isDef"></param>
|
||||
public CodecRoomPreset(int id, string description, bool def, bool isDef)
|
||||
: base(id, description, def, isDef)
|
||||
{
|
||||
|
||||
@@ -2,27 +2,69 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.DeviceInfo;
|
||||
using System.Timers;
|
||||
|
||||
namespace PepperDash.Essentials.AppServer.Messengers
|
||||
{
|
||||
/// <summary>
|
||||
/// Facilitates communication of device information by providing mechanisms for status updates and device
|
||||
/// information reporting.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="DeviceInfoMessenger"/> class integrates with an <see
|
||||
/// cref="IDeviceInfoProvider"/> to manage device-specific information. It uses a debounce timer to limit the
|
||||
/// frequency of updates, ensuring efficient communication. The timer is initialized with a 1-second interval and
|
||||
/// is disabled by default. This class also subscribes to device information change events and provides actions for
|
||||
/// reporting full device status and triggering updates.</remarks>
|
||||
public class DeviceInfoMessenger : MessengerBase
|
||||
{
|
||||
private readonly IDeviceInfoProvider _deviceInfoProvider;
|
||||
|
||||
private readonly Timer debounceTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DeviceInfoMessenger"/> class, which facilitates communication
|
||||
/// of device information.
|
||||
/// </summary>
|
||||
/// <remarks>The messenger uses a debounce timer to limit the frequency of certain operations. The
|
||||
/// timer is initialized with a 1-second interval and is disabled by default.</remarks>
|
||||
/// <param name="key">A unique identifier for the messenger instance.</param>
|
||||
/// <param name="messagePath">The path used for sending and receiving messages.</param>
|
||||
/// <param name="device">An implementation of <see cref="IDeviceInfoProvider"/> that provides device-specific information.</param>
|
||||
public DeviceInfoMessenger(string key, string messagePath, IDeviceInfoProvider device) : base(key, messagePath, device as Device)
|
||||
{
|
||||
_deviceInfoProvider = device;
|
||||
}
|
||||
|
||||
debounceTimer = new Timer(1000)
|
||||
{
|
||||
Enabled = false,
|
||||
AutoReset = false
|
||||
};
|
||||
|
||||
debounceTimer.Elapsed += DebounceTimer_Elapsed;
|
||||
}
|
||||
|
||||
private void DebounceTimer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
PostStatusMessage(JToken.FromObject(new
|
||||
{
|
||||
deviceInfo = _deviceInfoProvider.DeviceInfo
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers actions and event handlers for device information updates and status reporting.
|
||||
/// </summary>
|
||||
/// <remarks>This method sets up actions for handling device status updates and reporting full
|
||||
/// device status. It also subscribes to the <see cref="IDeviceInfoProvider.DeviceInfoChanged"/> event to
|
||||
/// trigger debounced updates when the device information changes.</remarks>
|
||||
protected override void RegisterActions()
|
||||
{
|
||||
base.RegisterActions();
|
||||
|
||||
_deviceInfoProvider.DeviceInfoChanged += (o, a) =>
|
||||
{
|
||||
PostStatusMessage(JToken.FromObject(new
|
||||
{
|
||||
deviceInfo = a.DeviceInfo
|
||||
}));
|
||||
debounceTimer.Stop();
|
||||
debounceTimer.Start();
|
||||
};
|
||||
|
||||
AddAction("/fullStatus", (id, context) => PostStatusMessage(new DeviceInfoStateMessage
|
||||
@@ -34,6 +76,12 @@ namespace PepperDash.Essentials.AppServer.Messengers
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a message containing the state information of a device, including detailed device information.
|
||||
/// </summary>
|
||||
/// <remarks>This class is used to encapsulate the state of a device along with its associated
|
||||
/// information. It extends <see cref="DeviceStateMessageBase"/> to provide additional details about the
|
||||
/// device.</remarks>
|
||||
public class DeviceInfoStateMessage : DeviceStateMessageBase
|
||||
{
|
||||
[JsonProperty("deviceInfo")]
|
||||
|
||||
@@ -491,7 +491,7 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
{
|
||||
public MobileControlTouchpanelControllerFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel" };
|
||||
TypeNames = new List<string>() { "mccrestronapp", "mctsw550", "mctsw750", "mctsw1050", "mctsw560", "mctsw760", "mctsw1060", "mctsw570", "mctsw770", "mcts770", "mctsw1070", "mcts1070", "mcxpanel", "mcdge1000" };
|
||||
MinimumEssentialsFrameworkVersion = "2.0.0";
|
||||
}
|
||||
|
||||
@@ -555,7 +555,10 @@ namespace PepperDash.Essentials.Touchpanel
|
||||
return new Tsw1070(id, Global.ControlSystem);
|
||||
else if (type == "ts1070")
|
||||
return new Ts1070(id, Global.ControlSystem);
|
||||
else
|
||||
else if (type == "dge1000")
|
||||
return new Dge1000(id, Global.ControlSystem);
|
||||
else
|
||||
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "WARNING: Cannot create TSW controller with type '{0}'", type);
|
||||
return null;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,145 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.AppServer.Messengers;
|
||||
using PepperDash.Essentials.RoomBridges;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using WebSocketSharp;
|
||||
using WebSocketSharp.Server;
|
||||
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.WebSocketServer
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the behaviour to associate with a UiClient for WebSocket communication
|
||||
/// </summary>
|
||||
public class UiClient : WebSocketBehavior
|
||||
{
|
||||
public MobileControlSystemController Controller { get; set; }
|
||||
|
||||
public string RoomKey { get; set; }
|
||||
|
||||
private string _clientId;
|
||||
|
||||
private DateTime _connectionTime;
|
||||
|
||||
public TimeSpan ConnectedDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Context.WebSocket.IsAlive)
|
||||
{
|
||||
return DateTime.Now - _connectionTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
return new TimeSpan(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public UiClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnOpen()
|
||||
{
|
||||
base.OnOpen();
|
||||
|
||||
var url = Context.WebSocket.Url;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "New WebSocket Connection from: {0}", null, url);
|
||||
|
||||
var match = Regex.Match(url.AbsoluteUri, "(?:ws|wss):\\/\\/.*(?:\\/mc\\/api\\/ui\\/join\\/)(.*)");
|
||||
|
||||
if (!match.Success)
|
||||
{
|
||||
_connectionTime = DateTime.Now;
|
||||
return;
|
||||
}
|
||||
|
||||
var clientId = match.Groups[1].Value;
|
||||
_clientId = clientId;
|
||||
|
||||
if (Controller == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Controller is null");
|
||||
_connectionTime = DateTime.Now;
|
||||
}
|
||||
|
||||
var clientJoinedMessage = new MobileControlMessage
|
||||
{
|
||||
Type = "/system/clientJoined",
|
||||
Content = JToken.FromObject(new
|
||||
{
|
||||
clientId,
|
||||
roomKey = RoomKey,
|
||||
})
|
||||
};
|
||||
|
||||
Controller.HandleClientMessage(JsonConvert.SerializeObject(clientJoinedMessage));
|
||||
|
||||
var bridge = Controller.GetRoomBridge(RoomKey);
|
||||
|
||||
if (bridge == null) return;
|
||||
|
||||
SendUserCodeToClient(bridge, clientId);
|
||||
|
||||
bridge.UserCodeChanged -= Bridge_UserCodeChanged;
|
||||
bridge.UserCodeChanged += Bridge_UserCodeChanged;
|
||||
|
||||
// TODO: Future: Check token to see if there's already an open session using that token and reject/close the session
|
||||
}
|
||||
|
||||
private void Bridge_UserCodeChanged(object sender, EventArgs e)
|
||||
{
|
||||
SendUserCodeToClient((MobileControlEssentialsRoomBridge)sender, _clientId);
|
||||
}
|
||||
|
||||
private void SendUserCodeToClient(MobileControlBridgeBase bridge, string clientId)
|
||||
{
|
||||
var content = new
|
||||
{
|
||||
userCode = bridge.UserCode,
|
||||
qrUrl = bridge.QrCodeUrl,
|
||||
};
|
||||
|
||||
var message = new MobileControlMessage
|
||||
{
|
||||
Type = "/system/userCodeChanged",
|
||||
ClientId = clientId,
|
||||
Content = JToken.FromObject(content)
|
||||
};
|
||||
|
||||
Controller.SendMessageObjectToDirectClient(message);
|
||||
}
|
||||
|
||||
protected override void OnMessage(MessageEventArgs e)
|
||||
{
|
||||
base.OnMessage(e);
|
||||
|
||||
if (e.IsText && e.Data.Length > 0 && Controller != null)
|
||||
{
|
||||
// Forward the message to the controller to be put on the receive queue
|
||||
Controller.HandleClientMessage(e.Data);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnClose(CloseEventArgs e)
|
||||
{
|
||||
base.OnClose(e);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Closing: {0} reason: {1}", null, e.Code, e.Reason);
|
||||
}
|
||||
|
||||
protected override void OnError(ErrorEventArgs e)
|
||||
{
|
||||
base.OnError(e);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "WebSocket UiClient Error: {exception} message: {message}", e.Exception, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user