mirror of
https://github.com/PepperDash/Essentials.git
synced 2026-01-26 10:54:59 +00:00
Compare commits
84 Commits
2.0.0-alph
...
2.0.0-alph
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bac65002d | ||
|
|
ed0141a536 | ||
|
|
25ebcdfb5d | ||
|
|
3a56e47c48 | ||
|
|
171bd6b1ec | ||
|
|
e61fd7777a | ||
|
|
027bdd5bf4 | ||
|
|
b876b8123d | ||
|
|
6e05653c6c | ||
|
|
16d32bc720 | ||
|
|
0bef5d4b77 | ||
|
|
a3c1572a77 | ||
|
|
cc06c7bfd8 | ||
|
|
00a7b25026 | ||
|
|
edc916c9d3 | ||
|
|
c755ecb16c | ||
|
|
a8c36ba243 | ||
|
|
f630d3f410 | ||
|
|
35e0662b27 | ||
|
|
effefc939c | ||
|
|
5afdc2effa | ||
|
|
448cc273ec | ||
|
|
0a2aaa693f | ||
|
|
1ebee58ad6 | ||
|
|
3c5fe88e5a | ||
|
|
8255328be1 | ||
|
|
4bf026601f | ||
|
|
888f1b3888 | ||
|
|
19bd5723c8 | ||
|
|
e3e7add5b9 | ||
|
|
dd66de0463 | ||
|
|
3823943cd9 | ||
|
|
577e111f26 | ||
|
|
528fff569d | ||
|
|
06a6b1caa2 | ||
|
|
621d848418 | ||
|
|
2f9038a501 | ||
|
|
983b18d25a | ||
|
|
048004d441 | ||
|
|
e7e448f02c | ||
|
|
2e61d8d709 | ||
|
|
d8d2c5b340 | ||
|
|
7e736ae519 | ||
|
|
0067e11d3d | ||
|
|
7942c91f73 | ||
|
|
e1638762a1 | ||
|
|
3566400379 | ||
|
|
dde85f39a2 | ||
|
|
735433f660 | ||
|
|
734149960b | ||
|
|
cb16f2a505 | ||
|
|
a95d44e405 | ||
|
|
5820c9d282 | ||
|
|
4d19ecde00 | ||
|
|
826b7fd6d5 | ||
|
|
cb9eb5dafa | ||
|
|
eb955aa014 | ||
|
|
2d9ffca78e | ||
|
|
98f1a09c25 | ||
|
|
a11ad421f0 | ||
|
|
8878ff7ddd | ||
|
|
7e4b5f984f | ||
|
|
64ab315142 | ||
|
|
c47a93f4d0 | ||
|
|
5a55a701d6 | ||
|
|
01862ab9aa | ||
|
|
8ec6fa785e | ||
|
|
e37c675da1 | ||
|
|
3ee8cb7ea3 | ||
|
|
2b6f79b68f | ||
|
|
65369606a4 | ||
|
|
e9954b3081 | ||
|
|
bc67a4382b | ||
|
|
e1b50649fd | ||
|
|
5e69ea1947 | ||
|
|
80b8cc6385 | ||
|
|
82f78bf068 | ||
|
|
d8d27949c3 | ||
|
|
fd1b92a6c0 | ||
|
|
fa9b493431 | ||
|
|
06d806687d | ||
|
|
d8e2f8cd51 | ||
|
|
e5d5c90aa9 | ||
|
|
5fad706cd8 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -391,3 +391,4 @@ FodyWeavers.xsd
|
||||
essentials-framework/Essentials Interfaces/PepperDash_Essentials_Interfaces/PepperDash_Essentials_Interfaces.csproj
|
||||
.DS_Store
|
||||
/._PepperDash.Essentials.sln
|
||||
.vscode/settings.json
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharpPro.EthernetCommunication;
|
||||
@@ -168,7 +168,7 @@ namespace PepperDash.Essentials.Core.Bridges
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Linking Device: '{0}'", device.Key);
|
||||
|
||||
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetCType()))
|
||||
if (!typeof(IBridgeAdvanced).IsAssignableFrom(device.GetType().GetType()))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, this,
|
||||
"{0} is not compatible with this bridge type. Please use 'eiscapi' instead, or updae the device.",
|
||||
|
||||
@@ -23,12 +23,12 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (objectType == typeof(ComPort.ComPortSpec))
|
||||
if (objectType == typeof(ComPort.ComPortSpec?))
|
||||
{
|
||||
var newSer = new JsonSerializer();
|
||||
newSer.Converters.Add(new ComSpecPropsJsonConverter());
|
||||
newSer.ObjectCreationHandling = ObjectCreationHandling.Replace;
|
||||
return newSer.Deserialize<ComPort.ComPortSpec>(reader);
|
||||
return newSer.Deserialize<ComPort.ComPortSpec?>(reader);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(ComPort.ComPortSpec);
|
||||
return objectType == typeof(ComPort.ComPortSpec?);
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return true; } }
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace PepperDash.Essentials.Core
|
||||
switch (controlConfig.Method)
|
||||
{
|
||||
case eControlMethod.Com:
|
||||
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams, controlConfig);
|
||||
comm = new ComPortController(deviceConfig.Key + "-com", GetComPort, controlConfig.ComParams.Value, controlConfig);
|
||||
break;
|
||||
case eControlMethod.Cec:
|
||||
comm = new CecPortController(deviceConfig.Key + "-cec", GetCecPort, controlConfig);
|
||||
@@ -115,7 +115,7 @@ namespace PepperDash.Essentials.Core
|
||||
var comPar = config.ComParams;
|
||||
var dev = GetIComPortsDeviceFromManagedDevice(config.ControlPortDevKey);
|
||||
if (dev != null && config.ControlPortNumber <= dev.NumberOfComPorts)
|
||||
return dev.ComPorts[config.ControlPortNumber];
|
||||
return dev.ComPorts[config.ControlPortNumber.Value];
|
||||
Debug.LogMessage(LogEventLevel.Information, "GetComPort: Device '{0}' does not have com port {1}", config.ControlPortDevKey, config.ControlPortNumber);
|
||||
return null;
|
||||
}
|
||||
@@ -201,23 +201,26 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class EssentialsControlPropertiesConfig :
|
||||
PepperDash.Core.ControlPropertiesConfig
|
||||
public class EssentialsControlPropertiesConfig :
|
||||
ControlPropertiesConfig
|
||||
{
|
||||
|
||||
[JsonProperty("comParams", NullValueHandling = NullValueHandling.Ignore)]
|
||||
[JsonConverter(typeof(ComSpecJsonConverter))]
|
||||
public ComPort.ComPortSpec ComParams { get; set; }
|
||||
public ComPort.ComPortSpec? ComParams { get; set; }
|
||||
|
||||
public string CresnetId { get; set; }
|
||||
[JsonProperty("cresnetId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string CresnetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to provide uint conversion of string CresnetId
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint CresnetIdInt
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
try
|
||||
{
|
||||
return Convert.ToUInt32(CresnetId, 16);
|
||||
}
|
||||
@@ -228,11 +231,13 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty("infinetId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string InfinetId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Attepmts to provide uiont conversion of string InifinetId
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public uint InfinetIdInt
|
||||
{
|
||||
get
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Net.Http;
|
||||
using Crestron.SimplSharp.Net.Http;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.DebugThings;
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
|
||||
[Obsolete("Please use the builtin HttpClient class instead: https://learn.microsoft.com/en-us/dotnet/fundamentals/networking/http/httpclient-guidelines")]
|
||||
public class GenericHttpClient : Device, IBasicCommunication
|
||||
{
|
||||
public HttpClient Client;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Crestron.SimplSharpPro;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Config
|
||||
{
|
||||
public class AudioControlPointListItem
|
||||
{
|
||||
[JsonProperty("levelControls")]
|
||||
public Dictionary<string, LevelControlListItem> LevelControls { get; set; } = new Dictionary<string, LevelControlListItem>();
|
||||
|
||||
[JsonProperty("presets")]
|
||||
public Dictionary<string, PresetListItem> Presets { get; set; } = new Dictionary<string, PresetListItem>();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,10 @@ namespace PepperDash.Essentials.Core.Config
|
||||
public Dictionary<string, Dictionary<string, SourceListItem>> SourceLists { get; set; }
|
||||
|
||||
[JsonProperty("destinationLists")]
|
||||
public Dictionary<string, Dictionary<string,DestinationListItem>> DestinationLists { get; set; }
|
||||
public Dictionary<string, Dictionary<string, DestinationListItem>> DestinationLists { get; set; }
|
||||
|
||||
[JsonProperty("audioControlPointLists")]
|
||||
public Dictionary<string, AudioControlPointListItem> AudioControlPointLists { get; set; }
|
||||
|
||||
[JsonProperty("tieLines")]
|
||||
public List<TieLineConfig> TieLines { get; set; }
|
||||
@@ -37,6 +40,7 @@ namespace PepperDash.Essentials.Core.Config
|
||||
Devices = new List<DeviceConfig>();
|
||||
SourceLists = new Dictionary<string, Dictionary<string, SourceListItem>>();
|
||||
DestinationLists = new Dictionary<string, Dictionary<string, DestinationListItem>>();
|
||||
AudioControlPointLists = new Dictionary<string, AudioControlPointListItem>();
|
||||
TieLines = new List<TieLineConfig>();
|
||||
JoinMaps = new Dictionary<string, JObject>();
|
||||
}
|
||||
@@ -55,8 +59,8 @@ namespace PepperDash.Essentials.Core.Config
|
||||
/// <summary>
|
||||
/// Retrieves a DestinationListItem based on the key
|
||||
/// </summary>
|
||||
/// <param name="key">key of the item to retrieve</param>
|
||||
/// <returns>DestinationListItem if the key exists, null otherwise</returns>
|
||||
/// <param name="key">key of the list to retrieve</param>
|
||||
/// <returns>DestinationList if the key exists, null otherwise</returns>
|
||||
public Dictionary<string, DestinationListItem> GetDestinationListForKey(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key) || !DestinationLists.ContainsKey(key))
|
||||
@@ -65,7 +69,20 @@ namespace PepperDash.Essentials.Core.Config
|
||||
}
|
||||
|
||||
return DestinationLists[key];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a AudioControlPointList based on the key
|
||||
/// </summary>
|
||||
/// <param name="key">key of the list to retrieve</param>
|
||||
/// <returns>AudioControlPointList if the key exists, null otherwise</returns>
|
||||
public AudioControlPointListItem GetAudioControlPointListForKey(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key) || !AudioControlPointLists.ContainsKey(key))
|
||||
return null;
|
||||
|
||||
return AudioControlPointLists[key];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks Devices for an item with a Key that matches and returns it if found. Otherwise, retunes null
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
|
||||
|
||||
using Crestron.SimplSharp;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the info section of a Config file
|
||||
/// </summary>
|
||||
public class InfoConfig
|
||||
/// <summary>
|
||||
/// Represents the info section of a Config file
|
||||
/// </summary>
|
||||
public class InfoConfig
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
@@ -15,10 +15,22 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
/// <example>
|
||||
/// See MockDisplay for example implemntation
|
||||
/// </example>
|
||||
public interface IHasInputs<TKey, TSelector>: IKeyName
|
||||
[Obsolete("Use IHasInputs<T> instead. Will be removed for 2.0 release")]
|
||||
public interface IHasInputs<T, TSelector>: IKeyName
|
||||
{
|
||||
ISelectableItems<TKey> Inputs { get; }
|
||||
ISelectableItems<T> Inputs { get; }
|
||||
}
|
||||
|
||||
void SetInput(TSelector selector);
|
||||
|
||||
/// <summary>
|
||||
/// Describes a device that has selectable inputs
|
||||
/// </summary>
|
||||
/// <typeparam name="T">the type to use as the key for each input item. Most likely an enum or string</typeparam>\
|
||||
/// <example>
|
||||
/// See MockDisplay for example implemntation
|
||||
/// </example>
|
||||
public interface IHasInputs<T> : IKeyName
|
||||
{
|
||||
ISelectableItems<T> Inputs { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,10 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
void AddDeviceMessenger(IMobileControlMessenger messenger);
|
||||
|
||||
bool CheckForDeviceMessenger(string key);
|
||||
}
|
||||
|
||||
IMobileControlRoomMessenger GetRoomMessenger(string key);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a mobile control messenger
|
||||
@@ -106,4 +109,11 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
Action<string,string, JToken> Action { get; }
|
||||
}
|
||||
|
||||
public interface IMobileControlTouchpanelController : IKeyed
|
||||
{
|
||||
string DefaultRoomKey { get; }
|
||||
void SetAppUrl(string url);
|
||||
bool UseDirectServer { get; }
|
||||
bool ZoomRoomController { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class that has warm up and cool down
|
||||
/// </summary>
|
||||
public interface IProjectorScreenLiftControl
|
||||
{
|
||||
void Raise();
|
||||
void Lower();
|
||||
BoolFeedback IsInUpPosition { get; }
|
||||
bool InUpPosition { get; }
|
||||
event EventHandler<EventArgs> PositionChanged;
|
||||
string DisplayDeviceKey { get; }
|
||||
eScreenLiftControlType Type { get; } // screen/lift
|
||||
}
|
||||
|
||||
public enum eScreenLiftControlType
|
||||
{
|
||||
lift,
|
||||
screen
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,6 @@ namespace PepperDash.Essentials.Core.DeviceTypeInterfaces
|
||||
Dictionary<TKey, ISelectableItem> Items { get; set; }
|
||||
|
||||
[JsonProperty("currentItem")]
|
||||
string CurrentItem { get; set; }
|
||||
TKey CurrentItem { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public abstract class AudioControlListItemBase
|
||||
{
|
||||
[JsonProperty("parentDeviceKey")]
|
||||
public string ParentDeviceKey { get; set; }
|
||||
|
||||
[JsonProperty("itemKey")]
|
||||
public string ItemKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A name that will override the items's name on the UI
|
||||
/// </summary>
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the item should be included in the user accessible list
|
||||
/// </summary>
|
||||
[JsonProperty("includeInUserList")]
|
||||
public bool IncludeInUserList { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used to specify the order of the items in the source list when displayed
|
||||
/// </summary>
|
||||
[JsonProperty("order")]
|
||||
public int Order { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,9 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using PepperDash.Core;
|
||||
@@ -63,7 +64,7 @@ namespace PepperDash.Essentials.Core
|
||||
action.Params = new object[0];
|
||||
}
|
||||
|
||||
CType t = obj.GetType();
|
||||
Type t = obj.GetType();
|
||||
try
|
||||
{
|
||||
var methods = t.GetMethods().Where(m => m.Name == action.MethodName).ToList();
|
||||
@@ -82,7 +83,8 @@ namespace PepperDash.Essentials.Core
|
||||
var convertedParams = mParams
|
||||
.Select((p, i) => ConvertType(action.Params[i], p.ParameterType))
|
||||
.ToArray();
|
||||
method.Invoke(obj, convertedParams);
|
||||
|
||||
Task.Run(() => method.Invoke(obj, convertedParams));
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("Method {0} successfully called on device {1}", method.Name,
|
||||
action.DeviceKey);
|
||||
@@ -121,7 +123,7 @@ namespace PepperDash.Essentials.Core
|
||||
if (obj == null)
|
||||
return "{ \"error\":\"No Device\"}";
|
||||
|
||||
CType t = obj.GetType();
|
||||
Type t = obj.GetType();
|
||||
// get the properties and set them into a new collection of NameType wrappers
|
||||
var props = t.GetProperties().Select(p => new PropertyNameType(p, obj));
|
||||
return JsonConvert.SerializeObject(props, Formatting.Indented);
|
||||
@@ -139,7 +141,7 @@ namespace PepperDash.Essentials.Core
|
||||
if(dev == null)
|
||||
return "{ \"error\":\"No Device\"}";
|
||||
|
||||
object prop = dev.GetType().GetCType().GetProperty(propertyName).GetValue(dev, null);
|
||||
object prop = dev.GetType().GetType().GetProperty(propertyName).GetValue(dev, null);
|
||||
|
||||
// var prop = t.GetProperty(propertyName);
|
||||
if (prop != null)
|
||||
@@ -165,7 +167,7 @@ namespace PepperDash.Essentials.Core
|
||||
return "{ \"error\":\"No Device\"}";
|
||||
|
||||
// Package up method names using helper objects
|
||||
CType t = obj.GetType();
|
||||
Type t = obj.GetType();
|
||||
var methods = t.GetMethods()
|
||||
.Where(m => !m.IsSpecialName)
|
||||
.Select(p => new MethodNameParams(p));
|
||||
@@ -179,7 +181,7 @@ namespace PepperDash.Essentials.Core
|
||||
return "{ \"error\":\"No Device\"}";
|
||||
|
||||
// Package up method names using helper objects
|
||||
CType t = obj.GetType();
|
||||
Type t = obj.GetType();
|
||||
var methods = t.GetMethods()
|
||||
.Where(m => !m.IsSpecialName)
|
||||
.Where(m => m.GetCustomAttributes(typeof(ApiAttribute), true).Any())
|
||||
@@ -226,7 +228,7 @@ namespace PepperDash.Essentials.Core
|
||||
Debug.LogMessage(LogEventLevel.Information, dev, " Checking for collection '{0}', index '{1}'", objName, indexStr);
|
||||
}
|
||||
|
||||
CType oType = obj.GetType();
|
||||
Type oType = obj.GetType();
|
||||
var prop = oType.GetProperty(objName);
|
||||
if (prop == null)
|
||||
{
|
||||
@@ -256,7 +258,7 @@ namespace PepperDash.Essentials.Core
|
||||
obj = indexedPropInfo.GetValue(collection, new object[] { properParam });
|
||||
}
|
||||
// if the index is bad, catch it here.
|
||||
catch (Crestron.SimplSharp.Reflection.TargetInvocationException e)
|
||||
catch (TargetInvocationException e)
|
||||
{
|
||||
if (e.InnerException is ArgumentOutOfRangeException)
|
||||
Debug.LogMessage(LogEventLevel.Information, " Index Out of range");
|
||||
@@ -287,7 +289,7 @@ namespace PepperDash.Essentials.Core
|
||||
//if (obj == null)
|
||||
// return "{\"error\":\"No object found\"}";
|
||||
|
||||
//CType t = obj.GetType();
|
||||
//Type t = obj.GetType();
|
||||
|
||||
|
||||
//// get the properties and set them into a new collection of NameType wrappers
|
||||
@@ -365,7 +367,7 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class ApiAttribute : CAttribute
|
||||
public class ApiAttribute : Attribute
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace PepperDash.Essentials.Core
|
||||
// var dev = GetDeviceForKey(devKey);
|
||||
// if(dev != null)
|
||||
// {
|
||||
// var type = dev.GetType().GetCType();
|
||||
// var type = dev.GetType().GetType();
|
||||
// var methods = type.GetMethods(BindingFlags.Public|BindingFlags.Instance);
|
||||
// var sb = new StringBuilder();
|
||||
// sb.AppendLine(string.Format("{2} methods on [{0}] ({1}):", dev.Key, type.Name, methods.Length));
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public interface IHasDspPresets
|
||||
{
|
||||
List<IDspPreset> Presets { get; }
|
||||
|
||||
void RecallPreset(IDspPreset preset);
|
||||
|
||||
}
|
||||
|
||||
public interface IDspPreset
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
12
src/PepperDash.Essentials.Core/Devices/IDspPresets.cs
Normal file
12
src/PepperDash.Essentials.Core/Devices/IDspPresets.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using PepperDash.Core;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public interface IDspPresets
|
||||
{
|
||||
Dictionary<string, IKeyName> Presets { get; }
|
||||
|
||||
void RecallPreset(string key);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
@@ -23,7 +21,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public static void DumpFeedbacksToConsole(this IHasFeedback source, bool getCurrentStates)
|
||||
{
|
||||
CType t = source.GetType();
|
||||
Type t = source.GetType();
|
||||
// get the properties and set them into a new collection of NameType wrappers
|
||||
var props = t.GetProperties().Select(p => new PropertyNameType(p, t));
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Devices;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class LevelControlListItem : AudioControlListItemBase
|
||||
{
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
public IBasicVolumeWithFeedback LevelControl
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_levelControl == null)
|
||||
_levelControl = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IBasicVolumeWithFeedback;
|
||||
return _levelControl;
|
||||
}
|
||||
}
|
||||
IBasicVolumeWithFeedback _levelControl;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name from the device if it implements IKeyName or else returns the Name property
|
||||
/// </summary>
|
||||
[JsonProperty("preferredName")]
|
||||
public string PreferredName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Name)) return Name;
|
||||
else
|
||||
{
|
||||
if (LevelControl is IKeyName namedLevelControl)
|
||||
{
|
||||
if (namedLevelControl == null)
|
||||
return "---";
|
||||
return namedLevelControl.Name;
|
||||
}
|
||||
else return "---";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The key of the device in the DeviceManager for control
|
||||
/// </summary>
|
||||
[JsonProperty("deviceKey")]
|
||||
public string DeviceKey => DeviceManager.AllDevices.
|
||||
Where(d => d.Key.Contains(ParentDeviceKey) && d.Key.Contains(ItemKey)).FirstOrDefault()?.Key ?? $"{ParentDeviceKey}--{ItemKey}";
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the item is a level, mute , or both
|
||||
/// </summary>
|
||||
[JsonProperty("type")]
|
||||
[JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public eLevelControlType Type { get; set; }
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum eLevelControlType
|
||||
{
|
||||
Level = 0,
|
||||
Mute = 1,
|
||||
LevelAndMute = Level | Mute,
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using Crestron.SimplSharpPro;
|
||||
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Crestron.SimplSharpPro;
|
||||
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
@@ -70,7 +69,7 @@ namespace PepperDash.Essentials.Core.Devices
|
||||
{
|
||||
public LaptopFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "laptop" };
|
||||
TypeNames = new List<string>() { "deprecated" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
|
||||
45
src/PepperDash.Essentials.Core/Devices/PresetListItem.cs
Normal file
45
src/PepperDash.Essentials.Core/Devices/PresetListItem.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class PresetListItem : AudioControlListItemBase
|
||||
{
|
||||
[JsonIgnore]
|
||||
public IKeyName Preset
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_preset == null)
|
||||
{
|
||||
var parent = DeviceManager.GetDeviceForKey(ParentDeviceKey) as IDspPresets;
|
||||
if (parent == null || !parent.Presets.ContainsKey(ItemKey))
|
||||
return null;
|
||||
_preset = parent.Presets[ItemKey];
|
||||
}
|
||||
return _preset;
|
||||
}
|
||||
}
|
||||
private IKeyName _preset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name from the device if it implements IKeyName or else returns the Name property
|
||||
/// </summary>
|
||||
[JsonProperty("preferredName")]
|
||||
public string PreferredName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Name)) return Name;
|
||||
|
||||
else return Preset.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,22 +144,43 @@ namespace PepperDash.Essentials.Core
|
||||
[JsonProperty("isAudioSource")]
|
||||
public bool IsAudioSource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hide source on UI when Avanced Sharing is enabled
|
||||
/// </summary>
|
||||
[JsonProperty("disableAdvancedRouting")]
|
||||
public bool DisableAdvancedRouting { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Hide source on UI when Simpl Sharing is enabled
|
||||
/// </summary>
|
||||
[JsonProperty("disableSimpleRouting")]
|
||||
public bool DisableSimpleRouting { get; set; }
|
||||
|
||||
public SourceListItem()
|
||||
{
|
||||
Icon = "Blank";
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{SourceKey}:{Name}";
|
||||
}
|
||||
}
|
||||
|
||||
public class SourceRouteListItem
|
||||
{
|
||||
[JsonProperty("sourceKey")]
|
||||
public string SourceKey { get; set; }
|
||||
|
||||
[JsonProperty("sourcePortKey")]
|
||||
public string SourcePortKey { get; set; }
|
||||
|
||||
[JsonProperty("destinationKey")]
|
||||
public string DestinationKey { get; set; }
|
||||
|
||||
[JsonProperty("destinationPortKey")]
|
||||
public string DestinationPortKey { get; set; }
|
||||
|
||||
[JsonProperty("type")]
|
||||
public eRoutingSignalType Type { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Core.Bridges;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
using Crestron.SimplSharpPro.DM.Endpoints;
|
||||
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Bridges;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
|
||||
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
|
||||
public abstract class DisplayBase : EssentialsDevice, IHasFeedback, IRoutingSinkWithSwitching, IHasPowerControl, IWarmingCooling, IUsageTracking
|
||||
{
|
||||
public event SourceInfoChangeHandler CurrentSourceChange;
|
||||
public event InputChangedEventHandler InputChanged;
|
||||
|
||||
public string CurrentSourceInfoKey { get; set; }
|
||||
public SourceListItem CurrentSourceInfo
|
||||
@@ -96,7 +94,9 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void ExecuteSwitch(object selector);
|
||||
public RoutingInputPort CurrentInputPort => throw new NotImplementedException();
|
||||
|
||||
public abstract void ExecuteSwitch(object selector);
|
||||
|
||||
protected void LinkDisplayToApi(DisplayBase displayDevice, BasicTriList trilist, uint joinStart, string joinMapKey,
|
||||
EiscApiAdvanced bridge)
|
||||
@@ -266,16 +266,16 @@ namespace PepperDash.Essentials.Core
|
||||
abstract protected Func<bool> PowerIsOnFeedbackFunc { get; }
|
||||
|
||||
|
||||
public static MockDisplay DefaultDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultDisplay == null)
|
||||
_DefaultDisplay = new MockDisplay("default", "Default Display");
|
||||
return _DefaultDisplay;
|
||||
}
|
||||
}
|
||||
static MockDisplay _DefaultDisplay;
|
||||
// public static MockDisplay DefaultDisplay
|
||||
// {
|
||||
// get
|
||||
// {
|
||||
// if (_DefaultDisplay == null)
|
||||
// _DefaultDisplay = new MockDisplay("default", "Default Display");
|
||||
// return _DefaultDisplay;
|
||||
// }
|
||||
//}
|
||||
//static MockDisplay _DefaultDisplay;
|
||||
|
||||
public TwoWayDisplayBase(string key, string name)
|
||||
: base(key, name)
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
using Crestron.SimplSharpPro.DM.Endpoints;
|
||||
using Crestron.SimplSharpPro.DM.Endpoints.Transmitters;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Bridges;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
|
||||
public class MockDisplay : TwoWayDisplayBase, IBasicVolumeWithFeedback, IBridgeAdvanced
|
||||
|
||||
{
|
||||
public RoutingInputPort HdmiIn1 { get; private set; }
|
||||
public RoutingInputPort HdmiIn2 { get; private set; }
|
||||
public RoutingInputPort HdmiIn3 { get; private set; }
|
||||
public RoutingInputPort ComponentIn1 { get; private set; }
|
||||
public RoutingInputPort VgaIn1 { get; private set; }
|
||||
|
||||
bool _PowerIsOn;
|
||||
bool _IsWarmingUp;
|
||||
bool _IsCoolingDown;
|
||||
|
||||
protected override Func<bool> PowerIsOnFeedbackFunc
|
||||
{
|
||||
get
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** Display Power is {0}", _PowerIsOn ? "on" : "off");
|
||||
return _PowerIsOn;
|
||||
};
|
||||
} }
|
||||
protected override Func<bool> IsCoolingDownFeedbackFunc
|
||||
{
|
||||
get
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsCoolingDown ? "Display is cooling down" : "Display has finished cooling down");
|
||||
return _IsCoolingDown;
|
||||
};
|
||||
}
|
||||
}
|
||||
protected override Func<bool> IsWarmingUpFeedbackFunc
|
||||
{
|
||||
get
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "*************************************************** {0}", _IsWarmingUp ? "Display is warming up" : "Display has finished warming up");
|
||||
return _IsWarmingUp;
|
||||
};
|
||||
}
|
||||
}
|
||||
protected override Func<string> CurrentInputFeedbackFunc { get { return () => "Not Implemented"; } }
|
||||
|
||||
int VolumeHeldRepeatInterval = 200;
|
||||
ushort VolumeInterval = 655;
|
||||
ushort _FakeVolumeLevel = 31768;
|
||||
bool _IsMuted;
|
||||
|
||||
public MockDisplay(string key, string name)
|
||||
: base(key, name)
|
||||
{
|
||||
HdmiIn1 = new RoutingInputPort(RoutingPortNames.HdmiIn1, eRoutingSignalType.Audio | eRoutingSignalType.Video,
|
||||
eRoutingPortConnectionType.Hdmi, null, this);
|
||||
HdmiIn2 = new RoutingInputPort(RoutingPortNames.HdmiIn2, eRoutingSignalType.Audio | eRoutingSignalType.Video,
|
||||
eRoutingPortConnectionType.Hdmi, null, this);
|
||||
HdmiIn3 = new RoutingInputPort(RoutingPortNames.HdmiIn3, eRoutingSignalType.Audio | eRoutingSignalType.Video,
|
||||
eRoutingPortConnectionType.Hdmi, null, this);
|
||||
ComponentIn1 = new RoutingInputPort(RoutingPortNames.ComponentIn, eRoutingSignalType.Video,
|
||||
eRoutingPortConnectionType.Component, null, this);
|
||||
VgaIn1 = new RoutingInputPort(RoutingPortNames.VgaIn, eRoutingSignalType.Video,
|
||||
eRoutingPortConnectionType.Composite, null, this);
|
||||
InputPorts.AddRange(new[] { HdmiIn1, HdmiIn2, HdmiIn3, ComponentIn1, VgaIn1 });
|
||||
|
||||
VolumeLevelFeedback = new IntFeedback(() => { return _FakeVolumeLevel; });
|
||||
MuteFeedback = new BoolFeedback("MuteOn", () => _IsMuted);
|
||||
|
||||
WarmupTime = 10000;
|
||||
CooldownTime = 10000;
|
||||
}
|
||||
|
||||
public override void PowerOn()
|
||||
{
|
||||
if (!PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
|
||||
{
|
||||
_IsWarmingUp = true;
|
||||
IsWarmingUpFeedback.InvokeFireUpdate();
|
||||
// Fake power-up cycle
|
||||
WarmupTimer = new CTimer(o =>
|
||||
{
|
||||
_IsWarmingUp = false;
|
||||
_PowerIsOn = true;
|
||||
IsWarmingUpFeedback.InvokeFireUpdate();
|
||||
PowerIsOnFeedback.InvokeFireUpdate();
|
||||
}, WarmupTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override void PowerOff()
|
||||
{
|
||||
// If a display has unreliable-power off feedback, just override this and
|
||||
// remove this check.
|
||||
if (PowerIsOnFeedback.BoolValue && !_IsWarmingUp && !_IsCoolingDown)
|
||||
{
|
||||
_IsCoolingDown = true;
|
||||
IsCoolingDownFeedback.InvokeFireUpdate();
|
||||
// Fake cool-down cycle
|
||||
CooldownTimer = new CTimer(o =>
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Cooldown timer ending");
|
||||
_IsCoolingDown = false;
|
||||
IsCoolingDownFeedback.InvokeFireUpdate();
|
||||
_PowerIsOn = false;
|
||||
PowerIsOnFeedback.InvokeFireUpdate();
|
||||
}, CooldownTime);
|
||||
}
|
||||
}
|
||||
|
||||
public override void PowerToggle()
|
||||
{
|
||||
if (PowerIsOnFeedback.BoolValue && !IsWarmingUpFeedback.BoolValue)
|
||||
PowerOff();
|
||||
else if (!PowerIsOnFeedback.BoolValue && !IsCoolingDownFeedback.BoolValue)
|
||||
PowerOn();
|
||||
}
|
||||
|
||||
public override void ExecuteSwitch(object selector)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "ExecuteSwitch: {0}", selector);
|
||||
|
||||
if (!_PowerIsOn)
|
||||
{
|
||||
PowerOn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IBasicVolumeWithFeedback Members
|
||||
|
||||
public IntFeedback VolumeLevelFeedback { get; private set; }
|
||||
|
||||
public void SetVolume(ushort level)
|
||||
{
|
||||
_FakeVolumeLevel = level;
|
||||
VolumeLevelFeedback.InvokeFireUpdate();
|
||||
}
|
||||
|
||||
public void MuteOn()
|
||||
{
|
||||
_IsMuted = true;
|
||||
MuteFeedback.InvokeFireUpdate();
|
||||
}
|
||||
|
||||
public void MuteOff()
|
||||
{
|
||||
_IsMuted = false;
|
||||
MuteFeedback.InvokeFireUpdate();
|
||||
}
|
||||
|
||||
public BoolFeedback MuteFeedback { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region IBasicVolumeControls Members
|
||||
|
||||
public void VolumeUp(bool pressRelease)
|
||||
{
|
||||
//while (pressRelease)
|
||||
//{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Down {0}", pressRelease);
|
||||
if (pressRelease)
|
||||
{
|
||||
var newLevel = _FakeVolumeLevel + VolumeInterval;
|
||||
SetVolume((ushort)newLevel);
|
||||
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
public void VolumeDown(bool pressRelease)
|
||||
{
|
||||
//while (pressRelease)
|
||||
//{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Volume Up {0}", pressRelease);
|
||||
if (pressRelease)
|
||||
{
|
||||
var newLevel = _FakeVolumeLevel - VolumeInterval;
|
||||
SetVolume((ushort)newLevel);
|
||||
CrestronEnvironment.Sleep(VolumeHeldRepeatInterval);
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
public void MuteToggle()
|
||||
{
|
||||
_IsMuted = !_IsMuted;
|
||||
MuteFeedback.InvokeFireUpdate();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void LinkToApi(BasicTriList trilist, uint joinStart, string joinMapKey, EiscApiAdvanced bridge)
|
||||
{
|
||||
LinkDisplayToApi(this, trilist, joinStart, joinMapKey, bridge);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[Obsolete("Please use PepperDash.Essentials.Devices.Common, this will be removed in 2.1")]
|
||||
public class MockDisplayFactory : EssentialsDeviceFactory<MockDisplay>
|
||||
{
|
||||
public MockDisplayFactory()
|
||||
{
|
||||
TypeNames = new List<string>() { "mockdisplay" };
|
||||
}
|
||||
|
||||
public override EssentialsDevice BuildDevice(DeviceConfig dc)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Factory Attempting to create new Mock Display Device");
|
||||
return new MockDisplay(dc.Key, dc.Name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
@@ -14,13 +14,13 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class DeviceFactoryWrapper
|
||||
{
|
||||
public CType CType { get; set; }
|
||||
public Type Type { get; set; }
|
||||
public string Description { get; set; }
|
||||
public Func<DeviceConfig, IKeyed> FactoryMethod { get; set; }
|
||||
|
||||
public DeviceFactoryWrapper()
|
||||
{
|
||||
CType = null;
|
||||
Type = null;
|
||||
Description = "Not Available";
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
|
||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||
factory.LoadTypeFactories();
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -69,7 +69,7 @@ namespace PepperDash.Essentials.Core
|
||||
DeviceFactory.FactoryMethods.Add(typeName, new DeviceFactoryWrapper() { FactoryMethod = method});
|
||||
}
|
||||
|
||||
public static void AddFactoryForType(string typeName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
|
||||
public static void AddFactoryForType(string typeName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
||||
{
|
||||
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace PepperDash.Essentials.Core
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
|
||||
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||
DeviceFactory.FactoryMethods.Add(typeName, wrapper);
|
||||
}
|
||||
|
||||
@@ -149,18 +149,7 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "Exception occurred while creating device {0}: {1}", dc.Key, ex.Message);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.StackTrace);
|
||||
|
||||
if (ex.InnerException == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Error, "Inner exception while creating device {0}: {1}", dc.Key,
|
||||
ex.InnerException.Message);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "{0}", ex.InnerException.StackTrace);
|
||||
Debug.LogMessage(ex, "Exception occurred while creating device {0}: {1}", null, dc.Key, ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -180,17 +169,17 @@ namespace PepperDash.Essentials.Core
|
||||
foreach (var type in types.OrderBy(t => t.Key))
|
||||
{
|
||||
var description = type.Value.Description;
|
||||
var cType = "Not Specified by Plugin";
|
||||
var Type = "Not Specified by Plugin";
|
||||
|
||||
if (type.Value.CType != null)
|
||||
if (type.Value.Type != null)
|
||||
{
|
||||
cType = type.Value.CType.FullName;
|
||||
Type = type.Value.Type.FullName;
|
||||
}
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse(
|
||||
@"Type: '{0}'
|
||||
CType: '{1}'
|
||||
Description: {2}{3}", type.Key, cType, description, CrestronEnvironment.NewLine);
|
||||
Type: '{1}'
|
||||
Description: {2}{3}", type.Key, Type, description, CrestronEnvironment.NewLine);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class that is capable of loading device types
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using Serilog.Events;
|
||||
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
try
|
||||
{
|
||||
var factory = (IProcessorExtensionDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(extension);
|
||||
var factory = (IProcessorExtensionDeviceFactory)Activator.CreateInstance(extension);
|
||||
factory.LoadFactories();
|
||||
}
|
||||
catch( Exception e )
|
||||
@@ -55,7 +55,7 @@ namespace PepperDash.Essentials.Core
|
||||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, new DeviceFactoryWrapper() { FactoryMethod = method });
|
||||
}
|
||||
|
||||
public static void AddFactoryForType(string extensionName, string description, CType cType, Func<DeviceConfig, IKeyed> method)
|
||||
public static void AddFactoryForType(string extensionName, string description, Type Type, Func<DeviceConfig, IKeyed> method)
|
||||
{
|
||||
//Debug.LogMessage(LogEventLevel.Debug, "Adding factory method for type '{0}'", typeName);
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace PepperDash.Essentials.Core
|
||||
return;
|
||||
}
|
||||
|
||||
var wrapper = new DeviceFactoryWrapper() { CType = cType, Description = description, FactoryMethod = method };
|
||||
var wrapper = new DeviceFactoryWrapper() { Type = Type, Description = description, FactoryMethod = method };
|
||||
ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods.Add(extensionName, wrapper);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.CrestronXml;
|
||||
@@ -14,6 +10,10 @@ using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using Serilog.Events;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Fusion
|
||||
{
|
||||
|
||||
@@ -137,6 +137,7 @@ namespace PepperDash.Essentials.Core
|
||||
public static void SetFilePathPrefix(string prefix)
|
||||
{
|
||||
FilePathPrefix = prefix;
|
||||
Debug.LogMessage(LogEventLevel.Information, "File Path Prefix set to '{0}'", FilePathPrefix);
|
||||
}
|
||||
|
||||
static string _AssemblyVersion;
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace PepperDash.Essentials.Core
|
||||
protected void AddJoins(Type type)
|
||||
{
|
||||
var fields =
|
||||
type.GetCType()
|
||||
type.GetType()
|
||||
.GetFields(BindingFlags.Public | BindingFlags.Instance)
|
||||
.Where(f => f.IsDefined(typeof (JoinNameAttribute), true));
|
||||
|
||||
@@ -501,7 +501,7 @@ namespace PepperDash.Essentials.Core
|
||||
public string GetNameAttribute(MemberInfo memberInfo)
|
||||
{
|
||||
var name = string.Empty;
|
||||
var attribute = (JoinNameAttribute)CAttribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
|
||||
var attribute = (JoinNameAttribute)Attribute.GetCustomAttribute(memberInfo, typeof(JoinNameAttribute));
|
||||
|
||||
if (attribute == null) return name;
|
||||
|
||||
@@ -514,7 +514,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
|
||||
[AttributeUsage(AttributeTargets.All)]
|
||||
public class JoinNameAttribute : CAttribute
|
||||
public class JoinNameAttribute : Attribute
|
||||
{
|
||||
private string _Name;
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace PepperDash.Essentials.Core.Monitoring
|
||||
_uptimePollTimer = null;
|
||||
}
|
||||
|
||||
private void PollUptime(object obj)
|
||||
public void PollUptime(object obj)
|
||||
{
|
||||
var consoleResponse = string.Empty;
|
||||
|
||||
@@ -142,19 +142,22 @@ namespace PepperDash.Essentials.Core.Monitoring
|
||||
_uptime = uptimeRaw.Substring(forIndex + 4);
|
||||
}
|
||||
|
||||
private static void ProcessorReboot()
|
||||
public static void ProcessorReboot()
|
||||
{
|
||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
|
||||
Debug.LogMessage(LogEventLevel.Information, "Rebooting...");
|
||||
|
||||
var response = string.Empty;
|
||||
|
||||
var response = string.Empty;
|
||||
CrestronConsole.SendControlSystemCommand("reboot", ref response);
|
||||
}
|
||||
|
||||
private static void ProgramReset(uint index)
|
||||
public static void ProgramReset(uint index)
|
||||
{
|
||||
if (CrestronEnvironment.DevicePlatform == eDevicePlatform.Server) return;
|
||||
Debug.LogMessage(LogEventLevel.Information, "Resetting Program {0}...", index);
|
||||
|
||||
if (index <= 0 || index > 10) return;
|
||||
if (index <= 0 || index > 10) return;
|
||||
|
||||
var cmd = string.Format("progreset -p:{0}", index);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
@@ -17,9 +18,31 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
private IPartitionStateProvider _partitionSensor;
|
||||
|
||||
private bool isInAutoMode;
|
||||
public bool IsInAutoMode { get; private set; }
|
||||
|
||||
private bool partitionPresent;
|
||||
private bool _partitionPresent;
|
||||
|
||||
public bool PartitionPresent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _partitionPresent;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_partitionPresent == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_partitionPresent = value;
|
||||
|
||||
if (PartitionPresentFeedback != null)
|
||||
{
|
||||
PartitionPresentFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EssentialsPartitionController(string key, string name, IPartitionStateProvider sensor, bool defaultToManualMode, List<string> adjacentRoomKeys)
|
||||
{
|
||||
@@ -52,7 +75,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
void PartitionPresentFeedback_OutputChange(object sender, FeedbackEventArgs e)
|
||||
{
|
||||
if (isInAutoMode)
|
||||
if (IsInAutoMode)
|
||||
{
|
||||
PartitionPresentFeedback.FireUpdate();
|
||||
}
|
||||
@@ -64,7 +87,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public void SetAutoMode()
|
||||
{
|
||||
isInAutoMode = true;
|
||||
IsInAutoMode = true;
|
||||
if (PartitionPresentFeedback != null)
|
||||
{
|
||||
PartitionPresentFeedback.SetValueFunc(() => _partitionSensor.PartitionPresentFeedback.BoolValue);
|
||||
@@ -76,20 +99,21 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
if (_partitionSensor != null)
|
||||
{
|
||||
_partitionSensor.PartitionPresentFeedback.OutputChange -= PartitionPresentFeedback_OutputChange;
|
||||
_partitionSensor.PartitionPresentFeedback.OutputChange += PartitionPresentFeedback_OutputChange;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetManualMode()
|
||||
{
|
||||
isInAutoMode = false;
|
||||
IsInAutoMode = false;
|
||||
if (PartitionPresentFeedback != null)
|
||||
{
|
||||
PartitionPresentFeedback.SetValueFunc(() => partitionPresent);
|
||||
PartitionPresentFeedback.SetValueFunc(() => _partitionPresent);
|
||||
}
|
||||
else
|
||||
{
|
||||
PartitionPresentFeedback = new BoolFeedback(() => partitionPresent);
|
||||
PartitionPresentFeedback = new BoolFeedback(() => _partitionPresent);
|
||||
}
|
||||
|
||||
if (_partitionSensor != null)
|
||||
@@ -101,27 +125,30 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public void SetPartitionStatePresent()
|
||||
{
|
||||
if (!isInAutoMode)
|
||||
if (!IsInAutoMode)
|
||||
{
|
||||
partitionPresent = true;
|
||||
PartitionPresent = true;
|
||||
PartitionPresentFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPartitionStateNotPresent()
|
||||
{
|
||||
if (!isInAutoMode)
|
||||
if (!IsInAutoMode)
|
||||
{
|
||||
partitionPresent = false;
|
||||
PartitionPresent = false;
|
||||
PartitionPresentFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggglePartitionState()
|
||||
{
|
||||
if (!isInAutoMode)
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"Toggling Partition State for {Key}", this);
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, $"IsInAutoMode: {IsInAutoMode}", this);
|
||||
|
||||
if (!IsInAutoMode)
|
||||
{
|
||||
partitionPresent = !partitionPresent;
|
||||
PartitionPresent = !PartitionPresent;
|
||||
PartitionPresentFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
@@ -13,7 +9,11 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public interface IPartitionStateProvider : IKeyName
|
||||
{
|
||||
[JsonIgnore]
|
||||
BoolFeedback PartitionPresentFeedback { get; }
|
||||
|
||||
[JsonProperty("partitionPresent")]
|
||||
bool PartitionPresent { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -21,8 +21,12 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public interface IPartitionController : IPartitionStateProvider
|
||||
{
|
||||
[JsonProperty("adjacentRoomKeys")]
|
||||
List<string> AdjacentRoomKeys { get; }
|
||||
|
||||
[JsonProperty("isInAutoMode")]
|
||||
bool IsInAutoMode { get; }
|
||||
|
||||
void SetPartitionStatePresent();
|
||||
|
||||
void SetPartitionStateNotPresent();
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
<RootNamespace>PepperDash.Essentials.Core</RootNamespace>
|
||||
<Title>PepperDash Essentials Core</Title>
|
||||
<PackageId>PepperDash.Essentials.Core</PackageId>
|
||||
<InformationalVersion>$(Version)</InformationalVersion>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<Version>2.0.0-local</Version>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugType>full</DebugType>
|
||||
@@ -22,8 +25,8 @@
|
||||
<DebugType>pdbonly</DebugType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.42" />
|
||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-402" />
|
||||
<PackageReference Include="Crestron.SimplSharp.SDK.ProgramLibrary" Version="2.20.66" />
|
||||
<PackageReference Include="PepperDashCore" Version="2.0.0-alpha-424" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Crestron\CrestronGenericBaseDevice.cs.orig" />
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.CrestronIO;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using Serilog.Events;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials
|
||||
{
|
||||
@@ -27,24 +27,31 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
static List<LoadedAssembly> LoadedPluginFolderAssemblies;
|
||||
|
||||
public static LoadedAssembly EssentialsAssembly { get; private set; }
|
||||
|
||||
public static LoadedAssembly PepperDashCoreAssembly { get; private set; }
|
||||
|
||||
public static List<LoadedAssembly> EssentialsPluginAssemblies { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The directory to look in for .cplz plugin packages
|
||||
/// </summary>
|
||||
static string _pluginDirectory = Global.FilePathPrefix + "plugins";
|
||||
static string _pluginDirectory => Global.FilePathPrefix + "plugins";
|
||||
|
||||
/// <summary>
|
||||
/// The directory where plugins will be moved to and loaded from
|
||||
/// </summary>
|
||||
static string _loadedPluginsDirectoryPath = _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
|
||||
static string _loadedPluginsDirectoryPath => _pluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
|
||||
|
||||
// The temp directory where .cplz archives will be unzipped to
|
||||
static string _tempDirectory = _pluginDirectory + Global.DirectorySeparator + "temp";
|
||||
static string _tempDirectory => _pluginDirectory + Global.DirectorySeparator + "temp";
|
||||
|
||||
|
||||
static PluginLoader()
|
||||
{
|
||||
LoadedAssemblies = new List<LoadedAssembly>();
|
||||
LoadedPluginFolderAssemblies = new List<LoadedAssembly>();
|
||||
EssentialsPluginAssemblies = new List<LoadedAssembly>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,7 +66,7 @@ namespace PepperDash.Essentials
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length);
|
||||
|
||||
foreach (var fi in assemblyFiles)
|
||||
foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash")))
|
||||
{
|
||||
string version = string.Empty;
|
||||
Assembly assembly = null;
|
||||
@@ -69,6 +76,7 @@ namespace PepperDash.Essentials
|
||||
case ("PepperDashEssentials.dll"):
|
||||
{
|
||||
version = Global.AssemblyVersion;
|
||||
EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);
|
||||
break;
|
||||
}
|
||||
case ("PepperDash_Essentials_Core.dll"):
|
||||
@@ -81,9 +89,12 @@ namespace PepperDash.Essentials
|
||||
version = Global.AssemblyVersion;
|
||||
break;
|
||||
}
|
||||
case ("PepperDash_Core.dll"):
|
||||
case ("PepperDashCore.dll"):
|
||||
{
|
||||
version = PepperDash.Core.Debug.PepperDashCoreVersion;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll");
|
||||
version = Debug.PepperDashCoreVersion;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
|
||||
PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -119,24 +130,31 @@ namespace PepperDash.Essentials
|
||||
/// <param name="fileName"></param>
|
||||
static LoadedAssembly LoadAssembly(string filePath)
|
||||
{
|
||||
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
|
||||
var assembly = Assembly.LoadFrom(filePath);
|
||||
if (assembly != null)
|
||||
try
|
||||
{
|
||||
var assyVersion = GetAssemblyVersion(assembly);
|
||||
//Debug.LogMessage(LogEventLevel.Verbose, "Attempting to load {0}", filePath);
|
||||
var assembly = Assembly.LoadFrom(filePath);
|
||||
if (assembly != null)
|
||||
{
|
||||
var assyVersion = GetAssemblyVersion(assembly);
|
||||
|
||||
var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
|
||||
LoadedAssemblies.Add(loadedAssembly);
|
||||
Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
|
||||
return loadedAssembly;
|
||||
}
|
||||
else
|
||||
var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
|
||||
LoadedAssemblies.Add(loadedAssembly);
|
||||
Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
|
||||
return loadedAssembly;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
|
||||
Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,7 +162,7 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
/// <param name="assembly"></param>
|
||||
/// <returns></returns>
|
||||
static string GetAssemblyVersion(Assembly assembly)
|
||||
public static string GetAssemblyVersion(Assembly assembly)
|
||||
{
|
||||
var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
|
||||
if (ver != null && ver.Length > 0)
|
||||
@@ -190,12 +208,19 @@ namespace PepperDash.Essentials
|
||||
/// <param name="command"></param>
|
||||
public static void ReportAssemblyVersions(string command)
|
||||
{
|
||||
|
||||
CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
|
||||
foreach (var assembly in LoadedAssemblies)
|
||||
CrestronConsole.ConsoleCommandResponse("Essentials Version: {0}" + CrestronEnvironment.NewLine, Global.AssemblyVersion);
|
||||
CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);
|
||||
CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine);
|
||||
foreach (var assembly in EssentialsPluginAssemblies)
|
||||
{
|
||||
CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
|
||||
}
|
||||
|
||||
//CrestronConsole.ConsoleCommandResponse("Loaded Assemblies:" + CrestronEnvironment.NewLine);
|
||||
//foreach (var assembly in LoadedAssemblies)
|
||||
//{
|
||||
// CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
|
||||
//}
|
||||
}
|
||||
/// <summary>
|
||||
/// Moves any .dll assemblies not already loaded from the plugins folder to loadedPlugins folder
|
||||
@@ -354,7 +379,7 @@ namespace PepperDash.Essentials
|
||||
try
|
||||
{
|
||||
var assy = loadedAssembly.Assembly;
|
||||
CType[] types = {};
|
||||
Type[] types = {};
|
||||
try
|
||||
{
|
||||
types = assy.GetTypes();
|
||||
@@ -375,7 +400,7 @@ namespace PepperDash.Essentials
|
||||
if (typeof (IPluginDeviceFactory).IsAssignableFrom(type) && !type.IsAbstract)
|
||||
{
|
||||
var plugin =
|
||||
(IPluginDeviceFactory) Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
|
||||
(IPluginDeviceFactory)Activator.CreateInstance(type);
|
||||
LoadCustomPlugin(plugin, loadedAssembly);
|
||||
}
|
||||
}
|
||||
@@ -432,6 +457,9 @@ namespace PepperDash.Essentials
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Information, "Loading plugin: {0}", loadedAssembly.Name);
|
||||
plugin.LoadTypeFactories();
|
||||
|
||||
if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
|
||||
EssentialsPluginAssemblies.Add(loadedAssembly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -439,7 +467,7 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="loadPlugin"></param>
|
||||
static void LoadCustomLegacyPlugin(CType type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
|
||||
static void LoadCustomLegacyPlugin(Type type, MethodInfo loadPlugin, LoadedAssembly loadedAssembly)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "LoadPlugin method found in {0}", type.Name);
|
||||
|
||||
@@ -486,6 +514,8 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public static void LoadPlugins()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Attempting to Load Plugins from {_pluginDirectory}", _pluginDirectory);
|
||||
|
||||
if (Directory.Exists(_pluginDirectory))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins");
|
||||
@@ -514,8 +544,11 @@ namespace PepperDash.Essentials
|
||||
/// </summary>
|
||||
public class LoadedAssembly
|
||||
{
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; private set; }
|
||||
[JsonProperty("version")]
|
||||
public string Version { get; private set; }
|
||||
[JsonIgnore]
|
||||
public Assembly Assembly { get; private set; }
|
||||
|
||||
public LoadedAssembly(string name, string version, Assembly assembly)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
@@ -17,7 +17,33 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
private List<IEssentialsRoom> _rooms;
|
||||
|
||||
private bool isInAutoMode;
|
||||
public List<IKeyName> Rooms
|
||||
{
|
||||
get
|
||||
{
|
||||
return _rooms.Cast<IKeyName>().ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private bool _isInAutoMode;
|
||||
|
||||
public bool IsInAutoMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isInAutoMode;
|
||||
}
|
||||
set
|
||||
{
|
||||
if(value == _isInAutoMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isInAutoMode = value;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private CTimer _scenarioChangeDebounceTimer;
|
||||
|
||||
@@ -36,14 +62,14 @@ namespace PepperDash.Essentials.Core
|
||||
_scenarioChangeDebounceTimeSeconds = _propertiesConfig.ScenarioChangeDebounceTimeSeconds;
|
||||
}
|
||||
|
||||
IsInAutoModeFeedback = new BoolFeedback(() => isInAutoMode);
|
||||
IsInAutoModeFeedback = new BoolFeedback(() => _isInAutoMode);
|
||||
|
||||
// default to auto mode
|
||||
isInAutoMode = true;
|
||||
IsInAutoMode = true;
|
||||
|
||||
if (_propertiesConfig.defaultToManualMode)
|
||||
{
|
||||
isInAutoMode = false;
|
||||
IsInAutoMode = false;
|
||||
}
|
||||
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
@@ -56,7 +82,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
SetRooms();
|
||||
|
||||
if (isInAutoMode)
|
||||
if (IsInAutoMode)
|
||||
{
|
||||
DetermineRoomCombinationScenario();
|
||||
}
|
||||
@@ -111,7 +137,11 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
void StartDebounceTimer()
|
||||
{
|
||||
var time = _scenarioChangeDebounceTimeSeconds * 1000;
|
||||
// default to 500ms for manual mode
|
||||
var time = 500;
|
||||
|
||||
// if in auto mode, debounce the scenario change
|
||||
if(IsInAutoMode) time = _scenarioChangeDebounceTimeSeconds * 1000;
|
||||
|
||||
if (_scenarioChangeDebounceTimer == null)
|
||||
{
|
||||
@@ -185,7 +215,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
_currentScenario.Activate();
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, this, "Current Scenario: {0}", _currentScenario.Name);
|
||||
Debug.LogMessage(LogEventLevel.Debug, $"Current Scenario: {_currentScenario.Name}", this);
|
||||
}
|
||||
|
||||
var handler = RoomCombinationScenarioChanged;
|
||||
@@ -201,20 +231,17 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public void SetAutoMode()
|
||||
{
|
||||
isInAutoMode = true;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
IsInAutoMode = true;
|
||||
}
|
||||
|
||||
public void SetManualMode()
|
||||
{
|
||||
isInAutoMode = false;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
IsInAutoMode = false;
|
||||
}
|
||||
|
||||
public void ToggleMode()
|
||||
{
|
||||
isInAutoMode = !isInAutoMode;
|
||||
IsInAutoModeFeedback.FireUpdate();
|
||||
IsInAutoMode = !IsInAutoMode;
|
||||
}
|
||||
|
||||
public List<IRoomCombinationScenario> RoomCombinationScenarios { get; private set; }
|
||||
@@ -223,7 +250,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public void TogglePartitionState(string partitionKey)
|
||||
{
|
||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey)) as IPartitionController;
|
||||
var partition = Partitions.FirstOrDefault((p) => p.Key.Equals(partitionKey));
|
||||
|
||||
if (partition != null)
|
||||
{
|
||||
@@ -233,7 +260,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public void SetRoomCombinationScenario(string scenarioKey)
|
||||
{
|
||||
if (isInAutoMode)
|
||||
if (IsInAutoMode)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Cannot set room combination scenario when in auto mode. Set to auto mode first.");
|
||||
return;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
@@ -21,13 +18,21 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// The current room combination scenario
|
||||
/// </summary>
|
||||
[JsonProperty("currentScenario")]
|
||||
IRoomCombinationScenario CurrentScenario { get; }
|
||||
|
||||
/// <summary>
|
||||
/// When true, indicates the current mode is auto mode
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
BoolFeedback IsInAutoModeFeedback {get;}
|
||||
|
||||
[JsonProperty("isInAutoMode")]
|
||||
bool IsInAutoMode { get; }
|
||||
|
||||
[JsonProperty("rooms")]
|
||||
List<IKeyName> Rooms { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets auto mode
|
||||
/// </summary>
|
||||
@@ -46,11 +51,13 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// The available room combinatino scenarios
|
||||
/// </summary>
|
||||
[JsonProperty("roomCombinationScenarios")]
|
||||
List<IRoomCombinationScenario> RoomCombinationScenarios { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The partition
|
||||
/// </summary>
|
||||
[JsonProperty("partitions")]
|
||||
List<IPartitionController> Partitions { get; }
|
||||
|
||||
/// <summary>
|
||||
@@ -71,8 +78,12 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// When true, indicates that this room combination scenario is active
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
BoolFeedback IsActiveFeedback { get; }
|
||||
|
||||
[JsonProperty("isActive")]
|
||||
bool IsActive { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Activates this room combination scenario
|
||||
/// </summary>
|
||||
@@ -86,11 +97,13 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// The state of the partitions that would activate this scenario
|
||||
/// </summary>
|
||||
[JsonProperty("partitionStates")]
|
||||
List<PartitionState> PartitionStates { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The mapping of UIs by key to rooms by key
|
||||
/// </summary>
|
||||
[JsonProperty("uiMap")]
|
||||
Dictionary<string, string> UiMap { get; set; }
|
||||
}
|
||||
|
||||
|
||||
@@ -16,20 +16,41 @@ namespace PepperDash.Essentials.Core
|
||||
/// <summary>
|
||||
/// Represents a room combination scenario
|
||||
/// </summary>
|
||||
public class RoomCombinationScenario: IRoomCombinationScenario
|
||||
public class RoomCombinationScenario: IRoomCombinationScenario, IKeyName
|
||||
{
|
||||
private RoomCombinationScenarioConfig _config;
|
||||
|
||||
[JsonProperty("key")]
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonProperty("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonProperty("partitionStates")]
|
||||
public List<PartitionState> PartitionStates { get; private set; }
|
||||
|
||||
[JsonProperty("uiMap")]
|
||||
public Dictionary<string, string> UiMap { get; set; }
|
||||
|
||||
private bool _isActive;
|
||||
|
||||
[JsonProperty("isActive")]
|
||||
public bool IsActive
|
||||
{
|
||||
get { return _isActive; }
|
||||
set
|
||||
{
|
||||
if(value == _isActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isActive = value;
|
||||
IsActiveFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public BoolFeedback IsActiveFeedback { get; private set; }
|
||||
|
||||
private List<DeviceActionWrapper> activationActions;
|
||||
@@ -67,8 +88,7 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
_isActive = true;
|
||||
IsActiveFeedback.FireUpdate();
|
||||
IsActive = true;
|
||||
}
|
||||
|
||||
public void Deactivate()
|
||||
@@ -83,8 +103,7 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
|
||||
_isActive = false;
|
||||
IsActiveFeedback.FireUpdate();
|
||||
IsActive = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -198,6 +198,9 @@ namespace PepperDash.Essentials.Room.Config
|
||||
public string SourceListKey { get; set; }
|
||||
[JsonProperty("destinationListKey")]
|
||||
public string DestinationListKey { get; set; }
|
||||
[JsonProperty("audioControlPointListKey")]
|
||||
public string AudioControlPointListKey { get; set; }
|
||||
|
||||
[JsonProperty("defaultSourceItem")]
|
||||
public string DefaultSourceItem { get; set; }
|
||||
/// <summary>
|
||||
|
||||
@@ -59,28 +59,81 @@ namespace PepperDash.Essentials.Core
|
||||
/// </summary>
|
||||
public IMobileControlRoomMessenger MobileControlRoomBridge { get; private set; }
|
||||
|
||||
protected const string _defaultListKey = "default";
|
||||
|
||||
/// <summary>
|
||||
/// The config name of the source list
|
||||
/// </summary>
|
||||
///
|
||||
protected string _SourceListKey;
|
||||
private string _sourceListKey;
|
||||
public string SourceListKey {
|
||||
get
|
||||
{
|
||||
return _SourceListKey;
|
||||
}
|
||||
private set
|
||||
{
|
||||
if (value != _SourceListKey)
|
||||
if(string.IsNullOrEmpty(_sourceListKey))
|
||||
{
|
||||
_SourceListKey = value;
|
||||
return _defaultListKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _sourceListKey;
|
||||
}
|
||||
}
|
||||
protected set
|
||||
{
|
||||
if (value != _sourceListKey)
|
||||
{
|
||||
_sourceListKey = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string DestinationListKey { get; private set; }
|
||||
private string _destinationListKey;
|
||||
public string DestinationListKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_destinationListKey))
|
||||
{
|
||||
return _defaultListKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _destinationListKey;
|
||||
}
|
||||
}
|
||||
protected set
|
||||
{
|
||||
if (value != _destinationListKey)
|
||||
{
|
||||
_destinationListKey = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string _audioControlPointListKey;
|
||||
public string AudioControlPointListKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (string.IsNullOrEmpty(_audioControlPointListKey))
|
||||
{
|
||||
return _defaultListKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _destinationListKey;
|
||||
}
|
||||
}
|
||||
protected set
|
||||
{
|
||||
if (value != _audioControlPointListKey)
|
||||
{
|
||||
_audioControlPointListKey = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected const string _defaultSourceListKey = "default";
|
||||
|
||||
/// <summary>
|
||||
/// Timer used for informing the UIs of a shutdown
|
||||
@@ -141,6 +194,7 @@ namespace PepperDash.Essentials.Core
|
||||
if (!ShutdownPromptTimer.IsRunningFeedback.BoolValue)
|
||||
ShutdownType = eShutdownType.None;
|
||||
};
|
||||
|
||||
ShutdownPromptTimer.HasFinished += (o, a) => Shutdown(); // Shutdown is triggered
|
||||
|
||||
ShutdownPromptSeconds = 60;
|
||||
@@ -191,7 +245,7 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceListKey = _defaultSourceListKey;
|
||||
sourceListKey = _defaultListKey;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
string DestinationListKey { get; }
|
||||
|
||||
string AudioControlPointListKey { get; }
|
||||
|
||||
SecondsCountdownTimer ShutdownPromptTimer { get; }
|
||||
int ShutdownPromptSeconds { get; }
|
||||
int ShutdownVacancySeconds { get; }
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace PepperDash.Essentials.Core
|
||||
{
|
||||
void RunRouteAction(string routeKey, string sourceListKey);
|
||||
|
||||
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
|
||||
void RunRouteAction(string routeKey, string sourceListKey, Action successCallback);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,6 +77,34 @@ namespace PepperDash.Essentials.Core
|
||||
SecondsCountdownTimer ShutdownPromptTimer { get; }
|
||||
|
||||
void SetShutdownPromptSeconds(int seconds);
|
||||
|
||||
void StartShutdown(eShutdownType type);
|
||||
}
|
||||
|
||||
/// <summary""'""">
|
||||
/// Describes a room with a tech password
|
||||
/// </summary>
|
||||
public interface ITechPassword
|
||||
{
|
||||
event EventHandler<TechPasswordEventArgs> TechPasswordValidateResult;
|
||||
|
||||
event EventHandler<EventArgs> TechPasswordChanged;
|
||||
|
||||
int TechPasswordLength { get; }
|
||||
|
||||
void ValidateTechPassword(string password);
|
||||
|
||||
void SetTechPassword(string oldPassword, string newPassword);
|
||||
}
|
||||
|
||||
public class TechPasswordEventArgs : EventArgs
|
||||
{
|
||||
public bool IsValid { get; private set; }
|
||||
|
||||
public TechPasswordEventArgs(bool isValid)
|
||||
{
|
||||
IsValid = isValid;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -131,4 +159,13 @@ namespace PepperDash.Essentials.Core
|
||||
Core.Privacy.MicrophonePrivacyController MicrophonePrivacy { get; }
|
||||
}
|
||||
|
||||
public interface IHasAccessoryDevices : IKeyName
|
||||
{
|
||||
List<string> AccessoryDeviceKeys { get; }
|
||||
}
|
||||
|
||||
public interface IHasCiscoNavigatorTouchpanel
|
||||
{
|
||||
string CiscoNavigatorTouchpanelKey { get; }
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
public class DummyRoutingInputsDevice : Device, IRoutingSource
|
||||
public class DummyRoutingInputsDevice : Device, IRoutingSource, IRoutingOutputs
|
||||
{
|
||||
/// <summary>
|
||||
/// A single output port, backplane, audioVideo
|
||||
|
||||
295
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
295
src/PepperDash.Essentials.Core/Routing/Extensions.cs
Normal file
@@ -0,0 +1,295 @@
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||
/// on those destinations.
|
||||
/// </summary>
|
||||
public static class Extensions
|
||||
{
|
||||
private static readonly Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||
/// in RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
public static void ReleaseAndMakeRoute(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, string destinationPortKey = "", string sourcePortKey = "")
|
||||
{
|
||||
var inputPort = string.IsNullOrEmpty(destinationPortKey) ? null : destination.InputPorts.FirstOrDefault(p => p.Key == destinationPortKey);
|
||||
var outputPort = string.IsNullOrEmpty(sourcePortKey) ? null : source.OutputPorts.FirstOrDefault(p => p.Key == sourcePortKey);
|
||||
|
||||
ReleaseAndMakeRoute(destination, source, signalType, inputPort, outputPort);
|
||||
}
|
||||
|
||||
private static void ReleaseAndMakeRoute(IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort = null, RoutingOutputPort sourcePort = null)
|
||||
{
|
||||
var routeRequest = new RouteRequest
|
||||
{
|
||||
Destination = destination,
|
||||
DestinationPort = destinationPort,
|
||||
Source = source,
|
||||
SourcePort = sourcePort,
|
||||
SignalType = signalType
|
||||
};
|
||||
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
|
||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests[destination.Key] = routeRequest;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//New Request
|
||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests.Add(destination.Key, routeRequest);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is cooling down. Storing route request to route to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||
{
|
||||
RouteRequests.Remove(destination.Key);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", null, destination.Key, routeRequest.Source.Key);
|
||||
}
|
||||
|
||||
destination.ReleaseRoute();
|
||||
|
||||
RunRouteRequest(routeRequest);
|
||||
}
|
||||
|
||||
private static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
if (request.Source == null)
|
||||
return;
|
||||
|
||||
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType, request.DestinationPort, request.SourcePort);
|
||||
|
||||
if (newRoute == null)
|
||||
return;
|
||||
|
||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Executing full route", request.Destination);
|
||||
|
||||
newRoute.ExecuteRoutes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
public static void ReleaseRoute(this IRoutingInputs destination)
|
||||
{
|
||||
|
||||
if (RouteRequests.TryGetValue(destination.Key, out RouteRequest existingRequest) && destination is IWarmingCooling)
|
||||
{
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||
}
|
||||
|
||||
RouteRequests.Remove(destination.Key);
|
||||
|
||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||
if (current != null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Releasing current route: {0}", destination, current.Source.Key);
|
||||
current.ReleaseRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||
/// discovered, then null is returned
|
||||
/// </summary>
|
||||
public static RouteDescriptor GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source, eRoutingSignalType signalType, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
|
||||
{
|
||||
var routeDescriptor = new RouteDescriptor(source, destination, signalType);
|
||||
|
||||
// if it's a single signal type, find the route
|
||||
if (!signalType.HasFlag(eRoutingSignalType.AudioVideo))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build source route from {0}", null, source.Key);
|
||||
|
||||
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescriptor, destinationPort, sourcePort))
|
||||
routeDescriptor = null;
|
||||
|
||||
return routeDescriptor;
|
||||
}
|
||||
// otherwise, audioVideo needs to be handled as two steps.
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Attempting to build audio and video routes from {0}", destination, source.Key);
|
||||
|
||||
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescriptor, destinationPort, sourcePort);
|
||||
|
||||
if (!audioSuccess)
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Cannot find audio route to {0}", destination, source.Key);
|
||||
|
||||
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescriptor, destinationPort, sourcePort);
|
||||
|
||||
if (!videoSuccess)
|
||||
Debug.LogMessage(LogEventLevel.Debug, "Cannot find video route to {0}", destination, source.Key);
|
||||
|
||||
if (!audioSuccess && !videoSuccess)
|
||||
routeDescriptor = null;
|
||||
|
||||
|
||||
return routeDescriptor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||
/// desired source and if not found, invoke this function for the each input port
|
||||
/// hoping to find the source.
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="destinationPort">The RoutingOutputPort whose link is being checked for a route</param>
|
||||
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||
/// <param name="cycle">Just an informational counter</param>
|
||||
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||
/// <returns>true if source is hit</returns>
|
||||
private static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
||||
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
||||
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable, RoutingInputPort destinationPort, RoutingOutputPort sourcePort)
|
||||
{
|
||||
cycle++;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", null, cycle, source.Key, destination.Key);
|
||||
|
||||
RoutingInputPort goodInputPort = null;
|
||||
|
||||
IEnumerable<TieLine> destinationTieLines;
|
||||
TieLine directTie = null;
|
||||
|
||||
if (destinationPort == null)
|
||||
{
|
||||
|
||||
destinationTieLines = TieLineCollection.Default.Where(t =>
|
||||
t.DestinationPort.ParentDevice.Key == destination.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
|
||||
}
|
||||
else
|
||||
{
|
||||
destinationTieLines = TieLineCollection.Default.Where(t => t.DestinationPort.ParentDevice.Key == destination.Key && (t.Type == signalType || t.Type.HasFlag(eRoutingSignalType.AudioVideo)));
|
||||
}
|
||||
|
||||
// find the TieLine without a port
|
||||
if (destinationPort == null && sourcePort == null)
|
||||
{
|
||||
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.ParentDevice.Key == source.Key);
|
||||
}
|
||||
// find a tieLine to a specific destination port without a specific source port
|
||||
else if (destinationPort != null && sourcePort == null)
|
||||
{
|
||||
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.Key == destinationPort.Key && t.SourcePort.ParentDevice.Key == source.Key);
|
||||
}
|
||||
// find a tieline to a specific source port without a specific destination port
|
||||
else if (destinationPort == null & sourcePort != null)
|
||||
{
|
||||
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.ParentDevice.Key == destination.Key && t.SourcePort.Key == sourcePort.Key);
|
||||
}
|
||||
// find a tieline to a specific source port and destination port
|
||||
else if (destinationPort != null && sourcePort != null)
|
||||
{
|
||||
directTie = destinationTieLines.FirstOrDefault(t => t.DestinationPort.Key == destinationPort.Key && t.SourcePort.Key == sourcePort.Key);
|
||||
}
|
||||
|
||||
if (directTie != null) // Found a tie directly to the source
|
||||
{
|
||||
goodInputPort = directTie.DestinationPort;
|
||||
}
|
||||
else // no direct-connect. Walk back devices.
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "is not directly connected to {0}. Walking down tie lines", destination, source.Key);
|
||||
|
||||
// No direct tie? Run back out on the inputs' attached devices...
|
||||
// Only the ones that are routing devices
|
||||
var midpointTieLines = destinationTieLines.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||
|
||||
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||
if (alreadyCheckedDevices == null)
|
||||
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
||||
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
||||
|
||||
foreach (var tieLine in midpointTieLines)
|
||||
{
|
||||
var midpointDevice = tieLine.SourcePort.ParentDevice as IRoutingInputsOutputs;
|
||||
|
||||
// Check if this previous device has already been walked
|
||||
if (alreadyCheckedDevices.Contains(midpointDevice))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Skipping input {0} on {1}, this was already checked", destination, midpointDevice.Key, destination.Key);
|
||||
continue;
|
||||
}
|
||||
|
||||
var midpointOutputPort = tieLine.SourcePort;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Trying to find route on {0}", destination, midpointDevice.Key);
|
||||
|
||||
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||
// level to enable switching on success
|
||||
var upstreamRoutingSuccess = midpointDevice.GetRouteToSource(source, midpointOutputPort,
|
||||
alreadyCheckedDevices, signalType, cycle, routeTable, null, sourcePort);
|
||||
|
||||
if (upstreamRoutingSuccess)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Upstream device route found", destination);
|
||||
goodInputPort = tieLine.DestinationPort;
|
||||
break; // Stop looping the inputs in this cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (goodInputPort == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "No route found to {0}", destination, source.Key);
|
||||
return false;
|
||||
}
|
||||
|
||||
// we have a route on corresponding inputPort. *** Do the route ***
|
||||
|
||||
if (destination is IRoutingSink)
|
||||
{
|
||||
// it's a sink device
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
||||
}
|
||||
else if (destination is IRouting)
|
||||
{
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor(outputPortToUse, goodInputPort));
|
||||
}
|
||||
else // device is merely IRoutingInputOutputs
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "No routing. Passthrough device", destination);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* Unmerged change from project 'PepperDash.Essentials.Core (net6)'
|
||||
Before:
|
||||
namespace PepperDash.Essentials.Core.Routing.Interfaces
|
||||
After:
|
||||
using PepperDash;
|
||||
using PepperDash.Essentials;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using PepperDash.Essentials.Core.Routing.Interfaces
|
||||
*/
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// The handler type for a Room's SourceInfoChange
|
||||
/// </summary>
|
||||
public delegate void SourceInfoChangeHandler(SourceListItem info, ChangeType type);
|
||||
//*******************************************************************************************
|
||||
// Interfaces
|
||||
|
||||
/// <summary>
|
||||
/// For rooms with a single presentation source, change event
|
||||
/// </summary>
|
||||
public interface IHasCurrentSourceInfoChange
|
||||
{
|
||||
string CurrentSourceInfoKey { get; set; }
|
||||
SourceListItem CurrentSourceInfo { get; set; }
|
||||
event SourceInfoChangeHandler CurrentSourceChange;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
public interface IVideoSync: IKeyed
|
||||
public interface IVideoSync : IKeyed
|
||||
{
|
||||
bool VideoSyncDetected { get; }
|
||||
|
||||
|
||||
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
10
src/PepperDash.Essentials.Core/Routing/IRmcRouting.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
|
||||
/// </summary>
|
||||
public interface IRmcRouting : IRoutingNumeric
|
||||
{
|
||||
IntFeedback AudioVideoSourceNumericFeedback { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an IRmcRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface IRmcRoutingWithFeedback : IRmcRouting
|
||||
{
|
||||
}
|
||||
}
|
||||
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
21
src/PepperDash.Essentials.Core/Routing/IRouting.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
|
||||
/// signal chain, that do switching, must implement this for routing to work otherwise
|
||||
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
|
||||
/// device.
|
||||
/// </summary>
|
||||
public interface IRouting : IRoutingInputsOutputs
|
||||
{
|
||||
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
|
||||
}
|
||||
|
||||
/*public interface IRouting<TInputSelector,TOutputSelector> : IRoutingInputsOutputs
|
||||
{
|
||||
void ExecuteSwitch(TInputSelector inputSelector, TOutputSelector outputSelector, eRoutingSignalType signalType);
|
||||
}*/
|
||||
}
|
||||
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
16
src/PepperDash.Essentials.Core/Routing/IRoutingFeedback.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an event structure for reporting output route data
|
||||
/// </summary>
|
||||
public interface IRoutingFeedback : IKeyName
|
||||
{
|
||||
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
|
||||
//void OnSwitchChange(RoutingNumericEventArgs e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
|
||||
public interface IRoutingHasVideoInputSyncFeedbacks
|
||||
{
|
||||
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
|
||||
}
|
||||
}
|
||||
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
18
src/PepperDash.Essentials.Core/Routing/IRoutingInputs.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class that has a collection of RoutingInputPorts
|
||||
/// </summary>
|
||||
public interface IRoutingInputs : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
|
||||
}
|
||||
|
||||
/* public interface IRoutingInputs<TSelector> : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingInputPort<TSelector>, TSelector> InputPorts { get; }
|
||||
}*/
|
||||
}
|
||||
@@ -1,426 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RouteRequest
|
||||
{
|
||||
public IRoutingSink Destination {get; set;}
|
||||
public IRoutingOutputs Source {get; set;}
|
||||
public eRoutingSignalType SignalType {get; set;}
|
||||
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
var coolingDevice = sender as IWarmingCooling;
|
||||
|
||||
if(args.BoolValue == false)
|
||||
{
|
||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||
|
||||
if(sender == null) return;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions added to any IRoutingInputs classes to provide discovery-based routing
|
||||
/// on those destinations.
|
||||
/// </summary>
|
||||
public static class IRoutingInputsExtensions
|
||||
{
|
||||
private static Dictionary<string, RouteRequest> RouteRequests = new Dictionary<string, RouteRequest>();
|
||||
/// <summary>
|
||||
/// Gets any existing RouteDescriptor for a destination, clears it using ReleaseRoute
|
||||
/// and then attempts a new Route and if sucessful, stores that RouteDescriptor
|
||||
/// in RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
public static void ReleaseAndMakeRoute(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeRequest = new RouteRequest {
|
||||
Destination = destination,
|
||||
Source = source,
|
||||
SignalType = signalType
|
||||
};
|
||||
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
RouteRequest existingRouteRequest;
|
||||
|
||||
//We already have a route request for this device, and it's a cooling device and is cooling
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRouteRequest) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRouteRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests[destination.Key] = routeRequest;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down and already has a routing request stored. Storing new route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//New Request
|
||||
if (coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == true)
|
||||
{
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= routeRequest.HandleCooldown;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange += routeRequest.HandleCooldown;
|
||||
|
||||
RouteRequests.Add(destination.Key, routeRequest);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is cooling down. Storing route request to route to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (RouteRequests.ContainsKey(destination.Key) && coolingDevice != null && coolingDevice.IsCoolingDownFeedback.BoolValue == false)
|
||||
{
|
||||
RouteRequests.Remove(destination.Key);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "******************************************************** Device: {0} is NOT cooling down. Removing stored route request and routing to source key: {1}", destination.Key, routeRequest.Source.Key);
|
||||
}
|
||||
|
||||
destination.ReleaseRoute();
|
||||
|
||||
RunRouteRequest(routeRequest);
|
||||
}
|
||||
|
||||
public static void RunRouteRequest(RouteRequest request)
|
||||
{
|
||||
if (request.Source == null) return;
|
||||
var newRoute = request.Destination.GetRouteToSource(request.Source, request.SignalType);
|
||||
if (newRoute == null) return;
|
||||
RouteDescriptorCollection.DefaultCollection.AddRouteDescriptor(newRoute);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, request.Destination, "Executing full route");
|
||||
newRoute.ExecuteRoutes();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will release the existing route on the destination, if it is found in
|
||||
/// RouteDescriptorCollection.DefaultCollection
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
public static void ReleaseRoute(this IRoutingSink destination)
|
||||
{
|
||||
RouteRequest existingRequest;
|
||||
|
||||
if (RouteRequests.TryGetValue(destination.Key, out existingRequest) && destination is IWarmingCooling)
|
||||
{
|
||||
var coolingDevice = destination as IWarmingCooling;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= existingRequest.HandleCooldown;
|
||||
}
|
||||
|
||||
RouteRequests.Remove(destination.Key);
|
||||
|
||||
var current = RouteDescriptorCollection.DefaultCollection.RemoveRouteDescriptor(destination);
|
||||
if (current != null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Releasing current route: {0}", current.Source.Key);
|
||||
current.ReleaseRoutes();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds a RouteDescriptor that contains the steps necessary to make a route between devices.
|
||||
/// Routes of type AudioVideo will be built as two separate routes, audio and video. If
|
||||
/// a route is discovered, a new RouteDescriptor is returned. If one or both parts
|
||||
/// of an audio/video route are discovered a route descriptor is returned. If no route is
|
||||
/// discovered, then null is returned
|
||||
/// </summary>
|
||||
public static RouteDescriptor GetRouteToSource(this IRoutingSink destination, IRoutingOutputs source, eRoutingSignalType signalType)
|
||||
{
|
||||
var routeDescr = new RouteDescriptor(source, destination, signalType);
|
||||
// if it's a single signal type, find the route
|
||||
if ((signalType & (eRoutingSignalType.Audio & eRoutingSignalType.Video)) == (eRoutingSignalType.Audio & eRoutingSignalType.Video))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build source route from {0}", source.Key);
|
||||
if (!destination.GetRouteToSource(source, null, null, signalType, 0, routeDescr))
|
||||
routeDescr = null;
|
||||
}
|
||||
// otherwise, audioVideo needs to be handled as two steps.
|
||||
else
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Attempting to build audio and video routes from {0}", source.Key);
|
||||
var audioSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Audio, 0, routeDescr);
|
||||
if (!audioSuccess)
|
||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find audio route to {0}", source.Key);
|
||||
var videoSuccess = destination.GetRouteToSource(source, null, null, eRoutingSignalType.Video, 0, routeDescr);
|
||||
if (!videoSuccess)
|
||||
Debug.LogMessage(LogEventLevel.Debug, destination, "Cannot find video route to {0}", source.Key);
|
||||
if (!audioSuccess && !videoSuccess)
|
||||
routeDescr = null;
|
||||
}
|
||||
|
||||
//Debug.LogMessage(LogEventLevel.Debug, destination, "Route{0} discovered", routeDescr == null ? " NOT" : "");
|
||||
return routeDescr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The recursive part of this. Will stop on each device, search its inputs for the
|
||||
/// desired source and if not found, invoke this function for the each input port
|
||||
/// hoping to find the source.
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="source"></param>
|
||||
/// <param name="outputPortToUse">The RoutingOutputPort whose link is being checked for a route</param>
|
||||
/// <param name="alreadyCheckedDevices">Prevents Devices from being twice-checked</param>
|
||||
/// <param name="signalType">This recursive function should not be called with AudioVideo</param>
|
||||
/// <param name="cycle">Just an informational counter</param>
|
||||
/// <param name="routeTable">The RouteDescriptor being populated as the route is discovered</param>
|
||||
/// <returns>true if source is hit</returns>
|
||||
static bool GetRouteToSource(this IRoutingInputs destination, IRoutingOutputs source,
|
||||
RoutingOutputPort outputPortToUse, List<IRoutingInputsOutputs> alreadyCheckedDevices,
|
||||
eRoutingSignalType signalType, int cycle, RouteDescriptor routeTable)
|
||||
{
|
||||
cycle++;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "GetRouteToSource: {0} {1}--> {2}", cycle, source.Key, destination.Key);
|
||||
|
||||
RoutingInputPort goodInputPort = null;
|
||||
var destDevInputTies = TieLineCollection.Default.Where(t =>
|
||||
t.DestinationPort.ParentDevice == destination && (t.Type == signalType || (t.Type & (eRoutingSignalType.Audio | eRoutingSignalType.Video)) == (eRoutingSignalType.Audio | eRoutingSignalType.Video)));
|
||||
|
||||
// find a direct tie
|
||||
var directTie = destDevInputTies.FirstOrDefault(
|
||||
t => t.DestinationPort.ParentDevice == destination
|
||||
&& t.SourcePort.ParentDevice == source);
|
||||
if (directTie != null) // Found a tie directly to the source
|
||||
{
|
||||
goodInputPort = directTie.DestinationPort;
|
||||
}
|
||||
else // no direct-connect. Walk back devices.
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "is not directly connected to {0}. Walking down tie lines", source.Key);
|
||||
|
||||
// No direct tie? Run back out on the inputs' attached devices...
|
||||
// Only the ones that are routing devices
|
||||
var attachedMidpoints = destDevInputTies.Where(t => t.SourcePort.ParentDevice is IRoutingInputsOutputs);
|
||||
|
||||
//Create a list for tracking already checked devices to avoid loops, if it doesn't already exist from previous iteration
|
||||
if (alreadyCheckedDevices == null)
|
||||
alreadyCheckedDevices = new List<IRoutingInputsOutputs>();
|
||||
alreadyCheckedDevices.Add(destination as IRoutingInputsOutputs);
|
||||
|
||||
foreach (var inputTieToTry in attachedMidpoints)
|
||||
{
|
||||
var upstreamDeviceOutputPort = inputTieToTry.SourcePort;
|
||||
var upstreamRoutingDevice = upstreamDeviceOutputPort.ParentDevice as IRoutingInputsOutputs;
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Trying to find route on {0}", upstreamRoutingDevice.Key);
|
||||
|
||||
// Check if this previous device has already been walked
|
||||
if (alreadyCheckedDevices.Contains(upstreamRoutingDevice))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Skipping input {0} on {1}, this was already checked", upstreamRoutingDevice.Key, destination.Key);
|
||||
continue;
|
||||
}
|
||||
// haven't seen this device yet. Do it. Pass the output port to the next
|
||||
// level to enable switching on success
|
||||
var upstreamRoutingSuccess = upstreamRoutingDevice.GetRouteToSource(source, upstreamDeviceOutputPort,
|
||||
alreadyCheckedDevices, signalType, cycle, routeTable);
|
||||
if (upstreamRoutingSuccess)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "Upstream device route found");
|
||||
goodInputPort = inputTieToTry.DestinationPort;
|
||||
break; // Stop looping the inputs in this cycle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have a route on corresponding inputPort. *** Do the route ***
|
||||
if (goodInputPort != null)
|
||||
{
|
||||
//Debug.LogMessage(LogEventLevel.Verbose, destination, "adding RouteDescriptor");
|
||||
if (outputPortToUse == null)
|
||||
{
|
||||
// it's a sink device
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor(goodInputPort));
|
||||
}
|
||||
else if (destination is IRouting)
|
||||
{
|
||||
routeTable.Routes.Add(new RouteSwitchDescriptor (outputPortToUse, goodInputPort));
|
||||
}
|
||||
else // device is merely IRoutingInputOutputs
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, " No routing. Passthrough device");
|
||||
//Debug.LogMessage(LogEventLevel.Verbose, destination, "Exiting cycle {0}", cycle);
|
||||
return true;
|
||||
}
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, destination, "No route found to {0}", source.Key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE MOVE
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||
/// </summary>
|
||||
public class RouteDescriptorCollection
|
||||
{
|
||||
public static RouteDescriptorCollection DefaultCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultCollection == null)
|
||||
_DefaultCollection = new RouteDescriptorCollection();
|
||||
return _DefaultCollection;
|
||||
}
|
||||
}
|
||||
static RouteDescriptorCollection _DefaultCollection;
|
||||
|
||||
List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||
/// destination exists already, it will not be added - in order to preserve
|
||||
/// proper route releasing.
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||
return;
|
||||
}
|
||||
RouteDescriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RouteDescriptor for a destination
|
||||
/// </summary>
|
||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
||||
{
|
||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||
/// Returns null if no route with the provided destination exists.
|
||||
/// </summary>
|
||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
||||
{
|
||||
var descr = GetRouteDescriptorForDestination(destination);
|
||||
if (descr != null)
|
||||
RouteDescriptors.Remove(descr);
|
||||
return descr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", route.ToString());
|
||||
if (route.SwitchingDevice is IRoutingSink)
|
||||
{
|
||||
var device = route.SwitchingDevice as IRoutingSinkWithSwitching;
|
||||
if (device == null)
|
||||
continue;
|
||||
|
||||
device.ExecuteSwitch(route.InputPort.Selector);
|
||||
}
|
||||
else if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
(route.SwitchingDevice as IRouting).ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||
// responsible for responding appropriately.
|
||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if(SwitchingDevice is IRouting)
|
||||
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||
else
|
||||
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// For devices like RMCs, baluns, other devices with no switching.
|
||||
/// </summary>
|
||||
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
|
||||
{
|
||||
}
|
||||
|
||||
/* /// <summary>
|
||||
/// For devices like RMCs, baluns, other devices with no switching.
|
||||
/// </summary>
|
||||
public interface IRoutingInputsOutputs<TInputSelector, TOutputSelector> : IRoutingInputs<TInputSelector>, IRoutingOutputs<TOutputSelector>
|
||||
{
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public interface IRoutingNumeric : IRouting
|
||||
{
|
||||
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an IRoutingNumeric with a feedback event
|
||||
/// </summary>
|
||||
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
|
||||
{
|
||||
}
|
||||
}
|
||||
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
19
src/PepperDash.Essentials.Core/Routing/IRoutingOutputs.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a class that has a collection of RoutingOutputPorts
|
||||
/// </summary>
|
||||
|
||||
public interface IRoutingOutputs : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
|
||||
}
|
||||
|
||||
/* public interface IRoutingOutputs<TSelector> : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingOutputPort<TSelector>, TSelector> OutputPorts { get; }
|
||||
}*/
|
||||
}
|
||||
23
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
23
src/PepperDash.Essentials.Core/Routing/IRoutingSink.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// For fixed-source endpoint devices
|
||||
/// </summary>
|
||||
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
|
||||
{
|
||||
}
|
||||
|
||||
public interface IRoutingSinkWithInputPort :IRoutingSink
|
||||
{
|
||||
RoutingInputPort CurrentInputPort { get; }
|
||||
}
|
||||
/*/// <summary>
|
||||
/// For fixed-source endpoint devices
|
||||
/// </summary>
|
||||
public interface IRoutingSink<TSelector> : IRoutingInputs<TSelector>, IHasCurrentSourceInfoChange
|
||||
{
|
||||
void UpdateRouteRequest<TOutputSelector>(RouteRequest<TSelector, TOutputSelector> request);
|
||||
|
||||
RouteRequest<TSelector, TOutputSelector> GetRouteRequest<TOutputSelector>();
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// For fixed-source endpoint devices
|
||||
/// </summary>
|
||||
public interface IRoutingSinkWithFeedback : IRoutingSinkWithSwitching
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/* /// <summary>
|
||||
/// For fixed-source endpoint devices
|
||||
/// </summary>
|
||||
public interface IRoutingSinkWithFeedback<TSelector> : IRoutingSinkWithSwitching<TSelector>
|
||||
{
|
||||
RouteSwitchDescriptor CurrentRoute { get; }
|
||||
|
||||
event EventHandler InputChanged;
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public delegate void InputChangedEventHandler(IRoutingSinkWithSwitching destination, RoutingInputPort currentPort);
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint device like a display, that selects inputs
|
||||
/// </summary>
|
||||
public interface IRoutingSinkWithSwitching : IRoutingSink
|
||||
{
|
||||
void ExecuteSwitch(object inputSelector);
|
||||
}
|
||||
|
||||
public interface IRoutingSinkWithSwitchingWithInputPort:IRoutingSinkWithSwitching, IRoutingSinkWithInputPort
|
||||
{
|
||||
event InputChangedEventHandler InputChanged;
|
||||
}
|
||||
|
||||
/* /// <summary>
|
||||
/// Endpoint device like a display, that selects inputs
|
||||
/// </summary>
|
||||
public interface IRoutingSinkWithSwitching<TSelector> : IRoutingSink<TSelector>
|
||||
{
|
||||
void ExecuteSwitch(TSelector inputSelector);
|
||||
}*/
|
||||
}
|
||||
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
9
src/PepperDash.Essentials.Core/Routing/IRoutingSource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||
/// </summary>
|
||||
public interface IRoutingSource : IRoutingOutputs
|
||||
{
|
||||
}
|
||||
}
|
||||
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
12
src/PepperDash.Essentials.Core/Routing/IRoutingWithClear.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public interface IRoutingWithClear : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears a route to an output, however a device needs to do that
|
||||
/// </summary>
|
||||
/// <param name="outputSelector">Output to clear</param>
|
||||
/// <param name="signalType">signal type to clear</param>
|
||||
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public delegate void RouteChangedEventHandler(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute);
|
||||
/// <summary>
|
||||
/// Defines an IRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface IRoutingWithFeedback : IRouting
|
||||
{
|
||||
List<RouteSwitchDescriptor> CurrentRoutes { get; }
|
||||
|
||||
event RouteChangedEventHandler RouteChanged;
|
||||
}
|
||||
}
|
||||
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
8
src/PepperDash.Essentials.Core/Routing/ITxRouting.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public interface ITxRouting : IRoutingNumeric
|
||||
{
|
||||
IntFeedback VideoSourceNumericFeedback { get; }
|
||||
IntFeedback AudioSourceNumericFeedback { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines an IRmcRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface ITxRoutingWithFeedback : ITxRouting
|
||||
{
|
||||
}
|
||||
}
|
||||
152
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
152
src/PepperDash.Essentials.Core/Routing/RouteDescriptor.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharpPro;
|
||||
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor
|
||||
{
|
||||
public IRoutingInputs Destination { get; private set; }
|
||||
public IRoutingOutputs Source { get; private set; }
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
public List<RouteSwitchDescriptor> Routes { get; private set; }
|
||||
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs source, IRoutingInputs destination, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
Routes = new List<RouteSwitchDescriptor>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}",null, route.ToString());
|
||||
|
||||
if (route.SwitchingDevice is IRoutingSinkWithSwitching sink)
|
||||
{
|
||||
sink.ExecuteSwitch(route.InputPort.Selector);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||
{
|
||||
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||
|
||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting)
|
||||
{
|
||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||
// responsible for responding appropriately.
|
||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}",null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// Represents an collection of individual route steps between Source and Destination
|
||||
/// </summary>
|
||||
public class RouteDescriptor<TInputSelector, TOutputSelector>
|
||||
{
|
||||
public IRoutingInputs<TInputSelector> Destination { get; private set; }
|
||||
public IRoutingOutputs<TOutputSelector> Source { get; private set; }
|
||||
public eRoutingSignalType SignalType { get; private set; }
|
||||
public List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>> Routes { get; private set; }
|
||||
|
||||
|
||||
public RouteDescriptor(IRoutingOutputs<TOutputSelector> source, IRoutingInputs<TInputSelector> destination, eRoutingSignalType signalType)
|
||||
{
|
||||
Destination = destination;
|
||||
Source = source;
|
||||
SignalType = signalType;
|
||||
Routes = new List<RouteSwitchDescriptor<TInputSelector, TOutputSelector>>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes all routes described in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ExecuteRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "ExecuteRoutes: {0}", null, route.ToString());
|
||||
|
||||
if (route.SwitchingDevice is IRoutingSinkWithSwitching<TInputSelector> sink)
|
||||
{
|
||||
sink.ExecuteSwitch(route.InputPort.Selector);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (route.SwitchingDevice is IRouting switchingDevice)
|
||||
{
|
||||
switchingDevice.ExecuteSwitch(route.InputPort.Selector, route.OutputPort.Selector, SignalType);
|
||||
|
||||
route.OutputPort.InUseTracker.AddUser(Destination, "destination-" + SignalType);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Output port {0} routing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases all routes in this collection. Typically called via
|
||||
/// extension method IRoutingInputs.ReleaseAndMakeRoute()
|
||||
/// </summary>
|
||||
public void ReleaseRoutes()
|
||||
{
|
||||
foreach (var route in Routes)
|
||||
{
|
||||
if (route.SwitchingDevice is IRouting<TInputSelector, TOutputSelector>)
|
||||
{
|
||||
// Pull the route from the port. Whatever is watching the output's in use tracker is
|
||||
// responsible for responding appropriately.
|
||||
route.OutputPort.InUseTracker.RemoveUser(Destination, "destination-" + SignalType);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Port {0} releasing. Count={1}", null, route.OutputPort.Key, route.OutputPort.InUseTracker.InUseCountFeedback.UShortValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var routesText = Routes.Select(r => r.ToString()).ToArray();
|
||||
return string.Format("Route table from {0} to {1}:\r{2}", Source.Key, Destination.Key, string.Join("\r", routesText));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using PepperDash.Core;
|
||||
using Serilog.Events;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||
/// </summary>
|
||||
public class RouteDescriptorCollection
|
||||
{
|
||||
public static RouteDescriptorCollection DefaultCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultCollection == null)
|
||||
_DefaultCollection = new RouteDescriptorCollection();
|
||||
return _DefaultCollection;
|
||||
}
|
||||
}
|
||||
private static RouteDescriptorCollection _DefaultCollection;
|
||||
|
||||
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||
/// destination exists already, it will not be added - in order to preserve
|
||||
/// proper route releasing.
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||
return;
|
||||
}
|
||||
RouteDescriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RouteDescriptor for a destination
|
||||
/// </summary>
|
||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs destination)
|
||||
{
|
||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||
/// Returns null if no route with the provided destination exists.
|
||||
/// </summary>
|
||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs destination)
|
||||
{
|
||||
var descr = GetRouteDescriptorForDestination(destination);
|
||||
if (descr != null)
|
||||
RouteDescriptors.Remove(descr);
|
||||
return descr;
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// A collection of RouteDescriptors - typically the static DefaultCollection is used
|
||||
/// </summary>
|
||||
public class RouteDescriptorCollection<TInputSelector, TOutputSelector>
|
||||
{
|
||||
public static RouteDescriptorCollection<TInputSelector, TOutputSelector> DefaultCollection
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_DefaultCollection == null)
|
||||
_DefaultCollection = new RouteDescriptorCollection<TInputSelector, TOutputSelector>();
|
||||
return _DefaultCollection;
|
||||
}
|
||||
}
|
||||
private static RouteDescriptorCollection<TInputSelector, TOutputSelector> _DefaultCollection;
|
||||
|
||||
private readonly List<RouteDescriptor> RouteDescriptors = new List<RouteDescriptor>();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a RouteDescriptor to the list. If an existing RouteDescriptor for the
|
||||
/// destination exists already, it will not be added - in order to preserve
|
||||
/// proper route releasing.
|
||||
/// </summary>
|
||||
/// <param name="descriptor"></param>
|
||||
public void AddRouteDescriptor(RouteDescriptor descriptor)
|
||||
{
|
||||
if (RouteDescriptors.Any(t => t.Destination == descriptor.Destination))
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, descriptor.Destination,
|
||||
"Route to [{0}] already exists in global routes table", descriptor.Source.Key);
|
||||
return;
|
||||
}
|
||||
RouteDescriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RouteDescriptor for a destination
|
||||
/// </summary>
|
||||
/// <returns>null if no RouteDescriptor for a destination exists</returns>
|
||||
public RouteDescriptor GetRouteDescriptorForDestination(IRoutingInputs<TInputSelector> destination)
|
||||
{
|
||||
return RouteDescriptors.FirstOrDefault(rd => rd.Destination == destination);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the RouteDescriptor for a given destination AND removes it from collection.
|
||||
/// Returns null if no route with the provided destination exists.
|
||||
/// </summary>
|
||||
public RouteDescriptor RemoveRouteDescriptor(IRoutingInputs<TInputSelector> destination)
|
||||
{
|
||||
var descr = GetRouteDescriptorForDestination(destination);
|
||||
if (descr != null)
|
||||
RouteDescriptors.Remove(descr);
|
||||
return descr;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
47
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
47
src/PepperDash.Essentials.Core/Routing/RouteRequest.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RouteRequest
|
||||
{
|
||||
public RoutingInputPort DestinationPort { get; set; }
|
||||
|
||||
public RoutingOutputPort SourcePort { get; set; }
|
||||
public IRoutingInputs Destination { get; set; }
|
||||
public IRoutingOutputs Source { get; set; }
|
||||
public eRoutingSignalType SignalType { get; set; }
|
||||
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
var coolingDevice = sender as IWarmingCooling;
|
||||
|
||||
if (args.BoolValue == false)
|
||||
{
|
||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||
|
||||
if (sender == null) return;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*public class RouteRequest<TInputSelector, TOutputSelector>
|
||||
{
|
||||
public IRoutingSink<TInputSelector> Destination { get; set; }
|
||||
public IRoutingOutputs<TOutputSelector> Source { get; set; }
|
||||
public eRoutingSignalType SignalType { get; set; }
|
||||
|
||||
public void HandleCooldown(object sender, FeedbackEventArgs args)
|
||||
{
|
||||
var coolingDevice = sender as IWarmingCooling;
|
||||
|
||||
if (args.BoolValue == false)
|
||||
{
|
||||
Destination.ReleaseAndMakeRoute(Source, SignalType);
|
||||
|
||||
if (sender == null) return;
|
||||
|
||||
coolingDevice.IsCoolingDownFeedback.OutputChange -= HandleCooldown;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor
|
||||
{
|
||||
public IRoutingInputs SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
|
||||
public RouteSwitchDescriptor(RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
public RouteSwitchDescriptor(RoutingOutputPort outputPort, RoutingInputPort inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (SwitchingDevice is IRouting)
|
||||
return $"{SwitchingDevice?.Key} switches output {OutputPort.Key} to input {InputPort.Key}";
|
||||
else
|
||||
return $"{SwitchingDevice.Key} switches to input {InputPort.Key}";
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// Represents an individual link for a route
|
||||
/// </summary>
|
||||
public class RouteSwitchDescriptor<TInputSelector, TOutputSelector>
|
||||
{
|
||||
public IRoutingInputs<TInputSelector> SwitchingDevice { get { return InputPort.ParentDevice; } }
|
||||
public RoutingOutputPort<TOutputSelector> OutputPort { get; set; }
|
||||
public RoutingInputPort<TInputSelector> InputPort { get; set; }
|
||||
|
||||
public RouteSwitchDescriptor(RoutingInputPort<TInputSelector> inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
}
|
||||
|
||||
public RouteSwitchDescriptor(RoutingOutputPort<TOutputSelector> outputPort, RoutingInputPort<TInputSelector> inputPort)
|
||||
{
|
||||
InputPort = inputPort;
|
||||
OutputPort = outputPort;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (SwitchingDevice is IRouting)
|
||||
return string.Format("{0} switches output '{1}' to input '{2}'", SwitchingDevice.Key, OutputPort.Selector, InputPort.Selector);
|
||||
else
|
||||
return string.Format("{0} switches to input '{1}'", SwitchingDevice.Key, InputPort.Selector);
|
||||
}
|
||||
}*/
|
||||
}
|
||||
269
src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
Normal file
269
src/PepperDash.Essentials.Core/Routing/RoutingFeedbackManager.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
{
|
||||
public class RoutingFeedbackManager:EssentialsDevice
|
||||
{
|
||||
public RoutingFeedbackManager(string key, string name): base(key, name)
|
||||
{
|
||||
AddPreActivationAction(SubscribeForMidpointFeedback);
|
||||
AddPreActivationAction(SubscribeForSinkFeedback);
|
||||
}
|
||||
|
||||
|
||||
private void SubscribeForMidpointFeedback()
|
||||
{
|
||||
var midpointDevices = DeviceManager.AllDevices.OfType<IRoutingWithFeedback>();
|
||||
|
||||
foreach (var device in midpointDevices)
|
||||
{
|
||||
device.RouteChanged += HandleMidpointUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeForSinkFeedback()
|
||||
{
|
||||
var sinkDevices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||
|
||||
foreach (var device in sinkDevices)
|
||||
{
|
||||
device.InputChanged += HandleSinkUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleMidpointUpdate(IRoutingWithFeedback midpoint, RouteSwitchDescriptor newRoute)
|
||||
{
|
||||
try
|
||||
{
|
||||
var devices = DeviceManager.AllDevices.OfType<IRoutingSinkWithSwitchingWithInputPort>();
|
||||
|
||||
foreach (var device in devices)
|
||||
{
|
||||
UpdateDestination(device, device.CurrentInputPort);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error handling midpoint update from {midpointKey}:{Exception}", this, midpoint.Key, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleSinkUpdate(IRoutingSinkWithSwitching sender, RoutingInputPort currentInputPort)
|
||||
{
|
||||
try
|
||||
{
|
||||
UpdateDestination(sender, currentInputPort);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error handling Sink update from {senderKey}:{Exception}", this, sender.Key, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDestination(IRoutingSinkWithSwitching destination, RoutingInputPort inputPort)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Updating destination {destination} with inputPort {inputPort}", this,destination?.Key, inputPort?.Key);
|
||||
|
||||
if(inputPort == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "Destination {destination} has not reported an input port yet", this,destination.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
TieLine firstTieLine;
|
||||
try
|
||||
{
|
||||
var tieLines = TieLineCollection.Default;
|
||||
|
||||
firstTieLine = tieLines.FirstOrDefault(tl => tl.DestinationPort.Key == inputPort.Key && tl.DestinationPort.ParentDevice.Key == inputPort.ParentDevice.Key);
|
||||
|
||||
if (firstTieLine == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No tieline found for inputPort {inputPort}. Clearing current source", this, inputPort);
|
||||
|
||||
var tempSourceListItem = new SourceListItem
|
||||
{
|
||||
SourceKey = "$transient",
|
||||
Name = inputPort.Key,
|
||||
};
|
||||
|
||||
|
||||
destination.CurrentSourceInfo = tempSourceListItem; ;
|
||||
destination.CurrentSourceInfoKey = "$transient";
|
||||
return;
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error getting first tieline: {Exception}", this, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Getting source for first TieLine {tieLine}", this, firstTieLine);
|
||||
|
||||
TieLine sourceTieLine;
|
||||
try
|
||||
{
|
||||
sourceTieLine = GetRootTieLine(firstTieLine);
|
||||
|
||||
if (sourceTieLine == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route found to source for inputPort {inputPort}. Clearing current source", this, inputPort);
|
||||
|
||||
var tempSourceListItem = new SourceListItem
|
||||
{
|
||||
SourceKey = "$transient",
|
||||
Name = "None",
|
||||
};
|
||||
|
||||
destination.CurrentSourceInfo = tempSourceListItem;
|
||||
destination.CurrentSourceInfoKey = string.Empty;
|
||||
return;
|
||||
}
|
||||
} catch(Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error getting sourceTieLine: {Exception}", this, ex);
|
||||
return;
|
||||
}
|
||||
|
||||
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.
|
||||
var room = DeviceManager.AllDevices.OfType<IEssentialsRoom>().FirstOrDefault((r) => {
|
||||
if(r is IHasMultipleDisplays roomMultipleDisplays)
|
||||
{
|
||||
return roomMultipleDisplays.Displays.Any(d => d.Value.Key == destination.Key);
|
||||
}
|
||||
|
||||
if(r is IHasDefaultDisplay roomDefaultDisplay)
|
||||
{
|
||||
return roomDefaultDisplay.DefaultDisplay.Key == destination.Key;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if(room == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Warning, "No room found for display {destination}", this, destination.Key);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found room {room} for destination {destination}", this, room.Key, destination.Key);
|
||||
|
||||
var sourceList = ConfigReader.ConfigObject.GetSourceListForKey(room.SourceListKey);
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found sourceList for room {room}", this, room.Key);
|
||||
|
||||
var sourceListItem = sourceList.FirstOrDefault(sli => {
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose,
|
||||
"SourceListItem {sourceListItem}:{sourceKey} tieLine sourceport device key {sourcePortDeviceKey}",
|
||||
this,
|
||||
sli.Key,
|
||||
sli.Value.SourceKey,
|
||||
sourceTieLine.SourcePort.ParentDevice.Key);
|
||||
|
||||
return sli.Value.SourceKey.Equals(sourceTieLine.SourcePort.ParentDevice.Key,StringComparison.InvariantCultureIgnoreCase);
|
||||
});
|
||||
|
||||
var source = sourceListItem.Value;
|
||||
|
||||
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);
|
||||
|
||||
var tempSourceListItem = new SourceListItem
|
||||
{
|
||||
SourceKey = "$transient",
|
||||
Name = sourceTieLine.SourcePort.Key,
|
||||
};
|
||||
|
||||
destination.CurrentSourceInfo = tempSourceListItem; ;
|
||||
destination.CurrentSourceInfoKey = "$transient";
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Got Source {source}", this, source);
|
||||
|
||||
destination.CurrentSourceInfo = source;
|
||||
destination.CurrentSourceInfoKey = source.SourceKey;
|
||||
}
|
||||
|
||||
private TieLine GetRootTieLine(TieLine tieLine)
|
||||
{
|
||||
TieLine nextTieLine = null;
|
||||
try
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "**Following tieLine {tieLine}**", this, tieLine);
|
||||
|
||||
if (tieLine.SourcePort.ParentDevice is IRoutingWithFeedback midpoint)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "TieLine Source device {sourceDevice} is midpoint", this, midpoint);
|
||||
|
||||
if(midpoint.CurrentRoutes == null || midpoint.CurrentRoutes.Count == 0)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "Midpoint {midpointKey} has no routes",this, midpoint.Key);
|
||||
return null;
|
||||
}
|
||||
|
||||
var currentRoute = midpoint.CurrentRoutes.FirstOrDefault(route => {
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Checking {route} against {tieLine}", this, route, tieLine);
|
||||
|
||||
return route.OutputPort.Key == tieLine.SourcePort.Key && route.OutputPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key;
|
||||
});
|
||||
|
||||
if (currentRoute == null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Information, "No route through midpoint {midpoint} for outputPort {outputPort}", this, midpoint.Key, tieLine.SourcePort);
|
||||
return null;
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found currentRoute {currentRoute} through {midpoint}", this, currentRoute, midpoint);
|
||||
|
||||
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => {
|
||||
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; });
|
||||
|
||||
if (nextTieLine != null)
|
||||
{
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found next tieLine {tieLine}. Walking the chain", this, nextTieLine);
|
||||
return GetRootTieLine(nextTieLine);
|
||||
}
|
||||
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Verbose, "Found root tieLine {tieLine}", this,nextTieLine);
|
||||
return nextTieLine;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
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);
|
||||
return tieLine;
|
||||
}
|
||||
|
||||
nextTieLine = TieLineCollection.Default.FirstOrDefault(tl => tl.DestinationPort.Key == tieLine.SourcePort.Key && tl.DestinationPort.ParentDevice.Key == tieLine.SourcePort.ParentDevice.Key );
|
||||
|
||||
if (nextTieLine != null)
|
||||
{
|
||||
return GetRootTieLine(nextTieLine);
|
||||
}
|
||||
} catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(ex, "Error walking tieLines: {Exception}", this, ex);
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
82
src/PepperDash.Essentials.Core/Routing/RoutingInputPort.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic RoutingInput with no statuses.
|
||||
/// </summary>
|
||||
public class RoutingInputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingInputs object this lives on
|
||||
/// </summary>
|
||||
public IRoutingInputs ParentDevice { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a basic RoutingInputPort
|
||||
/// </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>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingInputs parent)
|
||||
: this (key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a virtual routing input port that lives inside a device. For example
|
||||
/// the ports that link a DM card to a DM matrix bus
|
||||
/// </summary>
|
||||
/// <param name="isInternal">true for internal ports</param>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingInputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
ParentDevice = parent;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// Basic RoutingInput with no statuses.
|
||||
/// </summary>
|
||||
public class RoutingInputPort<TSelector> : RoutingPort<TSelector>
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingInputs object this lives on
|
||||
/// </summary>
|
||||
public IRoutingInputs<TSelector> ParentDevice { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a basic RoutingInputPort
|
||||
/// </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>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
TSelector selector, IRoutingInputs<TSelector> parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a virtual routing input port that lives inside a device. For example
|
||||
/// the ports that link a DM card to a DM matrix bus
|
||||
/// </summary>
|
||||
/// <param name="isInternal">true for internal ports</param>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
TSelector selector, IRoutingInputs<TSelector> parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
||||
/// Will provide video statistics on connected signals
|
||||
/// </summary>
|
||||
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
||||
{
|
||||
/// <summary>
|
||||
/// Video statuses attached to this port
|
||||
/// </summary>
|
||||
public VideoStatusOutputs VideoStatus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </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>
|
||||
public RoutingInputPortWithVideoStatuses(string key,
|
||||
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
|
||||
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
|
||||
base(key, type, connType, selector, parent)
|
||||
{
|
||||
VideoStatus = new VideoStatusOutputs(funcs);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The handler type for a Room's SourceInfoChange
|
||||
/// </summary>
|
||||
public delegate void SourceInfoChangeHandler(/*EssentialsRoomBase room,*/ SourceListItem info, ChangeType type);
|
||||
|
||||
|
||||
//*******************************************************************************************
|
||||
// Interfaces
|
||||
|
||||
/// <summary>
|
||||
/// For rooms with a single presentation source, change event
|
||||
/// </summary>
|
||||
public interface IHasCurrentSourceInfoChange
|
||||
{
|
||||
string CurrentSourceInfoKey { get; set; }
|
||||
SourceListItem CurrentSourceInfo { get; set; }
|
||||
event SourceInfoChangeHandler CurrentSourceChange;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a class that has a collection of RoutingInputPorts
|
||||
/// </summary>
|
||||
public interface IRoutingInputs : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingInputPort> InputPorts { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a class that has a collection of RoutingOutputPorts
|
||||
/// </summary>
|
||||
|
||||
public interface IRoutingOutputs : IKeyed
|
||||
{
|
||||
RoutingPortCollection<RoutingOutputPort> OutputPorts { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For fixed-source endpoint devices
|
||||
/// </summary>
|
||||
public interface IRoutingSink : IRoutingInputs, IHasCurrentSourceInfoChange
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Endpoint device like a display, that selects inputs
|
||||
/// </summary>
|
||||
public interface IRoutingSinkWithSwitching : IRoutingSink
|
||||
{
|
||||
//void ClearRoute();
|
||||
void ExecuteSwitch(object inputSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For devices like RMCs, baluns, other devices with no switching.
|
||||
/// </summary>
|
||||
public interface IRoutingInputsOutputs : IRoutingInputs, IRoutingOutputs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a midpoint device as have internal routing. Any devices in the middle of the
|
||||
/// signal chain, that do switching, must implement this for routing to work otherwise
|
||||
/// the routing algorithm will treat the IRoutingInputsOutputs device as a passthrough
|
||||
/// device.
|
||||
/// </summary>
|
||||
public interface IRouting : IRoutingInputsOutputs
|
||||
{
|
||||
void ExecuteSwitch(object inputSelector, object outputSelector, eRoutingSignalType signalType);
|
||||
}
|
||||
|
||||
public interface IRoutingWithClear : IRouting
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears a route to an output, however a device needs to do that
|
||||
/// </summary>
|
||||
/// <param name="outputSelector">Output to clear</param>
|
||||
/// <param name="signalType">signal type to clear</param>
|
||||
void ClearRoute(object outputSelector, eRoutingSignalType signalType);
|
||||
}
|
||||
|
||||
public interface IRoutingNumeric : IRouting
|
||||
{
|
||||
void ExecuteNumericSwitch(ushort input, ushort output, eRoutingSignalType type);
|
||||
}
|
||||
|
||||
public interface ITxRouting : IRoutingNumeric
|
||||
{
|
||||
IntFeedback VideoSourceNumericFeedback { get; }
|
||||
IntFeedback AudioSourceNumericFeedback { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines a receiver that has internal routing (DM-RMC-4K-Z-SCALER-C)
|
||||
/// </summary>
|
||||
public interface IRmcRouting : IRoutingNumeric
|
||||
{
|
||||
IntFeedback AudioVideoSourceNumericFeedback { get; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Defines an IRmcRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface ITxRoutingWithFeedback : ITxRouting
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an IRmcRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface IRmcRoutingWithFeedback : IRmcRouting
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an IRoutingOutputs devices as being a source - the start of the chain
|
||||
/// </summary>
|
||||
public interface IRoutingSource : IRoutingOutputs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an event structure for reporting output route data
|
||||
/// </summary>
|
||||
public interface IRoutingFeedback : IKeyName
|
||||
{
|
||||
event EventHandler<RoutingNumericEventArgs> NumericSwitchChange;
|
||||
//void OnSwitchChange(RoutingNumericEventArgs e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an IRoutingNumeric with a feedback event
|
||||
/// </summary>
|
||||
public interface IRoutingNumericWithFeedback : IRoutingNumeric, IRoutingFeedback
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines an IRouting with a feedback event
|
||||
/// </summary>
|
||||
public interface IRoutingWithFeedback : IRouting, IRoutingFeedback
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public class RoutingNumericEventArgs : EventArgs
|
||||
{
|
||||
|
||||
public uint? Output { get; set; }
|
||||
public uint? Input { get; set; }
|
||||
|
||||
public eRoutingSignalType SigType { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
|
||||
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
||||
eRoutingSignalType sigType)
|
||||
: this(null, null, outputPort, inputPort, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs()
|
||||
: this(null, null, null, null, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
||||
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
||||
{
|
||||
OutputPort = outputPort;
|
||||
InputPort = inputPort;
|
||||
|
||||
Output = output;
|
||||
Input = input;
|
||||
SigType = sigType;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IRoutingHasVideoInputSyncFeedbacks
|
||||
{
|
||||
FeedbackCollection<BoolFeedback> VideoInputSyncFeedbacks { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RoutingNumericEventArgs : EventArgs
|
||||
{
|
||||
|
||||
public uint? Output { get; set; }
|
||||
public uint? Input { get; set; }
|
||||
|
||||
public eRoutingSignalType SigType { get; set; }
|
||||
public RoutingInputPort InputPort { get; set; }
|
||||
public RoutingOutputPort OutputPort { get; set; }
|
||||
|
||||
public RoutingNumericEventArgs(uint output, uint input, eRoutingSignalType sigType) : this(output, input, null, null, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs(RoutingOutputPort outputPort, RoutingInputPort inputPort,
|
||||
eRoutingSignalType sigType)
|
||||
: this(null, null, outputPort, inputPort, sigType)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs()
|
||||
: this(null, null, null, null, 0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RoutingNumericEventArgs(uint? output, uint? input, RoutingOutputPort outputPort,
|
||||
RoutingInputPort inputPort, eRoutingSignalType sigType)
|
||||
{
|
||||
OutputPort = outputPort;
|
||||
InputPort = inputPort;
|
||||
|
||||
Output = output;
|
||||
Input = input;
|
||||
SigType = sigType;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
75
src/PepperDash.Essentials.Core/Routing/RoutingOutputPort.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class RoutingOutputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingOutputs object this port lives on
|
||||
/// </summary>
|
||||
public IRoutingOutputs ParentDevice { get; private set; }
|
||||
|
||||
public InUseTracking InUseTracker { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </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>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
InUseTracker = new InUseTracking();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ParentDevice.Key}|{Key}|{Type}|{ConnectionType}";
|
||||
}
|
||||
}
|
||||
|
||||
/*public class RoutingOutputPort<TSelector> : RoutingPort<TSelector>
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingOutputs object this port lives on
|
||||
/// </summary>
|
||||
public IRoutingOutputs ParentDevice { get; private set; }
|
||||
|
||||
public InUseTracking InUseTracker { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </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>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
TSelector selector, IRoutingOutputs parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
TSelector selector, IRoutingOutputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
ParentDevice = parent ?? throw new ArgumentNullException(nameof(parent));
|
||||
InUseTracker = new InUseTracking();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ParentDevice.Key + ":" + Key;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -1,15 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for RoutingInput and Output ports
|
||||
/// </summary>
|
||||
public abstract class RoutingPort : IKeyed
|
||||
/// <summary>
|
||||
/// Base class for RoutingInput and Output ports
|
||||
/// </summary>
|
||||
public abstract class RoutingPort : IKeyed
|
||||
{
|
||||
public string Key { get; private set; }
|
||||
public eRoutingSignalType Type { get; private set; }
|
||||
@@ -26,183 +23,26 @@ namespace PepperDash.Essentials.Core
|
||||
ConnectionType = connType;
|
||||
Selector = selector;
|
||||
IsInternal = isInternal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum eRoutingSignalType
|
||||
{
|
||||
Audio = 1,
|
||||
Video = 2,
|
||||
AudioVideo = Audio | Video,
|
||||
UsbOutput = 8,
|
||||
UsbInput = 16,
|
||||
SecondaryAudio = 32
|
||||
}
|
||||
/*public abstract class RoutingPort<TSelector>:IKeyed
|
||||
{
|
||||
public string Key { get; private set; }
|
||||
public eRoutingSignalType Type { get; private set; }
|
||||
public eRoutingPortConnectionType ConnectionType { get; private set; }
|
||||
public readonly TSelector Selector;
|
||||
public bool IsInternal { get; private set; }
|
||||
public object FeedbackMatchObject { get; set; }
|
||||
public object Port { get; set; }
|
||||
|
||||
public enum eRoutingPortConnectionType
|
||||
{
|
||||
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
|
||||
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Basic RoutingInput with no statuses.
|
||||
/// </summary>
|
||||
public class RoutingInputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingInputs object this lives on
|
||||
/// </summary>
|
||||
public IRoutingInputs ParentDevice { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a basic RoutingInputPort
|
||||
/// </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>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingInputs parent)
|
||||
: this (key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for a virtual routing input port that lives inside a device. For example
|
||||
/// the ports that link a DM card to a DM matrix bus
|
||||
/// </summary>
|
||||
/// <param name="isInternal">true for internal ports</param>
|
||||
public RoutingInputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingInputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException("parent");
|
||||
ParentDevice = parent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// Static method to get a named port from a named device
|
||||
///// </summary>
|
||||
///// <returns>Returns null if device or port doesn't exist</returns>
|
||||
//public static RoutingInputPort GetDevicePort(string deviceKey, string portKey)
|
||||
//{
|
||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingInputs;
|
||||
// if (sourceDev == null)
|
||||
// return null;
|
||||
// return sourceDev.InputPorts[portKey];
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Static method to get a named port from a card in a named ICardPortsDevice device
|
||||
///// Uses ICardPortsDevice.GetChildInputPort
|
||||
///// </summary>
|
||||
///// <param name="cardKey">'input-N'</param>
|
||||
///// <returns>null if device, card or port doesn't exist</returns>
|
||||
//public static RoutingInputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
|
||||
//{
|
||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
|
||||
// if (sourceDev == null)
|
||||
// return null;
|
||||
// return sourceDev.GetChildInputPort(cardKey, portKey);
|
||||
//}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A RoutingInputPort for devices like DM-TX and DM input cards.
|
||||
/// Will provide video statistics on connected signals
|
||||
/// </summary>
|
||||
public class RoutingInputPortWithVideoStatuses : RoutingInputPort
|
||||
{
|
||||
/// <summary>
|
||||
/// Video statuses attached to this port
|
||||
/// </summary>
|
||||
public VideoStatusOutputs VideoStatus { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </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>
|
||||
public RoutingInputPortWithVideoStatuses(string key,
|
||||
eRoutingSignalType type, eRoutingPortConnectionType connType, object selector,
|
||||
IRoutingInputs parent, VideoStatusFuncsWrapper funcs) :
|
||||
base(key, type, connType, selector, parent)
|
||||
{
|
||||
VideoStatus = new VideoStatusOutputs(funcs);
|
||||
}
|
||||
}
|
||||
|
||||
public class RoutingOutputPort : RoutingPort
|
||||
{
|
||||
/// <summary>
|
||||
/// The IRoutingOutputs object this port lives on
|
||||
/// </summary>
|
||||
public IRoutingOutputs ParentDevice { get; private set; }
|
||||
|
||||
public InUseTracking InUseTracker { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// </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>
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent)
|
||||
: this(key, type, connType, selector, parent, false)
|
||||
{
|
||||
}
|
||||
|
||||
public RoutingOutputPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType,
|
||||
object selector, IRoutingOutputs parent, bool isInternal)
|
||||
: base(key, type, connType, selector, isInternal)
|
||||
{
|
||||
if (parent == null)
|
||||
throw new ArgumentNullException("parent");
|
||||
ParentDevice = parent;
|
||||
InUseTracker = new InUseTracking();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return ParentDevice.Key + ":" + Key;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Static method to get a named port from a named device
|
||||
///// </summary>
|
||||
///// <returns>Returns null if device or port doesn't exist</returns>
|
||||
//public static RoutingOutputPort GetDevicePort(string deviceKey, string portKey)
|
||||
//{
|
||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as IRoutingOutputs;
|
||||
// if (sourceDev == null)
|
||||
// return null;
|
||||
// var port = sourceDev.OutputPorts[portKey];
|
||||
// if (port == null)
|
||||
// Debug.LogMessage(LogEventLevel.Information, "WARNING: Device '{0}' does does not contain output port '{1}'", deviceKey, portKey);
|
||||
// return port;
|
||||
//}
|
||||
|
||||
///// <summary>
|
||||
///// Static method to get a named port from a card in a named ICardPortsDevice device
|
||||
///// Uses ICardPortsDevice.GetChildOutputPort on that device
|
||||
///// </summary>
|
||||
///// <param name="cardKey">'input-N' or 'output-N'</param>
|
||||
///// <returns>null if device, card or port doesn't exist</returns>
|
||||
//public static RoutingOutputPort GetDeviceCardPort(string deviceKey, string cardKey, string portKey)
|
||||
//{
|
||||
// var sourceDev = DeviceManager.GetDeviceForKey(deviceKey) as ICardPortsDevice;
|
||||
// if (sourceDev == null)
|
||||
// return null;
|
||||
// var port = sourceDev.GetChildOutputPort(cardKey, portKey);
|
||||
//}
|
||||
}
|
||||
public RoutingPort(string key, eRoutingSignalType type, eRoutingPortConnectionType connType, TSelector selector, bool isInternal)
|
||||
{
|
||||
Key = key;
|
||||
Type = type;
|
||||
ConnectionType = connType;
|
||||
Selector = selector;
|
||||
IsInternal = isInternal;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -23,4 +23,21 @@ namespace PepperDash.Essentials.Core
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* /// <summary>
|
||||
/// Basically a List , with an indexer to find ports by key name
|
||||
/// </summary>
|
||||
public class RoutingPortCollection<T, TSelector> : List<T> where T : RoutingPort<TSelector>
|
||||
{
|
||||
/// <summary>
|
||||
/// Case-insensitive port lookup linked to ports' keys
|
||||
/// </summary>
|
||||
public T this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.FirstOrDefault(i => i.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Routing
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// These should correspond directly with the portNames var in the config tool.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharpPro;
|
||||
using Crestron.SimplSharpPro.DM;
|
||||
@@ -88,24 +89,26 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("Tie line: [{0}]{1} --> [{2}]{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
||||
return string.Format("Tie line: {0}:{1} --> {2}:{3}", SourcePort.ParentDevice.Key, SourcePort.Key,
|
||||
DestinationPort.ParentDevice.Key, DestinationPort.Key);
|
||||
}
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
//********************************************************************************
|
||||
|
||||
public class TieLineCollection : List<TieLine>
|
||||
{
|
||||
public static TieLineCollection Default
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Default == null)
|
||||
_Default = new TieLineCollection();
|
||||
return _Default;
|
||||
}
|
||||
}
|
||||
public class TieLineCollection : List<TieLine>
|
||||
{
|
||||
public static TieLineCollection Default
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_Default == null)
|
||||
_Default = new TieLineCollection();
|
||||
return _Default;
|
||||
}
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
static TieLineCollection _Default;
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace PepperDash.Essentials.Core.Config
|
||||
/// <returns>null if config data does not match ports, cards or devices</returns>
|
||||
public TieLine GetTieLine()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}", this);
|
||||
Debug.LogMessage(LogEventLevel.Information, "Build TieLine: {0}",null, this);
|
||||
// Get the source device
|
||||
var sourceDev = DeviceManager.GetDeviceForKey(SourceKey) as IRoutingOutputs;
|
||||
if (sourceDev == null)
|
||||
@@ -48,68 +48,29 @@ namespace PepperDash.Essentials.Core.Config
|
||||
}
|
||||
|
||||
//Get the source port
|
||||
RoutingOutputPort sourceOutputPort = null;
|
||||
//// If it's a card-based device, get the card and then the source port
|
||||
//if (sourceDev is ICardPortsDevice)
|
||||
//{
|
||||
// if (SourceCard == null)
|
||||
// {
|
||||
// LogError("Card missing from source device config");
|
||||
// return null;
|
||||
// }
|
||||
// sourceOutputPort = (sourceDev as ICardPortsDevice).GetChildOutputPort(SourceCard, SourcePort);
|
||||
// if (sourceOutputPort == null)
|
||||
// {
|
||||
// LogError("Source card does not contain port");
|
||||
// return null;
|
||||
// }
|
||||
//}
|
||||
//// otherwise it's a normal port device, get the source port
|
||||
//else
|
||||
//{
|
||||
sourceOutputPort = sourceDev.OutputPorts[SourcePort];
|
||||
if (sourceOutputPort == null)
|
||||
{
|
||||
LogError("Source does not contain port");
|
||||
return null;
|
||||
}
|
||||
//}
|
||||
var sourceOutputPort = sourceDev.OutputPorts[SourcePort];
|
||||
|
||||
if (sourceOutputPort == null)
|
||||
{
|
||||
LogError("Source does not contain port");
|
||||
return null;
|
||||
}
|
||||
|
||||
//Get the Destination port
|
||||
RoutingInputPort destinationInputPort = null;
|
||||
//// If it's a card-based device, get the card and then the Destination port
|
||||
//if (destDev is ICardPortsDevice)
|
||||
//{
|
||||
// if (DestinationCard == null)
|
||||
// {
|
||||
// LogError("Card missing from destination device config");
|
||||
// return null;
|
||||
// }
|
||||
// destinationInputPort = (destDev as ICardPortsDevice).GetChildInputPort(DestinationCard, DestinationPort);
|
||||
// if (destinationInputPort == null)
|
||||
// {
|
||||
// LogError("Destination card does not contain port");
|
||||
// return null;
|
||||
// }
|
||||
//}
|
||||
//// otherwise it's a normal port device, get the Destination port
|
||||
//else
|
||||
//{
|
||||
destinationInputPort = destDev.InputPorts[DestinationPort];
|
||||
if (destinationInputPort == null)
|
||||
//Get the Destination port
|
||||
var destinationInputPort = destDev.InputPorts[DestinationPort];
|
||||
|
||||
if (destinationInputPort == null)
|
||||
{
|
||||
LogError("Destination does not contain port");
|
||||
return null;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
return new TieLine(sourceOutputPort, destinationInputPort);
|
||||
}
|
||||
|
||||
void LogError(string msg)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Debug, "WARNING: Cannot create tie line: {0}:\r {1}", msg, this);
|
||||
Debug.LogMessage(LogEventLevel.Error, "WARNING: Cannot create tie line: {message}:\r {tieLineConfig}",null, msg, this);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public enum eRoutingPortConnectionType
|
||||
{
|
||||
None, BackplaneOnly, DisplayPort, Dvi, Hdmi, Rgb, Vga, LineAudio, DigitalAudio, Sdi,
|
||||
Composite, Component, DmCat, DmMmFiber, DmSmFiber, Speaker, Streaming, UsbC, HdBaseT
|
||||
}
|
||||
}
|
||||
16
src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
Normal file
16
src/PepperDash.Essentials.Core/Routing/eRoutingSignalType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
[Flags]
|
||||
public enum eRoutingSignalType
|
||||
{
|
||||
Audio = 1,
|
||||
Video = 2,
|
||||
AudioVideo = Audio | Video,
|
||||
UsbOutput = 8,
|
||||
UsbInput = 16,
|
||||
SecondaryAudio = 32
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ namespace PepperDash.Essentials.Core
|
||||
public IntFeedback PercentFeedback { get; private set; }
|
||||
public StringFeedback TimeRemainingFeedback { get; private set; }
|
||||
|
||||
public IntFeedback SecondsRemainingFeedback { get; private set; }
|
||||
|
||||
public bool CountsDown { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@@ -50,8 +52,8 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
var timeSpan = FinishTime - DateTime.Now;
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this,
|
||||
"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}",
|
||||
Debug.LogMessage(LogEventLevel.Verbose,
|
||||
"timeSpan.Minutes == {0}, timeSpan.Seconds == {1}, timeSpan.TotalSeconds == {2}", this,
|
||||
timeSpan.Minutes, timeSpan.Seconds, timeSpan.TotalSeconds);
|
||||
|
||||
if (Math.Floor(timeSpan.TotalSeconds) < 60 && Math.Floor(timeSpan.TotalSeconds) >= 0) //ignore milliseconds
|
||||
@@ -64,6 +66,8 @@ namespace PepperDash.Essentials.Core
|
||||
: String.Format("{0:00}:{1:00}", timeSpan.Minutes, timeSpan.Seconds);
|
||||
});
|
||||
|
||||
SecondsRemainingFeedback = new IntFeedback(() => (int)(FinishTime - DateTime.Now).TotalSeconds);
|
||||
|
||||
PercentFeedback =
|
||||
new IntFeedback(
|
||||
() =>
|
||||
@@ -99,6 +103,7 @@ namespace PepperDash.Essentials.Core
|
||||
public void Reset()
|
||||
{
|
||||
_isRunning = false;
|
||||
IsRunningFeedback.FireUpdate();
|
||||
Start();
|
||||
}
|
||||
|
||||
@@ -129,7 +134,11 @@ namespace PepperDash.Essentials.Core
|
||||
void StopHelper()
|
||||
{
|
||||
if (_secondTimer != null)
|
||||
{
|
||||
_secondTimer.Stop();
|
||||
_secondTimer = null;
|
||||
}
|
||||
|
||||
_isRunning = false;
|
||||
IsRunningFeedback.FireUpdate();
|
||||
}
|
||||
@@ -144,6 +153,7 @@ namespace PepperDash.Essentials.Core
|
||||
|
||||
PercentFeedback.FireUpdate();
|
||||
TimeRemainingFeedback.FireUpdate();
|
||||
SecondsRemainingFeedback.FireUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,74 @@
|
||||
namespace PepperDash.Essentials.Core
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace PepperDash.Essentials.Core
|
||||
{
|
||||
public class CrestronTouchpanelPropertiesConfig
|
||||
{
|
||||
[JsonProperty("control")]
|
||||
public EssentialsControlPropertiesConfig ControlProperties { get; set; }
|
||||
|
||||
[JsonProperty("ipId", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string IpId { get; set; }
|
||||
|
||||
[JsonProperty("defaultRoomKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string DefaultRoomKey { get; set; }
|
||||
|
||||
[JsonProperty("roomListKey", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string RoomListKey { get; set; }
|
||||
|
||||
[JsonProperty("sgdFile", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SgdFile { get; set; }
|
||||
|
||||
[JsonProperty("projectName", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string ProjectName { get; set; }
|
||||
public bool ShowVolumeGauge { get; set; }
|
||||
public bool UsesSplashPage { get; set; }
|
||||
public bool ShowDate { get; set; }
|
||||
public bool ShowTime { get; set; }
|
||||
|
||||
[JsonProperty("showVolumeGauge", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowVolumeGauge { get; set; }
|
||||
|
||||
[JsonProperty("usesSplashPage", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? UsesSplashPage { get; set; }
|
||||
|
||||
[JsonProperty("showDate", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowDate { get; set; }
|
||||
|
||||
[JsonProperty("showTime", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowTime { get; set; }
|
||||
|
||||
[JsonProperty("setup", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public UiSetupPropertiesConfig Setup { get; set; }
|
||||
|
||||
[JsonProperty("headerStyle", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string HeaderStyle { get; set; }
|
||||
public bool IncludeInFusionRoomHealth { get; set; }
|
||||
public uint ScreenSaverTimeoutMin { get; set; }
|
||||
public uint ScreenSaverMovePositionIntervalMs { get; set; }
|
||||
|
||||
[JsonProperty("includeInFusionRoomHealth", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? IncludeInFusionRoomHealth { get; set; }
|
||||
|
||||
[JsonProperty("screenSaverTimeoutMin", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public uint? ScreenSaverTimeoutMin { get; set; }
|
||||
|
||||
[JsonProperty("screenSaverMovePositionIntervalMs", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public uint? ScreenSaverMovePositionIntervalMs { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The count of sources that will trigger the "additional" arrows to show on the SRL.
|
||||
/// Defaults to 5
|
||||
/// </summary>
|
||||
public int SourcesOverflowCount { get; set; }
|
||||
[JsonProperty("sourcesOverflowCount", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public int? SourcesOverflowCount { get; set; }
|
||||
|
||||
public CrestronTouchpanelPropertiesConfig()
|
||||
public CrestronTouchpanelPropertiesConfig() : this(false) { }
|
||||
|
||||
public CrestronTouchpanelPropertiesConfig(bool setDefaultValues = false)
|
||||
{
|
||||
if(!setDefaultValues) { return; }
|
||||
SourcesOverflowCount = 5;
|
||||
HeaderStyle = CrestronTouchpanelPropertiesConfig.Habanero;
|
||||
HeaderStyle = Habanero;
|
||||
|
||||
// Default values
|
||||
ScreenSaverTimeoutMin = 5;
|
||||
ScreenSaverMovePositionIntervalMs = 15000;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "habanero"
|
||||
@@ -49,6 +85,7 @@
|
||||
/// </summary>
|
||||
public class UiSetupPropertiesConfig
|
||||
{
|
||||
[JsonProperty("isVisible", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool IsVisible { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace PepperDash.Essentials.Core.Web
|
||||
Name = "DevList",
|
||||
RouteHandler = new DevListRequestHandler()
|
||||
},
|
||||
new HttpCwsRoute("deviceCommands")
|
||||
new HttpCwsRoute("deviceCommands/{deviceKey}")
|
||||
{
|
||||
Name = "DevJson",
|
||||
RouteHandler = new DevJsonRequestHandler()
|
||||
@@ -163,6 +163,11 @@ namespace PepperDash.Essentials.Core.Web
|
||||
{
|
||||
Name = "Load Config",
|
||||
RouteHandler = new LoadConfigRequestHandler()
|
||||
},
|
||||
new HttpCwsRoute("getTielines")
|
||||
{
|
||||
Name = "Get TieLines",
|
||||
RouteHandler = new GetTieLinesRequestHandler()
|
||||
}
|
||||
|
||||
};
|
||||
@@ -233,32 +238,32 @@ namespace PepperDash.Essentials.Core.Web
|
||||
/// </example>
|
||||
public void GetPaths()
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "{0}", new String('-', 50));
|
||||
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
|
||||
|
||||
var currentIp = CrestronEthernetHelper.GetEthernetParameter(
|
||||
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_CURRENT_IP_ADDRESS, 0);
|
||||
|
||||
var hostname = CrestronEthernetHelper.GetEthernetParameter(
|
||||
CrestronEthernetHelper.ETHERNET_PARAMETER_TO_GET.GET_HOSTNAME, 0);
|
||||
|
||||
var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
|
||||
? $"http(s)://{hostname}/VirtualControl/Rooms/{InitialParametersClass.RoomId}/cws{BasePath}"
|
||||
: $"http(s)://{currentIp}/cws{BasePath}";
|
||||
|
||||
var path = CrestronEnvironment.DevicePlatform == eDevicePlatform.Server
|
||||
? string.Format("http(s)://{0}/VirtualControl/Rooms/{1}/cws{2}", hostname, InitialParametersClass.RoomId, BasePath)
|
||||
: string.Format("http(s)://{0}/cws{1}", currentIp, BasePath);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Server:{0}", path);
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Server:{path:l}", path);
|
||||
|
||||
var routeCollection = _server.GetRouteCollection();
|
||||
if (routeCollection == null)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Server route collection is null");
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Server route collection is null");
|
||||
return;
|
||||
}
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "Configured Routes:");
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "Configured Routes:");
|
||||
foreach (var route in routeCollection)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "{0}: {1}/{2}", route.Name, path, route.Url);
|
||||
Debug.LogMessage(LogEventLevel.Information, this, "{routeName:l}: {routePath:l}/{routeUrl:l}", route.Name, path, route.Url);
|
||||
}
|
||||
Debug.LogMessage(LogEventLevel.Verbose, this, "{0}", new String('-', 50));
|
||||
Debug.LogMessage(LogEventLevel.Information, this, new string('-', 50));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,11 +7,11 @@ using PepperDash.Core;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Web
|
||||
{
|
||||
public class EssentialsWebApiHelpers
|
||||
public static class EssentialsWebApiHelpers
|
||||
{
|
||||
public static string GetRequestBody(HttpCwsRequest request)
|
||||
public static string GetRequestBody(this HttpCwsRequest request)
|
||||
{
|
||||
var bytes = new Byte[request.ContentLength];
|
||||
var bytes = new byte[request.ContentLength];
|
||||
|
||||
request.InputStream.Read(bytes, 0, request.ContentLength);
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace PepperDash.Essentials.Core.Web
|
||||
{
|
||||
return new
|
||||
{
|
||||
Name = assembly.Name,
|
||||
Version = assembly.Version
|
||||
assembly.Name,
|
||||
assembly.Version
|
||||
};
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace PepperDash.Essentials.Core.Web
|
||||
{
|
||||
return new
|
||||
{
|
||||
Key = device.Key,
|
||||
device.Key,
|
||||
Name = (device is IKeyName)
|
||||
? (device as IKeyName).Name
|
||||
: "---"
|
||||
@@ -80,7 +80,7 @@ namespace PepperDash.Essentials.Core.Web
|
||||
{
|
||||
Type = device.Key,
|
||||
Description = device.Value.Description,
|
||||
CType = device.Value.CType == null ? "---": device.Value.CType.ToString()
|
||||
CType = device.Value.Type == null ? "---": device.Value.Type.ToString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
return;
|
||||
}
|
||||
|
||||
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
|
||||
var data = context.Request.GetRequestBody();
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using Serilog.Events;
|
||||
@@ -25,28 +26,55 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
/// <param name="context"></param>
|
||||
protected override void HandlePost(HttpCwsContext context)
|
||||
{
|
||||
var routeData = context.Request.RouteData;
|
||||
|
||||
if(routeData == null)
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Bad Request";
|
||||
context.Response.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!routeData.Values.TryGetValue("deviceKey", out var deviceKey))
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Bad Request";
|
||||
context.Response.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.Request.ContentLength < 0)
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Bad Request";
|
||||
context.Response.StatusDescription = "Bad Request: no body";
|
||||
context.Response.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
|
||||
var data = context.Request.GetRequestBody();
|
||||
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Bad Request";
|
||||
context.Response.StatusDescription = "Bad Request: no body";
|
||||
context.Response.End();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
DeviceJsonApi.DoDeviceActionWithJson(data);
|
||||
{
|
||||
var daw = new DeviceActionWrapper { DeviceKey = (string) deviceKey};
|
||||
|
||||
JsonConvert.PopulateObject(data, daw);
|
||||
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Device Action Wrapper: {@wrapper}", null, daw);
|
||||
|
||||
DeviceJsonApi.DoDeviceAction(daw);
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.StatusDescription = "OK";
|
||||
@@ -54,12 +82,11 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogMessage(LogEventLevel.Error, "Exception Message: {0}", ex.Message);
|
||||
Debug.LogMessage(LogEventLevel.Verbose, "Exception Stack Trace: {0}", ex.StackTrace);
|
||||
if(ex.InnerException != null) Debug.LogMessage(LogEventLevel.Error, "Exception Inner: {0}", ex.InnerException);
|
||||
Debug.LogMessage(ex, "Error handling device command: {Exception}");
|
||||
|
||||
context.Response.StatusCode = 400;
|
||||
context.Response.StatusDescription = "Bad Request";
|
||||
context.Response.Write(JsonConvert.SerializeObject(new { error = ex.Message }), false);
|
||||
context.Response.End();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
return;
|
||||
}
|
||||
|
||||
allDevices.Sort((a, b) => System.String.Compare(a.Key, b.Key, System.StringComparison.Ordinal));
|
||||
allDevices.Sort((a, b) => string.Compare(a.Key, b.Key, System.StringComparison.Ordinal));
|
||||
|
||||
var deviceList = allDevices.Select(d => EssentialsWebApiHelpers.MapToDeviceListObject(d)).ToList();
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
@@ -25,6 +26,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
var routeData = context.Request.RouteData;
|
||||
Debug.LogMessage(Serilog.Events.LogEventLevel.Debug, "Getting DevMethods: {@routeData}", routeData);
|
||||
if (routeData == null)
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
return;
|
||||
}
|
||||
|
||||
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
|
||||
var data = context.Request.GetRequestBody();
|
||||
if (string.IsNullOrEmpty(data))
|
||||
{
|
||||
context.Response.StatusCode = 400;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
using Crestron.SimplSharp.WebScripting;
|
||||
using Newtonsoft.Json;
|
||||
using PepperDash.Core.Web.RequestHandlers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
{
|
||||
public class GetTieLinesRequestHandler:WebApiBaseRequestHandler
|
||||
{
|
||||
public GetTieLinesRequestHandler() : base(true) { }
|
||||
|
||||
protected override void HandleGet(HttpCwsContext context)
|
||||
{
|
||||
var tieLineString = JsonConvert.SerializeObject(TieLineCollection.Default.Select((tl) => new {
|
||||
sourceKey = tl.SourcePort.ParentDevice.Key,
|
||||
sourcePort = tl.SourcePort.Key,
|
||||
destinationKey = tl.DestinationPort.ParentDevice.Key,
|
||||
destinationPort = tl.DestinationPort.Key
|
||||
}));
|
||||
|
||||
context.Response.StatusCode = 200;
|
||||
context.Response.StatusDescription = "OK";
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.ContentEncoding = Encoding.UTF8;
|
||||
context.Response.Write(tieLineString, false);
|
||||
context.Response.End();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ namespace PepperDash.Essentials.Core.Web.RequestHandlers
|
||||
return;
|
||||
}
|
||||
|
||||
var data = EssentialsWebApiHelpers.GetRequestBody(context.Request);
|
||||
var data = context.Request.GetRequestBody();
|
||||
if (data == null)
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
|
||||
@@ -8,7 +8,6 @@ using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
|
||||
using PepperDash.Essentials.Core.Routing;
|
||||
using Serilog.Events;
|
||||
|
||||
namespace PepperDash.Essentials.Devices.Common
|
||||
@@ -18,6 +17,8 @@ namespace PepperDash.Essentials.Devices.Common
|
||||
/// </summary>
|
||||
public class GenericAudioOut : EssentialsDevice, IRoutingSink
|
||||
{
|
||||
public RoutingInputPort CurrentInputPort => AnyAudioIn;
|
||||
|
||||
public event SourceInfoChangeHandler CurrentSourceChange;
|
||||
|
||||
public string CurrentSourceInfoKey { get; set; }
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using Crestron.SimplSharpPro.DeviceSupport;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
|
||||
@@ -12,7 +12,7 @@ using PepperDash.Essentials.Core.Bridges;
|
||||
using PepperDash.Essentials.Core.Config;
|
||||
using PepperDash.Essentials.Devices.Common.Codec;
|
||||
using System.Text.RegularExpressions;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using Serilog.Events;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Crestron.SimplSharp.Reflection;
|
||||
using System.Reflection;
|
||||
using PepperDash.Core;
|
||||
using PepperDash.Essentials.Core;
|
||||
using Serilog.Events;
|
||||
@@ -25,7 +25,7 @@ namespace PepperDash.Essentials.Devices.Common
|
||||
{
|
||||
try
|
||||
{
|
||||
var factory = (IDeviceFactory)Crestron.SimplSharp.Reflection.Activator.CreateInstance(type);
|
||||
var factory = (IDeviceFactory)Activator.CreateInstance(type);
|
||||
factory.LoadTypeFactories();
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user